Skip to main content

Struggling with Lazy constraints

Answered

Comments

4 comments

  • Official comment
    Simranjit Kaur
    • Gurobi Staff
    This post is more than three years old. Some information may not be up to date. For current information, please check the Gurobi Documentation or Knowledge Base. If you need more help, please create a new post in the community forum. Or why not try our AI Gurobot?.
  • Eli Towle
    • Gurobi Staff

    Regarding the "'gurobipy.GenConstr' object has no attribute 'Lazy'" error: The Lazy attribute can only be used with linear constraints (Constr objects), not general constraints (GenConstr objects) like indicator constraints. To resolve the issue, you must reformulate your general constraint using linear constraints.

    Let \( z \) be a binary variable, \( y \in [\ell, u] \) a continuous variable, and \( c \) a constant. The indicator constraint 

    $$\begin{align*} z = 1 \implies y = c\end{align*}$$

    can be linearized with the following two constraints:

    $$\begin{align*} y &\leq u + (c - u)z \\ y &\geq \ell + (c - \ell)z.\end{align*}$$

    If \( z = 0 \), the above two constraints become \( \ell \leq y \leq u \). In this case, \( y \) is not restricted by these constraints. If \( z = 1 \), the two constraints together imply \( y = c \), as desired.

    Regarding the "object of type 'NoneType' has no len()" error: there are no variables in your model with a name exactly equal to \( \texttt{y_r_i_j} \). As a result, your call to Model.getVarByName() returns \( \texttt{None} \). Model.addVars() uses the \( \texttt{name} \) keyword argument as a base for constructing unique variable names. The actual variable names will be something like \( \texttt{y_r_i_j[0,0,0]} \), \( \texttt{y_r_i_j[0,0,1]} \), etc. For example:

    >>> x = m.addVars(3, name='x')
    >>> m.update()
    >>> m.getAttr('VarName', m.getVars())
    ['x[0]', 'x[1]', 'x[2]']

    An easier way to access your \( y \) variable in a callback is to create a new attribute for your Model object and assign the \( \texttt{y} \) variables to it. Then, you can simply query this new attribute inside the callback function:

    y = m.addVars(rij, vtype=GRB.BINARY, name='y')
    m._y = y

    def subtourelim(model, where):
    if where == GRB.Callback.MIPSOL:
    yvals = model.cbGetSolution(model._y)

    Note that lazy constraints added via callbacks must also be linear. So in this case, you will also have to linearize your indicator constraints.

    Regarding the "No variable names available to index" error: Model.getVarByName() queries the variables' VarName attributes. However, these attributes are not available until the model is updated. To resolve the issue, call Model.update() first:

    m.update()
    0
  • Evan Triou
    • Gurobi-versary
    • First Comment
    • First Question

    Thank you for your clear and complete support, it helped a lot and my script works fine now ! 

    I would just like to mention some strange error I faced and solved:

    rij = [list of tuples (r,i,j)]

    z = model.addVars(rij, vtype = GRB.CONTINUOUS, name = 'z')
    model._z = z
    def mycallback(where, model):
    if where == GRB.Callback.MIPSOL:
    zvals = model.cbGetSolution(model._z)
    print(zvals) #everything looks fine, it prints the dict I expect
    for r,i,j in zvals.keys():
    model.cbLazy(zvals[r,i,j] >= 0)

    Message error at the constraint: Unsupported operand type(s) for -: 'bool' and 'NoneType'  my dict changed to NoneType once I used it in the loop. I replaced zvals by model._z and it solved the problem but I can not understand why ? Is is still a linearity pb ? My constraint seems classic... 

    I thought It was because z was not used anywhere else so Gurobi discarded it during the resolution and affected no value to it. But I red here, (https://support.gurobi.com/hc/en-us/community/posts/360078009912-set-the-value-of-unused-variables- https://support.gurobi.com/hc/en-us/community/posts/360078009912-set-the-value-of-unused-variables- ) that it can not be the case.

     

    Any idea ? 

    In any case, thank you again for help :)

    0
  • Eli Towle
    • Gurobi Staff

    The \( \texttt{MIPSOL} \) callback is called whenever Gurobi finds a new incumbent solution. Model.cbGetSolution() returns the values of your variables at this new solution.

    Consider any \( (r, i, j) \) tuple. In the new incumbent solution found by Gurobi, \( z_{rij} \) might have a value of \( 0 \). In this case, \( \texttt{zvals[r,i,j]} \) equals \( 0 \). So, \( \texttt{zvals[r,i,j] >= 0} \) is really the expression \( \texttt{0 >= 0} \). In Python, this evaluates to \( \texttt{True} \). As a result, the following call to Model.cbLazy()

    model.cbLazy(zvals[r,i,j] >= 0)

    is equivalent to

    model.cbLazy(True)

    which isn't a valid way of calling Model.cbLazy().

    As you noted, your code works when you use \( \texttt{model._z} \) instead of \( \texttt{zvals} \) in your call to Model.cbLazy(). This is because \( \texttt{model._z} \) stores the actual \( z \) variables instead of their solution values. Thus, the code

    model.cbLazy(model._z[r,i,j] >= 0)

    adds the lazy constraint \( z_{rij} \geq 0 \), which I assume is what you're trying to do.

    I'll also note the lower bound on the \( z \) variables is \( 0 \) by default. This means in any solution found by Gurobi, all of the \( z \) variables are nonnegative. Consequently, the lazy constraint \( z_{rij} \geq 0 \) is always redundant.

    0

Post is closed for comments.