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 modelThis 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-initializationsSparse 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 SubParameters
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 SubRedirecting 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.