【问题标题】:Implementing binary constraint in PuLP Objective Function在 PuLP 目标函数中实现二元约束
【发布时间】:2022-02-11 00:47:22
【问题描述】:

我正在尝试使用线性规划确定连接到电网的电池可以获得的最大收入。电池可以在两个市场赚取收入,能源市场和频率市场。当我在目标函数中包含二元约束时,我的模型抛出错误(TypeError: 非常量表达式不能相乘)。

我的目标函数是:

  • N 是优化的时间范围
  • 是时间 t 的能源价格
  • 是时间t分配的放电和充电功率
  • 是时间t的频率价格
  • 是时间t分配的频率功率

在每个时间段 t,电池应仅在一个市场(能量或频率)中有效。所以需要一个看起来像这样的约束:

其中 是激活活动x 的二进制变量。

好的,这就是我想要实现的目标。我正在努力在纸浆中创建这样的限制,如果另一个市场的价值更高(满足所有其他限制),则基本上会关闭对其中一个市场的参与。在我的电池类中,我为每个电源活动以及它们的开/关状态创建了决策变量。


self.charge = \
pulp.LpVariable.dicts(
    "charging_power",
    ('c_t_' + str(i) for i in range(0,time_horizon)),
    lowBound=0, upBound=max_charge_power_capacity,
    cat='Continuous')

self.discharge = \
pulp.LpVariable.dicts(
    "discharging_power",
    ('d_t_' + str(i) for i in range(0,time_horizon)),
    lowBound=0, upBound=max_discharge_power_capacity,
    cat='Continuous')

self.freq = \
pulp.LpVariable.dicts(
    "freq_power",
    ('f_t_' + str(i) for i in range(0,time_horizon)),
    lowBound=0, upBound=max_freq_power_capacity,
    cat='Continuous')

self.charge_status = \
pulp.LpVariable.dicts(
    "charge_status",
    ('c_status_t_' + str(i) for i in range(0,time_horizon)),
    cat='Binary')

self.discharge_status = \
pulp.LpVariable.dicts(
    "discharge_status",
    ('d_status_t_' + str(i) for i in range(0,time_horizon)),
    cat='Binary')

self.freq_status = \
pulp.LpVariable.dicts(
    "freq_status",
    ('ds3_status_t_' + str(i) for i in range(0,time_horizon)),
    cat='Binary')

在我的目标函数中,我包含了这些二进制变量。

    
self.model = pulp.LpProblem("Max Profit", pulp.LpMaximize)

self.model +=\
pulp.lpSum(
    [self.charge['c_t_' + str(i)]*-1*prices[i] *
     self.charge_status['c_status_t_' + str(i)] for i in range(0,self.time_horizon)]
    + [self.discharge['d_t_' + str(i)]*prices[i] * 
     self.discharge_status['d_status_t_' + str(i)] for i in range(0,self.time_horizon)]
    + [self.freq['f_t_' + str(i)]*freq_prices[i] *
     self.freq_status['freq_status_t_' + str(i)] for i in range(0,self.time_horizon)]
)

对于这些二元变量的约束,我设置如下:


 for hour_of_sim in range(1,self.time_horizon+1):     
    self.model += \
        pulp.lpSum([self.charge_status['c_status_t_' + str(i)] for i in range(0,self.time_horizon)] +\
                   [self.discharge_status['d_status_t_' + str(i)] for i in range(0,self.time_horizon)] +\
                   [self.freq_status['freq_status_t_' + str(i)] for i in range(0,self.time_horizon)]
                  ) <= 1

当我尝试解决时,我得到一个

TypeError: 非常数表达式不能相乘

关于目标函数。不喜欢我的二进制变量,如果它们被删除就会运行。一定有另一种设置方法让我无法理解?

【问题讨论】:

  • 一般情况下不能乘以变量。您需要在模型时对其进行线性化。谷歌对一些连续变量和一些二进制变量的乘积进行线性化。

标签: python optimization linear-programming pulp


【解决方案1】:

评论是正确的...您通过将 2 个变量相乘来违反“线性”。幸运的是,这很容易线性化。你有一个控制模式的二进制变量,所以你正在寻找的关键元素(谷歌它)是一个 Big-M 约束,你使用二进制变量乘以最大值(或只是足够大的值)来限制另一个变量要么最大化,要么钳制为零。

