Search code examples
pythonlinear-programminggurobiinteger-programming

Adding binary variable in Gurobi


So I want to add a binary variable z where z[i, j] = 1 when the distance between i and j are less than or equal to 150 and z[i, j] = 0 otherwise. I have a list c where each c[i][j] represents the distance between i and j. I certainly can't set up z as an ordinary binary variable below:

y = m.addVars(I, J, vtype=GRB.BINARY, name="assign")

And I want to add constraints:

# One day mailing
m.addConstrs(
    z[i,j] <= y[i,j] for i in I for j in J,
    "Serve")

# Coverage Constraint
m.addConstr(
   quicksum(h[i] * z[i, j] for i in I for j in J) <= 
        0.9 * quicksum(h[i] * y[i, j] for i in I for j in J),
        "Cover")

where h is a list with integers. How do I set up z?


Solution

  • First you need to add z as a binary variable:

    z = m.addVars(I, J, vtype=GRB.BINARY, name="z")
    

    Then you need constraints to ensure that z[i, j] = 1 if and only if c[i, j] <= 150. One way to do this is by using indicator constraints:

    z = 1 -> c <= 150
    z = 0 -> c >= 150
    

    This is equivalent to

    c > 150 -> z = 0
    c < 150 -> z = 1
    

    You add these as follows:

    m.addConstrs((z[i, j] == 1) >> (c[i][j] <= 150) for i in I for j in J)
    m.addConstrs((z[i, j] == 0) >> (c[i][j] >= 150) for i in I for j in J)
    

    You can also model this yourself explicitly: If you have upper and lower bounds M and m on the value of c[i][j] - 150 (i.e., M >= c[i][j] - 150 >= m for all i, j), you can use the following constraints:

    M * (1-z) >= c - 150
    m * z <= c - 150
    

    If c > 150, the right-hand-sides of both inequalities will be positive. The first one then forces 1 - z = 1 and hence z = 0. The second inequality will trivially be satisfied.

    If c < 150, the right-hand-sides are negative. The first inequality becomes trivial while the second one forces z = 1.

    For M the maximum entry in c will do, for m you can choose -150 if all c[i][j] are non-negative.

    You add these constraints as follows:

    m.addConstrs( M * (1 - z[i, j]) >= c[i][j] - 150 for i in I for j in J )
    m.addConstrs( m * z[i,j] <= c[i][j] - 150 for i in I for j in J )
    

    Note that I neglected the case where c = 150. This is because for floating point numbers equalities are always only considered to be satisfied within tolerances and hence there is no easy way to distinguish between strict and non-strict inequalities. You can approximate this with an epsilon, e.g.:

    z = 0 -> c >= 150 + epsilon