Output
Topics covered in this chapter:
Producing formatted output
In some of the previous examples the procedures write and writeln have been used for displaying data, solution values and some accompanying text. To produce better formatted output, these procedures can be combined with the formatting procedure strfmt. In its simplest form, strfmt's second argument indicates the (minimum) space reserved for writing the first argument and its placement within this space (negative values mean left justified printing, positive right justified). When writing a real, a third argument may be used to specify the maximum number of digits after the decimal point.
For example, if file fo.mos contains
model FO parameters r = 1.0 ! A real i = 0 ! An integer end-parameters writeln("i is ", i) writeln("i is ", strfmt(i,6) ) writeln("i is ", strfmt(i,-6) ) writeln("r is ", r) writeln("r is ", strfmt(r,6) ) writeln("r is ",strfmt(r,10,4) ) end-model
and we run Mosel thus:
mosel exec fo 'i=123, r=1.234567'
we get output
i is 123 i is 123 i is 123 r is 1.23457 r is 1.23457 r is 1.2346
The following example (model transport2.mos) prints out the solution of model `Transport' (Section A transport example) in table format. The reader may be reminded that the objective of this problem is to compute the product flows from a set of plants (PLANT) to a set of sales regions (REGION) so as to minimize the total cost. The solution needs to comply with the capacity limits of the plants (PLANTCAP) and satisfy the demand DEMAND of all regions.
procedure print_table declarations rsum: array(REGION) of integer ! Auxiliary data table for printing psum,ct,iflow: integer ! Counters end-declarations ! Print heading and the first line of the table writeln("\nProduct Distribution\n", "="*20) writeln(strfmt("Sales Region",48)) write(strfmt("",15), "| ") forall(r in REGION) write(strfmt(r,9)) writeln(" |", strfmt("TOTAL",6), " Capacity") writeln("-"*80) ! Print the solution values of the flow variables and ! calculate totals per region and per plant ct:=0 forall(p in PLANT, ct as counter) do if ct=2 then write(" Plant ", strfmt(p,-8), "|") else write(" ", strfmt(p,-8), "|") end-if psum:=0 forall(r in REGION) do iflow:=round(getsol(flow(p,r))) psum += iflow rsum(r) += iflow if iflow<>0 then write(strfmt(iflow,9)) else write(" -- ") end-if end-do writeln(" |", strfmt(psum,6), strfmt(round(PLANTCAP(p)),8)) end-do ! Print the column totals writeln("-"*80) write(strfmt(" TOTAL",-15), "|") forall(r in REGION) write(strfmt(rsum(r),9)) writeln(" |", strfmt(sum(r in REGION) rsum(r),6) strfmt(sum(p in PLANT) PLANTCAP(p),8)) ! Print demand of every region write(strfmt(" Demand",-15), "|") forall(r in REGION) write(strfmt(round(DEMAND(r)),9)) ! Print objective function value writeln("\n\nTotal cost of distribution = ", strfmt(getobjval/1e6,0,3), " million.") end-procedure
Notice the shorthand "-"*80 meaning that the string '-' is repeated 80 times. This functionality is provided by the module mmsystem, however, it is generally more efficient to work with the type text for such string operations (see Section Text handling and regular expressions). In particular, it is usually preferrable to work with textfmt in place of strfmt for producing formatted output of numbers, and write statements with several formatted elements can use a single format string via formattext as shown in the following extract from the previous model example.
! Print the column totals writeln("-"*80) write(formattext("%-15s|"," TOTAL")) forall(r in REGION) write(textfmt(rsum(r),9)) writeln(formattext(" |%6g%8g", sum(r in REGION) rsum(r), sum(p in PLANT) PLANTCAP(p)))
With the data from Chapter More advanced modeling features the procedure print_table produces the following output:
Product Distribution ==================== Sales Region | Scotland North SWest SEast Midlands | TOTAL Capacity -------------------------------------------------------------------------------- Corby | -- 80 -- 920 2000 | 3000 3000 Plant Deeside | -- 1450 1000 -- 250 | 2700 2700 Glasgow | 2840 1270 -- -- -- | 4110 4500 Oxford | -- -- 1600 1900 500 | 4000 4000 -------------------------------------------------------------------------------- TOTAL | 2840 2800 2600 2820 2750 | 13810 14200 Demand | 2840 2800 2600 2820 2750 Total cost of distribution = 81.018 million.
File output
If we do not want the output of procedure print_tab in the previous section to be displayed on screen but to be saved in the file out.txt, we simply open the file for writing at the beginning of the procedure by adding
fopen("out.txt",F_OUTPUT)
before the first writeln statement, and close it at the end of the procedure, after the last writeln statement with
fclose(F_OUTPUT)
If we do not want any existing contents of the file out.txt to be deleted, so that the result table is appended to the end of the file, we need to write the following for opening the file (closing it the same way as before):
fopen("out.txt",F_OUTPUT+F_APPEND)
As with input of data from file, there are several ways of outputting data to a file in Mosel. The following example demonstrates three different ways of writing the contents of an array A to a file. The last section (Solution output with initializations to) shows how to proceed if the data is not readily available in the form of an array but results from the evaluation of an expression (e.g., solution values, function calls).
Data output with initializations to
The first method uses the initializations block for creating or updating a file in Mosel's initializations format.
model "Trio output (1)" declarations A: array(-1..1,5..7) of real end-declarations A :: [ 2, 4, 6, 12, 14, 16, 22, 24, 26] ! First method: use an initializations block initializations to "out_1.dat" A as "MYOUT" end-initializations end-model
File out_1.dat will contain the following:
'MYOUT': [(-1 5) 2 4 6 (0 5) 12 14 16 (1 5) 22 24 26]
If this file contains already a data entry MYOUT, it is replaced with this output without modifying or deleting any other contents of this file. Otherwise, the output is appended at the end of it.
Note: For solution output with initializations to please see Section Solution output with initializations to below.
Data output with writeln
As mentioned above, we may create freely formatted output files by redirecting the output of write and writeln statements to a file:
model "Trio output (2)" declarations A: array(-1..1,5..7) of real end-declarations A :: [ 2, 4, 6, 12, 14, 16, 22, 24, 26] ! Second method: use the built-in writeln function fopen("out_2.dat", F_OUTPUT) forall(i in -1..1, j in 5..7) writeln('A_out(', i, ' and ', j, ') = ', A(i,j)) fclose(F_OUTPUT) end-model
The nicely formatted output to out_2.dat results in the following:
A_out(-1 and 5) = 2 A_out(-1 and 6) = 4 A_out(-1 and 7) = 6 A_out(0 and 5) = 12 A_out(0 and 6) = 14 A_out(0 and 7) = 16 A_out(1 and 5) = 22 A_out(1 and 6) = 24 A_out(1 and 7) = 26
Data output with diskdata
As a third possibility, one may use the diskdata subroutine from module mmetc to write out comma separated value (CSV) files.
model "Trio output (3)" uses "mmetc" declarations A: array(-1..1,5..7) of real end-declarations A :: [ 2, 4, 6, 12, 14, 16, 22, 24, 26] ! Third method: use diskdata diskdata(ETC_OUT+ETC_SPARSE,"out_3.dat", A) end-model
The output with diskdata simply prints the contents of the array to out_3.dat, with option ETC_SPARSE each entry is preceded by the corresponding indices:
-1,5,2 -1,6,4 -1,7,6 0,5,12 0,6,14 0,7,16 1,5,22 1,6,24 1,7,26
Without option ETC_SPARSE out_3.dat looks as follows:
2,4,6 12,14,16 22,24,26
Instead of using the diskdata subroutine, we may equally use the diskdata I/O driver that is defined by the same module, mmetc. In the example above we replace the diskdata statement by the following initializations to block.
[ initializations to 'mmetc.diskdata:' A as 'sparse,out_3.dat' end-initializations
Solution output with initializations to
In the previous examples we have seen how to write out to a file the contents of a data array defined in Mosel. The free format output with write/writeln can be applied to any type of expression. However, if we wish to use the initializations to functionality for writing out, for instance, the solution values after an optimization run or the result of a Mosel function we need to proceed in a slightly different way from what we have seen so far.
There are two options:
- Save the solution values or results into a new Mosel object and work with this copy for the file output. For example,
declarations x: mpvar x_sol: real y: array(R) of mpvar y_sol: array(R) of real end-declarations ... ! Define and solve an optimization problem x_sol:= x.sol ! Retrieve the solution values forall(i in R) y_sol(i):= y(i).sol initializations to "out.txt" x_sol y_sol end-initializations
- Use the keyword evaluation in the initializations block.
declarations x: mpvar y: array(R) of mpvar end-declarations ... ! Define and solve an optimization problem initializations to "out.txt" evaluation of x.sol as "x_sol" evaluation of array(i in R) y(i).sol as "y_sol" end-initializations
The array construct is used in the model extract above to generate a new array 'on the fly'. Its use is similar to aggregate operators such as sum or union.
The use of the marker evaluation of is not restricted to solution values; it allows you to work with any type of expression directly in the initializations block, including results of Mosel functions or computations as shown in the following example initeval.mos. This model writes out detailed results for our introductory Chess example (see Section Solving the chess set problem).
model "Evaluations" uses "mmxprs" declarations small,large: mpvar ! Decision variables: produced quantities end-declarations Profit:= 5*small + 20*large ! Objective function Lathe:= 3*small + 2*large <= 160 ! Lathe-hours Boxwood:= small + 3*large <= 200 ! kg of boxwood small is_integer; large is_integer ! Integrality constraints maximize(Profit) ! Solve the problem initializations to "chessout.txt" evaluation of getparam("XPRS_mipstatus") as "Status" evaluation of getobjval as "Objective" evaluation of small.sol as "small_sol" evaluation of getsol(large) as "large_sol" evaluation of Lathe.slack as "Spare time" evaluation of Boxwood.act as "Used wood" evaluation of Boxwood.act-200 as "Spare wood" evaluation of [ small.sol, large.sol ] as "x_sol" end-initializations end-model
The resulting output file chessout.txt has the following contents:
'Status': 6 'Objective': 1330 'small_sol': 2 'large_sol': 66 'Spare time': 22 'Used wood': 200 'Spare wood': 0 'x_sol': [2 66]
Real number format
Whenever output is printed (including matrix export to a file) Mosel uses a representation of floating point numbers with up to 10 digits (C format %.10g). This format may apply rounding when printing large numbers or numbers with many decimals and it is therefore not conservative for repeated reading and writing of the same value. Besides fixed format printing that is discussed in Section Producing formatted output Mosel defines several output formats for real numbers that generate conservative textual representations of floating point numbers that can be used, for example, to see the exact results of an optimization run or to produce a matrix output file with greater accuracy. Consider the following example (model numformat.mos):
model "Formatting numbers" parameters a = 12345678000.0 b = 12345678048.9 c = 12.000000045 d = 12.0 end-parameters setparam("TXTZTOL", false) ! Disable use of 'zerotol' setting for display writeln("Default: ", a, " ", b, " ", c, " ", d) setparam("REALFMT", "%1.2f") ! Fixed number of decimals writeln("%1.2f: ", a, " ", b, " ", c, " ", d) ! Exact (conservative) representations of floating point numbers setparam("REALFMT", "%.17g") writeln("%.17g: ", a, " ", b, " ", c, " ", d) setparam("REALFMT", "%y") writeln("%y: ", a, " ", b, " ", c, " ", d) setparam("REALFMT", "%j") writeln("%j: ", a, " ", b, " ", c, " ", d) end-model
This model produces the following output.
Default: 1.23456789e+11 1.23456789e+11 12 12 %1.2f: 123456789000.00 123456789008.90 12.00 12.00 %.17g: 123456789000 123456789008.89999 12.0000000045 12 %y: 1.23456789e11 1.234567890089e11 1.20000000045e1 1.2e1 %j: 123456789000 123456789008.9 12.0000000045 12
That is, with the default printing format it is not possible to distinguish between a and b or to see that c is not an integer. All these numbers are output with their exact values when a sufficiently large number of decimals is specified or by using one of the formats %j or %y that produce exact floating point representations of minimal length.
© 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.