Food Manufacture 2

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