[英]Pulp: How to add/subtract decision variable outputs together
I am working on a rail scheduling problem that moves product from a production plant to a storage facility to satisfy demand.我正在研究将产品从生产工厂转移到存储设施以满足需求的铁路调度问题。
I am new to pulp so finding this difficult to understand why this isn't working, and unfortunately there is very little documentation on the subject.我是纸浆新手,所以很难理解为什么这不起作用,不幸的是,关于这个主题的文档很少。
The problem问题
There are three decision variables to monitor:需要监控三个决策变量:
The availability/inventory of product at each plant - note each plant can manufacture different products.每个工厂的产品可用性/库存 - 请注意每个工厂可以生产不同的产品。
Rail - how much to move of each product from each plant.铁路 - 每个工厂的每种产品要移动多少。 Each train can move 8400 tons.
每列火车可运载8400吨。
Upon running the program, the rail decision variable works correctly ie the output is as expected, however the inventory at the plant and storage facility is not showing the amount removed and subsequently added by the rail.运行程序后,铁路决策变量正常工作,即 output 符合预期,但工厂和存储设施的库存未显示铁路移除和随后添加的数量。
Data & code below:数据和代码如下:
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
When I access the outputs of each decision variable:当我访问每个决策变量的输出时:
train_consignment_vars.varValue = output ok.
For both plant and storage facilities I get the following:对于工厂和存储设施,我得到以下信息:
storage_facility_vars.varValue = AttributeError: 'float' object has no attribute 'value'. storage_facility_vars.varValue = AttributeError: 'float' object 没有属性 'value'。 If I dont call.varValue, I simply get the dictionary values without accounting for the amount added/removed by rail.
如果我不调用.varValue,我只是获取字典值而不考虑铁路添加/删除的数量。
Without your code in the form of a reproducible example I can't be sure of all of the issues but here are a few:如果没有可重现示例形式的代码,我无法确定所有问题,但这里有一些:
model += pulp.lpSum(plant_inventory_vars[(date, plant, product)]) - pulp.lpSum(train_consignment_variables[(date, plant, product)])
This is not a constraint.这不是一个约束。 The thing after
model +=
needs to take the form "A == B", or "A <= B", or "A >= B". model +=
之后的东西需要采用“A == B”或“A <= B”或“A >= B”的形式。 Your expression does not.你的表情没有。
There is another one here:这里还有一个:
model += pulp.lpSum(port_inventory_vars[(date, product)]) + pulp.lpSum(train_consignment_variables[(date, plant, product)] for plant, product in plant_combinations)
storage_facility_vars[(date, product)] = plant_current_inv[product]
The general approach in linear programming is that you declare the variables, the objective to be optimized and then the constraints that exist.线性规划的一般方法是声明变量、要优化的目标以及存在的约束。 In PULP the objective and constraints are added to the problem using the
model +=
syntax.在 PULP 中,使用
model +=
语法将目标和约束添加到问题中。 What you are doing here is taking a linear variable you've created and overwritting it with whatever is in plant_current_inv[product'
.您在这里所做的是获取您创建的线性变量并用
plant_current_inv[product'
中的任何内容覆盖它。 I think what you want to be doing instead is setting an equality constraint and adding that to the problem.我认为您想要做的是设置一个等式约束并将其添加到问题中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.