XJep - extensions to Jep


The com.singularsys.extensions.xjep package provides the XJep which extends the standard Jep class and add various symbolic operations plus a preprocessing facility allowing symbolic operators and an enhanced variable type.

A simple usage is

XJep xjep = new XJep();
Node node = xjep.parse("1 x^2 + 0");
Node simp = xjep.simplify(node);
top

Symbolic functions

Symbolic operations exposed by the class include:

Node clean(Node node)
simplifies an expression using the Expression cleaner
Node simplify(Node node)
simplifies expression using the polynomials package.
Node expand(Node node)
expands brackets in an expression using the polynomials package.
Node deepCopy(Node node)
produces a copy of a node
Node equals(Node l,Node r)
compares two simplified expressions without expansion.
Node substitute(Node orig,String name,Object value)
substitutes all occurrences of variable name with value.
Node substitute(Node orig,String name,Node replacement)
substitutes all occurrences of variable name with the expression specified by replacement.
Node substitute(Node orig,String sub)
perform substitution using expression of the form x=.....
Node substitute(Node orig, String[] names, Object[] values)
substitute a set of valuables by a set of values.
Node substitute(Node orig, String[] names, Node[] replacement)
substitute a set of valuables by a set of expressions.
Node substitute(Node orig, Node[] subs)
substitute using a set of expressions of the form x=... .
Node substituteConstantVariables(Node orig)
substitutes all constant variables like pi and e by their values.
Node replaceVariablesByEquations(Node node)
replace any symbolic variables by their equations
Node replaceRHSVariablesByEquations(Node node)
replace any symbolic variables on the RHS of an assignment by their equations

Expression can be interrogated using Set<Variable> getVarsInEquation(Node n)

which returns a set of all the variables mentioned in a single expression. A more detailed analysis of expression can be found by using the com.singularsys.jep.walkers.TreeAnalyzer class.

top

Preprocessing

The class adds a Node preprocess(Node node) method, which process any symbolic operations, like expansion or symbolic differentiation, that appear in an expression. PFMC's which should be run at pre-processing times should implement CommandVisitorI. This adds a Node process(Node node, Node children[]) method which is executed by the CommandVisitor whenever the preprocess(Node) method is called.

Current preprocessor functions/operators are:

XAssign
Symbolic assignment operator, see variables section below
Clean
cleans up an expression removing redundant terms.
Simplify*
simplifies expressions without expansion
Expand*
expands and simplifies the expression
Compare*
compares two expressions symbolically, return -1, 0, 1 depending on their ordering
SymbolicEquals*
compares two expressions symbolically, returns true or false
Coeffs*
extract polynomial coefficients
Eval
evaluates an expression during preprocessing.
ExtractEqn
extracts the expression of a symbolic variable.
Subst
Applies a substitution.

A further PFMC Preprocess causes the preprocessor to be run during evaluation. Note functions marked * are defined in the polynomials package.

XJep jep = new XJep();
// not strictly necessary as set by default
jep.getOperatorTable().getAssign().setPFMC(new XAssign());

jep.addFunction("clean", new Clean());
jep.addFunction("subst", new Subst());
jep.addFunction("eqn", new ExtractEqn());
jep.addFunction("eval",new Eval());
jep.addFunction("preprocess", new Preprocess());

// Add polynomial functions
jep.addFunction("simplify", new Simplify());
jep.addFunction("expand", new Expand());
jep.addFunction("coeffs", new Coeffs());
jep.addFunction("compare", new Compare(false));
jep.addFunction("symequals", new SymbolicEquals(false));

jep.reinitializeComponents();

Node to_clean = jep.parse("clean(1*x+0)");
Node cleaned = jep.preprocess(to_clean);
assertEquals("x",jep.toString(cleaned));

Node to_subst = jep.parse("subst(x^2+y^2,x=z+1,y=w-1)");
Node substituted = jep.preprocess(to_subst);
assertEquals("(z+1.0)^2.0+(w-1.0)^2.0",jep.toString(substituted));

Node to_simplify = jep.parse("simplify(x+2*x)");
Node simplified = jep.preprocess(to_simplify);
assertEquals("3.0*x",jep.toString(simplified));

Node to_expand = jep.parse("expand((x+1)^2)");
Node expanded = jep.preprocess(to_expand);
assertEquals("1.0+2.0*x+x^2.0",jep.toString(expanded));

Node to_coeffs = jep.parse("coeffs(2+3x+4x^2,x)");
Node coeffs = jep.preprocess(to_coeffs);
assertEquals("[2.0,3.0,4.0]",jep.toString(coeffs));

Node to_compare = jep.parse("compare(1+2x,x+x+1)");
Node compared = jep.preprocess(to_compare);
assertEquals("0",jep.toString(compared));

Node to_symequals = jep.parse("symequals(1+2x,x+x+1)");
Node symequals = jep.preprocess(to_symequals);
assertEquals("true",jep.toString(symequals));

