Factory Planning 2

Model

import sasoptpy as so
import pandas as pd


def test(cas_conn):

    m = so.Model(name='factory_planning_2', session=cas_conn)

    # Input data
    product_list = ['prod{}'.format(i) for i in range(1, 8)]
    product_data = pd.DataFrame([10, 6, 8, 4, 11, 9, 3],
                                columns=['profit'], index=product_list)
    demand_data = [
        [500, 1000, 300, 300,  800, 200, 100],
        [600,  500, 200,   0,  400, 300, 150],
        [300,  600,   0,   0,  500, 400, 100],
        [200,  300, 400, 500,  200,   0, 100],
        [0,    100, 500, 100, 1000, 300,   0],
        [500,  500, 100, 300, 1100, 500,  60]]
    demand_data = pd.DataFrame(
        demand_data, columns=product_list, index=range(1, 7))
    machine_type_product_data = [
        ['grinder', 0.5,  0.7,  0,    0,    0.3,  0.2, 0.5],
        ['vdrill',  0.1,  0.2,  0,    0.3,  0,    0.6, 0],
        ['hdrill',  0.2,  0,    0.8,  0,    0,    0,   0.6],
        ['borer',   0.05, 0.03, 0,    0.07, 0.1,  0,   0.08],
        ['planer',  0,    0,    0.01, 0,    0.05, 0,   0.05]]
    machine_type_product_data = \
        pd.DataFrame(machine_type_product_data, columns=['machine_type'] +
                     product_list).set_index(['machine_type'])
    machine_types_data = [
        ['grinder', 4, 2],
        ['vdrill',  2, 2],
        ['hdrill',  3, 3],
        ['borer',   1, 1],
        ['planer',  1, 1]]
    machine_types_data = pd.DataFrame(machine_types_data, columns=[
        'machine_type', 'num_machines', 'num_machines_needing_maintenance'])\
        .set_index(['machine_type'])

    store_ub = 100
    storage_cost_per_unit = 0.5
    final_storage = 50
    num_hours_per_period = 24 * 2 * 8

    # Problem definition
    PRODUCTS = product_list
    profit = product_data['profit']
    PERIODS = range(1, 7)
    MACHINE_TYPES = machine_types_data.index.tolist()

    num_machines = machine_types_data['num_machines']

    make = m.add_variables(PRODUCTS, PERIODS, lb=0, name='make')
    sell = m.add_variables(PRODUCTS, PERIODS, lb=0, ub=demand_data.transpose(),
                           name='sell')

    store = m.add_variables(PRODUCTS, PERIODS, lb=0, ub=store_ub, name='store')
    for p in PRODUCTS:
        store[p, 6].set_bounds(lb=final_storage, ub=final_storage)

    storageCost = so.expr_sum(
        storage_cost_per_unit * store[p, t] for p in PRODUCTS for t in PERIODS)
    revenue = so.expr_sum(profit[p] * sell[p, t]
                           for p in PRODUCTS for t in PERIODS)
    m.set_objective(revenue-storageCost, sense=so.MAX, name='total_profit')

    num_machines_needing_maintenance = \
        machine_types_data['num_machines_needing_maintenance']
    numMachinesDown = m.add_variables(MACHINE_TYPES, PERIODS, vartype=so.INT,
                                      lb=0, name='numMachinesDown')

    production_time = machine_type_product_data
    m.add_constraints((
        so.expr_sum(production_time.at[mc, p] * make[p, t] for p in PRODUCTS)
        <= num_hours_per_period *
        (num_machines[mc] - numMachinesDown[mc, t])
        for mc in MACHINE_TYPES for t in PERIODS), name='machine_hours_con')

    m.add_constraints((so.expr_sum(numMachinesDown[mc, t] for t in PERIODS) ==
                       num_machines_needing_maintenance[mc]
                       for mc in MACHINE_TYPES), name='maintenance_con')

    m.add_constraints(((store[p, t-1] if t-1 in PERIODS else 0) + make[p, t] ==
                      sell[p, t] + store[p, t]
                      for p in PRODUCTS for t in PERIODS),
                      name='flow_balance_con')

    res = m.solve()
    if res is not None:
        print(so.get_solution_table(make, sell, store))
        print(so.get_solution_table(numMachinesDown).unstack(level=-1))

    print(m.get_solution_summary())
    print(m.get_problem_summary())

    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.factory_planning_2 import test

