Fast evaluation of mathematical equations in Java - extensions to Jep


The org.lsmp.djep.rpe and org.lsmp.djep.mrpe packages offers fast evaluation routines for expressions involving scalars or matrices respectively. These offer a 5-10 times speed improvement over the standard Jep evaluation and approach the speed obtainable by native java code.

top

Scaler expressions

To optimise the speed of evaluation, separate packages are provided for equations involving doubles, i.e. standard single valued functions and those involving vectors and matrices. The org.lsmp.djep.rpe package provides support for non vector equations.

To use do

  JEP j = new XJep();
  j.addStandardConstants();
  j.addStandardFunctions();
  j.addComplex();
  j.setAllowUndeclared(true);
  j.setImplicitMul(true);
  j.setAllowAssignment(true);

  RpEval rpe = new RpEval(j);
  
  Node node = j.parse("cos(pi/3)^2"); 
  RpCommandList list = rpe.compile(node);
  double val = rpe.evaluate(list);
  System.out.println(val);
  rpe.cleanUp();

Variable

Variable values in the evaluator are stored in a array. The array index of a variable can be found using

  Variable v = j.getVar("x");
  int ref = rpe.getVarRef(v);
and the value of the variable set using
  rpe.setVarValue(ref,0.1234);

Variable values can also be set using the standard Variable.setValue() or (slower) JEP.setVarVal(name,vlaue) methods. Setting the value of a jep variable will automatically update the corresponding rpe value but there will be a performance hit. Setting the value of the rpe variable does not change the value of the corresponding jep value.

How it works

The compile methods converts the expression represented by node into a string of commands. For example the expression "4+5*6" will be converted into the sequence of commands

  Constant no 1 (4) (pushes constant onto stack)
  Constant no 2 (5)
  Constant no 3 (6)
  Multiply scalers (multiplies last two entries on stack)
  -  (6*5), the result (30) is pushed onto the top of the stack
  Add scalers (adds last two entries on stack)
  -  (30+4) the result (34) is pushed onto top of the stack

The evaluate method executes these methods sequentially using a stack and returns the last object on the stack.

Implementation notes

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.

A lot of things have been done to make it as fast as possible:

Example applications

Supported functions

Functions which take double arguments and return double results are supported. Other functions which return strings or complex numbers will raise exceptions when used.

Some functions have been optimised for speed these are: sin, cos, tan, sec, cosec, cot, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, abs, exp, log, ln, sqrt, atan2, if these are hand coded in the routines for high performance.

Other jep functions which take double arguments and return double results are also supported. These can be used directly and no additional calls are required.

To improve performance four interfaces org.lsmp.djep.RealNullaryFunction (0 arguments) org.lsmp.djep.RealUnaryFunction (1 argument) org.lsmp.djep.RealBinaryFunction (2 arguments) org.lsmp.djep.RealNaryFunction (any number of arguments) have been created, a PostfixMathCommand which implements one of these interfaces will be faster to evaluate.

Performance

The table below indicates the evaluation speeds. Times are in milliseconds for a million evaluations.
ExpressionSpeed using JepSpeed using rpeSpeed using Java
515647
x29778
1+x78194
x^2844109
x*x78194
5*x79794
cos(x)65615679
1+x+x^21828172
1+x+x^2+x^32844281
1+x+x^2+x^3+x^43891312
1+x+x^2+x^3+x^4+x^54891375
1+x(1+x(1+x(1+x(1+x))))476639162
if(x>0.5,1,0)1219219
This indicates a speed up of between 3 and 13 times as fast.
top