Search code examples
pyomo

Implementing a 'such that' constraint in pyomo


I am new to pyomo and would like to implement the following AMPL constraint:

param Tele=48;

set T=1..Tele; #set of epochs
set A ordered; #set of appliances
 
var active{A,T}, binary;    
param d{A}>=0; 


subject to max_duration{a in A, t in T, ti in T:t>ti}:
active[a,t]*t-active[a,ti]*ti<=d[a]*(1- Tele*(active[a,t]+active[a,ti]-2)); 

I am confused about how to implement the 'such that' condition t in T, ti in T:t>ti of the constraint. Would it involve defining a new set Tcomp along these lines (but unsure how to handle the else case here):

def tlarge_rule(m, t1, t2):
    if t1 > t2:
        return (t1,t2)    
model.Tcomp = Set(model.T, model.T, initialize=tlarge_rule)

Another query I have is with regards to the most suitable (open-source) pyomo solver for large-scale MILP optimisation (as I not able to use Gurobi in my project). Can GLPK or CBC handle such optimisations (or is there another open-source solver that is more suitable)?

Many thanks for your help.


Solution

  • Welcome to the site.

    The "such that" sub-setting can be done pretty easily in pyomo. You cannot "bury" an if statement in your model because those values are unknown at the time the model is built.... Not sure how AMPL handles such things. The trick is to use core python to construct arbitrarily complicated subsets and use those to construct your model. Fortunately, python's list/set comprehensions are immensely powerful and make this pretty straightforward. Below is a snippet that shows this. 2 methods... Well, 2nd is just an enhancement. Note that you don't necessarily need to bring the subset into your model, but sometimes it is good for consistency and T/S, and using the "within" keyword in Method 2 allows error checking (buffoonery prevention...). Note that the 2 constraints constructed in the model are equivalent/redundant... Just shown for consistency. [Aside: active is a keyword in pyomo, so I used act.]

    Edit: There is probably a legit 3rd method here to by using a for-loop over the desired indices (with the inner loop for t_prime controlled by the loop over t with individual calls to make constraint for each combo, but that seems much clumsier than making your own subset... you would be just replicating the set comprehension. Seems inferior to what is shown below...

    Regarding solvers, GLPK and CBC are both free and great. Installs can be a little challenging depending on type of machine. On a mac with homebrew, it is a snap.

    import pyomo.environ as pyo
    
    tele = 4
    appliances = ['fridge', 'tv']
    
    
    m = pyo.ConcreteModel('subset_maker')
    
    ### SETS
    
    m.T = pyo.Set(initialize=range(1, tele + 1))
    m.A = pyo.Set(initialize=appliances)
    
    ### VARIABLES
    m.act = pyo.Var(m.A, m.T, domain=pyo.Binary)
    
    ### Method 1:  Make subset on fly...
    c1_set = {(a, t, t_prime) for a in m.A for t in m.T for t_prime in m.T if t_prime < t}
    def C1(model, a, t, t_prime):
        return model.act[a, t] * t_prime <= 10  # some nonsense...
    m.C1 = pyo.Constraint(c1_set, rule=C1)
    
    ### Method 2:  Bring that subset into the model (good for T/S and consistency...)
    m.C1_SET = pyo.Set(within=m.A*m.T*m.T, initialize=c1_set)
    m.C1_method_2 = pyo.Constraint(m.C1_SET, rule=C1)
    
    m.pprint()
    

    Yields:

    7 Set Declarations
        A : Size=1, Index=None, Ordered=Insertion
            Key  : Dimen : Domain : Size : Members
            None :     1 :    Any :    2 : {'fridge', 'tv'}
        C1_SET : Size=1, Index=None, Ordered=Insertion
            Key  : Dimen : Domain        : Size : Members
            None :     3 : C1_SET_domain :   12 : {('tv', 4, 1), ('fridge', 4, 3), ('fridge', 3, 2), ('fridge', 4, 2), ('tv', 2, 1), ('tv', 4, 3), ('tv', 3, 2), ('tv', 4, 2), ('fridge', 3, 1), ('fridge', 4, 1), ('fridge', 2, 1), ('tv', 3, 1)}
        C1_SET_domain : Size=1, Index=None, Ordered=True
            Key  : Dimen : Domain                  : Size : Members
            None :     3 : C1_SET_domain_index_0*T :   32 : {('fridge', 1, 1), ('fridge', 1, 2), ('fridge', 1, 3), ('fridge', 1, 4), ('fridge', 2, 1), ('fridge', 2, 2), ('fridge', 2, 3), ('fridge', 2, 4), ('fridge', 3, 1), ('fridge', 3, 2), ('fridge', 3, 3), ('fridge', 3, 4), ('fridge', 4, 1), ('fridge', 4, 2), ('fridge', 4, 3), ('fridge', 4, 4), ('tv', 1, 1), ('tv', 1, 2), ('tv', 1, 3), ('tv', 1, 4), ('tv', 2, 1), ('tv', 2, 2), ('tv', 2, 3), ('tv', 2, 4), ('tv', 3, 1), ('tv', 3, 2), ('tv', 3, 3), ('tv', 3, 4), ('tv', 4, 1), ('tv', 4, 2), ('tv', 4, 3), ('tv', 4, 4)}
        C1_SET_domain_index_0 : Size=1, Index=None, Ordered=True
            Key  : Dimen : Domain : Size : Members
            None :     2 :    A*T :    8 : {('fridge', 1), ('fridge', 2), ('fridge', 3), ('fridge', 4), ('tv', 1), ('tv', 2), ('tv', 3), ('tv', 4)}
        C1_index : Size=1, Index=None, Ordered=False
            Key  : Dimen : Domain : Size : Members
            None :     3 :    Any :   12 : {('fridge', 2, 1), ('fridge', 3, 1), ('fridge', 3, 2), ('fridge', 4, 1), ('fridge', 4, 2), ('fridge', 4, 3), ('tv', 2, 1), ('tv', 3, 1), ('tv', 3, 2), ('tv', 4, 1), ('tv', 4, 2), ('tv', 4, 3)}
        T : Size=1, Index=None, Ordered=Insertion
            Key  : Dimen : Domain : Size : Members
            None :     1 :    Any :    4 : {1, 2, 3, 4}
        act_index : Size=1, Index=None, Ordered=True
            Key  : Dimen : Domain : Size : Members
            None :     2 :    A*T :    8 : {('fridge', 1), ('fridge', 2), ('fridge', 3), ('fridge', 4), ('tv', 1), ('tv', 2), ('tv', 3), ('tv', 4)}
    
    1 Var Declarations
        act : Size=8, Index=act_index
            Key           : Lower : Value : Upper : Fixed : Stale : Domain
            ('fridge', 1) :     0 :  None :     1 : False :  True : Binary
            ('fridge', 2) :     0 :  None :     1 : False :  True : Binary
            ('fridge', 3) :     0 :  None :     1 : False :  True : Binary
            ('fridge', 4) :     0 :  None :     1 : False :  True : Binary
                ('tv', 1) :     0 :  None :     1 : False :  True : Binary
                ('tv', 2) :     0 :  None :     1 : False :  True : Binary
                ('tv', 3) :     0 :  None :     1 : False :  True : Binary
                ('tv', 4) :     0 :  None :     1 : False :  True : Binary
    
    2 Constraint Declarations
        C1 : Size=12, Index=C1_index, Active=True
            Key              : Lower : Body            : Upper : Active
            ('fridge', 2, 1) :  -Inf :   act[fridge,2] :  10.0 :   True
            ('fridge', 3, 1) :  -Inf :   act[fridge,3] :  10.0 :   True
            ('fridge', 3, 2) :  -Inf : 2*act[fridge,3] :  10.0 :   True
            ('fridge', 4, 1) :  -Inf :   act[fridge,4] :  10.0 :   True
            ('fridge', 4, 2) :  -Inf : 2*act[fridge,4] :  10.0 :   True
            ('fridge', 4, 3) :  -Inf : 3*act[fridge,4] :  10.0 :   True
                ('tv', 2, 1) :  -Inf :       act[tv,2] :  10.0 :   True
                ('tv', 3, 1) :  -Inf :       act[tv,3] :  10.0 :   True
                ('tv', 3, 2) :  -Inf :     2*act[tv,3] :  10.0 :   True
                ('tv', 4, 1) :  -Inf :       act[tv,4] :  10.0 :   True
                ('tv', 4, 2) :  -Inf :     2*act[tv,4] :  10.0 :   True
                ('tv', 4, 3) :  -Inf :     3*act[tv,4] :  10.0 :   True
        C1_method_2 : Size=12, Index=C1_SET, Active=True
            Key              : Lower : Body            : Upper : Active
            ('fridge', 2, 1) :  -Inf :   act[fridge,2] :  10.0 :   True
            ('fridge', 3, 1) :  -Inf :   act[fridge,3] :  10.0 :   True
            ('fridge', 3, 2) :  -Inf : 2*act[fridge,3] :  10.0 :   True
            ('fridge', 4, 1) :  -Inf :   act[fridge,4] :  10.0 :   True
            ('fridge', 4, 2) :  -Inf : 2*act[fridge,4] :  10.0 :   True
            ('fridge', 4, 3) :  -Inf : 3*act[fridge,4] :  10.0 :   True
                ('tv', 2, 1) :  -Inf :       act[tv,2] :  10.0 :   True
                ('tv', 3, 1) :  -Inf :       act[tv,3] :  10.0 :   True
                ('tv', 3, 2) :  -Inf :     2*act[tv,3] :  10.0 :   True
                ('tv', 4, 1) :  -Inf :       act[tv,4] :  10.0 :   True
                ('tv', 4, 2) :  -Inf :     2*act[tv,4] :  10.0 :   True
                ('tv', 4, 3) :  -Inf :     3*act[tv,4] :  10.0 :   True
    
    10 Declarations: T A act_index act C1_index C1 C1_SET_domain_index_0 C1_SET_domain C1_SET C1_method_2
    [Finished in 413ms]