-/*******************************************************************************\r
- * Copyright (c) 2013 Semantum Oy.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * Semantum Oy - initial API and implementation\r
- * VTT Technical Research Centre of Finland - #4488\r
- *******************************************************************************/\r
-package fi.semantum.sysdyn.solver;\r
-\r
-import fi.semantum.sysdyn.solver.Array.Modifier;\r
-import gnu.trove.list.array.TIntArrayList;\r
-import gnu.trove.map.hash.TObjectIntHashMap;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Iterator;\r
-import java.util.Map;\r
-import java.util.Random;\r
-import java.util.Set;\r
-import java.util.TreeMap;\r
-\r
-interface Fn {\r
- public Object evaluate(IEnvironment environment, int argc);\r
- public Object evaluateInput(IEnvironment environment, int argPosition);\r
- public void setLocals(IEnvironment environment);\r
- public int offset();\r
- public Variable[] parameters(int args);\r
-}\r
-\r
-abstract class Fn1 implements Fn {\r
-\r
- Variable[] parameters;\r
- \r
- public Fn1(int pCount) {\r
- if(pCount > 0) {\r
- parameters = new Variable[pCount];\r
- for(int i=0;i<pCount;i++)\r
- parameters[i] = new Variable(new VariableBase("", i));\r
- }\r
- }\r
- \r
- public int offset() {\r
- return 0;\r
- }\r
- \r
- public Variable[] parameters(int argc) {\r
- if(parameters != null) return parameters;\r
- else {\r
- Variable[] ret = new Variable[argc];\r
- for(int i=0;i<argc;i++)\r
- ret[i] = new Variable(new VariableBase("", i));\r
- return ret;\r
- }\r
- }\r
- \r
- @Override\r
- public void setLocals(IEnvironment environment) {\r
- }\r
- \r
- @Override\r
- public Object evaluateInput(IEnvironment environment, int argPosition) {\r
- // Function frame size is constant - this is called when padding is needed\r
- return null;\r
- }\r
- \r
-}\r
-\r
-abstract class FnRandom extends Fn1 {\r
- \r
- protected Random random = null;\r
-\r
- public FnRandom(int pCount) {\r
- super(pCount);\r
- }\r
- \r
-}\r
-\r
-final public class Environment implements IEnvironment, ISystem {\r
- \r
- final Map<String,Object> named = new HashMap<String,Object>();\r
-\r
- public Model model;\r
- public double step;\r
- public double time;\r
- public boolean initial = true;\r
- public int size;\r
- \r
- public Object[] valueTable;\r
- \r
- public TObjectIntHashMap<String> keyIndexMap = new TObjectIntHashMap<String>(); \r
-\r
- HashMap<String,TreeMap<Double,Double>> history = new HashMap<String,TreeMap<Double,Double>>();\r
-\r
- public Environment(Model model, double step, double start) {\r
- this.model = model;\r
- this.step = step;\r
- this.time = start;\r
- \r
- model.functions.put("sin", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.sin(x);\r
- }\r
- \r
- });\r
- model.functions.put("cos", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.cos(x);\r
- }\r
- \r
- });\r
- model.functions.put("tan", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.tan(x);\r
- }\r
- \r
- });\r
- model.functions.put("asin", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.asin(x);\r
- }\r
- \r
- });\r
- model.functions.put("acos", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.acos(x);\r
- }\r
- \r
- });\r
- model.functions.put("atan", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.atan(x);\r
- }\r
- \r
- });\r
- model.functions.put("toRadians", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.toRadians(x);\r
- }\r
- \r
- });\r
- model.functions.put("toDegrees", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.toDegrees(x);\r
- }\r
- \r
- });\r
- model.functions.put("exp", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.exp(x);\r
- }\r
- \r
- });\r
- model.functions.put("log", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.log(x);\r
- }\r
- \r
- });\r
- model.functions.put("log10", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.log10(x);\r
- }\r
- \r
- });\r
- model.functions.put("sqrt", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.sqrt(x);\r
- }\r
- \r
- });\r
- model.functions.put("cbrt", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.cbrt(x);\r
- }\r
- \r
- });\r
- model.functions.put("mod", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Double y = (Double)environment.getValue(1);\r
- return mod(x, y);\r
- }\r
-\r
- double mod(double x, double y) {\r
- return x - Math.floor(x/y)*y;\r
- }\r
-\r
- });\r
- model.functions.put("rem", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Double y = (Double)environment.getValue(1);\r
- return Math.IEEEremainder(x, y);\r
- }\r
-\r
- });\r
- model.functions.put("ceil", new Fn1(1) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object value = environment.getValue(0);\r
- if(value instanceof Double) {\r
- return Math.ceil((Double)value);\r
- } else if(value instanceof Array) {\r
- return ((Array)value).copy(new Modifier() {\r
-\r
- @Override\r
- public Object modify(Object o) {\r
- if(o instanceof Double) {\r
- return Math.ceil((Double)o);\r
- } else {\r
- throw new IllegalStateException();\r
- }\r
- }\r
- \r
- });\r
- } else {\r
- throw new IllegalStateException();\r
- }\r
- }\r
- \r
- });\r
- model.functions.put("floor", new Fn1(1) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object value = environment.getValue(0);\r
- if(value instanceof Double) {\r
- return Math.floor((Double)value);\r
- } else if(value instanceof Array) {\r
- return ((Array)value).copy(new Modifier() {\r
-\r
- @Override\r
- public Object modify(Object o) {\r
- if(o instanceof Double) {\r
- return Math.floor((Double)o);\r
- } else {\r
- throw new IllegalStateException();\r
- }\r
- }\r
- \r
- });\r
- } else {\r
- throw new IllegalStateException();\r
- }\r
- }\r
- \r
- });\r
- model.functions.put("rint", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.rint(x);\r
- }\r
- \r
- });\r
- model.functions.put("atan2", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Double y = (Double)environment.getValue(1);\r
- return Math.atan2(x, y);\r
- }\r
- \r
- });\r
- model.functions.put("pow", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Double y = (Double)environment.getValue(1);\r
- return Math.pow(x, y);\r
- }\r
- \r
- });\r
- model.functions.put("round", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- long result = Math.round(x); \r
- return (int)result;\r
- }\r
- \r
- });\r
-\r
- model.functions.put("abs", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object o = environment.getValue(0);\r
- if(o instanceof Double) {\r
- return Math.abs((Double)o);\r
- } else if (o instanceof Integer) {\r
- return Math.abs((Integer)o);\r
- } else {\r
- throw new IllegalStateException("Unsupported argument to abs: " + o);\r
- }\r
- }\r
- \r
- });\r
-\r
- model.functions.put("min", new Fn1(5) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object first = environment.getValue(0);\r
- if(first instanceof Double) {\r
- Double result = (Double)first;\r
- for(int i=1;i<argc;i++) {\r
- Double d = (Double)environment.getValue(i);\r
- if(d < result) result = d;\r
- }\r
- return result;\r
- } else if (first instanceof Integer) {\r
- Integer result = (Integer )first;\r
- for(int i=1;i<argc;i++) {\r
- Integer d = (Integer )environment.getValue(i);\r
- if(d < result) result = d;\r
- }\r
- return result;\r
- } else {\r
- throw new IllegalStateException("Unsupported argument to min: " + first);\r
- }\r
- }\r
- \r
- });\r
- model.functions.put("max", new Fn1(5) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object first = environment.getValue(0);\r
- if(first instanceof Double) {\r
- Double result = (Double)first;\r
- for(int i=1;i<argc;i++) {\r
- Double d = (Double)environment.getValue(i);\r
- if(d > result) result = d;\r
- }\r
- return result;\r
- } else if (first instanceof Integer) {\r
- Integer result = (Integer )first;\r
- for(int i=1;i<argc;i++) {\r
- Integer d = (Integer )environment.getValue(i);\r
- if(d > result) result = d;\r
- }\r
- return result;\r
- } else {\r
- throw new IllegalStateException("Unsupported argument to max: " + first);\r
- }\r
- }\r
- \r
- });\r
-\r
- model.functions.put("ulp", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.ulp(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("signum", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.signum(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("sinh", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.sinh(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("cosh", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.cosh(x);\r
- }\r
- \r
- });\r
- \r
- model.functions.put("tanh", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.tanh(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("hypot", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Double y = (Double)environment.getValue(1);\r
- return Math.hypot(x, y);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("expm1", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.expm1(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("log1p", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.log1p(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("copySign", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Double y = (Double)environment.getValue(1);\r
- return Math.copySign(x, y);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("getExponent", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.getExponent(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("nextAfter", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Double y = (Double)environment.getValue(1);\r
- return Math.nextAfter(x, y);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("nextUp", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- return Math.nextUp(x);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("scalb", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double x = (Double)environment.getValue(0);\r
- Integer y = (Integer)environment.getValue(1);\r
- return Math.scalb(x, y);\r
- }\r
- \r
- });\r
-\r
- model.functions.put("size", new Fn1(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Array array = (Array)environment.getValue(0);\r
- Double col = (Double)environment.getValue(1);\r
- return Double.valueOf(array.size(col.intValue()));\r
- }\r
- \r
- });\r
- model.functions.put("zidz", new Fn1(2) {\r
-\r
- private Object evaluate(Double p1, Double p2) {\r
- if(Math.abs(p2) < 1e-12) return 0.0;\r
- else return p1 / p2;\r
- }\r
- \r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object o1 = environment.getValue(0);\r
- Object o2 = environment.getValue(1);\r
- \r
- if(o1 instanceof Double && o2 instanceof Double) {\r
- return evaluate((Double)o1, (Double)o2);\r
- }\r
- if(o1 instanceof Array && o2 instanceof Array) {\r
- Array la = (Array)o1;\r
- Array ra = (Array)o2;\r
- Collection<Object> lae = la.elements();\r
- Collection<Object> rae = ra.elements();\r
- if(lae.size() != rae.size()) throw new UnsupportedOperationException();\r
- Iterator<Object> li = lae.iterator();\r
- Iterator<Object> ri = rae.iterator();\r
- Array result = new Array();\r
- for(int i=0;i<lae.size();i++) {\r
- double ld = (Double)li.next();\r
- double rd = (Double)ri.next();\r
- result.addElement(evaluate(ld,rd));\r
- }\r
- return result;\r
- }\r
- throw new IllegalStateException();\r
- \r
- \r
- }\r
- \r
- });\r
- model.functions.put("xidz", new Fn1(3) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double p1 = (Double)environment.getValue(0);\r
- Double p2 = (Double)environment.getValue(1);\r
- Double x = (Double)environment.getValue(2);\r
- if(Math.abs(p2) < 1e-12) return x;\r
- else return p1 / p2;\r
- }\r
- \r
- });\r
- model.functions.put("availabilityExternal", new Fn1(4) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double numberOfDays = (Double)environment.getValue(0);\r
- Double numberOfDesigners = (Double)environment.getValue(1);\r
- @SuppressWarnings("unused")\r
- Double rseed = (Double)environment.getValue(2);\r
- @SuppressWarnings("unused")\r
- Array availability = (Array)environment.getValue(3);\r
- Array result = new Array();\r
- for(int i=0;i<numberOfDays.intValue()*numberOfDesigners.intValue();i++) {\r
- result.addElement(Math.random());\r
- }\r
- return result;\r
- }\r
- \r
- });\r
- model.functions.put("pre", new Fn1(1) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- \r
- return (Double)environment.getValue(0);\r
- \r
- }\r
- \r
- });\r
- model.functions.put("fill", new Fn1(3) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double value = (Double)environment.getValue(0);\r
- Double dim1 = (Double)environment.getValue(1);\r
- if(argc == 2) {\r
- Array result = new Array();\r
- for(int i=0;i<dim1.intValue();i++) {\r
- result.addElement(value);\r
- }\r
- return result;\r
- } else if(argc == 3) {\r
- Double dim2 = (Double)environment.getValue(2);\r
- Array result = new Array();\r
- for(int i=0;i<dim1.intValue();i++) {\r
- Array array = new Array();\r
- for(int j=0;j<dim2.intValue();j++) {\r
- array.addElement(value);\r
- }\r
- result.addElement(array);\r
- }\r
- return result;\r
- } else throw new UnsupportedOperationException();\r
- }\r
- \r
- });\r
- model.functions.put("randomNumber", new FnRandom(2) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double seed = (Double)environment.getValue(0);\r
- \r
- if (random == null) {\r
- random = new Random(Double.doubleToLongBits(seed));\r
- }\r
- \r
- return random.nextDouble();\r
- }\r
- \r
- });\r
- model.functions.put("randomUniform", new FnRandom(4) {\r
- \r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double min = (Double)environment.getValue(0);\r
- Double max = (Double)environment.getValue(1);\r
- Double seed = (Double)environment.getValue(2);\r
- \r
- // the fourth parameter should be "time" and is only necessary\r
- // because sysdyn treats all functions as side-effect-free and\r
- // does not evaluate a function "needlessly" if the parameters\r
- // do not change. the parameter does not actually do anything\r
- // though so we can safely ignore it here\r
- \r
- if (random == null) {\r
- random = new Random(Double.doubleToLongBits(seed));\r
- }\r
- return random.nextDouble() * (max - min) + min;\r
- }\r
- \r
- });\r
- model.functions.put("randomNormal", new FnRandom(6) {\r
- \r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Double min = (Double)environment.getValue(0);\r
- Double max = (Double)environment.getValue(1);\r
- Double mean = (Double)environment.getValue(2);\r
- Double stdev = (Double)environment.getValue(3);\r
- Double seed = (Double)environment.getValue(4);\r
- \r
- if (random == null) {\r
- random = new Random(Double.doubleToLongBits(seed));\r
- }\r
- \r
- // TODO: use an actual library for this stuff\r
- \r
- return NormalDistribution.uniformVariableToNormal(min, max, mean, stdev, random.nextDouble());\r
- }\r
- \r
- });\r
- model.functions.put("initial", new Fn1(0) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment _environment, int argc) {\r
- return _environment.getSystem().initial();\r
- }\r
- \r
- });\r
- model.functions.put("noEvent", new Fn1(1) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- \r
- return environment.getValue(0);\r
- \r
- }\r
- \r
- });\r
- model.functions.put("integer", new Fn1(1) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object value = environment.getValue(0);\r
- if(value instanceof Double) {\r
- double d = ((Double)value).intValue(); \r
- return d;\r
- } else if(value instanceof Array) {\r
- return ((Array)value).copy(new Modifier() {\r
-\r
- @Override\r
- public Object modify(Object o) {\r
- if(o instanceof Double) {\r
- double d = ((Double)o).intValue(); \r
- return d;\r
- } else {\r
- throw new IllegalStateException();\r
- }\r
- }\r
- \r
- });\r
- } else {\r
- throw new IllegalStateException();\r
- }\r
- }\r
- \r
- });\r
- model.functions.put("sum", new Fn1(1) {\r
-\r
- public double sum(Array a) {\r
- double res = 0;\r
- for(Object element : a.elements()) {\r
- if(element instanceof Array) {\r
- res += sum((Array)element);\r
- } else if(element instanceof Double) {\r
- res += (Double)element;\r
- } else {\r
- throw new IllegalStateException("Tried to sum a non-numerical array");\r
- }\r
- }\r
- return res;\r
- }\r
- \r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- Object value = environment.getValue(0);\r
- if(value instanceof Double) return (Double)value;\r
- Array arr = (Array)value;\r
- return sum(arr);\r
- }\r
- \r
- });\r
- model.functions.put("ones", new Fn1(-1) {\r
-\r
- private Array make(int[] dims, int pos) {\r
- Array result = new Array();\r
- int d = dims[pos];\r
- if(pos == dims.length - 1) {\r
- for(int i=0;i<d;i++) result.addElement(1.0);\r
- } else {\r
- for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));\r
- }\r
- return result;\r
- }\r
- \r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- int[] dims = new int[argc];\r
- for(int i=0;i<argc;i++) {\r
- Double d = (Double)environment.getValue(i);\r
- dims[i] = d.intValue();\r
- }\r
- return make(dims, 0);\r
- }\r
- \r
- });\r
- model.functions.put("zeros", new Fn1(-1) {\r
-\r
- private Array make(int[] dims, int pos) {\r
- Array result = new Array();\r
- int d = dims[pos];\r
- if(pos == dims.length - 1) {\r
- for(int i=0;i<d;i++) result.addElement(0.0);\r
- } else {\r
- for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));\r
- }\r
- return result;\r
- }\r
- \r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- int[] dims = new int[argc];\r
- for(int i=0;i<argc;i++) {\r
- Double d = (Double)environment.getValue(i);\r
- dims[i] = d.intValue();\r
- }\r
- return make(dims, 0);\r
- }\r
- \r
- });\r
- model.functions.put("transpose", new Fn1(1) {\r
-\r
- @Override\r
- public Object evaluate(IEnvironment environment, int argc) {\r
- \r
- Array array = (Array)environment.getValue(0);\r
- return array.transposed();\r
- \r
- }\r
- \r
- });\r
- model.functions.put("delay", new Fn1(4) {\r
- \r
- @Override\r
- public Object evaluate(IEnvironment _environment, int argc) {\r
- \r
- String ident = (String)_environment.getValue(0);\r
- Double value = (Double)_environment.getValue(1);\r
- Double p1 = (Double)_environment.getValue(2);\r
- \r
- ISystem system = _environment.getSystem();\r
- \r
- TreeMap<Double,Double> history = system.getHistory(ident);\r
- double time = system.time();\r
- \r
- history.put(time, value);\r
- \r
- Double key = history.ceilingKey(time-p1);\r
- \r
- return history.get(key);\r
- \r
- }\r
- \r
- }); \r
- model.functions.put("cat", new Fn1(10) {\r
- \r
- @Override\r
- public Object evaluate(IEnvironment _environment, int argc) {\r
- \r
- Double dim = (Double)_environment.getValue(0);\r
- \r
- Array array = new Array();\r
- for(int i=1;i<argc;i++) {\r
- Object ar = _environment.getValue(i);\r
- if (ar instanceof Array) {\r
- Array a = (Array)ar;\r
- for(Object o : a.elements())\r
- array.addElement(o);\r
- } else {\r
- throw new IllegalStateException();\r
- }\r
- }\r
- \r
- return array;\r
- \r
- }\r
- \r
- }); \r
- }\r
- \r
- public TreeMap<Double,Double> getHistory(String ident) {\r
- TreeMap<Double,Double> result = history.get(ident);\r
- if(result == null) {\r
- result = new TreeMap<Double,Double>();\r
- history.put(ident, result);\r
- }\r
- return result;\r
- }\r
-\r
- @Override\r
- public Object getValue(int key) {\r
- return valueTable[key];\r
- }\r
- \r
- @Override\r
- public void put(int key, Object value) {\r
- valueTable[key] = value;\r
- }\r
- \r
- public void put(String key, Object value) {\r
- named.put(key, value);\r
- }\r
-\r
- public void setSize(int size) {\r
- this.size = size;\r
- }\r
- \r
- @Override\r
- public int offset() {\r
- return size;\r
- }\r
- \r
- @Override\r
- public ISystem getSystem() {\r
- return this;\r
- }\r
- \r
- @Override\r
- public boolean initial() {\r
- return initial;\r
- }\r
- \r
- public Object evaluateFunction(IEnvironment environment, String name, ArgumentList args) {\r
- return model.evaluateFunction(environment, name, args);\r
- }\r
- \r
- @Override\r
- public double time() {\r
- return time;\r
- }\r
-\r
- double[] valueArray;\r
- int[] valueIndices;\r
- \r
- public void addIndexed(int dimensions[], ArrayList<String> keys, String prefix, int d) {\r
-\r
- if(d == dimensions.length) {\r
- keys.add(prefix);\r
- return;\r
- }\r
-\r
- String prefix2 = prefix;\r
- if(d== 0) prefix2 += "[";\r
- if(d>0) prefix2 += ",";\r
-\r
- for(int i=0;i<dimensions[d];i++) {\r
- String prefix3 = prefix2 + (i+1);\r
- if(d+1 == dimensions.length) prefix3 += "]";\r
- addIndexed(dimensions, keys, prefix3, d+1);\r
- }\r
- \r
- }\r
- \r
- public int length(int[] dimension) {\r
- int result = 1;\r
- for(int d : dimension) result *=d;\r
- return result;\r
- }\r
- \r
- public void addVariable(String name, int index, int[] dimensions, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {\r
-\r
- if(!used.add(name)) return;\r
- \r
- if(dimensions == null) {\r
- keys.add(name);\r
- indices.add(index);\r
- } else {\r
- addIndexed(dimensions, keys, name, 0);\r
- for(int i=0;i<length(dimensions);i++)\r
- indices.add(index+i);\r
- }\r
-\r
- }\r
- \r
- public void addVariable(Variable var, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {\r
- addVariable(var.base.name, var.base.index, var.base.dimensions, keys, indices, used);\r
- }\r
-\r
- public String[] getValueKeyArray() {\r
- \r
- ArrayList<String> keys = new ArrayList<String>();\r
- TIntArrayList indices = new TIntArrayList();\r
- Set<String> used = new HashSet<String>();\r
- \r
- for (int i = 0; i < model.assignmentArray.length; i++) {\r
- addVariable(model.assignmentArray[i].target, keys, indices, used);\r
- }\r
- \r
- for (int i = 0; i < model.derivativeArray.length; i++) {\r
- addVariable(model.derivativeArray[i].target, keys, indices, used);\r
- }\r
- \r
- // NOTE: there is room for optimization as parameter values that do not\r
- // change should only be obtained once (and parameter values that do\r
- // change are (possibly) included in assignments or derivatives)\r
- for(int i = 0; i < model.parameterArray.length; i++) {\r
- addVariable(model.parameterArray[i].variable, keys, indices, used);\r
- }\r
- \r
- for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {\r
- addVariable(entry.getKey(), entry.getValue().index, entry.getValue().dimensions, keys, indices, used);\r
- }\r
- \r
- valueArray = new double[keys.size()];\r
- \r
- keyIndexMap.clear();\r
- for(int i=0;i<keys.size();i++) {\r
- String key = keys.get(i);\r
- int index = indices.get(i);\r
- keyIndexMap.put(key, index);\r
- }\r
- \r
- valueIndices = new int[keys.size()];\r
- getValueIndices();\r
- \r
- return keys.toArray(new String[keys.size()]);\r
- \r
- }\r
- \r
- public int[] getValueIndices() {\r
-\r
- int offset = 0;\r
-\r
- Set<String> used = new HashSet<String>();\r
-\r
- for (int i = 0; i < model.assignmentArray.length; i++) {\r
- Variable v = model.assignmentArray[i].target;\r
- if(!used.add(v.base.name)) continue;\r
- for(int index=0;index<v.base.dimension();index++)\r
- valueIndices[offset++] = v.base.index(this, null)+index;\r
- }\r
- \r
- for (int i = 0; i < model.derivativeArray.length; i++) {\r
- Variable v = model.derivativeArray[i].target;\r
- if(!used.add(v.base.name)) continue;\r
- for(int index=0;index<v.base.dimension();index++) {\r
- valueIndices[offset++] = v.base.index(this, null)+index;\r
- }\r
- }\r
- \r
- // NOTE: there is room for optimization as parameter values that do not\r
- // change should only be obtained once (and parameter values that do\r
- // change are (possibly) included in assignments or derivatives)\r
- for(int i = 0; i < model.parameterArray.length; i++) {\r
- Variable v = model.parameterArray[i].variable;\r
- if(!used.add(v.base.name)) continue;\r
- for(int index=0;index<v.base.dimension();index++)\r
- valueIndices[offset++] = v.base.index(this, null)+index;\r
- }\r
-\r
- for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {\r
- VariableBase base = entry.getValue();\r
- if(!used.add(entry.getKey())) continue;\r
- for(int index=0;index<base.dimension();index++)\r
- valueIndices[offset++] = base.index(this, null)+index;\r
- }\r
- \r
- return valueIndices;\r
- \r
- }\r
-\r
- // TODO: this is probably not smart at all, figure out a better way to obtain results\r
- public double[] getValueArray() {\r
-\r
- for(int i=0;i<valueArray.length;i++) {\r
- Double value = (Double)getValue(valueIndices[i]);\r
- // FIXME: should not be fixed like this\r
- if(value == null) value = 0.0;\r
- valueArray[i] = value;\r
- }\r
- \r
- return valueArray;\r
- \r
-// int offset = 0;\r
-//\r
-// Set<String> used = new HashSet<String>();\r
-//\r
-// for (int i = 0; i < model.assignmentArray.length; i++) {\r
-// Variable v = model.assignmentArray[i].target;\r
-// if(!used.add(v.base.name)) continue;\r
-// for(int index=0;index<v.base.dimension();index++)\r
-// valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);\r
-// }\r
-// \r
-// for (int i = 0; i < model.derivativeArray.length; i++) {\r
-// Variable v = model.derivativeArray[i].target;\r
-// if(!used.add(v.base.name)) continue;\r
-// for(int index=0;index<v.base.dimension();index++) {\r
-// Double value = (Double)getValue(v.base.index(this, null)+index);\r
-// // FIXME: should not be fixed like this\r
-// if(value == null) value = 0.0;\r
-// valueArray[offset++] = value;\r
-// }\r
-// }\r
-// \r
-// // NOTE: there is room for optimization as parameter values that do not\r
-// // change should only be obtained once (and parameter values that do\r
-// // change are (possibly) included in assignments or derivatives)\r
-// for(int i = 0; i < model.parameterArray.length; i++) {\r
-// Variable v = model.parameterArray[i].variable;\r
-// if(!used.add(v.base.name)) continue;\r
-// for(int index=0;index<v.base.dimension();index++)\r
-// valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);\r
-// }\r
-//\r
-// for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {\r
-// VariableBase base = entry.getValue();\r
-// if(!used.add(entry.getKey())) continue;\r
-// for(int index=0;index<base.dimension();index++)\r
-// valueArray[offset++] = (Double)getValue(base.index(this, null)+index);\r
-// }\r
-// \r
-// return valueArray;\r
- \r
- }\r
-\r
- @Override\r
- public Object getNamedValue(String key) {\r
- return named.get(key);\r
- }\r
-\r
- public void setValue(String key, double value) {\r
- int index = keyIndexMap.get(key);\r
- put(index, value);\r
- }\r
- \r
- public void setAssigned(Object ass) {\r
- assigned.add(ass);\r
- }\r
- \r
- public boolean isAssigned(Object o) {\r
- return assigned.contains(o);\r
- }\r
- \r
- private Set<Object> assigned = new HashSet<Object>();\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2013 Semantum Oy.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Semantum Oy - initial API and implementation
+ * VTT Technical Research Centre of Finland - #4488
+ *******************************************************************************/
+package fi.semantum.sysdyn.solver;
+
+import fi.semantum.sysdyn.solver.Array.Modifier;
+import gnu.trove.list.array.TIntArrayList;
+import gnu.trove.map.hash.TObjectIntHashMap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeMap;
+
+interface Fn {
+ public Object evaluate(IEnvironment environment, int argc);
+ public Object evaluateInput(IEnvironment environment, int argPosition);
+ public void setLocals(IEnvironment environment);
+ public int offset();
+ public Variable[] parameters(int args);
+}
+
+abstract class Fn1 implements Fn {
+
+ Variable[] parameters;
+
+ public Fn1(int pCount) {
+ if(pCount > 0) {
+ parameters = new Variable[pCount];
+ for(int i=0;i<pCount;i++)
+ parameters[i] = new Variable(new VariableBase("", i));
+ }
+ }
+
+ public int offset() {
+ return 0;
+ }
+
+ public Variable[] parameters(int argc) {
+ if(parameters != null) return parameters;
+ else {
+ Variable[] ret = new Variable[argc];
+ for(int i=0;i<argc;i++)
+ ret[i] = new Variable(new VariableBase("", i));
+ return ret;
+ }
+ }
+
+ @Override
+ public void setLocals(IEnvironment environment) {
+ }
+
+ @Override
+ public Object evaluateInput(IEnvironment environment, int argPosition) {
+ // Function frame size is constant - this is called when padding is needed
+ return null;
+ }
+
+}
+
+abstract class FnRandom extends Fn1 {
+
+ protected Random random = null;
+
+ public FnRandom(int pCount) {
+ super(pCount);
+ }
+
+}
+
+final public class Environment implements IEnvironment, ISystem {
+
+ final Map<String,Object> named = new HashMap<String,Object>();
+
+ public Model model;
+ public double step;
+ public double time;
+ public boolean initial = true;
+ public int size;
+
+ public Object[] valueTable;
+
+ public TObjectIntHashMap<String> keyIndexMap = new TObjectIntHashMap<String>();
+
+ HashMap<String,TreeMap<Double,Double>> history = new HashMap<String,TreeMap<Double,Double>>();
+
+ public Environment(Model model, double step, double start) {
+ this.model = model;
+ this.step = step;
+ this.time = start;
+
+ model.functions.put("sin", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.sin(x);
+ }
+
+ });
+ model.functions.put("cos", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.cos(x);
+ }
+
+ });
+ model.functions.put("tan", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.tan(x);
+ }
+
+ });
+ model.functions.put("asin", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.asin(x);
+ }
+
+ });
+ model.functions.put("acos", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.acos(x);
+ }
+
+ });
+ model.functions.put("atan", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.atan(x);
+ }
+
+ });
+ model.functions.put("toRadians", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.toRadians(x);
+ }
+
+ });
+ model.functions.put("toDegrees", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.toDegrees(x);
+ }
+
+ });
+ model.functions.put("exp", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.exp(x);
+ }
+
+ });
+ model.functions.put("log", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.log(x);
+ }
+
+ });
+ model.functions.put("log10", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.log10(x);
+ }
+
+ });
+ model.functions.put("sqrt", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.sqrt(x);
+ }
+
+ });
+ model.functions.put("cbrt", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.cbrt(x);
+ }
+
+ });
+ model.functions.put("mod", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Double y = (Double)environment.getValue(1);
+ return mod(x, y);
+ }
+
+ double mod(double x, double y) {
+ return x - Math.floor(x/y)*y;
+ }
+
+ });
+ model.functions.put("rem", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Double y = (Double)environment.getValue(1);
+ return Math.IEEEremainder(x, y);
+ }
+
+ });
+ model.functions.put("ceil", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object value = environment.getValue(0);
+ if(value instanceof Double) {
+ return Math.ceil((Double)value);
+ } else if(value instanceof Array) {
+ return ((Array)value).copy(new Modifier() {
+
+ @Override
+ public Object modify(Object o) {
+ if(o instanceof Double) {
+ return Math.ceil((Double)o);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ });
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ });
+ model.functions.put("floor", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object value = environment.getValue(0);
+ if(value instanceof Double) {
+ return Math.floor((Double)value);
+ } else if(value instanceof Array) {
+ return ((Array)value).copy(new Modifier() {
+
+ @Override
+ public Object modify(Object o) {
+ if(o instanceof Double) {
+ return Math.floor((Double)o);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ });
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ });
+ model.functions.put("rint", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.rint(x);
+ }
+
+ });
+ model.functions.put("atan2", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Double y = (Double)environment.getValue(1);
+ return Math.atan2(x, y);
+ }
+
+ });
+ model.functions.put("pow", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Double y = (Double)environment.getValue(1);
+ return Math.pow(x, y);
+ }
+
+ });
+ model.functions.put("round", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ long result = Math.round(x);
+ return (int)result;
+ }
+
+ });
+
+ model.functions.put("abs", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object o = environment.getValue(0);
+ if(o instanceof Double) {
+ return Math.abs((Double)o);
+ } else if (o instanceof Integer) {
+ return Math.abs((Integer)o);
+ } else {
+ throw new IllegalStateException("Unsupported argument to abs: " + o);
+ }
+ }
+
+ });
+
+ model.functions.put("min", new Fn1(5) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object first = environment.getValue(0);
+ if(first instanceof Double) {
+ Double result = (Double)first;
+ for(int i=1;i<argc;i++) {
+ Double d = (Double)environment.getValue(i);
+ if(d < result) result = d;
+ }
+ return result;
+ } else if (first instanceof Integer) {
+ Integer result = (Integer )first;
+ for(int i=1;i<argc;i++) {
+ Integer d = (Integer )environment.getValue(i);
+ if(d < result) result = d;
+ }
+ return result;
+ } else {
+ throw new IllegalStateException("Unsupported argument to min: " + first);
+ }
+ }
+
+ });
+ model.functions.put("max", new Fn1(5) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object first = environment.getValue(0);
+ if(first instanceof Double) {
+ Double result = (Double)first;
+ for(int i=1;i<argc;i++) {
+ Double d = (Double)environment.getValue(i);
+ if(d > result) result = d;
+ }
+ return result;
+ } else if (first instanceof Integer) {
+ Integer result = (Integer )first;
+ for(int i=1;i<argc;i++) {
+ Integer d = (Integer )environment.getValue(i);
+ if(d > result) result = d;
+ }
+ return result;
+ } else {
+ throw new IllegalStateException("Unsupported argument to max: " + first);
+ }
+ }
+
+ });
+
+ model.functions.put("ulp", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.ulp(x);
+ }
+
+ });
+
+ model.functions.put("signum", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.signum(x);
+ }
+
+ });
+
+ model.functions.put("sinh", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.sinh(x);
+ }
+
+ });
+
+ model.functions.put("cosh", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.cosh(x);
+ }
+
+ });
+
+ model.functions.put("cross", new Fn1(2) {
+
+ //Returns index 0 with input 1 as a Double
+ //index 1 with input 2 as a Double, etc.
+ private Double get(Array a, int crossIndex) {
+ return (Double)a.element(crossIndex - 1);
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Array X = (Array)environment.getValue(0);
+ Array Y = (Array)environment.getValue(1);
+ if(X.dimension() != 3 && Y.dimension() != 3) {
+ return null;
+ } else {
+ Array result = new Array(3);
+ Double a = (get( X,2 )*get( Y,3 )) - (get( X,3 )*get( Y,2 ));
+ Double b = (get( X,3 )*get( Y,1 )) - (get( X,1 )*get( Y,3 ));
+ Double c = (get( X,1 )*get( Y,2 )) - (get( X,2 )*get( Y,1 ));
+ result.addElement(a);
+ result.addElement(b);
+ result.addElement(c);
+ return result;
+ }
+ }
+
+ });
+ model.functions.put("tanh", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.tanh(x);
+ }
+
+ });
+
+ model.functions.put("hypot", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Double y = (Double)environment.getValue(1);
+ return Math.hypot(x, y);
+ }
+
+ });
+
+ model.functions.put("expm1", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.expm1(x);
+ }
+
+ });
+
+ model.functions.put("log1p", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.log1p(x);
+ }
+
+ });
+
+ model.functions.put("copySign", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Double y = (Double)environment.getValue(1);
+ return Math.copySign(x, y);
+ }
+
+ });
+
+ model.functions.put("getExponent", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.getExponent(x);
+ }
+
+ });
+
+ model.functions.put("nextAfter", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Double y = (Double)environment.getValue(1);
+ return Math.nextAfter(x, y);
+ }
+
+ });
+
+ model.functions.put("nextUp", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ return Math.nextUp(x);
+ }
+
+ });
+
+ model.functions.put("scalb", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ Integer y = (Integer)environment.getValue(1);
+ return Math.scalb(x, y);
+ }
+
+ });
+
+ model.functions.put("ndims", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Array array = (Array)environment.getValue(0);
+ return Double.valueOf(array.elements().size());
+ }
+
+ });
+
+ model.functions.put("diagonal", new Fn1(1) {
+
+ private Array make(int[] dims, int pos) {
+ Array result = new Array();
+ int d = dims[pos];
+ if(pos == dims.length - 1) {
+ for(int i=0;i<d;i++) result.addElement(0.0);
+ } else {
+ for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+ }
+ return result;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Array diagonalInputValues = (Array)environment.getValue(0);
+ int s = diagonalInputValues.dimension();
+ Array result = make(new int[] {s, s}, 0);
+ for(int i = 0; i < s; i++) {
+ Array sub = (Array)result.element(i);
+ sub.setElement(i, diagonalInputValues.element(i));
+ result.setElement(i, sub);
+ }
+ return result;
+ }
+ });
+
+ model.functions.put("identity", new Fn1(1) {
+
+ private Array make(int[] dims, int pos) {
+ Array result = new Array();
+ int d = dims[pos];
+ if(pos == dims.length - 1) {
+ for(int i=0;i<d;i++) result.addElement(0.0);
+ } else {
+ for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+ }
+ return result;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double size = (Double)environment.getValue(0);
+ int s = size.intValue();
+ Array result = make(new int[] {s, s}, 0);
+ for(int i = 0; i < s; i++) {
+ Array sub = (Array)result.element(i);
+ sub.setElement(i, 1.0);
+ result.setElement(i, sub);
+ }
+ return result;
+ }
+ });
+
+ model.functions.put("linspace", new Fn1(3){
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double startValue = (Double)environment.getValue(0);
+ Double endValue = (Double)environment.getValue(1);
+ Double n = (Double)environment.getValue(2);
+
+ if(n <= 2) {
+ Array res = new Array(2);
+ res.addElement(startValue);
+ res.addElement(endValue);
+ return res;
+ }
+
+ Array result = new Array(n.intValue());
+ Double stepSize = (endValue - startValue) / (n - 1);
+ for(int i = 0; i < (n-1); i++) {
+ result.addElement(startValue + (stepSize*i));
+ }
+ result.addElement(endValue);
+ return result;
+ }
+
+ });
+
+ model.functions.put("size", new Fn1(-1){
+
+ private List<Integer> findRecursive(Array array) {
+ int s = array.elements().size();
+ List<Integer> result = new ArrayList<>();
+ result.add(s);
+ if(s > 0) {
+ Object e = array.element(0);
+ if(e instanceof Array) {
+ List<Integer> found = findRecursive((Array)e);
+ result.addAll(found);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ if(argc==1) {
+ Array array = (Array)environment.getValue(0);
+ List<Integer> resultL = findRecursive(array);
+ Array result = new Array();
+ for(Integer i : resultL) {
+ result.addElement(i);
+ }
+ return result;
+ } else if(argc==2) {
+ Array array = (Array)environment.getValue(0);
+ Double col = (Double)environment.getValue(1);
+ return Double.valueOf(array.size(col.intValue()));
+ } else {
+ return null;
+ }
+ }
+
+ });
+
+ model.functions.put("zidz", new Fn1(2) {
+
+ private Object evaluate(Double p1, Double p2) {
+ if(Math.abs(p2) < 1e-12) return 0.0;
+ else return p1 / p2;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object o1 = environment.getValue(0);
+ Object o2 = environment.getValue(1);
+
+ if(o1 instanceof Double && o2 instanceof Double) {
+ return evaluate((Double)o1, (Double)o2);
+ }
+ if(o1 instanceof Array && o2 instanceof Array) {
+ Array la = (Array)o1;
+ Array ra = (Array)o2;
+ Collection<Object> lae = la.elements();
+ Collection<Object> rae = ra.elements();
+ if(lae.size() != rae.size()) throw new UnsupportedOperationException();
+ Iterator<Object> li = lae.iterator();
+ Iterator<Object> ri = rae.iterator();
+ Array result = new Array();
+ for(int i=0;i<lae.size();i++) {
+ double ld = (Double)li.next();
+ double rd = (Double)ri.next();
+ result.addElement(evaluate(ld,rd));
+ }
+ return result;
+ }
+ throw new IllegalStateException();
+
+
+ }
+
+ });
+ model.functions.put("xidz", new Fn1(3) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double p1 = (Double)environment.getValue(0);
+ Double p2 = (Double)environment.getValue(1);
+ Double x = (Double)environment.getValue(2);
+ if(Math.abs(p2) < 1e-12) return x;
+ else return p1 / p2;
+ }
+
+ });
+ model.functions.put("availabilityExternal", new Fn1(4) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double numberOfDays = (Double)environment.getValue(0);
+ Double numberOfDesigners = (Double)environment.getValue(1);
+ @SuppressWarnings("unused")
+ Double rseed = (Double)environment.getValue(2);
+ @SuppressWarnings("unused")
+ Array availability = (Array)environment.getValue(3);
+ Array result = new Array();
+ for(int i=0;i<numberOfDays.intValue()*numberOfDesigners.intValue();i++) {
+ result.addElement(Math.random());
+ }
+ return result;
+ }
+
+ });
+ model.functions.put("pre", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+
+ return (Double)environment.getValue(0);
+
+ }
+
+ });
+ model.functions.put("fill", new Fn1(3) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double value = (Double)environment.getValue(0);
+ Double dim1 = (Double)environment.getValue(1);
+ if(argc == 2) {
+ Array result = new Array();
+ for(int i=0;i<dim1.intValue();i++) {
+ result.addElement(value);
+ }
+ return result;
+ } else if(argc == 3) {
+ Double dim2 = (Double)environment.getValue(2);
+ Array result = new Array();
+ for(int i=0;i<dim1.intValue();i++) {
+ Array array = new Array();
+ for(int j=0;j<dim2.intValue();j++) {
+ array.addElement(value);
+ }
+ result.addElement(array);
+ }
+ return result;
+ } else throw new UnsupportedOperationException();
+ }
+
+ });
+ model.functions.put("randomNumber", new FnRandom(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double seed = (Double)environment.getValue(0);
+
+ if (random == null) {
+ random = new Random(Double.doubleToLongBits(seed));
+ }
+
+ return random.nextDouble();
+ }
+
+ });
+ model.functions.put("randomUniform", new FnRandom(4) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double min = (Double)environment.getValue(0);
+ Double max = (Double)environment.getValue(1);
+ Double seed = (Double)environment.getValue(2);
+
+ // the fourth parameter should be "time" and is only necessary
+ // because sysdyn treats all functions as side-effect-free and
+ // does not evaluate a function "needlessly" if the parameters
+ // do not change. the parameter does not actually do anything
+ // though so we can safely ignore it here
+
+ if (random == null) {
+ random = new Random(Double.doubleToLongBits(seed));
+ }
+ return random.nextDouble() * (max - min) + min;
+ }
+
+ });
+ model.functions.put("randomNormal", new FnRandom(6) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double min = (Double)environment.getValue(0);
+ Double max = (Double)environment.getValue(1);
+ Double mean = (Double)environment.getValue(2);
+ Double stdev = (Double)environment.getValue(3);
+ Double seed = (Double)environment.getValue(4);
+
+ if (random == null) {
+ random = new Random(Double.doubleToLongBits(seed));
+ }
+
+ // TODO: use an actual library for this stuff
+
+ return NormalDistribution.uniformVariableToNormal(min, max, mean, stdev, random.nextDouble());
+ }
+
+ });
+ model.functions.put("initial", new Fn1(0) {
+
+ @Override
+ public Object evaluate(IEnvironment _environment, int argc) {
+ return _environment.getSystem().initial();
+ }
+
+ });
+ model.functions.put("noEvent", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+
+ return environment.getValue(0);
+
+ }
+
+ });
+ model.functions.put("integer", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object value = environment.getValue(0);
+ if(value instanceof Double) {
+ double d = ((Double)value).intValue();
+ return d;
+ } else if(value instanceof Array) {
+ return ((Array)value).copy(new Modifier() {
+
+ @Override
+ public Object modify(Object o) {
+ if(o instanceof Double) {
+ double d = ((Double)o).intValue();
+ return d;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ });
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ });
+ model.functions.put("sum", new Fn1(1) {
+
+ public double sum(Array a) {
+ double res = 0;
+ for(Object element : a.elements()) {
+ if(element instanceof Array) {
+ res += sum((Array)element);
+ } else if(element instanceof Double) {
+ res += (Double)element;
+ } else {
+ throw new IllegalStateException("Tried to sum a non-numerical array");
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Object value = environment.getValue(0);
+ if(value instanceof Double) return (Double)value;
+ Array arr = (Array)value;
+ return sum(arr);
+ }
+
+ });
+
+ model.functions.put("product", new Fn1(1) {
+
+ public Double productRecursive(Array a) {
+ List<Object> elements = a.elements();
+ Double result = 1.0;
+ if(elements.size() > 0) {
+ if(elements.get(0) instanceof Array) {
+ for(Object e : elements) {
+ result = result * productRecursive((Array)e);
+ }
+ } else {
+ for(Object e : elements) {
+ result = result * (Double)e;
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Array array = (Array)environment.getValue(0);
+ Double result = productRecursive(array);
+ return result;
+ }
+ });
+
+ model.functions.put("ones", new Fn1(-1) {
+
+ private Array make(int[] dims, int pos) {
+ Array result = new Array();
+ int d = dims[pos];
+ if(pos == dims.length - 1) {
+ for(int i=0;i<d;i++) result.addElement(1.0);
+ } else {
+ for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+ }
+ return result;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ int[] dims = new int[argc];
+ for(int i=0;i<argc;i++) {
+ Double d = (Double)environment.getValue(i);
+ dims[i] = d.intValue();
+ }
+ return make(dims, 0);
+ }
+
+ });
+ model.functions.put("zeros", new Fn1(-1) {
+
+ private Array make(int[] dims, int pos) {
+ Array result = new Array();
+ int d = dims[pos];
+ if(pos == dims.length - 1) {
+ for(int i=0;i<d;i++) result.addElement(0.0);
+ } else {
+ for(int i=0;i<d;i++) result.addElement(make(dims, pos+1));
+ }
+ return result;
+ }
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ int[] dims = new int[argc];
+ for(int i=0;i<argc;i++) {
+ Double d = (Double)environment.getValue(i);
+ dims[i] = d.intValue();
+ }
+ return make(dims, 0);
+ }
+
+ });
+ model.functions.put("transpose", new Fn1(1) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+
+ Array array = (Array)environment.getValue(0);
+ return array.transposed();
+
+ }
+
+ });
+
+ model.functions.put("div", new Fn1(2) {
+
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ int x = ((Double)environment.getValue(0)).intValue();
+ int y = ((Double)environment.getValue(1)).intValue();
+ return (double)(x/y);
+ }
+
+ });
+
+
+ model.functions.put("sign", new Fn1(1) {
+ @Override
+ public Object evaluate(IEnvironment environment, int argc) {
+ Double x = (Double)environment.getValue(0);
+ if (x > 0.0) return 1.0;
+ else if (x < 0.0) return -1.0;
+ else return 0.0;
+ }
+ });
+
+ model.functions.put("delay", new Fn1(4) {
+
+ @Override
+ public Object evaluate(IEnvironment _environment, int argc) {
+
+ String ident = (String)_environment.getValue(0);
+ Double value = (Double)_environment.getValue(1);
+ Double p1 = (Double)_environment.getValue(2);
+
+ ISystem system = _environment.getSystem();
+
+ TreeMap<Double,Double> history = system.getHistory(ident);
+ double time = system.time();
+
+ history.put(time, value);
+
+ Double key = history.ceilingKey(time-p1);
+
+ return history.get(key);
+
+ }
+
+ });
+ model.functions.put("cat", new Fn1(10) {
+
+ @Override
+ public Object evaluate(IEnvironment _environment, int argc) {
+
+ Double dim = (Double)_environment.getValue(0);
+
+ Array array = new Array();
+ for(int i=1;i<argc;i++) {
+ Object ar = _environment.getValue(i);
+ if (ar instanceof Array) {
+ Array a = (Array)ar;
+ for(Object o : a.elements())
+ array.addElement(o);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ return array;
+
+ }
+
+ });
+ }
+
+ public TreeMap<Double,Double> getHistory(String ident) {
+ TreeMap<Double,Double> result = history.get(ident);
+ if(result == null) {
+ result = new TreeMap<Double,Double>();
+ history.put(ident, result);
+ }
+ return result;
+ }
+
+ @Override
+ public Object getValue(int key) {
+ return valueTable[key];
+ }
+
+ @Override
+ public void put(int key, Object value) {
+ valueTable[key] = value;
+ }
+
+ public void put(String key, Object value) {
+ named.put(key, value);
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ @Override
+ public int offset() {
+ return size;
+ }
+
+ @Override
+ public ISystem getSystem() {
+ return this;
+ }
+
+ @Override
+ public boolean initial() {
+ return initial;
+ }
+
+ public Object evaluateFunction(IEnvironment environment, String name, ArgumentList args) {
+ return model.evaluateFunction(environment, name, args);
+ }
+
+ @Override
+ public double time() {
+ return time;
+ }
+
+ double[] valueArray;
+ int[] valueIndices;
+
+ public void addIndexed(int dimensions[], ArrayList<String> keys, String prefix, int d) {
+
+ if(d == dimensions.length) {
+ keys.add(prefix);
+ return;
+ }
+
+ String prefix2 = prefix;
+ if(d== 0) prefix2 += "[";
+ if(d>0) prefix2 += ",";
+
+ for(int i=0;i<dimensions[d];i++) {
+ String prefix3 = prefix2 + (i+1);
+ if(d+1 == dimensions.length) prefix3 += "]";
+ addIndexed(dimensions, keys, prefix3, d+1);
+ }
+
+ }
+
+ public int length(int[] dimension) {
+ int result = 1;
+ for(int d : dimension) result *=d;
+ return result;
+ }
+
+ public void addVariable(String name, int index, int[] dimensions, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {
+
+ if(!used.add(name)) return;
+
+ if(dimensions == null) {
+ keys.add(name);
+ indices.add(index);
+ } else {
+ addIndexed(dimensions, keys, name, 0);
+ for(int i=0;i<length(dimensions);i++)
+ indices.add(index+i);
+ }
+
+ }
+
+ public void addVariable(Variable var, ArrayList<String> keys, TIntArrayList indices, Set<String> used) {
+ addVariable(var.base.name, var.base.index, var.base.dimensions, keys, indices, used);
+ }
+
+ public String[] getValueKeyArray() {
+
+ ArrayList<String> keys = new ArrayList<String>();
+ TIntArrayList indices = new TIntArrayList();
+ Set<String> used = new HashSet<String>();
+
+ for (int i = 0; i < model.assignmentArray.length; i++) {
+ addVariable(model.assignmentArray[i].target, keys, indices, used);
+ }
+
+ for (int i = 0; i < model.derivativeArray.length; i++) {
+ addVariable(model.derivativeArray[i].target, keys, indices, used);
+ }
+
+ // NOTE: there is room for optimization as parameter values that do not
+ // change should only be obtained once (and parameter values that do
+ // change are (possibly) included in assignments or derivatives)
+ for(int i = 0; i < model.parameterArray.length; i++) {
+ addVariable(model.parameterArray[i].variable, keys, indices, used);
+ }
+
+ for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {
+ addVariable(entry.getKey(), entry.getValue().index, entry.getValue().dimensions, keys, indices, used);
+ }
+
+ valueArray = new double[keys.size()];
+
+ keyIndexMap.clear();
+ for(int i=0;i<keys.size();i++) {
+ String key = keys.get(i);
+ int index = indices.get(i);
+ keyIndexMap.put(key, index);
+ }
+
+ valueIndices = new int[keys.size()];
+ getValueIndices();
+
+ return keys.toArray(new String[keys.size()]);
+
+ }
+
+ public int[] getValueIndices() {
+
+ int offset = 0;
+
+ Set<String> used = new HashSet<String>();
+
+ for (int i = 0; i < model.assignmentArray.length; i++) {
+ Variable v = model.assignmentArray[i].target;
+ if(!used.add(v.base.name)) continue;
+ for(int index=0;index<v.base.dimension();index++)
+ valueIndices[offset++] = v.base.index(this, null)+index;
+ }
+
+ for (int i = 0; i < model.derivativeArray.length; i++) {
+ Variable v = model.derivativeArray[i].target;
+ if(!used.add(v.base.name)) continue;
+ for(int index=0;index<v.base.dimension();index++) {
+ valueIndices[offset++] = v.base.index(this, null)+index;
+ }
+ }
+
+ // NOTE: there is room for optimization as parameter values that do not
+ // change should only be obtained once (and parameter values that do
+ // change are (possibly) included in assignments or derivatives)
+ for(int i = 0; i < model.parameterArray.length; i++) {
+ Variable v = model.parameterArray[i].variable;
+ if(!used.add(v.base.name)) continue;
+ for(int index=0;index<v.base.dimension();index++)
+ valueIndices[offset++] = v.base.index(this, null)+index;
+ }
+
+ for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {
+ VariableBase base = entry.getValue();
+ if(!used.add(entry.getKey())) continue;
+ for(int index=0;index<base.dimension();index++)
+ valueIndices[offset++] = base.index(this, null)+index;
+ }
+
+ return valueIndices;
+
+ }
+
+ // TODO: this is probably not smart at all, figure out a better way to obtain results
+ public double[] getValueArray() {
+
+ for(int i=0;i<valueArray.length;i++) {
+ Double value = (Double)getValue(valueIndices[i]);
+ // FIXME: should not be fixed like this
+ if(value == null) value = 0.0;
+ valueArray[i] = value;
+ }
+
+ return valueArray;
+
+// int offset = 0;
+//
+// Set<String> used = new HashSet<String>();
+//
+// for (int i = 0; i < model.assignmentArray.length; i++) {
+// Variable v = model.assignmentArray[i].target;
+// if(!used.add(v.base.name)) continue;
+// for(int index=0;index<v.base.dimension();index++)
+// valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);
+// }
+//
+// for (int i = 0; i < model.derivativeArray.length; i++) {
+// Variable v = model.derivativeArray[i].target;
+// if(!used.add(v.base.name)) continue;
+// for(int index=0;index<v.base.dimension();index++) {
+// Double value = (Double)getValue(v.base.index(this, null)+index);
+// // FIXME: should not be fixed like this
+// if(value == null) value = 0.0;
+// valueArray[offset++] = value;
+// }
+// }
+//
+// // NOTE: there is room for optimization as parameter values that do not
+// // change should only be obtained once (and parameter values that do
+// // change are (possibly) included in assignments or derivatives)
+// for(int i = 0; i < model.parameterArray.length; i++) {
+// Variable v = model.parameterArray[i].variable;
+// if(!used.add(v.base.name)) continue;
+// for(int index=0;index<v.base.dimension();index++)
+// valueArray[offset++] = (Double)getValue(v.base.index(this, null)+index);
+// }
+//
+// for(Map.Entry<String,VariableBase> entry : model.copies.entrySet()) {
+// VariableBase base = entry.getValue();
+// if(!used.add(entry.getKey())) continue;
+// for(int index=0;index<base.dimension();index++)
+// valueArray[offset++] = (Double)getValue(base.index(this, null)+index);
+// }
+//
+// return valueArray;
+
+ }
+
+ @Override
+ public Object getNamedValue(String key) {
+ return named.get(key);
+ }
+
+ public void setValue(String key, double value) {
+ int index = keyIndexMap.get(key);
+ put(index, value);
+ }
+
+ public void setAssigned(Object ass) {
+ assigned.add(ass);
+ }
+
+ public boolean isAssigned(Object o) {
+ return assigned.contains(o);
+ }
+
+ private Set<Object> assigned = new HashSet<Object>();
+
+}