org.lsmp.djep.rpe.RpEval Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jep Show documentation
Show all versions of jep Show documentation
JEP is a Java library for parsing and evaluating mathematical expressions.
The newest version!
/* @author rich
* Created on 14-Apr-2004
*/
package org.lsmp.djep.rpe;
import org.nfunk.jep.*;
import org.nfunk.jep.function.*;
import java.util.*;
/**
* A fast evaluation algorithm for equations over Doubles, does not work with vectors or matricies.
* This is based around reverse polish notation
* and is optimised for speed at every opportunity.
*
* To use do
*
* JEP j = ...;
* Node node = ...;
* RpEval rpe = new RpEval(j);
* RpCommandList list = rpe.compile(node);
* double val = rpe.evaluate(list);
* System.out.println(val);
* rpe.cleanUp();
*
* The compile methods converts the expression represented by node
* into a string of commands. For example the expression "1+2*3" will
* be converted into the sequence of commands
*
* Constant no 1 (pushes constant onto stack)
* Constant no 2
* Constant no 3
* Multiply scalers (multiplies last two entries on stack)
* Add scalers (adds last two entries on stack)
*
* The evaluate method executes these methods sequentially
* using a stack
* and returns the last object on the stack.
*
* A few cautionary notes:
* Its very unlikely to be thread safe. It only works over doubles
* expressions with complex numbers or strings will cause problems.
* It only works for expressions involving scalers.
*
* Implementation notes
* A lot of things have been done to make it as fast as possible:
*
* - Everything is final which maximises the possibility for in-lining.
* - All object creation happens during compile.
* - All calculations done using double values.
* - Each operator/function is hand coded. To extend functionality you will have to modify the source.
*
*
* @author Rich Morris
* Created on 14-Apr-2004
*/
public final class RpEval implements ParserVisitor {
private OperatorSet opSet;
private ScalerStore scalerStore = new ScalerStore();
/** Contains the constant values **/
double constVals[] = new double[0];
/** Temporary holder for command list used during compilation */
private RpCommandList curCommandList;
public RpEval(JEP jep) {
this.opSet = jep.getOperatorSet();
}
private RpEval() {}
/** Index for each command */
public static final short CONST = 0;
public static final short VAR = 1;
public static final short ADD = 2;
public static final short SUB = 3;
public static final short MUL = 4;
public static final short DIV = 5;
public static final short MOD = 6;
public static final short POW = 7;
public static final short AND = 8;
public static final short OR = 9;
public static final short NOT = 10;
public static final short LT = 11;
public static final short LE = 12;
public static final short GT = 13;
public static final short GE = 14;
public static final short NE = 15;
public static final short EQ = 16;
public static final short LIST = 17;
public static final short DOT = 18;
public static final short CROSS = 19;
public static final short ASSIGN = 20;
public static final short VLIST = 21;
public static final short MLIST = 22;
public static final short FUN = 23;
public static final short UMINUS = 24;
/** Standard functions **/
private static final short SIN = 1;
private static final short COS = 2;
private static final short TAN = 3;
private static final short ASIN = 4;
private static final short ACOS = 5;
private static final short ATAN = 6;
private static final short SINH = 7;
private static final short COSH = 8;
private static final short TANH = 9;
private static final short ASINH = 10;
private static final short ACOSH = 11;
private static final short ATANH = 12;
private static final short ABS = 13;
private static final short EXP = 14;
private static final short LOG = 15;
private static final short LN = 16;
private static final short SQRT = 17;
private static final short SEC = 18;
private static final short COSEC = 19;
private static final short COT = 20;
// 2 argument functions
// private static final short ANGLE = 21;
// private static final short MODULUS = 22;
/** Hashtable for function name lookup **/
private static final Hashtable functionHash = new Hashtable();
{
functionHash.put("sin",new Short(SIN));
functionHash.put("cos",new Short(COS));
functionHash.put("tan",new Short(TAN));
functionHash.put("asin",new Short(ASIN));
functionHash.put("acos",new Short(ACOS));
functionHash.put("atan",new Short(ATAN));
functionHash.put("sinh",new Short(SINH));
functionHash.put("cosh",new Short(COSH));
functionHash.put("tanh",new Short(TANH));
functionHash.put("asinh",new Short(ASINH));
functionHash.put("acosh",new Short(ACOSH));
functionHash.put("atanh",new Short(ATANH));
functionHash.put("abs",new Short(ABS));
functionHash.put("exp",new Short(EXP));
functionHash.put("log",new Short(LOG));
functionHash.put("ln",new Short(LN));
functionHash.put("sqrt",new Short(SQRT));
functionHash.put("sec",new Short(SEC));
functionHash.put("cosec",new Short(COSEC));
functionHash.put("cot",new Short(COT));
}
/**
* Base class for storage for each type of data.
* Each subclass should define
*
* private double stack[];
* private double vars[]= new double[0];
*
* and the stack is the current data used for calculations.
* Data for Variables is stored in vars and references to the Variables
* in varRefs.
*/
private abstract static class ObjStore implements Observer {
/** Contains references to Variables of this type */
Hashtable varRefs = new Hashtable();
/** The stack pointer */
int sp=0;
/** Maximum size of stack */
int stackMax=0;
final void incStack() {sp++; if(sp > stackMax) stackMax = sp; }
final void decStack() throws ParseException {--sp; if(sp <0 ) throw new ParseException("RPEval: stack error");}
/** call this to reset pointers as first step in evaluation */
final void reset() { sp = 0; }
/** Add a reference to this variable.
* @return the index of variable in table
*/
final int addVar(Variable var){
Object index = varRefs.get(var);
if(index==null)
{
int size = varRefs.size();
expandVarArray(size+1);
varRefs.put(var,new Integer(size));
copyFromVar(var,size);
var.addObserver(this);
return size;
}
return ((Integer) index).intValue();
}
final public void update(Observable obs, Object arg1)
{
Variable var = (Variable) obs;
Object index = varRefs.get(var);
copyFromVar(var,((Integer) index).intValue());
}
/** allocates space needed */
abstract void alloc();
final void cleanUp()
{
for(Enumeration e=varRefs.keys();e.hasMoreElements();)
{
Variable var = (Variable) e.nextElement();
var.deleteObserver(this);
}
varRefs.clear();
}
/** Copy variable values into into private storage.
*
* @param var The variable
* @param i index of element in array
*/
abstract void copyFromVar(Variable var,int i);
/** expand size of array used to hold variable values. */
abstract void expandVarArray(int i);
/** add two objects of same type */
abstract void add();
/** subtract two objects of same type */
abstract void sub();
/** multiply by a scaler either of left or right */
abstract void mulS();
/** assign a variable to stack value
* @param i index of variable */
abstract void assign(int i);
Variable getVariable(int ref)
{
for(Enumeration en=varRefs.keys();en.hasMoreElements();)
{
Variable var = (Variable) en.nextElement();
Integer index = (Integer) varRefs.get(var);
if(index.intValue()==ref) return var;
}
return null;
}
}
private final class ScalerStore extends ObjStore {
double stack[]=new double[0];
double vars[]= new double[0];
final void alloc() {
stack = new double[stackMax];
}
final void expandVarArray(int size)
{
double newvars[] = new double[size];
System.arraycopy(vars,0,newvars,0,vars.length);
vars = newvars;
}
final void copyFromVar(Variable var,int i){
if(var.hasValidValue())
{
Double val = (Double) var.getValue();
vars[i]=val.doubleValue();
}
}
final void add(){
double r = stack[--sp];
stack[sp-1] += r;
}
final void sub(){
double r = stack[--sp];
stack[sp-1] -= r;
}
final void uminus(){
double r = stack[--sp];
stack[sp++] = -r;
}
final void mulS(){
double r = stack[--sp];
stack[sp-1] *= r;
}
final void div(){
double r = stack[--sp];
stack[sp-1] /= r;
}
final void mod(){
double r = stack[--sp];
stack[sp-1] %= r;
}
final void pow(){
double r = stack[--sp];
double l = stack[--sp];
stack[sp++] = Math.pow(l,r);
}
final void powN(int n){
double r = stack[--sp];
switch(n){
case 0: r = 1.0; break;
case 1: break;
case 2: r *= r; break;
case 3: r *= r*r; break;
case 4: r *= r*r*r; break;
case 5: r *= r*r*r*r; break;
default:
r = Math.pow(r,n); break;
}
stack[sp++] = r;
}
final void assign(int i) {
vars[i] = stack[--sp]; ++sp;
}
final void and(){
double r = stack[--sp];
double l = stack[--sp];
if((l != 0.0) && (r != 0.0))
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void or(){
double r = stack[--sp];
double l = stack[--sp];
if((l != 0.0) || (r != 0.0))
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void not(){
double r = stack[--sp];
if(r == 0.0)
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void lt(){
double r = stack[--sp];
double l = stack[--sp];
if(l < r)
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void gt(){
double r = stack[--sp];
double l = stack[--sp];
if(l > r)
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void le(){
double r = stack[--sp];
double l = stack[--sp];
if(l <= r)
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void ge(){
double r = stack[--sp];
double l = stack[--sp];
if(l >= r)
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void eq(){
double r = stack[--sp];
double l = stack[--sp];
if(l == r)
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
final void neq(){
double r = stack[--sp];
double l = stack[--sp];
if(l != r)
stack[sp++] = 1.0;
else
stack[sp++] = 0.0;
}
}
/**
* Compile the expressions to produce a set of commands in reverse Polish notation.
*/
public final RpCommandList compile(Node node) throws ParseException
{
curCommandList = new RpCommandList(this);
node.jjtAccept(this,null);
scalerStore.alloc();
return curCommandList;
}
public final Object visit(ASTStart node, Object data) throws ParseException {
throw new ParseException("RpeEval: Start node encountered");
}
public final Object visit(SimpleNode node, Object data) throws ParseException {
throw new ParseException("RpeEval: Simple node encountered");
}
public final Object visit(ASTConstant node, Object data) throws ParseException {
Object obj = node.getValue();
double val;
if(obj instanceof Double)
val = ((Double) node.getValue()).doubleValue();
else
throw new ParseException("RpeEval: only constants of double type allowed");
scalerStore.incStack();
for(short i=0;i