简体   繁体   English

纸浆:如何将决策变量输出加/减在一起

[英]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:需要监控三个决策变量:

  1. The availability/inventory of product at each plant - note each plant can manufacture different products.每个工厂的产品可用性/库存 - 请注意每个工厂可以生产不同的产品。

  2. Rail - how much to move of each product from each plant.铁路 - 每个工厂的每种产品要移动多少。 Each train can move 8400 tons.每列火车可运载8400吨。

  3. The inventory of each product at the storage facility.存储设施中每种产品的库存。

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:如果没有可重现示例形式的代码,我无法确定所有问题,但这里有一些:

  1. After you've already added in your objective functions you seem to add further expressions which are not in the form of contraints for example:在您已经添加了目标函数之后,您似乎添加了更多不是约束形式的表达式,例如:

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)

  1. As pointed out by @pchtsp you are overwritting some of your pulp variables:正如@pchtsp 所指出的,您正在覆盖一些纸浆变量:

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.

相关问题 纸浆中的 LP - 决策变量问题 - LP in pulp - problem with decision variable 作为预算约束的决策变量的累积和(纸浆) - Cumulative Sum of decision variable as budget constraint (pulp) 如何指定至少一个决策变量应在python纸浆中取最小值? - How to specify at least one decision variable should take minimum value in python pulp? 如何在 Python 上使用 PuLP GLPK 为混合 Integer 线性规划 (MILP) 的决策变量编写 IF 条件? - How can I write an IF condition for my decision variable for Mixed Integer Linear Programming (MILP) using PuLP GLPK on Python? 输出数量可变的Python Scikit决策树 - Python Scikit Decision Tree with variable number of outputs 如何在 PuLP 中使用变量作为除数 - How to use a variable as a divisor in PuLP 如何在纸浆优化中限制变量? - How to cap a variable in Pulp optimization? 如何将纸浆决策变量的输出打印成矩阵格式 - How to print the output of pulp decision variables into matrix format 当时间用作 Pulp 变量中的索引时,如何向 LP 添加优先约束? - How to add precedence constraints to LP when time is used as an index in a Pulp variable? 惩罚 Python 纸浆优化 function 因为决策变量偏离特定常数 - Penalize Python PuLP Optimization function as decision variable diverges from a specific constant
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM