Search code examples
pythonconstraintsabstractpyomo

How to iterate through a set in a constraint?


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.


Solution

  • 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