简体   繁体   English

Pyomo 处理索引索引

[英]Pyomo dealing with an indexed index

Some context to my optimization: I have a warehouse that has stocked products that can be allocated to several retail stores.我的优化的一些背景:我有一个仓库,里面有可以分配给几个零售店的库存产品。 Each retail store has a monthly demand that needs to be satisifed but I would like to charge each store the highest possible price.每个零售店都有每月需要满足的需求,但我想向每个商店收取尽可能高的价格。 So I have a set of stores and a set of products, but these products can only be used in certain months.所以我有一套商店和一套产品,但是这些产品只能在某些月份使用。

Below is the mathematical formulation:下面是数学公式:

在此处输入图像描述

Where在哪里

在此处输入图像描述

So x_{i,j} represents the allocation to store n of product i.所以 x_{i,j} 表示产品 i 存储 n 的分配。 r_n is just a known risk factor for each store, pi_n is the current profit per store, p_n is the current price and paid by store n and demand_n is the demand for store n. r_n 只是每个商店的已知风险因素,pi_n 是每个商店的当前利润,p_n 是当前价格并由商店 n 支付,demand_n 是商店 n 的需求。 These are all known.这些都是众所周知的。

So for simplicity's sake, we could simply maximise avgPrice, the rest of the variables I can handle.因此,为了简单起见,我们可以简单地最大化 avgPrice,即我可以处理的变量的 rest。

So the average price paid by a store is the average of the monthly prices they pay for their demanded products.因此,商店支付的平均价格是他们每月为所需产品支付的平均价格。 I assume a store will only have demands during a year, hence the division by 12. So for each month I allocate products to n stores, find the total price paid by each store, and then find the average of these values.我假设一家商店一年内只有需求,因此除以 12。因此,我每个月将产品分配给 n 家商店,找出每家商店支付的总价格,然后找到这些值的平均值。

The issue I am having is how to deal with the我遇到的问题是如何处理

在此处输入图像描述

part.部分。 So P_{t} is the available products I have in month t.所以 P_{t} 是我在 t 月的可用产品。 This is stored in a dictionary of pandas dataframes that look something like:这存储在 pandas 数据帧的字典中,看起来像这样:

Product ID产品编号 Product Amount商品金额 Price per unit每单位价格
123 123 10 10 2 2个
456 456 20 20 6 6个
... ... ... ... ... ...
999 999 30 30 7 7