In [8]: test(cas_conn)
NOTE: Initialized model factory_planning_2.
NOTE: Added action set 'optimization'.
NOTE: Converting model factory_planning_2 to OPTMODEL.
NOTE: Submitting OPTMODEL code to CAS server.
NOTE: Problem generation will use 8 threads.
NOTE: The problem has 156 variables (0 free, 13 fixed).
NOTE: The problem has 0 binary and 30 integer variables.
NOTE: The problem has 77 linear constraints (30 LE, 47 EQ, 0 GE, 0 range).
NOTE: The problem has 341 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 27 variables and 15 constraints.
NOTE: The MILP presolver removed 63 constraint coefficients.
NOTE: The MILP presolver modified 16 constraint coefficients.
NOTE: The presolved problem has 129 variables, 62 constraints, and 278 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      2  92755.0000000         116455   20.35%       0
                0        1      2  92755.0000000         116455   20.35%       0
                0        1      2  92755.0000000         116141   20.14%       0
                0        1      2  92755.0000000         115660   19.80%       0
                0        1      2  92755.0000000         114136   18.73%       0
                0        1      2  92755.0000000         113334   18.16%       0
                0        1      2  92755.0000000         112487   17.54%       0
                0        1      2  92755.0000000         111392   16.73%       0
                0        1      2  92755.0000000         111136   16.54%       0
                0        1      2  92755.0000000         110056   15.72%       0
                0        1      2  92755.0000000         109718   15.46%       0
                0        1      2  92755.0000000         109122   15.00%       0
                0        1      2  92755.0000000         108904   14.83%       0
                0        1      2  92755.0000000         108868   14.80%       0
                0        1      2  92755.0000000         108855   14.79%       0
                0        1      3         108855         108855    0.00%       0
NOTE: The MILP solver added 38 cuts with 136 cut coefficients at the root.
NOTE: Optimal within relative gap.
NOTE: Objective = 108855.00961.
NOTE: The output table 'SOLUTION' in caslib 'CASUSER(casuser)' has 156 rows and 6 columns.
NOTE: The output table 'DUAL' in caslib 'CASUSER(casuser)' has 77 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.
                   make         sell       store
