Calling Java With Scala
May 19, 2016

Is Calling Java With Scala as Easy as It Seems?

Open Source

The JMSL Numerical Library, a pure Java implementation of the IMSL algorithms, is used easily in environments that utilize Java code. Scala is a prime example of such cross-language functionality. Each language has its own traits and idiosyncrasies, especially for users new to the language. In this article, I'll discuss my experiences as a Java developer exploring Scala, and what I found when trying to call a library in Java with Scala.

Getting Started With Scala

Anyone reading this is likely savvy enough to find, install and configure Scala on their own. For the examples shown here, I’m using CentOS 6 in a virtual machine. The only trickiness at all encountered during configuration is that my favorite Linux editor gedit does not recognize .scala files out of the box. While it’s certainly possible to write code without syntax highlighting, why would you? I packed up my vi shortcut muscle memory long ago. Adding syntax highlighting for a new language just requires a definition file, which is easy to install. You can find a README along with the scala.lang file here.

Using Java With Scala

Scala is designed to work smoothly with Java and Java libraries like JMSL. This is clearly evident in how Scala picks up the same CLASSPATH environment variable Java uses, so if the Java library you wish to use is already there in the CLASSPATH, then you’re good to go. The CLASSPATH can also be configured at compile-time and run-time through switches, again very similar to Java.

Scala Import Statements

Scala also uses import statements to simplify writing and reading code so the full class name isn’t required. The syntax is very similar with a key difference being the ability to use a comma-separated of classes used in the same package. While Java allows you to import all classes from a package with import com.imsl.math.* or individual classes, Scala allows writing import com.imsl.math.{PrintMatrix, LU} for example to import just two classes from the package. Each of the examples below use classes from the com.imsl.math package of the JMSL library. For brevity, the import statements, object definition and main method definition are not repeated after the first example.

Calling the JMSL Library With Scala

Java static method calls are generally the best place to start when looking at cross-language functionality because they require no object to be created. The JMSL library has numerous static methods, most with a method signature that requires passing one or more values. The method then computes an answer and returns it, often as a double precision value or array. Scala is designed to be more object-oriented, with singleton objects rather than mimicking the procedural tricks of C++ and Java, but thankfully this does not prevent Java static methods from being referenced easily. With many to choose from, I picked one of the “special function” calculations to compute the error function, erf, and the complete Scala code is presented below:

import com.imsl.math.Sfun object HelloJmsl { def main(args: Array[String]): Unit = { /* easy case of static method */ val erf = Sfun.erf(0.5) println("erf(0.5) = " + erf) } }

This example is contained in a source file named HelloJmsl.scala, which is compiled with the command scalac HelloJmsl.scala and executed with scala HelloJmsl (again, all very similar to Java). These commands and the output are shown below:

[~/dev/scala]% scalac HelloJmsl.scala
[~/dev/scala]% scala HelloJmsl
erf(0.5) = 0.5204998778130465

Instantiating a class is not much more complex. For this example, I want to solve the polynomial equation 5x2 + 2x + 1= 0 for x. As a second-order polynomial, there will be two roots, which may be complex numbers. The JMSL ZeroPolynomial class is used, which will return an array of complex numbers; the Complex class includes its own toString() method allowing printing of the results. The code and result are shown below, without the surrounding import, object and main method as mentioned above:

 	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))

x_1 = -0.20000000000000004+0.39999999999999997i
x_2 = -0.2-0.4i

Using the quadratic formula, one can easily verify the roots for this problem are x=-0.2 ± 0.4i which is what the algorithm found, within the precision of a standard 64-bit double.

Scala Arrays vs Java Arrays

Most classes in the JMSL Library require the use of arrays. There are some fundamental differences in the syntax between Java and Scala when dealing with arrays, especially multi-dimensional arrays. However, they are both identical in helpful ways as well; for example, multi-dimensional arrays are implemented as “arrays of arrays” in both languages. While numeric types in Java are primitives compared to everything, even numbers, being objects in Scala, no marshalling is required to pass numeric values or arrays between the two languages.

To a Java developer, the Scala way of assigning an array appears more verbose, especially hard coding values for tests or prototypes. For example, in Java you can quickly define a matrix (a rectangular 2D array) with something like the following:

        double a[][] = {
		{1, 3, 3},
		{1, 3, 4},
		{1, 4, 3}
	};

The equivalent syntax in Scala would be:

 	val a: Array[Array[Double]] = Array(
		Array(1, 3, 3),
		Array(1, 3, 4),
		Array(1, 4, 3))

The repeated use of Array can get verbose. The type declaration is required to designate that these are doubles and not integers; this can be avoided if you use 1.0 instead of 1 to specify that you want double Arrays. Again, this is a challenge limited to prototyping and testing rather than writing production code, but that’s where many of us spend a lot of time.

To cut down on the verboseness, it’s possible in Scala to define an alias for the word Array such as using the symbol “>”. For developers who cut their teeth on languages with very strict syntax, this is an interesting feature, but I have not yet found it to be useful in my short examples. The readability of the code is significantly reduced while the reduction of verboseness is minimal.

Creating Macros in Scala

With Scala version 2.10, it’s also possible to create a macro that allows you to define arbitrary matrices using strings. From what I’ve read, this is another powerful and interesting feature, but not easily accessible to someone getting starting with the language. Given a year or two, I’m sure very smart people will solve this trivial problem cleanly. Until then, I’ll just deal with the verbose, but readable, code.

Passing Scala Array Variables to Java Methods

The JMSL Library API relies extensively on 1D and 2D double arrays. Thankfully, there is no marshalling or conversions required to pass Scala Array variables to Java methods expecting double[] or double[][] arguments. An example, computing an LU factorization of a small matrix, is shown below:

        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 PrintMatrix("x").print(x)
	println("LU condition number = " + lu.condition(a))

LU answer = 3.000000000000001     2.0       1.0
  x
   0
0  3
1  2
2  3

LU condition number = 0.015120274914089344

In this example, both a 1D vector (b) and a 2D matrix (a) are defined. The matrix a is passed to the LU constructor, and the vector b is passed when calling the solve() method. Since the answer is only three values, it’s straightforward to print them out. The IMSL helper function PrintMatrix is also shown, which is valuable when printing out more complex matrices.

User-Defined Functions With JMSL Visualization

Now that we’ve covered the basics, let's 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 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 With JFrameChart

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

Advanced Analytics and Numerical Software Solutions

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.

Try JMSL FOR Free

Full Scala Code Example

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


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)	
	}
}