Pyomo create a constraint based on an indexed set

Question:

I have a set of constraint that is given as follows:

for all n in N, for all i in l(n): ...

N is a set of nodes, in a concrete model in Pyomo:

model.Nodes = pyo.Set(initialize = range(len(nodes)))

Each node has a different length. I need a RangeSet for each node running from 0 to its respective range. This, I solved as follows:

longest_node = max(map(len, nodes))
model.Lengths = pyo.Set(initialize = list(range(longest_node)))
model.NodeRangeSet = pyo.Set(model.nodes, within = model.Lengths, initialize = lambda model, node: list(range(len(nodes[node]))))

Which gives for e.g. 3 nodes of length 1, 3 and 2:

NodeRangeSet : Size=3, Index=Nodes, Ordered=Insertion
    Key : Dimen : Domain   : Size : Members
     0  :     1 : Lengths  :    1 : {0}
     1  :     1 : Lengths  :    3 : {0, 1, 2}
     2  :     1 : Lengths  :    2 : {0, 1}

But what I am seemingly not able to is to create a constraint as desired e.g.

model.Constr = model.Constraint(model.Nodes, model.NodeRangeSet[model.Nodes]
    rule = lambda model, node, offset: some_condition)

How do I replace this placeholder model.NodeRangeSet[model.Nodes] correctly? The number of nodes, as well as their lengths depends solely on the given input parameter. So each node in the constraint has its own RangeSet. Is there a completely different approach I can/have to take?

Asked By: baxbear

||

Answers:

Here is an example that I think covers the topic well along with a few variants. The basic answer to your question is that if you want to use your indexed set within the constraint, then just put it in the constraint body (or rule) and pass in the index (node) to get what you want. If you want to "flatten it" and produce a constraint for all combinations (which would be weird because the values in the indexed set are tied to the value of the main set), you could make a product of the 2 sets with a set comprehension or some such.

Anyhow, the below shows how I would set up the key sets for a similar problem, one with nodes, another that is indexed by node, which is the connections, and perhaps, if needed, a flat set of all the legal combinations.

Also shown are 3 styles of constraints using these, depending on what "the math" is for the summations and what the "for each" statement is.

Code

import pyomo.environ as pyo

# some data for nodes & connections for a directed graph
graph = {   1: [2, 3, 4],
            2: [3,],
            3: [1, 4],
            4: [2, 3]
        }
connections = [(s, t) for s in graph.keys() for t in graph[s]]

m = pyo.ConcreteModel('graph')

# SETS
m.N = pyo.Set(initialize=graph.keys())                      # the set of all nodes
m.C = pyo.Set(m.N, within=m.N, initialize=graph)            # set of all connections, indexed by node
m.NN = pyo.Set(within=m.N * m.N, initialize=connections)    # a flat set of all "legal", directed arcs

# VARS
# initializing with only the legal connections keeps this small (it is only 8 members vs. 16)
m.traffic = pyo.Var(m.NN, domain=pyo.NonNegativeReals)

# CONSTRAINTS

# an outbound "traffic" constraint *for each node*
def traffic(m, node):
    return sum(m.traffic[node, other] for other in m.C[node]) <= 4
m.C1 = pyo.Constraint(m.N, rule=traffic)

# a traffic restriction *for each arc*
def arc_traffic(m, node, other):
    return m.traffic[node, other] <= 3
m.C2 = pyo.Constraint(m.NN, rule=arc_traffic)

# flow balance at each node *for each node* using an internally generated set/list
def flow_balance(m, node):
    # we can make an "on the fly" list from model parts, as needed...
    inbound_arcs = [s for (s, other) in m.NN if other==node]
    return sum(m.traffic[s, node] for s in inbound_arcs) == sum(m.traffic[node, t] for t in m.C[node])
m.C3 = pyo.Constraint(m.N, rule=flow_balance)

m.pprint()

Yields:

4 Set Declarations
    C : Size=4, Index=N, Ordered=Insertion
        Key : Dimen : Domain : Size : Members
          1 :     1 :      N :    3 : {2, 3, 4}
          2 :     1 :      N :    1 : {3,}
          3 :     1 :      N :    2 : {1, 4}
          4 :     1 :      N :    2 : {2, 3}
    N : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    4 : {1, 2, 3, 4}
    NN : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain    : Size : Members
        None :     2 : NN_domain :    8 : {(1, 2), (1, 3), (1, 4), (2, 3), (3, 1), (3, 4), (4, 2), (4, 3)}
    NN_domain : Size=1, Index=None, Ordered=True
        Key  : Dimen : Domain : Size : Members
        None :     2 :    N*N :   16 : {(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4), (3, 1), (3, 2), (3, 3), (3, 4), (4, 1), (4, 2), (4, 3), (4, 4)}

1 Var Declarations
    traffic : Size=8, Index=NN
        Key    : Lower : Value : Upper : Fixed : Stale : Domain
        (1, 2) :     0 :  None :  None : False :  True : NonNegativeReals
        (1, 3) :     0 :  None :  None : False :  True : NonNegativeReals
        (1, 4) :     0 :  None :  None : False :  True : NonNegativeReals
        (2, 3) :     0 :  None :  None : False :  True : NonNegativeReals
        (3, 1) :     0 :  None :  None : False :  True : NonNegativeReals
        (3, 4) :     0 :  None :  None : False :  True : NonNegativeReals
        (4, 2) :     0 :  None :  None : False :  True : NonNegativeReals
        (4, 3) :     0 :  None :  None : False :  True : NonNegativeReals

3 Constraint Declarations
    C1 : Size=4, Index=N, Active=True
        Key : Lower : Body                                       : Upper : Active
          1 :  -Inf : traffic[1,2] + traffic[1,3] + traffic[1,4] :   4.0 :   True
          2 :  -Inf :                               traffic[2,3] :   4.0 :   True
          3 :  -Inf :                traffic[3,1] + traffic[3,4] :   4.0 :   True
          4 :  -Inf :                traffic[4,2] + traffic[4,3] :   4.0 :   True
    C2 : Size=8, Index=NN, Active=True
        Key    : Lower : Body         : Upper : Active
        (1, 2) :  -Inf : traffic[1,2] :   3.0 :   True
        (1, 3) :  -Inf : traffic[1,3] :   3.0 :   True
        (1, 4) :  -Inf : traffic[1,4] :   3.0 :   True
        (2, 3) :  -Inf : traffic[2,3] :   3.0 :   True
        (3, 1) :  -Inf : traffic[3,1] :   3.0 :   True
        (3, 4) :  -Inf : traffic[3,4] :   3.0 :   True
        (4, 2) :  -Inf : traffic[4,2] :   3.0 :   True
        (4, 3) :  -Inf : traffic[4,3] :   3.0 :   True
    C3 : Size=4, Index=N, Active=True
        Key : Lower : Body                                                                       : Upper : Active
          1 :   0.0 :                traffic[3,1] - (traffic[1,2] + traffic[1,3] + traffic[1,4]) :   0.0 :   True
          2 :   0.0 :                                 traffic[1,2] + traffic[4,2] - traffic[2,3] :   0.0 :   True
          3 :   0.0 : traffic[1,3] + traffic[2,3] + traffic[4,3] - (traffic[3,1] + traffic[3,4]) :   0.0 :   True
          4 :   0.0 :                traffic[1,4] + traffic[3,4] - (traffic[4,2] + traffic[4,3]) :   0.0 :   True

8 Declarations: N C NN_domain NN traffic C1 C2 C3
Answered By: AirSquid
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.