PROC OPTMODEL Statements and Output

The first several PROC OPTMODEL statements declare index sets and parameters and then read the input data:

proc optmodel;
   set PERIODS;
   num length {PERIODS};
   num demand {PERIODS};
   read data period_data into PERIODS=[_N_] length demand;

   set TYPES;
   num num_avail {TYPES};
   num min_level {TYPES};
   num max_level {TYPES};
   num unit_cost {TYPES};
   num excess_cost {TYPES};
   num startup_cost {TYPES};
   read data type_data into TYPES=[_N_]
      num_avail min_level max_level unit_cost excess_cost startup_cost;

The following VAR statements declare decision variables, with bounds that depend on type:

   var NumWorking {PERIODS, type in TYPES} >= 0 <= num_avail[type] integer;
   var Excess {PERIODS, TYPES} >= 0;
   var NumStartup {PERIODS, type in TYPES} >= 0 <= num_avail[type] integer;

The following IMPVAR statement declares Output as an implicit variable:

   impvar Output {period in PERIODS, type in TYPES} = 
      min_level[type] * NumWorking[period,type] + Excess[period,type];

The following MIN statement declares the objective:

   min TotalCost =
      sum {period in PERIODS, type in TYPES} (
         unit_cost[type] * length[period] * NumWorking[period,type]
         + excess_cost[type] * length[period] * Excess[period,type]
         + startup_cost[type] * NumStartup[period,type]);

The following CON statements declare the constraints, with an IF-THEN/ELSE expression in the last constraint to account for a boundary condition in the first period:

   con Demand_con {period in PERIODS}:
      sum {type in TYPES} Output[period,type]
   >= demand[period];

   con Reserve_con {period in PERIODS}:
      sum {type in TYPES} max_level[type] * NumWorking[period,type]
   >= (1 + &reserve) * demand[period];

   con Excess_ub {period in PERIODS, type in TYPES}:
      Excess[period,type] 
   <= (max_level[type] - min_level[type]) * NumWorking[period,type];

   con Startup_con {period in PERIODS, type in TYPES}:
      NumStartup[period,type] 
   >= NumWorking[period,type] 
    - (if period - 1 in PERIODS then NumWorking[period-1,type] 
       else NumWorking[card(PERIODS),type]);

The following statements call the mixed integer linear programming solver, print the optimal solution, and create a data set that contains the optimal solution, with one observation per period-type pair:

   solve;
   print NumWorking NumStartup Excess Output;
   create data sol_data from [period type] NumWorking NumStartup Excess Output;

Figure 16.1 shows the output from the mixed integer linear programming solver.

Figure 16.1: Output from Mixed Integer Linear Programming Solver

The OPTMODEL Procedure

Problem Summary
Objective Sense Minimization
Objective Function TotalCost
Objective Type Linear
   
Number of Variables 45
Bounded Above 0
Bounded Below 15
Bounded Below and Above 30
Free 0
Fixed 0
Binary 0
Integer 30
   
Number of Constraints 40
Linear LE (<=) 15
Linear EQ (=) 0
Linear GE (>=) 25
Linear Range 0
   
Constraint Coefficients 120

Performance Information
Execution Mode Single-Machine
Number of Threads 1

Solution Summary
Solver MILP
Algorithm Branch and Cut
Objective Function TotalCost
Solution Status Optimal
Objective Value 988540
   
Relative Gap 0
Absolute Gap 0
Primal Infeasibility 0
Bound Infeasibility 0
Integer Infeasibility 3.552714E-15
   
Best Bound 988540
Nodes 1
Iterations 32
Presolve Time 0.02
Solution Time 0.02

[1] [2] NumWorking NumStartup Excess Output
1 1 12 0 0 10200
1 2 3 0 1050 4800
1 3 0 0 0 0
2 1 12 0 5800 16000
2 2 8 5 4000 14000
2 3 0 0 0 0
3 1 12 0 800 11000
3 2 8 0 4000 14000
3 3 0 0 0 0
4 1 12 0 11050 21250
4 2 9 1 4500 15750
4 3 2 2 0 3000
5 1 12 0 1050 11250
5 2 9 0 4500 15750
5 3 0 0 0 0


The following statements fix the integer variables to their optimal values, call the linear programming solver, and use the .dual constraint suffix to compute marginal costs. The RELAXINT option in the SOLVE statement relaxes the integer variables to be continuous.

   fix NumWorking;
   fix NumStartup;
   solve with LP relaxint;
   print NumWorking NumStartup Excess Output;
   print {period in PERIODS} (demand_con[period].dual / length[period]);