(prod1, 1)   500.000000   500.000000    0.000000
(prod1, 2)   600.000200   600.000000    0.000200
(prod1, 3)   399.999201   300.000000   99.999401
(prod1, 4)     0.001198   100.000599    0.000000
(prod1, 5)     0.000000     0.000000    0.000000
(prod1, 6)   550.000000   500.000000   50.000000
(prod2, 1)  1000.000000  1000.000000    0.000000
(prod2, 2)   500.000188   500.000000    0.000188
(prod2, 3)   699.998215   599.999002   99.999401
(prod2, 4)     0.001797   100.001198    0.000000
(prod2, 5)   100.002196   100.000000    0.002196
(prod2, 6)   549.997804   500.000000   50.000000
(prod3, 1)   300.000000   300.000000    0.000000
(prod3, 2)   200.000000   200.000000    0.000000
(prod3, 3)   100.000000     0.000000  100.000000
(prod3, 4)     0.001283   100.001283    0.000000
(prod3, 5)   500.000072   500.000000    0.000072
(prod3, 6)   149.999928   100.000000   50.000000
(prod4, 1)   300.000000   300.000000    0.000000
(prod4, 2)     0.000000     0.000000    0.000000
(prod4, 3)    99.999401     0.000000   99.999401
(prod4, 4)     0.002994   100.002396    0.000000
(prod4, 5)   100.000000   100.000000    0.000000
(prod4, 6)   350.000000   300.000000   50.000000
(prod5, 1)   800.000522   800.000000    0.000522
(prod5, 2)   399.999338   399.999460    0.000399
(prod5, 3)   599.998374   499.999201   99.999572
(prod5, 4)     0.001027   100.000599    0.000000
(prod5, 5)  1000.006512  1000.000000    0.006512
(prod5, 6)  1149.992981  1099.999493   50.000000
(prod6, 1)   200.000000   200.000000    0.000000
(prod6, 2)   300.000000   300.000000    0.000000
(prod6, 3)   400.000000   400.000000    0.000000
(prod6, 4)     0.000000     0.000000    0.000000
(prod6, 5)   300.000000   300.000000    0.000000
(prod6, 6)   550.000000   500.000000   50.000000
(prod7, 1)   100.000000   100.000000    0.000000
(prod7, 2)   150.000000   150.000000    0.000000
(prod7, 3)   199.999572   100.000000   99.999572
(prod7, 4)     0.000428   100.000000    0.000000
(prod7, 5)     0.000000     0.000000    0.000000
(prod7, 6)   110.000000    60.000000   50.000000
numMachinesDown  (grinder, 1)   -0.000000e+00
numMachinesDown  (grinder, 2)   -0.000000e+00
numMachinesDown  (grinder, 3)   -0.000000e+00
numMachinesDown  (grinder, 4)    2.000000e+00
numMachinesDown  (grinder, 5)   -0.000000e+00
numMachinesDown  (grinder, 6)   -0.000000e+00
numMachinesDown  (vdrill, 1)     0.000000e+00
numMachinesDown  (vdrill, 2)    -0.000000e+00
numMachinesDown  (vdrill, 3)    -0.000000e+00
numMachinesDown  (vdrill, 4)     1.999994e+00
numMachinesDown  (vdrill, 5)     3.955573e-06
numMachinesDown  (vdrill, 6)     2.033239e-06
numMachinesDown  (hdrill, 1)     1.000000e+00
numMachinesDown  (hdrill, 2)     2.000000e+00
numMachinesDown  (hdrill, 3)    -0.000000e+00
numMachinesDown  (hdrill, 4)    -0.000000e+00
numMachinesDown  (hdrill, 5)    -0.000000e+00
numMachinesDown  (hdrill, 6)    -0.000000e+00
numMachinesDown  (borer, 1)      0.000000e+00
numMachinesDown  (borer, 2)     -0.000000e+00
numMachinesDown  (borer, 3)      1.996271e-06
numMachinesDown  (borer, 4)      9.999940e-01
numMachinesDown  (borer, 5)     -0.000000e+00
numMachinesDown  (borer, 6)      3.992541e-06
numMachinesDown  (planer, 1)     0.000000e+00
numMachinesDown  (planer, 2)     1.798545e-06
numMachinesDown  (planer, 3)     1.996271e-06
numMachinesDown  (planer, 4)     9.999957e-01
numMachinesDown  (planer, 5)     8.120488e-16
numMachinesDown  (planer, 6)     4.829074e-07
dtype: float64
Selected Rows from Table SOLUTIONSUMMARY

                                             Value
Label                                             
Solver                                        MILP
Algorithm                           Branch and Cut
Objective Function                    total_profit
Solution Status        Optimal within Relative Gap
Objective Value                       108855.00961
                                                  
Relative Gap                          3.0634424E-6
Absolute Gap                          0.3334720743
Primal Infeasibility                  5.684342E-14
Bound Infeasibility                              0
Integer Infeasibility                 5.9888119E-6
                                                  
Best Bound                            108855.34308
Nodes                                            1
Solutions Found                                  3
Iterations                                     377
Presolve Time                                 0.00
Solution Time                                 0.33
Selected Rows from Table PROBLEMSUMMARY

                                Value
Label                                
Objective Sense          Maximization
Objective Function       total_profit
Objective Type                 Linear
                                     
Number of Variables               156
Bounded Above                       0
Bounded Below                      72
Bounded Below and Above            71
Free                                0
Fixed                              13
Binary                              0
Integer                            30
                                     
Number of Constraints              77
Linear LE (<=)                     30
Linear EQ (=)                      47
Linear GE (>=)                      0
Linear Range                        0
                                     
Constraint Coefficients           341
Out[8]: 108855.00960810453