Food Manufacture 2¶
Reference¶
SAS/OR example: http://go.documentation.sas.com/?docsetId=ormpex&docsetTarget=ormpex_ex2_toc.htm&docsetVersion=15.1&locale=en
SAS/OR code for example: http://support.sas.com/documentation/onlinedoc/or/ex_code/151/mpex02.html
Model¶
import sasoptpy as so
import pandas as pd
def test(cas_conn):
# Problem data
OILS = ['veg1', 'veg2', 'oil1', 'oil2', 'oil3']
PERIODS = range(1, 7)
cost_data = [
[110, 120, 130, 110, 115],
[130, 130, 110, 90, 115],
[110, 140, 130, 100, 95],
[120, 110, 120, 120, 125],
[100, 120, 150, 110, 105],
[90, 100, 140, 80, 135]]
cost = pd.DataFrame(cost_data, columns=OILS, index=PERIODS).transpose()
hardness_data = [8.8, 6.1, 2.0, 4.2, 5.0]
hardness = {OILS[i]: hardness_data[i] for i in range(len(OILS))}
revenue_per_ton = 150
veg_ub = 200
nonveg_ub = 250
store_ub = 1000
storage_cost_per_ton = 5
hardness_lb = 3
hardness_ub = 6
init_storage = 500
max_num_oils_used = 3
min_oil_used_threshold = 20
# Problem initialization
m = so.Model(name='food_manufacture_2', session=cas_conn)
# Problem definition
buy = m.add_variables(OILS, PERIODS, lb=0, name='buy')
use = m.add_variables(OILS, PERIODS, lb=0, name='use')
manufacture = m.add_implicit_variable((use.sum('*', p) for p in PERIODS),
name='manufacture')
last_period = len(PERIODS)
store = m.add_variables(OILS, [0] + list(PERIODS), lb=0, ub=store_ub,
name='store')
for oil in OILS:
store[oil, 0].set_bounds(lb=init_storage, ub=init_storage)
store[oil, last_period].set_bounds(lb=init_storage, ub=init_storage)
VEG = [i for i in OILS if 'veg' in i]
NONVEG = [i for i in OILS if i not in VEG]
revenue = so.expr_sum(revenue_per_ton * manufacture[p] for p in PERIODS)
rawcost = so.expr_sum(cost.at[o, p] * buy[o, p]
for o in OILS for p in PERIODS)
storagecost = so.expr_sum(storage_cost_per_ton * store[o, p]
for o in OILS for p in PERIODS)
m.set_objective(revenue - rawcost - storagecost, sense=so.MAX,
name='profit')
# Constraints
m.add_constraints((use.sum(VEG, p) <= veg_ub for p in PERIODS),
name='veg_ub')
m.add_constraints((use.sum(NONVEG, p) <= nonveg_ub for p in PERIODS),
name='nonveg_ub')
m.add_constraints((store[o, p-1] + buy[o, p] == use[o, p] + store[o, p]
for o in OILS for p in PERIODS),
name='flow_balance')
m.add_constraints((so.expr_sum(hardness[o]*use[o, p] for o in OILS) >=
hardness_lb * manufacture[p] for p in PERIODS),
name='hardness_ub')
m.add_constraints((so.expr_sum(hardness[o]*use[o, p] for o in OILS) <=
hardness_ub * manufacture[p] for p in PERIODS),
name='hardness_lb')
# Additions to the first problem
isUsed = m.add_variables(OILS, PERIODS, vartype=so.BIN, name='is_used')
for p in PERIODS:
for o in VEG:
use[o, p].set_bounds(ub=veg_ub)
for o in NONVEG:
use[o, p].set_bounds(ub=nonveg_ub)
m.add_constraints((use[o, p] <= use[o, p]._ub * isUsed[o, p]
for o in OILS for p in PERIODS), name='link')
m.add_constraints((isUsed.sum('*', p) <= max_num_oils_used
for p in PERIODS), name='logical1')
m.add_constraints((use[o, p] >= min_oil_used_threshold * isUsed[o, p]
for o in OILS for p in PERIODS), name='logical2')
m.add_constraints((isUsed[o, p] <= isUsed['oil3', p]
for o in ['veg1', 'veg2'] for p in PERIODS),
name='logical3')
res = m.solve()
if res is not None:
print(so.get_solution_table(buy, use, store, isUsed))
return m.get_objective_value()
Output¶
In [1]: import os
In [2]: hostname = os.getenv('CASHOST')
In [3]: port = os.getenv('CASPORT')
In [4]: from swat import CAS
In [5]: cas_conn = CAS(hostname, port)
In [6]: import sasoptpy
In [7]: from examples.client_side.food_manufacture_2 import test
In [8]: test(cas_conn)
NOTE: Initialized model food_manufacture_2.
NOTE: Added action set 'optimization'.
NOTE: Converting model food_manufacture_2 to OPTMODEL.
NOTE: Submitting OPTMODEL code to CAS server.
NOTE: Problem generation will use 8 threads.
NOTE: The problem has 125 variables (0 free, 10 fixed).
NOTE: The problem has 30 binary and 0 integer variables.
NOTE: The problem has 132 linear constraints (66 LE, 30 EQ, 36 GE, 0 range).
NOTE: The problem has 384 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 initial MILP heuristics are applied.
NOTE: The MILP presolver value AUTOMATIC is applied.
NOTE: The MILP presolver removed 50 variables and 10 constraints.
NOTE: The MILP presolver removed 66 constraint coefficients.
NOTE: The MILP presolver modified 6 constraint coefficients.
NOTE: The presolved problem has 75 variables, 122 constraints, and 318 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 8 threads.
Node Active Sols BestInteger BestBound Gap Time
0 1 5 36900.0000000 343250 89.25% 0
0 1 5 36900.0000000 107333 65.62% 0
0 1 5 36900.0000000 105799 65.12% 0
0 1 5 36900.0000000 105650 65.07% 0
0 1 5 36900.0000000 105650 65.07% 0
0 1 5 36900.0000000 105650 65.07% 0
0 1 5 36900.0000000 105650 65.07% 0
0 1 5 36900.0000000 105650 65.07% 0
0 1 6 99491.6666667 105650 5.83% 0
NOTE: The MILP solver added 15 cuts with 77 cut coefficients at the root.
10 6 7 99683.3343492 105090 5.15% 0
28 19 8 99908.3333333 104782 4.65% 0
68 45 9 99908.3333333 104564 4.45% 0
139 80 10 100054 104225 4.00% 0
145 75 11 100192 103683 3.37% 0
177 86 12 100192 103516 3.21% 0
183 87 13 100214 103516 3.19% 0
189 85 14 100279 103268 2.89% 0
283 40 15 100279 102053 1.74% 0
284 40 16 100279 102053 1.74% 0
333 0 16 100279 100279 0.00% 0
NOTE: Optimal.
NOTE: Objective = 100278.70577.
NOTE: The output table 'SOLUTION' in caslib 'CASUSER(casuser)' has 125 rows and 6 columns.
NOTE: The output table 'DUAL' in caslib 'CASUSER(casuser)' has 132 rows and 4 columns.
NOTE: The CAS table 'solutionSummary' in caslib 'CASUSER(casuser)' has 18 rows and 4 columns.
NOTE: The CAS table 'problemSummary' in caslib 'CASUSER(casuser)' has 20 rows and 4 columns.
buy use store is_used
veg1 1 0.000000e+00 8.518519e+01 4.148148e+02 1.000000e+00
veg1 2 0.000000e+00 8.518519e+01 3.296296e+02 1.000000e+00
veg1 3 0.000000e+00 0.000000e+00 3.296296e+02 0.000000e+00
veg1 4 0.000000e+00 1.550000e+02 1.746296e+02 9.999948e-01
veg1 5 0.000000e+00 1.550000e+02 1.962960e+01 1.000000e+00
veg1 6 4.803704e+02 0.000000e+00 5.000000e+02 0.000000e+00
veg2 1 0.000000e+00 1.148148e+02 3.851852e+02 1.000000e+00
veg2 2 0.000000e+00 1.148148e+02 2.703704e+02 1.000000e+00
veg2 3 0.000000e+00 2.000000e+02 7.037037e+01 1.000000e+00
veg2 4 -1.421085e-14 0.000000e+00 7.037037e+01 -1.949310e-15
veg2 5 0.000000e+00 0.000000e+00 7.037037e+01 -0.000000e+00
veg2 6 6.296296e+02 2.000000e+02 5.000000e+02 1.000000e+00
oil1 1 0.000000e+00 0.000000e+00 5.000000e+02 0.000000e+00
oil1 2 -5.684342e-14 0.000000e+00 5.000000e+02 0.000000e+00
oil1 3 0.000000e+00 0.000000e+00 5.000000e+02 -0.000000e+00
oil1 4 5.684342e-14 0.000000e+00 5.000000e+02 0.000000e+00
oil1 5 0.000000e+00 0.000000e+00 5.000000e+02 0.000000e+00
oil1 6 0.000000e+00 0.000000e+00 5.000000e+02 0.000000e+00
oil2 1 0.000000e+00 0.000000e+00 5.000000e+02 -0.000000e+00
oil2 2 1.900001e+02 2.273737e-13 6.900001e+02 9.094947e-16
oil2 3 0.000000e+00 2.300000e+02 4.600001e+02 1.000000e+00
oil2 4 -2.842171e-14 2.300001e+02 2.300000e+02 1.000000e+00
oil2 5 2.842171e-14 2.300000e+02 0.000000e+00 1.000000e+00
oil2 6 7.300000e+02 2.300000e+02 5.000000e+02 1.000000e+00
oil3 1 0.000000e+00 2.500000e+02 2.500000e+02 1.000000e+00
oil3 2 0.000000e+00 2.500000e+02 2.557954e-13 1.000000e+00
oil3 3 5.799999e+02 2.000000e+01 5.599999e+02 1.000000e+00
oil3 4 0.000000e+00 1.999990e+01 5.400000e+02 9.999948e-01
oil3 5 0.000000e+00 2.000000e+01 5.200000e+02 1.000000e+00
oil3 6 0.000000e+00 2.000000e+01 5.000000e+02 1.000000e+00
veg1 0 NaN NaN 5.000000e+02 NaN
veg2 0 NaN NaN 5.000000e+02 NaN
oil1 0 NaN NaN 5.000000e+02 NaN
oil2 0 NaN NaN 5.000000e+02 NaN
oil3 0 NaN NaN 5.000000e+02 NaN
Out[8]: 100278.70576513262