[英]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 ![]() |
10 ![]() |
2 ![]() |
456 ![]() |
20 ![]() |
6 ![]() |
... ![]() |
... ![]() |
... ![]() |
999 ![]() |
30 ![]() |
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 .
我也在这篇文章中展示了这个概念。
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()
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.