Factory Planning 1¶
Reference¶
SAS/OR example: http://go.documentation.sas.com/?docsetId=ormpex&docsetTarget=ormpex_ex3_toc.htm&docsetVersion=15.1&locale=en
SAS/OR code for example: https://support.sas.com/documentation/onlinedoc/or/ex_code/151/mpex03.html
Model¶
import sasoptpy as so
import pandas as pd
def test(cas_conn):
m = so.Model(name='factory_planning_1', 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_types_data = [
['grinder', 4],
['vdrill', 2],
['hdrill', 3],
['borer', 1],
['planer', 1]]
machine_types_data = pd.DataFrame(machine_types_data, columns=[
'machine_type', 'num_machines']).set_index(['machine_type'])
machine_type_period_data = [
['grinder', 1, 1],
['hdrill', 2, 2],
['borer', 3, 1],
['vdrill', 4, 1],
['grinder', 5, 1],
['vdrill', 5, 1],
['planer', 6, 1],
['hdrill', 6, 1]]
machine_type_period_data = pd.DataFrame(machine_type_period_data, columns=[
'machine_type', 'period', 'num_down'])
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'])
store_ub = 100
storage_cost_per_unit = 0.5
final_storage = 50
num_hours_per_period = 24 * 2 * 8
# Problem definition
PRODUCTS = product_list
PERIODS = range(1, 7)
MACHINE_TYPES = machine_types_data.index.values
num_machine_per_period = pd.DataFrame()
for i in range(1, 7):
num_machine_per_period[i] = machine_types_data['num_machines']
for _, row in machine_type_period_data.iterrows():
num_machine_per_period.at[row['machine_type'],
row['period']] -= row['num_down']
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+1)
storageCost = storage_cost_per_unit * store.sum('*', '*')
revenue = so.expr_sum(product_data.at[p, 'profit'] * sell[p, t]
for p in PRODUCTS for t in PERIODS)
m.set_objective(revenue-storageCost, sense=so.MAX, name='total_profit')
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_machine_per_period.at[mc, t]
for mc in MACHINE_TYPES for t in PERIODS), name='machine_hours')
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')
res = m.solve()
if res is not None:
print(so.get_solution_table(make, sell, store))
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_1 import test
In [8]: test(cas_conn)
NOTE: Initialized model factory_planning_1.
NOTE: Added action set 'optimization'.
NOTE: Converting model factory_planning_1 to OPTMODEL.
NOTE: Submitting OPTMODEL code to CAS server.
NOTE: Problem generation will use 8 threads.
NOTE: The problem has 126 variables (0 free, 6 fixed).
NOTE: The problem has 72 linear constraints (30 LE, 42 EQ, 0 GE, 0 range).
NOTE: The problem has 281 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 time is 0.01 seconds.
NOTE: The LP presolver removed 60 variables and 46 constraints.
NOTE: The LP presolver removed 178 constraint coefficients.
NOTE: The presolved problem has 66 variables, 26 constraints, and 103 constraint coefficients.
NOTE: The LP solver is called.
NOTE: The Dual Simplex algorithm is used.
Objective
Phase Iteration Value Time
D 2 1 9.478510E+04 0
P 2 21 9.371518E+04 0
NOTE: Optimal.
NOTE: Objective = 93715.178571.
NOTE: The Dual Simplex solve time is 0.01 seconds.
NOTE: The output table 'SOLUTION' in caslib 'CASUSER(casuser)' has 126 rows and 6 columns.
NOTE: The output table 'DUAL' in caslib 'CASUSER(casuser)' has 72 rows and 4 columns.
NOTE: The CAS table 'solutionSummary' in caslib 'CASUSER(casuser)' has 13 rows and 4 columns.
NOTE: The CAS table 'problemSummary' in caslib 'CASUSER(casuser)' has 18 rows and 4 columns.
make sell store
(prod1, 1) 500.000000 500.000000 0.0
(prod1, 2) 700.000000 600.000000 100.0
(prod1, 3) 0.000000 100.000000 0.0
(prod1, 4) 200.000000 200.000000 0.0
(prod1, 5) 0.000000 0.000000 0.0
(prod1, 6) 550.000000 500.000000 50.0
(prod2, 1) 888.571429 888.571429 0.0
(prod2, 2) 600.000000 500.000000 100.0
(prod2, 3) 0.000000 100.000000 0.0
(prod2, 4) 300.000000 300.000000 0.0
(prod2, 5) 100.000000 100.000000 0.0
(prod2, 6) 550.000000 500.000000 50.0
(prod3, 1) 382.500000 300.000000 82.5
(prod3, 2) 117.500000 200.000000 0.0
(prod3, 3) 0.000000 0.000000 0.0
(prod3, 4) 400.000000 400.000000 0.0
(prod3, 5) 600.000000 500.000000 100.0
(prod3, 6) 0.000000 50.000000 50.0
(prod4, 1) 300.000000 300.000000 0.0
(prod4, 2) 0.000000 0.000000 0.0
(prod4, 3) 0.000000 0.000000 0.0
(prod4, 4) 500.000000 500.000000 0.0
(prod4, 5) 100.000000 100.000000 0.0
(prod4, 6) 350.000000 300.000000 50.0
(prod5, 1) 800.000000 800.000000 0.0
(prod5, 2) 500.000000 400.000000 100.0
(prod5, 3) 0.000000 100.000000 0.0
(prod5, 4) 200.000000 200.000000 0.0
(prod5, 5) 1100.000000 1000.000000 100.0
(prod5, 6) 0.000000 50.000000 50.0
(prod6, 1) 200.000000 200.000000 0.0
(prod6, 2) 300.000000 300.000000 0.0
(prod6, 3) 400.000000 400.000000 0.0
(prod6, 4) 0.000000 0.000000 0.0
(prod6, 5) 300.000000 300.000000 0.0
(prod6, 6) 550.000000 500.000000 50.0
(prod7, 1) 0.000000 0.000000 0.0
(prod7, 2) 250.000000 150.000000 100.0
(prod7, 3) 0.000000 100.000000 0.0
(prod7, 4) 100.000000 100.000000 0.0
(prod7, 5) 100.000000 0.000000 100.0
(prod7, 6) 0.000000 50.000000 50.0
Out[8]: 93715.17857142858