下面的例子。我也重新安排了一些事情。您可能会发现这种风格更具可读性。关于风格的两个主要方面:

  1. 您不断地重新创建您正在使用的索引,这读起来真的很痛苦并且容易出错。只需制作它们并重复使用它们......您不需要复杂的索引设置值

  2. 您可以轻松地对该模型进行双索引,我认为这比制作多组变量更清楚。您基本上有 2 组正在使用:时间段和操作模式。只需制作这些集合和双索引。

示例

# battery modes

import pulp

# some data
num_periods = 3
rate_limits = { 'energy'    : 10,
                'freq'      : 20}
price = 2  # this could be a table or double-indexed table of [t, m] or ....

# SETS
M = rate_limits.keys()   # modes.  probably others...  discharge?
T = range(num_periods)   # the time periods

TM = {(t, m) for t in T for m in M}

model = pulp.LpProblem('Batts', pulp.LpMaximize)

# VARS
model.batt = pulp.LpVariable.dicts('batt_state', indexs=TM, lowBound=0, cat='Continuous')
model.op_mode = pulp.LpVariable.dicts('op_mode', indexs=TM, cat='Binary')

# Constraints

# only one op mode in each time period...
for t in T:
    model += sum(model.op_mode[t, m] for m in M) <= 1

# Big-M constraint. limit rates for each rate, in each period.
# this does 2 things:  it is equivalent to the upper bound parameter in the var declaration
#                      It is a Big-M type of constraint which uses the binary var as a control <-- key point
for t, m in TM:
    model += model.batt[t, m] <= rate_limits[m] * model.op_mode[t, m]

# OBJ
model += sum(model.batt[t, m] * price for t, m in TM)

print(model)

#  solve...

产量:

Batts:
MAXIMIZE
2*batt_state_(0,_'energy') + 2*batt_state_(0,_'freq') + 2*batt_state_(1,_'energy') + 2*batt_state_(1,_'freq') + 2*batt_state_(2,_'energy') + 2*batt_state_(2,_'freq') + 0
SUBJECT TO
_C1: op_mode_(0,_'energy') + op_mode_(0,_'freq') <= 1

_C2: op_mode_(1,_'energy') + op_mode_(1,_'freq') <= 1

_C3: op_mode_(2,_'energy') + op_mode_(2,_'freq') <= 1

_C4: batt_state_(2,_'freq') - 20 op_mode_(2,_'freq') <= 0

_C5: batt_state_(2,_'energy') - 10 op_mode_(2,_'energy') <= 0

_C6: batt_state_(1,_'freq') - 20 op_mode_(1,_'freq') <= 0

_C7: batt_state_(1,_'energy') - 10 op_mode_(1,_'energy') <= 0

_C8: batt_state_(0,_'freq') - 20 op_mode_(0,_'freq') <= 0

_C9: batt_state_(0,_'energy') - 10 op_mode_(0,_'energy') <= 0

VARIABLES
batt_state_(0,_'energy') Continuous
batt_state_(0,_'freq') Continuous
batt_state_(1,_'energy') Continuous
batt_state_(1,_'freq') Continuous
batt_state_(2,_'energy') Continuous
batt_state_(2,_'freq') Continuous
0 <= op_mode_(0,_'energy') <= 1 Integer
0 <= op_mode_(0,_'freq') <= 1 Integer
0 <= op_mode_(1,_'energy') <= 1 Integer
0 <= op_mode_(1,_'freq') <= 1 Integer
0 <= op_mode_(2,_'energy') <= 1 Integer
0 <= op_mode_(2,_'freq') <= 1 Integer

【讨论】:

  • 我想我现在已经实现了大 M 并从 obj func 中删除了变量乘法。对于每小时的二进制变量,我乘以 M 并将其设置为 >= 该小时的功率决策变量。当功率决策变量为正时,它应该强制二进制变量为 1。我保持二进制约束,其中二进制变量的总和
  • 我不确定……你在评论中所说的听起来是正确的。你有“最大化”设置吗?结果中是否有任何变量/目标是积极的?您是否检查过求解器输出以确保它是可行/最优的?如果您仍然卡住,您可能需要将其缩减为另一个示例并发布另一个具有相同标签的问题(或者在评论中标记我)
  • 是的求解器是最优的。认为这可能是我的索引的问题,您已经指出它容易出错。不愿重构,直到我有一个工作模型并且觉得我已经很接近了。我会花更多时间在上面,如果需要,我会跟进。我会将其标记为已回答,感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多