The following example shows how to use PROC OPTMODEL to solve the example "Production, Inventory, Distribution Problem" in Chapter 5: The NETFLOW Procedure in SAS/OR 14.1 User's Guide: Mathematical Programming Legacy Procedures. The input data sets are the same as in that example.
title 'Minimum-Cost Flow Problem'; title2 'Production Planning/Inventory/Distribution'; data node0; input _node_ $ _supdem_ ; datalines; fact1_1 1000 fact2_1 850 fact1_2 1000 fact2_2 1500 shop1_1 -900 shop2_1 -900 shop1_2 -900 shop2_2 -1450 ;
data arc0; input _tail_ $ _head_ $ _cost_ _capac_ _lo_ diagonal factory key_id $10. mth_made $ _name_&$17.; datalines; fact1_1 f1_mar_1 127.9 500 50 19 1 production March prod f1 19 mar fact1_1 f1_apr_1 78.6 600 50 19 1 production April prod f1 19 apl fact1_1 f1_may_1 95.1 400 50 19 1 production May . f1_mar_1 f1_apr_1 15 50 . 19 1 storage March . f1_apr_1 f1_may_1 12 50 . 19 1 storage April . f1_apr_1 f1_mar_1 28 20 . 19 1 backorder April back f1 19 apl f1_may_1 f1_apr_1 28 20 . 19 1 backorder May back f1 19 may f1_mar_1 f2_mar_1 11 . . 19 . f1_to_2 March . f1_apr_1 f2_apr_1 11 . . 19 . f1_to_2 April . f1_may_1 f2_may_1 16 . . 19 . f1_to_2 May . f1_mar_1 shop1_1 -327.65 250 . 19 1 sales March . f1_apr_1 shop1_1 -300 250 . 19 1 sales April . f1_may_1 shop1_1 -285 250 . 19 1 sales May . f1_mar_1 shop2_1 -362.74 250 . 19 1 sales March . f1_apr_1 shop2_1 -300 250 . 19 1 sales April . f1_may_1 shop2_1 -245 250 . 19 1 sales May . fact2_1 f2_mar_1 88.0 450 35 19 2 production March prod f2 19 mar fact2_1 f2_apr_1 62.4 480 35 19 2 production April prod f2 19 apl fact2_1 f2_may_1 133.8 250 35 19 2 production May . f2_mar_1 f2_apr_1 18 30 . 19 2 storage March . f2_apr_1 f2_may_1 20 30 . 19 2 storage April . f2_apr_1 f2_mar_1 17 15 . 19 2 backorder April back f2 19 apl f2_may_1 f2_apr_1 25 15 . 19 2 backorder May back f2 19 may f2_mar_1 f1_mar_1 10 40 . 19 . f2_to_1 March . f2_apr_1 f1_apr_1 11 40 . 19 . f2_to_1 April . f2_may_1 f1_may_1 13 40 . 19 . f2_to_1 May . f2_mar_1 shop1_1 -297.4 250 . 19 2 sales March . f2_apr_1 shop1_1 -290 250 . 19 2 sales April . f2_may_1 shop1_1 -292 250 . 19 2 sales May . f2_mar_1 shop2_1 -272.7 250 . 19 2 sales March . f2_apr_1 shop2_1 -312 250 . 19 2 sales April . f2_may_1 shop2_1 -299 250 . 19 2 sales May . fact1_2 f1_mar_2 217.9 400 40 25 1 production March prod f1 25 mar fact1_2 f1_apr_2 174.5 550 50 25 1 production April prod f1 25 apl fact1_2 f1_may_2 133.3 350 40 25 1 production May . f1_mar_2 f1_apr_2 20 40 . 25 1 storage March . f1_apr_2 f1_may_2 18 40 . 25 1 storage April . f1_apr_2 f1_mar_2 32 30 . 25 1 backorder April back f1 25 apl f1_may_2 f1_apr_2 41 15 . 25 1 backorder May back f1 25 may f1_mar_2 f2_mar_2 23 . . 25 . f1_to_2 March . f1_apr_2 f2_apr_2 23 . . 25 . f1_to_2 April . f1_may_2 f2_may_2 26 . . 25 . f1_to_2 May . f1_mar_2 shop1_2 -559.76 . . 25 1 sales March . f1_apr_2 shop1_2 -524.28 . . 25 1 sales April . f1_may_2 shop1_2 -475.02 . . 25 1 sales May . f1_mar_2 shop2_2 -623.89 . . 25 1 sales March . f1_apr_2 shop2_2 -549.68 . . 25 1 sales April . f1_may_2 shop2_2 -460.00 . . 25 1 sales May . fact2_2 f2_mar_2 182.0 650 35 25 2 production March prod f2 25 mar fact2_2 f2_apr_2 196.7 680 35 25 2 production April prod f2 25 apl fact2_2 f2_may_2 201.4 550 35 25 2 production May . f2_mar_2 f2_apr_2 28 50 . 25 2 storage March . f2_apr_2 f2_may_2 38 50 . 25 2 storage April . f2_apr_2 f2_mar_2 31 15 . 25 2 backorder April back f2 25 apl f2_may_2 f2_apr_2 54 15 . 25 2 backorder May back f2 25 may f2_mar_2 f1_mar_2 20 25 . 25 . f2_to_1 March . f2_apr_2 f1_apr_2 21 25 . 25 . f2_to_1 April . f2_may_2 f1_may_2 43 25 . 25 . f2_to_1 May . f2_mar_2 shop1_2 -567.83 500 . 25 2 sales March . f2_apr_2 shop1_2 -542.19 500 . 25 2 sales April . f2_may_2 shop1_2 -461.56 500 . 25 2 sales May . f2_mar_2 shop2_2 -542.83 500 . 25 2 sales March . f2_apr_2 shop2_2 -559.19 500 . 25 2 sales April . f2_may_2 shop2_2 -489.06 500 . 25 2 sales May . ;
The following PROC OPTMODEL statements read the data sets, build the linear programming model, solve the model, and output
the optimal solution to SAS data sets called ARC1
and NODE2
:
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 = constant('BIG'); num excess = sum {i in NODES} _supdem_[i]; if (excess > 0) then do; /* change equality constraint to le constraint for supply nodes */ 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 demand nodes */ 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_ _rcost_ = (if Flow[_tail_,_head_].rc ne 0 then Flow[_tail_,_head_].rc else .) _status_ = Flow.status diagonal factory key_id mth_made; create data node2 from [_node_] _supdem_ = (if _supdem_[_node_] ne 0 then _supdem_[_node_] else .) _dual_ = balance.dual; quit;
The PROC OPTMODEL 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 PROC 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 most of the same information as in the NETFLOW example, including reduced cost, basis status, and
dual values. The _ANUMB_ and _TNUMB_ values do not apply here.
The PROC PRINT statements are similar to the PROC NETFLOW example:
options ls=80 ps=54; proc print data=arc1 heading=h width=min; var _tail_ _head_ _cost_ _capac_ _lo_ _name_ _supply_ _demand_ _flow_ _fcost_; sum _fcost_; run; proc print data=arc1 heading=h width=min; var _rcost_ _status_ diagonal factory key_id mth_made; run; proc print data=node2; run;
The output data sets are displayed in Output 7.8.1.
Output 7.8.1: Output Data Sets
Obs | _tail_ | _head_ | _cost_ | _capac_ | _lo_ | _name_ | _supply_ | _demand_ | _flow_ | _fcost_ |
---|---|---|---|---|---|---|---|---|---|---|
1 | fact1_1 | f1_mar_1 | 127.90 | 500 | 50 | prod f1 19 mar | 1000 | . | 345 | 44125.50 |
2 | fact1_1 | f1_apr_1 | 78.60 | 600 | 50 | prod f1 19 apl | 1000 | . | 600 | 47160.00 |
3 | fact1_1 | f1_may_1 | 95.10 | 400 | 50 | 1000 | . | 50 | 4755.00 | |
4 | f1_mar_1 | f1_apr_1 | 15.00 | 50 | 0 | . | . | 0 | 0.00 | |
5 | f1_apr_1 | f1_may_1 | 12.00 | 50 | 0 | . | . | 50 | 600.00 | |
6 | f1_apr_1 | f1_mar_1 | 28.00 | 20 | 0 | back f1 19 apl | . | . | 20 | 560.00 |
7 | f1_may_1 | f1_apr_1 | 28.00 | 20 | 0 | back f1 19 may | . | . | 0 | 0.00 |
8 | f1_mar_1 | f2_mar_1 | 11.00 | . | 0 | . | . | 0 | 0.00 | |
9 | f1_apr_1 | f2_apr_1 | 11.00 | . | 0 | . | . | 30 | 330.00 | |
10 | f1_may_1 | f2_may_1 | 16.00 | . | 0 | . | . | 100 | 1600.00 | |
11 | f1_mar_1 | shop1_1 | -327.65 | 250 | 0 | . | 900 | 155 | -50785.75 | |
12 | f1_apr_1 | shop1_1 | -300.00 | 250 | 0 | . | 900 | 250 | -75000.00 | |
13 | f1_may_1 | shop1_1 | -285.00 | 250 | 0 | . | 900 | 0 | 0.00 | |
14 | f1_mar_1 | shop2_1 | -362.74 | 250 | 0 | . | 900 | 250 | -90685.00 | |
15 | f1_apr_1 | shop2_1 | -300.00 | 250 | 0 | . | 900 | 250 | -75000.00 | |
16 | f1_may_1 | shop2_1 | -245.00 | 250 | 0 | . | 900 | 0 | 0.00 | |
17 | fact2_1 | f2_mar_1 | 88.00 | 450 | 35 | prod f2 19 mar | 850 | . | 290 | 25520.00 |
18 | fact2_1 | f2_apr_1 | 62.40 | 480 | 35 | prod f2 19 apl | 850 | . | 480 | 29952.00 |
19 | fact2_1 | f2_may_1 | 133.80 | 250 | 35 | 850 | . | 35 | 4683.00 | |
20 | f2_mar_1 | f2_apr_1 | 18.00 | 30 | 0 | . | . | 0 | 0.00 | |
21 | f2_apr_1 | f2_may_1 | 20.00 | 30 | 0 | . | . | 15 | 300.00 | |
22 | f2_apr_1 | f2_mar_1 | 17.00 | 15 | 0 | back f2 19 apl | . | . | 0 | 0.00 |
23 | f2_may_1 | f2_apr_1 | 25.00 | 15 | 0 | back f2 19 may | . | . | 0 | 0.00 |
24 | f2_mar_1 | f1_mar_1 | 10.00 | 40 | 0 | . | . | 40 | 400.00 | |
25 | f2_apr_1 | f1_apr_1 | 11.00 | 40 | 0 | . | . | 0 | 0.00 | |
26 | f2_may_1 | f1_may_1 | 13.00 | 40 | 0 | . | . | 0 | 0.00 | |
27 | f2_mar_1 | shop1_1 | -297.40 | 250 | 0 | . | 900 | 250 | -74350.00 | |
28 | f2_apr_1 | shop1_1 | -290.00 | 250 | 0 | . | 900 | 245 | -71050.00 | |
29 | f2_may_1 | shop1_1 | -292.00 | 250 | 0 | . | 900 | 0 | 0.00 | |
30 | f2_mar_1 | shop2_1 | -272.70 | 250 | 0 | . | 900 | 0 | 0.00 | |
31 | f2_apr_1 | shop2_1 | -312.00 | 250 | 0 | . | 900 | 250 | -78000.00 | |
32 | f2_may_1 | shop2_1 | -299.00 | 250 | 0 | . | 900 | 150 | -44850.00 | |
33 | fact1_2 | f1_mar_2 | 217.90 | 400 | 40 | prod f1 25 mar | 1000 | . | 400 | 87160.00 |
34 | fact1_2 | f1_apr_2 | 174.50 | 550 | 50 | prod f1 25 apl | 1000 | . | 550 | 95975.00 |
35 | fact1_2 | f1_may_2 | 133.30 | 350 | 40 | 1000 | . | 40 | 5332.00 | |
36 | f1_mar_2 | f1_apr_2 | 20.00 | 40 | 0 | . | . | 0 | 0.00 | |
37 | f1_apr_2 | f1_may_2 | 18.00 | 40 | 0 | . | . | 0 | 0.00 | |
38 | f1_apr_2 | f1_mar_2 | 32.00 | 30 | 0 | back f1 25 apl | . | . | 30 | 960.00 |
39 | f1_may_2 | f1_apr_2 | 41.00 | 15 | 0 | back f1 25 may | . | . | 15 | 615.00 |
40 | f1_mar_2 | f2_mar_2 | 23.00 | . | 0 | . | . | 0 | 0.00 | |
41 | f1_apr_2 | f2_apr_2 | 23.00 | . | 0 | . | . | 0 | 0.00 | |
42 | f1_may_2 | f2_may_2 | 26.00 | . | 0 | . | . | 0 | 0.00 | |
43 | f1_mar_2 | shop1_2 | -559.76 | . | 0 | . | 900 | 0 | 0.00 | |
44 | f1_apr_2 | shop1_2 | -524.28 | . | 0 | . | 900 | 0 | 0.00 | |
45 | f1_may_2 | shop1_2 | -475.02 | . | 0 | . | 900 | 25 | -11875.50 | |
46 | f1_mar_2 | shop2_2 | -623.89 | . | 0 | . | 1450 | 455 | -283869.95 | |
47 | f1_apr_2 | shop2_2 | -549.68 | . | 0 | . | 1450 | 535 | -294078.80 | |
48 | f1_may_2 | shop2_2 | -460.00 | . | 0 | . | 1450 | 0 | 0.00 | |
49 | fact2_2 | f2_mar_2 | 182.00 | 650 | 35 | prod f2 25 mar | 1500 | . | 645 | 117390.00 |
50 | fact2_2 | f2_apr_2 | 196.70 | 680 | 35 | prod f2 25 apl | 1500 | . | 680 | 133756.00 |
51 | fact2_2 | f2_may_2 | 201.40 | 550 | 35 | 1500 | . | 35 | 7049.00 | |
52 | f2_mar_2 | f2_apr_2 | 28.00 | 50 | 0 | . | . | 0 | 0.00 | |
53 | f2_apr_2 | f2_may_2 | 38.00 | 50 | 0 | . | . | 0 | 0.00 | |
54 | f2_apr_2 | f2_mar_2 | 31.00 | 15 | 0 | back f2 25 apl | . | . | 0 | 0.00 |
55 | f2_may_2 | f2_apr_2 | 54.00 | 15 | 0 | back f2 25 may | . | . | 15 | 810.00 |
56 | f2_mar_2 | f1_mar_2 | 20.00 | 25 | 0 | . | . | 25 | 500.00 | |
57 | f2_apr_2 | f1_apr_2 | 21.00 | 25 | 0 | . | . | 0 | 0.00 | |
58 | f2_may_2 | f1_may_2 | 43.00 | 25 | 0 | . | . | 0 | 0.00 | |
59 | f2_mar_2 | shop1_2 | -567.83 | 500 | 0 | . | 900 | 500 | -283915.00 | |
60 | f2_apr_2 | shop1_2 | -542.19 | 500 | 0 | . | 900 | 375 | -203321.25 | |
61 | f2_may_2 | shop1_2 | -461.56 | 500 | 0 | . | 900 | 0 | 0.00 | |
62 | f2_mar_2 | shop2_2 | -542.83 | 500 | 0 | . | 1450 | 120 | -65139.60 | |
63 | f2_apr_2 | shop2_2 | -559.19 | 500 | 0 | . | 1450 | 320 | -178940.80 | |
64 | f2_may_2 | shop2_2 | -489.06 | 500 | 0 | . | 1450 | 20 | -9781.20 | |
-1281110.35 |
Obs | _rcost_ | _status_ | diagonal | factory | key_id | mth_made |
---|---|---|---|---|---|---|
1 | . | B | 19 | 1 | production | March |
2 | -0.65 | U | 19 | 1 | production | April |
3 | 0.85 | L | 19 | 1 | production | May |
4 | 63.65 | L | 19 | 1 | storage | March |
5 | -3.00 | U | 19 | 1 | storage | April |
6 | -20.65 | U | 19 | 1 | backorder | April |
7 | 43.00 | L | 19 | 1 | backorder | May |
8 | 50.90 | L | 19 | . | f1_to_2 | March |
9 | . | B | 19 | . | f1_to_2 | April |
10 | . | B | 19 | . | f1_to_2 | May |
11 | . | B | 19 | 1 | sales | March |
12 | -21.00 | U | 19 | 1 | sales | April |
13 | 9.00 | L | 19 | 1 | sales | May |
14 | -46.09 | U | 19 | 1 | sales | March |
15 | -32.00 | U | 19 | 1 | sales | April |
16 | 38.00 | L | 19 | 1 | sales | May |
17 | . | B | 19 | 2 | production | March |
18 | -27.85 | U | 19 | 2 | production | April |
19 | 23.55 | L | 19 | 2 | production | May |
20 | 15.75 | L | 19 | 2 | storage | March |
21 | . | B | 19 | 2 | storage | April |
22 | 19.25 | L | 19 | 2 | backorder | April |
23 | 45.00 | L | 19 | 2 | backorder | May |
24 | -29.90 | U | 19 | . | f2_to_1 | March |
25 | 22.00 | L | 19 | . | f2_to_1 | April |
26 | 29.00 | L | 19 | . | f2_to_1 | May |
27 | -9.65 | U | 19 | 2 | sales | March |
28 | . | B | 19 | 2 | sales | April |
29 | 18.00 | L | 19 | 2 | sales | May |
30 | 4.05 | L | 19 | 2 | sales | March |
31 | -33.00 | U | 19 | 2 | sales | April |
32 | . | B | 19 | 2 | sales | May |
33 | -45.16 | U | 25 | 1 | production | March |
34 | -14.35 | U | 25 | 1 | production | April |
35 | 2.11 | L | 25 | 1 | production | May |
36 | 94.21 | L | 25 | 1 | storage | March |
37 | 75.66 | L | 25 | 1 | storage | April |
38 | -42.21 | U | 25 | 1 | backorder | April |
39 | -16.66 | U | 25 | 1 | backorder | May |
40 | 104.06 | L | 25 | . | f1_to_2 | March |
41 | 13.49 | L | 25 | . | f1_to_2 | April |
42 | 28.96 | L | 25 | . | f1_to_2 | May |
43 | 47.13 | L | 25 | 1 | sales | March |
44 | 8.40 | L | 25 | 1 | sales | April |
45 | . | B | 25 | 1 | sales | May |
46 | . | B | 25 | 1 | sales | March |
47 | . | B | 25 | 1 | sales | April |
48 | 32.02 | L | 25 | 1 | sales | May |
49 | . | B | 25 | 2 | production | March |
50 | -1.66 | U | 25 | 2 | production | April |
51 | 73.17 | L | 25 | 2 | production | May |
52 | 11.64 | L | 25 | 2 | storage | March |
53 | 108.13 | L | 25 | 2 | storage | April |
54 | 47.36 | L | 25 | 2 | backorder | April |
55 | -16.13 | U | 25 | 2 | backorder | May |
56 | -61.06 | U | 25 | . | f2_to_1 | March |
57 | 30.51 | L | 25 | . | f2_to_1 | April |
58 | 40.04 | L | 25 | . | f2_to_1 | May |
59 | -42.00 | U | 25 | 2 | sales | March |
60 | . | B | 25 | 2 | sales | April |
61 | 10.50 | L | 25 | 2 | sales | May |
62 | . | B | 25 | 2 | sales | March |
63 | . | B | 25 | 2 | sales | April |
64 | . | B | 25 | 2 | sales | May |
Obs | _node_ | _supdem_ | _dual_ |
---|---|---|---|
1 | fact1_1 | 1000 | 0.00 |
2 | fact2_1 | 850 | 0.00 |
3 | fact1_2 | 1000 | 0.00 |
4 | fact2_2 | 1500 | 0.00 |
5 | shop1_1 | -900 | 199.75 |
6 | shop2_1 | -900 | 188.75 |
7 | shop1_2 | -900 | 343.83 |
8 | shop2_2 | -1450 | 360.83 |
9 | f1_mar_1 | . | -127.90 |
10 | f1_apr_1 | . | -79.25 |
11 | f1_may_1 | . | -94.25 |
12 | f2_mar_1 | . | -88.00 |
13 | f2_apr_1 | . | -90.25 |
14 | f2_may_1 | . | -110.25 |
15 | f1_mar_2 | . | -263.06 |
16 | f1_apr_2 | . | -188.85 |
17 | f1_may_2 | . | -131.19 |
18 | f2_mar_2 | . | -182.00 |
19 | f2_apr_2 | . | -198.36 |
20 | f2_may_2 | . | -128.23 |
The log is displayed in Output 7.8.2.
Output 7.8.2: OPTMODEL Log
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: Problem generation will use 4 threads. |
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 LP presolver value AUTOMATIC is applied. |
NOTE: The LP presolver removed 0 variables and 0 constraints. |
NOTE: The LP presolver removed 0 constraint coefficients. |
NOTE: The presolved problem has 64 variables, 20 constraints, and 128 |
constraint coefficients. |
NOTE: The LP solver is called. |
NOTE: The Dual Simplex algorithm is used. |
Objective |
Phase Iteration Value Time |
D 1 1 0.000000E+00 0 |
D 2 2 -4.020320E+06 0 |
D 2 32 -1.281110E+06 0 |
NOTE: Optimal. |
NOTE: Objective = -1281110.35. |
NOTE: The Dual Simplex solve time is 0.00 seconds. |
NOTE: The data set WORK.ARC1 has 64 observations and 16 variables. |
NOTE: The data set WORK.NODE2 has 20 observations and 3 variables. |
NOTE: The PROCEDURE OPTMODEL printed pages 13-14. |