Initializing help system before first use

Java

To use the Mosel Java classes the line import com.dashoptimization.*; must be added at the beginning of the program.

Compiling and executing a model in Java

With Java Mosel is initialized by creating a new instance of class XPRM. To execute a Mosel model in Java we call the three Mosel functions performing the standard compile/load/run sequence as shown in the following example (file ugcomp.java).

import com.dashoptimization.*;

public class ugcomp
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;

  mosel = new XPRM();                       // Initialize Mosel

  System.out.println("Compiling `burglar2'");
  mosel.compile("burglar2.mos");

  System.out.println("Loading `burglar2'");
  mod = mosel.loadModel("burglar2.bim");

  System.out.println("Executing `burglar2'");
  mod.run();

  System.out.println("`burglar2' returned: " + mod.getResult());
 }
}

Termination

If the model execution is embedded in a larger appplication it may be useful to reset the model after its execution to free some resources allocated to it:

  mod.reset();                        // Reset the model

This will release all intermediate objects created during the execution without deleting the model itself.

It is also possible to explicitly remove the temporary directory/files created by the execution of Mosel:

  mosel.removeTmpDir();               // Delete temporary files

Unloading models or Mosel from memory is ensured through standard finalization + garbage collection functionalities of Java. The finalizers are public and may be called from the user's Java program. Finalization of Mosel only takes effect once all loaded models have been finalized. Finalizing Mosel also removes the temporary directory/files created by the execution of Mosel.

  mod.finalize();                     // Finalize a model
  mod = null;
  mosel.finalize();                   // Finalize Mosel
  mosel = null;

Parameters

When executing a Mosel model in Java, it is possible to pass new values for its parameters into the model. If, for instance, we want to run model `Prime' from Section Working with sets to obtain all prime numbers up to 500 (instead of the default value 100 set for the parameter LIMIT in the model), we may write the following program:

import com.dashoptimization.*;

public class ugparam
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  int LIM=500;

  mosel = new XPRM();                       // Initialize Mosel

  System.out.println("Compiling `prime'");
  mosel.compile("prime.mos");

  System.out.println("Loading `prime'");
  mod = mosel.loadModel("prime.bim");

  System.out.println("Executing `prime'");
  mod.execParams = "LIMIT=" + LIM;
  mod.run();

  System.out.println("`prime' returned: " + mod.getResult());
 }
}

Using the Mosel Java interface, it is not only possible to compile and run models, but also to access information on the different modeling objects as is shown in the following sections.

Accessing sets

A complete version of a program (file ugparam.java) for running the model `Prime' may look as follows (we work with a model prime2 that corresponds to the one printed in Section Working with sets but with all output printing removed because we are doing this in Java, and all entities accessed from Java are explicitly declared as public):

import com.dashoptimization.*;

public class ugparam
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  XPRMSet set;
  int LIM=500, first, last;

  mosel = new XPRM();                   // Initialize Mosel

  System.out.println("Compiling `prime2'");
  mosel.compile("prime2.mos");

  System.out.println("Loading `prime2'");
  mod = mosel.loadModel("prime2.bim");

  System.out.println("Executing `prime2'");
  mod.execParams = "LIMIT=" + LIM;
  mod.run();

  System.out.println("`prime2' returned: " + mod.getResult());

  set=(XPRMSet)mod.findIdentifier("SPrime");  // Get the object 'SPrime'
                                              // it must be a set
  if(!set.isEmpty())
  {
   first = set.getFirstIndex();         // Get the number of the first index
   last = set.getLastIndex();           // Get the number of the last index
   System.out.println("Prime numbers from 2 to " + LIM);
   for(int i=first;i<=last;i++)         // Print all set elements
    System.out.print(" " + set.getAsInteger(i) + ",");
   System.out.println();
  }
 }
}

To print the contents of set SPrime that contains the desired result (prime numbers between 2 and 500), we retrieve the Mosel object of this name using method findIdentifier. If this set is not empty, then we enumerate the elements of the set (using methods getFirstIndex and getLastIndex to obtain the index range).

Retrieving solution values

The following program ugsol.java executes the model `Burglar3' (the same as model `Burglar2' from Chapter Some illustrative examples but with all output printing removed and all model entities declared as public) and prints out its solution.

import com.dashoptimization.*;

