我目前正在尝试将 pyomo 从 6.5.0 升级到 6.6.2
但是,当我尝试将布尔值与表达式对象相乘时,我遇到了 * 运算符的问题。它对于二进制变量可以正常工作,但对于返回这些错误的本机布尔值则失败:
- TypeError: unsupported operand type(s) for *: '_GeneralExpressionData' and 'bool'
- TypeError: unsupported operand type(s) for *: 'bool' and 'SumExpression'
- TypeError: unsupported operand type(s) for *: 'ScalarExpression' and 'bool'
- TypeError: unsupported operand type(s) for *: 'bool' and 'NPV_SumExpression'
所有这些操作在 6.5.0 中都运行良好,我总是可以通过在乘法之前将布尔值转换为整数来解决它,但这意味着对项目进行大量更改。
为什么不再支持此操作以及如何有效解决此问题?
谢谢
配置:
工作示例:
import pyomo.environ as pyo
from pyomo.repn.standard_repn import generate_standard_repn
from pyomo.opt import SolverFactory
model = pyo.ConcreteModel()
model.x = pyo.Var([1,2], domain=pyo.NonNegativeReals)
model.exp = pyo.Expression(rule= lambda m: 3*m.x[1] + 4*m.x[2])
model.OBJ = pyo.Objective(expr = 2*model.x[1] + 3*model.x[2])
model.Constraint1 = pyo.Constraint(expr = model.exp >= 1)
def update_expression(attribute, update, add_expression: bool = False):
original_index = attribute.index_set()
update_values = (update[index] for index in original_index)
for index, update_value in zip(original_index, update_values, strict=True):
attribute[index] = generate_standard_repn(update_value + attribute[index] * add_expression).to_expression()
model.new_expression = pyo.Expression(rule= lambda _ :-1)
update_expression(model.exp, model.new_expression, add_expression=True)
opt = SolverFactory('cbc')
result = opt.solve(model, tee=True)
model.display()
用于动态更新表达式的函数示例
这种行为是故意的,您看到的异常是为了捕获建模错误而添加的。问题的关键在于,逻辑运算和代数运算在概念上属于不同的表达系统,不应该混合在一起。例如,“
5 * True
”应该是代数运算(乘法)并返回5
(假设逻辑状态True
应映射到数字状态1
),还是应该是逻辑运算(和)并返回 True
(将 5
映射到 True
后)? Python 使用前者,这似乎是正确的。不幸的是,在构建表达系统时这会变得更加混乱。考虑:
(m.p >= 5) * 10
如果
m.p
是一个值为 6 的不可变 Param
,那么根据 Python 规则,这个表达式将为 10。不幸的是,如果 m.p
是一个 Var
,你会得到一个表达式,并且该表达式无法写出到任何求解器 [*1]。
随着我们在 Pyomo 中开发和扩展对逻辑表达式的支持(例如,通过 GDP 中的工作以及新的草案约束编程接口),代数和逻辑表达式系统之间的区别变得更加明显。从 Pyomo 6.6 开始,我们在代数表达式(由于历史原因位于
NumericExpression
层次结构中)、逻辑表达式(在 BooleanExpression
层次结构中)和关系表达式(源自 RelationalExpression
)之间进行了强烈区分。两者(关系表达式是具有代数表达式参数的逻辑表达式)。
至于您的用例,而不是:
for index, update_value in zip(original_index, update_values, strict=True):
attribute[index] = generate_standard_repn(update_value + attribute[index] * add_expression).to_expression()
我会推荐类似的东西:
if add_expression:
for index, update_value in zip(original_index, update_values, strict=True):
attribute[index] += update_value
else:
for index, update_value in zip(original_index, update_values, strict=True):
attribute[index] = update_value
[*1] 从技术上讲,您可以将该表达式写入 NL 文件中,但需要将其转换为
Expr_if(IF_=m.p >= 5, THEN_=10, ELSE_=0)
。虽然您可以发出该表达式,但 m.p == 5
处的不连续性可能会给许多求解器带来问题。