Calling Java from Scala is as easy as it seems (part 2)

Calling Java from Scala is as easy as it seems (part 2)

on May 31, 16 • by Ed Stewart • with No Comments

In part 2, we look at how user-defined functions are used to create solutions to unique problems and how the charting functionality in JMSL lets you visualize results...

Home » Analytics and Data Mining » Calling Java from Scala is as easy as it seems (part 2)

As a Java developer exploring Scala, I am relating what I found when trying to learn this language through calling a familiar Java library. In part 1 we covered the basics, now we’ll look at how user-defined functions are used to create solutions to unique problems and how the charting functionality in JMSL lets you visualize results.

User-defined functions
User-defined functions are a key aspect of any numerical library. While much mathematical and statistical analysis can be performed by passing data, many scenarios require a solution to a specific problem, unique to each user. Root solving and optimization are the obvious examples where the user has a specific equation form, with specific coefficients, that needs to be solved for their use case. The JMSL Library uses Interfaces to allow users to define their functions. The API defines the interface name that needs to be implemented including the method signature. Once the user has that in place, internally the function they defined is called repeatedly by the solver algorithm.

For the first example, the JMSL solve algorithm ZerosFunction is used. This class solves for the roots of a function specified by the user. In Java one simply creates an object for the interface to implement. In this case, the interface is named ZeroSystem.Function, which requires a public method named f that accepts two double arrays, returning void. There are variations across the JMSL Library classes that utilize interfaces for user-defined functions. Some require values to be returned, but in this case the user is literally implementing the function f(x) where the x values are passed into the function and values of f are computed.

The function in the documentation example to solve is the following system of equations:


f1(x) = x1 + ex1-1 + (x2 + x3)2 – 27
f2(x) = ex2-2 / x1 + x32 – 10
f3(x) = x3 + sin(x2-2) + x22 – 7

In Java, this user-defined function is implemented as follows:

	ZeroSystem.Function fcn = new ZeroSystem.Function() {
		public void f(double x[], double f[]) {
			f[0] = x[0] + Math.exp(x[0] - 1.0)
			+ (x[1] + x[2]) * (x[1] + x[2]) - 27.0;
			f[1] = Math.exp(x[1] - 2.0) / x[0] + x[2] * x[2] - 10.0;
			f[2] = x[2] + Math.sin(x[1] - 2.0) + x[1] * x[1] - 7.0;
		}
	};

The translation to Scala is almost exactly the same:

	val fcn = new ZeroSystem.Function {
		def f(x: Array[Double], f: Array[Double]) {
			f[0] = x[0] + Math.exp(x[0] - 1.0)
			+ (x[1] + x[2]) * (x[1] + x[2]) - 27.0;
			f[1] = Math.exp(x[1] - 2.0) / x[0] + x[2] * x[2] - 10.0;
			f[2] = x[2] + Math.sin(x[1] - 2.0) + x[1] * x[1] - 7.0;
		}
	};

The rest of the example creates the actual ZeroSystem object, sets an initial guess, then solves for the roots and prints them out:

	val zs = new ZeroSystem(3)
	zs.setGuess(Array(4.0, 4.0, 4.0))
	val zsx = zs.solve(fcn)
	new PrintMatrix("ZeroSystem solution").print(zsx)

ZeroSystem solution
   0
0  1
1  2
2  3

Interestingly a trait can also be defined in Scala and used similarly. This method is a little more verbose and less Java-like, but some users may prefer it. To avoid repetition, the solver MinUncon is used below. This function is an unconstrained minimization algorithm, solving the function f(x)=ex-5x:

	trait muFcn extends MinUncon with MinUncon.Function {
		def f(x: Double): Double = {
			Math.exp(x) - 5.0 * x
		}
	}

	val mu = new MinUncon with muFcn
	mu.setGuess(0.0)
	mu.setAccuracy(0.00001)
	println("\nMinUncon solution = " + mu.computeMin(mu))

MinUncon solution = 1.6094353603002598

The trait is defined first, explicitly declaring the class being extended and the interface being implemented. When the MinUncon object is instantiated, the trait is referenced using the with keyword. The function call mu.computeMin(mu) looks recursive, but the mu object itself is implementing the interface. This is consistent with the Java technique of declaring the class to implement the interface, then passing in this as an argument to the computeMin() method.

Charting
The JMSL Library includes charting functionality along with the numerical algorithms. Generally, the chart would be part of a larger user application and use the JPanelChart as a container, used wherever a JPanel would be used. A ChartServlet class is provided for JSP applications, and 3D graphics are supported through Java3D. For this example test with Scala, things are kept simple by using the JFrameChart, which behaves like a JFrame including its own menu bar.

	object MyChart extends JFrameChart {
		var a = new AxisXY(this.getChart)
		var d = new Data(a, Array(3.0, 6.0, 8.0, 2.0, 4.0))
		
		var ann = new Annotation(a, "Scala!", 3.5, 5.5)
		ann.setTextColor(java.awt.Color.blue)
	}
	
	MyChart.setVisible(true)

This program creates a singleton object, so there’s no need to instantiate anything and the setVisible() method is called on the object rather than an instance of it. An annotation is included on the graph using chart coordinates to position the text. The output is the following image:
JMSL output

Summary
The JMSL Numerical Library is used to explore the features of Scala and cross-language functionality. Static methods, instance classes, user-defined function and charting are all shown through examples. As Java developer with no Scala knowledge, the experience is uncomplicated given all the parallels between these languages.

A complete Scala program including all these examples can be found below.

Full code example

import com.imsl.math.{Sfun, LU, Bessel, ZeroPolynomial}
import com.imsl.math.{ZerosFunction, ZeroSystem}
import com.imsl.math.{MinUncon, MinConGenLin}
import com.imsl.chart._

object HelloJmsl {
	def main(args: Array[String]): Unit = {
		/* easy case of static method */
		println("\n -- Static method call --")
		val erf2 = Sfun.erf(0.5)
		println("erf(0.5) = " + erf2)
		
		/* instantiate a class with vector input and output */
        println("\n -- Class instance --")
		val zp = new ZeroPolynomial
		val coef = Array(1.0, 2.0, 5.0)
		val roots = zp.computeRoots(coef)
		println("x_1 = " + roots(0))
		println("x_2 = " + roots(1))
		
		/* instantiate a class that requires a matrix */
		println("\n -- Class instance --")
		val a = Array(
			Array(1.0, 3.0, 3.0), 
			Array(1.0, 3.0, 4.0), 
			Array(1.0, 4.0, 3.0))
		val b = Array(12.0, 13.0, 14.0)
		val lu = new LU(a)
		val x = lu.solve(b)
		println("LU answer = " + x(0) + "\t" + x(1) + "\t" + x(2))
		new com.imsl.math.PrintMatrix("x").print(x)
		println("LU condition number = " + lu.condition(a))
		
        /* User-defined function */
		println("\n -- User-defined function --")		
        val zsFcn = new ZeroSystem.Function {
			def f(x: Array[Double], f: Array[Double]) {
				f(0) = x(0) + Math.exp(x(0) - 1.0) + (x(1) + x(2)) * (x(1) + x(2)) - 27.0
                		f(1) = Math.exp(x(1) - 2.0) / x(0) + x(2) * x(2) - 10.0
                                f(2) = x(2) + Math.sin(x(1) - 2.0) + x(1) * x(1) - 7.0
                        }
                }
                
        var zs = new ZeroSystem(3) // with zsFcn
        zs.setGuess(Array(4.0, 4.0, 4.0))
        var zsx = zs.solve(zsFcn)
        new com.imsl.math.PrintMatrix("ZeroSystem solution").print(zsx)

		/* Using a trait for a user-defined function */
		println("\n -- User-defined function with a trait --")
		trait muFcn extends MinUncon with MinUncon.Function {
			def f(x: Double): Double = {
				Math.exp(x) - 5.0 * x
			}
		}
		val mu = new MinUncon with muFcn
		mu.setGuess(0.0)
		mu.setAccuracy(0.00001)
		println("\nMinUncon solution = " + mu.computeMin(mu))
	
		/* charting example */
		println("\n -- Chart example (blocking window should open) --")					
		object MyChart extends JFrameChart {
			var a = new AxisXY(this.getChart)
			var d = new Data(a, Array(3.0, 6.0, 8.0, 2.0, 4.0))
			
			var ann = new Annotation(a, "Scala!", 3.5, 5.5)
			ann.setTextColor(java.awt.Color.blue)
		}
		
		// note that this object (singleton instance of a class) but it 
		// could be a class then you'd need to instantiate it with		
		// val c = new MyChart and display with c.setVisible(true)
		MyChart.setVisible(true)	
	}
}

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Scroll to top