public class ugsol
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  XPRMArray varr, darr;
  XPRMMPVar x;
  XPRMSet set;
  int[] indices;
  double val;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar3.mos");      // Compile, load & run the model
  mod = mosel.loadModel("burglar3.bim");
  mod.run();

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

  System.out.println("Objective value: " + mod.getObjectiveValue());
                                      // Print the objective function value

  varr=(XPRMArray)mod.findIdentifier("take");  // Get model object 'take',
                                               // it must be an array
  darr=(XPRMArray)mod.findIdentifier("VALUE"); // Get model object 'VALUE',
                                               // it must be an array
  set=(XPRMSet)mod.findIdentifier("ITEMS");    // Get model object 'ITEMS',
                                               // it must be a set

  indices = varr.getFirstIndex();     // Get the first entry of array varr
                                      // (we know that the array is dense)
  do
  {
   x = varr.get(indices).asMPVar();   // Get a variable from varr
   val = darr.getAsReal(indices);     // Get the corresponding value
   System.out.println("take(" + set.get(indices[0]) + "): " +
                      x.getSolution() + "\t (item value: " + val + ")");
                                      // Print the solution value
  } while(varr.nextIndex(indices));   // Get the next index

  mod.reset();                        // Reset the model
 }
}

The array of variables varr is enumerated using the array functions getFirstIndex and nextIndex. These methods may be applied to arrays of any type and dimension. With these functions we run systematically through all possible combinations of index tuples, hence the hint at dense arrays in the example. In the case of sparse arrays it is preferrable to use different enumeration functions that only enumerate those entries that are defined (see next section).

Sparse arrays

We now again work with the problem `Transport' that has been introduced in Chapter More advanced modeling features. The objective of this problem is to calculate the flows flowpr from a set of plants to a set of sales regions that satisfy all demand and supply constraints and minimize the total cost. Not all plants may deliver goods to all regions. The flow variables flowpr are therefore defined as a sparse array. The following example ugarray.java prints out all existing entries of the array of variables.

import com.dashoptimization.*;

public class ugarray
{
 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  XPRMArray varr;
  XPRMSet[] sets;
  int[] indices;
  int dim;

  mosel = new XPRM();                  // Initialize Mosel

  mosel.compile("transport.mos");      // Compile, load & run the model
  mod = mosel.loadModel("transport.bim");
  mod.run();

  varr=(XPRMArray)mod.findIdentifier("flow"); // Get model object 'flow'
                                       // it must be an array
  dim = varr.getDimension();           // Get the number of dimensions
                                       // of the array
  sets = varr.getIndexSets();          // Get the indexing sets

  indices = varr.getFirstTEIndex();    // Get the first true entry index
  do
  {
   System.out.print("flow(");
   for(int i=0;i<dim-1;i++)
    System.out.print(sets[i].get(indices[i]) + ",");
   System.out.print(sets[dim-1].get(indices[dim-1]) + "), ");
  } while(varr.nextTEIndex(indices));  // Get next true entry index tuple
  System.out.println();

  mod.reset();                         // Reset the model
 }
}

In this example, we first get the number of indices (dimensions) of the array of variables varr (using method getDimension). We use this information to enumerate the entries of every index tuple for generating a nicely formatted output. The array sets holds all the index sets of varr and the array indices corresponds to a single index tuple.

The enumeration starts with the first defined index tuple, obtained with method getFirstTEIndex, and continues with a series of calls to nextTEIndex until all defined entries have been enumerated.

Exchanging data between an application and a model

In the previous examples we have seen how to retrieve information about the model objects from a Mosel model after its execution. In all cases the input data is defined in the model itself or read in from an external (text) file. However, when embedding a model into an application frequently the input data for the model will be stored (or generated by) the application itself. In such a case the user will certainly wish a more immediate means of communication to the model than having to write the input data to an external text file or database. In the following two subsections we therefore show how to pass data in memory from an application to a Mosel model, and with the same mechanism (namely, using the jraw I/O driver) from the model back to the calling application.

Dense arrays

As a first example we shall look at the case of dense arrays holding the input and solution data. In the underlying Mosel model this corresponds to arrays indexed by range sets that are known in the model before the data are read in. In this example, we shall work with a version of the `Burglar model based on the very first version we have seen in Section The burglar problem where all arrays are indexed by the range set ITEMS = 1..8.

