如何在Pyomo中处理布尔逻辑?适配GLPK的线性规划替代方案
问题:Pyomo线性规划中嵌入价格逻辑触发布尔表达式报错
需要在Pyomo优化模型中加入价格计算逻辑,但触发布尔逻辑相关报错,当前使用GLPK求解器(仅支持线性规划),寻求可行的替代方案。
原代码
spread_min_150 = 0.01 spread_150_max = 0.05 SPOT = 600 avg = 10 h = 730 def price(SPOT, POSITION, spread_min_150, spread_150_max): if SPOT < 150: spread_price = SPOT * (1 + spread_min_150) if POSITION >= 0 else SPOT * (1 - spread_min_150) else: spread_price = SPOT * (1 + spread_150_max) if POSITION >= 0 else SPOT * (1 - spread_150_max) return spread_price model = pyo.ConcreteModel() model.a = pyo.Var(domain=pyo.NonNegativeReals) model.b = pyo.Var(domain=pyo.NonNegativeReals) mcp = avg * 730 - (model.a*730 + model.b*730) B = mcp * price(SPOT, mcp, spread_min_150, spread_150_max) model.obj = pyo.Objective(expr=B, sense=pyo.minimize) model.obj.pprint() opt = pyo.SolverFactory('glpk') # ('glpk', executable='/usr/bin/glpsol') result = opt.solve(model) print(result) model.display()
报错信息
--------------------------------------------------------------------------- PyomoException Traceback (most recent call last) <ipython-input-5-c3d5ce23abb2> in <cell line: 22>() 20 mcp = avg * 730 - (model.a*730 + model.b*730) 21 ---> 22 B = mcp * price(SPOT, mcp, spread_min_150, spread_150_max) 23 24 model.obj = pyo.Objective(expr=B, sense=pyo.minimize) 1 frames /usr/local/lib/python3.10/dist-packages/pyomo/core/expr/relational_expr.py in __bool__(self) 46 if self.is_constant(): 47 return bool(self()) ---> 48 raise PyomoException( 49 """ 50 Cannot convert non-constant Pyomo expression (%s) to bool. PyomoException: Cannot convert non-constant Pyomo expression (0 <= 7300 - (730*a + 730*b)) to bool. This error is usually caused by using a Var, unit, or mutable Param in a Boolean context such as an "if" statement, or when checking container membership or equality. For example, >>> m.x = Var() >>> if m.x >= 1: ... pass and >>> m.y = Var() >>> if m.y in [m.x, m.y]: ... pass would both cause this exception.
原因分析
报错核心是不能将Pyomo变量/表达式用于Python原生的if判断:
- 原代码中
price函数里的POSITION >= 0是对Pyomo表达式mcp的判断,Python会尝试将其转为布尔值,但Pyomo的非恒定表达式不支持这种隐式转换。 - 原逻辑中的分支判断属于非线性分段函数,直接用Python条件语句无法被GLPK识别为线性约束或目标。
解决方案
由于当前案例中SPOT=600是固定值(大于150),可以简化分支逻辑,通过辅助变量线性化处理mcp的正负分支,适配GLPK的线性规划求解能力:
修改后的代码
import pyomo.environ as pyo spread_min_150 = 0.01 spread_150_max = 0.05 SPOT = 600 avg = 10 h = 730 model = pyo.ConcreteModel() model.a = pyo.Var(domain=pyo.NonNegativeReals) model.b = pyo.Var(domain=pyo.NonNegativeReals) # 定义mcp表达式 mcp = avg * 730 - (model.a*730 + model.b*730) # 引入辅助变量,分别表示mcp的正部和负部 model.mcp_pos = pyo.Var(domain=pyo.NonNegativeReals) model.mcp_neg = pyo.Var(domain=pyo.NonNegativeReals) # 添加约束:mcp = 正部 - 负部,确保仅其中一个变量非零 model.mcp_decompose = pyo.Constraint(expr=mcp == model.mcp_pos - model.mcp_neg) # SPOT=600>150,直接使用对应spread系数 price_pos = SPOT * (1 + spread_150_max) # mcp>=0时的价格 price_neg = SPOT * (1 - spread_150_max) # mcp<0时的价格 # 线性化目标函数:将mcp*price拆分为正部和负部的线性组合 model.obj = pyo.Objective(expr=model.mcp_pos*price_pos - model.mcp_neg*price_neg, sense=pyo.minimize) model.obj.pprint() opt = pyo.SolverFactory('glpk') result = opt.solve(model) print(result) model.display()
扩展说明(若SPOT为变量)
如果后续SPOT变为优化变量,需要引入二进制变量实现分支逻辑(模型转为混合整数线性规划MILP,GLPK支持该类型):
- 定义二进制变量
y(取值0或1),表示SPOT < 150的状态:y=1:SPOT < 150,使用spread_min_150y=0:SPOT >=150,使用spread_150_max
- 添加约束限制SPOT的范围:
M = 1e6 # 足够大的常数 model.y = pyo.Var(domain=pyo.Binary) model.spot_low = pyo.Constraint(expr=SPOT >= 0 - M*(1 - model.y)) model.spot_high = pyo.Constraint(expr=SPOT <= 150 + M*model.y) - 基于
y切换价格系数,构建线性目标函数。
内容的提问来源于stack exchange,提问作者cirogalvao
相关产品推荐
相关产品推荐

