Exchange of information with embedded models
Topics covered in this section:
- mem: working in memory
- raw: binary data format of the host application
- cb: output to callback functions
- sysfd: working with system file descriptors
In this section we show several examples of the use of I/O drivers in the Mosel distribution. The full documentation of these drivers is contained in the `Mosel Libraries Reference Manual'. When exchanging data between a Mosel model and a host application, the employed data structures need to match the representation of data in the particular host language and the choice of communication mechanisms (e.g., memory blocks, streams) depends on the functionality available in this language. Each Mosel library interface therefore provides its own I/O drivers, presented as subsections to the following examples that are organized around the functionality provided by Mosel's C libraries.
mem: working in memory
The mem I/O driver makes it possible to use a block of memory as a data source. The following example shows how to use this driver to compile a Mosel model to memory (instead of creating a physical BIM file). This means that the resulting application does not generate any auxiliary file for handling the model. This feature may be of interest especially in distributed systems.
In the code printed below and in all subsequent examples the error handling is omitted for clarity's sake. The full examples are on the Xpress website.
#include <stdio.h> #include "xprm_mc.h" int main() { XPRMmodel mod; int result; char bimfile[2000]; /* Buffer to store BIM file */ size_t bimfile_size; /* Buffer to store actual size of BIM file */ char bimfile_name[64]; /* File name of BIM file */ XPRMinit(); /* Initialize Mosel */ /* Prepare file name for compilation using 'mem' driver: */ /* "mem:base address/size[/actual size pointer]" */ bimfile_size=0; sprintf(bimfile_name, "mem:%p/%d/%p", bimfile, (int)sizeof(bimfile), &bimfile_size); /* Compile model file to memory */ XPRMcompmod(NULL, "burglar2.mos", bimfile_name, "Knapsack example"); printf("BIM file uses %lu bytes of memory.\n", bimfile_size); /* Load the BIM file from memory */ mod=XPRMloadmod(bimfile_name, NULL); XPRMrunmod(mod, &result, NULL); /* Run the model */ return 0; }
The only change to standard use of the Mosel library functions is the replacement of the (physical) BIM file by an extended filename in function XPRMcompmod. The extended filename indicates the driver we want to use (mem), an address where to store the BIM file, and the space reserved for it. Notice that the size of the resulting BIM file is not known before compilation. In this example we reserve roughly twice the size of the model file for the BIM file. However, there is no guarantee that this is sufficient for other models. We therefore print out the size of the generated file as a control.
We may take working in memory one step further, by including the source of the Mosel model directly in the C source code. After compilation, we obtain a single all-in-one application file that does not require or generate any auxiliary file for handling the model.
The source of the model is included in the C source file as an array of characters:
const char source_of_model[]= "model Burglar\n" "uses 'mmxprs'\n" "declarations\n" " WTMAX = 102 ! Maximum weight allowed\n" " ITEMS = {'camera', 'necklace', 'vase', 'picture', 'tv', 'video', \n" " 'chest', 'brick'} ! Index set for items\n" " VALUE: array(ITEMS) of real ! Value of items\n" " WEIGHT: array(ITEMS) of real ! Weight of items\n" " take: array(ITEMS) of mpvar ! 1 if we take item i; 0 otherwise\n" "end-declarations\n" "VALUE::(['camera', 'necklace', 'vase', 'picture', 'tv', 'video',\n" " 'chest', 'brick'])[15,100,90,60,40,15,10,1]\n" "WEIGHT::(['camera', 'necklace', 'vase', 'picture', 'tv', 'video',\n" " 'chest', 'brick'])[2,20,20,30,40,30,60,10]\n" "! Objective: maximize total value\n" "MaxVal:= sum(i in ITEMS) VALUE(i)*take(i)\n" "! Weight restriction\n" "sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX\n" "! All variables are 0/1\n" "forall(i in ITEMS) take(i) is_binary\n" "maximize(MaxVal) ! Solve the problem\n" "! Print out the solution\n" "writeln(\"Solution:\\n Objective: \", getobjval)\n" "forall(i in ITEMS) writeln(' take(', i, '): ', getsol(take(i)))\n" "end-model";
In the compilation command we replace the filename burglar2.mos by an extended filename, consisting in the driver to be used (mem) and the address and size of the model source.
char mosfile_name[40]; /* File name of MOS file */ sprintf(mosfile_name, "mem:%p/%d", source_of_model, (int)sizeof(source_of_model)); /* Compile model from memory to memory */ XPRMcompmod(NULL, mosfile_name, bimfile_name, "Knapsack example");
The data in this Mosel model are hardcoded directly in the model. In the following section we show how to extend this example with data input from memory, using the raw driver.
Java version of the example
The Java implementation of the model version compiling a Mosel model held in memory to a BIM file that is equally stored in memory uses objects of type ByteBuffer to communicate with Mosel.
// The source of the model as a string static final String source_of_model= "model Burglar\n"+ "uses 'mmxprs'\n"+ ... "end-model"; public static void main(String[] args) throws Exception { XPRM mosel; XPRMModel mod; ByteBuffer mosfile; // Buffer to store source file ByteBuffer bimfile; // Buffer to store BIM file mosel = new XPRM(); // Initialize Mosel // Prepare file names for compilation: // Wrap source in a byte buffer mosfile=ByteBuffer.wrap(source_of_model.getBytes()); bimfile=ByteBuffer.allocateDirect(2048); // Create a 2K byte buffer mosel.bind("mybim", bimfile); // Associate Java object mosel.bind("modelfile", mosfile); // with a name in Mosel try { // Compile model from memory to memory mosel.compile("", "java:modelfile", "java:mybim", ""); } catch(XPRMCompileException e) { System.out.println(e.getMessage()); } mosel.unbind("mosfile"); // Release memory mosfile=null; bimfile.limit(bimfile.position()); // Mark end of data in the buffer bimfile.rewind(); // Back to the beginning System.out.println("BIM file uses "+bimfile.limit()+" bytes of memory."); mod=mosel.loadModel("java:mybim"); // Load BIM file from memory mosel.unbind("mybim"); // Release memory bimfile=null; mod.run(); // Run the model }
.NET version of the example
The .NET interface defines a combined compile+load method that keeps a temporary BIM file in memory. We can therefore simply write the following program to compile a Mosel model from and to memory.
// String containing the model const string modelSource= "model Burglar\n"+ "uses 'mmxprs'\n"+ ... "end-model"; // Main entry point for the application static void Main(string[] args) { // Initialize Mosel XPRM mosel = XPRM.Init(); // Compile and load the Mosel model XPRMModel model = mosel.CompileAndLoad(new StringReader(modelSource)); // Run the model model.Run(); }
If however, we explicitly wish to create a physical BIM file we can use the separate Compile and LoadModel methods:
static void Main(string[] args) { // Initialize Mosel XPRM mosel = XPRM.Init(); // Compile the Mosel model to a physical file FileStream file= new FileStream("burglar2.bim", FileMode.Create, FileAccess.Write); mosel.Compile("", new StringReader(modelSource), file); file.Close(); // Load the Mosel model XPRMModel model = mosel.LoadModel("burglar2.bim"); // Run the model model.Run(); }
mmjobs version of the example
An alternative form of 'embedding' a Mosel model is running a (sub)model from another Mosel model (the parent or main model). The functionality for managing and coordinating multiple models is provided by the module mmjobs (documented in the chapter `mmjobs' of the `Mosel Language Reference Manual'). The reader is referred to the Mosel whitepaper `Multiple models and parallel solving with Mosel' for a comprehensive set of examples. With mmjobs we use the driver shmem for saving data in shared memory (memory accessible to models running in the same Mosel instance).
model "Run model burglar" uses "mmjobs" declarations modBurg: Model ! Submodel end-declarations ! Compile the model, save bim in memory if compile("", "burglar2.mos", "shmem:burglar2.bim")<>0 then exit(1); end-if load(modBurg, "shmem:burglar2.bim") ! Load the bim file run(modBurg) ! Start model execution wait ! Wait for model termination dropnextevent ! Ignore termination event message end-model
raw: binary data format of the host application
The raw I/O driver provides an implementation of the initializations block in binary mode: instead of translating information from/to text format, data is kept in its raw representation. The following example shows how to use this driver in combination with the mem driver to exchange arrays of data between a model and an application through memory. As shown in the previous section, we may work with a separate model file (this example) or alternatively include the model source directly in the model.
#include <stdio.h> #include "xprm_mc.h" const struct { /* Initial values for array 'data': */ const char *ind; /* index name */ double val,wght; /* value and weight data entries */ } data[]={{"camera",15,2}, {"necklace",100,20}, {"vase",90,20}, {"picture",60,30}, {"tv",40,40}, {"video",15,30}, {"chest",10,60}, {"brick",1,10}}; double solution[8]; /* Array for solution values */ int main() { XPRMmodel mod; int result; XPRMalltypes rvalue, itemname; XPRMset set; char bimfile[2000]; /* Buffer to store BIM file */ size_t bimfile_size; /* Buffer to store actual size of BIM file */ char bimfile_name[64]; /* File name of BIM file */ char data_name[40]; /* File name of initial values for 'data' */ char solution_name[40]; /* File name of solution values */ char params[96]; /* Parameter string for model execution */ XPRMinit(); /* Initialize Mosel */ /* Prepare file name for compilation using 'mem' driver: */ /* "mem:base address/size[/actual size pointer]" */ bimfile_size=0; sprintf(bimfile_name, "mem:%p/%d/%p", bimfile, (int)sizeof(bimfile), &bimfile_size); /* Compile model file to memory */ XPRMcompmod(NULL, "burglar2r.mos", bimfile_name, "Knapsack example"); printf("BIM file uses %lu bytes of memory.\n", bimfile_size); /* Load a BIM file from memory */ mod=XPRMloadmod(bimfile_name, NULL); /* Prepare file names for 'initializations' using the 'raw' driver: */ /* "rawoption[,...],filename" */ /* (Here, 'filename' uses the 'mem' driver, data is stored in memory) */ /* Options for 'raw': */ /* 'slength=0': strings are represented by pointers to null terminated */ /* arrays of characters (C-string) instead of fixed size arrays*/ /* 'noindex': only array values are expected - no indices requested */ sprintf(data_name, "slength=0,mem:%p/%d", data, (int)sizeof(data)); sprintf(solution_name, "noindex,mem:%p/%d", solution, (int)sizeof(solution)); /* Pass file names as execution param.s */ sprintf(params, "DATA='%s',SOL='%s'", data_name, solution_name); XPRMrunmod(mod, &result, params); /* Run the model */ /* Display solutions values obtained from the model */ printf("Objective: %g\n", XPRMgetobjval(mod)); XPRMfindident(mod, "ITEMS", &rvalue); /* Get the model object 'ITEMS' */ set = rvalue.set; for(i=0;i<8;i++) printf(" take(%s): %g\n", XPRMgetelsetval(set, i+1, &itemname)->string, solution[i]); return 0; }
The input data in the C code above is stored and communicated to the model in sparse format, that is, each data item with its index tuple. For retrieving and saving the solution data this example uses dense format, that is, just the data items without explicitly specifying the indices. To obtain a printout with the (string) index names, we retrieve separately the set of indices from the Mosel model. Alternatively, we could also use sparse format for the solution data similarly to the input data, in which case we did not have to retrieve the index set separately.
The model file burglar2r.mos of this example is very similar to the model versions for the odbc and diskdata I/O drivers in Section Data source access from Mosel models. The only changes made are to the name of the I/O driver and to the strings DATA and SOL for the data source and the destination of results respectively. Furthermore, the set ITEMS that is accessed from the C program needs to be declared as public.
model Burglar2r uses 'mmxprs' parameters IODRV = 'raw:' DATA='' SOL='' WTMAX = 102 ! Maximum weight allowed end-parameters declarations public ITEMS: set of string ! Index set for items VALUE: array(ITEMS) of real ! Value of items WEIGHT: array(ITEMS) of real ! Weight of items SOLTAKE: array(ITEMS) of real ! Solution values end-declarations initialisations from IODRV [VALUE,WEIGHT] as DATA end-initialisations declarations take: array(ITEMS) of mpvar ! 1 if we take item i; 0 otherwise end-declarations ! Objective: maximize total value MaxVal:= sum(i in ITEMS) VALUE(i)*take(i) ! Weight restriction sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX ! All variables are 0/1 forall(i in ITEMS) take(i) is_binary maximize(MaxVal) ! Solve the problem ! Solution output forall(i in ITEMS) SOLTAKE(i):= getsol(take(i)) initialisations to IODRV SOLTAKE as SOL end-initialisations end-model
The example above uses sparse data format, that is, every data entry comprises its index (or index tuple) and the actual value (or list of values in our case). A model version using dense data format, so communicating just the data values without the indices, is described in Chapter 13.4.1 `Exchanging data between an application and a modelDense arrays' of the `Mosel User Guide'.
The raw format communicates data via fixed-size memory blocks. Greater flexibility through dynamic sizing of the data structures can be achieved by using the callback functionality provided through the driver cb (see Section cb: output to callback functions below and the examples of Chapters 13.4.3 and 14.1.7.3 `Exchanging data between an application and a model Dynamic data' of the `Mosel User Guide' for C and Java examples respectively).
Java version of the example
With the Mosel Java libraries, we replace the C driver raw by the Java binary format I/O driver jraw. Just as in the C version, we have stored the model data in a single structure containing the string index and the two data values for 'weight' and 'value' of every item. Mosel returns the solution into a second structure that also stores index and data values.
// 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("burglar2r.mos"); // Compile & load the model mod = mosel.loadModel("burglar2r.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)',IODRV='jraw:'"; 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 }
A model version using dense data format is discussed in Chapter 14.1.7.1 `Exchanging data between an application and a modelDense arrays' of the `Mosel User Guide'.
.NET version of the example
The .NET version of our program is again very similar to its Java version. We now use the I/O driver dotnetraw in the place of jraw.
// Arrays containing initialization data for the model static double[] vdata = new double[] {15,100,90,60,40,15,10, 1}; // VALUE static double[] wdata = new double[] { 2, 20,20,30,40,30,60,10}; // WEIGHT // Structure to store initial values for the array 'data' class MyData { public string ind; public double val; public double wght; public MyData(string i, double v, double w) { this.ind = i; this.val = v; this.wght = w; } } // Structure to receive solution values class MySol { public string ind; public double val; } // The initial values for the array 'data' private static MyData[] data = new MyData[] { 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) }; // Main entry point for the application static void Main(string[] args) { // Initialize Mosel XPRM mosel = XPRM.Init(); // Compile and load the Mosel model XPRMModel model = mosel.CompileAndLoad("burglar2r.mos"); // Associate the .NET object with a name in Mosel model.Bind("dt", data); // Create a new array for solution data and bind that to the name 'SOL' MySol[] solution=new MySol[8]; for(int i=0;i<8;i++) solution[i] = new MySol(); mosel.Bind("sol", solution); // Pass data location as a parameter to the model model.ExecParams = "DATA='dt(ind,val,wght)',SOL='sol(ind,val)',IODRV='dotnetraw:'"; // Run the model model.Run(); // Print the solution Console.WriteLine("Objective value: {0}", model.ObjectiveValue); for(int i=0;i<8;i++) Console.Write(" take({0}): {1}", solution[i].ind, solution[i].val); Console.WriteLine(); }
mmjobs version of the example
When running a Mosel model from another model, the two models can exchange data in memory using the binary format defined by raw. Given the restrictions of the raw format (each 'file' only contains a single data array, composite Mosel data structures are not supported) we recommend however to give preference to the more flexible binary format defined by the bin driver that supports the full set of data structures of initializations blocks (see Section bin: using Mosel's binary format below). Here is an example of a Mosel model reading and writing data to memory in binary format using bin.
model "Run model burglar IO" uses "mmjobs" declarations modBurg: Model ! Submodel ISet,SSet: set of string ! Index set for data arrays V,W: array(ISet) of real ! Data arrays Solution: array(SSet) of real ! Solution values end-declarations V:: (["camera","necklace","vase","picture","tv","video","chest","brick"]) [15, 100, 90, 60, 40, 15, 10, 1] W:: (["camera","necklace","vase","picture","tv","video","chest","brick"]) [ 2, 20, 20, 30, 40, 30, 60, 10] ! Compile the model, save bim in memory if compile("burglar2r.mos")<>0 then exit(1); end-if load(modBurg, "burglar2r.bim") ! Load the bim file setdefstream(modBurg, F_OUTPUT, "null:") ! Disable output from submodel ! Save data in shared memory IODRV:="bin:shmem:burgdata" initializations to IODRV [V,W] as "Burgdata" end-initializations ! Start model execution, setting parameters run(modBurg, "IODRV='" +IODRV + "',DATA='Burgdata',SOL='Solution'") wait ! Wait for model termination dropnextevent ! Ignore termination event message ! Retrieve solution from shared memory initializations from IODRV Solution end-initializations forall(i in SSet) writeln(" take(", i, "): ", Solution(i)) end-model
The relevant part of the model above look as follows with the raw I/O driver.
! Save data in shared memory IODRV:="raw:" initializations to IODRV [V,W] as "shmem:Burgdata" end-initializations ! Start model execution, setting parameters run(modBurg, "IODRV='" +IODRV + "',DATA='shmem:Burgdata',SOL='shmem:SolTake'") wait ! Wait for model termination dropnextevent ! Ignore termination event message ! Retrieve solution from shared memory initializations from IODRV Solution as "shmem:SolTake" end-initializations
cb: output to callback functions
Using the cb I/O driver a function is used as a file. Depending on the type of stream to manage (i.e. a general stream or a stream for an initializations block) a specific function type has to be provided. In the following example we define a callback function to receive the output stream. Our function simply prints all output lines preceeded by Mosel: .
#include <stdio.h> #include "xprm_mc.h" /* Callback function to handle output */ long XPRM_RTC cbmsg(XPRMmodel model, void *info, char *buf, unsigned long size) { /* Note: 'model' is NULL if the stream is used outside of an execution */ printf("Mosel: %.*s", (int)size, buf); return 0; } int main() { XPRMmodel mod; int result; char outfile_name[40]; /* File name of output stream */ XPRMinit(); /* Initialize Mosel */ /* Prepare file name for output stream */ /* using 'cb' driver: */ /* "cb:function pointer[/callback data]" */ sprintf(outfile_name, "cb:%p", cbmsg); /* Set default output stream to callback */ XPRMsetdefstream(NULL, XPRM_F_WRITE, outfile_name); /* Execute = compile/load/run model file `burglar2.mos' Generates the BIM file `burglar2.bim' */ XPRMexecmod(NULL, "burglar2.mos", NULL, &result, NULL); return 0; }
The output produced by this program looks as follows:
Mosel: Solution: Mosel: Objective: 280 Mosel: [(`camera',1),(`necklace',1),(`vase',1),(`picture',1),(`tv',0), (`video',1),(`chest',0),(`brick',0)]
Callback functionality can also be used for data I/O in initializations blocks—see Chapters 13.4.3 and 14.1.7.3 (Exchanging data between an application and a model – Dynamic data) of the `Mosel User Guide' for C and Java examples respectively.
Java version of the example
The stream redirections functions of the Mosel Java library expect objects of the type InputStream or OutputStream. The following Java program implements the same behavior as the C program above. With Java, we replace the cb driver by the Java memory driver java.
// 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); } // The following 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(); }
.NET version of the example
With the Mosel .NET libraries, the stream redirection functions accept the objects TextWriter and TextReader in addition to strings with Mosel file names. We therefore have several different implementation options for our example.
The following version of our example closely matches the structure of the Java program from the previous section—it makes use of the dotnet I/O driver for redirecting the model output to a custom TextWriter object:
static void Main(string[] args) { // Initialize Mosel XPRM mosel = XPRM.Init(); // Associate .NET object with a name in Mosel mosel.Bind("mycb", new MyOut()); // Compile and load the Mosel model XPRMModel model = mosel.CompileAndLoad("burglar2.mos"); // Redirect the model's output to our printing function 'cbmsg' model.SetDefaultStream(XPRMStreamType.F_OUTPUT_LINEBUF, "dotnet:mycb"); // Run the model model.Run(); }
Alternatively, we can simply use this form of our program (without employing any I/O driver):
static void Main(string[] args) { // Initialize Mosel XPRM mosel = XPRM.Init(); // Compile and load the Mosel model XPRMModel model = mosel.CompileAndLoad("burglar2.mos"); // Redirect the model's output to a custom TextWriter MyOut modelOut = new MyOut(); model.SetDefaultStream(XPRMStreamType.F_OUTPUT_LINEBUF, modelOut); // Run the model model.Run(); }
In both cases, we work with the following definition for the class MyOut:
public class MyOut: TextWriter { private bool atStartOfLine = true; public override void Write(char b) { if (atStartOfLine) { Console.Write("Mosel: "); atStartOfLine=false; } if (b=='\n') { Console.WriteLine(); atStartOfLine=true; } else if (b=='\r') { // ignore } else { Console.Write(b); } } public override Encoding Encoding { get { return Encoding.UTF8; } } }
sysfd: working with system file descriptors
With the sysfd driver a file descriptor provided by the operating system can be used in place of a file. File descriptors are returned by the C functions open or fileno. They also include the default input, output, and error streams (values 0, 1, and 2 respectively) that are opened automatically when a program starts. For instance, to redirect the error stream for a model mod to the default output we may write:
XPRMsetdefstream(mod, XPRM_F_ERROR, "sysfd:1");
Java version of the example
In Java, we use the driver java to indicate redirection to a system stream.
mod.setDefaultStream(XPRM.F_ERROR, "java:java.lang.System.out");
.NET version of the example
With .NET we can immediately specify an output location, such as
model.SetDefaultStream(XPRMStreamType.F_ERROR, Console.Out);
© 2001-2025 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.