如何基于预定的数据设置有条件地将变量添加到Pyomo约束中

时间:2019-12-04 02:06:36

标签: pyomo

我是Pyomo的新手。

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

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

我在下面为仓库位置示例开发了一个小演示。在方程式 buildLimit 中,我添加了一个松弛量,以允许仓库数量超过构建限制[代码可能包含一些我没有运行过的语法错误]

# 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 个答案:

答案 0 :(得分:1)

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

编辑1-稀疏组件:对于与稀疏性相关的问题,请参见以下问题:Create a variable with sparse index in pyomo 本质上,您应该初始化您的 sets ,以便它们仅包含“有效”值,这将自动确保您仅在初始化必需的组件。

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

如果所有约束都具有这种形式(即,变量的“中立”状态为0),则可以将其重写为:

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)