Input Statements
There are three functions discussed in this section: @READLN (read line), @REAMRM (read to record marker) and @INPUT (user terminal input). These functions are user for programmatically reading text input data in calc sections. This contrasts with input functions in data sections. Data section input cannot be programmatically controlled and will be executed regardless whenever the model is solved. With @READLN, @REAMRM and @INPUT, you may dynamically test conditions to determine if and/or how the text reads are executed. You may also dynamically specify how much text to read based on things such as the dimensions of the current model.
In addition to the statements above, the following statements may also be used for input in calc sections: @ODBC for databases, @OLE for Excel workbooks, @TEXT for text-based output and @POINTER for interfacing with programming languages.
@READLN( [‘FILE_NAME’][, LINES])
The @READLN function reads a specified number of lines (LINES) from a text file ('FILE_NAME'). The file name argument is optional, and if it is omitted the last file name used in either a @READLN or @READRM will be substituted. The number of lines argument, LINES, can be a constant or a scalar variable giving the number of lines to read. If LINES is omitted, then a single line will be read.
@READRM( [‘FILE_NAME’][, 'RECORD_MARKER'])
The @READRM function reads from a text file ('FILE_NAME') until either it encounters any specified record marker string or the end of the file. The file name argument is optional, and if it is omitted the last file name used in either a @READLN or @READRM will be substituted. The record marker string may also be omitted, in which case, @READRM will read to the end of the file.
We will now illustrate the use of the two input functions above with the following transportation model, TRAN, found in the LINGO samples model set:
MODEL:
! A 3 Warehouse, 4 Customer
Transportation Problem;
SETS:
WAREHOUSE / WH1, WH2, WH3/ : CAPACITY;
CUSTOMER / C1, C2, C3, C4/ : DEMAND;
ROUTES( WAREHOUSE, CUSTOMER) : COST, VOLUME;
ENDSETS
! The objective;
[OBJ] MIN = @SUM( ROUTES: COST * VOLUME);
! The demand constraints;
@FOR( CUSTOMER( J): [DEM]
@SUM( WAREHOUSE( I): VOLUME( I, J)) >=
DEMAND( J));
! The supply constraints;
@FOR( WAREHOUSE( I): [SUP]
@SUM( CUSTOMER( J): VOLUME( I, J)) <=
CAPACITY( I));
! Here are the parameters;
DATA:
CAPACITY = 30, 25, 21 ;
DEMAND = 15, 17, 22, 12;
COST = 6, 2, 6, 7,
4, 9, 5, 3,
8, 8, 1, 5;
ENDDATA
END
Model: TRAN
In this model, we see that all the data (the set names and the parameters) are explicitly contained in the model. This is really only practical for small models. For models with large amounts of data, we would typically want to keep the model separate from the data, given that the data tends to change frequently, while the model does not. Here is a version of the same model that removes the data, placing it in an external text file, and a calc section has been added to replace the original data section. The calc section uses @READLN and @READRM to read the data:
MODEL:
! A 3 Warehouse, 4 Customer
Transportation Problem;
! Illustrates use of the file reading functions:
@READLN and @READRM;
SETS:
WAREHOUSE : CAPACITY;
CUSTOMER : DEMAND;
ROUTES( WAREHOUSE, CUSTOMER) : COST, VOLUME;
ENDSETS
CALC:
!Read 1 line with number of warehouses
and number of customers;
NW, NC = @READLN( 'TRANREAD.LDT', 1);
! Read NW lines of warehouses;
WAREHOUSE = @READLN( , NW);
! Read NC lines of customers;
CUSTOMER = @READLN( , NC);
! Read cost data up to record
marker: '~';
COST = @READRM( , '~');
! Read capacities up to
record marker;
CAPACITY = @READRM( , '~');
! Read demand data up to end-of-file;
DEMAND = @READRM();
ENDCALC
! The objective;
[OBJ] MIN = @SUM( ROUTES: COST * VOLUME);
! The demand constraints;
@FOR( CUSTOMER( J): [DEM]
@SUM( WAREHOUSE( I): VOLUME( I, J)) >=
DEMAND( J));
! The supply constraints;
@FOR( WAREHOUSE( I): [SUP]
@SUM( CUSTOMER( J): VOLUME( I, J)) <=
CAPACITY( I));
END
Model: TRANREAD
The data file used for the model is:
3 4 !4 warehouses, 3 customers;
W1 !List of warehouse;
W2
W3
C1 !List of sustomers ;
C2
C3
C4
6 2 6 7
4 9 5 3
8 8 1 5 ~
30 25 21 ~
15 17 22 12
A brief description of the input statements used follows:
• | First, we have an @READLN that reads one line containing the number of customers and warehouses. Note that we specify the input file name, given that this is the first call to one of the input functions. |
• | Second, now that we know the number of warehouses and customers, we use @READLN to read NW warehouses and another @READLN to read NC customers. Here, we do not have to specify the file name and are letting it default to the previous name. |
• | Finally, there are three calls to @READRM to read in the cost data, warehouse capacities and customer demands. The first two calls specify an end-of-record marker (here we use a tilde), but the final call has no record marker specified, meaning we simply read to the end of the file. |
@INPUT( 'PROMPT')
The @INPUT function displays the prompt string 'PROMPT' to the user and then reads their typed response from the keyboard. As an example, you will recall the staff scheduling model, STAFFDEM, discussed in the section A Staff Scheduling Problem:
MODEL:
SETS:
DAYS: REQUIRED, START;
ENDSETS
DATA:
DAYS = MON TUE WED THU FRI SAT SUN;
REQUIRED = 20 16 13 16 19 14 12;
ENDDATA
MIN = @SUM( DAYS( I): START( I));
@FOR( DAYS( J):
@SUM( DAYS( I) | I #LE# 5:
START( @WRAP( J - I + 1, 7)))
>= REQUIRED( J)
);
END
Model: STAFFDEM
In this example, you will note the the daily staffing requirements are hard coded in the model with the statement:
REQUIRED = 20 16 13 16 19 14 12;
If this model were to be run frequently for different sets of staffing requirements, we might find it convenient to prompt the users for their current staffing needs. In this case, we could change the model as follows (modified sections in bold):
MODEL:
SETS:
DAYS: REQUIRED, START, ONDUTY;
ENDSETS
DATA:
DAYS = MON TUE WED THU FRI SAT SUN;
ENDDATA
CALC:
REQUIRED = @INPUT('Enter staffing needs:');
ENDCALC
MIN = @SUM( DAYS( I): START( I));
@FOR( DAYS( J):
ONDUTY( J) =
@SUM( DAYS( I) | I #LE# 5:
START( @WRAP( J - I + 1, 7)));
ONDUTY( J) >= REQUIRED( J)
);
END
The data is no longer hard coded, and we now prompt the user via @INPUT for their staffing requirements. Note that the @INPUT statement may only appear in programmatic (i.e., calc) sections of a model, so we have added a calc section to accommodate this.
When we run the model, we receive the following prompt for the staffing data:
To which, we've gone ahead and entered the original staffing requirements. Clicking the OK button will then result in the model continuing to solve, but using the runtime staffing requirements entered by the user.