Pyomo:Param 和 Var 放在列表中时未构造

Pyomo: Param and Var not constructed when placed in a list

在使用 pyomo 对优化问题建模时,我注意到在使用 VarParamlist 时出现奇怪的行为:我总是收到以下错误 ValueError: Evaluating the numeric value of parameter 'SimpleParam' before the Param has been constructed (there is currently no value to return).

以下代码(最小化 4*x+1 使得 x >= 0)完全按预期运行:

import pyomo.environ as pyo
from pyomo.opt import SolverFactory

def _obj(model):
    return model.c*model.x + 1

model = pyo.ConcreteModel()
model.x = pyo.Var(domain=pyo.NonNegativeReals)
model.c = pyo.Param(initialize=lambda model: 4, domain=pyo.NonNegativeReals)
model.obj = pyo.Objective(rule=_obj, sense=pyo.minimize)
opt = SolverFactory('glpk')
opt.solve(model)

但是当我在 list 中设置 model.xmodel.c 时,程序在创建 objective 函数时崩溃:

import pyomo.environ as pyo
from pyomo.opt import SolverFactory

def _obj(model):
    return model.c[0]*model.x[0] + 1

model = pyo.ConcreteModel()
model.x = [pyo.Var(domain=pyo.NonNegativeReals)]
model.c = [pyo.Param(initialize=lambda model: 4, domain=pyo.NonNegativeReals)]
model.obj = pyo.Objective(rule=_obj, sense=pyo.minimize)
opt = SolverFactory('glpk')
opt.solve(model)

导致此错误的原因是什么?这是出于我不理解的原因而需要的行为还是这是一个错误?无论如何,如何在问题中使用 ParamVar 的列表?我知道理论上我可以 展平 我的所有参数和变量到一个 IndexedVarIndexedParam 并自己处理新索引,但这会很乏味,因为我的 xc 的第三个和第四个索引的范围取决于第一个和第二个索引,因此如果我可以使用 lists 在我的代码中会更清楚。

更准确地说:我有一个看起来像这样的代码(尽管我仍然想知道为什么上面的 MWE 不起作用):

# I, J are lists of indices and N is a list of integer values
model.Vs = [pyo.RangeSet(N[i]) for i in range(len(N))]
model.xs = [[pyo.Var(model.Vs[i], model.Vs[j]) for j in J] for i in I]
model.cs = [[pyo.Param(model.Vs[i], model.Vs[j]) for j in J] for i in I]
def _obj(model):
    sum(model.xs[i][j][k,ell] * model.xs[i][j][k,ell] \
        for i in I for j in J \
        for k in model.Vs[i] for ell in model.Vs[j])

model.obj = Objective(rule=_obj, sense=pyo.minimize)
model.constraints = [
    [pyo.Constraint(model.Vs[i], model.Vs[j], rule=...) for j in J]
    for i in I
]
opt = SolverFactory('glpk')
opt.solve(model)

也许这会有所帮助。除了说 pyomo 是一种将结构化数学问题传递给求解器的建模语言之外,我不确定我能否回答为什么您的示例失败,并且需要离散定义集合,而不是在对象列表中。也许其他人可以参与并解释得更清楚。

在您的建模中,您似乎想要为 x[i,j] 构建某种参差不齐的集合,其中 j 的范围可能会因 i 而异。您通常希望同时为 IJ 创建集合以支持各种约束构造。然后你可以为任何需要被这个参差不齐的集合索引的模型组件创建一个“有效”(i,j)元组的子集。您可以使用此子集作为迭代的基础,或者如果您正在构造事物 on-the-fly.

,则可以使用它来检查成员资格

这是一个使用您的列表 N 的示例:

import pyomo.environ as pyo

N = [1, 4, 3]

m = pyo.ConcreteModel()

m.I  = pyo.Set(initialize=range(len(N)))
m.J  = pyo.Set(initialize=range(max(N)))
m.IJ = pyo.Set(within=m.I * m.J, initialize =
    [(i, j) for i in range(len(N)) for j in range(N[i])])

