用于评估优化变量的最大值/最小值的 Pyomo 约束的解决方法

问题描述 投票:0回答:1

我目前正在致力于用 Python 实现 MINLP 优化问题。为了解决这个问题,我使用了 Pyomo 环境和 Couenne 求解器(版本 0.2.2)。

除以下限制外,到目前为止,所有限制均已制定,没有任何问题。具体来说,挑战是为优化变量制定约束,我们称之为

model.x = pyo.Var(range(0, 25), bounds=(Min_value, Max_value), domain=pyo.NonNegativeReals)

这样:

model.x
的最大值和最小值应具有最小间隔S。

换句话说,

max(model.x) - min(model.x) >= S

不幸的是,你不能直接使用内置的Python max() 和 min() 函数。

def Min_Max(model):
    return max(model.x)- min(model.x) >= S

model.Const_MinMax =  pyo.Constraint(rule=Min_Max)

出现以下错误:

ValueError: Invalid constraint expression.
The constraint expression resolved to a trivial Boolean (False) instead of a Pyomo object.
Please modify your rule to return Constraint.Infeasible instead of False.

在Python中,由于某种原因,它只取

model.x
的最大和最小索引(整数值)并减去它们。很明显,返回了一个布尔表达式,这是一个不正确的 Pyomo 数据类型。

我认为可能使用的数据类型是:

<class 'pyomo.core.expr.numeric_expr.LinearExpression'>
或者
<class 'pyomo.core.expr.relational_expr.EqualityExpression'>
<class 'pyomo.core.expr.relational_expr.InequalityExpression'>

使用

max(pyo.value(model.x[i]) for i in range(0, 25))
也是不可行的,因为它只在开始时检查
model.x
的初始化值,并且在优化过程中不会检索该值。

此外,我尝试使用循环和 if 语句来找到最小值和最大值。这也不起作用,好像条件不允许(再次由于布尔上下文)。 错误信息:

PyomoException: Cannot convert non-constant Pyomo expression (x[1]  <  x[0]) 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 提供了

pyo.Expr_if
条件。因此,我尝试按如下方式构建循环。

 def Min_Max (model):
       Min=model.x[0]
       Max=model.x[0]
       for i in range(0,25):  
          Max=pyo.Expr_if(model.x[i]>Max,model.x[i],Max)
          Min=pyo.Expr_if(model.x[i]<Min,model.x[i],Min)       
       return Max-Min>= S 

model.Const_MinMax =  pyo.Constraint(rule=Min_Max)

不幸的是,这也没有取得成功。虽然求解器不再给出错误消息,但它也不会开始求解。造成这种情况的可能原因是循环创建的高度嵌套的 if 条件。

我现在的问题是是否有人有解决方法来实现这个条件? 如果我想实现这个条件,是否建议使用 Pyomo 以外的环境?

如果对病情仍有疑问或需要进一步解释,请告诉我。

python pyomo
1个回答
0
投票

好吧,你已经尝试了所有“非法”方式:

  • 您不能在公式中使用
    max(), min(), abs()
    等,因为它们是非线性函数
  • 您不能使用基于变量值的条件语句来确定求解过程,因为当问题移交给求解器时,该值是未知的

所以,挑战是如何使其线性。如果您想强制 x 值之间的

maximal
分离,问题会容易得多。如果是这种情况,您将需要:

  • 一个捕获最大值的变量,以及一个将其限制为最大值的约束
  • 一个捕获最小值的变量,...
  • 限制它们之间差异的约束。

您的问题有点复杂,因为您想要强制执行最小间隔。下面是一个例子。我认为您可能会陷入进行成对比较的困境,正如我在约束中所示的那样。然后,您有一个次要约束来基本上强制分离约束至少一次有效。

为了使这个机制发挥作用,您还需要一个 Big-M 值来限制,在不是选定值的情况下使分离约束过低。您应该适当选择

M
。另外,您应该尽可能使用 pyomo Sets,而不是 range(),它更容易编写、排除故障或修改。

该示例只是尝试最小化

x
的总和,但强制执行最小增量。您可以看到
x[4]
“为团队拿一份。”

代码:

import pyomo.environ as pyo

delta = 5  # the required sep between at least 2 elements
M = 100  # the maximum possible separation
m = pyo.ConcreteModel()

### SETS
m.S = pyo.Set(initialize=range(5))

### VARS
m.x = pyo.Var(m.S, domain=pyo.NonNegativeReals)
m.selected = pyo.Var(m.S, m.S, domain=pyo.Binary,
                     doc='Selected indices for the pair with required delta')

### OBJ:  minimize sum of x
m.obj = pyo.Objective(expr=sum(m.x[s] for s in m.S))


### CONSTRAINTS
@m.Constraint(m.S, m.S)
def delta_met(m, s1, s2):
    return m.x[s1] - m.x[s2] >= delta * m.selected[s1, s2] - M * (1 - m.selected[s1, s2])


m.requirement_met = pyo.Constraint(expr=sum(m.selected[s1, s2] for s1 in m.S for s2 in m.S) >= 1)

m.pprint()

### SOLVE
solver = pyo.SolverFactory('cbc')
res = solver.solve(m)
print(res)
m.x.display()
# m.selected.display()

输出(截断):

x : Size=5, Index=S
    Key : Lower : Value : Upper : Fixed : Stale : Domain
      0 :     0 :   0.0 :  None : False : False : NonNegativeReals
      1 :     0 :   0.0 :  None : False : False : NonNegativeReals
      2 :     0 :   0.0 :  None : False : False : NonNegativeReals
      3 :     0 :   0.0 :  None : False : False : NonNegativeReals
      4 :     0 :   5.0 :  None : False : False : NonNegativeReals
© www.soinside.com 2019 - 2024. All rights reserved.