Search code examples
pythonconditional-statementspyomo

Conditional summing in Pyomo


I'm trying to reduce the number of binary variables I need in a Big-M reformulation from a linear number to a logarithmic one. I have the following equation:

image link (for i = 1, ..., m)

Here, hi are known vectors where each element has a binary value. zj are unknown binary variables.

So, if hi = z the constraint is enforced.

What I was wondering is, is there a way to code these conditional sums in Pyomo?

I know that in Mosel we can use the '|' operator to add conditions but was unable to find something similar for Pyomo. Any suggestions are greatly appreciated.


Solution

  • There are probably a couple ways to do this, but the below gets the job done w/ no additional variables and just a little "examination" of h inside of the constraint construction. Bringing h "inside" the model is optional, but I think good practice.

    Code

    # big M with vector
    
    # making a big-M constraint from a vector matching operation
    
    import pyomo.environ as pyo
    
    big_m = 11
    
    h = [[1, 0, 1], [0, 0, 1]]
    
    m = pyo.ConcreteModel()
    
    m.I = pyo.Set(initialize=[0, 1])
    m.J = pyo.Set(initialize=[0, 1, 2])
    
    m.z = pyo.Var(m.J, domain=pyo.Binary)
    m.x = pyo.Var()
    
    m.h = pyo.Param(m.I, domain=pyo.Any, initialize=h)
    m.A = pyo.Param(m.I, initialize=[2, 3])
    m.b = pyo.Param(m.I, initialize=[4, 5])
    
    @m.Constraint(m.I)
    def big_m(m, i):
        # make a couple subsets of J on-the-fly
        ones = {idx for idx, value in enumerate(m.h[i]) if value==1}
        zeros = m.J.difference(ones)
        
        # build the "enforcement" summation
        enforce = len(ones) - sum(m.z[j] for j in ones) + sum(m.z[j] for j in zeros)
    
        # use it
        return m.A[i] * m.x <= m.b[i] + enforce * big_m
        
    m.big_m.pprint()
    
    # set some values to see the constraint evaluated... (simulate solving)
    m.z.set_values({0:1, 1:0, 2:1})
    m.x.set_value(1.5)
    
    m.big_m.display()
    

    Output:

    big_m : Size=2, Index=I, Active=True
        Key : Lower : Body                                      : Upper : Active
          0 :  -Inf : 2*x - (4 + (2 - (z[0] + z[2]) + z[1])*11) :   0.0 :   True
          1 :  -Inf :   3*x - (5 + (1 - z[2] + z[0] + z[1])*11) :   0.0 :   True
    big_m : Size=2
        Key : Lower : Body  : Upper
          0 :  None :  -1.0 :   0.0
          1 :  None : -11.5 :   0.0