And I have one of these for each month (so my dictionary can be indexed like df_dict[jan], df_dict[feb] etc.我每个月都有其中一个(所以我的字典可以像df_dict[jan], df_dict[feb]等一样被索引。

For example, store n = 1 could have a demand of 30 units in January, so I could allocate product 123 and 456 to them, and get a total price in January of 10 * 2+20 * 6 (c_{i} in the formulation above is the unit price).例如,商店 n = 1 在 1 月份可能有 30 个单位的需求,因此我可以将产品 123 和 456 分配给它们,并在 1 月份获得总价 10 * 2+20 * 6(c_{i} 在以上公式为单价)。

Some code for reproducibility:一些重现性代码:

demand_dict = {('store_1', 'jan'): 237.2,
 ('store_1', 'feb'): 239,
 ('store_1', 'mar'): 216,
 ('store_1', 'apr'): 119,
 ('store_2', 'may'): 624}
# this constains over 50 stores, each of which have 12 monthly demands

This is used to ensure demand constraints.这用于确保需求约束。 So I create a set of stores by:所以我通过以下方式创建了一组商店:

store_set = demand_dict.keys()

And I have a set of months:我有一组月份:

mon_set = ['jan', 'feb',...'dec']

The product dictionary looks something like:产品字典看起来像:

prod_dict['jan'] = {'Product Amount': {123: 50,
  456: 31,
  789: 50,
  101: 31,
  102: 70,
  103: 33,
  104: 30,
  105: 14},
'Unit price': {123: 9,
  456: 9,
  789: 7.6,
  101: 7.2,
  102: 6.4,
  103: 5.5,
  104: 5.2,
  105: 5.1}

prod_dict['feb'] = {'Product Amount': {200: 50,
  201: 31,
  202: 50,
  203: 31,
  204: 70,
  205: 33,
  206: 30,
  207: 14},
'Unit price': {200: 9,
  201: 9,
  202: 7,
  203: 7,
  204: 6,
  205: 5,
  206: 5,
  207: 5}

Given this formulation of the product set, I don't understand how I can create this as a pyomo set.鉴于产品集的这种表述,我不明白如何将其创建为 pyomo 集。 I am confused because each product (indexed by its id) can be allocated to each store.我很困惑,因为每个产品(由其 id 索引)都可以分配给每个商店。 So if I have 5 stores, product id 123 can be allocated to each of them, as long as I do not allocate more than what is available in the product amount.因此,如果我有 5 家商店,可以将产品 ID 123 分配给每家商店,只要我分配的数量不超过产品数量中的可用数量即可。 This constraint I think I can handle.我认为我可以处理这个约束。 I am however completely lost when it comes to creating the product set in pyomo because the set itself is indexed by a month.然而,当谈到在 pyomo 中创建产品集时,我完全迷失了,因为该集本身是按月索引的。

Lastly, I know this does not look like an optimization problem because I could simply allocate the most expensive products to each store.最后,我知道这看起来不像是优化问题,因为我可以简单地将最昂贵的产品分配给每个商店。 However, the risk factor, r_n, contains variables that make this an actual QP.但是,风险因素 r_n 包含使其成为实际 QP 的变量。

The piece that I think you are missing is an indexed set that indexes which products are available/priced for particular months.我认为您缺少的部分是一个索引集,用于索引特定月份哪些产品可用/定价。 That is essentially the P_t piece that you want.这基本上就是您想要的P_t部分。 So you can create a "set of sets" in pyomo where the inner set is indexed by another set, in this case, you have sets of products that are indexed by another component, months.因此,您可以在pyomo中创建一个“集合集”,其中内部集合由另一个集合索引,在这种情况下,您有一组产品由另一个组件 months 索引。 These can be highly useful, but also tricky to use, and I think it is almost always required to "flatten" this set out after you make it so that you can use it in other contexts.这些可能非常有用,但使用起来也很棘手,而且我认为几乎总是需要在制作后将其“展平”,以便您可以在其他情况下使用它。 Below is an example.下面是一个例子。 I also showed this concept in this post .我也在这篇文章中展示了这个概念。

Code:代码:

import pyomo.environ as pyo

# DATA
prod_dict = {}
prod_dict['jan'] = {'Product Amount': {123: 50,
  456: 31,
  789: 50,
  101: 31,
  102: 70,
  103: 33,
  104: 30,
  105: 14},
'Unit price': {123: 9,
  456: 9,
  789: 7.6,
  101: 7.2,
  102: 6.4,
  103: 5.5,
  104: 5.2,
  105: 5.1}}

prod_dict['feb'] = {'Product Amount': {200: 50,
  201: 31,
  202: 50,
  203: 31,
  204: 70,
  205: 33,
  206: 30,
  207: 14},
'Unit price': {200: 9,
  201: 9,
  202: 7,
  203: 7,
  204: 6,
  205: 5,
  206: 5,
  207: 5}}

# helper function
def products_by_month(month):
  products = set(prod_dict[month]['Product Amount'])
  # sanity check:
  assert set(prod_dict[month]['Unit price']) == products
  return products

# make set of all products, if not already available...
products = set.union(*[products_by_month(m) for m in prod_dict.keys()])

# Model Parts
model = pyo.ConcreteModel('sales')

# SETS
model.M =  pyo.Set(initialize=list(prod_dict.keys()))  # Set of Months
# aside:  making a list of the set keeps pyomo 
#         from complaining about unordered collection...
model.P =  pyo.Set(initialize=list(products))    # Set of all Products
model.MP = pyo.Set(model.M, within=model.P, initialize={m: list(products_by_month(m)) for m in model.M})

# a flattened set for convenience...
model.MP_flat = pyo.Set(within=model.M * model.P, initialize={(m, p) for m in model.M for p in model.MP[m]})

# PARAMS
model.price     = pyo.Param(model.MP_flat, initialize={(m, p): prod_dict[m]['Unit price'][p] for m, p in model.MP_flat})
model.inventory = pyo.Param(model.MP_flat, initialize={(m, p): prod_dict[m]['Product Amount'][p] for m, p in model.MP_flat})

# VARS
model.deliver = pyo.Var(model.MP_flat, domain=pyo.NonNegativeReals)

# CONSTRAINTS
# example to limit sale of product by month to available in that month
@model.Constraint(model.MP_flat)
def delivery_limit(model, month, product):
  return model.deliver[month, product] <= model.inventory[month, product]

# example to limit all sales of product in a month to arbitrary cost (this uses the indexed set that you will need)
@model.Constraint(model.M)
def cost_limit(model, month):
  return sum(model.deliver[month, product] * model.price[month, product] for product in model.MP[month]) <= 100

model.pprint()

Output (a little long, based on your example data): Output(有点长,基于您的示例数据):

5 Set Declarations
    M : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    2 : {'jan', 'feb'}
    MP : Size=2, Index=M, Ordered=Insertion
        Key : Dimen : Domain : Size : Members
        feb :     1 :      P :    8 : {200, 201, 202, 203, 204, 205, 206, 207}
        jan :     1 :      P :    8 : {101, 102, 103, 456, 104, 105, 789, 123}
    MP_flat : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain         : Size : Members
        None :     2 : MP_flat_domain :   16 : {('jan', 456), ('feb', 204), ('feb', 200), ('jan', 105), ('jan', 102), ('feb', 202), ('feb', 203), ('feb', 206), ('jan', 101), ('jan', 789), ('jan', 104), ('feb', 205), ('jan', 123), ('jan', 103), ('feb', 201), ('feb', 207)}
    MP_flat_domain : Size=1, Index=None, Ordered=True
        Key  : Dimen : Domain : Size : Members
        None :     2 :    M*P :   32 : {('jan', 101), ('jan', 102), ('jan', 103), ('jan', 456), ('jan', 104), ('jan', 105), ('jan', 200), ('jan', 201), ('jan', 202), ('jan', 203), ('jan', 204), ('jan', 205), ('jan', 206), ('jan', 207), ('jan', 789), ('jan', 123), ('feb', 101), ('feb', 102), ('feb', 103), ('feb', 456), ('feb', 104), ('feb', 105), ('feb', 200), ('feb', 201), ('feb', 202), ('feb', 203), ('feb', 204), ('feb', 205), ('feb', 206), ('feb', 207), ('feb', 789), ('feb', 123)}
    P : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :   16 : {101, 102, 103, 456, 104, 105, 200, 201, 202, 203, 204, 205, 206, 207, 789, 123}

2 Param Declarations
    inventory : Size=16, Index=MP_flat, Domain=Any, Default=None, Mutable=False
        Key          : Value
        ('feb', 200) :    50
        ('feb', 201) :    31
        ('feb', 202) :    50
        ('feb', 203) :    31
        ('feb', 204) :    70
        ('feb', 205) :    33
        ('feb', 206) :    30
        ('feb', 207) :    14
        ('jan', 101) :    31
        ('jan', 102) :    70
        ('jan', 103) :    33
        ('jan', 104) :    30
        ('jan', 105) :    14
        ('jan', 123) :    50
        ('jan', 456) :    31
        ('jan', 789) :    50
    price : Size=16, Index=MP_flat, Domain=Any, Default=None, Mutable=False
        Key          : Value
        ('feb', 200) :     9
        ('feb', 201) :     9
        ('feb', 202) :     7
        ('feb', 203) :     7
        ('feb', 204) :     6
        ('feb', 205) :     5
        ('feb', 206) :     5
        ('feb', 207) :     5
        ('jan', 101) :   7.2
        ('jan', 102) :   6.4
        ('jan', 103) :   5.5
        ('jan', 104) :   5.2
        ('jan', 105) :   5.1
        ('jan', 123) :     9
        ('jan', 456) :     9
        ('jan', 789) :   7.6

1 Var Declarations
    deliver : Size=16, Index=MP_flat
        Key          : Lower : Value : Upper : Fixed : Stale : Domain
        ('feb', 200) :     0 :  None :  None : False :  True : NonNegativeReals
        ('feb', 201) :     0 :  None :  None : False :  True : NonNegativeReals
        ('feb', 202) :     0 :  None :  None : False :  True : NonNegativeReals
        ('feb', 203) :     0 :  None :  None : False :  True : NonNegativeReals
        ('feb', 204) :     0 :  None :  None : False :  True : NonNegativeReals
        ('feb', 205) :     0 :  None :  None : False :  True : NonNegativeReals
        ('feb', 206) :     0 :  None :  None : False :  True : NonNegativeReals
        ('feb', 207) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 101) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 102) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 103) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 104) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 105) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 123) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 456) :     0 :  None :  None : False :  True : NonNegativeReals
        ('jan', 789) :     0 :  None :  None : False :  True : NonNegativeReals

