A question was raised recently asking if the JMSL Library could work with an expression parser. In this case, the user wishes to use one or more of the JMSL Classes that require user-defined functions, but wants create the equations as a string. Theoretically, this is possible - all you need is an efficient parser for the string.

The tool used in this example is Jep - Java Math Expression Parser. Visual Numerics has no partnership with Singular Systems, the creators of Jep, and have selected this tool because it is a fully featured example of an expression parser and is under active development. Any other parser could be used in a similar manner.

In short, following the few steps outlined below and by referring to the complete example included here, one should be able to use Jep to evaluate arbitrary functions within the interfaces defined by the JMSL Library. Please let us know if you find this helpful.

User Functions in JMSL
Many different classes in the JMSL Library require user-defined functions. These vary from root finding to optimization. The core idea is that the primary class includes an Interface to be implemented by the user. For the ZeroFunction class, for example, there is the ZeroFunction.Function interface to be implemented by the class. The requirement is a public function "f" that accepts a double and returns a double. The basic example in the documentation evaluates the sine function as follows:

Code:
class MyFunction implements ZeroFunction.Function {
	public double f(double x) {
		return Math.sin(x);
	}
}
Some other interfaces have different requirements in that some may accept an array or return an array, but the concept is the same. This is the point where an expression parse would be implemented to evaluate the string expression and return a value.

Implementing Jep
To replicate this simple example using the Jeb library is relatively straightforward. The equation string to evaluate is simply "sin(x)", which must be parsed and evaluated at the given value. The Jep.evaluate() method returns an Object, which must be cast to a Double, on which the doubleValue() method is called to return a regular "double". The rest of the class should be easy to follow:
Code:
class MyJepFunction implements ZeroFunction.Function {
	Jep jep;
	String eqn;
	Double dresult;

	MyJepFunction() {
		jep = new Jep();
		eqn = "sin(x)";
		dresult = new Double(0);
	}

	public double f(double x) {
		try {
			jep.addVariable("x", x);
			Object result = jep.evaluate(jep.parse(eqn));
			dresult = (Double)result;
		} catch (Exception e) {
			System.out.println("Exception: " + e.getMessage());
		}
		return dresult.doubleValue();
	}
}
Repeated Evaluations
The Jep documentation makes reference to the fact that if you're repeatedly evaluating the same expression for different input values, the best way to code this is to call Jep.parse() a single time, and then simply call evaluate() on the Node object that is returned from parse(). In the complete example that follows, we use this method to parse the more complicated polynomial used in the MinConGenLin optimization class.

A complete example
The example code below is complete and runs with JMSL 5.0 and Jep 3.2. There are several interfaces implemented:
  • MyFunction, which uses ZeroFunction.Function the standard way
  • MyJepFunction, which uses Jep for ZeroFunction.Function
  • MyJepPoly, which implements MinConGenLin.Function to evaluate a 5 variable polynomial. The input now is an array, so Jep.addVariable() is called several times.

Exceptions are caught as required, but there's not really much in the way of exception handling in this basic example. The standard MinConGen method is included as an anonymous inner class, but has been commented out.

Code:
import com.imsl.math.*;
import com.singularsys.jep.*;

public class TestJepFcn {
    public static void main(String args[]) {
        new TestJepFcn();
    }

