The first several PROC OPTMODEL statements are straightforward declarations of index sets, parameters, and variables:
proc optmodel;
set <str> MINES;
num cost {MINES};
num extract_ub {MINES};
num quality {MINES};
read data mine_data into MINES=[mine] cost extract_ub quality;
set YEARS;
num quality_required {YEARS};
read data year_data into YEARS=[year] quality_required;
var IsOpen {MINES, YEARS} binary;
var IsWorked {MINES, YEARS} binary;
var Extract {mine in MINES, YEARS} >= 0 <= extract_ub[mine];
The following IMPVAR statement declares ExtractedPerYear as an implicit variable. Using the IMPVAR statement is an alternative to using a VAR statement and an additional constraint.
impvar ExtractedPerYear {year in YEARS}
= sum {mine in MINES} Extract[mine,year];
The following NUM statement uses a formula to compute the discount (shown in Figure 7.1) that is used in the objective function:
num discount {year in YEARS} = 1 / (1 + &discount_rate)^(year - 1);
print discount;
impvar TotalRevenue =
&revenue_per_ton * sum {year in YEARS} discount[year] *
ExtractedPerYear[year];
impvar TotalCost =
sum {mine in MINES, year in YEARS} discount[year] * cost[mine] *
IsOpen[mine,year];
max TotalProfit = TotalRevenue - TotalCost;
The following Link constraint enforces the rule that
implies that
(as in Chapter 2):
con Link {mine in MINES, year in YEARS}:
Extract[mine,year] <= Extract[mine,year].ub * IsWorked[mine,year];
The following Cardinality constraint enforces the limit on number of mines worked per year:
con Cardinality {year in YEARS}:
sum {mine in MINES} IsWorked[mine,year] <= &max_num_worked_per_year;
The following Worked_implies_open constraint enforces the rule that
implies that
:
con Worked_implies_open {mine in MINES, year in YEARS}:
IsWorked[mine,year] <= IsOpen[mine,year];
The following Continuity constraint enforces the rule that
implies that
:
con Continuity {mine in MINES, year in YEARS diff {1}}:
IsOpen[mine,year] <= IsOpen[mine,year-1];
As expressed on , the quality of the blended ore for each year is a ratio of linear functions of the decision variables. The following CON statement linearizes the nonlinear ratio constraint by clearing the denominator:
con Quality_con {year in YEARS}:
sum {mine in MINES} quality[mine] * Extract[mine,year]
= quality_required[year] * ExtractedPerYear[year];
By using the .sol suffix, the numeric parameter quality_sol computes the quality of the blended ore from the optimal decision variable values that are returned by the solver:
num quality_sol {year in YEARS} =
(sum {mine in MINES} quality[mine] * Extract[mine,year].sol) /
ExtractedPerYear[year].sol;
solve;
print IsOpen IsWorked Extract;
print ExtractedPerYear quality_sol quality_required;
create data sol_data1 from [mine year] IsOpen IsWorked Extract;
create data sol_data2 from [year] ExtractedPerYear;
quit;
Figure 7.2 shows the output from the mixed integer linear programming solver. The values of quality_sol and quality_required agree, as enforced by the Quality_con constraint.
Figure 7.2: Output from Mixed Integer Linear Programming Solver
| Problem Summary | |
|---|---|
| Objective Sense | Maximization |
| Objective Function | TotalProfit |
| Objective Type | Linear |
| Number of Variables | 60 |
| Bounded Above | 0 |
| Bounded Below | 0 |
| Bounded Below and Above | 60 |
| Free | 0 |
| Fixed | 0 |
| Binary | 40 |
| Integer | 0 |
| Number of Constraints | 66 |
| Linear LE (<=) | 61 |
| Linear EQ (=) | 5 |
| Linear GE (>=) | 0 |
| Linear Range | 0 |
| Constraint Coefficients | 151 |
| Solution Summary | |
|---|---|
| Solver | MILP |
| Algorithm | Branch and Cut |
| Objective Function | TotalProfit |
| Solution Status | Optimal |
| Objective Value | 146.86197436 |
| Relative Gap | 0 |
| Absolute Gap | 0 |
| Primal Infeasibility | 1.400502E-11 |
| Bound Infeasibility | 1.400502E-11 |
| Integer Infeasibility | 1.960765E-11 |
| Best Bound | 146.86197436 |
| Nodes | 1 |
| Iterations | 102 |
| Presolve Time | 0.00 |
| Solution Time | 0.02 |
| [1] | [2] | IsOpen | IsWorked | Extract |
|---|---|---|---|---|
| mine1 | 1 | 1 | 1 | 2.0000 |
| mine1 | 2 | 1 | 0 | 0.0000 |
| mine1 | 3 | 1 | 1 | 1.9500 |
| mine1 | 4 | 1 | 1 | 0.1250 |
| mine1 | 5 | 1 | 1 | 2.0000 |
| mine2 | 1 | 1 | 0 | 0.0000 |
| mine2 | 2 | 1 | 1 | 2.5000 |
| mine2 | 3 | 1 | 0 | 0.0000 |
| mine2 | 4 | 1 | 1 | 2.5000 |
| mine2 | 5 | 1 | 1 | 2.1667 |
| mine3 | 1 | 1 | 1 | 1.3000 |
| mine3 | 2 | 1 | 1 | 1.3000 |
| mine3 | 3 | 1 | 1 | 1.3000 |
| mine3 | 4 | 1 | 0 | 0.0000 |
| mine3 | 5 | 1 | 1 | 1.3000 |
| mine4 | 1 | 1 | 1 | 2.4500 |
| mine4 | 2 | 1 | 1 | 2.2000 |
| mine4 | 3 | 1 | 0 | 0.0000 |
| mine4 | 4 | 1 | 1 | 3.0000 |
| mine4 | 5 | 0 | 0 | 0.0000 |