皮莫 |库恩求解器 |将索引变量域限制为两个整数值



我是 Pyomo的新手,正在努力理解 Pyomo 语法背后的直觉以及它如何构建模型。这可能就是为什么我无法弄清楚如何使用 Pyomo 和 Couenne 求解器定义和解决N个变量必须仅取 ±1 个值的"二进制"问题。

首先,我尝试了bounds=(-1, 1)Integers域,并尝试添加严格的不等式:

import pyomo.environ as pyo
import numpy as np
N = 5
w = np.ones(range(N))
pyoModel = pyo.ConcreteModel('binary model')
pyoModel.o = pyo.Var(range(N), bounds=(-1, 1), within=pyo.Integers, initialize=1)
pyoModel.binaryConstraintP = pyo.Constraint(range(N), rule=strictlyPositive)
pyoModel.binaryConstraintN = pyo.Constraint(range(N), rule=strictlyNegative)
pyoModel.objective = pyo.Objective(expr=pyo.sum_product(pyoModel.o, w, index=range(N)), sense=pyo.maximize)
def strictlyPositive(model, i):
return model.o[i] > 0
def strictlyNegative(model, i):
return model.o[i] < 0

这最终得到:

ValueError:约束"binaryConstraintP[0]"遇到严格的不等式表达式(">"或"<")。所有约束都必须使用"<="、">="或"=="来表述。

好的,不允许严格的不等式(不知道为什么!),我尝试切换到Binary域并通过操作目标中的变量来执行解决方法,使其位于 {-1, 1} 中 - 即,如果 o ∈{0, 1} 则 2 x o - 1 ∈ {-1, 1}

import pyomo.environ as pyo
import numpy as np
N = 5
w = np.ones(range(N))
pyoModel = pyo.ConcreteModel('binary model')
pyoModel.o = pyo.Var(range(N), within=pyo.Binary, initialize=1)
pyoModel.objective = pyo.Objective(expr=pyo.sum_product(2 * pyoModel.o - 1, w, index=range(N)), sense=pyo.maximize)

有:

类型错误:不支持 * 的操作数类型:"int"和"IndexedVar">

所以我使用了一个由 2 和 1 组成的数组,而不是 2 和 1,但得到了另一个关于形状广播的错误。我确定我在这里遗漏了一些东西,因为在目标中构建线性方程应该很容易吧?

我还尝试将域更改为用户定义的域:

...
pyoModel.domain = pyo.Set(initialize=[-1, 1])
...
pyoModel.o = pyo.Var(range(N), domain=pyoModel.domain, initialize=1)
...
with SolverFactory('couenne') as opt:
results = opt.solve(pyoModel, load_solutions=False)
...

并最终以库安错误结束:

类型错误: 名称为"%s"的变量的域类型无效。变量不是连续的、整数的或二进制的。

我也想过使用 SOS,但更难理解它们是如何工作的!

同样,我一定在每种方法中都遗漏了一些东西。任何帮助都将得到赞赏。

旁注:我尽可能简化了原始代码,使其更易于阅读。

由于使用了严格的不等式,您的第一次尝试失败了,这是禁忌。 这背后有理论,因为这些类型问题的求解者在问题空间的"凸壳"上工作。 有关更多信息,请选取有关线性规划的文本 - 它超出了堆栈溢出答案的范围。

您的第二次尝试是在正确的轨道上。 如果您正在寻找 ±1 的数学等效值,让 x 成为二进制变量并使用线性转换 2x-1 是完全合适的。 您的尝试失败了,因为您通过在Var()构造中提供索引来声明x变量为索引变量,但您没有在目标中使用索引。

下面是一个使用索引变量的示例。 如果只有一个单一实例,则只需删除索引集引用即可。

from pyomo.environ import *
some_constants = {  0: 100,
1: 200,
2: -50,
3: 300,
4: 50}
m = ConcreteModel('plus & minus ones project')
m.S = Set(initialize=range(5))
m.c = Param(m.S, initialize=some_constants)
m.x = Var(m.S, within=Binary)    # creates {m.x[0], m.x[1], ... , m.x[4]}
# try to maximize the sum of x*c
m.obj = Objective(expr=sum((2*m.x[s] - 1)*m.c[s] for s in m.S), sense=maximize)
# some constraint to limit the number of "+1 picks" to 2...easy with binary vars.
m.C1 = Constraint(expr=sum(m.x[s] for s in m.S) <= 2)
m.pprint()

收益 率:

1 Set Declarations
S : Size=1, Index=None, Ordered=Insertion
Key  : Dimen : Domain : Size : Members
None :     1 :    Any :    5 : {0, 1, 2, 3, 4}
1 Param Declarations
c : Size=5, Index=S, Domain=Any, Default=None, Mutable=False
Key : Value
0 :   100
1 :   200
2 :   -50
3 :   300
4 :    50
1 Var Declarations
x : Size=5, Index=S
Key : Lower : Value : Upper : Fixed : Stale : Domain
0 :     0 :  None :     1 : False :  True : Binary
1 :     0 :  None :     1 : False :  True : Binary
2 :     0 :  None :     1 : False :  True : Binary
3 :     0 :  None :     1 : False :  True : Binary
4 :     0 :  None :     1 : False :  True : Binary
1 Objective Declarations
obj : Size=1, Index=None, Active=True
Key  : Active : Sense    : Expression
None :   True : maximize : (2*x[0] - 1)*100 + (2*x[1] - 1)*200 + (2*x[2] - 1)*-50 + (2*x[3] - 1)*300 + (2*x[4] - 1)*50
1 Constraint Declarations
C1 : Size=1, Index=None, Active=True
Key  : Lower : Body                             : Upper : Active
None :  -Inf : x[0] + x[1] + x[2] + x[3] + x[4] :   2.0 :   True
5 Declarations: S c x obj C1

不允许严格不等式的原因是它可能没有得到基础解决方案理论的支持。我相信这适用于MIP。您是否尝试过在定义的约束中将限制设置为非常小的值?例如

def strictlyPositive(model, i):
return model.o[i] >= 0.000000000000001
def strictlyNegative(model, i):
return model.o[i] <= -0.000000000000001

以这种方式模拟严格的不等式并不理想,因为它可能会导致数值问题,但值得一试。

最新更新