Optimizing a Constraint: Reconstructing an Integer Programming Constraint More Simply


PROC OPTMODEL Statements and Output

The following PROC TRANSPOSE statements and DATA step create an input data set for the CLP procedure in SAS/OR software:

proc transpose data=a_data out=trans(drop=_name_) prefix=x;
run;

data condata_feas(drop=j);
   length _type_ $8;
   array x[&n];
   set trans;
   _type_ = 'le';
   _rhs_ = &b;
   output;
   do j = 1 to &n;
      x[j] = 1;
   end;
   _type_ = 'binary';
   _rhs_ = .;
   output;
run;

The following PROC CLP statements find all feasible solutions and save them in the out_feas data set:

proc clp data=condata_feas out=out_feas allsolns usecondatavars=1;
run;

The following DATA step creates another input data set for PROC CLP, for the purpose of finding all binary solutions that violate the original constraint:

data condata_infeas;
   set condata_feas;
   if _N_ = 1 then do;
      _type_ = 'ge';
      _rhs_ = _rhs_ + 1;
   end;
run;

The following PROC CLP statements find all such solutions and save them in the out_infeas data set:

proc clp data=condata_infeas out=out_infeas allsolns usecondatavars=1;
run;

The first several PROC OPTMODEL statements declare sets and parameters and read the original constraint data:

proc optmodel;
   set VARS;
   set VARS0 = VARS union {0};
   num a {VARS0};
   read data a_data into VARS=[_N_] a;
   a[0] = &b;

The following statements declare the $\text {FEAS}\mr{\_ }\text {POINTS}$ set and the $\Argument{x\_ feas}$ parameter and populate them by reading the out_feas data set:

   set FEAS_POINTS;
   num x_feas {FEAS_POINTS, VARS};
   read data out_feas into FEAS_POINTS=[_N_]
      {j in VARS} <x_feas[_N_,j]=col('x'||j)>;

The following statements declare the $\text {INFEAS}\mr{\_ }\text {POINTS}$ set and the $\Argument{x\_ infeas}$ parameter and populate them by reading the out_infeas data set:

   set INFEAS_POINTS;
   num x_infeas {INFEAS_POINTS, VARS};
   read data out_infeas into INFEAS_POINTS=[_N_]
      {j in VARS} <x_infeas[_N_,j]=col('x'||j)>;

The following statements declare the variables and constraints:

   var Scale {VARS0} >= 0;
   impvar Alpha {j in VARS0} = a[j] * Scale[j];

   con Feas_con {point in FEAS_POINTS}:
      sum {j in VARS} Alpha[j] * x_feas[point,j] <= Alpha[0];
   con Infeas_con {point in INFEAS_POINTS}:
      sum {j in VARS} Alpha[j] * x_infeas[point,j] >= Alpha[0] + 1;

The following statements solve the problem by using the first objective and then print the solution:

   min Objective1 = abs(a[0]) * Scale[0];
   solve;
   print a Scale Alpha;

Figure 18.1 shows the output from the linear programming solver for the first objective.

Figure 18.1: Output from Linear Programming Solver, First Objective

The OPTMODEL Procedure

Problem Summary
Objective Sense Minimization
Objective Function Objective1
Objective Type Linear
   
Number of Variables 9
Bounded Above 0
Bounded Below 9
Bounded Below and Above 0
Free 0
Fixed 0
   
Number of Constraints 256
Linear LE (<=) 152
Linear EQ (=) 0
Linear GE (>=) 104
Linear Range 0
   
Constraint Coefficients 1280

Performance Information
Execution Mode Single-Machine
Number of Threads 1

Solution Summary
Solver LP
Algorithm Dual Simplex
Objective Function Objective1
Solution Status Optimal
Objective Value 25
   
Primal Infeasibility 9.325873E-15
Dual Infeasibility 0
Bound Infeasibility 0
   
Iterations 24
Presolve Time 0.00
Solution Time 0.00

[1] a Scale Alpha
0 37 0.67568 25
1 9 0.66667 6
2 13 0.69231 9
3 -14 0.71429 -10
4 17 0.70588 12
5 13 0.69231 9
6 -19 0.68421 -13
7 23 0.69565 16
8 21 0.66667 14



