Added CoCos and tests

parent aa815bd3
#
#
# ******************************************************************************
# 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/>.
# *******************************************************************************
#
# Java Maven CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-java/ for more details
......
......@@ -10,12 +10,12 @@ grammar CNNArch extends de.monticore.lang.monticar.Common2 {
symbol InputDef implements VarDef = "def" "input" Name& (ArrayDeclaration)?;
symbol OutputDef implements VarDef = "def" "output" Name& (ArrayDeclaration)?;
interface ArchitectureElement extends GroupElement;
interface ArchitectureElement;
interface GroupElement;
interface Method;
interface GroupMethod extends Method,GroupElement;
interface MergeMethod extends Method;
interface LayerMethod extends Method,ArchitectureElement;
interface LayerMethod extends Method,ArchitectureElement,GroupElement;
interface InputStructure;
interface InputElement;
interface ArgumentListing;
......@@ -34,12 +34,12 @@ grammar CNNArch extends de.monticore.lang.monticar.Common2 {
elements:ArchitectureElement* "}" "->"
output:Name@OutputDef;
RepeatStructure implements ArchitectureElement = "repeat"
intLiteral:UnitNumberResolution "{"
ArchitectureElement* "}";
RepeatStructure implements ArchitectureElement,GroupElement = "repeat"
intLiteral:UnitNumberResolution "{"
ArchitectureElement* "}";
StandardGroupStructure implements ArchitectureElement,GroupStructure = groups:StandardGroup+
MergeStructure;
StandardGroupStructure implements ArchitectureElement,GroupElement,GroupStructure = groups:StandardGroup+
MergeStructure;
......@@ -88,19 +88,19 @@ grammar CNNArch extends de.monticore.lang.monticar.Common2 {
ConcatenateMethod implements MergeMethod = type:ConcatenateType "(" argumentListing:ConcatenateArgumentListing ")";
ConcatenateArgumentListing implements ArgumentListing = arguments:(ConcatenateArgumentAssignment || ",")*;
ConcatenateArgumentAssignment implements ArgumentAssignment = lhs:ConcatenateArgument "=" rhs:ArgumentRhs;
enum ConcatenateArgument = "placeholder";
enum ConcatenateArgument = XPLACEHOLDERX:"_placeholder";
enum ConcatenateType = "concatenate";
AddMethod implements MergeMethod = type:AddType "(" argumentListing:AddArgumentListing ")";
AddArgumentListing implements ArgumentListing = arguments:(AddArgumentAssignment|| ",")*;
AddArgumentAssignment implements ArgumentAssignment = lhs:AddArgument "=" rhs:ArgumentRhs;
enum AddArgument = "placeholder";
enum AddArgument = XPLACEHOLDERX:"_placeholder";
enum AddType = "add";
SplitMethod implements GroupMethod = type:SplitType "(" argumentListing:SplitArgumentListing ")";
SplitArgumentListing implements ArgumentListing = arguments:(SplitArgumentAssignment || ",")*;
SplitArgumentAssignment implements ArgumentAssignment = lhs:SplitArgument "=" rhs:ArgumentRhs;
enum SplitArgument = "placeholder";
enum SplitArgument = XPLACEHOLDERX:"_placeholder";
enum SplitType = "split";
......@@ -124,7 +124,7 @@ grammar CNNArch extends de.monticore.lang.monticar.Common2 {
ActivationMethod implements LayerMethod = "activation."type:ActivationType "(" argumentListing:ActivationArgumentListing ")";
ActivationArgumentListing implements ArgumentListing = arguments:(ActivationArgumentAssignment || ",")*;
ActivationArgumentAssignment implements ArgumentAssignment = lhs:ActivationArgument "=" rhs:ArgumentRhs;
enum ActivationArgument = "placeholder";
enum ActivationArgument = XPLACEHOLDERX:"_placeholder";
enum ActivationType = "relu"
| "sigmoid"
| "tanh"
......@@ -151,7 +151,7 @@ grammar CNNArch extends de.monticore.lang.monticar.Common2 {
BatchNormMethod implements LayerMethod = type:BatchNormType "(" argumentListing:BatchNormArgumentListing ")";
BatchNormArgumentListing implements ArgumentListing = arguments:(BatchNormArgumentAssignment || ",")*;
BatchNormArgumentAssignment implements ArgumentAssignment = lhs:BatchNormArgument "=" rhs:ArgumentRhs;
enum BatchNormArgument = "placeholder";
enum BatchNormArgument = XPLACEHOLDERX:"_placeholder";
enum BatchNormType = "batchNorm";
LrnMethod implements LayerMethod = type:LrnType "(" argumentListing:LrnArgumentListing ")";
......
......@@ -23,9 +23,22 @@ package de.monticore.lang.monticar.cnnarch._ast;
import de.monticore.lang.monticar.cnnarch._ast.ASTArgumentAssignment;
import java.util.List;
import java.util.Optional;
public interface ASTArgumentListing extends ASTArgumentListingTOP {
List<? extends ASTArgumentAssignment> getArguments();
default Optional<ASTArgumentRhs> getArgument(Enum lhs){
Optional<ASTArgumentRhs> rhs = Optional.empty();
for(ASTArgumentAssignment assignment: getArguments()){
if (assignment.getLhs() == lhs){
rhs = Optional.of(assignment.getRhs());
}
}
return rhs;
}
}
/**
*
* ******************************************************************************
* 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._ast.*;
import de.se_rwth.commons.logging.Log;
import org.jscience.mathematics.number.Rational;
import siunit.monticoresiunit.si._ast.ASTNumber;
import siunit.monticoresiunit.si._ast.ASTUnitNumber;
import javax.measure.unit.Unit;
import java.util.*;
public class ArgumentCheck implements CNNArchASTMethodCoCo {
@Override
public void check(ASTMethod node) {
checkDuplicateArgument(node);
checkPlaceholderArgument(node);
checkArgumentType(node);
}
public void checkDuplicateArgument(ASTMethod node){
Set<Enum> set = new HashSet<>();
for (ASTArgumentAssignment assignment : node.getArgumentListing().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());
}
}
}
public void checkPlaceholderArgument(ASTMethod node){
for(ASTArgumentAssignment assignment:node.getArgumentListing().getArguments()){
if (assignment.getLhs().name().equals("_placeholder")){
Log.error("0x0301B \"_placeholder\" is not an argument",
assignment.get_SourcePositionStart());
}
}
}
public void checkRequiredArguments(ASTMethod node){
}
public void checkArgumentType(ASTMethod node){
final String msg = "0x03424 Incorrect argument type. ";
String msgAddon;
for(ASTArgumentAssignment assignment:node.getArgumentListing().getArguments()){
boolean isValid = false;
ASTArgumentRhs rhs = assignment.getRhs();
switch(assignment.getLhs().name()){
case "UNITS":
isValid = checkInt(rhs);
msgAddon = "Argument 'units' has to be an integer.";
break;
case "NOBIAS":
isValid = checkBool(rhs);
msgAddon = "Argument 'no_bias' has to be a boolean.";
break;
case "FILTERS":
msgAddon = "Argument 'filters' has to be an integer.";
isValid = checkInt(rhs);
break;
case "KERNEL":
isValid = checkInt(rhs) || checkIntTuple(rhs);
msgAddon = "Argument 'kernel' has to be an integer or an integer-tuple.";
break;
case "STRIDE":
isValid = checkInt(rhs) || checkIntTuple(rhs);
msgAddon = "Argument 'stride' has to be an integer or an integer-tuple.";
break;
case "GLOBAL":
isValid = checkBool(rhs);
msgAddon = "Argument 'global' has to be a boolean.";
break;
case "P":
isValid = checkFloat(rhs) && checkBetweenOneAndZero(rhs);
msgAddon = "Argument 'p' has to be a float between 0 and 1.";
break;
case "NSIZE":
isValid = checkInt(rhs);
msgAddon = "Argument 'nsize' has to be an integer.";
break;
case "KNORM":
isValid = checkFloat(rhs);
msgAddon = "Argument 'knorm' has to be a float.";
break;
case "ALPHA":
isValid = checkFloat(rhs);
msgAddon = "Argument 'alpha' has to be a float.";
break;
case "BETA":
isValid = checkFloat(rhs);
msgAddon = "Argument 'beta' has to be a float.";
break;
default:
msgAddon = "Argument " +assignment.getLhs().toString().toLowerCase()+ " is unknwown (see ArgumentCheck.java).";
}
if (!isValid){
Log.error(msg + msgAddon);
}
}
}
private boolean checkFloat(ASTNumber number){
boolean res = false;
if (number.getUnitNumber().isPresent()){
ASTUnitNumber unitNum = number.getUnitNumber().get();
if(!unitNum.getInfSign().isPresent()
&& unitNum.getNumber().isPresent()
&&(!unitNum.getUnit().isPresent() || unitNum.getUnit().get().equals(Unit.ONE))){
res = true;
}
}
return res;
}
private boolean checkInt(ASTNumber number){
return checkFloat(number)
&& number.getUnitNumber().get().getNumber().get().getDivisor().equals(1);
}
private boolean checkBool(ASTArgumentRhs rhs){
return rhs.getBooleanVal().isPresent();
}
private boolean checkFloat(ASTArgumentRhs rhs){
boolean res = false;
if (rhs.getNumber().isPresent()){
ASTNumber number = rhs.getNumber().get();
res = checkFloat(number);
}
return res;
}
private boolean checkInt(ASTArgumentRhs rhs){
boolean res = false;
if (rhs.getNumber().isPresent()){
ASTNumber number = rhs.getNumber().get();
res = checkInt(number);
}
return res;
}
private boolean checkIntTuple(ASTArgumentRhs rhs){
boolean res = false;
if(rhs.getTuple().isPresent()){
ASTTuple tuple = rhs.getTuple().get();
res = true;
for (ASTNumber value : tuple.getValues()){
res = res && checkInt(value);
}
}
return res;
}
private boolean checkBetweenOneAndZero(ASTArgumentRhs rhs){
boolean isValid = checkFloat(rhs);
if (isValid){
boolean isUnitNum = rhs.getNumber().get().getUnitNumber().isPresent();
if (isUnitNum){
Rational rat = rhs.getNumber().get().getUnitNumber().get().getNumber().get();
isValid = (rat.isGreaterThan(Rational.ZERO) && rat.isLessThan(Rational.ONE))
|| rat.equals(Rational.ONE) || rat.equals(Rational.ZERO);
}
}
return isValid;
}
}
......@@ -25,7 +25,7 @@ public class CNNArchCocos {
public static CNNArchCoCoChecker createChecker() {
return new CNNArchCoCoChecker()
.addCoCo(new DuplicateArgumentCheck())
.addCoCo(new ArchitectureCheck());
.addCoCo(new ArgumentCheck())
.addCoCo(new OutputCheck());
}
}
\ No newline at end of file
/**
*
* ******************************************************************************
* 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._ast.*;
import de.se_rwth.commons.logging.Log;
import java.util.Optional;
public class OutputCheck implements CNNArchASTOutputStructureCoCo {
@Override
public void check(ASTOutputStructure node) {
checkFullyConnectedLayer(node);
}
public void checkFullyConnectedLayer(ASTOutputStructure node){
ASTFullyConnectedMethod lastFullyConnectedLayer = null;
for (ASTArchitectureElement element: node.getElements()){
if (element instanceof ASTFullyConnectedMethod){
if (lastFullyConnectedLayer != null){
Log.error("0x06021 The argument 'units' is required if the fullyConnected()-layer is not the last fc-layer"
, lastFullyConnectedLayer.get_SourcePositionStart());
lastFullyConnectedLayer = null;
}
ASTArgumentListing args = ((ASTFullyConnectedMethod) element).getArgumentListing();
Optional<ASTArgumentRhs> arg = args.getArgument(ASTFullyConnectedArgument.UNITS);
if (!arg.isPresent()){
// element has to be the last layer because it does not set the argument 'units'
lastFullyConnectedLayer = (ASTFullyConnectedMethod) element;
}
}
}
if (lastFullyConnectedLayer == null){
Log.error("0x06028 The output block has to contain a fullyConnectedLayer without the argument 'units'. " +
"This is because the number of outputs is variable for all architectures. " +
"Example: output{ fullyConnected() activation.softmax() } -> out"
, node.get_SourcePositionEnd());
}
}
}
......@@ -21,8 +21,12 @@
package de.monticore.lang.monticar.cnnarch.cocos;
import de.monticore.lang.monticar.cnnarch.ParserTest;
import de.monticore.lang.monticar.cnnarch._cocos.ArgumentCheck;
import de.monticore.lang.monticar.cnnarch._cocos.CNNArchCoCoChecker;
import de.monticore.lang.monticar.cnnarch._cocos.CNNArchCocos;
import de.monticore.lang.monticar.cnnarch._cocos.OutputCheck;
import de.monticore.lang.monticar.cnnarch._symboltable.CNNArchLanguage;
import de.se_rwth.commons.logging.Log;
import org.junit.Test;
import java.io.IOException;
......@@ -33,8 +37,13 @@ import static org.junit.Assert.assertTrue;
public class AllCoCoTest extends AbstractCoCoTest {
String baseDir="src/test/resources";
public AllCoCoTest() {
Log.enableFailQuick(false);
}
@Test
public void testCoCosSimulator() throws IOException {
public void testValidCoCos() throws IOException {
checkValid("architectures", "Alexnet");
checkValid("architectures", "Resnet34");
......@@ -49,11 +58,31 @@ public class AllCoCoTest extends AbstractCoCoTest {
checkValid("valid_tests", "SimpleNetworkLinear");
checkValid("valid_tests", "SimpleNetworkRelu");
checkValid("valid_tests", "SimpleNetworkTanh");
checkValid("valid_tests", "GroupTest");
checkValid("valid_tests", "MultiOutputTest");
checkValid("valid_tests", "MultiOutputArrayTest");
checkValid("valid_tests", "VGG16_alternative");
checkValid("valid_tests", "DirectPerception");
checkValid("valid_tests", "SafetyNetwork");
/*testInvalidModel("DuplicateArgument",1,"x03011");
}
@Test
public void testArgumentCoCos() throws IOException{
checkInvalid(new CNNArchCoCoChecker().addCoCo(new ArgumentCheck())
, getAstNode("invalid_tests", "DuplicateArgument")
, new ExpectedErrorInfo(1,"x03011"));
checkInvalid(new CNNArchCoCoChecker().addCoCo(new ArgumentCheck())
, getAstNode("invalid_tests", "BooleanArgumentTypeTest")
, new ExpectedErrorInfo(3,"x03424"));
checkInvalid(new CNNArchCoCoChecker().addCoCo(new ArgumentCheck())
, getAstNode("invalid_tests", "IntegerArgumentTypeTest")
, new ExpectedErrorInfo(7,"x03424"));
/*
testInvalidModel("IntegerArgumentTypeTest",9,"x03012");
testInvalidModel("InvalidActivationBeforeOutput1",1,"x03015");
testInvalidModel("InvalidLayerDimension",1,"x03018");
......@@ -63,17 +92,25 @@ public class AllCoCoTest extends AbstractCoCoTest {
testInvalidModel("MissingLRNArgument",1,"x0301A");
testInvalidModel("MissingPoolingArgument1",1,"x0301A");
testInvalidModel("MissingPoolingArgument2",1,"x0301A");*/
}
@Test
public void testOutputCoCos() throws IOException{
checkInvalid(new CNNArchCoCoChecker().addCoCo(new OutputCheck())
, getAstNode("invalid_tests", "InvalidFixedOutputUnits")
, new ExpectedErrorInfo(1,"x06028"));
}
checkInvalid(new CNNArchCoCoChecker().addCoCo(new OutputCheck())
, getAstNode("invalid_tests", "InvalidFixedOutputUnitsAndMissingArgument")
, new ExpectedErrorInfo(2,"x06021", "x06028"));
private void testModel(String modelName) {
checkValid("",modelName);
}
checkInvalid(new CNNArchCoCoChecker().addCoCo(new OutputCheck())
, getAstNode("invalid_tests", "InvalidOutput1")
, new ExpectedErrorInfo(1,"x06028"));
private void testInvalidModel(String modelName, int numExpectedFindings, String... expectedErrorCodes) {
ExpectedErrorInfo errorInfo = new ExpectedErrorInfo(numExpectedFindings, expectedErrorCodes);
checkInvalid(CNNArchCocos.createChecker(), getAstNode("", modelName), errorInfo);
checkInvalid(new CNNArchCoCoChecker().addCoCo(new OutputCheck())
, getAstNode("invalid_tests", "InvalidOutput2")
, new ExpectedErrorInfo(1,"x06028"));
}
}
\ No newline at end of file
architecture BooleanArgumentTypeTest{
def input in
def output out
in ->
convolution(filters=true, kernel=false)
pooling.max(global=3)
output{
fullyConnected()
activation.softmax()
} -> out
}
\ No newline at end of file
architecture FixedOutputUnitsAndMissingArgument{
architecture InvalidFixedOutputUnitsAndMissingArgument{
def input in
def output out
......
architecture InvalidGroupTest{
def input in
def output out
in ->
group 1..3{
fullyConnected(units=128)
}
group 5{
fullyConnected(units=128)
}
merge concatenate()
output{
fullyConnected()
activation.softmax()
} -> out
}
\ No newline at end of file
architecture InvalidLayerDimension1{
def input in
def output out
in ->
fullyConnected(units=128)
convolution(filters=4, kernel=(2,2))
output {
fullyConnected()
activation.softmax()
} -> out
}
\ No newline at end of file
architecture InvalidLayerDimension2{
def input in
def output out
in ->
group 1..2{
fullyConnected(units=64)
}
group 3{
fullyConnected(units=8)
activation.relu()
}
merge add()
output{
fullyConnected()
activation.softmax()
} -> out
}
\ No newline at end of file
architecture InvalidLayerDimension3{
def input in
def output out
in ->
group 1..2{
convolution(filters=16, kernel=(3,3))
activation.relu()
}
group 3{
convolution(filters=8, kernel=(3,3), stride=2)
activation.relu()
}
merge concatenate()
output{
fullyConnected()
activation.softmax()
} -> out
}
\ No newline at end of file
......@@ -4,7 +4,7 @@ architecture InvalidRepeatInput{
repeat 3{
in ->
fullyConnected(units=64
fullyConnected(units=64)
}
output{
fullyConnected()
......
architecture InvalidRepeatOutput{
def input in
def output out
in ->
repeat 3{
fullyConnected(units=64)
output{
fullyConnected()
activation.softmax()
} -> out