2 Constraint Declarations
    cost_limit : Size=2, Index=M, Active=True
        Key : Lower : Body                                                                                                                                                                              : Upper : Active
        feb :  -Inf :             9*deliver[feb,200] + 9*deliver[feb,201] + 7*deliver[feb,202] + 7*deliver[feb,203] + 6*deliver[feb,204] + 5*deliver[feb,205] + 5*deliver[feb,206] + 5*deliver[feb,207] : 100.0 :   True
        jan :  -Inf : 7.2*deliver[jan,101] + 6.4*deliver[jan,102] + 5.5*deliver[jan,103] + 9*deliver[jan,456] + 5.2*deliver[jan,104] + 5.1*deliver[jan,105] + 7.6*deliver[jan,789] + 9*deliver[jan,123] : 100.0 :   True
    delivery_limit : Size=16, Index=MP_flat, Active=True
        Key          : Lower : Body             : Upper : Active
        ('feb', 200) :  -Inf : deliver[feb,200] :  50.0 :   True
        ('feb', 201) :  -Inf : deliver[feb,201] :  31.0 :   True
        ('feb', 202) :  -Inf : deliver[feb,202] :  50.0 :   True
        ('feb', 203) :  -Inf : deliver[feb,203] :  31.0 :   True
        ('feb', 204) :  -Inf : deliver[feb,204] :  70.0 :   True
        ('feb', 205) :  -Inf : deliver[feb,205] :  33.0 :   True
        ('feb', 206) :  -Inf : deliver[feb,206] :  30.0 :   True
        ('feb', 207) :  -Inf : deliver[feb,207] :  14.0 :   True
        ('jan', 101) :  -Inf : deliver[jan,101] :  31.0 :   True
        ('jan', 102) :  -Inf : deliver[jan,102] :  70.0 :   True
        ('jan', 103) :  -Inf : deliver[jan,103] :  33.0 :   True
        ('jan', 104) :  -Inf : deliver[jan,104] :  30.0 :   True
        ('jan', 105) :  -Inf : deliver[jan,105] :  14.0 :   True
        ('jan', 123) :  -Inf : deliver[jan,123] :  50.0 :   True
        ('jan', 456) :  -Inf : deliver[jan,456] :  31.0 :   True
        ('jan', 789) :  -Inf : deliver[jan,789] :  50.0 :   True

