Reputation: 890
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?
Upvotes: 1
Views: 1229
Reputation: 11883
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.
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()
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
Upvotes: 2