### Example 5.8 Migration to OPTMODEL: Production, Inventory, Distribution

The following example shows how to solve Example 5.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 _name_ {ARCS};

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_
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 5.1.

The PROC PRINT statements are the same as in Example 5.1:

```proc print data=arc1 width=min;
var _tail_ _head_ _cost_ _capac_ _lo_ _flow_ _fcost_
sum _fcost_;
run;
```

The output is displayed in Output 5.8.1.

Output 5.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 5.1. The log is displayed in Output 5.8.2.

Output 5.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.