620 likes | 635 Views
AMPL. An Introduction. Outline. AMPL - What is it (good for)? Basics Starting a Problem Running the Problem Example. Outline (Continued). Two File Format Why Use a Data File? All About Sets Example (single file) Example with data file Logical Operators. Outline (A little More).
E N D
AMPL An Introduction
Outline • AMPL - What is it (good for)? • Basics • Starting a Problem • Running the Problem • Example
Outline (Continued) • Two File Format • Why Use a Data File? • All About Sets • Example (single file) • Example with data file • Logical Operators
Outline (A little More) • Set Operations • Set Indexing • Syntax • Where to get AMPL
AMPL What is it (good for)?
AMPL is: • A Mathematical Programming Language • Supports over 30 solvers • Only need to know syntax for AMPL to use many different solvers
AMPL does: • Solves LP’s • Solves IP’s • Solves MIP’s, and non-linear programs
Basics • Files you will need • How to write them • How to save them
Writing the .mod file Open up a non-contextual text editor (e.g. GNU Emacs, OxEdit, or textedit), type the model, save with file extension “.mod”
Syntax of File • AMPL is a computer language, so it doesn’t understand what you mean • Because of this, there is a particular format that AMPL files must have to run properly • The format is fairly straightforward • Here’s how it goes:
Order of entry Everything has to be identified before you use it, so AMPL knows it is there to use Enter the variables first, e.g.: var x1 >=0; var Sally >=4.5; var smileyface <=-1; var happy integer >=0;
Order of Entry- Cont. Next enter the objective: maximize OBJ: 3*x1 - happy + 2*Sally; minimize COST: 3*x3 + 2*x1 - Sally; maximize WHOCARES: 0;
A Quick Note: AMPL is case sensitive, so “SALLY”, “Sally”, and “sally” are three different names for variables. If something isn’t working correctly, a case error is an easy thing to identify and fix.
Order of Entry - Cont. Next enter the constraints: subject to WOOD: 2*x1 - 40*happy<=27; subject to WORK: x3 + 2.5*Sally<=34; subject to MACHINE: happy - Sally<=14;
Put it all together: var x1 >=0; var Sally >=4.5; var smileyface <=-1; var happy integer >=0; maximize OBJ: 3*x1 - happy + 2*Sally + 4.5*smileyface; subject to WOOD: 2*x1 - 40*happy<=27; subject to WORK: x3 + 2.5*Sally<=34; subject to MACHINE: happy - 3*smileyface<=14;
Running the File Once you have the file written and saved, we need to run it to get our solution.
How to Run AMPL • Open up AMPL • Tell AMPL what you want solved by: • “model <filepath>\project1.mod;” • If the file is in AMPL’s search path, you may be able to just enter “model FILENAME.mod;” • If you want to use a different solver: • “option solver <solver name>;” • Tell AMPL to solve it: • “solve;”
Subject to Semicolons • You may have noticed all the semicolons running around the file we put together, and in the commands to run the model. • These tell AMPL where to separate lines, and things will not work properly without them. • For instance, if you typed “solve” rather than “solve;”, AMPL would return a prompt that looks like this “ampl?”
Getting the variables • This will only give you the optimal value, if it exists. How do you know the variable values that give this objective? • Tell AMPL to give them to you: • “display x1,x3,Sally,happy,smileyface;” • This will display the variable values • Remember, AMPL is case sensitive.
Put it all together • ampl: model data\ampl1.mod;ampl: option solver cplex;ampl: solve;CPLEX 8.0.0, optimal integral solution found, objective value 712.2, 0 MIP iterations, 0 branch and bound nodesdisplay x1,x3,Sally,smileyface,happy;x1 = 0x3 = -1Sally = 22.33333happy = 11smileyface = 0
The .dat File • AMPL supports using a separate file for the particular data • The .dat file holds the values for the parameters, and the names of the variables
Why Use 2 Files? • You may want to do similar problems • You may need to modify the data, but not the model • It makes finding and changing parameters easy • It makes adding or removing variables easy
All About Sets • AMPL lets you define sets in the model by declaring them: set SETNAME • Once a set is declared, you can index parameters over it: param PARAMETER {i in SETNAME} • Alternatively: param PARAMETER {SETNAME} • You can even index variables over sets: var VARIABLE {i in SETNAME} • Also, you can use summation notation: sum {i in SETNAME} VARIABLE[i]*PARAMETER[i]
A (Relatively) Simple Example • Suppose there is a steel mill that can process raw steel into two products, bands and coils. • The manager of the mill wants to maximize his profit, given the restrictions on processing time available, and the limitations on how much he can realistically sell of each • Bands can be processed at 200 tons per hour, at a profit of $25 per ton, and at most 6000 tons can be sold • Coils are processed at 140 tons per hour, at a profit of $30 per ton, with a market cap of 4000 tons • There are 40 hours available per week to process the steel
The old way • Without using sets or a .dat file, the model would look something like this: var Bands; var Coils; maximize Profit: 25 * Bands + 30 * Coils; subject to Time: (1/200)*Bands + (1/140)*Coils <= 40; subject to B_Limit: 0 <= Bands <= 6000; subject to C_Limit: 0 <= Coils <= 4000;
The New Way Alternatively, we could split this into a .mod file and a .dat file:
steel.mod set Products; param rate {i in Products}; param profit {i in Products}; param market {i in Products}; param hours; var X {i in Products}; maximize Total_Profit: sum {i in Products} X[i] * profit[i]; subject to Time: sum {i in Products} X[i]/rate[i]<=hours; subject to Market {i in Products}: 0<= X[i] <= market[i];
steel.dat set Products := bands coils; param: rate market profit := bands 200 6000 25 coils 140 4000 30 ; param hours := 40;
WHY???? • Right now, it looks like the old way is easier, right? • What if we want to add a new product, rods? • In the old way, we would need to find every place that something might change and alter it individually • In the new way, we just have to change the .dat file
The Old Way, Updated var Bands; var Coils; var Rods; maximize Profit: 25 * Bands + 30 * Coils + 28 * Rods; subject to Time: (1/200)*Bands + (1/140)*Coils + (1/160)*Rods <= 40; subject to B_Limit: 0 <= Bands <= 6000; subject to C_Limit: 0 <= Coils <= 4000; subject to R_Limit: 0<= Rods <= 5000;
Or, just change steel.dat set Products := bands coils rods; param: rate market profit := bands 200 6000 25 coils 140 4000 30 rods 160 5000 28 ; param hours := 40;
Also… • If you are dealing with large models, the .mod file can get REALLY big without a .dat file • Changing the problem becomes very easy with a .dat file
Logical Operators • AMPL supports some logical operators • Boolean variables (true/false) • If-then • If-then-else
How to Use if-then-else • Suppose all variables, except one, have the same upper bound, and the other has an upper bound that is twice that of the rest • A constraint like this will take care of all the the upper bounds: subject to UB {i in SETNAME }: variable[i] <= 10*(if i=1 then 2 else 1);
Set Operations • Basic set operations: • Union: U = A union B • Intersection: U = A inter B • Cartesian Product: U = {A,B}
Advantage of Set Operations • Suppose we have 3 sets of products, • CANDY • TOYS • GAMES • In our model we may need to declare the following parameters for each: • Price • Supply • Demand
Naïve Setup • Param Price_C {CANDY}; • Param Price_T {TOYS}; • Param Price_G {GAMES}; • Param Supply_C {CANDY}; • Param Supply_T {TOYS}; • Param Supply_G {GAMES}; • Param Demand_C {CANDY}; • Param Demand_T {TOYS}; • Param Demand_G {GAMES};
Using Set Operation Union Param Price {CANDY union TOYS union GAMES}; Param Supply {CANDY union TOYS union GAMES}; Param Demand {CANDY union TOYS union GAMES};
Even Better… Set PRODUCTS = CANDY union TOYS union GAMES; Param Price {PRODUCTS}; Param Supply {PRODUCTS}; Param Demand {PRODUCTS};
Compound Sets • Suppose you have a set called PRODUCTS of products to sell at a store • Consider that instead of one store you have three stores in different parts of the country • Each store has a different level of each paramater
One Solution • Instead of using the set PRODUCTS, make three different sets: • PRODUCTS_1 • PRODUCTS_2 • PRODUCTS_3 • Let each of the three numbers represent one of your store locations
One Solution (cont…) • Next, define each parameter for each set of products: • Param Price {PRODUCTS_1}; • Param Supply {PRODUCTS_1}; • Param Demand {PRODUCTS_1}; • Param Price {PRODUCTS_2}; • Param Supply {PRODUCTS_2}; • …
Easier Solution For a better solution use compound sets: • Param Price {PRODUCTS, STORES}; • Param Supply {PRODUCTS, STORES}; • Param Demand {PRODUCTS, STORES};
Structure of {PRODUCTS, STORES} • Suppose • PRODUCTS := oreos jenga lazertag ; • STORES := 1 2 3 ; • Then {PRODUCTS, STORES} is the set of all combinations of products with stores: (oreos,1) (oreos,2) (oreos,3) (jenga,1) (jenga,2) (jenga,3) (lazertag,1) (lazertag,2) (lazertag,3)
Specifying Data • In your .dat file, your declaration of Demand could look like this: param Demand: 1 2 3 := oreos 10 20 30 jenga 30 33 42 lazertag 40 30 22 ;
Indexing of Sets • Indexing of a one dimensional set sum {i in PRODUCTS} cost[i]*make[i]; • Indexing is similar in compound sets • One can say sum { (i,j) in {PRODUCTS, STORES}} cost[i]*make[i,j]; or sum { i in PRODUCTS, j in STORES} cost[i]*make[i,j];
When do we need indexing? • We may declare a parameter with or without giving index values: param Demand { PRODUCTS, STORES }; or param Demand { i in PRODUCTS, j in STORES };
With PRODUCTS and STORES: “The sales at each store will not exceed the demand for any given product at that store.” Could be expressed as the following constraint: subject to DEMAND {(i,j) in {PRODUCTS, STORES}}: Demand [i,j] >= Sell [i,j];
With PRODUCTS and STORES: • Suppose we have another parameter: Param Capacity {STORES}; • Let the capacity of a store be the total number of items it can sell all together