You are on page 1of 19

An AMPL Tutorial

CE 4920

Introduction

AMPL is a powerful modeling language for representing optimization problems, and interfacing with solver programs for nding optimal solutions. AMPL itself is not a solver, but acts as a front-end for other programs which do the actual solving, such as CPLEX, MINOS, or NITRO. These are commercial-grade programs used heavily in practice the good news is that AMPL takes care of each programs specic details, so you can use any of these solvers without having to learn them separately. By default, the version of AMPL on the course syllabus uses MINOS. These instructions are based on the Windows Visual Basic GUI version of AMPL, which can be downloaded from http://www.ampl.com/GUI/amplvb.zip. This is the version which is found in the lab computers. If you are using another version, the interface may be dierent, but the instructions for writing models will be the same. This tutorial should give you enough information to get started on the homework. If you want to learn more about AMPL, the absolute best resource is AMPL: A Modeling Language for Mathematical Programming, by Robert Fourer, David M. Gay, and Bruce W. Kernighan and published by Duxbury. Unfortunately it isnt free, but this is the ocial manual1 . Many other free tutorials can be found online, depending on your skill with Google2 . A few that Ive found are http://www.ce.berkeley.edu/~bayen/ce191www/labs/lab3/ampl2.pdf http://holderfamily.dot5hosting.com/aholder/teach/courses/winter2008/OR/AMPLtutorial. pdf http://www.isye.gatech.edu/~jswann/teaching/AMPLTutorial.pdf A Google search is also a good bet if you are running into a specic problem with your model.

A First Example

Start AMPL by double-clicking the AmplWin icon (or nding it in the Start Menu). You should see a screen similar to that in Figure 1. Notice the three windows in AMPL: The Command Interface is where you type commands for AMPL, and where the program will display any results. This window is located on the left. The Model Window is where you type your optimization program. You can also write your program using another text editor (such as Notepad), save it with a .mod extension, and open it from AMPL. The model window is located at the top right.
1 UW 2 Or,

doesnt have it in its libraries either, but you can obtain it from an interlibrary loan through Prospector. as I prefer to call it, Google-fu

Figure 1: AMPL interface when rst loaded. Table 1: Reservoir and stream information. Reservoir Stream Cost ($/1000 gal) 100 50 Upper limit (1000 gal) 100 Pollution (ppm) 50 250 The Data Window is optional, and used if you have a long or complicated program. You have the option of putting all of your data in this window, making the model cleaner and making it easier if you need to change some of the input parameters. Usage of the data window will be discussed more next week. Lets go through a simple example, based on the water resources example from class on Monday. Recall that the city needs 500,000 gallons of water per day, which can be drawn from either a reservoir or a stream. Characteristics of these two sources are found in Table 1. No more than 100,000 gallons per day can be drawn from the stream, and the concentration of pollutants in the water served to the city cannot exceed 100 ppm.

In class, we showed that you could write this problem as min


R,S

100R + 50S R+S S 50R + 250S R+S R, S 500 100 100 0

s.t.

where R and S were the amount of water drawn from the reservoir and stream, respectively, in thousands of gallons per day. To represent this in AMPL, type the following into the model window: # Water source example from first lecture var Reservoir; # 1000s of gallons of water taken from the reservoir var Stream; # 1000s of gallons of water taken from the stream minimize cost: 100*Reservoir + 50*Stream; subject subject subject subject subject to to to to to water_demand: Reservoir + Stream >= 500; stream_capacity: Stream <= 100; pollution: (50*Reservoir + 250*Stream)/(Reservoir + Stream) <= 100; nonneg_stream: Stream >= 0; nonneg_reservoir: Reservoir >= 0;

