The com.singularsys.extensions.fastreal
packages offers fast evaluation routines for expressions involving double scalars.
These offer a 5-10 times speed improvement over the standard Jep evaluation and approaches the speed obtainable by native java code.
The classes are intended to be used in situations where the same expressions are repeatedly evaluated using different values of variables.
For expressions involving vectors and matrices see the fastmatrix package instead.
The com.singularsys.extensions.fastreal.RpEval
class is used to perform evaluation for scaler expressions using doubles. A two step process is used
first the expression is compiled into an RpCommandList
and this is later evaluated.
The name "RpEval" stems from
Reverse Polish notation
which is how the compiled expression is encoded.
Jep j = new Jep(); 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();
The evaluator maintains its only private list of variables values which are stored in an array. The array index of a variable can be found using
int ref = rpe.getVarRef("x");
or
Variable v = j.getVar("x"); int ref = rpe.getVarRef(v);and the value of the variable can be set and read using
rpe.setVarValue(ref,0.1234); double val = rpe.getVarValue(ref);
This mechanism allows very fast access to the variable value.
Variable values can also be set using the standard Variable.setValue()
or
Jep.setVarVal(name,value)
methods
but this is a little slower.
Setting the value of a Jep variable will automatically update the corresponding rpe value
but there will be a performance hit if done repeatedly.
Setting the value of the rpe variable does not change the value of the corresponding Jep value,
however calling rpe.updateJepVariables()
will set the values of corresponding Jep variables.
The system works best when the same expression is repeatedly evaluated with different values for the
variables. The following code calculates the hight of a surface which depends on two variables, x
and y
.
// Parse and compile an expression RpEval rpe = new RpEval(jep); String s = "a x^2 + b y^2"; Node n = jep.parse(s); RpCommandList coms = rpe.compile(n); // Get the references to the variables int xRef = rpe.getVarRef("x"); int yRef = rpe.getVarRef("y"); int aRef = rpe.getVarRef("a"); int bRef = rpe.getVarRef("b"); // sets the two parameters a, b rpe.setVarValue(aRef, 2.5); rpe.setVarValue(bRef, -3.1); // loop over different values of x and y for(double x=-1.0;x<=1.0;x+=0.1) { rpe.setVarValue(xRef, x); for(double y=-1.0;y<1.0;y+=0.1) { rpe.setVarValue(yRef, y); // evaluate the expression with these variable values double res = rpe.evaluate(coms); System.out.printf("%4.1f ",res); } System.out.println(); }
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.
The com.singularsys.extensions.fastreal.RpEvaluator
allows the evaluator to be used
in normal jep code. To set up use
jep = new Jep(new RpEvaluator(true));
And then continue to use the jep code as normal.
This is somewhat slower than using rpe.evaluate()
directly but is still faster than using the normal Jep evaluator.
Some speedup can be obtained by using new RpEvaluator(false)
which does not update the jep variables following evaluation.
A few cautionary notes:
RpEval.duplicate()
method can be used to allow the
same command strings to be used in multiple threads.A lot of things have been done to make it as fast as possible:
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.
The table below indicates the evaluation speeds. Times are in milliseconds for a million evaluations.
Expression | Speed using Jep | Speed using rpe | Speed using Java |
---|---|---|---|
5 | 125 | 47 | |
x | 281 | 94 | |
1+x | 515 | 110 | |
x^2 | 594 | 109 | |
x*x | 468 | 109 | |
5*x | 484 | 110 | |
1+x+x^2 | 1375 | 172 | |
1+x+x^2+x^3 | 1671 | 250 | |
1+x+x^2+x^3+x^4 | 2234 | 328 | |
1+x+x^2+x^3+x^4+x^5 | 2860 | 406 | |
1+x(1+x(1+x(1+x(1+x)))) | 2375 | 359 | 62 |
cos(x) | 485 | 203 | 79 |
cos(x)^2+sin(x)^2 | 1750 | 406 | |
if(x>0.5,1,0) | 734 | 218 | |
y=x*x; z=y*y; w=z*z | 1829 | 313 |
This indicates a speed up of between 3 and 13 times as fast.