Optimization Action Set

Simple Integer Linear Program

This section contains PROC CAS code.

Note: Input data must be accessible in your CAS session, either as a CAS table or as a transient-scope table. A CAS table has a two-level name: the first level is your CAS engine libref, and the second level is the table name. You refer to this table in the CAS procedure by specifying only the second level. For more information about two-level names, see Chapter 3, Shared Concepts (SAS Optimization: Mathematical Optimization Procedures). A transient-scope table is called directly from the action and exists in memory for the duration of the action. For more information about accessing data, see SAS Viya: System Programming Guide. For more information about PROC CAS and programming in CASL, see SAS Cloud Analytic Services: CASL Programmer’s Guide and SAS Cloud Analytic Services: CASL Reference.

This example illustrates a model in an MPS-format data table. This data table is created in CAS, and the solveMilp action solves the corresponding mixed integer linear program.

Consider a scenario where you have a container with a set of limiting attributes (volume V and weight W), and you have a set I of items that you want to pack. Each item type i has a certain value p Subscript i, a volume v Subscript i, and a weight w Subscript i. You must choose at most four items of each type so that the total value is maximized and all the chosen items fit into the container. Let x Subscript i be the number of items of type i to be included in the container. This model can be formulated as the following integer linear program:

StartLayout 1st Row 1st Column max 2nd Column sigma-summation Underscript i element-of upper I Endscripts p Subscript i Baseline x Subscript i 3rd Column Blank 2nd Row 1st Column normal s period normal t period 2nd Column sigma-summation Underscript i element-of upper I Endscripts v Subscript i Baseline x Subscript i 3rd Column less-than-or-equal-to 4th Column upper V 5th Column Blank 6th Column left-parenthesis normal v normal o normal l normal u normal m normal e normal bar normal c normal o normal n right-parenthesis 3rd Row 1st Column Blank 2nd Column sigma-summation Underscript i element-of upper I Endscripts w Subscript i Baseline x Subscript i 3rd Column less-than-or-equal-to 4th Column upper W 5th Column Blank 6th Column left-parenthesis normal w normal e normal i normal g normal h normal t normal bar normal c normal o normal n right-parenthesis 4th Row 1st Column Blank 2nd Column x Subscript i 3rd Column less-than-or-equal-to 4th Column 4 5th Column for-all i element-of upper I 5th Row 1st Column Blank 2nd Column x Subscript i Baseline element-of double-struck upper Z Superscript plus 3rd Column Blank 4th Column Blank 5th Column for-all i element-of upper I EndLayout

The constraint (volume_con) enforces the volume capacity limit, and the constraint (weight_con) enforces the weight capacity limit. The following DATA step shows an instance of this problem in an MPS-format data table. The DATA step assumes that your CAS engine libref is named mycas, but you can substitute any appropriately defined CAS engine libref.

data mycas.ex1data;
    input _id_ field1 $ field2 $ field3 $ field4 field5 $ field6;
    datalines;
1  NAME       .             ex1data           .     .                 .
2  ROWS       .             .                 .     .                 .
3  MAX        z             .                 .     .                 .
4  L          volume_con    .                 .     .                 .
5  L          weight_con    .                 .     .                 .
6  COLUMNS    .             .                 .     .                 .
7  .          .MRK0         'MARKER'          .     'INTORG'          .
8  .          x[1]          z                 1     volume_con       10
9  .          x[1]          weight_con       12     .                 .
10 .          x[2]          z                 2     volume_con      300
11 .          x[2]          weight_con       15     .                 .
12 .          x[3]          z                 3     volume_con      250
13 .          x[3]          weight_con       72     .                 .
14 .          x[4]          z                 4     volume_con      610
15 .          x[4]          weight_con      100     .                 .
16 .          x[5]          z                 5     volume_con      500
17 .          x[5]          weight_con      223     .                 .
18 .          x[6]          z                 6     volume_con      120
19 .          x[6]          weight_con       16     .                 .
20 .          x[7]          z                 7     volume_con       45
21 .          x[7]          weight_con       73     .                 .
22 .          x[8]          z                 8     volume_con      100
23 .          x[8]          weight_con       12     .                 .
24 .          x[9]          z                 9     volume_con      200
25 .          x[9]          weight_con      200     .                 .
26 .          x[10]         z                10     volume_con       61
27 .          x[10]         weight_con      110     .                 .
28 .          .MRK1         'MARKER'          .     'INTEND'          .
29 RHS        .             .                 .     .                 .
30 .          .RHS.         volume_con     1000     .                 .
31 .          .RHS.         weight_con      500     .                 .
32 BOUNDS     .             .                 .     .                 .
33 UP         .BOUNDS.      x[1]              4     .                 .
34 UP         .BOUNDS.      x[2]              4     .                 .
35 UP         .BOUNDS.      x[3]              4     .                 .
36 UP         .BOUNDS.      x[4]              4     .                 .
37 UP         .BOUNDS.      x[5]              4     .                 .
38 UP         .BOUNDS.      x[6]              4     .                 .
39 UP         .BOUNDS.      x[7]              4     .                 .
40 UP         .BOUNDS.      x[8]              4     .                 .
41 UP         .BOUNDS.      x[9]              4     .                 .
42 UP         .BOUNDS.      x[10]             4     .                 .
43 ENDATA     .             .                 .     .                 .
;

