Commit 20bd5929 authored by Thomas Michael Timmermanns's avatar Thomas Michael Timmermanns
Browse files

added first tests

parent 89d00968
......@@ -2,7 +2,7 @@ package de.monticore.lang.monticar;
grammar CNNArch extends de.monticore.lang.math.Math {
symbol scope CNNArchCompilationUnit = "architecture" Name& "{" Architecture "}";
symbol scope CNNArchCompilationUnit = "Architecture" Name& "{" Architecture "}";
Architecture = (MainLayer)+ OutputLayer;
......@@ -17,12 +17,14 @@ grammar CNNArch extends de.monticore.lang.math.Math {
ast ArgumentAssignment = method public Enum getLhs(){}
method public ASTArgumentRhs getRhs(){};
ArgumentRhs = stringVal:String
| number:Number
ArgumentRhs = number:Number
| tuple:Tuple
| booleanVal:Boolean;
| booleanVal:Boolean
| type:ArgumentKeyword;
Tuple = "(" values:Number "," values:(Number || ",")+ ")";
enum Boolean = TRUE:"true" | FALSE:"false" | TRUE:"True" | FALSE:"False";
interface ArgumentKeyword;
ast ArgumentKeyword = method Optional<String> getName(){};
......@@ -41,25 +43,33 @@ grammar CNNArch extends de.monticore.lang.math.Math {
FullyConnectedMethod implements MainLayerMethod = name:"FullyConnected" "(" arguments:(FullyConnectedArgumentAssignment || ",")* ")";
FullyConnectedArgumentAssignment implements ArgumentAssignment = lhs:FullyConnectedArgument "=" rhs:ArgumentRhs;
enum FullyConnectedArgument = NUMHIDDEN:"num_hidden" //required
enum FullyConnectedArgument = NUMHIDDEN:"units" //required
| NOBIAS:"no_bias";
ActivationMethod implements MainLayerMethod = name:"Activation" "(" arguments:(ActivationArgumentAssignment || ",")* ")";
ActivationArgumentAssignment implements ArgumentAssignment = lhs:ActivationArgument "=" rhs:ArgumentRhs;
enum ActivationArgument = ACTTYPE:"act_type"; //required
enum ActivationArgument = ACTTYPE:"type"; //required
ActivationType implements ArgumentKeyword = name:"relu"
| name:"sigmoid"
| name:"tanh"
| name:"softmax";
PoolingMethod implements MainLayerMethod = name:"Pooling" "(" arguments:(PoolingArgumentAssignment || ",")* ")";
PoolingArgumentAssignment implements ArgumentAssignment = lhs:PoolingArgument "=" rhs:ArgumentRhs;
enum PoolingArgument = KERNEL:"kernel" //required
| POOLTYPE:"pool_type" //required
| POOLTYPE:"type" //required
| POOLINGCONVENTION:"pooling_convention"
| STRIDE:"stride"
| PAD:"pad";
PoolingType implements ArgumentKeyword = name:"max"
| name:"avg"
| name:"sum";
PoolingConvention implements ArgumentKeyword = name:"full"
| name:"valid";
DropoutMethod implements MainLayerMethod = name:"Dropout" "(" arguments:(DropoutArgumentAssignment || ",")* ")";
DropoutArgumentAssignment implements ArgumentAssignment = lhs:DropoutArgument "=" rhs:ArgumentRhs;
enum DropoutArgument = P:"p"
| MODE:"mode";
enum DropoutArgument = P:"p";
LRNMethod implements MainLayerMethod = name:"LRN" "(" arguments:(LRNArgumentAssignment || ",")* ")";
LRNArgumentAssignment implements ArgumentAssignment = lhs:LRNArgument "=" rhs:ArgumentRhs;
......@@ -68,27 +78,22 @@ grammar CNNArch extends de.monticore.lang.math.Math {
| KNORM:"knorm"
| NSIZE:"nsize"; //required
FlattenMethod implements MainLayerMethod = name:"Flatten" "(" ")";
StandardOutputLayerMethod implements OutputLayerMethod = (optName:"SoftmaxOutput"
| optName:"MAERegressionOutput"
| optName:"LinearRegressionOutput"
| optName:"LogisticRegressionOutput") "("
arguments:(StandardOutputArgumentAssignment || ",")* ")";
ast StandardOutputLayerMethod = method public String getName(){
return optName.get();
};
StandardOutputLayerMethod implements OutputLayerMethod = name:"Output" "(" arguments:(StandardOutputArgumentAssignment || ",")* ")";
StandardOutputArgumentAssignment implements ArgumentAssignment = lhs:StandardOutputArgument "=" rhs:ArgumentRhs;
enum StandardOutputArgument = GRADSCALE:"grad_scale";
SVMOutputLayerMethod implements OutputLayerMethod = name:"SVMOutput" "("
arguments:(SVMOutputArgumentAssignment || ",")* ")";
SVMOutputArgumentAssignment implements ArgumentAssignment = lhs:SVMOutputArgument "=" rhs:ArgumentRhs;
enum SVMOutputArgument = MARGIN:"margin"
| REGULARIZATIONCOEFFICIENT:"regularization_coefficient"
| USELINEAR:"use_linear";
enum StandardOutputArgument = GRADSCALE:"grad_scale"
| TYPE:"type";
OutputType implements ArgumentKeyword = name:"softmax"
| name:"linear"
| name:"logistic"
| name:"mae";
//SVMOutputLayerMethod implements OutputLayerMethod = name:"SVMOutput" "("
// arguments:(SVMOutputArgumentAssignment || ",")* ")";
//SVMOutputArgumentAssignment implements ArgumentAssignment = lhs:SVMOutputArgument "=" rhs:ArgumentRhs;
//enum SVMOutputArgument = MARGIN:"margin"
// | REGULARIZATIONCOEFFICIENT:"regularization_coefficient"
// | USELINEAR:"use_linear";
}
\ No newline at end of file
......@@ -26,7 +26,7 @@ import java.util.List;
public class ArchitectureCheck implements CNNArchASTArchitectureCoCo {
private static final ASTFlattenMethod.Builder flattenMethodBuilder = new ASTFlattenMethod.Builder().name("Flatten");
//private static final ASTFlattenMethod.Builder flattenMethodBuilder = new ASTFlattenMethod.Builder().name("Flatten");
@Override
public void check(ASTArchitecture node) {
......@@ -35,7 +35,7 @@ public class ArchitectureCheck implements CNNArchASTArchitectureCoCo {
//set position for each layer and insert flatten layer if necessary
private void completeArchitecture(List<ASTMainLayer> layers) {
int pos = 0;
/*int pos = 0;
boolean isConvNet = false;
int flattenPos = -1;
......@@ -58,6 +58,6 @@ public class ArchitectureCheck implements CNNArchASTArchitectureCoCo {
ASTMainLayer flattenLayer = (new ASTMainLayer.Builder()).method(flattenMethodBuilder.build()).build();
layers.add(flattenPos, flattenLayer);
//flattenLayer.setPosition(flattenPos);
}
}*/
}
}
\ No newline at end of file
......@@ -24,7 +24,7 @@ public class CNNArchCocos {
public static CNNArchCoCoChecker createChecker() {
return new CNNArchCoCoChecker()
.addCoCo(new ArgumentCheck())
.addCoCo(new DuplicateArgumentCheck())
.addCoCo(new ArchitectureCheck());
}
}
\ No newline at end of file
......@@ -21,11 +21,25 @@
package de.monticore.lang.monticar.cnnarch._cocos;
import de.monticore.lang.monticar.cnnarch._ast.ASTArgumentAssignment;
import de.monticore.lang.monticar.cnnarch._ast.ASTMethod;
import de.se_rwth.commons.logging.Log;
public class ArgumentCheck implements CNNArchASTArgumentAssignmentCoCo {
import java.util.HashSet;
import java.util.Set;
@Override
public void check(ASTArgumentAssignment node) {
public class DuplicateArgumentCheck implements CNNArchASTMethodCoCo {
@Override
public void check(ASTMethod node) {
Set<Enum> set = new HashSet<>();
for (ASTArgumentAssignment assignment : node.getArguments()) {
if (set.contains(assignment.getLhs())) {
Log.error("0x03011 Multiple assignments of the same argument are not allowed",
assignment.get_SourcePositionStart());
}
else {
set.add(assignment.getLhs());
}
}
}
}
\ No newline at end of file
<configuration>
<timestamp key="bySecond" datePattern="yyyy-MM-dd-HHmmss"/>
<!-- this is a console appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%date{yyyy-MM-dd HH:mm:ss} %-7([%level]) %logger{26} %message%n
</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
/**
*
* ******************************************************************************
* MontiCAR Modeling Family, www.se-rwth.de
* Copyright (c) 2017, Software Engineering Group at RWTH Aachen,
* All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* *******************************************************************************
*/
package de.monticore.lang.monticar.cnnarch;
import de.monticore.ModelingLanguageFamily;
import de.monticore.io.paths.ModelPath;
import de.monticore.lang.embeddedmontiarc.LogConfig;
import de.monticore.lang.monticar.cnnarch._symboltable.CNNArchLanguage;
import de.monticore.symboltable.GlobalScope;
import de.monticore.symboltable.Scope;
import java.nio.file.Paths;
public class AbstractSymtabTest {
protected static Scope createSymTab(String... modelPath) {
ModelingLanguageFamily fam = new ModelingLanguageFamily();
fam.addModelingLanguage(new CNNArchLanguage());
final ModelPath mp = new ModelPath();
for (String m : modelPath) {
mp.addEntry(Paths.get(m));
}
GlobalScope scope = new GlobalScope(mp, fam);
LogConfig.init();
return scope;
}
}
/**
*
* ******************************************************************************
* MontiCAR Modeling Family, www.se-rwth.de
* Copyright (c) 2017, Software Engineering Group at RWTH Aachen,
* All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* *******************************************************************************
*/
package de.monticore.lang.monticar.cnnarch;
import de.monticore.lang.monticar.cnnarch._ast.ASTCNNArchCompilationUnit;
import de.monticore.lang.monticar.cnnarch._parser.CNNArchParser;
import de.se_rwth.commons.logging.Log;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.junit.Assert.assertTrue;
public class ParserTest {
public static final boolean ENABLE_FAIL_QUICK = false;
private static List<String> expectedParseErrorModels = Arrays.asList(
// incorrect argument name for a layer
"src/test/resources/WrongArgument.cnna",
"src/test/resources/MissingOutputLayer.cnna",
"src/test/resources/WrongOutputLayerPosition.cnna",
"src/test/resources/WrongActivationType.cnna")
.stream().map(s -> Paths.get(s).toString())
.collect(Collectors.toList());
@Before
public void setUp() {
// ensure an empty log
Log.getFindings().clear();
Log.enableFailQuick(ENABLE_FAIL_QUICK);
}
@Test
public void testCNNArch() throws Exception {
test("cnna");
if (Log.getErrorCount() > 0) {
throw new Exception("Test Failed, found errors");
}
}
private void test(String fileEnding) throws IOException {
ParseTest parserTest = new ParseTest("." + fileEnding);
Files.walkFileTree(Paths.get("src/test/resources"), parserTest);
if (!parserTest.getModelsInError().isEmpty()) {
Log.debug("Models in error", "ParserTest");
for (String model : parserTest.getModelsInError()) {
Log.debug(" " + model, "ParserTest");
}
}
Log.info("Count of tested models: " + parserTest.getTestCount(), "ParserTest");
Log.info("Count of correctly parsed models: "
+ (parserTest.getTestCount() - parserTest.getModelsInError().size()), "ParserTest");
assertTrue("There were models that could not be parsed", parserTest.getModelsInError()
.isEmpty());
}
/**
* Visits files of the given file ending and checks whether they are parsable.
*
* @author Robert Heim
* @see Files#walkFileTree(Path, java.nio.file.FileVisitor)
*/
private static class ParseTest extends SimpleFileVisitor<Path> {
private String fileEnding;
private List<String> modelsInError = new ArrayList<>();
private int testCount = 0;
public ParseTest(String fileEnding) {
super();
this.fileEnding = fileEnding;
}
/**
* @return testCount
*/
public int getTestCount() {
return this.testCount;
}
/**
* @return modelsInError
*/
public List<String> getModelsInError() {
return this.modelsInError;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if (file.toFile().isFile()
&& (file.toString().toLowerCase().endsWith(fileEnding))) {
Log.debug("Parsing file " + file.toString(), "ParserTest");
testCount++;
Optional<ASTCNNArchCompilationUnit> model = Optional.empty();
boolean expectingError = ParserTest.expectedParseErrorModels.contains(file.toString());
CNNArchParser parser = new CNNArchParser();
try {
if (expectingError) {
Log.enableFailQuick(false);
}
model = parser.parse(file.toString());
}
catch (Exception e) {
if (!expectingError) {
Log.error("Exception during test", e);
}
}
if (!expectingError && (parser.hasErrors() || !model.isPresent())) {
modelsInError.add(file.toString());
Log.error("There were unexpected parser errors");
}
else {
Log.getFindings().clear();
}
Log.enableFailQuick(ParserTest.ENABLE_FAIL_QUICK);
}
return FileVisitResult.CONTINUE;
}
}
}
/**
*
* ******************************************************************************
* MontiCAR Modeling Family, www.se-rwth.de
* Copyright (c) 2017, Software Engineering Group at RWTH Aachen,
* All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* *******************************************************************************
*/
package de.monticore.lang.monticar.cnnarch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import de.monticore.lang.monticar.cnnarch._parser.CNNArchParser;
import de.monticore.lang.monticar.cnnarch._symboltable.CNNArchCompilationUnitSymbol;
import de.monticore.symboltable.Scope;
import org.junit.Ignore;
import org.junit.Test;
public class SymtabTest extends AbstractSymtabTest {
@Test
public void testParsing() throws Exception {
CNNArchParser parser = new CNNArchParser();
assertTrue(parser.parse("src/test/resources/SimpleNetwork2.cnna").isPresent());
}
@Test
public void testAlexnet(){
Scope symTab = createSymTab("src/test/resources");
CNNArchCompilationUnitSymbol a = symTab.<CNNArchCompilationUnitSymbol>resolve(
"Alexnet",
CNNArchCompilationUnitSymbol.KIND).orElse(null);
assertNotNull(a);
}
}
/**
*
* ******************************************************************************
* MontiCAR Modeling Family, www.se-rwth.de
* Copyright (c) 2017, Software Engineering Group at RWTH Aachen,
* All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* *******************************************************************************
*/
package de.monticore.lang.monticar.cnnarch.cocos;
import de.monticore.lang.monticar.cnnarch.AbstractSymtabTest;
import de.monticore.lang.monticar.cnnarch._ast.ASTCNNArchCompilationUnit;
import de.monticore.lang.monticar.cnnarch._ast.ASTCNNArchNode;
import de.monticore.lang.monticar.cnnarch._cocos.CNNArchCoCoChecker;
import de.monticore.lang.monticar.cnnarch._cocos.CNNArchCocos;
import de.monticore.lang.monticar.cnnarch._symboltable.CNNArchCompilationUnitSymbol;
import de.monticore.symboltable.Scope;
import de.se_rwth.commons.logging.Finding;
import de.se_rwth.commons.logging.Log;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.junit.Assert.*;
public class AbstractCoCoTest extends AbstractSymtabTest {
private static final String MODEL_PATH = "src/test/resources/";
protected static ASTCNNArchNode getAstNode(String modelPath, String model) {
Scope symTab = createSymTab(MODEL_PATH + modelPath);
CNNArchCompilationUnitSymbol comp = symTab.<CNNArchCompilationUnitSymbol> resolve(
model, CNNArchCompilationUnitSymbol.KIND).orElse(null);
assertNotNull("Could not resolve model " + model, comp);
return (ASTCNNArchNode) comp.getAstNode().get();
}
/**
* Checks all cocos on the given model. Don't use for writing new test cases, use checkValid and
* checkInvalid instead.
*/
@Deprecated
protected static void runCheckerWithSymTab(String modelPath, String model) {
Log.getFindings().clear();
ASTCNNArchNode node = getAstNode(modelPath, model);
CNNArchCocos.createChecker().checkAll(node);
}
/**
* Checks all cocos on the given node, and checks for absence of errors. Use this for checking
* valid models.
*/
protected static void checkValid(String modelPath, String model) {
Log.getFindings().clear();
CNNArchCocos.createChecker().checkAll(getAstNode(modelPath, model));
new ExpectedErrorInfo().checkOnlyExpectedPresent(Log.getFindings());
}
/**
* Runs coco checks on the model with two different coco sets: Once with all cocos, checking that
* the expected errors are present; once only with the given cocos, checking that no addditional
* errors are present.
*/
protected static void checkInvalid(CNNArchCoCoChecker cocos, ASTCNNArchNode node,
ExpectedErrorInfo expectedErrors) {
// check whether all the expected errors are present when using all cocos
Log.getFindings().clear();
CNNArchCocos.createChecker().checkAll(node);
expectedErrors.checkExpectedPresent(Log.getFindings(), "Got no findings when checking all "
+ "cocos. Did you forget to add the new coco to MontiArcCocos?");
// check whether only the expected errors are present when using only the given cocos
Log.getFindings().clear();
cocos.checkAll(node);
expectedErrors.checkOnlyExpectedPresent(Log.getFindings(), "Got no findings when checking only "
+ "the given coco. Did you pass an empty coco checker?");
}
protected static class ExpectedErrorInfo {
private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("x[0-9A-F]{5}");
private int numExpectedFindings = 0;
private HashSet<String> expectedErrorCodes = new HashSet<>();
private Predicate<String> containsExpectedErrorCode;
/**
* Raises an error if the given error codes don't match the convention for error codes in test
* cases (no leading zero, capital hexadecimal digits)
*/
protected static void checkExpectedErrorCodes(String[] errorCodes) {
for (String errorCode : errorCodes) {
if (!ERROR_CODE_PATTERN.matcher(errorCode).matches()) {
Log.error(String.format(
"The given expected error code \"%s\" is not a valid error code (pattern: \"%s\")",
errorCode, ERROR_CODE_PATTERN.pattern()));
}