10 Declarations: M P MP MP_flat_domain MP_flat price inventory deliver delivery_limit cost_limit

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Pyomo无法使用索引集索引组件 - Pyomo Cannot index a component with an indexed set 索引对索引组件无效。 pyomo 中带有双索引的参数 - Index is not valid for indexed component. Parameter with double indexing in pyomo PYOMO 错误:KeyError:“索引'0'对索引组件'y'无效” - PYOMO ERROR: KeyError: “Index '0' is not valid for indexed component 'y' ” 由带有 Pyomo 的索引集索引的变量 - Variable indexed by an indexed Set with Pyomo 在 pyomo 中使用索引集 - Use indexed sets in pyomo Pyomo:从 json 存档中加载三个维度集数据错误:无法索引具有索引集的组件 - Pyomo: Loading three dimension set data from json archive error: Cannot index a component with an indexed set 将索引变量的值设置为索引表达式-pyomo - Setting value of an indexed Variable to an indexed Expression - pyomo Pyomo中的多索引约束或目标 - Multi indexed constraints or objectives in Pyomo Pyomo KeyError:“访问索引组件时出错:索引&#39;(&#39;student_5&#39;,&#39;company_3&#39;,&#39;meetingtime_1&#39;)&#39;对数组组件&#39;var_X&#39;无效” - Pyomo KeyError: “Error accessing indexed component: Index '('student_5', 'company_3', 'meetingtime_1')' is not valid for array component 'var_X'” 如何在Pyomo中索引矩阵 - How to index a matrix in Pyomo
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM