LIONoso orchestration of Java modules

LIONoso offers various means of integrating external models and data sources within its workflow.

The most efficient way to include an external model in LIONoso is to embed it within a Java package containing a class that extends the org.lionoso.FunctionBase interface.

In this example, we will implement a model with 3 inputs and 2 outputs, and we will import it into LIONoso in order to insert it in an orchestrated workflow.

Prerequisites

Please make sure that you have the latest version of LIONoso: 2.1.47 or later is recommended for this tutorial.

Knowledge of the Java language is a prerequisite.
You can download the complete code of this example from this lionoso.org link, ready to be compiled via Ant, but if you have any experience in creating Java packages we advise you to write down the code and work out the details by yourself with your favorite IDE.
Of course, you can use the provided file as a base for developing your own model.

You'll need the latest version of the LIONinterfaces.jar package containing the org.lionoso.FunctionBase interface. Please download LIONinterfaces.zip and unzip it in a convenient folder.

Step 1: Implement the model in Java

You are free to use any Java IDE to create the model, as well as operate from the command line. Let us create a JARSample.java file and declare the org.lionoso.examples.JARSample class in it that implements org.lionoso.FunctionBase:

class org.lionoso.examples;
import org.lionoso.FunctionBase;
public class JARSample implements FunctionBase {

Let's now declare a double array that will contain the output values computed by the model:

    private double[] output;

The class constructor will just instantiate the output vector:

    public JARSample ()
    {
        output = new double [2];
    }

Next, we must write the implementation of the interface's methods. The model's domain dimension is the number of real-valued inputs to the model, in our case 3, while the range dimension is the number of real-valued outputs:

    @Override
    public int getDomainDimension ()
    {
        return 3;
    }

    @Override
    public int getRangeDimension ()
    {
        return 2;
    }

For the user's convenience, and also to enable LIONoso to refer to single inputs and outputs, the model is required to provide names for its input and output values. Our model must provide a total of five names (three inputs and two outputs, remember?). Here we chose to name the inputs x, y and z, while the outputs are named according to the function that they provide (in our case, we chose the names output_1 and output_2, but they might as well be Expected income and Interest rate):

    @Override
    public String[] getNames ()
    {
        return new String[] {"x", "y", "z", "output_1", "output_2"};
    }

For the sake of visualization and other orchestration functionalities, it is important that you provide minimum and maximum values for all inputs and outputs. You don't need to be precise, just set reasonable ranges of variation for all five values. Minima and maxima are returned as arrays by two distinct functions:

    @Override
    public double[] getMin ()
    {
        return new double[] {-10.0, -10.0, -10.0, -1.0, -1.0};
    }

    @Override
    public double[] getMax ()
    {
        return new double[] {10.0, 10.0, 10.0, 1.0, 1.0};
    }

The model itself must have a name:

    @Override
    public String getName ()
    {
        return "JAR Sample model";
    }

Finally, we implement the method that will be called every time the model must be re-evaluated for new input values. Inputs are passed in the method's argument x as a vector of doubles, and the method must return a reference to a double-valued array of outputs (the output array that we declared above):

    @Override
    public double[] evaluate (double[] x)
    {
        double radius = Math.sqrt (x[0]*x[0] + x[1]*x[1] + x[2]*x[2]);
        double sine = Math.sin (radius);
        output[0] = radius < 1.0e-5 ? 1.0 : sine / radius;
        output[1] = sine;
        return output;
    }

The evaluate method can do anything you wish, even use other classes and packages: in our case, we chose to have it self-contained for simplicity.
Now the class is complete:

}

Step 2: Compile and package the model

Your final package might contain many different classes, and LIONoso needs to know which class implements the correct interface. To this end, create a manifest file (e.g., JARsample.mf) containing the following line:

Lion-Function-Implementation: org.lionoso.examples.JARSample

Now you can compile the Java files: remember to add LIONinterfaces.jar's path to the compiler's classpath. Finally create the JAR package, which must obey the following rules:

  • It must contain all classes and packages it relies upon; the only packages that can be left out are those contained in the system-wide classpath (no need to include the standard packages). You can also leave out the interface package LIONinterfaces.jar, which is already contained in LIONoso's own code.
  • It must contain JARsample.mf as manifest.

Step 3: Import the JAR package in LIONoso

From the left-side toolbox, open the Orchestration of external components / Models branch and drag the JAR package tool onto the workbench. From the file selection popup, select the JARsample.jar file that you just created.

The new file will appear in the workbench, ready to be used:

You can visualize the model with the Function Sweeper: just right-click its icon and select New sweeper panel:

From the Surface plot section of the Sweeper's property pane, increase both coordinate levels to 50 and press Create surface plot to see a 3D representation of the same function:

What next?

Integration of external models within LIONoso enables a wide range of applications where LIONoso can interact with external programs, or even with physical devices. A more complex example, where internal and external components are integrated in the field of machine learning, can be found in Using LIONoso orchestration.