Node to_assign = jep.parse("x=z+1"); // define an equation with a variable
Node assign = jep.preprocess(to_assign);
assertTrue(assign.getPFMC() instanceof XAssign);
XVariable var = (XVariable) jep.getVariable("x");
assertTrue(var.hasEquation());
assertEquals("z+1.0",jep.toString(var.getEquation()));

Node to_eqn = jep.parse("eqn(x)");	// 
Node eqn = jep.preprocess(to_eqn);
assertEquals("z+1.0",jep.toString(eqn));

Node to_eval = jep.parse("x^eval(n+1)");
jep.setVariable("n",2.0);
Node eval = jep.preprocess(to_eval);
assertEquals("x^3.0",jep.toString(eval));

Node to_preprocess = jep.parse("preprocess(eqn(x)^eval(n+1))");
Object evaluated = jep.evaluate(to_preprocess);
assertTrue(evaluated instanceof Node);
assertEquals("(z+1.0)^3.0",jep.toString((Node)evaluated));

top

XVariables

The xjep package introduce a new type of variable, XVariable, which allow variables to be associated with equations. Variable created by an XJep instance will automatically be of this type. In the processing step any assignment expression like y=x^2+1 set equation of the left hand variable to the expression on the right. The is acheived using the XAssign Jep function.

The equation for a variable can be recovered by using XVariable.getEquation(), and the value of a variable can be calculated from its equation using XJep.calcVarValue(name,flag). The flag option controls how any variables in the expression are evaluated. If set to true then all variables with equations are automatically evaluate. For example if z=y*y and y=x+1 then to evaluate z the equation for variable y will first be evaluated. And as y appears twice its equation will be evaluated twice. If the flag is set to false then equations will only be evaluated as needed. Each variable has a validValue flag, this is set to true whenever the value is set either by calling jep.addVariable(name,value) or through evaluating an equation with the variable on the left hand side. This flag can be cleared by calling Variable.setValidValue(false) or by calling VariableTable.clearValues() which clears all values. So with the above example the y will only be evaluated once.

XJep xjep = new XJep();
xjep.addVariable("x", 3.0); // set variable with no equation
Node node1 = xjep.parse("y=x+1"); // equation with variable on lhs
xjep.preprocess(node1); // sets the equation for variable y
Node node2 = xjep.parse("z=y*y");
xjep.preprocess(node2); // sets equation to variable z

// Get equation for variable y
XVariable varY = (XVariable) xjep.getVariable("y");
assertEquals("x+1.0",xjep.toString(varY.getEquation()));

// calculate the values of z using the calcVarValue method
// will also calculate value of variable y
Object val = xjep.calcVarValue("z",true);
assertEquals(16.0,val);

// Clear all the variable values
xjep.getVariableTable().clearValues();
// change value of x
xjep.setVariable("x", 4.0);
// recalculate z, also recalculates y (once)
Object val2 = xjep.calcVarValue("z",false);
assertEquals(25.0,val2);

// changing x without clearing values will use
// previously calculated results
xjep.setVariable("x", 5.0);
Object val3 = xjep.calcVarValue("z",false);
assertEquals(25.0,val3);

// But setting the flag to true recalculates 
// Subsequent equations
xjep.setVariable("x", 6.0);
Object val4 = xjep.calcVarValue("z",true);
assertEquals(49.0,val4);

The motivation behind this scheme comes into play if differentiation when partial derivatives of variables are automatically calculated.

Summary of Variable Use

Summary of use of variables in the XJep package:

Class Method Action
Jep public void addConstant(String name,Object value)
Adds a constant variable whose value can not be changed.
Jep public void addVariable(String name,Object value) Adds a variable or change the value of an existing variable. Throws exception on error.
Jep
public boolean setVariableValue(String name,Object value) Sets the value of a mutable variable. Returns false on error. New in Jep 3.5.
Jep
public Variable getVariable(String name) Returns the object representing the variable.
Jep
public Object getVariableValue(String name)
Gets the value of the variable. Does not re-calculate.
XJep
public Object calcVarValue(String name) Calculates the value of a variable from its equation.
XJep
public preprocess(Node node)
Causes the equations of variable on the lhs of an assignment equation to be set.
XVariable
public Node getEquation()
Returns the equation of a variable.
XVariable
public Object calcValue()
Calculates the value of a variable from its equation.
VariableTable
public void clearValues()
Marks all non constant variables as invalid.
top

Functions

Various functions are // Add XJep preprocessing functions jep.addFunction("compare", new Compare(xjep, false)); jep.addFunction("expand", new CExpand(xjep.getPolynomialCreator(),jep.addConstant("i",Complex.I))); jep.addFunction("simplify", new Simplify(xjep)); jep.addFunction("coeffs", new Coeffs(xjep)); jep.addFunction("clean", new Clean(xjep)); jep.addFunction("subst", new Subst(xjep)); jep.addFunction("eqn", new ExtractEqn(xjep));

Examples