Other programming language interfaces



In this chapter we show how the examples from Chapter C interface may be written with other programming languages, namely Java and Visual Basic.

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

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.

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

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 `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());

  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 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. 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 IO 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 IO 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 IO 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 IO driver jraw instead of raw.

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 IO 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();
 }
} 

Visual Basic

In Visual Basic, a Mosel model needs to be embedded into a project. In this section we shall only show the parts relevant to the Mosel functions, assuming that the execution of a model is trigged by the action of clicking on some object.

Compiling and executing a model in Visual Basic

As with the other programming languages, to execute a Mosel model in Visual Basic we need to perform the standard compile/load/run sequence as shown in the following example (contained in the file ugvb.frm). We use a slightly modified version burglar5.mos of the burglar problem where we have redirected the output printing to the file burglar_out.txt.

Private Sub burglar_Click()
  Dim model As Long
  Dim ret As Long
  Dim result As Long
    
'Initialize Mosel
  ret = XPRMinit
  If ret <> 0 Then
    MsgBox "Initialization error (" & ret & ")"
    Exit Sub
  End If

'Compile burglar5.mos
  ret = XPRMcompmod(vbNullString, "burglar5.mos", vbNullString, "Knapsack")
  If ret <> 0 Then
    MsgBox "Compile error (" & ret & ")"
    Exit Sub
  End If
  
'Load burglar5.bim
  model = XPRMloadmod("burglar5.bim", vbNullString)
  If model = 0 Then
    MsgBox "Error loading model"
    Exit Sub
  End If
  
'Run the model
  ret = XPRMrunmod(model, result, vbNullString)
  If ret <> 0 Then
    MsgBox "Execution error (" & ret & ")"
    Exit Sub
  End If
End Sub

Parameters

When executing a Mosel model in Visual Basic, it is possible to pass new values for its parameters into the model. The following program (also contained in the file ugvb.frm) extract shows how we may 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 use a slightly modified version prime4.mos of the model where we have redirected the output printing to the file prime_out.txt.

Private Sub prime_Click()
  Dim model As Long
  Dim ret As Long
  Dim result As Long    
    
'Initialize Mosel
  ret = XPRMinit
  If ret <> 0 Then
    MsgBox "Initialization error (" & ret & ")"
    Exit Sub
  End If

'Compile prime4.mos
  ret = XPRMcompmod(vbNullString, "prime4.mos", vbNullString, "Prime numbers")
  If ret <> 0 Then
    MsgBox "Compile error (" & ret & ")"
    Exit Sub
  End If
  
'Load prime4.bim
  model = XPRMloadmod("prime4.bim", vbNullString)
  If model = 0 Then
    MsgBox "Error loading model"
    Exit Sub
  End If
  
'Run model with new parameter settings
  ret = XPRMrunmod(model, result, "LIMIT=500")
  If ret <> 0 Then
    MsgBox "Execution error (" & ret & ")"
    Exit Sub
  End If
End Sub

Redirecting the Mosel output

In the previous example we have hardcorded the redirection of the output directly in the model. With Mosel's VB interface the user may also redirect all output produced by Mosel to files, that is, redirect the output stream.

To redirect all output of a model to the file myout.txt add the following function call before the execution of the Mosel model:

' Redirect all output to the file "myout.txt"
 XPRMsetdefstream vbNull, XPRM_F_OUTPUT, "myout.txt"

Similarly, any possible error messages produced by Mosel can be recovered by replacing in the line above XPRM_F_OUTPUT by XPRM_F_ERROR.This will redirect the error stream to the file myout.txt.

The following VB program extract (file ugcb.bas) shows how to use a callback in VB to receive all output from a Mosel model (standard output and errors). The output will be printed in the debug pane of the VB editor and in a window of the VB application, prefixing every line of Mosel output with the string Mosel:.

Public Sub example()
 Dim nReturn As Long
 Dim result As Long
    
' Initialize Mosel. Must be called first
 nReturn = XPRMinit
 If nReturn <> 0 Then
  PrintLn ("Failed to initialize Mosel")
  Exit Sub
 End If

' Redirect the output and error streams to the callback    
 nReturn = _
  XPRMsetdefstream(0, XPRM_F_WRITE, XPRM_IO_CB(AddressOf OutputCB))
 nReturn = _
  XPRMsetdefstream(0, XPRM_F_ERROR, XPRM_IO_CB(AddressOf OutputCB))

' Run the model    
 nReturn = XPRMexecmod(0, App.path & "\" & "burglar10.mos", _
                       "FULLPATH='" & App.path() & "'", result)
 If nReturn <> 0 Then
  PrintLn ("Failed to execute model")
  Exit Sub
 End If
    
 XPRMfree
End Sub


Public Sub OutputCB(ByVal model As Long, ByVal theref As Long, _
                    ByVal msg As String, ByVal size As Long)
  ' Output to the debug pane of the editor and our form
  Call PrintLn(msg)
End Sub

Public Sub PrintLn(ByVal text As String)
  ' Output to the debug pane of the editor and our form
  Debug.Print "Mosel: " & Mid(text, 1, Len(text) - 1)
  FormUGcb.messagepane.text = _
    FormUGcb.messagepane.text & "Mosel: " & text & vbCrLf
End Sub


If you have any comments or suggestions about these pages, please send mail to docs@dashoptimization.com.