The module mmjobs makes it possible to exchange information between models running
concurrently. Its functionality includes facilities for 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 whitepaperMultiple models and parallel solving with Moselthat is provided as a part of the Xpress documentation and also on the’Whitepapers’ pageof theXpress website. We show here how to use the basic
functionality for executing a model from a second model.
17.2.1
Running a model from another model
As a test case, we shall once more work with model prime.mos from Section8.2. 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 getvalueand 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.
17.2.2
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 Section17.1.
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).
17.2.3
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 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. 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 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(2) ! wait 2 seconds for an event
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 "raw:"
NumP as "shmem:NumP" SetP as "shmem: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: it needs to intercept the ‘STOPMOD’ event interrupting the calculations (via an additional test isqueueempty for the repeat-untilloop) 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 end-parameters
declarations
SNumbers: set of integer ! Set of numbers to be checked SPrime: set of integer ! Set of prime numbers
STOPMOD = 2 end-declarations
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 "raw:"
NumP as "shmem:NumP" SPrime as "shmem: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 Section15.2for an improved implementation of the prime number algorithm that considerably reduces its execution time.