I would like to iterate through a set in a constraint. I have built an abstract model. Let's assume the following dictionaries for the instance:
dict_Nodes = {None: ['N1', 'N2']}
dict_Nodes_ArcsOut = {'N1': ['N1N4', 'N1N3'], 'N2': ['N2N5', 'N2N7']}
dict_Time = {None: [0, 1, 2]}
dict_Arcs = {None: ['N1N4', 'N1N3', 'N2N5', 'N2N7']}
However, as I am constructing an abstract model the data should not really matter. I want to say that the variable in node N1 is the same value as in the arcs N1N4 and N1N3. For the abstract model I created several sets:
from pyomo.environ import AbstractModel, Param, minimize, Var, Constraint,\
SolverFactory, Set, Objective, NonNegativeReals, Reals, Binary, ConstraintList
model = AbstractModel()
model.Set_Nodes = Set()
model.Set_Arcs = Set()
model.Set_Time = Set()
model.Set_Nodes_ArcsOut = Set(model.Set_Nodes)
model.Var_Arc = Var(model.Set_Arcs, model.Set_Time, within=NonNegativeReals)
model.Var_Node = Var(model.Set_Nodes, model.Set_Time, within=NonNegativeReals)
Looking at the data I would like to say that:
Var_Arc['N1N4'] = Var_Node['N1']
Var_Arc['N1N3'] = Var_Node['N1']
Var_Arc['N2N5'] = Var_Node['N2']
Var_Arc['N2N7'] = Var_Node['N2']
To implement the constraint I tried the following two options:
def Arc_rule(model, node, t):
for arc in model.Set_Nodes_ArcsOut[node]:
return model.Var_Arc[arc, t] == model.Var_Node[node, t]
model.ArcInTemp_rule = Constraint(model.Set_Nodes, model.Set_Time, rule=ArcInTemp_rule)
This option only takes the first position of the list. This is probably caused by the return which stops the iteration. Option 2:
def _init(model):
for t in model.Set_Time:
for node in model.Set_Nodes_ArcsOut:
yield model.Var_Arcs_TempIn[model.Set_Nodes_ArcsOut[node], t] == model.Var_Nodes_TempMix[node, t]
model.init_conditions = ConstraintList(rule=_init)
This does not work and I get the following error: TypeError: unhashable type: '_InsertionOrderSetData'. I do not understand because I can do this operation if I do a summation. However, iteration seems to be impossible with an abstract model.
I rephrased the problem and illustrated it based on the pyomo example. The tryRule is what I needed:
import pyomo.environ as pyo
model = pyo.AbstractModel()
model.Nodes = pyo.Set()
model.Arcs = pyo.Set(dimen=2)
def NodesOut_init(m, node):
for i, j in m.Arcs:
if i == node:
yield j
model.NodesOut = pyo.Set(model.Nodes, initialize=NodesOut_init)
def NodesIn_init(m, node):
for i, j in m.Arcs:
if j == node:
yield i
model.NodesIn = pyo.Set(model.Nodes, initialize=NodesIn_init)
model.Flow = pyo.Var(model.Arcs, domain=pyo.NonNegativeReals)
model.NodeFlow = pyo.Var(model.Nodes, domain=pyo.NonNegativeReals)
model.FlowCost = pyo.Param(model.Arcs)
model.Demand = pyo.Param(model.Nodes)
model.Supply = pyo.Param(model.Nodes)
def Obj_rule(m):
return pyo.summation(m.FlowCost, m.Flow)
model.Obj = pyo.Objective(rule=Obj_rule, sense=pyo.minimize)
def tryRule(m, i, j):
return m.Flow[i, j] == m.NodeFlow[i]
model.tryRule = pyo.Constraint(model.Arcs, rule=tryRule)
def FlowBalance_rule(m, node):
return m.Supply[node] \
+ sum(m.Flow[i, node] for i in m.NodesIn[node]) \
- m.Demand[node] \
- sum(m.Flow[node, j] for j in m.NodesOut[node]) \
== 0
model.FlowBalance = pyo.Constraint(model.Nodes, rule=FlowBalance_rule)
dict_data = {
None: {
'Nodes': {None: ['CityA', 'CityB', 'CityC']},
'Arcs': {None: [('CityA', 'CityB'), ('CityA', 'CityC'), ('CityC', 'CityB')]},
'FlowCost': {
('CityA', 'CityB'): 1.4,
('CityA', 'CityC'): 2.7,
('CityC', 'CityB'): 1.6,
},
'Demand': {
'CityA': 0,
'CityB': 1,
'CityC': 1,
},
'Supply': {
'CityA': 2,
'CityB': 0,
'CityC': 0,
}
}
}
# create instance
instance = model.create_instance(dict_data)
instance.pprint()
The result for the rule is:
tryRule : Size=3, Index=Arcs, Active=True
Key : Lower : Body : Upper : Active
('CityA', 'CityB') : 0.0 : Flow[CityA,CityB] - NodeFlow[CityA] : 0.0 : True
('CityA', 'CityC') : 0.0 : Flow[CityA,CityC] - NodeFlow[CityA] : 0.0 : True
('CityC', 'CityB') : 0.0 : Flow[CityC,CityB] - NodeFlow[CityC] : 0.0 : True