Working with data
In this chapter we introduce some basic data handling facilities of Mosel:
- the initializations block for reading and writing data in Mosel-specific format,
- data output to a file in free format,
- parameterization of files names and numerical constants, and
- some output formatting.
Data input from file
With Mosel, there are several different ways of reading and writing data from and to external files. For simplicity's sake we shall limit the discussion here to files in text format. Mosel also provides specific modules to exchange data with spreadsheets and databases, for instance using an ODBC connection, but this is beyond the scope of this book and the interested reader is refered to the documentation of these modules (see the Mosel Language Reference Manual and the whitepaper Using ODBC and other database interfaces with Mosel).
The data file folio.dat that we are going to work with has the following contents:
! Data file for `folio*.mos' RET: [("treasury") 5 ("hardware") 17 ("theater") 26 ("telecom") 12 ("brewery") 8 ("highways") 9 ("cars") 7 ("bank") 6 ("software") 31 ("electronics") 21 ] RISK: ["hardware" "theater" "telecom" "software" "electronics"] NA: ["treasury" "hardware" "theater" "telecom"]
Just as in model files, single-line comments preceded by ! may be used in data files. Every data entry is labeled with the name given to the corresponding entity in the model. Data items may be separated by blanks, tabulations, line breaks, or commas.
We modify the Mosel model from Chapter 3 as follows:
declarations SHARES: set of string ! Set of shares RISK: set of string ! Set of high-risk values among shares NA: set of string ! Set of shares issued in N.-America RET: array(SHARES) of real ! Estimated return in investment end-declarations initializations from "folio.dat" RISK RET NA end-initializations declarations frac: array(SHARES) of mpvar ! Fraction of capital used per share end-declarations
As opposed to the previous model foliolp.mos, all index sets and the data array are now created as dynamic objects: their size is not known at their creation and they are initialized later with data from the file folio.dat. Optionally, after the initialization from file, we may finalize the sets to make them static. This will make more efficient the handling of any arrays declared later on and indexed by these sets, and more importantly, this allows Mosel to check for `out of range' errors that cannot be detected if the sets are dynamic.
finalize(SHARES); finalize(RISK); finalize(NA)
Notice that we do not initialize explicitly the set SHARES, it is filled automatically when the array RET is read. Notice further that we only declare the decision variables after initializing the data, and hence when their index set is known.
Formated data output to file
Just like initializations from in the previous section, initializations to also exists in Mosel to write out data in a standardized format. However, if we wish to redirect to a file exactly the text that is currently displayed in the logging window of Workbench, then we simply need to surround the printing of this text by calls to the procedures fopen and fclose:
fopen("result.dat", F_OUTPUT) writeln("Total return: ", getobjval) forall(s in SHARES) writeln(s, ": ", getsol(frac(s))*100, "%") fclose(F_OUTPUT)
The first argument of fopen is the name of the output file, the second indicates in which mode to open it: with the settings shown above, at every re-execution of the model the contents of the result file will be replaced. To append the new output to the existing file contents use:
fopen("result.dat", F_OUTPUT+F_APPEND)
We may now also wish to format the output more nicely, for instance:
forall(s in SHARES) writeln(strfmt(s,-12), ": \t", strfmt(getsol(frac(s))*100,5,2), "%")
The function strfmt indicates the minimum space reserved for printing a string or a number. A negative value for its second argument means left-justified printing. The optional third argument denotes the number of digits after the decimal point. With this formated way of printing the result file has the following contents:
Total return: 14.0667 treasury : 30.00% hardware : 0.00% theater : 20.00% telecom : 0.00% brewery : 6.67% highways : 30.00% cars : 0.00% bank : 0.00% software : 13.33% electronics : 0.00%
Parameters
It is commonly considered a good modeling style to hard-code as little information as possible directly in a model. Instead, parameters and data should be specified and read from external sources during the execution of a model to make it more versatile and easily re-usable. With Mosel it is therefore possible to define, for example, file names and numerical constants in the form of parameters the values of which may be modified at an execution without changing the model itself.
In our example, we may define the input and output file as parameters and also the constant terms (`right hand side' values) of the constraints and bounds. These parameter definitions must be added to the beginning of the model file, immediately after the uses statement:
parameters DATAFILE= "folio.dat" ! File with problem data OUTFILE= "result.dat" ! Output file MAXRISK = 1/3 ! Max. investment into high-risk values MAXVAL = 0.3 ! Max. investment per share MINAM = 0.5 ! Min. investment into N.-American values end-parameters
and in the rest of the model the actual file names and data values are replaced by the parameters.
To modify the settings of these parameters when executing a model with Workbench, enter the new values for the parameters after the filename in the Command input box of the output pane. For instance to change the value of MINAM:

Figure 5.1: Changing model parameter settings
Notice that parameters really become important when the model is not just run in the development environment Workbench but rather used for testing and experimentation (batch mode, scripts using the command line interface) and for final deployment (see Chapter 8). For example, we may wish to write a batch file that runs our model foliodata.mos repeatedly with different parameter settings, and writes out the results each time to a different file. To do so, we simply need to add the following lines to a batch file (we then use the standalone version of Mosel to execute the model, which is invoked with the command mosel):
mosel exec foliodata MAXRISK=0.1 OUTFILE='result1.dat' mosel exec foliodata MAXRISK=0.2 OUTFILE='result2.dat' mosel exec foliodata MAXRISK=0.3 OUTFILE='result3.dat' mosel exec foliodata MAXRISK=0.4 OUTFILE='result4.dat'
Another advantage of the use of parameters is that if models are distributed as BIM files (portable, compiled BInary Model files), then they remain parameterizable, without having to disclose the model itself and hence protecting your intellectual property.
Complete example
The complete model file foliodata.mos with all the features discussed in this chapter looks as follows:
model "Portfolio optimization with LP" uses "mmxprs" ! Use Xpress Optimizer parameters DATAFILE= "folio.dat" ! File with problem data OUTFILE= "result.dat" ! Output file MAXRISK = 1/3 ! Max. investment into high-risk values MAXVAL = 0.3 ! Max. investment per share MINAM = 0.5 ! Min. investment into N.-American values end-parameters declarations SHARES: set of string ! Set of shares RISK: set of string ! Set of high-risk values among shares NA: set of string ! Set of shares issued in N.-America RET: array(SHARES) of real ! Estimated return in investment end-declarations initializations from DATAFILE RISK RET NA end-initializations declarations frac: array(SHARES) of mpvar ! Fraction of capital used per share end-declarations ! Objective: total return Return:= sum(s in SHARES) RET(s)*frac(s) ! Limit the percentage of high-risk values sum(s in RISK) frac(s) <= MAXRISK ! Minimum amount of North-American values sum(s in NA) frac(s) >= MINAM ! Spend all the capital sum(s in SHARES) frac(s) = 1 ! Upper bounds on the investment per share forall(s in SHARES) frac(s) <= MAXVAL ! Solve the problem maximize(Return) ! Solution printing to a file fopen(OUTFILE, F_OUTPUT) writeln("Total return: ", getobjval) forall(s in SHARES) writeln(strfmt(s,-12), ": \t", strfmt(getsol(frac(s))*100,2,3), "%") fclose(F_OUTPUT) end-model