使用 Pyomo 中的一组元组使用 2 个索引对决策变量进行索引的可能性

Possibility of indexing decision variables with 2 indices using a set of tuples in Pyomo

我目前正在尝试解决一个不完全的网络问题connected.Thus,我尝试对数据进行一些预处理以形成一组元组,例如{(a,b , (c,e)}...,即从 a 到 b,从 c 到 e。

我能够通过使用元组集进行索引来声明带有键(a,b),(c,e)的二元决策变量。

但是,当我尝试使用规则来声明约束时,使用诸如 x[i][j] 之类的决策变量,会抛出错误,指出 (a,b) 是无效索引。

因此,我想问一下元组是否可以用作决策变量的索引。

如果不是,有没有办法只声明唯一需要的决策变量,而不是声明所有,然后将不需要的设置为 0。

谢谢!

欢迎来到本站。

你当然可以用任意维度的元组索引变量。下面的代码显示了使用节点元组索引 bad_X 的示例。当你有一个变量对你的索引的所有组合都有逻辑表示,或者你用智能集合符号以某种方式控制索引,或者你冒着一大堆无意义变量的风险,正如你在下面的例子中看到的 bad_X 在打印输出中。

对于您的网络,我建议只制作一组作为 NODES 有效组合的 ARCS。该集合将只包含有效连接的元组。请注意,在我下面的示例中,节点集可能会被吹走,因为它不需要。您可以直接创建 ARCS。如果您有一些基于节点的数据,例如 supply/demand.

,有时同时拥有两者会很方便
import pyomo.environ as pyo

mdl = pyo.ConcreteModel()

arc_data = {    ('a', 'b'): 10,
                ('c', 'd'): 5,
                ('e', 'j'): 44,
                ('a', 'j'): 2,
                ('j', 'e'): 12}

# collapse the nodes into a set from the keys of the arc data
node_set = set()
for k in arc_data.keys():
    node_set.update(*k)


# Sets
mdl.NODES = pyo.Set(initialize=node_set)
mdl.ARCS = pyo.Set(within=mdl.NODES * mdl.NODES, initialize=arc_data.keys())

# Params
mdl.cost = pyo.Param(mdl.NODES, mdl.NODES, initialize=arc_data)

# Selection Variable:  poor choice...
mdl.bad_X = pyo.Var(mdl.NODES, mdl.NODES, domain=pyo.Binary)

# some examples of using this "bad X"
print('printing just to show how to access...this will produce a None')
print(mdl.bad_X['a', 'e'].value)
print(mdl.bad_X[('c', 'd')].value)    # also valid

# Better selection variable
mdl.X = pyo.Var(mdl.ARCS, domain=pyo.Binary)

# toy constraint...ensure at least 3 are selected
mdl.c1 = pyo.Constraint(expr=sum(mdl.X[arc] for arc in mdl.ARCS) >= 3)

# Objective:  Minimize cost of 3 selected arcs
mdl.OBJ = pyo.Objective(expr=sum(mdl.X[arc] * mdl.cost[arc] for arc in mdl.ARCS))

# to show the difference between the choices of variables
mdl.pprint()

# solve and show results
solver = pyo.SolverFactory('cbc')
results = solver.solve(mdl)
for arc in mdl.ARCS:
    print(arc, mdl.X[arc].value)

输出:

printing just to show how to access...this will produce a None
None
None
5 Set Declarations
    ARCS : Dim=0, Dimen=2, Size=5, Domain=ARCS_domain, Ordered=False, Bounds=None
        [('a', 'b'), ('a', 'j'), ('c', 'd'), ('e', 'j'), ('j', 'e')]
    ARCS_domain : Dim=0, Dimen=2, Size=36, Domain=None, Ordered=False, Bounds=None
        Virtual
    NODES : Dim=0, Dimen=1, Size=6, Domain=None, Ordered=False, Bounds=None
        ['a', 'b', 'c', 'd', 'e', 'j']
    bad_X_index : Dim=0, Dimen=2, Size=36, Domain=None, Ordered=False, Bounds=None
        Virtual
    cost_index : Dim=0, Dimen=2, Size=36, Domain=None, Ordered=False, Bounds=None
        Virtual

1 Param Declarations
    cost : Size=5, Index=cost_index, Domain=Any, Default=None, Mutable=False
        Key        : Value
        ('a', 'b') :    10
        ('a', 'j') :     2
        ('c', 'd') :     5
        ('e', 'j') :    44
        ('j', 'e') :    12

2 Var Declarations
    X : Size=5, Index=ARCS
        Key        : Lower : Value : Upper : Fixed : Stale : Domain
        ('a', 'b') :     0 :  None :     1 : False :  True : Binary
        ('a', 'j') :     0 :  None :     1 : False :  True : Binary
        ('c', 'd') :     0 :  None :     1 : False :  True : Binary
        ('e', 'j') :     0 :  None :     1 : False :  True : Binary
        ('j', 'e') :     0 :  None :     1 : False :  True : Binary
    bad_X : Size=36, Index=bad_X_index
        Key        : Lower : Value : Upper : Fixed : Stale : Domain
        ('a', 'a') :     0 :  None :     1 : False :  True : Binary
        ('a', 'b') :     0 :  None :     1 : False :  True : Binary
        ('a', 'c') :     0 :  None :     1 : False :  True : Binary
        ('a', 'd') :     0 :  None :     1 : False :  True : Binary
        ('a', 'e') :     0 :  None :     1 : False :  True : Binary
        ('a', 'j') :     0 :  None :     1 : False :  True : Binary
        ('b', 'a') :     0 :  None :     1 : False :  True : Binary
        ('b', 'b') :     0 :  None :     1 : False :  True : Binary
        ('b', 'c') :     0 :  None :     1 : False :  True : Binary
        ('b', 'd') :     0 :  None :     1 : False :  True : Binary
        ('b', 'e') :     0 :  None :     1 : False :  True : Binary
        ('b', 'j') :     0 :  None :     1 : False :  True : Binary
        ('c', 'a') :     0 :  None :     1 : False :  True : Binary
        ('c', 'b') :     0 :  None :     1 : False :  True : Binary
        ('c', 'c') :     0 :  None :     1 : False :  True : Binary
        ('c', 'd') :     0 :  None :     1 : False :  True : Binary
        ('c', 'e') :     0 :  None :     1 : False :  True : Binary
        ('c', 'j') :     0 :  None :     1 : False :  True : Binary
        ('d', 'a') :     0 :  None :     1 : False :  True : Binary
        ('d', 'b') :     0 :  None :     1 : False :  True : Binary
        ('d', 'c') :     0 :  None :     1 : False :  True : Binary
        ('d', 'd') :     0 :  None :     1 : False :  True : Binary
        ('d', 'e') :     0 :  None :     1 : False :  True : Binary
        ('d', 'j') :     0 :  None :     1 : False :  True : Binary
        ('e', 'a') :     0 :  None :     1 : False :  True : Binary
        ('e', 'b') :     0 :  None :     1 : False :  True : Binary
        ('e', 'c') :     0 :  None :     1 : False :  True : Binary
        ('e', 'd') :     0 :  None :     1 : False :  True : Binary
        ('e', 'e') :     0 :  None :     1 : False :  True : Binary
        ('e', 'j') :     0 :  None :     1 : False :  True : Binary
        ('j', 'a') :     0 :  None :     1 : False :  True : Binary
        ('j', 'b') :     0 :  None :     1 : False :  True : Binary
        ('j', 'c') :     0 :  None :     1 : False :  True : Binary
        ('j', 'd') :     0 :  None :     1 : False :  True : Binary
        ('j', 'e') :     0 :  None :     1 : False :  True : Binary
        ('j', 'j') :     0 :  None :     1 : False :  True : Binary

1 Objective Declarations
    OBJ : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : minimize : 10*X[a,b] + 5*X[c,d] + 44*X[e,j] + 2*X[a,j] + 12*X[j,e]

1 Constraint Declarations
    c1 : Size=1, Index=None, Active=True
        Key  : Lower : Body                                       : Upper : Active
        None :   3.0 : X[a,b] + X[c,d] + X[e,j] + X[a,j] + X[j,e] :  +Inf :   True

10 Declarations: NODES ARCS_domain ARCS cost_index cost bad_X_index bad_X X c1 OBJ
('a', 'b') 1.0
('c', 'd') 1.0
('e', 'j') 0.0
('a', 'j') 1.0
('j', 'e') 0.0
[Finished in 4.3s]

关于在约束中建立索引的第二点的第二个答案...

这显示了您询问的概念的一些变体。请注意,因为我为每个可能的城市-年份组合构建了参数,所以我需要为不在我的稀疏数据中的那些提供默认值。

import pyomo.environ as pyo

mdl = pyo.ConcreteModel()

demand_data = { ('LA', 1999):       500,
                ('LA', 2001):       700,
                ('NY', 2001):       600,
                ('Pheonix', 2000):  300,
                ('Pheonix', 2001):  400 }

my_favorites = {'LA'}  # a subset for example use

# Sets
mdl.CITIES = pyo.Set(initialize= {k[0] for k in demand_data.keys()})
mdl.YEARS =  pyo.Set(initialize= {k[1] for k in demand_data.keys()})

# Params
mdl.demand = pyo.Param(mdl.CITIES, mdl.YEARS, initialize=demand_data, default=0)
mdl.cost =   pyo.Param(mdl.YEARS, initialize={1999: 5, 2000: 10, 2001: 15}, default=0)

# Variable for supply
mdl.supply = pyo.Var(mdl.CITIES, mdl.YEARS, domain=pyo.NonNegativeReals)

#### Constraints ####

#ensure 1/2 of demand in each city-year pair
def half_demand(self, city, year):   
    return mdl.supply[city, year] >= 0.5 * mdl.demand[city, year]
mdl.c1 = pyo.Constraint(mdl.CITIES, mdl.YEARS, rule=half_demand)

# ensure favorite cities get all demand every year
def all_demand(self, city, year):
    return mdl.supply[city, year] >= mdl.demand[city, year]
mdl.c2 = pyo.Constraint(my_favorites, mdl.YEARS, rule=all_demand)

# ensure all demand is eventually met over all years
def eventually_met(self, city):
    return  sum(mdl.supply[city, year] for year in mdl.YEARS) >= \
            sum(mdl.demand[city, year] for year in mdl.YEARS)
mdl.c3 = pyo.Constraint(mdl.CITIES, rule=eventually_met)

#### OBJECTIVE ####
def objective(self):
    return sum(mdl.supply[city, year] * mdl.cost[year]
                for year in mdl.YEARS
                for city in mdl.CITIES)
mdl.OBJ = pyo.Objective(rule=objective)


# solve and show results
solver = pyo.SolverFactory('cbc')
results = solver.solve(mdl)
mdl.supply.display()

输出:

supply : Size=9, Index=supply_index
    Key               : Lower : Value : Upper : Fixed : Stale : Domain
         ('LA', 1999) :     0 : 500.0 :  None : False : False : NonNegativeReals
         ('LA', 2000) :     0 :   0.0 :  None : False : False : NonNegativeReals
         ('LA', 2001) :     0 : 700.0 :  None : False : False : NonNegativeReals
         ('NY', 1999) :     0 : 300.0 :  None : False : False : NonNegativeReals
         ('NY', 2000) :     0 :   0.0 :  None : False : False : NonNegativeReals
         ('NY', 2001) :     0 : 300.0 :  None : False : False : NonNegativeReals
    ('Pheonix', 1999) :     0 : 350.0 :  None : False : False : NonNegativeReals
    ('Pheonix', 2000) :     0 : 150.0 :  None : False : False : NonNegativeReals
    ('Pheonix', 2001) :     0 : 200.0 :  None : False : False : NonNegativeReals
[Finished in 3.1s]

我想我已经解决了,请参考下面的例子

##First, create the set of tuples needed for filtering


#Op_Machine: set of (operation, machine) tuples created to avoid redundancy in decision variable declaration
Op_Machine=list()
for machine_id, op_proctime in Machine_Op_Time.items():
    for op in op_proctime.keys():
        print(Op_Machine)
        print((op,machine_id))
        Op_Machine.append((op,machine_id))
        print(Op_Machine)

##Next, invoke the rule using the if statement to filter across all possible indices accepting those combinations that are aligned with the tuples within the set
##Use Constraint.Skip to Skip creating constraints that do not belong to the set of tuples


def F1_rule(model,i,k):
    if (i,k) in Op_Machine:
        ##print(i,k)
        return model.Cmax>=model.completion_time[i,k]
    else:
        return Constraint.Skip            
            
#model.makespan= Constraint(model.op_set, model.mach_set, rule=Cmax_rule)
model.F1= Constraint(Operation_Set, Machine_Set, rule=F1_rule)

请注意,集合 Operation_Set、Machine_Set 用作通用集合,因为它包含操作和机器的所有组合。因此语句 model.F1= Constraint(Operation_Set, Machine_Set, rule=F1_rule) 可以被认为是迭代所有组合的 for 循环,而 def 函数中的 if 语句充当过滤器以生成所需的约束。