Alternatively, you can use the upload action on a CSV file that has the following content:

_id_ ,field1  ,field2     ,field3      ,field4 ,field5     ,field6
1    ,NAME    ,           ,ex1data     ,       ,           ,
2    ,ROWS    ,           ,            ,       ,           ,
3    ,MAX     ,z          ,            ,       ,           ,
4    ,L       ,volume_con ,            ,       ,           ,
5    ,L       ,weight_con ,            ,       ,           ,
6    ,COLUMNS ,           ,            ,       ,           ,
7    ,        ,.MRK0      ,'MARKER'    ,       ,'INTORG'   ,
8    ,        ,x[1]       ,z           ,1      ,volume_con ,10
9    ,        ,x[1]       ,weight_con  ,12     ,           ,
10   ,        ,x[2]       ,z           ,2      ,volume_con ,300
11   ,        ,x[2]       ,weight_con  ,15     ,           ,
12   ,        ,x[3]       ,z           ,3      ,volume_con ,250
13   ,        ,x[3]       ,weight_con  ,72     ,           ,
14   ,        ,x[4]       ,z           ,4      ,volume_con ,610
15   ,        ,x[4]       ,weight_con  ,100    ,           ,
16   ,        ,x[5]       ,z           ,5      ,volume_con ,500
17   ,        ,x[5]       ,weight_con  ,223    ,           ,
18   ,        ,x[6]       ,z           ,6      ,volume_con ,120
19   ,        ,x[6]       ,weight_con  ,16     ,           ,
20   ,        ,x[7]       ,z           ,7      ,volume_con ,45
21   ,        ,x[7]       ,weight_con  ,73     ,           ,
22   ,        ,x[8]       ,z           ,8      ,volume_con ,100
23   ,        ,x[8]       ,weight_con  ,12     ,           ,
24   ,        ,x[9]       ,z           ,9      ,volume_con ,200
25   ,        ,x[9]       ,weight_con  ,200    ,           ,
26   ,        ,x[10]      ,z           ,10     ,volume_con ,61
27   ,        ,x[10]      ,weight_con  ,110    ,           ,
28   ,        ,.MRK1      ,'MARKER'    ,       ,'INTEND'   ,
29   ,RHS     ,           ,            ,       ,           ,
30   ,        ,.RHS.      ,volume_con  ,1000   ,           ,
31   ,        ,.RHS.      ,weight_con  ,500    ,           ,
32   ,BOUNDS  ,           ,            ,       ,           ,
33   ,UP      ,.BOUNDS.   ,x[1]        ,4      ,           ,
34   ,UP      ,.BOUNDS.   ,x[2]        ,4      ,           ,
35   ,UP      ,.BOUNDS.   ,x[3]        ,4      ,           ,
36   ,UP      ,.BOUNDS.   ,x[4]        ,4      ,           ,
37   ,UP      ,.BOUNDS.   ,x[5]        ,4      ,           ,
38   ,UP      ,.BOUNDS.   ,x[6]        ,4      ,           ,
39   ,UP      ,.BOUNDS.   ,x[7]        ,4      ,           ,
40   ,UP      ,.BOUNDS.   ,x[8]        ,4      ,           ,
41   ,UP      ,.BOUNDS.   ,x[9]        ,4      ,           ,
42   ,UP      ,.BOUNDS.   ,x[10]       ,4      ,           ,
43   ,ENDATA  ,           ,            ,       ,           ,

In the COLUMNS section of this data table, the name of the objective is z, and the objective coefficients p Subscript i appear in field4. The coefficients v Subscript i of (volume_con) appear in field6. The coefficients w Subscript i of (weight_con) appear in field4. In the RHS section, the bounds V and W appear in field4. The _id_ column ensures that the data can be read in the correct order even if the table is stored on separate machines in CAS.

You can solve this problem by using the following statements to call the solveMilp action:

proc cas;
   loadactionset "optimization";
   action optimization.solveMilp result=r status=s /
      data      = {name = "ex1data"}
      primalOut = {name = "ex1soln" replace = true};
   run;
   print r.ProblemSummary; run;
   print r.SolutionSummary; run;
   action table.fetch / table = "ex1soln"; run;
quit;

The progress of the solver is shown in Output 2.13.1.

Output 2.13.1: Simple Integer Linear Program solveMilp Log

NOTE: Active Session now MYSESS.                                                
NOTE: Added action set 'optimization'.                                          
NOTE: The problem ex1data has 10 variables (0 binary, 10 integer, 0 free, 0     
      fixed).                                                                   
NOTE: The problem has 2 constraints (2 LE, 0 EQ, 0 GE, 0 range).                
NOTE: The problem has 20 constraint coefficients.                               
NOTE: The initial MILP heuristics are applied.                                  
NOTE: The MILP presolver value AUTOMATIC is applied.                            
NOTE: The MILP presolver removed 2 variables and 0 constraints.                 
NOTE: The MILP presolver removed 4 constraint coefficients.                     
NOTE: The MILP presolver modified 0 constraint coefficients.                    
NOTE: The presolved problem has 8 variables, 2 constraints, and 16 constraint   
      coefficients.                                                             
