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;
Figure 7.1: discount Parameter
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.643108E-16 |
Bound Infeasibility | 0 |
Integer Infeasibility | 2E-11 |
Best Bound | 146.86197436 |
Nodes | 1 |
Iterations | 87 |
Presolve Time | 0.06 |
Solution Time | 0.08 |
[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 |