The following statements solve the problem by using the second objective and then print the solution:

   min Objective2 = sum {j in VARS} abs(a[j]) * Scale[j];
   solve;
   print a Scale Alpha;
quit;

Figure 18.2 shows the output from the linear programming solver for the second objective.

Figure 18.2: Output from Linear Programming Solver, Second Objective

Problem Summary
Objective Sense Minimization
Objective Function Objective2
Objective Type Linear
   
Number of Variables 9
Bounded Above 0
Bounded Below 9
Bounded Below and Above 0
Free 0
Fixed 0
   
Number of Constraints 256
Linear LE (<=) 152
Linear EQ (=) 0
Linear GE (>=) 104
Linear Range 0
   
Constraint Coefficients 1280

Performance Information
Execution Mode Single-Machine
Number of Threads 1

Solution Summary
Solver LP
Algorithm Dual Simplex
Objective Function Objective2
Solution Status Optimal
Objective Value 89
   
Primal Infeasibility 6.883383E-15
Dual Infeasibility 0
Bound Infeasibility 0
   
Iterations 19
Presolve Time 0.00
Solution Time 0.02

[1] a Scale Alpha
0 37 0.67568 25
1 9 0.66667 6
2 13 0.69231 9
3 -14 0.71429 -10
4 17 0.70588 12
5 13 0.69231 9
6 -19 0.68421 -13
7 23 0.69565 16
8 21 0.66667 14



For this test instance, it turns out that the optimal solutions for the two different objectives agree.

In SAS/OR 13.2, you can also access the CLP solver from within PROC OPTMODEL by using the SOLVE WITH CLP statement. The first several PROC OPTMODEL statements are the same as before:

proc optmodel;
   set VARS;
   set VARS0 = VARS union {0};
   num a {VARS0};
   read data a_data into VARS=[_N_] a;
   a[0] = &b;

The following statements declare variables and a constraint for the CLP problem:

   var X {VARS} binary;
   con CLP_con:
      sum {j in VARS} a[j] * X[j] <= a[0];

The following statements call the CLP solver and use the FINDALLSOLNS option to find all solutions, and then they use the predeclared numeric parameter _NSOL_ and the .sol variable suffix to retrieve the resulting solutions:

   solve with CLP / findallsolns;
   set FEAS_POINTS;
   FEAS_POINTS = 1.._NSOL_;
   num x_feas {FEAS_POINTS, VARS};
   for {s in FEAS_POINTS, j in VARS} x_feas[s,j]=X[j].sol[s];

The following statements modify the right-hand side of the constraint by changing the .lb constraint suffix and then use the CONSTANT function to effectively remove the previously declared upper bound by replacing it with the largest machine-representable number:

   CLP_con.lb = CLP_con.ub + 1;
   CLP_con.ub = constant('BIG');

The following statements call the CLP solver again and retrieve the resulting solutions:

   solve with CLP / findallsolns;
   set INFEAS_POINTS;
   INFEAS_POINTS = 1.._NSOL_;
   num x_infeas {INFEAS_POINTS, VARS};
   for {s in INFEAS_POINTS, j in VARS} x_infeas[s,j]=X[j].sol[s];

The remaining statements are the same as before, except that now the PROBLEM and USE PROBLEM statements are used to switch the focus from the CLP problem to the LP problem:

   var Scale {VARS0} >= 0;
   impvar Alpha {j in VARS0} = a[j] * Scale[j];

   con Feas_con {point in FEAS_POINTS}:
      sum {j in VARS} Alpha[j] * x_feas[point,j] <= Alpha[0];
   con Infeas_con {point in INFEAS_POINTS}:
      sum {j in VARS} Alpha[j] * x_infeas[point,j] >= Alpha[0] + 1;

   min Objective1 = abs(a[0]) * Scale[0];

   problem LP_problem include Scale Feas_con Infeas_con Objective1;
   use problem LP_problem;

   solve;
   print a Scale Alpha;

   min Objective2 = sum {j in VARS} abs(a[j]) * Scale[j];
   solve;
   print a Scale Alpha;
quit;