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.
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.
# 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()
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