Я работаю над проблемой планирования железнодорожных перевозок, которая перемещает продукт с завода-изготовителя на склад для удовлетворения спроса.
Я новичок в целлюлозе, поэтому мне сложно понять, почему это не работает, и, к сожалению, очень мало документации по этому вопросу.
Проблема
Необходимо отслеживать три переменные решения:
Наличие / инвентарь продукции на каждом заводе - обратите внимание, что каждый завод может производить разные продукты.
Рельс - сколько нужно перемещать каждого продукта с каждого завода. Каждый поезд может двигаться 8400 тонн.
- Инвентаризация каждого продукта в хранилище.
После запуска программы переменная принятия решения о железнодорожном транспорте работает правильно, т.е. как и ожидалось, однако инвентарь на заводе и в хранилище не показывает сумму, удаленную и впоследствии добавленную по железной дороге.
Данные и код ниже:
import pulp as pulp
import pandas as pd
import datetime
#rail capacity df from plant: no_trains_per_day max
rail_capacity_df_daily = {'ABC': {'capacity_per_day': 1, 'max': 19},
'DEF': {'capacity_per_day': 1, 'max': 50}}
rail_capacity_df = pd.DataFrame.from_dict(rail_capacity_df_daily ,orient='Index')
# facilities_df
facilities_inventory = {'BZL': {'current': 100000, 'max': 210000},
'AFM': {'current': 100000, 'max': 190000},
'PRE': {'current': 100000, 'max': 245000}}
facilities_df = pd.DataFrame.from_dict(facilities_inventory, orient='Index')
# plants_df
plant_df_inventory = {('ABC', 'PRE'): {'inventory': 196710, 'daily_production': 6000},
('ABC', 'AFM'): {'inventory': 199910, 'daily_production': 5000},
('DEF', 'BZL'): {'inventory': 127110, 'daily_production': 5000},
('DEF', 'PRE'): {'inventory': 227100, 'daily_production': 6000}}
plants_df = pd.DataFrame.from_dict(plant_df_inventory,orient='Index').rename_axis(['plant', 'product'])
# Sales demand
sales_demand = {'2020-04-24': {'AFM': 10000, 'PRE': 15000, 'BZL': 10000},
'2020-04-25': {'AFM': 10000, 'PRE': 15000, 'BZL': 10000},
'2020-04-26': {'AFM': 10000, 'PRE': 15000, 'BZL': 10000},
'2020-04-27': {'AFM': 10000, 'PRE': 15000, 'BZL': 10000},
'2020-04-28': {'AFM': 10000, 'PRE': 15000, 'BZL': 10000},
'2020-04-29': {'AFM': 10000, 'PRE': 15000, 'BZL': 10000},}
sales_df = pd.DataFrame.from_dict(sales_demand, orient='Index').rename_axis(['date'])
# Demand: Current Sales Demand
sales_demand = sales_df.to_dict(orient='index')
# PLANNING HORIZON PARAMS
_current_date = pd.to_datetime(datetime.datetime.today().strftime('%Y%m%d'))
planning_horizon_max = datetime.datetime.today() + datetime.timedelta(4)
planning_horizon_max = pd.to_datetime(planning_horizon_max.strftime('%Y%m%d'))
# COMBINATION VARS
dates = [d.strftime('%F') for d in pd.date_range(_current_date,planning_horizon_max)]
plant_combinations = [(plant, product) for plant, product in plants_df.index]
products = [p for p in facilities_df.index]
plants = ['ABC', 'DEF']
# Sales Demand: Grade Combinations by Date
demand_requirements = [(d, p) for d in dates for p in products]
# INVENTORY
# Initial Storage Inventory
storage_inv = dict(zip(facilities_df.index, facilities_df['current']))
storage_max = dict(zip(facilities_df.index, facilities_df['max']))
# Initial Plant Inventory
plant_current_inventory = dict(zip(plants_df.index, plants_df.inventory))
plant_daily_production = dict(zip(plants_df.index, plants_df.daily_production))
# DECISION VARIABLES
# Plant facility vars
plant_inventory_vars = pulp.LpVariable.dicts(
'Plant Inventory',
((date, plant, product) for date in dates for (plant, product) in plant_combinations),
cat='Continuous',
lowBound=0)
# Storage Facility Vars
storage_facility_vars = pulp.LpVariable.dicts(
'Storage Inventory',
((d, p) for d in dates for p in products),
cat='Integer',
lowBound=0)
# Total train capacity per plant dict
train_load_limit_daily = dict(zip(rail_capacity_df.index,
rail_capacity_df.capacity_per_day))
# Decision Vars: date, plant, product
train_consignment_variables = pulp.LpVariable.dicts(
'Rail Loadings From plant',
((date, plant, product) for date in dates for (plant, product) in plant_combinations),
cat='Continuous',
lowBound=0)
# OPTIMISATION
# Instantiate
model = pulp.LpProblem('Rail Optimisation', pulp.LpMinimize)
solver = pulp.PULP_CBC_CMD()
solver.tmpDir = 'Users\CPrice2'
# Objective Function
model += pulp.lpSum(storage_max[product]
- storage_facility_vars[(date, product)] for (date, product) in storage_facility_vars), 'Minimise stockpile shortfalls'
# PLANT INVENTORY
for date in dates:
current_date = datetime.date.today().strftime('%F')
date_t_minus_one = datetime.datetime.strptime(date, '%Y-%m-%d') - datetime.timedelta(days=1)
date_t_minus_one = date_t_minus_one.strftime('%F')
for plant, product in plant_combinations:
if date == current_date:
# Set current inventory
model += plant_current_inventory[(plant, product)] - \
train_consignment_variables[(date, plant, product)] == \
plant_inventory_vars[(date, plant, product)] + \
plant_daily_production[(plant, product)]
else:
# Get inventory from t-1
model += plant_inventory_vars[(f'{date_t_minus_one}', plant, product)] - \
train_consignment_variables[(date, plant, product)] == \
plant_inventory_vars[(date, plant, product)] + \
plant_daily_production[(plant, product)]
# Trains: Daily Rail Out Constraint
for date in dates:
for plant in plants:
plant_product_combination = [tup for tup in plant_combinations if tup[0] == plant]
variable_list = []
for (plant_, product_) in plant_product_combination:
variable = train_consignment_variables[(date, plant_, product_)]
variable_list.append(variable)
model += pulp.lpSum(var for var in variable_list) == train_load_limit_daily[plant] * 8400
# STORAGE FACILITY
for date in dates:
current_date = datetime.date.today().strftime('%F')
date_t_minus_one = datetime.datetime.strptime(date, '%Y-%m-%d') - datetime.timedelta(days=1)
date_t_minus_one = date_t_minus_one.strftime('%F')
for plant, product in plant_combinations:
if date == current_date:
# Current Inv == current inventory + train in
model += storage_inv[product] + \
train_consignment_variables[(date, plant, product)] == \
storage_facility_vars[(date, product)] - sales_demand[date][product]
else:
model += storage_facility_vars[(f'{date_t_minus_one}', product)] + \
train_consignment_variables[(date, plant, product)] == \
storage_facility_vars[(date, product)] - sales_demand[date][product]
# Run solver
model.solve(solver)
pulp.LpStatus[model.status]
# Storage Out
storage_facility_out = []
for (date, product) in storage_facility_vars:
var_out = {
'Date': date,
'Product': product,
'Out Inventory': storage_facility_vars[(date, product)].varValue
}
storage_facility_out.append(var_out)
storage_facility_out_df = pd.DataFrame.from_records(storage_facility_out).sort_values(['Date', 'Product'])
storage_facility_out_df.set_index(['Date', 'Product'], inplace=True)
# Rail Out
rail_optimisation_outputs = []
for date, plant, product in train_consignment_variables:
var_output = {
'Date': date,
'Plant': plant,
'Product': product,
'Rail_Out': train_consignment_variables[(date, plant, product)].varValue
}
rail_optimisation_outputs.append(var_output)
output_df = pd.DataFrame.from_records(rail_optimisation_outputs).sort_values(['Date', 'Plant', 'Product'])
output_df.set_index(['Date', 'Plant', 'Product'], inplace=True)
# Production Plant Out
plant_stock_out = []
for date, plant, product in plant_inventory_vars:
var_out = {
'Date': date,
'Plant': plant,
'Product': product,
'Out Inventory': plant_inventory_vars[(date, plant, product)].varValue
}
plant_stock_out.append(var_out)
plant_stock_out_df = pd.DataFrame.from_records(plant_stock_out).sort_values(['Date', 'Product'])
plant_stock_out_df.set_index(['Date', 'Plant', 'Product'], inplace=True)
plant_stock_out_df
Когда я получаю доступ к выходам каждой переменной решения:
train_consignment_vars.varValue = output ok.
Как для завода, так и для хранилища я получаю следующее:
storage_facility_vars.varValue = AttributeError: у объекта 'float' нет атрибута 'value'. Если я не вызываю .varValue, я просто получаю значения словаря без учета суммы, добавленной / удаленной по железной дороге.