如何根据预先确定的数据设置有条件地将变量添加到 Pyomo 约束

How to conditionally add variables to a Pyomo constraint based on predetermined data settings

我是 Pyomo 的新手。

我想知道是否有一种优雅的方法来编写包含我们可能想要或不想包含的变量的约束。包含它们的选项将在模型求解时已知,并将基于它从数据库中读取的设置。

一个很好的例子可能是约束松弛,有时我们需要它们,有时我们不需要。

我在下面为仓库位置示例制作了一个小演示。在等式 buildLimit 我添加了一个 slack 以允许仓库数量超过构建限制 [代码可能包含一些语法错误我没有 运行 它]

# Import pyomo
from pyomo.environ import *
from pyomo.opt import SolverStatus, TerminationCondition

N = ['Harlingen', 'Memphis', 'Ashland']
M = ['NYC', 'LA', 'Chicago', 'Houston']

d = {('Harlingen', 'NYC'): 1956, \
     ('Harlingen', 'LA'): 1606, \
     ('Harlingen', 'Chicago'): 1410, \
     ('Harlingen', 'Houston'): 330, \
     ('Memphis', 'NYC'): 1096, \
     ('Memphis', 'LA'): 1792, \
     ('Memphis', 'Chicago'): 531, \
     ('Memphis', 'Houston'): 567, \
     ('Ashland', 'NYC'): 485, \
     ('Ashland', 'LA'): 2322, \
     ('Ashland', 'Chicago'): 324, \
     ('Ashland', 'Houston'): 1236 }

P = 2

model = ConcreteModel("warehouse location problem")

model.N = Set(dimen=1, initialize=N)
model.M = Set(dimen=1, initialize=M)
model.d = Param(model.N, model.M, within=PositiveIntegers, initialize=d)
model.P = Param(initialize=P)
model.y = Var(model.N, within=Binary)
model.x = Var(model.N, model.M, bounds=(0,1))
##########################
model.buildLimitSlack = Var(within=NonNegativeIntegers)
model.useSlacks = Param() # assume some data read will populate this at some stage before the solve
##########################

# Objective, minimise delivery costs
def obj_rule(model):
    return sum(model.d[n,m] * model.x[n,m] for n in model.N for m in model.M) + 99*model.buildLimitSlack
model.obj = Objective(rule=obj_rule)
# All customer demand must be met
def demand_rule(model, m):
    return sum(model.x[n,m] for n in model.N) == 1
model.demand = Constraint(model.M, rule=demand_rule)
# Can only ship from a warehouse if that warehouse is built
def supplyOnlyIfBuilt_rule(model, m, n):
    return model.x[n,m] <= model.y[n]
model.supplyOnlyIfBuilt = Constraint(model.M, model.N, rule=supplyOnlyIfBuilt_rule)
##############################
#### WE WANT THE SLACK IN THIS EQUATION TO BE OPTIONAL BASED ON DATA SETTINGS
def buildLimit_rule(model):
    return sum(model.y[n] for n in model.N) <= model.P + model.buildLimitSlack
model.buildLimit = Constraint(rule=buildLimit_rule)
##############################

我想我们可以在约束中有一个 if 语句,如下所示。但我们不希望这样,因为我们的模型方程可能在同一约束中有 许多 这样的可选变量,我不想有大量嵌套的 if 语句 [除非有这样做的好方法?]。

def buildLimit_rule(model):
  if model.useSlacks:    
    return sum(model.y[n] for n in model.N) <= model.P + model.buildLimitSlack
  else:
    return sum(model.y[n] for n in model.N) <= model.P
model.buildLimit = Constraint(rule=buildLimit_rule)

有什么建议吗?

提前致谢

(这个问题很笼统,所以我会提出一些方法,但由您决定哪种方法在哪种情况下效果最好。)

编辑 1 - 稀疏组件:对于与稀疏相关的问题,请参阅此问题: 本质上,您应该初始化 sets,以便它们仅包含 "valid" 值,这将自动确保您仅初始化所需的组件。

编辑 2 - 可选组件:有几种方法可以将可选组件添加到模型中。一个简单的 if 语句就是一种策略,或者您可以查看 BuildActions。另一种选择是创建 Expressions(参数和变量的组合),然后 "optionally" 更改这些而不是约束定义本身。

如果您的所有约束都具有这种形式(即如果 0 是变量的 "neutral" 状态),您可以将它们重写为:

def buildLimit_rule(m):  
    return sum(m.y[n] for n in m.N) <= m.P + m.buildLimitSlack * m.useSlacks

如果您有多个具有独立 on/off 参数的松弛变量:

def buildLimit_rule(m):  
    return sum(m.y[n] for n in m.N) <= m.P + m.v1 * m.use_v1 + m.v2 * m.use_v2 + ...

最通用(但可读性较差)的方法是索引所有潜在的松弛变量:

model.SLACKS = Set(...) # set for slack variables
model.slack = Var(model.SLACKS)
model.use_slack = Param(model.SLACKS, within=Binary)

def buildLimit_rule(m):  
    return sum(m.y[n] for n in m.N) <= m.P + sum(m.slack[i] * m.use_slack[i] for i in m.SLACKS)

另一种方法是 fix 您不需要的变量:

if not m.useSlacks:
    m.Slack.fix(0)
    m.OtherSlack.fix(0)