The following Java program ugiodense.java compiles, loads, and runs a Mosel model and then prints out the solution values. The input data (arrays vdata and wdata) and the array solution that is to receive the solution values are passed on to the model through model parameters. Communication of the data between the application and the Mosel model is achieved through the jraw I/O driver. File names for this driver have the form jrawoption[,...],filename, where filename is an object reference. Since we are working with dense, one-dimensional arrays we use the option noindex, indicating that only the data and not the index tuples are to be exchanged.

import com.dashoptimization.*;

public class ugiodense
{                                     // Input data
 static final double[] vdata={15,100,90,60,40,15,10, 1};   // VALUE
 static final double[] wdata={ 2, 20,20,30,40,30,60,10};   // WEIGHT

                                      // Array to receive solution values
 static double[] solution = new double[8];

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar8.mos");      // Compile & load the model
  mod = mosel.loadModel("burglar8.bim");

                      // Associate the Java objects with names in Mosel
  mosel.bind("vdat", vdata);
  mosel.bind("wdat", wdata);
  mosel.bind("sol", solution);
                      // File names are passed through execution parameters
  mod.execParams =
   "VDATA='noindex,vdat',WDATA='noindex,wdat',SOL='noindex,sol'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                      // Display solution values obtained from the model
  System.out.println("Objective value: " + mod.getObjectiveValue());
  for(int i=0;i<8;i++)
   System.out.println(" take(" + (i+1) + "): " + solution[i]);

  mod.reset();                        // Reset the model
 }
} 

The model file burglar8.mos is the same as model burglar6.mos from Section Dense arrays with the only difference that the name of the I/O driver in the initializations blocks now is jraw instead of raw, such as:

 initializations from 'jraw:'
  VALUE as VDATA  WEIGHT as WDATA
 end-initializations 

Sparse arrays

Let us now study the probably more frequent case of data stored in sparse format. In the Mosel model (burglar9.mos) we use a set of strings instead of a simple range set to index the various arrays and in the Java program (ugiosparse.java) we need to define slightly more complicated structures to hold the indices and the data entries. To save us writing out the indices twice, we have grouped the two input data arrays into a single class. When passing the data arrays to the Mosel model we now do not use any option, meaning that data is transferred in sparse format. Instead, we now need to indicate which fields of the Java objects are to be selected (in brackets after the object reference).

import com.dashoptimization.*;

public class ugiosparse
{
                        // Class to store initial values for array 'data'
 public static class MyData
 {
  public String ind;                // index name
  public double val,wght;           // value and weight data entries
  MyData(String i, double v, double w)
  { ind=i; val=v; wght=w; }
 }
                        // Class to receive solution values
 public static class MySol
 {
  public String ind;                // index name
  public double val;                // solution value
 }

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  MyData data[]={new MyData("camera",15,2), new MyData("necklace",100,20),
                 new MyData("vase",90,20), new MyData("picture",60,30),
		 new MyData("tv",40,40), new MyData("video",15,30),
                 new MyData("chest",10,60), new MyData("brick",1,10)};
  MySol[] solution=new MySol[8];

  for(int i=0;i<8;i++) solution[i] = new MySol();

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar9.mos");      // Compile & load the model
  mod = mosel.loadModel("burglar9.bim");

                        // Associate the Java objects with names in Mosel
  mosel.bind("dt", data);
  mosel.bind("sol", solution);
                        // File names are passed through execution parameters
  mod.execParams = "DATA='dt(ind,val,wght)',SOL='sol(ind,val)'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                        // Display solution values obtained from the model
  System.out.println("Objective value: " + mod.getObjectiveValue());
  for(int i=0;i<8;i++)
   System.out.println(" take(" + solution[i].ind + "): " + solution[i].val);

  mod.reset();                        // Reset the model
 }
} 

The model burglar9.mos run by this program is the same as the model burglar7.mos displayed in Section Sparse arrays, but using the I/O driver jraw instead of raw.

Dynamic data

The two examples of in-memory communication of dense and sparse data in the preceding sections have in commun that all data structures in the application, and in particular the structures to receive output data, are of fixed size. We therefore now introduce an alternative communication mechanism working with streams, that enables dynamic sizing of data structures on the application level, a feature that is particularly useful for solution output where effective data sizes are not known a priori. This communication mechanism employs the I/O driver java (see also Section Redirecting the Mosel output). The main part of our Java program (file ugiocb.java) now looks as follows.

 public static modelInit cbinit=new modelInit();

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar13.mos");     // Compile & load the model
  mod = mosel.loadModel("burglar13.bim");

                        // File names are passed through execution parameters
  mod.execParams = "DATAFILE='java:ugiocb.cbinit'," +
                   "SOLFILE='java:ugiocb.cbinit'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                        // Display solution values obtained from the model
  System.out.println("Objective value: " + mod.getObjectiveValue());
  for(int i=0;i<solsize;i++)
   System.out.println(" take(" + solution[i].ind + "): " + solution[i].val);

  mod.reset();                        // Reset the model
 } 

