The following example shows how to solve Example 4.1 using PROC OPTMODEL. The input data sets are the same as in that example.
The following PROC OPTMODEL statements read the data sets, build the linear programming model, solve the model, and output
the optimal solution to a SAS data set called ARC1
:
proc optmodel; set <str> NODES; num _supdem_ {NODES} init 0; read data node0 into NODES=[_node_] _supdem_; set <str,str> ARCS; num _lo_ {ARCS} init 0; num _capac_ {ARCS} init .; num _cost_ {ARCS}; num diagonal {ARCS}; num factory {ARCS}; str key_id {ARCS}; str mth_made {ARCS}; str _name_ {ARCS}; read data arc0 nomiss into ARCS=[_tail_ _head_] _lo_ _capac_ _cost_ diagonal factory key_id mth_made _name_; NODES = NODES union (union {<i,j> in ARCS} {i,j}); var Flow {<i,j> in ARCS} >= _lo_[i,j]; for {<i,j> in ARCS: _capac_[i,j] ne .} Flow[i,j].ub = _capac_[i,j]; min obj = sum {<i,j> in ARCS} _cost_[i,j] * Flow[i,j]; con balance {i in NODES}: sum {<(i),j> in ARCS} Flow[i,j] - sum {<j,(i)> in ARCS} Flow[j,i] = _supdem_[i]; num infinity = min {r in {}} r; num excess = sum {i in NODES} _supdem_[i]; if (excess > 0) then do; /* change equality constraint to le constraint */ for {i in NODES: _supdem_[i] > 0} balance[i].lb = -infinity; end; else if (excess < 0) then do; /* change equality constraint to ge constraint */ for {i in NODES: _supdem_[i] < 0} balance[i].ub = infinity; end; solve; num _supply_ {<i,j> in ARCS} = (if _supdem_[i] ne 0 then _supdem_[i] else .); num _demand_ {<i,j> in ARCS} = (if _supdem_[j] ne 0 then -_supdem_[j] else .); num _fcost_ {<i,j> in ARCS} = _cost_[i,j] * Flow[i,j].sol; create data arc1 from [_tail_ _head_] _cost_ _capac_ _lo_ _name_ _supply_ _demand_ _flow_=Flow _fcost_ diagonal factory key_id mth_made; quit;
The statements use both single-dimensional (NODES) and multiple-dimensional (ARCS) index sets, which are populated from the corresponding data set variables in the READ DATA statements. The _SUPDEM_, _LO_, and _CAPAC_ parameters are given initial values, and the NOMISS option in the READ DATA statement tells OPTMODEL to read only the nonmissing values from the input data set. The balance constraint is initially declared as an equality, but depending on the total supply or demand, the sense of this constraint is changed to “” or “” by relaxing the constraint’s lower or upper bound, respectively. The ARC1 output data set contains the same information as in Example 4.1.
The PROC PRINT statements are the same as in Example 4.1:
proc print data=arc1 width=min; var _tail_ _head_ _cost_ _capac_ _lo_ _flow_ _fcost_ diagonal factory key_id mth_made; sum _fcost_; run;
The output is displayed in Output 4.8.1.
Output 4.8.1: Output Data Set
Production Planning/Inventory/Distribution |
Minimum Cost Flow problem |
Obs | _tail_ | _head_ | _cost_ | _capac_ | _lo_ | _flow_ | _fcost_ | diagonal | factory | key_id | mth_made |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | fact1_1 | f1_mar_1 | 127.90 | 500 | 50 | 345 | 44125.50 | 19 | 1 | production | March |
2 | fact1_1 | f1_apr_1 | 78.60 | 600 | 50 | 600 | 47160.00 | 19 | 1 | production | April |
3 | fact1_1 | f1_may_1 | 95.10 | 400 | 50 | 50 | 4755.00 | 19 | 1 | production | May |
4 | f1_mar_1 | f1_apr_1 | 15.00 | 50 | 0 | 0 | 0.00 | 19 | 1 | storage | March |
5 | f1_apr_1 | f1_may_1 | 12.00 | 50 | 0 | 50 | 600.00 | 19 | 1 | storage | April |
6 | f1_apr_1 | f1_mar_1 | 28.00 | 20 | 0 | 20 | 560.00 | 19 | 1 | backorder | April |
7 | f1_may_1 | f1_apr_1 | 28.00 | 20 | 0 | 0 | 0.00 | 19 | 1 | backorder | May |
8 | f1_mar_1 | f2_mar_1 | 11.00 | . | 0 | 0 | 0.00 | 19 | . | f1_to_2 | March |
9 | f1_apr_1 | f2_apr_1 | 11.00 | . | 0 | 30 | 330.00 | 19 | . | f1_to_2 | April |
10 | f1_may_1 | f2_may_1 | 16.00 | . | 0 | 100 | 1600.00 | 19 | . | f1_to_2 | May |
11 | f1_mar_1 | shop1_1 | -327.65 | 250 | 0 | 155 | -50785.75 | 19 | 1 | sales | March |
12 | f1_apr_1 | shop1_1 | -300.00 | 250 | 0 | 250 | -75000.00 | 19 | 1 | sales | April |
13 | f1_may_1 | shop1_1 | -285.00 | 250 | 0 | 0 | 0.00 | 19 | 1 | sales | May |
14 | f1_mar_1 | shop2_1 | -362.74 | 250 | 0 | 250 | -90685.00 | 19 | 1 | sales | March |
15 | f1_apr_1 | shop2_1 | -300.00 | 250 | 0 | 250 | -75000.00 | 19 | 1 | sales | April |
16 | f1_may_1 | shop2_1 | -245.00 | 250 | 0 | 0 | 0.00 | 19 | 1 | sales | May |
17 | fact2_1 | f2_mar_1 | 88.00 | 450 | 35 | 290 | 25520.00 | 19 | 2 | production | March |
18 | fact2_1 | f2_apr_1 | 62.40 | 480 | 35 | 480 | 29952.00 | 19 | 2 | production | April |
19 | fact2_1 | f2_may_1 | 133.80 | 250 | 35 | 35 | 4683.00 | 19 | 2 | production | May |
20 | f2_mar_1 | f2_apr_1 | 18.00 | 30 | 0 | 0 | 0.00 | 19 | 2 | storage | March |
21 | f2_apr_1 | f2_may_1 | 20.00 | 30 | 0 | 15 | 300.00 | 19 | 2 | storage | April |
22 | f2_apr_1 | f2_mar_1 | 17.00 | 15 | 0 | 0 | 0.00 | 19 | 2 | backorder | April |
23 | f2_may_1 | f2_apr_1 | 25.00 | 15 | 0 | 0 | 0.00 | 19 | 2 | backorder | May |
24 | f2_mar_1 | f1_mar_1 | 10.00 | 40 | 0 | 40 | 400.00 | 19 | . | f2_to_1 | March |
25 | f2_apr_1 | f1_apr_1 | 11.00 | 40 | 0 | 0 | 0.00 | 19 | . | f2_to_1 | April |
26 | f2_may_1 | f1_may_1 | 13.00 | 40 | 0 | 0 | 0.00 | 19 | . | f2_to_1 | May |
27 | f2_mar_1 | shop1_1 | -297.40 | 250 | 0 | 250 | -74350.00 | 19 | 2 | sales | March |
28 | f2_apr_1 | shop1_1 | -290.00 | 250 | 0 | 245 | -71050.00 | 19 | 2 | sales | April |
29 | f2_may_1 | shop1_1 | -292.00 | 250 | 0 | 0 | 0.00 | 19 | 2 | sales | May |
30 | f2_mar_1 | shop2_1 | -272.70 | 250 | 0 | 0 | 0.00 | 19 | 2 | sales | March |
31 | f2_apr_1 | shop2_1 | -312.00 | 250 | 0 | 250 | -78000.00 | 19 | 2 | sales | April |
32 | f2_may_1 | shop2_1 | -299.00 | 250 | 0 | 150 | -44850.00 | 19 | 2 | sales | May |
33 | fact1_2 | f1_mar_2 | 217.90 | 400 | 40 | 400 | 87160.00 | 25 | 1 | production | March |
34 | fact1_2 | f1_apr_2 | 174.50 | 550 | 50 | 550 | 95975.00 | 25 | 1 | production | April |
35 | fact1_2 | f1_may_2 | 133.30 | 350 | 40 | 40 | 5332.00 | 25 | 1 | production | May |
36 | f1_mar_2 | f1_apr_2 | 20.00 | 40 | 0 | 0 | 0.00 | 25 | 1 | storage | March |
37 | f1_apr_2 | f1_may_2 | 18.00 | 40 | 0 | 0 | 0.00 | 25 | 1 | storage | April |
38 | f1_apr_2 | f1_mar_2 | 32.00 | 30 | 0 | 30 | 960.00 | 25 | 1 | backorder | April |
39 | f1_may_2 | f1_apr_2 | 41.00 | 15 | 0 | 15 | 615.00 | 25 | 1 | backorder | May |
40 | f1_mar_2 | f2_mar_2 | 23.00 | . | 0 | 0 | 0.00 | 25 | . | f1_to_2 | March |
41 | f1_apr_2 | f2_apr_2 | 23.00 | . | 0 | 0 | 0.00 | 25 | . | f1_to_2 | April |
42 | f1_may_2 | f2_may_2 | 26.00 | . | 0 | 0 | 0.00 | 25 | . | f1_to_2 | May |
43 | f1_mar_2 | shop1_2 | -559.76 | . | 0 | 0 | 0.00 | 25 | 1 | sales | March |
44 | f1_apr_2 | shop1_2 | -524.28 | . | 0 | 0 | 0.00 | 25 | 1 | sales | April |
45 | f1_may_2 | shop1_2 | -475.02 | . | 0 | 25 | -11875.50 | 25 | 1 | sales | May |
46 | f1_mar_2 | shop2_2 | -623.89 | . | 0 | 455 | -283869.95 | 25 | 1 | sales | March |
47 | f1_apr_2 | shop2_2 | -549.68 | . | 0 | 535 | -294078.80 | 25 | 1 | sales | April |
48 | f1_may_2 | shop2_2 | -460.00 | . | 0 | 0 | 0.00 | 25 | 1 | sales | May |
49 | fact2_2 | f2_mar_2 | 182.00 | 650 | 35 | 645 | 117390.00 | 25 | 2 | production | March |
50 | fact2_2 | f2_apr_2 | 196.70 | 680 | 35 | 680 | 133756.00 | 25 | 2 | production | April |
51 | fact2_2 | f2_may_2 | 201.40 | 550 | 35 | 35 | 7049.00 | 25 | 2 | production | May |
52 | f2_mar_2 | f2_apr_2 | 28.00 | 50 | 0 | 0 | 0.00 | 25 | 2 | storage | March |
53 | f2_apr_2 | f2_may_2 | 38.00 | 50 | 0 | 0 | 0.00 | 25 | 2 | storage | April |
54 | f2_apr_2 | f2_mar_2 | 31.00 | 15 | 0 | 0 | 0.00 | 25 | 2 | backorder | April |
55 | f2_may_2 | f2_apr_2 | 54.00 | 15 | 0 | 15 | 810.00 | 25 | 2 | backorder | May |
56 | f2_mar_2 | f1_mar_2 | 20.00 | 25 | 0 | 25 | 500.00 | 25 | . | f2_to_1 | March |
57 | f2_apr_2 | f1_apr_2 | 21.00 | 25 | 0 | 0 | 0.00 | 25 | . | f2_to_1 | April |
58 | f2_may_2 | f1_may_2 | 43.00 | 25 | 0 | 0 | 0.00 | 25 | . | f2_to_1 | May |
59 | f2_mar_2 | shop1_2 | -567.83 | 500 | 0 | 500 | -283915.00 | 25 | 2 | sales | March |
60 | f2_apr_2 | shop1_2 | -542.19 | 500 | 0 | 375 | -203321.25 | 25 | 2 | sales | April |
61 | f2_may_2 | shop1_2 | -461.56 | 500 | 0 | 0 | 0.00 | 25 | 2 | sales | May |
62 | f2_mar_2 | shop2_2 | -542.83 | 500 | 0 | 120 | -65139.60 | 25 | 2 | sales | March |
63 | f2_apr_2 | shop2_2 | -559.19 | 500 | 0 | 320 | -178940.80 | 25 | 2 | sales | April |
64 | f2_may_2 | shop2_2 | -489.06 | 500 | 0 | 20 | -9781.20 | 25 | 2 | sales | May |
-1281110.35 |
The optimal objective value is the same as in Example 4.1. The log is displayed in Output 4.8.2.
Output 4.8.2: OPTMODEL Log
Production Planning/Inventory/Distribution |
Minimum Cost Flow problem |
NOTE: There were 8 observations read from the data set WORK.NODE0. |
NOTE: There were 64 observations read from the data set WORK.ARC0. |
NOTE: The problem has 64 variables (0 free, 0 fixed). |
NOTE: The problem has 20 linear constraints (4 LE, 16 EQ, 0 GE, 0 range). |
NOTE: The problem has 128 linear constraint coefficients. |
NOTE: The problem has 0 nonlinear constraints (0 LE, 0 EQ, 0 GE, 0 range). |
NOTE: The OPTMODEL presolver is disabled for linear problems. |
NOTE: The OPTLP presolver value AUTOMATIC is applied. |
NOTE: The OPTLP presolver removed 0 variables and 0 constraints. |
NOTE: The OPTLP presolver removed 0 constraint coefficients. |
NOTE: The presolved problem has 64 variables, 20 constraints, and 128 |
constraint coefficients. |
NOTE: The DUAL SIMPLEX solver is called. |
Objective |
Phase Iteration Value |
2 1 -2952213 |
2 30 -1281110 |
NOTE: Optimal. |
NOTE: Objective = -1281110.35. |
NOTE: The data set WORK.ARC1 has 64 observations and 14 variables. |