Gekko中的基于时间的间距约束
我正在尝试限制“simu_total_volume”的输出向量,要求输出的元素(x7=1)之间间隔s条记录(周),同时还要控制x7总共可以等于1的最大次数。
下面的代码似乎可以运行,但我注意到在有间隔要求的情况下,x7的总和从10减少到了8,尽管在这些限制条件下,sum(x7)应该可以等于10。我也可以手动整理完整的解决方案,并在Excel中找到一个更优的解决方案,所以我不太明白为什么Gekko没有找到这个更好的结果。
以下是可以在本地重现的完整细节(已测试准确性):
import numpy as np
from gekko import GEKKO
m = GEKKO(remote=False)
m.options.NODES = 3
m.options.IMODE = 3
m.options.MAX_ITER = 1000
lnuc_weeks = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
min_promo_price = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,3]
max_promo_price = [3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5,3.5, 3.5, 3.5, 3.5, 3.5, 3.5]
base_srp = [3.48, 3.48, 3.48, 3.48, 3.0799, 3.0799, 3.0799, 3.0799,3.0799, 3.0799, 3.0799, 3.0799, 3.0799, 3.0799, 3.0799, 3.0799, 3.0799, 3.0799, 3.0799]
lnuc_min_promo_price = 1.99
lnuc_max_promo_price = 1.99
coeff_fedi = [0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589,0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589, 0.022589]
coeff_feao = [0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995, 0.02929995]
coeff_diso = [0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338, 0.05292338]
sumproduct_base = [0.20560305, 0.24735297, 0.24957423, 0.23155435, 0.23424058,0.2368096 , 0.27567109, 0.27820648, 0.2826393 , 0.28660598, 0.28583971, 0.30238505, 0.31726649, 0.31428312, 0.31073792, 0.29036779, 0.32679041, 0.32156337, 0.24633734]
neg_ln = [[0.14842000515],[0.14842000512],[0.14842000515],[0.14842000512],[-0.10407483058],[0.43676249024],[0.43676249019],[0.43676249024],[0.43676249019],[0.43676249024],[0.43676249019], [0.026284840258],[0.026284840291],[0.026284840258],[0.026284840291], [0.026185109811],[0.026284840258],[0.026284840291],[0.026284840258]]
neg_ln_ppi_coeff = [1.22293879, 1.22293879, 1.22293879, 1.22293879, 1.22293879,1.22293879, 1.22293879, 1.22293879, 1.22293879, 1.22293879, 1.22293879, 1.22293879, 1.22293879, 1.22293879, 1.22293879,1.22293879, 1.22293879, 1.22293879, 1.22293879]
base_volume = [124.38, 193.2, 578.72, 183.88, 197.42, 559.01, 67.68, 110.01,60.38, 177.11, 102.65, 66.02, 209.83, 81.22, 250.44, 206.44, 87.99, 298.95, 71.07]
week = pd.Series([13, 14, 17, 18, 19, 26, 28, 33, 34, 35, 39, 42, 45, 46, 47, 48, 50, 51, 52])
n = 19
x1 = m.Array(m.Var,(n), integer=True) #LNUC weeks
i = 0
for xi in x1:
xi.value = lnuc_weeks[i]
xi.lower = 0
xi.upper = lnuc_weeks[i]
i += 1
x2 = m.Array(m.Var,(n)) #Blended SRP
i = 0
for xi in x2:
xi.value = 5
m.Equation(xi >= m.if3((x1[i]) - 0.5, min_promo_price[i], lnuc_min_promo_price))
m.Equation(xi <= m.if3((x1[i]) - 0.5, max_promo_price[i], lnuc_max_promo_price))
i += 1
x3 = m.Array(m.Var,(n), integer=True) #F&D
x4 = m.Array(m.Var,(n), integer=True) #FO
x5 = m.Array(m.Var,(n), integer=True) #DO
x6 = m.Array(m.Var,(n), integer=True) #TPR
#Default to F&D
i = 0
for xi in x3:
xi.value = 1
xi.lower = 0
xi.upper = 1
i += 1
i = 0
for xi in x4:
xi.value = 0
xi.lower = 0
xi.upper = 1
i += 1
i = 0
for xi in x5:
xi.value = 0
xi.lower = 0
xi.upper = 1
i += 1
i = 0
for xi in x6:
xi.value = 0
xi.lower = 0
xi.upper = 1
i += 1
x7 = m.Array(m.Var,(n), integer=True) #Max promos
i = 0
for xi in x7:
xi.value = 1
xi.lower = 0
xi.upper = 1
i += 1
x = [x1,x2,x3,x4,x5,x6,x7]
neg_ln=[m.Intermediate(-m.log(x[1][i]/base_srp[i])) for i in range(n)]
total_vol_fedi =[m.Intermediate(coeff_fedi[0]+ sumproduct_base[i] + (neg_ln[i]*neg_ln_ppi_coeff[0])) for i in range(n)]
total_vol_feao =[m.Intermediate(coeff_feao[0]+ sumproduct_base[i] + (neg_ln[i]*neg_ln_ppi_coeff[0])) for i in range(n)]
total_vol_diso =[m.Intermediate(coeff_diso[0]+ sumproduct_base[i] + (neg_ln[i]*neg_ln_ppi_coeff[0])) for i in range(n)]
total_vol_tpro =[m.Intermediate(sumproduct_base[i] + (neg_ln[i]*neg_ln_ppi_coeff[0])) for i in range(n)]
simu_total_volume = [m.Intermediate((
(m.max2(0,base_volume[i]*(m.exp(total_vol_fedi[i])-1)) * x[2][i] +
m.max2(0,base_volume[i]*(m.exp(total_vol_feao[i])-1)) * x[3][i] +
m.max2(0,base_volume[i]*(m.exp(total_vol_diso[i])-1)) * x[4][i] +
m.max2(0,base_volume[i]*(m.exp(total_vol_tpro[i])-1)) * x[5][i]) + base_volume[i]) * x[6][i]) for i in range(n)]
[m.Equation(x3[i] + x4[i] + x5[i] + x6[i] == 1) for i in range(i)]
#Limit max promos
m.Equation(sum(x7)<=10)
#Enforce spacing
s=1
for s2 in range(1, s+1):
for i in range(0, n-s2):
f = week[week == week[i] + s2].index
if len(f)>0:
m.Equation(x7[i] + x7[f[0]]<=1)
m.Maximize(m.sum(simu_total_volume))
m.options.SOLVER=1
m.solve(disp = True)
1 个回答
1
这里是一个关于如何在一组时间段中强制设置间隔限制的例子,使用了一个滑动窗口的方式,比如:
m.Equation(sum(x[0:3])<=1)
m.Equation(sum(x[1:4])<=1)
m.Equation(sum(x[2:5])<=1)
这里有一个测试,展示了在不同的间隔限制下的解决方案,最多可以选择5个中的4个。间隔限制依次是 [0,1,2,3]:
from gekko import GEKKO
m = GEKKO(remote=False)
for s in [0,1,2,3]:
n = 5
x = m.Array(m.Var,n,integer=True,value=1,lb=0,ub=1)
m.Equation(sum(x)<=4)
for i in range(0,n-s):
m.Equation(sum(x[i:i+s+1])<=1)
m.Maximize(sum(x))
m.options.SOLVER=1
m.solve(disp=False)
print(f'spacing: {s} solution: {x}')
解决方案是:
spacing: 0 solution: [[0.0] [1.0] [1.0] [1.0] [1.0]]
spacing: 1 solution: [[1.0] [0.0] [1.0] [0.0] [1.0]]
spacing: 2 solution: [[0.0] [1.0] [0.0] [0.0] [1.0]]
spacing: 3 solution: [[1.0] [0.0] [0.0] [0.0] [1.0]]
在间隔为0和2的情况下,有多种解决方案,而在间隔为1和3的情况下,解决方案是唯一的。求解器会为每种情况返回其中一个解决方案。如果你希望优先选择更早的时间段,可能需要添加一个额外的目标。