The information passed to the model in the runtime parameters now is an instance of a class that implements interfaces for initialization from and to streams as shown below. The functionality for dynamic output retrieval employs the Mosel library functions that we have already seen in Sections Accessing sets and Retrieving solution values for accessing models after their termination. The dynamic data input to a Mosel model uses a new set of dedicated functions that are explained with some more detail after the program extract.

 static final double[] vdata={15,100,90,60,40,15,10, 1};   // VALUE
 static final double[] wdata={ 2, 20,20,30,40,30,60,10};   // WEIGHT
 static final String[] ind={"camera", "necklace", "vase", "picture",
     "tv", "video", "chest", "brick"};                     // Index names
 static final int datasize=8;

 public static class MySol {
  public String ind;                // index name
  public double val;                // solution value
 }
 static MySol[] solution;
 static int solsize;


 public static class modelInit implements XPRMInitializationFrom, XPRMInitializationTo
 {
  public boolean initializeTo(String label, XPRMValue value)
  {
   XPRMArray solarr;
   XPRMSet[] sets;
   int[] indices;
   int ct;

   if(label.equals("SOL"))
   {
    solarr=(XPRMArray)value;
    solsize=solarr.getSize();
    solution = new MySol[solsize];
    for(int i=0;i<solsize;i++) solution[i] = new MySol();

    sets = solarr.getIndexSets();          // Get the indexing sets
    ct=0;
    indices = solarr.getFirstTEIndex();    // Get the first entry of the array
    do
    {
     solution[ct].ind=sets[0].getAsString(indices[0]);
     solution[ct].val=solarr.getAsReal(indices);
     ct++;
    } while(solarr.nextTEIndex(indices));  // Get the next index
   }
   else System.out.println("Unknown output data item: " + label + "=" + value);
   return true;
  }

  public boolean initializeFrom(XPRMInitializeContext ictx, String label, XPRMTyped type)
  {
   try
   {
    if(label.equals("DATA"))
    {
     ictx.sendControl(ictx.CONTROL_OPENLST);
     for(int i=0;i<datasize;i++)
     {
      ictx.sendControl(ictx.CONTROL_OPENNDX);
       ictx.send(ind[i]);
      ictx.sendControl(ictx.CONTROL_CLOSENDX);
      ictx.sendControl(ictx.CONTROL_OPENLST);
       ictx.send(vdata[i]);
       ictx.send(wdata[i]);
      ictx.sendControl(ictx.CONTROL_CLOSELST);
     }
     ictx.sendControl(ictx.CONTROL_CLOSELST);
     return true;
    }
    else
    {
     System.err.println("Label `"+label+"' not found.");
     return false;
    }
   }
   catch(java.io.IOException e)
   {
    System.err.println("`"+label+"' could not be initialized - "+e);
    return false;
   }
  }
 } 

The format used to represent data for dynamic data input is the same as the default text format used by initializations blocks. For example, the array definition

 mydata: [ ("ind1" 3) [5 1.2] ("ind2" 7) [4 6.5] ] 

is represented by the following sequence of function calls:

ictx.sendControl(ictx.CONTROL_OPENLST);     ! [
ictx.sendControl(ictx.CONTROL_OPENNDX);     !   (
ictx.send("ind1");                          !     "ind1"
ictx.send(3);                               !     3
ictx.sendControl(ictx.CONTROL_CLOSENDX);    !   )
ictx.sendControl(ictx.CONTROL_OPENLST);     !   [
ictx.send(5);                               !     5
ictx.send(1.2);                             !     1.2
ictx.sendControl(ictx.CONTROL_CLOSELST);    !   ]
ictx.sendControl(ictx.CONTROL_OPENNDX);     !   (
ictx.send("ind2");                          !     "ind2"
ictx.send(7);                               !      7
ictx.sendControl(ictx.CONTROL_CLOSENDX);    !   )
ictx.sendControl(ictx.CONTROL_OPENLST);     !   [
ictx.send(4);                               !     4
ictx.send(6.5);                             !     6.5
ictx.sendControl(ictx.CONTROL_CLOSELST);    !   ]
ictx.sendControl(ictx.CONTROL_CLOSELST);    ! ]         

