On this page we go through a sample MPL model file and explain it step by step. You can also see how MPL model files are structured.
{ Planning.mpl } { Aggregate production planning for 12 months }
Any text that appears within curly braces is a comment. Comments may be anywhere, even inside a statement. The exclamation mark '!' is an alternative style for comments that extend only from that point to the end of the line.
TITLE Production_Planning;
The title is optional, but a convenient place to name the problem. After optimizing, the title is used in the solution file to identify the problem.
INDEX product = 1..3; month = (January,February,March,April,May,June,July, August,September,October,November,December) ;
In the index section the dimensions of the problem are declared. Here, we have products number 1, 2 and 3, and the period from January to December. The vector variables and the constraints can be conveniently specified when these dimensions have been given, encapsulating the sizes of the dimensions to this single place of declaration.
Notice there are two ways of defining indexes:
1) Subrange of integers as in the case of product, meaning all the integers in the given range, inclusive. Both numbers can be any non-negative integer.
2) Enumeration of items or simply a list of names like month. This is a convenient way of assigning meaning to indexes and the vectors that use them.
The data section defines data vectors and single value scalars.
DATA price[product] := (105.09, 234.00, 800.00);
In this case, price is a data vector over one dimension, the index product that has three elements. The number of elements in the list that follows need to be at least three to satisfy the dimension of the vector. Additional numbers are allowed and just ignored.
Demand[month,product] := 1000 DATAFILE(demand.dat) ;
The demand vector has 36 values, 12 months times 3 products, which are stored in a separate file, demand.dat. A datafile is a free-format textfile, where alphabetic text is ignored and numbers are read in the order they appear. The numbers are separated with commas or spaces and comments are allowed too.
ProductionCapacity[product] := 1000 (105, 42, 14) ; ProductionCost[product] := (64.30,188.10,653.20) ; InventoryCost := 8.8? ;
>The number 1000 on the first line, means a constant with which all items in the list are multiplied. Thus the actual values for the vector are (105000, 42000, 14000). A question mark denotes an interactive value. The user is prompted at runtime to type in a value for the InventoryCost.
DECISION Inventory[product,month] -> Invt; Production[product,month] -> Prod; Sales[product,month] -> Sale;
The decision section contains declaration of the subscripted variables of the problem. Inventory, Production and Sales all range over products and months, for a total of 36 normal decision variables for each vector variable.
The name that appears after the æ->Æ (read becomes) sign is an optional abbreviation of the vector name to combat the variable name size limitations of most LP solvers. This allows you to use long and descriptive names in your MPL model, while maintaining sufficient conciseness of terms needed in your LP package. Same abbreviations can also be given for constraint names and enumeration items. Macros are a convenient and a powerful way of naming and reusing complex expressions and parts of expressions.
MACRO Revenues := SUM(product,month: price * Sales); TotalCost := SUM(product,month: InventoryCost * Inventory + ProductionCost * Production) ;
In the example above, we have set up two macros: Revenues is the expression for the sum of the revenues of all three products over the whole period. TotalCost is the cost of the inventory plus cost of production over all months and all products. We can then, in the model, refer to these sums using the macro names. This makes the model more readable and sometimes even smaller, by eliminating unnecessary variables. Now we come to the actual model. The keyword MODEL is optional, denoting the end of declarations and the start of the model part.
MODEL MAX Profit = Revenues - TotalCost ;
The first section in the model is the definition of the objective function. Which is either to maximize MAX or minimized MIN. This function may have a name, in this case it is Profit. Finally, we come to the formula for the objective function. In this case, the objective is simple because of the macros used: maximize profit equal to the difference between revenues and total cost.
SUBJECT TO InventoryBalance[product,month] -> InvB : Inventory = Inventory[month-1] + Production - Sales ;
The constraints of this problem are shown above. This single equation is expanded to 36 normal constraints, one for each product and month. The intuitive meaning of the constraint is simple: the inventory of a given month (for any product) has to equal the previous monthÆs inventory, plus what we have produced, minus what we have sold. For the special case of January the constraint is: inventory is equal to production minus sales.
A constraint is specified by:
constraint_declaration : formula comparison formula ;
A constraint declaration consists of a name, a list of the indexes that it ranges over (unless it is a simple single constraint), and an optional abbreviation for input generation purposes. Here we have the constraint InventoryBalance which shortens to InvB, for each product and each month.
The formulas of the constraint can be a combination of constants, single variables, data vectors, variable vectors and sums, with the restriction that all the indexes of a vector must be accounted for, either as a part of the constraint declaration or as part of sum indexes, or by fixing it to a given value. Note that dimensions of vectors need not be specified, but are implicit. And even if they are listed, they can be placed in any order, unless it has dimensions with a fixed value, in which case it is important to correctly recognize order.
In the term Inventory[month-1], the month index has an offset value, which select the variable for the previous month. For example, the last generated constraint would look like:
InvB1Dec : Invt1Dec = Invt1Nov + Prod1Dec - Sale1Dec;
In the above constraint for product 1, month of December, November is used for the second inventory term. This method works for any integer offset value. Entries that fall out of bounds are ignored, thus for the January constraint the Inventory variable will be omitted.
Simple upper and lower bounds on variables are specified separately, in a manner similar to constraints. They are preceded with the keyword BOUNDS.
BOUNDS Sales <= Demand ; Production <="ProductionCapacity" ; Inventory[product,month="January..November]" <="90000" ; Inventory[product,December]="20000" ;
One side of a bound must be only a normal variable or variable vector, and the other side a scalar constant or data vector. It is possible to specify both lower and upper bound for variable in one single statement in the usual manner (1 <'x' < 3).
The meaning of the above bounds is very intuitive; we cannot sell more than there is demand for, we cannot produce more than we have capacity for, and we cannot keep more in inventory than our storage space allows. With the last bound we effectively want to constrain the solution to give us an inventory of 20000 at the end of the year, thus giving the variable a fixed value.
These bounds illustrate a special feature; the use of subindexes. Sometimes a constraint/bound should only be given for a subrange of the originally specified index. Here, the subrange month=January..November constrains the month index within the range specified, therefore, the MaxInventory bound is not generated for December.
The CloseInventory bound is similar and complementary, even if it looks different. Here we have an example of a fixed dimension, Inventory, that has the month dimension fixed to December. Alternatively, this bound could also have been entered as:
Inventory[product,month=December] = 20000;The last sections of the model specification involve labeling some variables with special attributes. Decision variables can be marked FREE, INTEGER or BINARY, as a status classification for the LP solver. Here we want the production variables to be integer variables.
INTEGER Production ENDThe END keyword is optional and marks the end of the model.