    public TestJepFcn() {
        // Basic usage of Jep
        System.out.println("\nBasic usage of Jep library");
        Jep jep = new Jep();
        jep.addVariable("x", 10);
        try {
            Object result = jep.evaluate(jep.parse("x+1"));

            System.out.println("x + 1 = " + result);

        } catch (EvaluationException e) {
            System.out.println("An error occured: " + e.getMessage());
        } catch (ParseException e) {
            System.out.println("An error occured: " + e.getMessage());
        }

        // Basic usage of ZeroFunction
        System.out.println("\n\nTypical usage of com.imsl.math.ZeroFunction");
        ZeroFunction zf = new ZeroFunction();
        double guess[] = {5, 18, -6};
        double zeros[] = zf.computeZeros(new MyFunction(), guess);
        for (int k = 0;  k < zeros.length;  k++) {
            System.out.println(zeros[k]+" = "+(zeros[k]/Math.PI) + " pi");
        }

        // Jep with ZeroFunction
        System.out.println("\n\nUsing Jep with com.imsl.math.ZeroFunction");
        ZeroFunction zfj = new ZeroFunction();
        double guessj[] = {5, 18, -6};
        double zerosj[] = zfj.computeZeros(new MyJepFunction(), guess);
        for (int k = 0;  k < zerosj.length;  k++) {
            System.out.println(zeros[k]+" = "+(zerosj[k]/Math.PI) + " pi");
        }

        // Jep with MinConGenLin
        System.out.println("\n\nUsing Jep with com.imsl.math.MinConGenLin");
        int neq = 2;
        int ncon = 2;
        int nvar = 5;
        double a[] = {1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, -2.0, -2.0};
        double b[] = {5.0, -3.0};
        double xlb[] = {0.0, 0.0, 0.0, 0.0, 0.0};
        double xub[] = {10.0, 10.0, 10.0, 10.0, 10.0};

        /*
        // This is how one defines the function in Java code
        // compared to the evaluation string using Jep
        // eqn = "v^2 + w^2 + x^2 + y^2 + z^2 - 2w*x - 2y*z - 2v";
        MinConGenLin.Function fcn = new MinConGenLin.Function() {
            public double f(double[] x) {
                return x[0]*x[0] + x[1]*x[1] + x[2]*x[2] + x[3]*x[3] +
                x[4]*x[4] - 2.0*x[1]*x[2] - 2.0*x[3] * x[4] - 2.0*x[0];
            }
        };
        */

        MinConGenLin mcgl = new MinConGenLin(new MyJepPoly(), nvar, ncon, neq, a, b, xlb, xub);

        try {
            mcgl.solve();
            new PrintMatrix("Solution").print(mcgl.getSolution());
        } catch (com.imsl.IMSLException e) {
            System.out.println("An error occured: " + e.getMessage());
        }
    }

    class MyFunction implements ZeroFunction.Function {
        public double f(double x) {
            return Math.sin(x);
        }
    }

    class MyJepFunction implements ZeroFunction.Function {
        Jep jep;
        String eqn;
        Double dresult;

        MyJepFunction() {
            jep = new Jep();
            eqn = "sin(x)";
            dresult = new Double(0);
        }

        public double f(double x) {
            try {
                jep.addVariable("x", x);
                Object result = jep.evaluate(jep.parse(eqn));
                dresult = (Double)result;
            } catch (EvaluationException e) {
                System.out.println("Exception: " + e.getMessage());
            } catch (ParseException e) {
                System.out.println("Exception: " + e.getMessage());
            }
            return dresult.doubleValue();
        }
    }

    class MyJepPoly implements MinConGenLin.Function {
        Jep jep;
        Double dresult;
        com.singularsys.jep.parser.Node eqnNode;

        MyJepPoly() {
            jep = new Jep();
            String eqn = "v^2 + w^2 + x^2 + y^2 + z^2 - 2w*x - 2y*z - 2v";
            dresult = new Double(0);
            // parse once and then just call evaluate() later
            try {
                eqnNode = jep.parse(eqn);
            } catch (ParseException e) {
                System.out.println("Exception: " + e.getMessage());
            }
        }

        public double f(double x[]) {
            try {
                jep.addVariable("v", x[0]);
                jep.addVariable("w", x[1]);
                jep.addVariable("x", x[2]);
                jep.addVariable("y", x[3]);
                jep.addVariable("z", x[4]);
                Object result = jep.evaluate(eqnNode);
                dresult = (Double)result;
            } catch (EvaluationException e) {
                System.out.println("Exception: " + e.getMessage());
            }
            return dresult.doubleValue();
        }
    }
}