The send and sendControl methods may take an additional last argument indicating whether data is to be processed immediately or only once the queue of tokens is full (default).

With Java, we use exactly the same model file burglar13.mos as with C (see Section Dynamic data for the listing).

Scalars

Besides arrays one might also wish to simply exchange scalars between the calling application and a Mosel model. One way of passing the value of a scalar to a model is to define it as a model parameter and pass the new value as an execution parameter to the model (as shown in Section Parameters). Alternatively, we might read or write scalar values in initializations blocks similarly to what we have seen in the previous section for arrays.

Consider the following Java program: we wish to exchange the values of the three scalars, wmax, numitem, and objval with the Mosel model run by this program. The value of the first scalar should be read in by the Mosel model and the last two receive solution values from the optimization run in the model. Since it is not possible to address scalars directly from the model we have collected them into a class MyData the fields of which are then specified in the execution parameters as the locations of the data.

import com.dashoptimization.*;

public class ugioscalar
{
 public static class MyData            // Scalars for data in/output
 {
  public int wmax;
  public int numitem;
  public double objval;
 }

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  MyData data=new MyData();

  data.wmax=100;

  mosel = new XPRM();                 // Initialize Mosel

  mosel.compile("burglar11.mos");     // Compile & load the model
  mod = mosel.loadModel("burglar11.bim");

                     // Associate the Java object with a name in Mosel
  mosel.bind("data", data);
                     // File names are passed through execution parameters
  mod.execParams =
   "WMAX='data(wmax)',NUM='data(numitem)',SOLVAL='data(objval)'";

  mod.run();                          // Run the model

  if(mod.getProblemStatus()!=mod.PB_OPTIMAL)
   System.exit(1);                    // Stop if no solution found

                     // Display solution values obtained from the model
  System.out.println("Objective value: " + data.objval);
  System.out.println("Total number of items: " + data.numitem);

  mod.reset();                        // Reset the model
 }
} 

The Mosel model burglar11.mos run by this program is the same as the model burglar12.mos displayed in Section Scalars, but using the I/O driver jraw instead of raw. This model takes as execution parameters the filenames (location in memory) of the three scalars. The integer WTMAX is initialized from the value in the Java application and the two other locations are written to in the initializations to block at the end of the model.

Redirecting the Mosel output

When executing a Mosel model from a Java application it may be desirable to be able to process the output produced by Mosel directly in the application. The following Java program ugcb.java shows a callback-style functionality that redirects the Mosel standard output to an OutputStream object which is used to prefix every line of Mosel output with the string Mosel: before printing it.

To redirect Mosel streams to a Java object (Java streams or ByteBuffer) we need to use the java I/O driver. The same mechanism that is used here for redirecting the output stream of Mosel (indicated by XPRM.F_OUTPUT, with the additional option XPRM.F_LINBUF to enable line buffering) can equally be used to redirect, for instance, the error stream (denoted by the constant XPRM.F_ERROR).

import java.io.*;
import com.dashoptimization.*;

public class ugcb
{
                            // OutputStream class to handle default output
 public static class MyOut extends OutputStream
 {
  public void flush()
  { System.out.flush(); }
  public void write(byte[] b)
  {
   System.out.print("Mosel: ");
   System.out.write(b,0,b.length);
  }
  // These methods are not used by Mosel:
  public void write(byte[] b,int off,int len) {}
  public void write(int b) {}
  public void close() {}
 }

 public static void main(String[] args) throws Exception
 {
  XPRM mosel;
  XPRMModel mod;
  MyOut cbmsg = new MyOut();            // Define output stream as "MyOut"

  mosel = new XPRM();                   // Initialize Mosel

  mosel.bind("mycb", cbmsg);    // Associate Java object with a name in Mosel
                                // Set default output stream to cbmsg
  mosel.setDefaultStream(XPRM.F_OUTPUT|XPRM.F_LINBUF, "java:mycb");

  mosel.compile("burglar2.mos");        // Compile, load & run the model
  mod = mosel.loadModel("burglar2.bim");
  mod.run();
 }
} 

© 2001-2019 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.