Written by Johan Vos
Many of the spectacular advances that are being achieved recently are in the field of data sciences, or at least related to it. Different forms of machine learning, big data calculations, and quantum computing are rapidly getting an increased impact on society in general.
The technical foundations of these advances are based on different scientific research areas. As is often the case, technical foundations are implementation agnostic. Different languages, platforms, and binaries can be used to implement the technical foundations.
In this chapter, we will explain why Java in general, and client Java (including JavaFX) in particular, is a great choice for developers who want to create scientific applications, using the language they master.
We will first demonstrate a few real samples and then explain a more general way that allows you to create scientific applications in Java, including doing the research work related to the scientific application.
JavaFX for Space Exploration
A great scientific application using JavaFX technologies is the Deep Space Trajectory Explorer, or DSTE. This is a product created by a.i. solutions and used by NASA. The product is described on the web site of a.i. solutions at https://ai-solutions.com/deep-space-trajectory-explorer.
According to the web site, the Deep Space Trajectory Explorer is an interactive software package that combines cutting-edge multibody trajectory design techniques with innovative visualizations to dramatically reduce time spent on trajectory design.
With DSTE, users can design trajectories of objects in space, which typically requires heavy calculations. Often, those calculations are done without any visualization or interactivity until the calculation is done. The DSTE product makes the design process more “agile” and interactive.
One of the key benefits of DSTE is that it allows the usage of interactive visualization in the design process for missions. Doing so, it becomes easier and more intuitive to select orbits that satisfy specific mission constraints.
The requirement to combine high-performant computing and complex visualization is a great use case for Java and JavaFX. Java itself is very scalable, and there are plenty of powerful Java APIs and framework that help developers and operators to scale Java code in multicore environments. The JavaFX platform allows for visualization including 3D models and Canvas rendering and thereby uses hardware-accelerated rendering, leveraging the availability of the GPU. Since JavaFX is pure Java, this rendering can easily be integrated with the pieces of code that are responsible for the high-performant computations.
The Deep Space Trajectory Explorer leverages the performance offered by the JavaFX platform in a number of ways. It contains different views, both in 2D and 3D. Many views allow for click-and-drag functionality. Canvas components are used to render millions of linked data points onscreen, without freezing the layout. Filters allow to select and deselect a number of options, and the resulting changes are real time rendered in the views.
The Deep Space Trajectory Explorer is not an application that is easily created. It requires deep knowledge both about the physics behind orbits, about high-performance computing, and about UI development. JavaFX is a crucial component in the total solution, proving the power of the platform.
JavaFX for Quantum Computing
Quantum computing is rapidly gaining interest, both in scientific environments and in IT departments.
One of the promises of quantum computing is that some problems that are extremely hard or practically impossible to solve using classic computers can easily be tackled with quantum computers. Especially in areas where algorithms show exponential time behavior, quantum computers can make a big difference.
Quantum computers use some core concepts that are fundamentally present in nature, but not in classical computers.
In a classic computer, the most granular unit is a bit. A bit is either 0 or 1. In a quantum computer, the most granular unit is a qubit. A qubit can hold the value 0 or 1, but it can also be in a so-called superposition state, in which it holds a linear combination of 0 and 1. When measured though, a qubit always returns either 0 or 1. Therefore, the algorithms for quantum computing must take advantage of the superposition states without measuring the qubits during the processing.
In classic computing, bits are manipulated by gates. For example, the NOT gate will flip the value of a bit. When the bit was 0 before it entered the gate, the result after the gate will be 1 and vice versa.
Similarly, in quantum computing, qubits are manipulated by quantum gates.
While there are some very early experimental chips for quantum computers available, quantum computing is by no means ready yet for mainstream development. The practical requirements to create a quantum computer with sufficient qubits that stay available for a reasonable amount of time are huge. Therefore, only a few prototypes exist with a limited amount of qubits.
However, because of the huge potential impact of quantum computing, many developers are already working on algorithms that might benefit from quantum computing. Typically, local or cloud-based simulators are used to develop these algorithms, and some companies are now starting to offer real quantum computers as a cloud service.
These algorithms are often developed in a programming language and visualized using circuit visualization.
One of those quantum simulators, Strange, is using a companion tool called StrangeFX which is build using JavaFX to render the circuits. StrangeFX is available on GitHub at https://github.com/gluonhq/strangefx.
StrangeFX allows developers to drag quantum gates onto qubit wires. While they do this, the local simulator evaluates the circuit and shows the results in real time. The drag-and-drop capabilities of JavaFX provide a very intuitive way for developing quantum circuits, and the integration with the quantum simulation algorithms is straightforward since both components are written in Java.
Using JShell
In the not so far past, we saw a fault line between activities in two different areas: developers on one hand, using programming languages to create applications, and people working on infrastructure and operations. Developers were mainly working on business solutions for a particular problem in an isolated environment. Once the problem was solved, the solution was handed over to the IT department, who had to bring it into productions. The gap between those worlds caused lots of issues related to scalability, documentation, versioning, accountability, dependencies, and so on. Too often the “it works for me” situation blocked the transition from developers who created a business solution toward operational people bringing the solution to high-scale production.
Overlapping parts of development and operations are tackled in a cross-domain devops environment.
Today, the growing interest in data sciences shows a new fault line between two groups: people working on research and developers working on development in production. Very often, the research is done by scientists who have extremely difficult and complex scientific problems to solve. Those researchers should focus on the core problem and not on the syntax or specific behavior of specific programming languages. Therefore, great scientific platforms or languages like Matlab and Python are often used by scientists to tackle those core problems.
Once the core problem is solved, though, it often needs to be integrated in a product and brought into production. This often creates new issues. Scientific platforms are focused on helping researchers finding the best solution to a scientific problem, not on finding the best way to integrate with databases, web services, and high-availability and security services.
The latter are areas where Java excels in. However, when scientists would have to use the same environment as Java Enterprise developers, their productivity would most likely drop.
When doing pure research, the iteration cycles are very different from developing business applications and running integration tests. During research, scientists want to measure the impact of changing a single variable, or they want to add a new layer to an existing deep learning model. They should be able to inspect the parameters and intermediate values of their algorithms. This is different from debugging business applications. It requires faster and deeper interactions with the algorithm itself, without the need of recompiling applications or running unit tests.
In the following, we will show how modern Java allows for this rapid scientific development, thanks to JShell. The JShell tool is included in the Java SE distributions starting from Java 9. It is a so-called REPL, which is an acronym for “read-eval-print-loop,” and it provides developers with a simple and interactive environment for creating and inspecting applications and algorithms. JShell is built on top of Java, and you can leverage all Java APIs with JShell, including the JavaFX APIs.
We will first show the basic functionalities of JShell. Next, we will show how you can easily work with linear algebra in JShell, using the ND4J library. Finally, we will demonstrate how using JavaFX with JShell allows for real easy and fast visualization during prototyping.
Using JShell
should start the tool. Once JShell is started, you enter the JShell environment in which you can enter commands or statements.
After starting JShell, you see the following:
It will result immediately in the following response:
(Note: As of Java 11, it is possible to skip the compilation step and directly run the class using java HelloWorld.java)
While the end result is the same (“Hello, JShell” is being printed), the steps are very different. Creating a class and a method with modifiers is very important when working on complex or modular software, and it should not be seen as overhead in this case. However, when we simply want to know what would be printed in the preceding snippet, the approach using JShell is giving us much faster answers.
Using JShell for printing “Hello, JShell” is not very ambitious, but we showed that you can use the same Java syntax in Java applications and in JShell. Hence, the familiarity with Java is one of the key advantages of JShell.
Scientific applications often require mathematical operations. We will now show an example that has more mathematical functionality.
The following snippet will print the value of the sine of a value that takes the value of 0, 30, 60, and 90 degrees:
One of the great things about JShell is the integrated editor functionalities. We can use the arrow up/down to move to a previous/next statement and edit that statement.
For example, suppose we made a mistake in our algorithm. Instead of the sine, we wanted to print the cosine.
We can easily do this by hitting the arrow up key once, which will show the previous line again, and we can modify it so that we replace “Math.sin” with “Math.cos.”
Hitting return immediately reevaluates the expression and prints the results:
As such, what JShell is allowing us here is to experiment with our statements or algorithm, by editing statements and immediately getting feedback about the results.
This approach is already more similar to how scientists work with Python and Matlab to create algorithms.
A huge advantage of JShell is that the code is 100% compatible with new or existing Java applications. The JShell code can easily be pasted in a Java class.
JShell contains a number of commands for saving and loading snippets. Those snippets can then be pasted in your applications.
Hence, the final resulting work, achieved by a scientist using JShell, can immediately be used by a Java developer who uses an IDE and works on integrating the scientific algorithms with other components in the project. The JShell snippet can be wrapped in a private method, submitted to an executor, surrounded with header parameters including security credentials and more.
While we touched some of the core concepts of JShell, we barely scratched the surface of what is possible. In the remainder of this chapter, we will focus on how JShell can be used in environments that combine scientific work and high-quality visualizations. If you want to learn more about JShell itself, you are recommended to have a look at the official JShell product page at https://docs.oracle.com/en/java/javase/11/jshell/introduction-jshell.html.
About ND4J
The ND4J linear algebra library, which was introduced in the previous chapter, allows for Java developers to access high-performant linear algebra functionality in a way that is very convenient to Java developers.
ND4J provides an abstraction layer on top of the platform-dependent libraries that provide linear algebra tools. The name ND4J refers to N-dimensional linear algebra for Java. Many platforms (e.g., Windows, Mac OS, Linux, iOS, Android) contain linear algebra libraries that are highly optimized for the specific platform. Moreover, the availability of specific hardware (e.g., GPUs) may lead to even more specific implementations of some functionality.
Since performance is very important in the data science area, it is crucial for Java developers to be able to leverage the functionality provided by those native libraries. However, it would be a pain for those developers if they have to write applications that only work for a specific hardware or operating system configuration.
This is where ND4J, and its dependencies, provides a solution. The top layer of ND4J provides APIs that users can interact with. These are Java APIs that are familiar to Java developers, but also to scientists as they provide the typical functionality one can expect from a linear algebra library. Under the hood, those APIs are mapped to the best available native library.
The ND4J library provides lots of value to Java developers who want to work with mathematical functionality, but also to researchers and scientists who want their work to be easily integrated in new or existing Java applications, whether they are running in a big enterprise or cloud environment or on embedded or mobile devices or anything between them.
Before we show how to use ND4J with JShell, we show a very simple application that uses ND4J for doing basic matrix manipulations:
The ND4J library requires other libraries to be available on the classpath. Since we don’t want to be bothered about exactly what libraries we need, we delegate that part to a build tool, for example, Maven. In a pom.xm file, we declare what we need, and Maven will make sure all related dependencies are downloaded and put on the classpath.
In this simple application, we created a few matrices and a vector, and we multiplied a matrix and a vector. While these are not extraordinary calculations, they show how ND4J works. If you want to read more about ND4J, and by extension about the projects that are related with it, we recommend to have a look at https://deeplearning4j.org/docs/latest/nd4j-overview. In the next section, we will explain how you can easily integrate applications based on ND4J in JShell.
Using ND4J in JShell
As a result, the command shown in the preceding text will start JShell with the classpath that is also used in the application. First, we import the packages that we require in our application:
We now enter the commands from the preceding application one by one. JShell will give immediate output after a single line has been entered. The first command that uses the ND4J API will initialize the ND4J backend, during which the most optimal provider for linear algebraic functions is selected and initialized. The feedback for this is printed before the result of this first command.
In our case, the first command is the creation of a 3 × 5 matrix which contains all zeroes. Entering this command generates the following output:
The warning message we see here tells us that we don’t have a logging provider for the SLF4J framework, which is something we do not want to use so we are happy with the warning.
We can now continue entering commands and inspect the output. For example, after entering the first line where we declare the 2 × 3 matrix, we get the following output:
After entering the last command (ignoring the System.out.println), the output of the matrix-vector multiplication is shown:
This is the same result as we got from our application.
One of the nice things we can now do with JShell is to just build on top of this result. For example, if we want to multiply all elements in the resulting vector with a scalar 3, we enter the command d.mul(3) . The ND4J Javadoc explains that the mul command will multiply the matrix elements by a provided number – see https://deeplearning4j.org/api/latest/org/nd4j/linalg/api/ndarray/INDArray.html#mul-java.lang.Number-.
We do not have to rerun the existing code or recompile an application. We simply type the command, and the result is immediately shown:
Finally, we will show some operations you can do using JShell. The examples we show have no scientific meaning, but they should illustrate the flexibility of the JShell tool, compared to the typical development cycle that involves modifying source code in an IDE, recompiling, and running from scratch.
After we executed the commands from the previous sample, we want to create a new function. In this new function, all elements of a matrix (or a vector) are multiplied by a number, and then another number is subtracted from the result.
Defining functions in JShell is very similar as defining functions in Java applications. We just enter the function definition. JShell will give confirmation about the created function, and from that moment on, we can use the function in all our operations:
For example, we can now use this new function on the b matrix that was created before. We will multiply all elements of b with 2 and then subtract 1 of each element. For clarity, we first print the current value of b, which is easily done by simply entering b; at the JShell prompt:
Note the “$31” prefixing the result. When no result variable is used, JShell will automatically create a variable itself and assign the result to this variable. Those variables can later be used again, similar to how other variables are used.
USING JavaFX in JShell
Since JShell is built on top of the JVM, any library, framework, or application that runs on the JVM can run using JShell. It may sound overkill to use a REPL to create JavaFX applications, and in many cases it is indeed not recommended. However, the Java visualization technologies in JavaFX allow for rapid visualization of data, which can be very useful when developing a scientific application.
Before we show an example of this rapid visualization, we explain how a JavaFX application can be executed inside JShell. But first, we will show an alternative way of starting stand-alone JavaFX applications.
Starting Stand-Alone JavaFX Code
Typically, a JavaFX application extends javafx.application.Application. Its start method is called by the JavaFX runtime. The JavaFX launcher manages the bootstrap of the JavaFX runtime.
This method will start the JavaFX runtime and call the supplied Runnable when the startup has been completed successfully. In our case, we don’t call a Runnable immediately; hence, we pass an empty Runnable. Once this method returns, the JavaFX Application Thread is created, and we can use Platform.runLater() statements to create or modify the SceneGraph, similar to how we should do it with JavaFX applications where the runtime is started by the JavaFX launcher.
The result is shown in the following:
In this snippet, we configured the exec task to call the Java command, but we don’t use the default launcher that Maven would otherwise use. Instead, we manually tell Java that it should take the module-path from the classpath (which includes the dependency javafx-controls) and add the javafx.controls module.
JavaFX Applications in JShell
We can now run the code in JShell. As we mentioned before, JShell is using a JVM for its execution. We can simply enter the same commands in JShell, and the output will immediately tell us what happened.
We can now enter the commands that ultimately make a JavaFX application, starting with the imports:
Note that we didn’t add a package declaration. This touches one of the few differences between regular Java applications and JShell code. Packages are created with the goal of exposing functionality and use that in other components, in other libraries. The JShell concept is to provide a stand-alone, interactive, and self-contained environment; hence, it does not make sense to expose packages.
Now that the imports are added, we can create the showHello() method. We will first use JShell in a less efficient way, but in the next section, we will show how this can be done in a much more productive way.
For now, we simply copy-paste the showHello method. This gives the following output:
The JShell editor allows for statements or declarations to be split over many lines. The parser recognizes that after the first line is entered, more content is needed. Hence, it will only process the method declaration after we completed it. It will detect the final closing curly bracket.
Note that we got one warning, telling us that the static keyword is ignored. In JShell, all top-level declarations are static so that keyword is useless in this context. This is another difference between regular applications and code living in the JShell context.
Now that the method is declared, we can call it. This is simply done by calling showHello() from the JShell prompt:
The statement returns immediately, and the same image as shown in the preceding screenshot.
JavaFX Libraries in JShell
While the example in the previous section works, it is very verbose, requires manual typing or copy-pasting, and does not really allow for fast prototyping. The real benefit for the combination of JavaFX and JShell comes from libraries and functions that provide simple things that can then be called from JShell statements . Typically, the functions are created in Java files, compiled, and made available to JShell. This is very similar to how the ND4J libraries are made available to JShell.
As an example, we create a simple function that creates a JavaFX chart containing some scattered data.
In this code, we defined a scatter function that takes three arguments: an array of doubles containing the x coordinates, an array of doubles containing the y coordinates, and a title for the chart. The scatter function initializes the JavaFX runtime, and then it creates a ScatterChart containing the data provided by the arguments. The chart is added to the Scene and rendered on the Stage.
We added a main function to this class so that we can test the function using regular Java invocations. In this main function, we pass three data points (hence three values for the x array and three values for the y array), and we supply the title “plot.”
We will now make this function available to JShell and call it from there.
Make sure to invoke this command in the directory that contains the pom.xml file for the plot-code. This will add the compiled classes and dependencies to the class and module path.
When JShell is started, the following message is shown:
We also create two arrays containing the x and y values for the data points we want to visualize:
One of the nice features of JShell is that there are a number of built-in commands that allow to work more efficient with the editor. The /list command for example shows the commands that have been executed in order. After we successfully applied the previous steps, the result of the /list command is shown in the following:
This screenshot clearly shows that it is much easier to call predefined functions in JShell than to declare all functions manually in JShell. As a rule of thumb, if you expect your function to be modified very often, you can declare it in JShell. But if you mainly want to evaluate functions and inspect the results, declaring them in classes and importing them in the JShell environment is more appropriate.
Conclusion
The combination of Java and JavaFX provides a solid foundation for scientific work. Java itself is already very performant and highly scalable. Using JShell and ND4J, it becomes possible for (data) scientists to work on research projects that require flexible manipulation of data, using familiar linear algebra routines.
Often, interactive visualization during the design process shortens the development cycle, and improves the quality of the result. Bringing the JavaFX visualization in the picture during the research phase allows for real-time visualization and highly interactive scientific applications.