Commit 315414cc authored by Nils Kaminski's avatar Nils Kaminski
Browse files

Integrate MathOpt generation to GeneratorCPP

parent b698b47b
...@@ -8,8 +8,11 @@ import de.monticore.lang.monticar.generator.cmake.CMakeConfig; ...@@ -8,8 +8,11 @@ import de.monticore.lang.monticar.generator.cmake.CMakeConfig;
import de.monticore.lang.monticar.generator.cmake.CMakeFindModule; import de.monticore.lang.monticar.generator.cmake.CMakeFindModule;
import de.monticore.lang.monticar.generator.cpp.Dynamics.DynamicHelper; import de.monticore.lang.monticar.generator.cpp.Dynamics.DynamicHelper;
import de.monticore.lang.monticar.generator.cpp.Dynamics.EventPortValueCheck; import de.monticore.lang.monticar.generator.cpp.Dynamics.EventPortValueCheck;
import de.monticore.lang.monticar.generator.cpp.converter.ExecuteMethodGenerator;
import de.monticore.lang.monticar.generator.cpp.converter.MathConverter; import de.monticore.lang.monticar.generator.cpp.converter.MathConverter;
import de.monticore.lang.monticar.generator.cpp.converter.OptimizationSymbolHandler;
import de.monticore.lang.monticar.generator.cpp.converter.TypeConverter; import de.monticore.lang.monticar.generator.cpp.converter.TypeConverter;
import de.monticore.lang.monticar.generator.cpp.mathopt.MathOptSolverConfig;
import de.monticore.lang.monticar.generator.cpp.template.AllTemplates; import de.monticore.lang.monticar.generator.cpp.template.AllTemplates;
import de.monticore.lang.monticar.generator.cpp.viewmodel.AutopilotAdapterViewModel; import de.monticore.lang.monticar.generator.cpp.viewmodel.AutopilotAdapterViewModel;
import de.monticore.lang.monticar.generator.cpp.viewmodel.ServerWrapperViewModel; import de.monticore.lang.monticar.generator.cpp.viewmodel.ServerWrapperViewModel;
...@@ -53,11 +56,20 @@ public class GeneratorCPP implements Generator { ...@@ -53,11 +56,20 @@ public class GeneratorCPP implements Generator {
private boolean generateCMake = false; private boolean generateCMake = false;
private CMakeConfig cMakeConfig; private CMakeConfig cMakeConfig;
//MathOpt
private MathOptSolverConfig mathOptSolverConfig = new MathOptSolverConfig();
private OptimizationSymbolHandler mathOptExecuteMethodGenerator = new OptimizationSymbolHandler();
private MathOptFunctionFixer mathOptFunctionFixer = new MathOptFunctionFixer();
public GeneratorCPP() { public GeneratorCPP() {
this.mathCommandRegister = new MathCommandRegisterCPP(); this.mathCommandRegister = new MathCommandRegisterCPP();
useOctaveBackend(); setGenerateCMake(true);
useArmadilloBackend();
TypeConverter.clearTypeSymbols(); TypeConverter.clearTypeSymbols();
currentInstance = this; currentInstance = this;
mathOptExecuteMethodGenerator.setSuccessor(ExecuteMethodGenerator.getInstance());
mathOptFunctionFixer.setSuccessor(MathFunctionFixer.getInstance());
} }
protected void setupCMake() { protected void setupCMake() {
...@@ -462,4 +474,13 @@ public class GeneratorCPP implements Generator { ...@@ -462,4 +474,13 @@ public class GeneratorCPP implements Generator {
return cMakeConfig; return cMakeConfig;
} }
public MathOptSolverConfig getMathOptSolverConfig() {
return mathOptSolverConfig;
}
public OptimizationSymbolHandler getMathOptExecuteMethodGenerator() {
return mathOptExecuteMethodGenerator;
}
} }
package de.monticore.lang.monticar.generator.cpp.mathopt; package de.monticore.lang.monticar.generator.cpp;
import de.monticore.lang.math._symboltable.expression.MathExpressionSymbol; import de.monticore.lang.math._symboltable.expression.MathExpressionSymbol;
import de.monticore.lang.mathopt._symboltable.MathOptimizationStatementSymbol; import de.monticore.lang.mathopt._symboltable.MathOptimizationStatementSymbol;
......
package de.monticore.lang.monticar.generator.cpp.converter;
import de.monticore.lang.math._symboltable.expression.MathValueSymbol;
import de.monticore.lang.math._symboltable.expression.MathValueType;
import de.monticore.lang.mathopt._symboltable.MathOptimizationStatementSymbol;
import de.monticore.lang.monticar.generator.BluePrint;
import de.monticore.lang.monticar.generator.FileContent;
import de.monticore.lang.monticar.generator.cpp.BluePrintCPP;
import de.monticore.lang.monticar.generator.cpp.GeneratorCPP;
import de.monticore.lang.monticar.generator.cpp.mathopt.MathOptSolverConfig;
import de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem.OptimizationProblemClassification;
import de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem.Problem;
import de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.solver.SolverGenerator;
import de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.solver.factory.SolverGeneratorFactory;
import de.monticore.lang.monticar.types2._ast.ASTElementType;
import de.se_rwth.commons.logging.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Manages the conversion from a MathOptimizationSymbol to executable C++ code.
*
* @author Christoph Richter
* @since 09th March 2018
*/
public class OptimizationSolverConverter {
// singleton implementation
private static OptimizationSolverConverter ourInstance = new OptimizationSolverConverter();
public static OptimizationSolverConverter getInstance() {
return ourInstance;
}
private OptimizationSolverConverter() {
}
// methods
/**
* Generates code solving command including auxiliary code files.
*
* @param symbol MathExpressionSymbol from which code should be generated
* @param includeStrings Additional include strings which are necessary to execute the code
* @return Code line which will execute the optimization solver on the optimization problem
*/
public static String getOptimizationExpressionCode(MathOptimizationStatementSymbol symbol, List<String> includeStrings, BluePrintCPP bluePrint) {
// first step: decide for correct solver for problem class
OptimizationProblemClassification problemClassification = new OptimizationProblemClassification(symbol);
Problem problemType = problemClassification.getProblemType();
// second step: decide for implementation
GeneratorCPP gen = getInstance().getGenerator(bluePrint);
SolverGenerator solverGenerator = SolverGeneratorFactory.getInstance().createPreferredSolverForProblem(problemType,
gen.getMathOptSolverConfig().getPreferedSolver(), gen.getMathOptSolverConfig().isForceUsePreferredSolver());
// third step: generate code from solver generator
// declare needed variables
String result = getOutputVariableDeclarations(symbol, problemType, bluePrint);
// call solverGenerator
ArrayList<FileContent> auxiliaryFiles = new ArrayList<FileContent>();
result += solverGenerator.generateSolverInstruction(problemType, auxiliaryFiles, bluePrint);
// also generate auxiliaryFiles
try {
// getInstance().getGenerator(bluePrint).saveFilesToDisk(auxiliaryFiles);
if(bluePrint.getGenerator() instanceof GeneratorCPP){
((GeneratorCPP) bluePrint.getGenerator()).saveFilesToDisk(auxiliaryFiles);
}
} catch (IOException e) {
Log.error(e.toString());
}
// do not forget to add include strings
bluePrint.additionalIncludeStrings.addAll(solverGenerator.getNecessaryIncludes());
return result;
}
private static boolean isOptimizationVariableAlreadyDefined(MathValueSymbol optimizationVariable, BluePrintCPP bluePrint) {
boolean defined = false;
if (optimizationVariable.getType() == null) {
defined = true;
} else if (bluePrint.getMathInformationRegister().getMathValueSymbol(optimizationVariable.getName()) != null) {
defined = true;
}
return defined;
}
private static String getOutputVariableDeclarations(MathOptimizationStatementSymbol symbol, Problem problemType, BluePrintCPP bluePrint) {
String result = "";
if (!isOptimizationVariableAlreadyDefined(symbol.getOptimizationVariable(), bluePrint))
result += ExecuteMethodGenerator.generateExecuteCode(symbol.getOptimizationVariable(), new ArrayList<>());
if (symbol.hasReturnValue()) {
MathValueSymbol expr = symbol.getObjectiveValue();
MathValueSymbol decl = new MathValueSymbol(expr.getName());
decl.setType(expr.getType());
result += ExecuteMethodGenerator.generateExecuteCode(decl, new ArrayList<>());
} else {
problemType.setObjectiveValueVariable("objectiveValue" + problemType.getId());
MathValueSymbol decl = new MathValueSymbol(problemType.getObjectiveValueVariable());
MathValueType type = new MathValueType();
ASTElementType astType = new ASTElementType();
astType.setName("Q");
type.setType(astType);
decl.setType(type);
result += ExecuteMethodGenerator.generateExecuteCode(decl, new ArrayList<>());
}
return result;
}
protected GeneratorCPP getGenerator(BluePrint bluePrint) {
GeneratorCPP g = null;
if (bluePrint.getGenerator() instanceof GeneratorCPP)
g = (GeneratorCPP) bluePrint.getGenerator();
return g;
}
}
package de.monticore.lang.monticar.generator.cpp.converter;
import de.monticore.lang.math._symboltable.expression.MathExpressionSymbol;
import de.monticore.lang.math._symboltable.matrix.MathMatrixAccessOperatorSymbol;
import de.monticore.lang.math._symboltable.matrix.MathMatrixAccessSymbol;
import de.monticore.lang.math._symboltable.matrix.MathMatrixArithmeticValueSymbol;
import de.monticore.lang.mathopt._symboltable.MathOptimizationStatementSymbol;
import de.monticore.lang.monticar.generator.cpp.converter.BaseExecuteMethodGeneratorHandler;
import de.monticore.lang.monticar.generator.cpp.converter.ComponentConverter;
import de.monticore.lang.monticar.generator.cpp.converter.ExecuteMethodGenerator;
import de.monticore.lang.monticar.generator.cpp.converter.OptimizationSolverConverter;
import de.se_rwth.commons.logging.Log;
import java.util.List;
/**
* Handles code generation for optimization symbols
*
* @author Christoph Richter
*/
public class OptimizationSymbolHandler extends BaseExecuteMethodGeneratorHandler {
// fields
private String currentOptimizationVariableName = "x";
private String currentOptimizationVariableMatrixType = "ADMat";
@Override
protected boolean canHandleSymbol(MathExpressionSymbol symbol) {
boolean canHandle = false;
if ((symbol instanceof MathOptimizationStatementSymbol) || (symbol instanceof MathMatrixArithmeticValueSymbol))
canHandle = true;
return canHandle;
}
@Override
protected String doGenerateExecuteCode(MathExpressionSymbol symbol, List<String> includeStrings) {
String result = "";
if (symbol instanceof MathOptimizationStatementSymbol) {
result = OptimizationSolverConverter.getOptimizationExpressionCode((MathOptimizationStatementSymbol) symbol, includeStrings, ComponentConverter.currentBluePrint);
} else if (symbol instanceof MathMatrixArithmeticValueSymbol) {
MathMatrixArithmeticValueSymbol matVal = (MathMatrixArithmeticValueSymbol) symbol;
if (containsOptimizationVariable(matVal))
result = generateCodeForMatrixDependentOnOptVar(matVal, includeStrings);// do something
else
result = ((BaseExecuteMethodGeneratorHandler) getSuccessor()).doGenerateExecuteCode(symbol, includeStrings);
} else {
Log.error(String.format("%s: Case not handled: %s", getRole(), symbol.getTextualRepresentation()), symbol.getSourcePosition());
}
return result;
}
private String generateCodeForMatrixDependentOnOptVar(MathMatrixArithmeticValueSymbol matVal, List<String> includeStrings) {
String result;
if ((matVal.getVectors().size() == 1) || (matVal.getVectors().get(0).getMathMatrixAccessSymbols().size() == 1))
result = generateCodeForVecDependentOnOptVar(matVal, includeStrings);
else {
StringBuilder matrixInitSB = new StringBuilder();
for (MathMatrixAccessOperatorSymbol vec : matVal.getVectors()) {
matrixInitSB.append("{");
for (MathMatrixAccessSymbol elem : vec.getMathMatrixAccessSymbols()) {
matrixInitSB.append(ExecuteMethodGenerator.generateExecuteCode(elem, includeStrings));
matrixInitSB.append(",");
}
matrixInitSB.deleteCharAt(matrixInitSB.length() - 1);
matrixInitSB.append("},");
}
matrixInitSB.deleteCharAt(matrixInitSB.length() - 1);
result = String.format("%s({%s})", getCurrentOptimizationVariableMatrixType(), matrixInitSB.toString());
}
return result;
}
private String generateCodeForVecDependentOnOptVar(MathMatrixArithmeticValueSymbol matVal, List<String> includeStrings) {
StringBuilder matrixInitSB = new StringBuilder();
for (MathMatrixAccessOperatorSymbol vec : matVal.getVectors()) {
for (MathMatrixAccessSymbol elem : vec.getMathMatrixAccessSymbols()) {
matrixInitSB.append(ExecuteMethodGenerator.generateExecuteCode(elem, includeStrings));
matrixInitSB.append(",");
}
}
matrixInitSB.deleteCharAt(matrixInitSB.length() - 1);
String result = String.format("%s({%s})", getCurrentOptimizationVariableMatrixType(), matrixInitSB.toString());
if ((matVal.getVectors().size() > 1) && (matVal.getVectors().get(0).getMathMatrixAccessSymbols().size() == 1))
result = String.format("(%s.t())", result); // transpose to colvec
return result;
}
private boolean containsOptimizationVariable(MathMatrixArithmeticValueSymbol matVal) {
boolean result = false;
for (MathMatrixAccessOperatorSymbol vec : matVal.getVectors()) {
for (MathMatrixAccessSymbol elem : vec.getMathMatrixAccessSymbols()) {
String textRep = elem.getTextualRepresentation();
if (textRep.contentEquals(getCurrentOptimizationVariableName()) || textRep.contains(getCurrentOptimizationVariableName() + "("))
return true;
}
}
return result;
}
public String getCurrentOptimizationVariableName() {
return currentOptimizationVariableName;
}
public void setCurrentOptimizationVariableName(String currentOptimizationVariableName) {
this.currentOptimizationVariableName = currentOptimizationVariableName;
}
public String getCurrentOptimizationVariableMatrixType() {
return currentOptimizationVariableMatrixType;
}
public void setCurrentOptimizationVariableMatrixType(String currentOptimizationVariableMatrixType) {
this.currentOptimizationVariableMatrixType = currentOptimizationVariableMatrixType;
}
@Override
public String getRole() {
return "OptimizationSymbolHandler";
}
}
package de.monticore.lang.monticar.generator.cpp.mathopt;
import de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.solver.Solver;
import de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.solver.SolverOptions;
public class MathOptSolverConfig {
private Solver preferedSolver = Solver.none;
private SolverOptions solverOptions = new SolverOptions();
private boolean forceUsePreferredSolver = false;
public Solver getPreferedSolver() {
return preferedSolver;
}
public void setPreferedSolver(Solver preferedSolver) {
this.preferedSolver = preferedSolver;
}
public SolverOptions getSolverOptions() {
return solverOptions;
}
public void setSolverOptions(SolverOptions solverOptions) {
this.solverOptions = solverOptions;
}
public boolean isForceUsePreferredSolver() {
return forceUsePreferredSolver;
}
public void setForceUsePreferredSolver(boolean forceUsePreferredSolver) {
this.forceUsePreferredSolver = forceUsePreferredSolver;
}
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
/**
* Represents a convex (possibly non linear) optimization problem
*
* @author Christoph Richter
*/
public class ConvexProblem extends QPProblem {
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
/**
* Discontinious NLP problem
*/
public class DNLPProblem extends Problem {
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
/**
* Represents a linear optimization problem
*
* @author Christoph Richter
*/
public class LPProblem extends Problem {
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
/**
* Mixed integer non linear programming
*/
public class MINLPProblem extends MIQPProblem {
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
public class MIPProblem extends Problem {
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
/**
* Mixed Integer Quadratic (constraint) Programming
*/
public class MIQPProblem extends MIPProblem{
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
/**
* Represents a continious non linear optimization problem of the following form:
* min f(x)
* x in R^n
* s.t. g_L <= g(x) <= g_U
* . x_L <= x <= x_U
* where f and g are nonlinear functions, x in R^n, f(x) in R, g(x) in R^m with
* n dimension of x
* m number of constraints
* A. Wächter and L. T. Biegler, ​On the Implementation of a Primal-Dual Interior Point Filter Line Search Algorithm for Large-Scale Nonlinear Programming, Mathematical Programming 106(1), pp. 25-57, 2006
*
* @author Christoph Richter
*/
public class NLPProblem extends ConvexProblem {
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
import de.monticore.lang.math._symboltable.expression.MathConditionalExpressionSymbol;
import de.monticore.lang.math._symboltable.expression.MathConditionalExpressionsSymbol;
import de.monticore.lang.math._symboltable.expression.MathExpressionSymbol;
import de.monticore.lang.math._symboltable.expression.MathValueType;
import de.monticore.lang.mathopt._symboltable.MathOptimizationStatementSymbol;
import de.se_rwth.commons.logging.Log;
import java.util.Arrays;
import java.util.List;
/**
* Analyses a MathOptimizationStatementSymbol for its problem class.
*
* @author Christoph Richter
*/
public class OptimizationProblemClassification {
// fields
private MathOptimizationStatementSymbol symbol;
private Problem problemType;
// constructor
public OptimizationProblemClassification(MathOptimizationStatementSymbol symbol) {
this.symbol = symbol;
this.problemType = classifySymbol(symbol);
}
/**
* Classifies MathOptimizationExpression to a optimization problem class
*
* @param symbol MathOptimizationStatementSymbol which should be classified
* @return Optimization problem type
*/
public static Problem classifySymbol(MathOptimizationStatementSymbol symbol) {
Problem result = null;
if (checkIfLP(symbol)) {
if (isMixedInteger(symbol))
result = SymbolToProblemConverter.getInstance().getMIPFromSymbol(symbol);
else
result = SymbolToProblemConverter.getInstance().getLPFromSymbol(symbol);
} else if (checkIfQP(symbol)) {
if (isMixedInteger(symbol))
result = SymbolToProblemConverter.getInstance().getMIQPFromSymbol(symbol);
else
result = SymbolToProblemConverter.getInstance().getQPFromSymbol(symbol);
} else if (checkIfNLP(symbol)) {
result = SymbolToProblemConverter.getInstance().getNLPFromSymbol(symbol);
} else if (checkIfDNLP(symbol)) {
result = SymbolToProblemConverter.getInstance().getDNLPFromSymbol(symbol);
} else {
Log.error(String.format("Can not classify %s: %s", symbol.getClass().toString(), symbol.getFullName()));
}
return result;
}
private static boolean checkIfQP(MathOptimizationStatementSymbol symbol) {
boolean result = false;
String optvar = symbol.getOptimizationVariable().getName();
String text = symbol.getTextualRepresentation();
if (!containsNonLinearFunctions(text)) {
}
return result;
}
private static boolean containsNonLinearFunctions(String text) {
List<String> nonLinFunctions = Arrays.asList("sin", "cos", "tan");
for (String fun : nonLinFunctions) {
if (text.contains(fun))
return true;
}
return false;
}
private static boolean checkIfLP(MathOptimizationStatementSymbol symbol) {
return false;
}
private static boolean checkIfNLP(MathOptimizationStatementSymbol symbol) {
// if it is not dnlp it must be nlp
return !checkIfDNLP(symbol);
}
private static boolean checkIfDNLP(MathOptimizationStatementSymbol symbol) {
boolean result = false;
// if objective function contains branches like if it must be discontius
MathExpressionSymbol objFunc = symbol.getObjectiveExpression();
if (objFunc instanceof MathConditionalExpressionsSymbol || objFunc instanceof MathConditionalExpressionSymbol)
result = true;
return result;
}
private static boolean isMixedInteger(MathOptimizationStatementSymbol symbol) {
boolean result = false;
MathValueType type = ProblemAssignmentHandler.getInstance().getVariableWithTypeInformations(symbol.getOptimizationVariable()).getType();
if (type.getType().isWholeNumber())
result = true;
return result;
}
// methods
public MathOptimizationStatementSymbol getSymbol() {
return symbol;
}
public Problem getProblemType() {
return problemType;
}
}
package de.monticore.lang.monticar.generator.cpp.mathopt.optimizationSolver.problem;
import de.monticore.lang.mathopt._symboltable.MathOptimizationType;
import java.util.Vector;
/**