我正在寻找在Pyomo中创建一个简单的指标变量。假设我有一个变量x,如果x> 0,则该指标函数的取值为1,否则为0。
这是我尝试执行的操作:
model = ConcreteModel()
model.A = Set(initialize=[1,2,3])
model.B = Set(initialize=['J', 'K'])
model.x = Var(model.A, model.B, domain = NonNegativeIntegers)
model.ix = Var(model.A, model.B, domain = Binary)
def ix_indicator_rule(model, a, b):
return model.ix[a, b] == int(model.x[a, b] > 0)
model.ix_constraint = Constraint(model.A, model.B,
rule = ix_indicator_rule)
我收到的错误消息与Avoid this error by using Pyomo-provided math functions
相似,根据this link可以在pyomo.environ
找到……但是我不确定如何执行此操作。我尝试使用validate_PositiveValues()
,如下所示:
def ix_indicator_rule(model, a, b):
return model.ix[a, b] == validate_PositiveValues(model.x[a, b])
model.ix_constraint = Constraint(model.A, model.B,
rule = ix_indicator_rule)
没有运气。任何帮助表示赞赏!
答案 0 :(得分:2)
您可以使用“ big-M”约束来实现此目标,如下所示:
model = ConcreteModel()
model.A = Set(initialize=[1, 2, 3])
model.B = Set(initialize=['J', 'K'])
# m must be larger than largest allowed value of x, but it should
# also be as small as possible to improve numerical stability
model.m = Param(initialize=1e9)
model.x = Var(model.A, model.B, domain=NonNegativeIntegers)
model.ix = Var(model.A, model.B, domain=Binary)
# force model.ix to be 1 if model.x > 0
def ix_indicator_rule(model, a, b):
return model.x <= model.ix[a, b] * model.m
model.ix_constraint = Constraint(
model.A, model.B, rule=ix_indicator_rule
)
但是请注意,big-M约束是单方面的。在此示例中,它在model.ix
时强制model.x > 0
开启,但在model.x == 0
时不强制关闭。通过将不等式翻转为model.x >= model.ix[a, b] * model.m
,可以实现后者(但不能达到前者)。但是您不能在同一模型中同时做这两个事情。通常,您只是选择适合您模型的版本,例如,如果将model.ix
设置为1
会使目标函数恶化,那么您将选择上面显示的版本,求解器将负责设置{{ 1}}到model.ix
。
Pyomo还提供了可能适合您需求的析取编程功能(请参阅here和here)。 cplex解算器提供了indicator constraints,但我不知道Pyomo是否支持它们。
答案 1 :(得分:0)
我最终使用了分段功能,并做了如下操作:
DOMAIN_PTS = [0,0,1,1000000000]
RANGE_PTS = [0,0,1,1]
model.ix_constraint = Piecewise(
model.A, model.B,
model.ix, model.x,
pw_pts=DOMAIN_PTS,
pw_repn='INC',
pw_constr_type = 'EQ',
f_rule = RANGE_PTS,
unbounded_domain_var = True)
def objective_rule(model):
return sum(model.ix[a,b] for a in model.A for b in model.B)
model.objective = Objective(rule = objective_rule, sense=minimize)
似乎工作正常。