Multiple models and parallel solving with mmjobs
The module mmjobs makes it possible to exchange information between models running concurrently—locally or in a network. Its functionality includes facilities for handling Mosel instances (e.g. connecting and disconnecting Mosel instances, access to remote files), model management (e.g. compiling, running, or interrupting a model from within a second model), synchronization of concurrent models based on event queues, and a shared memory IO driver for an efficient exchange of data between models that are executed concurrently.
Several complete examples (including examples of Benders decomposition and Dantzig-Wolfe decomposition) of the use of module mmjobs are described in the whitepaper Multiple models and parallel solving with Mosel that is provided as a part of the Xpress documentation and also on the 'Product Documentation' page of the Xpress website. We show here how to use the basic functionality for executing a model from a second model.
Running a model from another model
As a test case, we shall once more work with model prime.mos from Section Working with sets. In the first instance, we now show how to compile and run this model from a second model, runprime.mos:
model "Run model prime"
uses "mmjobs"
declarations
modPrime: Model
event: Event
end-declarations
! Compile 'prime.mos'
if compile("prime.mos")<>0 then exit(1); end-if
load(modPrime, "prime.bim") ! Load bim file
run(modPrime, "LIMIT=50000") ! Start execution and
wait(2) ! wait 2 seconds for an event
if isqueueempty then ! No event has been sent...
writeln("Model too slow: stopping it!")
stop(modPrime) ! ... stop the model,
wait ! ... and wait for the termination event
end-if
! An event is available: model finished
event:=getnextevent
writeln("Exit status: ", getvalue(event))
writeln("Exit code : ", getexitcode(modPrime))
unload(modPrime) ! Unload the submodel
end-model The compile command generates the BIM file for the given submodel; the command load loads the binary file into Mosel; and finally we start the model with the command run. The run command is not used in its basic version (single argument with the model reference): here its second argument sets a new value for the parameter LIMIT of the submodel.
In addition to the standard compile–load–run sequence, the model above shows some basic features of interaction with the submodel: if the submodel has not terminated after 2 seconds (that is, if it has not sent a termination message) it is stopped by the master model. After termination of the submodel (either by finishing its calculations within less than 2 seconds or stopped by the master model) its termination status and the exit value are retrieved (functions getvalue and getexitcode). Unloading a submodel explicitly as shown here is only really necessary in larger applications that continue after the termination of the submodel, so as to free the memory used by it.
Note: our example model shows an important property of submodels—they are running in parallel to the master model and also to any other submodels that may have been started from the master model. It is therefore essential to insert wait at appropriate places to coordinate the execution of the different models.
Compiling to memory
The model shown in the previous section compiles and runs a submodel. The default compilation of a Mosel file filename.mos generates a binary model file filename.bim. To avoid the generation of physical BIM files for submodels we may compile the submodel to memory, making use of the concept of I/O drivers introduced in Section Generalized file handling.
Compiling a submodel to memory is done by replacing the standard compile and load commands by the following lines (model runprime2.mos):
if compile("","prime.mos","shmem:bim")<>0 then
exit(1)
end-if
load(modPrime,"shmem:bim") ! Load bim file from memory...
fdelete("shmem:bim") ! ... and release the memory block The full version of compile takes three arguments: the compilation flags (e.g., use "g" for debugging), the model file name, and the output file name (here a label prefixed by the name of the shared memory driver). Having loaded the model we may free the memory used by the compiled model with a call to fdelete (this subroutine is provided by the module mmsystem).
Exchanging data between models
When working with submodels we are usually not just interested in executing the submodels, we also wish to retrieve their results in the master model. This is done most efficiently by exchanging data in (shared) memory as shown in the model runprimeio.mos below. Besides the retrieval and printout of the solution we have replaced the call to stop by sending the user event `STOPMOD' to the submodel: instead of simply terminating the submodel this event will make it interrupt its calculations and write out the current solution. To make sure that the submodel is actually running at the point where we sent the `STOPMOD' event, we have also introduced an event sent from the submodel to the master to indicate the point of time when it starts the calculations (with heavy operating system loads the actual submodel start may be delayed). Once the submodel has terminated (after sending the `STOPMOD' event we wait for the model's termination message) we may read its solution from memory, using the initializations block with the drivers raw (binary format) and shmem (read from shared memory).
model "Run model primeio"
uses "mmjobs"
declarations
modPrime: Model
NumP: integer ! Number of prime numbers found
SetP: set of integer ! Set of prime numbers
STOPMOD = 2 ! "Stop submodel" user event
MODREADY = 3 ! "Submodel ready" user event
end-declarations
! Compile 'prime.mos'
if compile("primeio.mos")<>0 then exit(1); end-if
load(modPrime, "primeio.bim") ! Load bim file
! Disable model output
setdefstream(modPrime,"","null:","null:")
run(modPrime, "LIMIT=35000") ! Start execution and
wait ! ... wait for an event
if getclass(getnextevent) <> MODREADY then
writeln("Problem with submodel run")
exit(1)
end-if
wait(2) ! Let the submodel run for 2 seconds
if isqueueempty then ! No event has been sent...
writeln("Model too slow: stopping it!")
send(modPrime, STOPMOD, 0) ! ... stop the model, then wait
wait
end-if
initializations from "bin:shmem:resdata"
NumP SetP as "SPrime"
end-initializations
writeln(SetP) ! Output the result
writeln(" (", NumP, " prime numbers.)")
unload(modPrime)
end-model We now have to modify the submodel (file primeio.mos) correspondingly: at its start it sends the `MODREADY' event to trigger the start of the time measurement in the master and it further needs to intercept the `STOPMOD' event interrupting the calculations (via an additional test isqueueempty for the repeat-until loop) and write out the solution to memory in the end:
model "Prime IO"
uses "mmjobs"
parameters
LIMIT=100 ! Search for prime numbers in 2..LIMIT
OUTPUTFILE = "bin:shmem:resdata" ! Location for output data
end-parameters
declarations
SNumbers: set of integer ! Set of numbers to be checked
SPrime: set of integer ! Set of prime numbers
MODREADY = 3 ! "Submodel ready" user event
end-declarations
send(MODREADY,0) ! Send "model ready" event
SNumbers:={2..LIMIT}
writeln("Prime numbers between 2 and ", LIMIT, ":")
n:=2
repeat
while (not(n in SNumbers)) n+=1
SPrime += {n} ! n is a prime number
i:=n
while (i<=LIMIT) do ! Remove n and all its multiples
SNumbers-= {i}
i+=n
end-do
until (SNumbers={} or not isqueueempty)
NumP:= getsize(SPrime)
initializations to OUTPUTFILE
NumP SPrime
end-initializations
end-model Note: since the condition isqueueempty is tested only once per iteration of the repeat-until loop, the termination of the submodel is not immediate for large values of LIMIT. If you wish to run this model with very large values, please see Section Efficient modeling through the Mosel Profiler for an improved implementation of the prime number algorithm that considerably reduces its execution time.
Distributed computing
The module mmjobs not only allows the user to start several models in parallel on a given machine, it also makes it possible to execute models remotely and to coordinate their processing. With only few additions, the model from Section Running a model from another model is extended to form model version runprimedistr.mos that launches the submodel prime.mos on another Mosel instance (either on the local machine as in the present example or on some other machine within a network specified by its name or IP address in the connect statement): we need to create a new Mosel instance (through a call to connect) and add an additional argument to the load statement to specify the Mosel instance we wish to use. All else remains the same as in the single-instance version.
model "Run model prime remotely"
uses "mmjobs"
declarations
moselInst: Mosel
modPrime: Model
event: Event
end-declarations
! Compile 'prime.mos' locally
if compile("prime.mos")<>0 then exit(1); end-if
! Start a remote Mosel instance:
! "" means the node running this model
if connect(moselInst, "")<>0 then exit(2); end-if
! Load bim file into remote instance
load(moselInst, modPrime, "rmt:prime.bim")
run(modPrime, "LIMIT=50000") ! Start execution and
wait(2) ! wait 2 seconds for an event
if isqueueempty then ! No event has been sent...
writeln("Model too slow: stopping it!")
stop(modPrime) ! ... stop the model, then wait
wait
end-if
! An event is available: model finished
event:=getnextevent
writeln("Exit status: ", getvalue(event))
writeln("Exit code : ", getexitcode(modPrime))
unload(modPrime) ! Unload the submodel
end-model This model can be extended to include data exchange between the master and the submodel exactly in the same way as in the example of Section Exchanging data between models. The main difference (besides the connection to a remote instance) lies in the use of the driver rmt to denote the Mosel instance where data is to be saved. In our case, we wish to save data on the instance running the master model, meaning that we need to use the rmt: prefix when writing output within the submodel. The new output file name is passed into the submodel via the runtime parameter OUTPUTFILE:
run(modPrime, "LIMIT=35000,OUTPUTFILE=bin:rmt:shmem:resdata")
The master model then simply reads as before from its own instance:
initializations from "bin:shmem:resdata" NumP SetP as "SPrime" end-initializations
The Mosel model for the extended version including data exchange is provided in the file runprimeiodistr.mos.
© 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.
