PROC OPTMODEL Statements and Output

For completeness, all statements are shown. Statements that are new or changed from Chapter 1 are indicated.

proc optmodel;
   set <str> OILS;
   num hardness {OILS};
   read data hardness_data into OILS=[oil] hardness;

   set PERIODS;
   num cost {OILS, PERIODS};
   read data cost_data into PERIODS=[_N_] {oil in OILS}
      <cost[oil,_N_]=col(oil)>;

   var Buy {OILS, PERIODS} >= 0;
   var Use {OILS, PERIODS} >= 0;
   impvar Manufacture {period in PERIODS} = sum {oil in OILS} Use[oil,period];

   num last_period = max {period in PERIODS} period;
   var Store {OILS, PERIODS union {0}} >= 0 <= &store_ub;
   for {oil in OILS} do;
      fix Store[oil,0]           = &init_storage;
      fix Store[oil,last_period] = &init_storage;
   end;

   set VEG = {oil in OILS: substr(oil,1,3) = 'veg'};
   set NONVEG = OILS diff VEG;

   impvar Revenue =
      sum {period in PERIODS} &revenue_per_ton * Manufacture[period];
   impvar RawCost =
      sum {oil in OILS, period in PERIODS} cost[oil,period] * Buy[oil,period];
   impvar StorageCost =
      sum {oil in OILS, period in PERIODS}
         &storage_cost_per_ton * Store[oil,period];
   max Profit = Revenue - RawCost - StorageCost;

   con Veg_ub_con {period in PERIODS}:
      sum {oil in VEG} Use[oil,period] <= &veg_ub;

   con Nonveg_ub_con {period in PERIODS}:
      sum {oil in NONVEG} Use[oil,period] <= &nonveg_ub;

   con Flow_balance_con {oil in OILS, period in PERIODS}:
      Store[oil,period-1] + Buy[oil,period]
         = Use[oil,period] + Store[oil,period];

   con Hardness_ub_con {period in PERIODS}:
      sum {oil in OILS} hardness[oil] * Use[oil,period]
      >= &hardness_lb * Manufacture[period];

   con Hardness_lb_con {period in PERIODS}:
      sum {oil in OILS} hardness[oil] * Use[oil,period]
      <= &hardness_ub * Manufacture[period];

The remaining statements are new in this example. The BINARY option in the following VAR statement declares IsUsed to be a binary variable:

   var IsUsed {OILS, PERIODS} binary;

The .ub variable suffix imposes an upper bound on the Use variable, in preparation for the subsequent Link constraint. The validity of this upper bound follows from the Veg_ub_con and Nonveg_ub_con constraints.

   for {period in PERIODS} do;
      for {oil in VEG}    Use[oil,period].ub = &veg_ub;
      for {oil in NONVEG} Use[oil,period].ub = &nonveg_ub;
   end;

The following Link constraint enforces the rule that $\Variable{Use[oil,period]} > 0$ implies that $\Variable{IsUsed[oil,period]} = 1$:

   con Link {oil in OILS, period in PERIODS}:
      Use[oil,period] <= Use[oil,period].ub * IsUsed[oil,period];

The following Logical1, Logical2, and Logical3 constraints correspond directly to the three extra conditions in the problem statement:

   con Logical1 {period in PERIODS}:
      sum {oil in OILS} IsUsed[oil,period] <= &max_num_oils_used;

   con Logical2 {oil in OILS, period in PERIODS}:
      Use[oil,period] >= &min_oil_used_threshold * IsUsed[oil,period];

   con Logical3 {oil in {'veg1','veg2'}, period in PERIODS}:
      IsUsed[oil,period] <= IsUsed['oil3',period];

   num hardness_sol {period in PERIODS} =
      (sum {oil in OILS} hardness[oil] * Use[oil,period].sol)
         / Manufacture[period].sol;

Because PROC OPTMODEL automatically recognizes that this model is a mixed integer linear programming problem, the following SOLVE statement calls the MILP solver, as shown in Figure 2.1:

   solve;

Figure 2.1: Summaries from Mixed Integer Linear Programming Solver

The OPTMODEL Procedure

Problem Summary
Objective Sense Maximization
Objective Function Profit
Objective Type Linear
   
Number of Variables 125
Bounded Above 0
Bounded Below 30
Bounded Below and Above 85
Free 0
Fixed 10
Binary 30
Integer 0
   
Number of Constraints 132
Linear LE (<=) 66
Linear EQ (=) 30
Linear GE (>=) 36
Linear Range 0
   
Constraint Coefficients 384

Performance Information
Execution Mode Single-Machine
Number of Threads 1

Solution Summary
Solver MILP
Algorithm Branch and Cut
Objective Function Profit
Solution Status Optimal within Relative Gap
Objective Value 100278.7037
   
Relative Gap 0.0000876456
Absolute Gap 8.7897553311
Primal Infeasibility 9.238204E-13
Bound Infeasibility 8.033221E-14
Integer Infeasibility 4.016611E-15
   
Best Bound 100287.49346
Nodes 483
Iterations 4680
Presolve Time 0.00
Solution Time 0.37


The following PRINT statement creates the output shown in Figure 2.2:

   print Buy Use Store IsUsed Manufacture hardness_sol Logical1.body;

The .body constraint suffix accesses the left-hand side value of the Logical1 constraint. For each period, the solution uses no more than three oils, as shown in Figure 2.2. The following CREATE DATA statements create multiple output data sets, as in Chapter 1:

   create data sol_data1 from [oil period] Buy Use Store IsUsed;
   create data sol_data2 from [period] Manufacture;
quit;

Figure 2.2: Output from Mixed Integer Linear Programming Solver

[1] [2] Buy Use Store IsUsed
oil1 0     500.00  
oil1 1 0.00 0.000 500.00 0
oil1 2 0.00 0.000 500.00 0
oil1 3 0.00 0.000 500.00 0
oil1 4 0.00 0.000 500.00 0
oil1 5 0.00 0.000 500.00 0
oil1 6 0.00 -0.000 500.00 -0
oil2 0     500.00  
oil2 1 0.00 40.000 460.00 1
oil2 2 0.00 0.000 460.00 0
oil2 3 0.00 0.000 460.00 0
oil2 4 0.00 230.000 230.00 1
oil2 5 0.00 230.000 0.00 1
oil2 6 730.00 230.000 500.00 1
oil3 0     500.00  
oil3 1 0.00 210.000 290.00 1
oil3 2 0.00 250.000 40.00 1
oil3 3 770.00 250.000 560.00 1
oil3 4 0.00 20.000 540.00 1
oil3 5 0.00 20.000 520.00 1
oil3 6 0.00 20.000 500.00 1
veg1 0     500.00  
veg1 1 0.00 0.000 500.00 0
veg1 2 0.00 85.185 414.81 1
veg1 3 0.00 85.185 329.63 1
veg1 4 0.00 155.000 174.63 1
veg1 5 0.00 155.000 19.63 1
veg1 6 480.37 -0.000 500.00 0
veg2 0     500.00  
veg2 1 0.00 200.000 300.00 1
veg2 2 0.00 114.815 185.19 1
veg2 3 0.00 114.815 70.37 1
veg2 4 0.00 0.000 70.37 0
veg2 5 0.00 0.000 70.37 0
veg2 6 629.63 200.000 500.00 1

[1] Manufacture hardness_sol Logical1.BODY
1 450 5.4178 3
2 450 6.0000 3
3 450 6.0000 3
4 405 6.0000 3
5 405 6.0000 3
6 450 5.0800 3


Note that the maximum profit of £100,279 is smaller than in Chapter 1. This result is expected because this model contains additional constraints.