The first several PROC OPTMODEL statements declare index sets and parameters and then read the input data:
proc optmodel;
set <str> RAWS;
num supply {RAWS};
read data raw_material_data into RAWS=[raw] supply;
set <str> PRODUCTS;
num prev_demand {PRODUCTS};
num prev_price {PRODUCTS};
num percent {PRODUCTS, RAWS};
read data product_data into PRODUCTS=[product]
{raw in RAWS} <percent[product,raw]=col(raw)> prev_demand prev_price;
num elasticity {PRODUCTS, PRODUCTS} init 0;
read data elasticity_data into [i j] elasticity;
The following model declaration statements correspond directly to the mathematical programming formulation described earlier:
var Price {PRODUCTS} >= 0;
var Demand {PRODUCTS} >= 0;
max TotalRevenue
= sum {product in PRODUCTS} Price[product] * Demand[product];
con Demand_con {i in PRODUCTS}:
(Demand[i] - prev_demand[i]) / prev_demand[i]
= sum {j in PRODUCTS} elasticity[i,j] * (Price[j] - prev_price[j]) /
prev_price[j];
con Supply_con {raw in RAWS: supply[raw] ne .}:
sum {product in PRODUCTS} (percent[product,raw]/100) * Demand[product]
<= supply[raw];
con Price_index_con:
sum {product in PRODUCTS} prev_demand[product] * Price[product]
<= sum {product in PRODUCTS} prev_demand[product] * prev_price[product];
In this example, all variables are real, the objective function is quadratic, and all constraints are linear. So PROC OPTMODEL automatically recognizes that this model is a quadratic programming problem, and the first SOLVE statement calls the quadratic programming solver. In this case, the QP solver detects that this maximization problem has a nonconcave objective, and PROC OPTMODEL instead calls the default nonlinear programming algorithm, which is the interior point algorithm.
solve; print Price Demand; print Price_index_con.dual;
Figure 22.1 shows the output when you use the (default) NLP interior point algorithm.
Figure 22.1: Output from NLP Interior Point Algorithm
| Problem Summary | |
|---|---|
| Objective Sense | Maximization |
| Objective Function | TotalRevenue |
| Objective Type | Quadratic |
| Number of Variables | 8 |
| Bounded Above | 0 |
| Bounded Below | 8 |
| Bounded Below and Above | 0 |
| Free | 0 |
| Fixed | 0 |
| Number of Constraints | 7 |
| Linear LE (<=) | 3 |
| Linear EQ (=) | 4 |
| Linear GE (>=) | 0 |
| Linear Range | 0 |
| Constraint Coefficients | 22 |
| Performance Information | |
|---|---|
| Execution Mode | Single-Machine |
| Number of Threads | 4 |
| Solution Summary | |
|---|---|
| Solver | NLP |
| Algorithm | Interior Point |
| Objective Function | TotalRevenue |
| Solution Status | Optimal |
| Objective Value | 1991585768.2 |
| Optimality Error | 7.8630478E-7 |
| Infeasibility | 7.8630478E-7 |
| Iterations | 40 |
| Presolve Time | 0.00 |
| Solution Time | 0.64 |
| [1] | Price | Demand |
|---|---|---|
| Butter | 667.31 | 383231 |
| Cheese1 | 889.58 | 251766 |
| Cheese2 | 1066.22 | 57091 |
| Milk | 303.84 | 4775603 |
| Price_index_con.DUAL |
|---|
| 0.61421 |
To invoke the active set algorithm (which is not the default NLP algorithm), you can use the ALGORITHM= option in the SOLVE
WITH NLP statement:
solve with NLP / algorithm=activeset; print Price Demand; print Price_index_con.dual; quit;
Figure 22.2 shows the output when you use the ALGORITHM=ACTIVESET option to invoke the active set algorithm.
Figure 22.2: Output from NLP Active Set Algorithm
| Problem Summary | |
|---|---|
| Objective Sense | Maximization |
| Objective Function | TotalRevenue |
| Objective Type | Quadratic |
| Number of Variables | 8 |
| Bounded Above | 0 |
| Bounded Below | 8 |
| Bounded Below and Above | 0 |
| Free | 0 |
| Fixed | 0 |
| Number of Constraints | 7 |
| Linear LE (<=) | 3 |
| Linear EQ (=) | 4 |
| Linear GE (>=) | 0 |
| Linear Range | 0 |
| Performance Information | |
|---|---|
| Execution Mode | Single-Machine |
| Number of Threads | 4 |
| Solution Summary | |
|---|---|
| Solver | NLP |
| Algorithm | Active Set |
| Objective Function | TotalRevenue |
| Solution Status | Best Feasible |
| Objective Value | 1991585768.2 |
| Optimality Error | 0.0024622983 |
| Infeasibility | 7.8630478E-7 |
| Iterations | 93 |
| Presolve Time | 0.00 |
| Solution Time | 0.28 |
| [1] | Price | Demand |
|---|---|---|
| Butter | 667.31 | 383231 |
| Cheese1 | 889.58 | 251766 |
| Cheese2 | 1066.22 | 57091 |
| Milk | 303.84 | 4775603 |
| Price_index_con.DUAL |
|---|
| 0.61558 |
The optimal solutions and dual values for the two algorithms agree.
You can also replace the Demand and Demand_con declarations with the following IMPVAR and CON statements to perform the substitutions described on page 299
of Williams:
impvar Demand {i in PRODUCTS} =
prev_demand[i] * (1 +
sum {j in PRODUCTS}
elasticity[i,j] * (Price[j] - prev_price[j]) / prev_price[j]);
con Demand_nonnegative {i in PRODUCTS}:
Demand[i] >= 0;
The resulting formulation is mathematically equivalent but yields a concave objective function, as shown in Williams.
Figure 22.3 shows the output when you use the (default) quadratic programming solver on the reformulated problem.
Figure 22.3: Output from Quadratic Programming Solver, Reformulated Problem
| Problem Summary | |
|---|---|
| Objective Sense | Maximization |
| Objective Function | TotalRevenue |
| Objective Type | Quadratic |
| Number of Variables | 4 |
| Bounded Above | 0 |
| Bounded Below | 4 |
| Bounded Below and Above | 0 |
| Free | 0 |
| Fixed | 0 |
| Number of Constraints | 7 |
| Linear LE (<=) | 3 |
| Linear EQ (=) | 0 |
| Linear GE (>=) | 4 |
| Linear Range | 0 |
| Constraint Coefficients | 16 |
| Performance Information | |
|---|---|
| Execution Mode | Single-Machine |
| Number of Threads | 4 |
| Solution Summary | |
|---|---|
| Solver | QP |
| Algorithm | Interior Point |
| Objective Function | TotalRevenue |
| Solution Status | Optimal |
| Objective Value | 1991585591.9 |
| Primal Infeasibility | 4.371373E-17 |
| Dual Infeasibility | 1.473482E-16 |
| Bound Infeasibility | 5.755324E-17 |
| Duality Gap | 1.197129E-16 |
| Complementarity | 3.0307127E-8 |
| Iterations | 11 |
| Presolve Time | 0.00 |
| Solution Time | 0.02 |
| [1] | Price | Demand |
|---|---|---|
| Butter | 667.29 | 383255 |
| Cheese1 | 889.89 | 251695 |
| Cheese2 | 1066.17 | 57101 |
| Milk | 303.83 | 4775678 |
| Price_index_con.DUAL |
|---|
| 0.6161 |
Figure 22.4 shows the output when you use the WITH NLP option to invoke the nonlinear programming interior point algorithm on the reformulated problem.
Figure 22.4: Output from NLP Interior Point Algorithm, Reformulated Problem
| Problem Summary | |
|---|---|
| Objective Sense | Maximization |
| Objective Function | TotalRevenue |
| Objective Type | Quadratic |
| Number of Variables | 4 |
| Bounded Above | 0 |
| Bounded Below | 4 |
| Bounded Below and Above | 0 |
| Free | 0 |
| Fixed | 0 |
| Number of Constraints | 7 |
| Linear LE (<=) | 3 |
| Linear EQ (=) | 0 |
| Linear GE (>=) | 4 |
| Linear Range | 0 |
| Performance Information | |
|---|---|
| Execution Mode | Single-Machine |
| Number of Threads | 4 |
| Solution Summary | |
|---|---|
| Solver | NLP |
| Algorithm | Interior Point |
| Objective Function | TotalRevenue |
| Solution Status | Optimal |
| Objective Value | 1991585591.9 |
| Optimality Error | 5E-7 |
| Infeasibility | 0 |
| Iterations | 7 |
| Presolve Time | 0.00 |
| Solution Time | 0.07 |
| [1] | Price | Demand |
|---|---|---|
| Butter | 667.29 | 383255 |
| Cheese1 | 889.89 | 251695 |
| Cheese2 | 1066.17 | 57101 |
| Milk | 303.83 | 4775678 |
| Price_index_con.DUAL |
|---|
| 0.6161 |
Figure 22.5 shows the output when you use the ALGORITHM=ACTIVESET option to invoke the active set algorithm on the reformulated problem.
Figure 22.5: Output from NLP Active Set Algorithm, Reformulated Problem
| Problem Summary | |
|---|---|
| Objective Sense | Maximization |
| Objective Function | TotalRevenue |
| Objective Type | Quadratic |
| Number of Variables | 4 |
| Bounded Above | 0 |
| Bounded Below | 4 |
| Bounded Below and Above | 0 |
| Free | 0 |
| Fixed | 0 |
| Number of Constraints | 7 |
| Linear LE (<=) | 3 |
| Linear EQ (=) | 0 |
| Linear GE (>=) | 4 |
| Linear Range | 0 |
| Performance Information | |
|---|---|
| Execution Mode | Single-Machine |
| Number of Threads | 4 |
| Solution Summary | |
|---|---|
| Solver | NLP |
| Algorithm | Active Set |
| Objective Function | TotalRevenue |
| Solution Status | Optimal |
| Objective Value | 1991585591.9 |
| Optimality Error | 4.052786E-19 |
| Infeasibility | 2.3841858E-7 |
| Iterations | 9 |
| Presolve Time | 0.00 |
| Solution Time | 0.02 |
| [1] | Price | Demand |
|---|---|---|
| Butter | 667.29 | 383255 |
| Cheese1 | 889.89 | 251695 |
| Cheese2 | 1066.17 | 57101 |
| Milk | 303.83 | 4775678 |
| Price_index_con.DUAL |
|---|
| 0.6161 |
The optimal solutions and dual values for all three algorithms agree with the previous results.