Report Functions
Report functions are used to construct reports based on a model’s results, and are valid on both calc and data sections. Combining report functions with interface functions in a data section allows you to export the reports to text files, spreadsheets, databases, or your own calling application.
Note: | The interested reader will find an exhaustive example of the use of report functions in the sample model TRANSOL.LG4 in the Samples subfolder. This model makes extensive use of many of the report functions to mimic the standard LINGO solution report. |
@DUAL( variable_or_row_name)
The @DUAL function outputs the dual value of a variable or a row. For example, consider a model with the following data section:
DATA:
@TEXT( 'C:\RESULTS\OUTPUT.TXT') =
@WRITEFOR( SET1( I): X( I), @DUAL( X( I)), @NEWLINE( 1));
ENDDATA
When this model is solved, the values of attribute X and their reduced costs will be written to the file C:\RESULTS\OUTPUT.TXT. Output may be routed to a file, spreadsheet, database or memory location. The exact destination will depend on the export function used on the left-hand side of the output statement.
If the argument to the @DUAL function is a row name, then the dual price on the generated row will be output.
@FORMAT( value, format_descriptor)
@FORMAT may be used in @WRITE and @WRITEFOR statements to format a numeric or string value for output as text, where value is the numeric or string value to be formatted, and format_descriptor is a string detailing how the number is to be formatted. The format descriptor is interpreted using C programming conventions. For instance, a format descriptor of ‘12.2f’ would cause a numeric value to be printed in a field of 12 characters with 2 digits after the decimal point. For a string values, such as a set member name, a format descriptor of '12s' would cause the string to be right justified in a field of 12 characters, while '-12s' would cause the string to be left justified in a field of 12. You can refer to a C reference manual for more details on the available formatting options.
The following example uses the @FORMAT function to place a shipping quantity into a field of eight characters with no trailing decimal value:
DATA:
@TEXT() = @WRITE( ' From To Quantity', @NEWLINE( 1));
@TEXT() = @WRITE( '--------------------------', @NEWLINE( 1));
@TEXT() = @WRITEFOR( ROUTES( I, J) | X( I, J) #GT# 0:
3*' ', WAREHOUSE( I), 4*' ',
CUSTOMER( J), 4*' ', @FORMAT( X( I, J), '8.0f'),
@NEWLINE( 1));
ENDDATA
The report will resemble the following:
From To Quantity
--------------------------
WH1 C1 2
WH1 C2 17
WH1 C3 1
WH2 C1 13
WH2 C4 12
WH3 C3 21
This next example, GRAPHPSN.LG4, graphs the standard normal function, @PSN, over the interval [ –2.4, 2.4]. The @FORMAT function is used to print the X coordinates with one trailing decimal point.
! Graphs @PSN() over a specified
interval around 0;
DATA:
! height of graph;
H = 49;
! width of graph;
W = 56;
! interval around 0;
R = 2.4;
ENDDATA
SETS:
S1 /1..H/: X, FX;
ENDSETS
@FOR( S1( I):
! X can be negative;
@FREE( X);
! Compute x coordinate;
X( I) = -R + ( I - 1)* 2 * R / ( H - 1);
! Compute y coordinate = @psn( x);
FX( I) = @PSN( X( I));
);
DATA:
! Print the header;
@TEXT() = @WRITE(
'Graph of @PSN() on the interval [-',
R,',+',R,']:',@NEWLINE(1));
@TEXT() = @WRITE( '| 0 ',(W/2-5)*'-',
' 0.5 ',(W/2-5)*'-', '1.0 X(i)',@NEWLINE(1));
! Loop to print the graph over;
@TEXT() = @WRITEFOR( S1( I): '| ',
( W * FX( I) + 1/2) * '*',
@IF( X( I) #LT# 0, '', ' '), ( W -
( W * FX( I) + 1/2) + 3)*' ',
@FORMAT( X(I), '.1f'),@NEWLINE(1));
!Trailer;
@TEXT() = @WRITE( '| 0 ',(W/2-5)*'-',
' 0.5 ',(W/2-5)*'-', '1.0',@NEWLINE(1));
ENDDATA
Model: GRAPHPSN
Here is how the graph will appear when the model is solved:
Graph of @PSN() on the interval [-2.4,+2.4]:
| 0 ----------------------- 0.5 -----------------------1.0 X(i)
| -2.4
| * -2.3
| * -2.2
| * -2.1
| * -2.0
| ** -1.9
| ** -1.8
| ** -1.7
| *** -1.6
| **** -1.5
| ***** -1.4
| ***** -1.3
| ****** -1.2
| ******** -1.1
| ********* -1.0
| ********** -0.9
| ************ -0.8
| ************** -0.7
| *************** -0.6
| ***************** -0.5
| ******************* -0.4
| ********************* -0.3
| ************************ -0.2
| ************************** -0.1
| **************************** 0.0
| ****************************** 0.1
| ******************************** 0.2
| *********************************** 0.3
| ************************************* 0.4
| *************************************** 0.5
| ***************************************** 0.6
| ****************************************** 0.7
| ******************************************** 0.8
| ********************************************** 0.9
| *********************************************** 1.0
| ************************************************ 1.1
| ************************************************** 1.2
| *************************************************** 1.3
| *************************************************** 1.4
| **************************************************** 1.5
| ***************************************************** 1.6
| ****************************************************** 1.7
| ****************************************************** 1.8
| ****************************************************** 1.9
| ******************************************************* 2.0
| ******************************************************* 2.1
| ******************************************************* 2.2
| ******************************************************* 2.3
| ******************************************************** 2.4
| 0 ----------------------- 0.5 -----------------------1.0
@ITERS()
The @ITERS function returns the total number of iterations required to solve the model. @ITERS is available only in the data and calc sections, and is not allowed in the constraints of a model. For example, the following output statement writes the iteration count to the standard output device:
DATA:
@TEXT() = @WRITE('Iterations= ', @ITERS());
ENDDATA
@NAME( var_or_row_reference)
Use @NAME to return the name of a variable or row as text. @NAME is available only in the data and calc sections, and is not allowed in the constraints of a model. The following example prints a variable name and its value:
DATA:
@TEXT() = @WRITEFOR( ROUTES( I, J) |
X( I, J) #GT# 0: @NAME( X), ' ', X( I, J), @NEWLINE( 1));
ENDDATA
The report will resemble the following:
X( WH1, C1) 2
X( WH1, C2) 17
X( WH1, C3) 1
X( WH2, C1) 13
X( WH2, C4) 12
X( WH3, C3) 21
@NEWLINE( n)
Use @NEWLINE to write n new lines to the output device. @NEWLINE is available only in the data and calc sections, and is not allowed in the constraints of a model. See the example immediately below in the @RANGED section.
@OBJBND()
@OBJBND returns the bound on the objective value.
@RANGED( variable_or_row_name)
@RANGED outputs the allowable decrease on a specified variable’s objective coefficient or on a specified row’s right-hand side. @RANGED is available only in the data and calc sections, and is not allowed in the constraints of a model. For example, consider a model with the following data section:
DATA:
@TEXT( 'C:\RESULTS\OUTPUT.TXT') =
@WRITEFOR( SET( I): X( I), @RANGED( X( I), @NEWLINE(1));
ENDDATA
When this model is solved, the values of attribute X and the allowable decreases on its objective coefficients will be written to the file C:\RESULTS\OUTPUT.TXT. If @RANGED is passed a row name it will output the allowable decrease on the right-hand side value for the row. Output may be routed to a file, spreadsheet, database, or memory location. The exact destination will depend on the export function used on the left-hand side of the output statement. Import_Export_Functions Range computations must be enabled in order for @RANGED to function properly. For more information on the interpretation of allowable decreases, refer to the Solver|Range command.
@RANGEU( variable_or_row_name)
@RANGEU outputs the allowable increase on a specified variable’s objective coefficient or on a specified row’s right-hand side. @RANGEU is available only in the data and calc sections, and is not allowed in the constraints of a model. For example, consider a model with the following data section:
DATA:
@TEXT( 'C:\RESULTS\OUTPUT.TXT') = @WRITEFOR( SET( I):
X, @RANGEU( X), @NEWLINE(1));
ENDDATA
When this model is solved, the values of X and the allowable increases on its objective coefficients will be written to the file C:\RESULTS\OUTPUT.TXT. If @RANGEU is passed a row name it will output the allowable increase on the right-hand side value for the row. Output may be routed to a file, spreadsheet, database, or memory location. The exact destination will depend on the export function used on the left-hand side of the output statement. Range computations must be enabled in order for @RANGEU to function properly. Import_Export_FunctionsFor more information on the interpretation of allowable increases, refer to the Solver|Range command.
@STATUS()
This returns the final status of the solution process using the following codes:
@STATUS() Code |
Interpretation |
0 |
Global Optimum — The optimal solution has been found. |
1 |
Infeasible — No solution exists that satisfies all constraints. |
2 |
Unbounded — The objective can be improved without bound. |
3 |
Undetermined — The solution process failed. |
4 |
Feasible — A feasible solution was found that may, or may not, be the optimal solution. |
5 |
Infeasible or Unbounded — The preprocessor determined the model is either infeasible or unbounded. If you need to narrow the result down to either infeasible or unbounded, then you will need to turn off presolving and run the model again. |
6 |
Local Optimum — Although a better solution may exist, a locally optimal solution has been found. |
7 |
Locally Infeasible — Although feasible solutions may exist, LINGO was not able to find one. |
8 |
Cutoff — The objective cutoff level was achieved. |
9 |
Numeric Error — The solver stopped due to an undefined arithmetic operation in one of the constraints. |
In general, if @STATUS does not return a code of 0, 4, 6, or 8, then the solution is of little use and should not be trusted. The @STATUS function is available only in the data and calc sections. The @STATUS function is not allowed in the constraints of a model.
For example, the following output statement uses @STATUS to print a message to let the user know if the solution is globally optimal, or not:
DATA:
@TEXT() = @WRITE( @IF( @STATUS() #EQ# 0,
'Global solution found',
'WARNING: Solution *not* globally optimal!');
ENDDATA
For additional examples of the use of the @STATUS function, refer to Interfacing with Other Applications.
@STRLEN( string)
Use @STRLEN to get the length of a specified string. This can be a useful feature when formatting reports. As an example, @STRLEN( ‘123’) would return the value 3. @STRLEN is available only in the data and calc sections, and is not allowed in the constraints of a model.
@TABLE( ‘attr|set’)
The @TABLE function is used to display either an attribute’s values or a set’s members in tabular format. The @TABLE function is available only in the data and calc sections of a model. You can refer to either QUEENS8.LG4 or PERT.LG4 for examples of @TABLE. These models can be found in the SAMPLES folder off the main LINGO folder.
For instance, QUEENS8.LG4 is a model for positioning eight queens on a chessboard so that no one queen can attack another. At the end of this model you will find the following data section:
DATA:
@TEXT() = ' The final chessboard:';
@TEXT() = @TABLE( X);
ENDDATA
Here we are using the @TABLE function to display the X attribute in the standard output window via the @TEXT interface function (see below for more on @TEXT). The X attribute is an 8-by-8 table of 0’s and 1’s indicating if a queen is positioned in a given square of the chessboard, or not. The output generated by @TABLE in this instance follows:
The final chessboard:
E1 E2 E3 E4 E5 E6 E7 E8
E1 0 1 0 0 0 0 0 0
E2 0 0 0 0 1 0 0 0
E3 0 0 0 0 0 0 1 0
E4 0 0 0 1 0 0 0 0
E5 1 0 0 0 0 0 0 0
E6 0 0 0 0 0 0 0 1
E7 0 0 0 0 0 1 0 0
E8 0 0 1 0 0 0 0 0
Note that all eight queens (indicated by the 1’s on the board) are safe from attack.
In addition to displaying attributes, @TABLE can also display sets. When displaying a set, @TABLE will print the letter X in the table cell if the corresponding set member exists, otherwise it will leave the table cell blank.
The PERT.LG4 sample model is a project scheduling model. A project consists of many tasks, and some tasks must be completed before others can begin. The list of all the tasks that must precede certain other tasks is called the precedence relations. At the end of PERT4.LG4 there is the following data section which uses @TABLE to display the precedence relations set, PRED:
DATA:
!Use @TABLE() to display the precedence relations set, PRED;
@TEXT() = @TABLE( PRED);
ENDDATA
When we run the model, @TABLE displays the following table:
DESIGN FORECAST SURVEY PRICE SCHEDULE COSTOUT TRAIN
DESIGN X X
FORECAST X X
SURVEY X
PRICE X
SCHEDULE X
COSTOUT X
TRAIN
Whenever one task must precede another, an ‘X’ appears in the particular cell of the table. So, for instance, the DESIGN task must precede the FORECAST and SURVEY tasks.
If a line of a table exceeds the page width setting in Lingo, it simply gets wrapped around. So, if you want to display wide tables without line wraps, you may need to increase the page width.
Note: | Currently, @TABLE can only send tables to the standard solution window or to text files. In other words, it is not possible to send tables to Excel, databases or to applications calling the LINGO DLL. |
@TABLE can also display sets and attributes of more than two dimensions. In fact, @TABLE allows you great control over how multidimensional objects get displayed. Specifically, four forms of @TABLE are supported:
• | @TABLE( attr/set) – This is the simplest form of @TABLE. If the object is of one dimension, it will be displayed as a column. Otherwise, the first n-1 dimensions will be displayed on the vertical axis, while the n-th dimension will be displayed on the horizontal axis of the table. An example follows: |
@TABLE( X)
• | @TABLE( attr/set, num_horz_indices) – In this form, a second argument, num_horz_indices, is supplied. This argument is an integer quantity equal to the number of the object’s dimensions to display along the horizontal axis. In which case, dimensions (n – num_horz_indices) to n will be displayed along the horizontal axis of the table. The following example displays dimension 2 and 3 of a 3-dimensional set along the horizontal axis: |
@TABLE( MY_3_DIM_SET, 2)
• | @TABLE( attr/set, prim_set1,...,prim_setn) – Here we specify the exact ordering of the object’s n dimensions. In which case, the first n-1 dimensions specified will be displayed along the vertical axis, while the last dimension specified will be displayed along the horizontal axis. Here’s an example that displays a 4-dimensional attribute with dimensions 3, 4 and 1 on the vertical, and dimension 2 on the horizontal: |
@TABLE( MY_4_DIM_ATTRIBUTE, 3, 4, 1, 2)
• | @TABLE( attr/set, prim_set1,...,prim_setn, num_horz_indices) – In this final form, we again specify the ordering of all the indices, but a final integer argument is added to specify the number of dimensions to be displayed on the horizontal axis. The following example displays a 4-dimensional attribute with dimensions 3 and 4 on the vertical, and dimensions 1 and 2 on the horizontal: |
@TABLE( MY_4_DIM_ATTRIBUTE, 3, 4, 1, 2, 2)
@WRITE( obj1[, …, objn])
Use @WRITE to output one or more objects. @WRITE is available only in the data and calc sections, and is not allowed in the constraints of a model. In a data section, output from @WRITE may be routed to a file, spreadsheet, or database. The exact destination will depend on the interface function used on the left-hand side of the output statement.
@WRITE may also be used to display computations that are a function of variable values. As an example, the output statement below prints the ratio of the two variables X and Y:
DATA:
@TEXT() = @WRITE( 'The ratio of X to Y is: ', X / Y);
ENDDATA .
Using @WRITEFOR in conjunction with export functions in data sections allows you to route output to a file, spreadsheet, or database. The exact destination will depend on the export function used on the left-hand side of the output statement.
As an example, the output statement below prints the number of units to ship, the warehouse name, and the customer name for each route with nonzero volume:
DATA:
@TEXT() = @WRITEFOR( ROUTES( I, J) | X( I, J) #GT# 0:
'Ship ', X( I, J), ' units from warehouse ', WAREHOUSE( I),
' to customer ', CUSTOMER( J), @NEWLINE( 1));
ENDDATA
The resulting report would appear as follows:
Ship 2 units from warehouse WH1 to customer C1
Ship 17 units from warehouse WH1 to customer C2
Ship 1 units from warehouse WH1 to customer C3
Ship 13 units from warehouse WH2 to customer C1
Ship 12 units from warehouse WH2 to customer C4
Ship 21 units from warehouse WH3 to customer C3
Text Replication Operator (*)
The text replication operator (*) may be used inside either the @WRITE or @WRITEFFOR functions to repeat a string a specified number of times. The operator should be preceded by a numeric value and then followed by a string (e.g., 3*’text’), which will cause the string to be printed n times, where n is the numeric value.
In the following example, the text replication operator is used twice to produce a simple graph of on-duty staff during the seven days of the week:
DATA:
LEAD = 3;
@TEXT() = 'Staff on duty graph:';
@TEXT() = @WRITEFOR( DAY( D): LEAD*' ',
DAY( D), ' ', ON_DUTY( D), ' ', ON_DUTY( D)*'+',
@NEWLINE(1)
);
ENDDATA
The graph would appear as follows, with one plus sign displayed for each staff member on duty:
Staff on duty graph:
MON 20 ++++++++++++++++++++
TUE 16 ++++++++++++++++
WED 12 ++++++++++++
THU 16 ++++++++++++++++
FRI 19 +++++++++++++++++++
SAT 14 ++++++++++++++
SUN 13 +++++++++++++