Commit 0dbde05f authored by Nico Mack's avatar Nico Mack

Extended cps engine to handle octave, python and javascript engines

parent 3614648c
......@@ -50,9 +50,25 @@
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>dk.ange</groupId>
<groupId>maven2.dk.ange</groupId>
<artifactId>javaoctave</artifactId>
<version>0.6.4</version>
</dependency>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.1b3</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>Kenai</id>
<name>Kenai</name>
<url>https://svn.kenai.com/svn/javaoctave~maven-repository</url>
</repository>
</repositories>
</project>
\ No newline at end of file
......@@ -22,15 +22,16 @@ package lu.list.itis.dkd.tui.cps.system;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.cps.system.executor.Executor;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import com.google.common.base.Preconditions;
import java.util.LinkedHashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
/**
* Class modelling a mathematical equation that is evaluated by an engine.
......@@ -44,9 +45,12 @@ public class Equation {
private Mapping mapping;
private Mapping lockedMapping;
@Nullable
private ScriptEngine scriptEngine;
// private ScriptEngine scriptEngine;
private Executor scriptExecutor;
private String script;
private static Logger LOGGER = Logger.getLogger(Equation.class.getSimpleName());
/**
* Constructor
*
......@@ -77,11 +81,49 @@ public class Equation {
* mapping.
*/
public void evaluate() {
Preconditions.checkState(scriptEngine != null);
// Preconditions.checkState(scriptEngine != null);
// try {
// mapping.getDependentVariable().setValue(scriptEngine.eval(replaceVariables()));
// } catch (ScriptException e) {
// Logger.getLogger(Equation.class.getSimpleName()).log(Level.SEVERE, "Exception while
// executing script!", e); //$NON-NLS-1$
// }
LinkedHashSet<Variable> dependentVariables;
String evaluationErrors;
Preconditions.checkState(scriptExecutor != null);
try {
mapping.getDependentVariable().setValue(scriptEngine.eval(replaceVariables()));
} catch (ScriptException e) {
Logger.getLogger(Equation.class.getSimpleName()).log(Level.SEVERE, "Exception while executing script!", e); //$NON-NLS-1$
scriptExecutor.resetExecutionErrors();
for (Variable variable : lockedMapping.getIndependentVariables()) {
scriptExecutor.set(variable);
}
scriptExecutor.eval(this.script);
} catch (Exception exception) {
LOGGER.log(Level.SEVERE, "Error while evaluating script " + this.script, exception); //$NON-NLS-1$
}
dependentVariables = mapping.getDependentVariables();
for (Variable variable : dependentVariables) {
try {
variable = scriptExecutor.get(variable);
} catch (Exception exception) {
LOGGER.log(Level.SEVERE, "Error while retrieving variable " + variable.getName(), exception); //$NON-NLS-1$
}
}
evaluationErrors = scriptExecutor.getExecutionErrors();
if (evaluationErrors.length() > 0) {
LOGGER.log(Level.WARNING, "Error while evaluating equation :" + evaluationErrors); //$NON-NLS-1$
LOGGER.log(Level.WARNING, "Script =" + this.script); //$NON-NLS-1$
LinkedHashSet<Variable> independentVariables = mapping.getIndependentVariables();
for (Variable variable : independentVariables) {
LOGGER.log(Level.INFO, variable.getName() + " = " + variable.getValue()); //$NON-NLS-1$
}
}
}
......@@ -90,17 +132,20 @@ public class Equation {
*
* @return The formula with variables replaced by their values, ready for evaluation.
*/
private String replaceVariables() {
String result = new String(script);
for (Variable variable : lockedMapping.getIndependentVariables()) {
result = result.replaceAll(variable.getName(), variable.getValue().toString());
}
result = result.replaceAll(lockedMapping.getDependentVariable().getName(), lockedMapping.getDependentVariable().getValue().toString());
assert result != null;
return result;
}
// private String replaceVariables() {
// String result = new String(script);
//
// for (Variable variable : lockedMapping.getIndependentVariables()) {
// result = result.replaceAll(variable.getName(), variable.getValue().toString());
// }
//
// for (Variable variable : lockedMapping.getDependentVariables()) {
// result = result.replaceAll(variable.getName(), variable.getValue().toString());
// }
//
// assert result != null;
// return result;
// }
/**
* Method for setting the script engine to use.
......@@ -108,8 +153,17 @@ public class Equation {
* @param engine
* The {@link ScriptEngine} instance to use for evaluating the scripted formulas.
*/
public void setScriptEngine(ScriptEngine engine) {
scriptEngine = engine;
// public void setScriptEngine(ScriptEngine engine) {
// scriptEngine = engine;
// }
/**
* Method for setting the script engine to use.
*
* @param engine
* The {@link ScriptEngine} instance to use for evaluating the scripted formulas.
*/
public void setExecutor(Executor executor) {
this.scriptExecutor = executor;
}
/**
......
package lu.list.itis.dkd.tui.cps.system;
public class Import
{
private String function;
private String from;
public Import (String function, String from)
{
this.function = function;
this.from = from;
}
public String getFunction()
{
return function;
}
public void setFunction(String function)
{
this.function = function;
}
public String getFrom()
{
return from;
}
public void setFrom(String from)
{
this.from = from;
}
}
......@@ -28,9 +28,6 @@ import com.google.common.base.Preconditions;
import java.util.concurrent.ConcurrentHashMap;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
/**
* Class implementing a system of linear equations. The class holds a number of equations and
* defines the engine that evaluates the equations. The class may be driven by a clock react to
......@@ -43,7 +40,7 @@ import javax.script.ScriptEngineManager;
@NonNullByDefault
public class LinearEquationSystem extends System {
private ConcurrentHashMap<Mapping, Equation> equations;
private ScriptEngine scriptEngine;
// private ScriptEngine scriptEngine;
/**
* Constructor issuing a super call and initialising the map of equations.
......@@ -57,9 +54,9 @@ public class LinearEquationSystem extends System {
public LinearEquationSystem(boolean lockToNesting) {
super();
equations = new ConcurrentHashMap<>();
scriptEngine = new ScriptEngineManager().getEngineByName("js"); //$NON-NLS-1$
// scriptEngine = new ScriptEngineManager().getEngineByName("js"); //$NON-NLS-1$
this.lockSystemForNesting = lockToNesting;
assert scriptEngine != null : "Script engine cannot be null;"; //$NON-NLS-1$
// assert scriptEngine != null : "Script engine cannot be null;"; //$NON-NLS-1$
}
/**
......@@ -70,7 +67,7 @@ public class LinearEquationSystem extends System {
* @return An instance of the equation map for chain-calling.
*/
public ConcurrentHashMap<Mapping, Equation> addEquation(Equation equation) {
equation.setScriptEngine(scriptEngine);
// equation.setScriptEngine(scriptEngine);
equations.put(equation.getMapping(), equation);
return equations;
}
......
......@@ -24,7 +24,6 @@ import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
......@@ -37,8 +36,8 @@ import java.util.LinkedHashSet;
*/
@NonNullByDefault
public class Mapping {
private Collection<Variable> independentVariables;
private Variable dependentVariable;
private LinkedHashSet<Variable> independentVariables;
private LinkedHashSet<Variable> dependentVariables;
/**
* Constructor initialising the mapping.
......@@ -48,11 +47,11 @@ public class Mapping {
* @param dependentVariable
* The dependent variable of the mapping.
*/
public Mapping(Collection<Variable> independentVariables, Variable dependentVariable) {
public Mapping(LinkedHashSet<Variable> independentVariables, LinkedHashSet<Variable> dependentVariables) {
// Preconditions.checkArgument(!independentVariables.isEmpty(), "The set of input variables
// cannot be empty."); //$NON-NLS-1$
this.independentVariables = independentVariables;
this.dependentVariable = dependentVariable;
this.dependentVariables = dependentVariables;
}
/**
......@@ -60,17 +59,17 @@ public class Mapping {
*
* @return The value of independentVariables.
*/
public Collection<Variable> getIndependentVariables() {
public LinkedHashSet<Variable> getIndependentVariables() {
return independentVariables;
}
/**
* Simple getter method for dependentVariable.
* Simple getter method for dependentVariables.
*
* @return The value of dependentVariable.
* @return The value of dependentVariables.
*/
public Variable getDependentVariable() {
return dependentVariable;
public LinkedHashSet<Variable> getDependentVariables() {
return dependentVariables;
}
/** {@inheritDoc} */
......@@ -84,7 +83,14 @@ public class Mapping {
separator = ", "; //$NON-NLS-1$
}
renderer.append(" => ").append(dependentVariable.getName()); //$NON-NLS-1$
renderer.append(" => "); //$NON-NLS-1$
separator = ""; //$NON-NLS-1$
for (Variable variable : dependentVariables) {
renderer.append(separator).append(variable.getName());
separator = ", "; //$NON-NLS-1$
}
return renderer.toString();
}
......@@ -143,12 +149,16 @@ public class Mapping {
* @return A deep copy of the {@link Mapping}.
*/
public Mapping lock() {
LinkedHashSet<Variable> clones = new LinkedHashSet<>();
LinkedHashSet<Variable> clonedIndependentVariables = new LinkedHashSet<>();
for (Variable variable : independentVariables) {
clones.add(variable.clone());
clonedIndependentVariables.add(variable.clone());
}
LinkedHashSet<Variable> clonedDependentVariables = new LinkedHashSet<>();
for (Variable variable : dependentVariables) {
clonedDependentVariables.add(variable.clone());
}
return new Mapping(clones, dependentVariable.clone());
return new Mapping(clonedIndependentVariables, clonedDependentVariables);
}
// /**
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2016.
*
* This file is part of TULIP.
*
* TULIP is licensed under a dual-licensing scheme. For non-commercial purposes, the LGPL version 3,
* as stated below, is applicable. For all commercial purposes TULIP is licensed under a LIST
* proprietary license. Please contact LIST at tto@list.lu to obtain a commercial license.
*
* For all non-commercial purposes, TULIP 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, version 3 of the License.
*
* TULIP 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 TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.cps.system;
import lu.list.itis.dkd.dbc.annotation.NonNullByDefault;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import com.google.common.base.Preconditions;
import java.io.CharArrayWriter;
import java.util.LinkedHashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import dk.ange.octave.OctaveEngine;
import dk.ange.octave.type.Octave;
import dk.ange.octave.type.OctaveDouble;
/**
* @author Nico Mack [nico.mack@list.lu]
* @since 1.1
* @version 1.3.0
*/
@NonNullByDefault
public class OctaveEquation extends Equation {
private OctaveMapping mapping, lockedMapping;
@Nullable
private String script;
private OctaveEngine scriptEngine;
private CharArrayWriter engineErrors;
private static final Logger logger = Logger.getLogger(OctaveEquation.class.getSimpleName());
/**
* Constructor
*
* @param mapping
* The mapping of variables to take into account for the equation.
* @param script
* The mathematical representation of the equation in string form. Note that the names of
* the variables in the script must coincide with the names of the variables in the
* mapping.
*/
public OctaveEquation(OctaveMapping mapping, String script) {
super(mapping, script);
this.mapping = mapping;
this.lockedMapping = mapping;
this.script = script;
this.engineErrors = new CharArrayWriter();
}
/**
* Method called to evaluate the script of the equation with the variables provided by the
* mapping.
*/
@Override
public synchronized void evaluate() {
LinkedHashSet<Variable> dependentVariables;
String evaluationErrors;
Preconditions.checkState(scriptEngine != null);
try {
engineErrors.reset();
scriptEngine.setErrorWriter(engineErrors);
initParameters();
scriptEngine.eval(this.script);
} catch (Exception exception) {
logger.log(Level.SEVERE, "Error while evaluating script " + this.script, exception); //$NON-NLS-1$
}
dependentVariables = mapping.getDependentVariables();
for (Variable variable : dependentVariables) {
try {
OctaveDouble result = scriptEngine.get(OctaveDouble.class, variable.getName());
variable.setValue(result.get(1));
} catch (Exception exception) {
logger.log(Level.SEVERE, "Error while retrieving variable " + variable.getName(), //$NON-NLS-1$
exception);
}
}
evaluationErrors = engineErrors.toString();
if (evaluationErrors.length() > 0) {
logger.log(Level.WARNING, "Error while evaluating equation :" + evaluationErrors); //$NON-NLS-1$
}
}
/**
* Method used to replace variables with their actual values in the formula.
*/
private void initParameters() {
for (Variable variable : lockedMapping.getIndependentVariables()) {
Double numericValue = Double.valueOf(variable.getValue().toString());
if (isNegative(numericValue)) {
numericValue *= -1d;
}
scriptEngine.put(variable.getName(), Octave.scalar(numericValue));
}
}
private static boolean isNegative(double _double) {
return Double.doubleToRawLongBits(_double) < 0;
}
/**
* Method for setting the script engine to use.
*
* @param engine
* The {@link ScriptEngine} instance to use for evaluating the scripted formulas.
*/
public void setScriptEngine(OctaveEngine engine) {
scriptEngine = engine;
}
}
\ No newline at end of file
/**
* Copyright Luxembourg Institute of Science and Technology, 2016.
*
* This file is part of TULIP.
*
* TULIP is licensed under a dual-licensing scheme. For non-commercial purposes, the LGPL version 3,
* as stated below, is applicable. For all commercial purposes TULIP is licensed under a LIST
* proprietary license. Please contact LIST at tto@list.lu to obtain a commercial license.
*
* For all non-commercial purposes, TULIP 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, version 3 of the License.
*
* TULIP 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 TULIP. If
* not, see <http://www.gnu.org/licenses/lgpl-3.0.html>.
*/
package lu.list.itis.dkd.tui.cps.system;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
*
* @author Nico Mack [nico.mack@list.lu]
* @since 1.1
* @version 1.3.1
*/
public class OctaveMapping extends Mapping {
private LinkedHashSet<Variable> dependentVariables;
/**
*
* @param independentVariables
* @param dependentVariables
*/
public OctaveMapping(LinkedHashSet<Variable> independentVariables, LinkedHashSet<Variable> dependentVariables) {
super(independentVariables, null);
Preconditions.checkArgument(!dependentVariables.isEmpty(), "The set of output variables cannot be empty."); //$NON-NLS-1$
this.dependentVariables = dependentVariables;
}
/**
* Simple getter method for dependentVariable.
*
* @return The value of dependentVariable.
*/
public LinkedHashSet<Variable> getDependentVariables() {
return dependentVariables;
}
@Override
public String toString() {
String separator = ""; //$NON-NLS-1$
StringBuilder renderer = new StringBuilder();
Collection<Variable> independentVariables = super.getIndependentVariables();
for (Variable variable : independentVariables) {
renderer.append(separator).append(variable.getName());
separator = ", "; //$NON-NLS-1$
}
renderer.append(" => "); //$NON-NLS-1$
separator = ""; //$NON-NLS-1$
for (Variable variable : dependentVariables) {
renderer.append(separator).append(variable.getName());
separator = ", "; //$NON-NLS-1$
}
return renderer.toString();
}
/**
* Method for locking the mapping; cloning all variables such that they may not be influenced by
* the evaluation of the equation.
*
* @return A deep copy of the {@link Mapping}.
*/
@Override
public synchronized Mapping lock() {
Collection<Variable> independentVariables = super.getIndependentVariables();
LinkedHashSet<Variable> clonedIndependentVariables = new LinkedHashSet<>();
for (Variable variable : independentVariables) {
clonedIndependentVariables.add(variable.clone());
}
LinkedHashSet<Variable> clonedDependentVariables = new LinkedHashSet<>();
for (Variable variable : dependentVariables) {
clonedDependentVariables.add(variable.clone());
}
return new OctaveMapping(clonedIndependentVariables, clonedDependentVariables);
}
}
\ No newline at end of file
package lu.list.itis.dkd.tui.cps.system.executor;
import lu.list.itis.dkd.tui.cps.system.Import;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import java.io.CharArrayWriter;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;
abstract public class Executor {
protected CharArrayWriter executionErrors;
private static Logger LOGGER = Logger.getLogger(Executor.class.getSimpleName());
public Executor() {
executionErrors = new CharArrayWriter();
}
@SuppressWarnings("unused")
public Executor(Properties properties) {
executionErrors = new CharArrayWriter();
}
public String getExecutionErrors() {
return executionErrors.toString();
}
public void resetExecutionErrors() {
executionErrors.reset();
}
@SuppressWarnings("unused")
public void resolve(List<Import> imports) {
LOGGER.warning("Imports not supported by Executor!"); //$NON-NLS-1$
}
abstract public void set(Variable variable);
abstract public boolean eval(String script);
abstract public Variable get(Variable variable);
}
package lu.list.itis.dkd.tui.cps.system.executor;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import java.util.Properties;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class JavascriptExecutor extends Executor {
private ScriptEngine engine;
private Object result;
private static Logger LOGGER = Logger.getLogger(JavascriptExecutor.class.getSimpleName());
public JavascriptExecutor() {
engine = new ScriptEngineManager().getEngineByName("js"); //$NON-NLS-1$
}
public JavascriptExecutor(Properties properties) {
super(properties);
engine = new ScriptEngineManager().getEngineByName("js"); //$NON-NLS-1$
}
@Override
public void set(Variable variable) {
Double numericValue = Double.valueOf(variable.getValue().toString());
engine.put(variable.getName(), numericValue);
}
@Override
public boolean eval(String script) {
try {
result = engine.eval(script);
} catch (ScriptException exception) {
this.executionErrors.append(exception.toString());
return false;
}
return true;
}
@Override
public Variable get(Variable variable) {
variable.setValue(result);
return variable;
}
}
package lu.list.itis.dkd.tui.cps.system.executor;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import java.io.File;
import java.util.Properties;
import dk.ange.octave.OctaveEngine;
import dk.ange.octave.OctaveEngineFactory;
import dk.ange.octave.type.Octave;
import dk.ange.octave.type.OctaveDouble;
public class OctaveExecutor extends Executor {
private OctaveEngine engine;
private static final String SCRIPT_ENGINE_EXECUTABLE = "octave.executable"; //$NON-NLS-1$
private static final String SCRIPT_ENGINE_WORKING_DIR = "octave.workingDir"; //$NON-NLS-1$
// private static Logger LOGGER = Logger.getLogger(OctaveExecutor.class.getSimpleName());
public OctaveExecutor(Properties properties) {
super(properties);
OctaveEngineFactory engineFactory = new OctaveEngineFactory();
engineFactory.setOctaveProgram(new File(properties.getProperty(SCRIPT_ENGINE_EXECUTABLE, "/opt/local/bin/octave"))); //$NON-NLS-1$
engineFactory.setWorkingDir(new File(properties.getProperty(SCRIPT_ENGINE_WORKING_DIR, "octave"))); //$NON-NLS-1$
engine = engineFactory.getScriptEngine();
engine.setErrorWriter(this.executionErrors);
}
@Override
public void set(Variable variable) {
Double numericValue = Double.valueOf(variable.getValue().toString());
engine.put(variable.getName(), Octave.scalar(numericValue));
}
@Override
public boolean eval(String script) {