Search code examples
pyomo

Having a constraint over a set that is indexed by another rangeset


I'm new-ish to pyomo, and I was curious about how I could start to implement the following constraint in pyomo.

enter image description here

My constraint is over a set that is also indexed over a range-set k. There is no assumption that the amount of elements in the set for a specific k value is uniform across all k. I couldn't find a similar example to this in the documentation, that makes me question if this is even doable in pyomo.

Any suggestions and/or directions to sample codes would be greatly appreciated. Or a suggestion of how to repose this constraint so it would be "pyomo friendly" would be awesome too!

EDIT: As asked by a user - d is a known number that is unique to the given tuple in A_k for a given k value. To be precise for a given k, A_k can be defined as {(p_n_1,q_n_1,d_n_1), ....., (p_n_k,q_n_k,q_n_k)}. The given k value will determine the size of A_k.


Solution

  • I made some assumptions in making this as the specification is a bit incomplete, but I think you are looking to do something like this. Of course this gets huge quickly because you have a 6-tuple indexed variable.

    Edit...

    Looking at this a second time, I think I made a wrong assumption in the first option below that y was indexed by k. If you intend to just gather the tuples associated with k and k + 1 then the 2nd option is likely what you want.

    Option 1:

    # constraint over members of indexed set
    
    import pyomo.environ as pyo
    
    m = pyo.ConcreteModel()
    
    m.T = pyo.Set(initialize=[1, 2, 3])
    m.K = pyo.Set(initialize=[1, 2, 3])
    m.I = pyo.Set(initialize=[4, 5, 6])
    m.J = pyo.Set(initialize=[7, 8, 9])
    m.P = pyo.Set(initialize=[10, 13, 22])
    m.Q = pyo.Set(initialize=[11, 14, 24])
    m.D = pyo.Set(initialize=[12, 15, 19])
    m.A = pyo.Set(m.K, within=m.P*m.Q*m.D, initialize={ 1: [(10, 11, 12), (13, 14, 15)], 
                                                        2: [(22, 24, 19),],
                                                        3: [(10, 11, 15),]})
    
    m.y = pyo.Var(m.K, m.P, m.Q, m.I, m.J, m.T, domain=pyo.Binary)
    
    # flatten out the nested reference to A_k...
    m.A_k_flat = pyo.Set(dimen=4, initialize=[(k, p, q, d) for k in m.K for (p, q, d) in m.A[k] ])
    
    @m.Constraint(m.A_k_flat)
    def nested_constraint(m, k, p, q, d):
        if k == m.K.last():
            return pyo.Constraint.Skip
        sum_1 = sum(m.y[k, p, q, i, j, t]*t + 1 for i in m.I for j in m.J for t in m.T)
        sum_2 = sum(m.y[m.K.next(k), p, q, i, j, t]*t for i in m.I for j in m.J for t in m.T)
        return sum_1 <= sum_2
    
    m.pprint()
    

    Option 2:

    # constraint over members of indexed set
    
    import pyomo.environ as pyo
    
    m = pyo.ConcreteModel()
    
    m.T = pyo.Set(initialize=[1, 2, 3])
    m.K = pyo.Set(initialize=[1, 2, 3])
    m.I = pyo.Set(initialize=[4, 5, 6])
    m.J = pyo.Set(initialize=[7, 8, 9])
    m.P = pyo.Set(initialize=[10, 13, 22])
    m.Q = pyo.Set(initialize=[11, 14, 24])
    m.D = pyo.Set(initialize=[12, 15, 19])
    m.A = pyo.Set(m.K, within=m.P*m.Q*m.D, initialize={ 1: [(10, 11, 12), (13, 14, 15)], 
                                                        2: [(22, 24, 19),],
                                                        3: [(10, 11, 15),]})
    
    m.y = pyo.Var(m.P, m.Q, m.I, m.J, m.T, domain=pyo.Binary)
    
    # flatten out the nested reference to A_k... and flatten again for k, k+1 capture
    m.A_k_pairs_flat = pyo.Set(dimen=6, initialize=[(p, q, d, pp, qq, dd) 
        for k in m.K-{m.K.last(), }                 # subset of m.K without last element
        for (p, q, d) in m.A[k]                     # the tuple for k
        for (pp, qq, dd) in m.A[m.K.next(k)] ])     # the tuple for k+1
    
    @m.Constraint(m.A_k_pairs_flat)
    def nested_constraint(m, p, q, d, pp, qq, dd):
        sum_1 = sum(m.y[p, q, i, j, t]*t + 1 for i in m.I for j in m.J for t in m.T)
        sum_2 = sum(m.y[pp, qq, i, j, t]*t   for i in m.I for j in m.J for t in m.T)
        return sum_1 <= sum_2
    
    m.A_k_pairs_flat.pprint()
    

    Yields:

    A_k_pairs_flat : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     6 :    Any :    3 : {(10, 11, 12, 22, 24, 19), (13, 14, 15, 22, 24, 19), (22, 24, 19, 10, 11, 15)}
    [Finished in 361ms]