Figure 16.2 shows the output from the linear programming solver when the integer variables are fixed to their optimal values. As expected, the same optimal solution is obtained; the point of calling the LP solver is to obtain the dual variables used to compute the marginal costs, as shown in Figure 16.3.

Figure 16.2: Output from Linear Programming Solver, Integer Variables Fixed

Problem Summary
Objective Sense Minimization
Objective Function TotalCost
Objective Type Linear
   
Number of Variables 45
Bounded Above 0
Bounded Below 15
Bounded Below and Above 0
Free 0
Fixed 30
Binary 17
Integer 13
   
Number of Constraints 40
Linear LE (<=) 15
Linear EQ (=) 0
Linear GE (>=) 25
Linear Range 0
   
Constraint Coefficients 120

Performance Information
Execution Mode Single-Machine
Number of Threads 1

Solution Summary
Solver LP
Algorithm Dual Simplex
Objective Function TotalCost
Solution Status Optimal
Objective Value 988540
   
Primal Infeasibility 0
Dual Infeasibility 0
Bound Infeasibility 0
   
Iterations 8
Presolve Time 0.00
Solution Time 0.00

[1] [2] NumWorking NumStartup Excess Output
1 1 12 0 0 10200
1 2 3 0 1050 4800
1 3 0 0 0 0
2 1 12 0 5800 16000
2 2 8 5 4000 14000
2 3 0 0 0 0
3 1 12 0 800 11000
3 2 8 0 4000 14000
3 3 0 0 0 0
4 1 12 0 11050 21250
4 2 9 1 4500 15750
4 3 2 2 0 3000
5 1 12 0 1050 11250
5 2 9 0 4500 15750
5 3 0 0 0 0


Figure 16.3: Marginal Costs for Demand Constraints

[1]  
1 1.3
2 2.0
3 2.0
4 2.0
5 2.0


The following statements unfix the integer variables, call the linear programming solver, and use the .dual constraint suffix to compute marginal costs:

   unfix NumWorking;
   unfix NumStartup;
   solve with LP relaxint;
   print NumWorking NumStartup Excess Output;
   print {period in PERIODS} (demand_con[period].dual / length[period]);
   print {period in PERIODS} (reserve_con[period].dual / length[period]);
quit;

Figure 16.4 shows the output from the linear programming solver when the integer variables are unfixed and relaxed to be continuous. As expected, some relaxed integer variables now take fractional values, and the total cost is slightly less than before. Figure 16.5 and Figure 16.6 show the resulting marginal costs.

Figure 16.4: Output from Linear Programming Solver, Integer Variables Unfixed and Relaxed

Problem Summary
Objective Sense Minimization
Objective Function TotalCost
Objective Type Linear
   
Number of Variables 45
Bounded Above 0
Bounded Below 15
Bounded Below and Above 30
Free 0
Fixed 0
Binary 0
Integer 30
   
Number of Constraints 40
Linear LE (<=) 15
Linear EQ (=) 0
Linear GE (>=) 25
Linear Range 0
   
Constraint Coefficients 120

Performance Information
Execution Mode Single-Machine
Number of Threads 1

Solution Summary
Solver LP
Algorithm Dual Simplex
Objective Function TotalCost
Solution Status Optimal
Objective Value 985164.28571
   
Primal Infeasibility 2.273737E-13
Dual Infeasibility 0
Bound Infeasibility 0
   
Iterations 28
Presolve Time 0.00
Solution Time 0.00

[1] [2] NumWorking NumStartup Excess Output
1 1 12.0000 0.0000 0.0 10200
1 2 2.7429 0.0000 1371.4 4800
1 3 0.0000 0.0000 0.0 0
2 1 12.0000 0.0000 5000.0 15200
2 2 8.4571 5.7143 4228.6 14800
2 3 0.0000 0.0000 0.0 0
3 1 12.0000 0.0000 0.0 10200
3 2 8.4571 0.0000 4228.6 14800
3 3 0.0000 0.0000 0.0 0
4 1 12.0000 0.0000 11050.0 21250
4 2 9.6000 1.1429 4800.0 16800
4 3 1.3000 1.3000 0.0 1950
5 1 12.0000 0.0000 0.0 10200
5 2 9.6000 0.0000 4800.0 16800
5 3 0.0000 0.0000 0.0 0


Figure 16.5: Marginal Costs for Demand Constraints

[1]  
1 1.7619
2 2.0000
3 1.7857
4 2.0000
5 1.8601


Figure 16.6: Marginal Costs for Reserve Constraints

[1]  
1 0.000000
2 0.000000
3 0.000000
4 0.041667
5 0.000000