PROC OPTMODEL Statements and Output

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 OPTMODEL Procedure

[1] discount
1 1.00000
2 0.90909
3 0.82645
4 0.75131
5 0.68301


The following Link constraint enforces the rule that $\Variable{Extract[mine,year]} > 0$ implies that $\Variable{IsWorked[mine,year]} = 1$ (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 $\Variable{IsWorked[mine,year]} = 1$ implies that $\Variable{IsOpen[mine,year]} = 1$:

   con Worked_implies_open {mine in MINES, year in YEARS}:
      IsWorked[mine,year] <= IsOpen[mine,year];

The following Continuity constraint enforces the rule that $\Variable{IsOpen[mine,year]} = 1$ implies that $\Variable{IsOpen[mine,year}-1\Variable{]} = 1$:

   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

Performance Information
Execution Mode Single-Machine
Number of Threads 1

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 3.774758E-15
Bound Infeasibility 3.996803E-15
Integer Infeasibility 4.107825E-15
   
Best Bound 146.86197436
Nodes 1
Iterations 74
Presolve Time 0.00
Solution Time 0.00

[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

[1] ExtractedPerYear quality_sol quality_required
1 5.7500 0.9 0.9
2 6.0000 0.8 0.8
3 3.2500 1.2 1.2
4 5.6250 0.6 0.6
5 5.4667 1.0 1.0