The rst line is a comment: whenever AMPL sees the pound sign (#), AMPL will ignore everything else in that line, so you can use it to provide descriptions in plain English. Its good practice to use comments frequently, especially if you need to go back and look at an old problem, or if somebody else (such as the instructor!) will have to read your model and make sense of it. The next two lines dene the decision variables, Reservoir and Stream. Again, note the descriptive names for the variables: we could have just used R and S, but using the full name makes it easy to understand the rest of the program. Comments are used at the end of the line to make it absolutely clear what is being represented. (In this case, its important to remember that the units are in thousands of gallons.) One important thing: every line must end with a semicolon (;), except for comments. Those of you who are familiar with programming languages like C/C++ may recognize this rule. The reason is that AMPL lets you split complicated equations across multiple lines, like this: subject to really_complicated_equation: (238x_1 + 385x_2 + sin(83x_3))^2 + sqrt(log(x_4)) + x_5 / x_6 + x_7 + x_8 + (x_1 + x_3 / sum{i in I} Area[i]) <= 10923589; Once AMPL starts reading a line, it doesnt stop until it sees a semicolon. If youre getting errors, this is one of the rst things to check. (This is most important when entering constraints, described a little bit later.) Another important thing: AMPL is case-sensitive. That means you need to be consistent in using uppercase and lowercase letters: stream, Stream, StReAm, and STREAM are all considered dierent variables. 3

You also have the choice of giving AMPL a starting value for any of the variables, like this: var Reservoir := 50; # AMPL starts off by assuming 50,000 gallons from the reservoir. The colon before the equals sign is needed if you just type Reservoir = 50, AMPL interprets that to mean that Reservoir should always equal 50, not just at the start. For this example, a starting value isnt needed, but in more complicated problems you may need to give AMPL something in the right neighborhood. If AMPL is giving you strange solutions, or failing to solve your models altogether, try giving starting values for some of your decision variables. Continuing with the example, the model le skips a line and then presents the objective function: minimize cost: 100*Reservoir + 50*Stream; You tell AMPL whether to minimize or maximize, give a label for the objective function, a colon (:), and then give the mathematical expression. You have to use an asterisk (*) for multiplication (100Reservoir gives you an error), and again note the semicolon at the end of the line. The last part of the le shows the constraints, which all are of the form subject to pollution: (50*Reservoir + 250*Stream)/(Reservoir + Stream) <= 100; Starting the line with subject to tells AMPL that this is a constraint, and a label is provided. Just like the variable names, the constraints should be clearly labeled. Finally, the constraint itself is provided. The solidus (/) indicates division, parentheses makes the order of operations right, and is typed as <=. (Again, if you are experienced with MATLAB or a programming language, this will all be familiar to you.) You can also abbreviate subject to with s.t. Dont forget the non-negativity constraints either! You can either place them with the constraints, as done here, or as a shortcut you can put bounds on the variable when theyre rst dened: var Reservoir >= 0; # Nonnegativity constraints defined here var Stream >= 0; # so they dont take up space in the constraints Note that the model is logically grouped according to the parts of an optimization problem: the decision variables are listed rst, followed by the objective function, followed by the constraints. Now were ready to solve the model! Click on the Solve button (the fth from the left, just below the menu bar). The model you just typed will appear, in the command window, followed by something that looks like Presolve eliminates 3 constraints. Adjusted problem: 2 variables, all nonlinear 2 constraints; 4 linear nonzeros 1 nonlinear constraints 1 linear constraints 1 linear objective; 2 nonzeros. 4

Table 2: Selected mathematical functions in AMPL. Command Function abs(x) Returns |x| ceil(x) Rounds x up to the next largest integer cos(x) Returns the cosine of x (x in radians) exp(x) Returns ex floor(x) Rounds x down to the next smallest integer log(x) Natural logarithm of x x mod y The remainder when you divide x by y max(x,y,z,...) Returns the largest of x, y, z, ... min(x,y,z,...) Returns the smallest of x, y, z, ... sin(x) Returns the sine of x (x in radians) sqrt(x) Returns x tan(x) Returns the sine of x (x in radians)

MINOS 5.5: optimal solution found. 1 iterations, objective 45000 Nonlin evals: constrs = 5, Jac = 4. Most of these lines are details given by the solver MINOS, about how it actually solved the problem. The one you are most concerned with is the second from the bottom: objective 45000 tells you that the optimal solution costs the city $45,000. But what about the decision variables? After all, what were really interested in is the values of Reservoir and Stream which give you that cost. The easiest way to do this is to double-click on the variable names in the model window, and look at the command interface. Double-clicking on the words Reservoir and Stream tells you that R = 400, S = 100 is the best solution. Another way is to give AMPL a command directly, in the text box at the very top of the command interface (labeled ampl:). Typing display Stream; will give you the value of Stream (dont forget the semicolon!), and typing display Reservoir, Stream; will give you both at once.

Math functions

For some of your models, you may need access to mathematical functions like logarithms, sine, cosine, and so forth. A partial listing of such functions is shown in Table 2 More math functions can be found at http://archive.ite.journal.informs.org/Vol7No1/LeeRaffensperger/ scite/amplhelp.html.

Sets and the Data Window

Entering everything into the model window is ne for small problems, like the above example, and like Problem 4 on the rst homework. However, lets say that the problem changed and a third water source became available (say, an aquifer under the city). If you knew the cost of obtaining water, the pollution, and the capacity, you could change the objective function and constraints accordingly. But what if there 5

was a fourth, or a fth? Eventually, it becomes very cumbersome to keep track of everything. To help with this, AMPL lets you dene sets to group similar items together, and gives you a data window so you can separate the specic numbers from the structure of your model. This section describes how to use both. Lets use a transportation-based example to demonstrate. The state of Utah has $100 million to spend on roadway improvements this coming year, and they have to decide how much to spend on rural projects, and how much to spend on urban projects. So, let xrural and xurban represent the amount of money spent on these two categories, in millions of dollars. The benet from spending xrural on rural projects is Brural = 7000 log(1 + xrural ) and the benet from spending xurban on urban projects is Burban = 5000 log(1 + xurban ) We want to maximize the net benet to the state, that is, Brural + Burban xrural xurban . If we wanted, we could write this in the same style as before: var urban >= 0; var rural >= 0; maximize netBenefit: 7000log(1 + rural) + 5000log(1 + urban) - rural - urban; s.t. budgetLimit: rural + urban <= 100; However, using sets, we can write the model more generally. (For instance, what if more categories of roads were added?) Mathematically, if I = {rural, urban} is the set of roadway categories, we want to maximize
0 Bi log(1 + xi ) iI iI

xi

where Brural = 7000 and Burban = 5000, subject to xi 100


iI

and the nonnegativity constraints. This way, the objective function and constraints look the same regardless of how many roadway categories were contained in I. In AMPL, you can write this as follows, in the model window: # Example 2 - Transportation funding allocation # # Allocate money to different roadway categories to # maximize benefits to society set LocationTypes; # urban vs rural param budget; param baseBenefit{i in LocationTypes};

var spending{i in LocationTypes} >= 0; #nonnegativity here maximize net_benefit: sum{i in LocationTypes} baseBenefit[i]*log(1 + spending[i]) - sum{i in LocationTypes} spending[i]; s.t. budgetLimit: sum{i in LocationTypes} spending[i] <= budget; Note that the only number which appears in this model is the zero for the nonnegativity constraints! Everything else has been replaced by a symbolic equivalent. This is good programming practice, because it removes any ambiguity about what each line of code does. There are a few new commands you see here. The set command gives a name for the set of roadway categories, here called LocationTypes. The param command denes a model parameter: these are constants which you cant change. In this case, we have 0 0 the budget limit, and the base benets Brural and Burban . Because there is a base benet dened for each roadway category type, we write param baseBenefit{i in LocationTypes}; to tell AMPL to dene a baseBenefit for every possible LocationType. Note the use of curly braces, not parentheses. The decision variables are treated similarly: because a spending decision must be made for each roadway category, we write var spending{i in LocationTypes} >= 0;, including the nonnegativity constraint here to improve readability. To write the objective function, we can use summation notation using the set we have dened previously: 0 sum{i in LocationTypes} baseBenefit[i]*log(1 + spending[i]) is exactly equivalent to iI Bi log(1+ xi ). Note that we refer to a particular baseBenefit or spending variable by using the index {[i]}, in square brackets. All well and good, but AMPL cant solve the model until we give it actual data. We havent even told it how many dierent roadway categories there are! This is where the data window comes in handy. Type the following into the data window (the bottom right window): # Example 2 - Data set LocationTypes := urban rural; param budget := 100; # millions of dollars param baseBenefit := urban 5000 rural 7000; Notice that the set LocationTypes is dened a second time, except that here we actually say what the elements are, following a colon and equals sign. We give two elements: urban and rural. The next line gives the value of budget as 100 million dollars dont forget the colon before the equals sign. Finally, we dene the base benets. You give AMPL the label for each element, followed by the actual value, so budget[urban] is 5000 and budget[rural] is 7000. Now we can solve the model. Click the Solve button, and AMPL will tell you that the optimal net benets are approximately 47,250. Double-clicking on spending gives you the entire vector of spending values: spending [*] := rural 58.5 urban 41.5; 7

Table 3: Base benets by facility type and year. Urban Suburban Rural Year 1 5000 6000 7000 Year 2 3500 5000 6500 Year 3 2000 4000 6000

The use of sets saved you a lot of time if you had a lot of variables dened individually, you would have to double click on each of them to see their optimal value. By dening a set, you can view all of them at once, and see that Utah should spend $58.5 million on rural roadways, and $41.5 million on urban ones. To see the exibility that sets give you, lets say we wanted to add a third roadway category representing 0 suburban areas, with Bsuburban = 6000. All we have to change are two lines in the data le, so it looks like this: set LocationTypes := urban suburban rural; param budget := 100; # millions of dollars param baseBenefit := urban 5000 suburban 6000 rural 7000; Clicking Solve gives you the answer to your new problem: spend $39.1 million on rural roadways, $33.3 million on suburban ones, and $27.6 on urban roadways. This increases the net benets to approximately 63,720. If we didnt use sets and the model window, we would have had to do a lot more typing and duplication of eort (since the formula for benets is still the same), and it would make the model appear much more complicated than it really is. A good principle of programming is to separate structure from data, which is exactly what AMPL accomplishes by including both a model window and a data window.

Two-Dimensional Sets and Sets of Constraints

When working with the spending allocation problem in the previous section, notice that its only concerned with spending in the current year. What if we knew what the budget would be for the next three years (or at least an estimate)? In this case, we would have to decide how much to spend on each category in each year. That is, the decision variables would be of the form xi,t , where i denotes the type of project (urban, suburban, rural), and t denotes the year in which the money is spent. Each year, the total amount of money spent cant exceed that years budget; lets say the budget is predicted to be $100 million in year 1, $95 million in year 2, and $105 million in year 3. The benets of spending xi,t million dollars is given by 0 0 Bi,t = Bi,t log(1 + xi,t ) where Bi,t gives the base benets for roadway type i in year t, given as in Table 3 As before, our objective is to maximize the net benets over the three years. We can write this optimization

problem as
3 3 0 Bi,t log(1 + xi,t ) iI t=1 iI t=1

min
x

xi,t t {1, 2, 3} t {1, 2, 3}, i I

s.t.
iI

xi,t bt xi,t 0

So, how does our AMPL program change? We need to account for several new things: x now has two dimensions: roadway class, and year when the money is spent B0 is now two-dimensional as well. We have a budget constraint for each of three three years, not just one. Heres the new AMPL source code; exactly how each of these changes were implemented is discussed below. The new model le is # Multiyear budget allocation model set LocationTypes; set Years; param budget{t in Years}; param baseBenefit{i in LocationTypes, t in Years}; var spending{i in LocationTypes, t in Years} >= 0; maximize net_benefit: sum{i in LocationTypes, t in Years} baseBenefit[i,t]*log(1 + spending[i,t]) - sum{i in LocationTypes, t in Years} spending[i,t]; s.t. budgetLimit{t in Years}: sum{i in LocationTypes} spending[i,t] <= budget[t]; while the new data le is #Multiyear budget allocation set LocationTypes := urban suburban rural; set Years := 1 2 3; param budget := 1 2 3 100 95 105; 9

param baseBenefit: urban suburban rural

1 5000 6000 7000

2 3500 5000 6500

3 := 2000 4000 6000;

First, notice that we dened a second set Years, consisting of the elements 1, 2, and 3, to represent each year in our problem. We have to make the budget depend on the year, so we add the set notation {t in Years} after its declaration as a parameter. The rst new syntax youll see is used to make baseBenefit and spending two-dimensional: param baseBenefit{i in LocationTypes, t in Years}; var spending{i in LocationTypes, t in Years} >= 0; Since both of these depend on the location type and the year, both index sets are included in the curly brackets, separated by a comma.
0 The same notation is used to represent the double summation iI t=1 Bi,t log(1 + xi,t ) in the objective function: to sum over both indexes, we type sum{i in LocationTypes, t in Years} and refer to xi,t as spending[i,t] (note the square braces, with the two indices separated by a comma). 3

A budget constraint exists for each year; but these constraints are very similar. So we can write them compactly by using the label s.t. budgetLimit{t in Years}:. This notation creates a budget constraint for every year, dened by the rest of the line: sum{i in LocationTypes} spending[i,t] <= budget[t]. Notice that since both the indices i and t appear in the constraint, we must control for them in some way: either through a summation (as with i) or in the constraint label (as with t). Failing to do so will create a syntax error. Finally, in the data le, the two-dimensional baseBenefit array must be dened. Pay careful attention to the syntax. First we write param baseBenefit followed by a colon, the labels for the second dimension (time), then a colon and equals sign. The next lines each indicate the label for the rst dimension (location 0 0 0 type), followed by the values for each time for that location type (Burban,1 , Burban,2 , and Burban,3 ). Finally, a semicolon ends the list. Solving the model, we obtain the following solution: display spending; spending := rural 1 39.0556 rural 2 41.4667 rural 3 53 suburban 1 33.3333 suburban 2 31.6667 suburban 3 35 urban 1 27.6111 urban 2 21.8667 urban 3 17 ;

10

showing the spending breakdown for each roadway type and year. Notice that relatively little spending occurs on urban areas. This might cause political problems; lets say that your experience with Wyoming politics tells you that you need to spend at least a third of the money on urban areas over the three years in order to avoid nasty partisan bickering. In this case, we need to add a constraint saying that you spend at least $100 million on urban areas in the duration of the project. In AMPL we can include this by adding the constraint s.t. urbanPolitics: sum{t in Years} spending[urban,t] >= 100;

The important thing to notice here is that we put urban in single quotes. Why? t doesnt have to be in quotes, because its a variable which ranges over all possible years. urban, on the other hand, refers to a specic element of spending. AMPL requires you to distinguish between these cases by enclosing specic labels in single quotes. Re-solving the model, we get display spending; spending := rural 1 33.9181 rural 2 35.8767 rural 3 44.5453 suburban 1 28.9298 suburban 2 27.3667 suburban 3 29.3635 urban 1 37.1521 urban 2 31.7567 urban 3 31.0912 ;

An Alternative Way to Dene Sets

Quite commonly the values in a set are all numeric in the previous example, the set Years simply consisted of the values 1, 2, and 3. Conceivably the number of years could be large, if we were doing a long-range plan, and it would certainly be tedious to list all of those. If we were allocating money to certain districts which were indexed numerically, again it could be very tedious (and typo-prone) to be forced to write out each index. In such cases, AMPL provides a shortcut notation for dening sets of numbers: a..b represents the set of all numbers between a and b inclusive. This notation is only accepted in the model window, not the data window. At the end of this section, Ill show you a workaround so you can still use the compact notation while keeping your code exible and reusable. Consider the following infrastructure management problem. Your agency is responsible for maintaining ve bridges, and you need to plan how youll spend that money over the next ten years. The state of each bridge t i in the t-th year is represented by a reliability score ri ranging from 0 to 100, where 100 represents like-new condition and 0 means its going to fall down the next time a truck drives over it. Each year, the reliability score for every bridge drops by 5 to represent deterioration; any money you spend on that bridge in that year will increase its reliability score. For convenience, assume that money is measured in the same units as reliability score, so spending 1 unit of money will increase the reliability score on a bridge by 1. Agency policy is that no bridge should ever have a reliability score below 70, and the initial reliability scores for the 5 bridges are 85, 75, 90, 95, and 100, respectively. How should you spend money to minimize total cost? Recall that the decision variables are anything you can control: in this case, you control the maintenance 11

t expenditure on each bridge in each year (call this xt ), and in turn each bridges reliability score ri . The i 1 parameters are the quantities outside your control: the initial reliability score ri for each bridge, and the amount of deterioration each year. Your objective is to minimize total cost, and your constraints are the t+1 t deterioration equation ri = ri 5 + xt and the maximum and minimum reliability scores (in addition to i non-negativity of maintenance costs).

We can write this mathematically as


10

min
x,r i=15 t=1

xt i
t1 t ri = ri 5 + xt i

s.t.

i {2, . . . , 5}, t {1, . . . , 10} i {1, . . . , 5}, t {1, . . . , 10} i {1, . . . , 5}, t {1, . . . , 10}

70 xt i

t ri

100

or, in AMPL, we can use this model: set Bridges := 1..5; set Years := 1..10; param currentCondition{b in Bridges}; param deteriorationRate; var maintenance{i in Bridges, t in Years} >= 0; var reliability{i in Bridges, t in Years}; minimize totalCost: sum{i in Bridges, t in Years} maintenance[i,t]; s.t. initialCondition{i in Bridges}: reliability[i,1] = currentCondition[i]; s.t. deterioration{i in Bridges, t in 2..10}: reliability[i,t] = reliability[i,t-1] - deteriorationRate + maintenance[i,t]; s.t. reliabilityLB{i in Bridges, t in Years}: reliability[i,t] >= 70; s.t. reliabilityUB{i in Bridges, t in Years}: reliability[i,t] <= 100; with this data: param deteriorationRate := 5; param currentCondition := 1 2 3 4 5 A few things to notice: 12 85 75 90 95 100;

The sets are dened in the model window so that Bridges runs from 1 to 5, and Years runs from 1 to 10. The reliability constraints were split into two: initialCondition forces the condition in year 1 to equal the initial value; and deterioration enforces the deterioration condition for all subsequent years. Note that deterioration is only dened for years 2 to 10; year 1 is accounted for by the initial condition. Solving this model, AMPL can tell you both the optimal maintenance schedule and the resulting reliability scores: display maintenance; maintenance [*,*] (tr) : 1 2 3 4 5 1 0 0 0 0 0 2 0 0 0 0 0 3 0 5 0 0 0 4 0 5 0 0 0 5 5 5 0 0 0 6 5 5 5 0 0 7 5 5 5 5 0 8 5 5 5 5 5 9 5 5 5 5 5 10 5 5 5 5 5 ; display reliability; reliability [*,*] (tr) : 1 2 3 4 1 85 75 90 95 2 80 70 85 90 3 75 70 80 85 4 70 70 75 80 5 70 70 70 75 6 70 70 70 70 7 70 70 70 70 8 70 70 70 70 9 70 70 70 70 10 70 70 70 70 ;

:=

5 100 95 90 85 80 75 70 70 70 70

:=

One thing might bother you if youre picky about reusable code: the fact that you typed out the number of years (10) in the model window, both when dening the set of years, and when dening the set of constraints. A way to get around that is to dene a new parameter, such as numYears, so your code looks like this: # Model window param numYears; set Bridges := 1..5; set Years := 1..numYears; 13

param currentCondition{b in Bridges}; param deteriorationRate; var maintenance{i in Bridges, t in Years} >= 0; var reliability{i in Bridges, t in Years}; minimize totalCost: sum{i in Bridges, t in Years} maintenance[i,t]; s.t. initialCondition{i in Bridges}: reliability[i,1] = currentCondition[i]; s.t. deterioration{i in Bridges, t in 2..numYears}: reliability[i,t] = reliability[i,t-1] - deteriorationRate + maintenance[i,t]; s.t. reliabilityLB{i in Bridges, t in Years}: reliability[i,t] >= 70; s.t. reliabilityUB{i in Bridges, t in Years}: reliability[i,t] <= 100; # Data window param numYears := 10; param deteriorationRate := 5; param currentCondition := 2 3 4 5 1 85 75 90 95 100;

This way, if youre changing the number of years, you only have to change the parameter denition in the data le. Do note that you have to include the declaration of numYears before using it in set Years := 1..numYears;.

Linear Programming Sensitivity Analysis

AMPL is capable of doing basic sensitivity analysis for linear programs, automating the procedures given in the notes. However, to do this, we need to use a dierent solver to this point, AMPL has been using the solver MINOS by default. To do sensitivity analysis, we need to use CPLEX. A student version of CPLEX can be downloaded from http://netlib.bell-labs.com/netlib/ampl/student/mswin/cplex. zip for Windows; versions for other operating systems can be found at http://www.ampl.com/DOWNLOADS/ cplex80.html. You should unpack the cplex.exe and cplex112.dll les and place them in the same folder as AMPL (for Windows, this is usually at \Program Files\AMPLWIN). Furthermore, to instruct AMPL to use CPLEX and to activate the sensitivity analysis routines, enter the following two commands in the command interface: option solver cplex; 14

option cplex_options sensitivity; Then, proceed to solve the model as usual. In addition to the previous functions, you now have access to these commands: display <var>.rc; displays the reduced cost of the decision variable <var>. display <var>.down; displays the lowest that the objective function coecient of <var> can be, for which the current basis is optimal. display <var>.up; same as display <var>.down, but gives the upper bound. display <constraint>; shows the marginal change in the objective function if the right-hand side of <constraint> increases. display <constraint>.down; displays the lowest that the right-hand side of <constraint> can be before the optimal basis changes. display <constraint>.up; same as display <constraint>.down, but gives the upper bound. display <constraint>.slack; shows the slack in <constraint>. For example, with the infrastructure spending problem from Homework 33 , we can see which years the budget constraint is binding by typing display budget.slack; budget.slack [*] := 0 50 1 0 2 0 3 7.55081 4 29 5 29 6 35.1123 7 40 8 47.438 9 50 10 50 ; In years 1 and 2, the budget constraint is tight (no slack left), whereas in the remaining years, the amount of unspent money can be seen. To nd the range of budget values for which the current basic solution is topimal, type display budget.down, budget.up; : 0 1 2 3 budget.down 0 43.7597 43.1356 42.4492
3 AMPL

budget.up 0 52.5361 67.4897 1e+20

:=

code can be found in the solutions to this homework.

15

4 5 6 7 8 9 10 ;

21 21 14.8877 10 2.56198 0 0

1e+20 1e+20 1e+20 1e+20 1e+20 1e+20 1e+20

Notice how this relates to the slack in these constraints. For years 310, the entire budget isnt being spent; so no matter how large the budget is in these years, the optimal solution wont change. On the other hand, in years 1 and 2, if the budget increases by a large enough amount ($2.5 million and $17.4 million, respectively), having this extra money available would fundamentally alter our spending plan. Likewise, looking at the lower bound, we see that no money at all is being spent in years 9 and 10; therefore, even if the budget is zero in these years, our optimal solution wont change. On the other hand, for the other years, if the budget decreases by a large enough amount, the optimal plan would change.

Linear Programming and Network Optimization

You may be wondering how to solve network problems (such as shortest path and minimum spanning tree) using software. One option is to write specialized code (or use an existing library). Another option is to formulate them as a linear program, and solve using AMPL. Consider the problem of nding the shortest path from node 1 to node 4 in Figure 2 and Table 4 (reproduced from the network optimization notes). Remember that the rst step in formulating an optimization problem is to specify the decision variables; in this case, we need to nd a certain path. Because each path consists of a set of arc, we might think to make a decision variable for each arc, indicating whether that arc is in our path or not. That is, xij = 1 if arc (i, j) is in the shortest path, and xij = 0 if not. The next step is to write the objective function. In this case, the cost of a path is simply the cost of its component arcs: (i,j)A:xij =1 cij . However, notice that we can write this even more simply as (i,j)A xij cij .4 This should look familiar! Because the objective function is linear, we have hope that we can write a linear program to solve the shortest path problem. Thus, we hope that we can write linear constraints. The constraints indicate what is permissible for our decision variables xij . Clearly we have nonnegativity. Furthermore, for this problem, they must represent a path from node 1 to node 4. For one, this means that they have to be adjacent to each other. So, excluding the origin and destination r and s, at every node i on the path we must have one arc entering this node, and one arc leaving this node. That is, xhi = 1 for some arc (h, i) A, and likewise xij = 1 for some other arc (i, j) A. Right now, it isnt clear how we can translate this into a constraint, but we summon mathematical courage and bravely push on. What if node i is not on the path? Then xhi and xij should equal zero for every arc (h, i) and (i, j). But again, it isnt clear how we say that this condition should hold only for nodes not on the path. Is there anything common between the two? After some thinking, you might notice that both of these cases can be accounted for by requiring (h,i)A xhi = (i,j)A xij at every node (except the origin and destination). This can be thought of as ow conservation: if the path enters the node (xhi = 1 for some (h, i)), it must leave as well (xij = 1 for some (i, j)). Likewise, if the path doesnt enter the node (xhi = 0 for all (h, i)) it cannot leave the node (xij = 0 for all
4 Because (i,j)A

xij cij =

(i,j)A:xij =1

xij cij +

(i,j)A:xij =0

xij cij =

(i,j)A:xij =1

1(cij ) +

(i,j)A:xij =0

0(cij ) =

(i,j)A:xij =1 cij

16

(i, j)). Almost there! What about the origin and destination? It would be nice if we could write similar constraints for these nodes, and indeed, we can. The path leaves the origin, but does not enter it; therefore (h,r)A xhr + 1 = (r,j)A xrj . Why does this work? The left-hand side is at least equal to one, so something on the right-hand hand side must equal one as well (something leaves). Furthermore, because the objective is to minimize the total cost, the optimal solution will set xhr = 0 for all (h, r) A. Similarly, the path enters the destination, but does not leave, so (h,s)A xhs = 1 + (s,j)A xsj . A concise way to write all of these constraints is 1 i = r xhi xij = +1 i = s (h,i)A (i,j)A 0 otherwise for every node i. One last trick before going to AMPL. As written, each of the summations is dierent for every node, because dierent arcs enter and leave. It would be more convenient if every node was directly connected to every other node, because then the left-hand side of the constraints would be the same for every node. One way to do this without disturbing the optimal solution is to create an arc between every pair of nodes with very high cost (cij = M 0 if (i, j) A). Then we can write the constraint as / xhi
hN jN

xij = bi

1 where bi = +1 0

i=r . Thus, the linear program for the shortest path problem is i=s otherwise
mathbf x

min

xij cij
(i,j)N 2

s.t.
hN

xhi
jN

xij = bi

i N (i, j) N 2

xij 0

which can write using the following AMPL code: # Model param numNodes; set Nodes := 1..numNodes; param cost{i in Nodes, j in Nodes}; param demand{i in Nodes}; var flow{i in Nodes, j in Nodes} >= 0; minimize tripcost: sum{i in Nodes, j in Nodes} flow[i,j] * cost[i,j]; s.t. conservation{i in Nodes}: sum{j in Nodes} flow[i,j] = demand[i] + sum{h in Nodes} flow[h,i]; # Data 17

Table 4: Costs for demonstration of shortest paths. Arc Cost (1,2) 2 (1,3) 4 (2,3) 1 (2,4) 5 (3,4) 2

param numNodes = 4; param cost: 1 1 0 2 9999 3 9999 4 9999 param demand := 1 1 2 0 3 0 4 -1; Executing this code gives display flow; flow := 1 1 0 1 2 1 1 3 0 1 4 0 2 1 0 2 2 0 2 3 1 2 4 0 3 1 0 3 2 0 3 3 0 3 4 1 4 1 0 4 2 0 4 3 0 4 4 0 ;

2 2 0 9999 9999

3 4 1 0 9999

4 9999 5 2 0;

:=

indicating that the three arcs in the path are (1, 2), (2, 3), and (3, 4).

18

3 4

1 2
Figure 2: Labeling of nodes and arcs.

19

You might also like