【问题标题】:How to add sequential (time series) constraint to optimization problem using python PuLP?如何使用 python PuLP 为优化问题添加顺序(时间序列)约束?
【发布时间】:2021-02-27 06:15:22
【问题描述】:

一个简单的优化问题:根据能源成本找出冰箱的最佳控制序列。唯一的限制是保持在温度阈值以下,并且目标函数试图最小化所用能源的成本。这个问题被简化了,所以控件只是一个二进制数组,即。 [0, 1, 0, 1, 0],其中1表示用电冷却冰箱,0表示打开冷却机构(表示这段时间没有成本,但温度会升高)。我们可以假设每个周期都是固定的时间段,并且根据它的开/关状态有一个恒定的温度变化。

以下是示例值:

Cost of energy (for our example 5 periods): [466, 426, 423, 442, 494]
Minimum cooling periods (just as a test): 3
Starting temperature: 0
Temperature threshold(must be less than or equal): 1
Temperature change per period of cooling: -1
Temperature change per period of warming (when control input is 0): 2

这是 PuLP 中的代码

from pulp import LpProblem, LpMinimize, LpVariable, lpSum, LpStatus, value 
from itertools import accumulate


l = list(range(5))
costy = [466, 426, 423, 442, 494]
cost = dict(zip(l, costy))
min_cooling_periods = 3

prob = LpProblem("Fridge", LpMinimize)
si = LpVariable.dicts("time_step", l, lowBound=0, upBound=1, cat='Integer')
prob += lpSum([cost[i]*si[i] for i in l])                  # cost function to minimize
prob += lpSum([si[i] for i in l]) >= min_cooling_periods   # how many values must be positive
prob.solve()

在我尝试考虑温度阈值之前,优化似乎已经奏效。仅使用成本函数,它返回一个 0 数组,这确实使成本最小化(duh)。使用第一个约束(有多少个值必须是正数),它会选择最便宜的 3 个冷却期,并正确计算总成本。

obj = value(prob.objective)
print(f'Solution is {LpStatus[prob.status]}\nThe total cost of this regime is: {obj}\n')
for v in prob.variables():
    print(f'{v.name} = {v.varValue}')

output:
Solution is Optimal
The total cost of this regime is: 1291.0

time_step_0 = 0.0
time_step_1 = 1.0
time_step_2 = 1.0
time_step_3 = 1.0
time_step_4 = 0.0

所以,如果我们的控制序列是 [0, 1, 1, 1, 0],那么在每个冷却/加热周期结束时温度将如下所示:[2, 1, 0, -1, 1] .当控制输入为 1 时,温度上升 2,当控制输入为 1 时,温度下降 1。此示例序列是一个有效的答案,但如果我们添加最大温度阈值 1,则必须更改,这意味着第一个value 必须是 1,否则冰箱会升温到 2。

但是,在尝试指定保持在温度阈值内的顺序约束时,我得到了不正确的结果:

up_temp_thresh = 1
down = -1
up = 2
# here is where I try to ensure that the control sequence would never cause the temperature to
# surpass the threshold. In practice I would like a lower and upper threshold but for now 
# let us focus only on the upper threshold.
prob += lpSum([e <= up_temp_thresh for e in accumulate([down if si[i] == 1. else up for i in l])]) >= len(l)

在这种情况下,答案和以前一样,我显然没有正确地表述它,因为序列 [0, 1, 1, 1, 0] 会超过阈值。

我正在尝试编码“每个控制序列结束时的温度必须低于阈值”。我通过将控制序列转换为温度变化数组来做到这一点,因此控制序列 [0, 1, 1, 1, 0] 为我们提供了温度变化 [2, -1, -1, -1, 2]。然后使用accumulate 函数,它计算一个累积和,等于每一步之后的冰箱温度,即[2, 1, 0, -1, 1]。我只想检查这个数组的最大值是否小于阈值,但是使用 lpSum 我检查数组中小于阈值的值的总和是否等于数组的长度,这应该是同一件事.

但是,我显然错误地制定了这一步。正如所写的,最后一个约束对输出没有影响,小的变化会给出其他错误的答案。似乎答案应该是 [1, 1, 1, 0, 0],它给出了一个可接受的温度序列 [-1, -2, -3, -1, 1]。如何使用 PuLP 或其他免费的 python 优化库指定控制输入的顺序性质?

【问题讨论】:

    标签: optimization mathematical-optimization linear-programming pulp integer-programming


    【解决方案1】:

    最简单且最不容易出错的方法是为您的问题创建一组新的辅助变量,用于跟踪每个时间间隔内的冰箱温度。这些不是“主要决策变量”,因为您不能直接选择它们 - 而是它们的值受冰箱的开/关决策变量的约束。

    然后,您将在这些温度状态变量上添加约束以表示动态。所以在未经测试的代码中:

    l_plus_1 = list(range(6))
    fridge_temp = LpVariable.dicts("fridge_temp", l_plus_1, cat='Continuos')
    fridge_temp[0] = init_temp   # initial temperature of fridge - a known value
    
    for i in l:
        prob += fridge_temp[i+1] == fridge_temp[i] + 2 - 3*s[i]
    

    然后您可以在这些新的fridge_temp 变量上发送最低/最高温度限制。

    请注意,在上面我假设冰箱温度变量的定义间隔比冰箱的开/关决定多一个。冰箱温度变量表示区间开始处的温度 - 多出一个意味着我们可以确保冰箱的最终温度是可以接受的。

    【讨论】:

    • 好答案。可以将cat更改为Integer,将s[i]更改为si[i],然后添加临时约束即可。谢谢
    猜你喜欢
    • 1970-01-01
    • 2018-05-24
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多