NOTE: The MILP solver is called.                                                
NOTE: The parallel Branch and Cut algorithm is used.                            
NOTE: The Branch and Cut algorithm is using up to 80 threads.                   
          Node   Active   Sols    BestInteger      BestBound      Gap    Time   
             0        1      4     85.0000000    158.0000000   46.20%       0   
             0        1      4     85.0000000     88.0955497    3.51%       0   
             0        1      4     85.0000000     87.4545455    2.81%       0   
NOTE: The MILP presolver is applied again.                                      
             0        1      5     87.0000000     87.4545455    0.52%       0   
NOTE: Optimal.                                                                  
NOTE: Objective = 87.                                                           


The problem summary and solution summary are shown in Output 2.13.2 and Output 2.13.3.

Output 2.13.2: Problem Summary

ProblemSummary: Results from optimization.solveMilp

Problem Summary
Problem Nameex1data
Objective SenseMaximization
Objective Functionz
RHS.RHS.
  
Number of Variables10
Bounded Above0
Bounded Below0
Bounded Above and Below10
Free0
Fixed0
Binary0
Integer10
  
Number of Constraints2
LE (<=)2
EQ (=)0
GE (>=)0
Range0
  
Constraint Coefficients20


Output 2.13.3: Solution Summary

SolutionSummary: Results from optimization.solveMilp

Solution Summary
SolverMILP
AlgorithmBranch and Cut
Objective Functionz
Solution StatusOptimal
Objective Value87
  
Relative Gap0
Absolute Gap0
Primal Infeasibility0
Bound Infeasibility0
Integer Infeasibility0
  
Best Bound87
Nodes1
Solutions Found5
Iterations14
  
Presolve Time0.00
Solution Time0.05


The data table ex1soln displayed in Output 2.13.4 shows the optimal solution that is found.

Output 2.13.4: Simple Integer Linear Program Solution

Results from table.fetch

Selected Rows from Table EX1SOLN
_Index_Objective Function IDRHS IDVariable NameVariable
Type
Objective CoefficientLower BoundUpper BoundVariable ValueSolution
1z.RHS.x[1]I10401
2z.RHS.x[2]I20401
3z.RHS.x[3]I30401
4z.RHS.x[4]I40401
5z.RHS.x[5]I50401
6z.RHS.x[6]I60431
7z.RHS.x[7]I70411
8z.RHS.x[8]I80441
9z.RHS.x[9]I90401
10z.RHS.x[10]I100431


The optimal solution is x 6 equals 3 comma x 7 equals 1 comma x 8 equals 4, and x 10 equals 3, with a total value of 87. From this solution, you can compute the total volume used, which is 988 (less-than-or-equal-to upper V equals 1000); the total weight used is 499 (less-than-or-equal-to upper W equals 500).

Simple Integer Linear Program

This section contains Lua code for the analysis in the CASL version of this example, which contains details about the results.

Note: In order to run this code, the data that are described in the CASL version need to be accessible to the CAS server. One way to do this is to convert the ex1data data to the comma-separated-value (CSV) file ex1data.csv and then use the following code to load the CSV file into CAS:

s:loadtable{casLib="casuser", path="ex1data.csv"}

For more information about coding in Lua, see Getting Started with SAS Viya for Lua and SAS Viya: System Programming Guide.

The following code solves the simple integer linear program stored in the data table ex1data:

s:optimization_solveMilp{
   data        = {name = "ex1data"},
   primalOut   = {name = "ex1soln", replace = true}}

Simple Integer Linear Program

This section contains Python code for the analysis in the CASL version of this example, which contains details about the results.

Note: In order to run this code, the data that are described in the CASL version need to be accessible to the CAS server. One way to do this is to convert the ex1data data to the comma-separated-value (CSV) file ex1data.csv and then use the following code to load the CSV file into CAS:

s.upload_file('ex1data.csv')

For more information about coding in Python, see Getting Started with SAS Viya for Python and SAS Viya: System Programming Guide.

The following code solves the simple integer linear program stored in the data table ex1data:

s.optimization.solveMilp(
    data        = {"name": "ex1data"},
    primalOut   = {"name": "ex1soln", "replace": True})

Simple Integer Linear Program

This section contains R code for the analysis in the CASL version of this example, which contains details about the results.

Note: In order to run this code, the data that are described in the CASL version need to be accessible to the CAS server. One way to do this is to convert the ex1data data to the comma-separated-value (CSV) file ex1data.csv and then use the following code to load the CSV file into CAS:

m <- s$upload("ex1data.csv", casOut=list(name="ex1data"))

For more information about coding in R, see Getting Started with SAS Viya for R and SAS Viya: System Programming Guide.

The following code solves the simple integer linear program stored in the data table ex1data:

cas.optimization.solveMilp(s,
   data        = "ex1data",
   primalOut   = list(name="ex1soln", replace=TRUE))
Last updated: December 08, 2021