m.x = pyo.Var(m.IJ, domain=pyo.NonNegativeReals)

def _obj(model):
    return sum(m.x[t] for t in m.IJ)
m.obj = pyo.Objective(rule=_obj)

def constrain_x2(model):
    return sum(m.x[2, j] for j in m.J if (2, j) in m.IJ) >=1
m.c1 = pyo.Constraint(rule=constrain_x2)

m.pprint()

产量:

4 Set Declarations
    I : Dim=0, Dimen=1, Size=3, Domain=None, Ordered=False, Bounds=(0, 2)
        [0, 1, 2]
    IJ : Dim=0, Dimen=2, Size=8, Domain=IJ_domain, Ordered=False, Bounds=None
        [(0, 0), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2)]
    IJ_domain : Dim=0, Dimen=2, Size=12, Domain=None, Ordered=False, Bounds=None
        Virtual
    J : Dim=0, Dimen=1, Size=4, Domain=None, Ordered=False, Bounds=(0, 3)
        [0, 1, 2, 3]

1 Var Declarations
    x : Size=8, Index=IJ
        Key    : Lower : Value : Upper : Fixed : Stale : Domain
        (0, 0) :     0 :  None :  None : False :  True : NonNegativeReals
        (1, 0) :     0 :  None :  None : False :  True : NonNegativeReals
        (1, 1) :     0 :  None :  None : False :  True : NonNegativeReals
        (1, 2) :     0 :  None :  None : False :  True : NonNegativeReals
        (1, 3) :     0 :  None :  None : False :  True : NonNegativeReals
        (2, 0) :     0 :  None :  None : False :  True : NonNegativeReals
        (2, 1) :     0 :  None :  None : False :  True : NonNegativeReals
        (2, 2) :     0 :  None :  None : False :  True : NonNegativeReals

1 Objective Declarations
    obj : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : minimize : x[0,0] + x[1,0] + x[1,1] + x[1,2] + x[1,3] + x[2,0] + x[2,1] + x[2,2]

1 Constraint Declarations
    c1 : Size=1, Index=None, Active=True
        Key  : Lower : Body                     : Upper : Active
        None :   1.0 : x[2,0] + x[2,1] + x[2,2] :  +Inf :   True

7 Declarations: I J IJ_domain IJ x obj c1

你的最小示例

import pyomo.environ as pyo
from pyomo.opt import SolverFactory

def _obj(model):
    return model.c[0]*model.x[0] + 1

model = pyo.ConcreteModel()
model.x = [pyo.Var(domain=pyo.NonNegativeReals)]
model.c = [pyo.Param(initialize=lambda model: 4, domain=pyo.NonNegativeReals)]
model.obj = pyo.Objective(rule=_obj, sense=pyo.minimize)
opt = SolverFactory('glpk')
opt.solve(model)

生成以下错误:

ValueError: Evaluating the numeric value of parameter 'SimpleParam' before
        the Param has been constructed (there is currently no value to return).

原因是您没有直接将生成的 VarParam 附加到模型。当您将 Pyomo 建模组件附加到 Block 时会发生很多事情(ConcreteModel 对象是构造块的实例):

  • 组件分配了一个名称(匹配块属性名称)
  • 组件被插入到层次结构中(基本上,设置了指针,以便方法可以在模型层次结构中上下移动)
  • 组件已分类,以便编写器、求解器和转换稍后可以找到它
  • (如果Block已经构建),组件自动构建

通过将组件放在列表中,您可以有效地向 Pyomo“隐藏”它的存在。您遇到的第一个错误与最后一个项目符号有关(尚未构建 Param)。然而,在构建列表时简单地构建 Param 和 Var 是不够的,因为其他操作不会发生,您稍后会遇到一个不同的错误(当 LP 作者遇到时,下一个错误将是一个模糊的错误) Objective 中的一个 Var,它在第一次遍历模型层次结构时没有找到)。