使用 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 语句充当过滤器以生成所需的约束。
我目前正在尝试解决一个不完全的网络问题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 语句充当过滤器以生成所需的约束。