Pyomo定义目标函数时能否使用numpy分段函数?报错如何解决?
报错原因
你调用的numpy.piecewise、numpy.logical_and都是面向数值计算的函数,无法处理Pyomo的符号变量(model.x[i]、model.y[i]属于Pyomo Var类型,是符号表达式)。numpy在执行区间判断时会尝试将Lmin <= x这类符号不等式转为布尔值,而Pyomo禁止直接对非常量符号表达式做布尔求值,因此触发报错。
修复方案
方案1:使用Pyomo原生Expr_if实现分段函数(最简便)
Expr_if是Pyomo内置的符号条件表达式工具,可以直接基于符号变量的判断条件生成分段表达式,无需修改你的目标函数逻辑,只需替换alpha_beta的实现即可:
首先导入依赖:
from pyomo.environ import Expr_if
重写alpha_beta函数:
def alpha_beta(x): a = 0.0019727939 b = 0.0078887 Lmin, Lnom, Lmax = 0.8035, 2.3811, 3.084 base_val = 0.006226 # 外层判断x是否落在有效区间内 return Expr_if( condition = (x >= Lmin) & (x <= Lmax), then_ = Expr_if( # 内层判断属于左半段还是右半段 condition = x < Lnom, then_ = a * (x - Lnom)**2 + base_val, else_ = b * (x - Lnom)**2 + base_val ), else_ = 0 )
该方案适配所有支持非线性规划(NLP)的求解器,比如IPOPT,无需额外添加约束。
方案2:大M约束法(适配MINLP求解器)
如果你使用的求解器对Expr_if这类逻辑表达式支持不好,可以引入二进制变量明确标记区间,将分段函数转为线性约束+二次表达式的形式:
- 首先给模型添加二进制变量,每个
x[i]、y[i]对应2个二进制变量,分别标记是否落在左段/右段:
# 假设I是x/y的索引集合 model.z1 = Var(model.I, domain=Binary) # z1=1表示变量落在[Lmin, Lnom) model.z2 = Var(model.I, domain=Binary) # z2=1表示变量落在[Lnom, Lmax]
- 添加区间约束,M取大于
Lmax-Lmin的常数即可,这里取3:
M = 3 Lmin, Lnom, Lmax = 0.8035, 2.3811, 3.084 def interval_rule(model, i): x = model.x[i] return [ Lmin*model.z1[i] + Lnom*model.z2[i] - M*(1-model.z1[i]) <= x, x <= (Lnom - 1e-6)*model.z1[i] + Lmax*model.z2[i] + M*(1 - model.z1[i] - model.z2[i]), model.z1[i] + model.z2[i] <= 1 ] model.interval_cons = Constraint(model.I, rule=interval_rule) # y变量同理添加对应的约束即可
- 重写
alpha_beta函数,直接用二进制变量加权得到分段值:
def alpha_beta(x, z1, z2): a = 0.0019727939 b = 0.0078887 Lnom = 2.3811 base_val = 0.006226 return z1*(a*(x-Lnom)**2 + base_val) + z2*(b*(x-Lnom)**2 + base_val)
- 目标函数调用时传入对应的z1、z2变量即可。
内容的提问来源于stack exchange,提问作者Jian ZUO
相关产品推荐
相关产品推荐

