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 master 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