Skip to main content

Weird behavior indicator function

Answered

Comments

14 comments

  • Riley Clement
    • Gurobi Staff

    Hi Salvador,

    I find this behaviour a bit weird

    It is weird, but maybe less weird in context of the warnings

    Warning: max constraint violation (6.8001e-01) exceeds tolerance
    Warning: max general constraint violation (6.8001e-01) exceeds tolerance
            (model may be infeasible or unbounded - try turning presolve off)

    The solution being found has at least one significantly large constraint violation.  It was likely a valid solution in the presolved space with respect to tolerances, but when it is “uncrushed” it is not a clean solution.

    I notice that you are using v10.0.3, I would use our latest v12.0.2 in case this is related to something that has been fixed or improved.

    If you want to share a model file (MPS) with us, via a service like dropbox, google drive, github etc, then please do and I'll look at it further. 

    - Riley

    0
  • spineda
    • Gurobi-versary
    • Conversationalist
    • First Question

    Dear Riley,

    Thanks for the reply.

    I have another example to this repository (https://github.com/salvapineda/gurobi_indicator) including a .mps file and a .py file to read it and execute with a feasible solution. I also include the log I get in Gurobi 10.0.3.

    The initial solution is feasible with an objective function of 1432.46. Then Gurobi “jumps” to a solution with an objective function of 1331.42, but then it realizes this is infeasible with a constraint violation of 1.0819e+00 and concludes that the model may be infeasible. 

    I do not understand why the solver says the problem can be infeasible if it already knows a feasible solution. Or maybe I'm missing something.

    In any case, thanks a lot for the help.

    Best, 

    Salva.

     

    0
  • Riley Clement
    • Gurobi Staff

    Hi Salva,

    I took a look, it seems similar to https://support.gurobi.com/hc/en-us/community/posts/12035360261649

    I used SolFiles to write all solutions found to a directory.  On my machine, with v12.0.3, we found 62 solutions.  12 of these were “infeasible”, i.e. had very large constraint violations.

    The solutions are all ok in the presolved space but then when translated back to the original space errors are magnified, which is why the warning suggests to turn off presolve.  Turning off presolve is an extreme measure, here is what I think you should do:

    * Use v12.0.3 - with each release we generally improve performance and numeric stability

    * Use IntegralityFocus=1.  Your indicator constraints are being translated to Big M linear constraints, which are susceptible to trickle flow error.

    * Add bounds to variables in RHS of indicator constraints.  To translate indicator constraints we need to derive bounds for the expressions appearing on the RHS of indicator constraints.  A quick glance suggests does not look like your variables that appear in the RHS do not have bounds (upper or lower).  If you provide bounds for variables then they may be tighter than what Gurobi is able to derive, or perhaps enables Gurobi to tighten bounds further.

    - Riley

    0
  • spineda
    • Gurobi-versary
    • Conversationalist
    • First Question

    Dear Riley,

    Thank you very much for your reply.

    Yes, the problem is similar to the one you pointed out.

    What I don’t understand is the following: if Gurobi is able to find 62 solutions, with 12 of them being infeasible, why doesn’t it return the best among the 50 feasible ones? I don’t know much about this internal behavior, but it seems like the most natural thing to do.

    Unfortunately, I don’t have access to Gurobi 12. I only have a license for Gurobi 10.

    The reason I’m researching this topic is that I argue that using indicator constraints out of the box in current solvers is quite risky, and that it’s often better to reformulate them using big-M constants. The thing is, whenever I present this argument at a conference, someone from Gurobi usually approaches me to say they disagree and insist that Gurobi handles these problems correctly.

    I agree that I could start tuning Gurobi’s parameters, but in my experience it’s much easier and more effective to do as you suggested: find suitable big-M values and reformulate the indicator constraints manually.

    Thanks again for your feedback!

    Best regards,
    Salva

    0
  • Gwyneth Butera
    • Gurobi Staff

    Unfortunately, I don’t have access to Gurobi 12. I only have a license for Gurobi 10.

    As an Academic user, you should not have this restriction.

    0
  • Riley Clement
    • Gurobi Staff

    Hi Salva,

    why doesn’t it return the best among the 50 feasible ones?

    Technically it returns up to n solutions where n is the value of the PoolSolutions parameter, it's just the last one found is the one reported in the log.  There may be other solutions found that may have violations within the allowed tolerances but what if they are not within the prescribed MIP gap?  I think logging a different solution other than the last would cause confusion.

    The thing is, whenever I present this argument at a conference, someone from Gurobi usually approaches me to say they disagree and insist that Gurobi handles these problems correctly.

    I would agree with them - and certainly with Python can lead to more readable code.  Indicator constraints were introduced to handle the numerical issues introduced by Big M constraints - in particular trickle flow, which is the culprit here.  The reason it is an issue here is because the indicator constraints are being translated into linear constraints.  The PreSOS1BigM parameter can be used to control this.

    To do the translation Gurobi has to derive bounds on the RHS of the indicator constraints expressions.  This is where they can be a downfall.  If coding Big M constraints the user is forced to think about bounds.  If using indicator constraints they can ignore bounds (at their peril).  If the user defines bounds for all variables appearing in indicator constraints then I see no disadvantage, there is only the advantage of flexibility.

    - Riley 

     

    0
  • spineda
    • Gurobi-versary
    • Conversationalist
    • First Question

    Thanks Riley, 

    I think I get your point. So what you're saying is that I can use knowledge about the problem to compute bounds on RHS, feed them to Gurobi, and still get some benefits from the indicator constraints since it can help with numerical issues. Is that correctly understood?

    I thought that indicator constraints were use for lazy people that did not want to find appropiate bigM values, but it seems I was wrong, right?

    Best,

    Salva.

    0
  • Riley Clement
    • Gurobi Staff

    Hi Salva,

    Is that correctly understood?

    Correct.

    I thought that indicator constraints were use for lazy people that did not want to find appropiate bigM values, but it seems I was wrong, right?

    Also correct.  We are always interested in making things easier for users but this is not the motivation here.

     

    As an experiment, consider this Python snippet:

    m = gp.Model()
    b = m.addVar(vtype="B", name="b")
    x = m.addVar(name="x")
    y = m.addVar(name="y")
    m.addConstr((b==0) >> (x+y == 0))
    m.update()
    m.params.DualReductions=0
    p= m.presolve()
    p.write("presolved.lp")

    Gurobi does not know bounds on x and y and so cannot linearize the indicator constraint.  If you inspect the presolved model in the LP file you will see that it has been translated to a SOS1 constraint, but not translated any further.

    Subject To
    R0: x + y - C2 = 0
    Bounds
    Binaries
    C3
    SOS
    s0:  S1 :: C3:1 C2:2
    End
    

    Now try with very large bounds on x and y, 

    x = m.addVar(ub=1e10, name="x")
    y = m.addVar(ub=1e10, name="y")

    You could manually linearize the indicator yourself, with x + y ≤ 2e10b but you will almost certainly get trickle flow due to the huge big M value of 2e10.  Gurobi avoids this by not translating the SOS1 constraint.  (ignore C2, it is left over from the indicator → SOS translation and would be removed by dual reductions if they were enabled).

    presolved.lp:

    Subject To
     R0: x + y - C2 = 0
    Bounds
     x <= 1e+10
     y <= 1e+10
    Binaries
     C3
    SOS
     s0:  S1 :: C3:1 C2:2
    End

     

    Now try with small bounds on x and y, 

    x = m.addVar(ub=10, name="x")
    y = m.addVar(ub=10, name="y")

    presolved.lp:

    Gurobi will also translate this since the big M is not too big:

    Subject To
    R0: C2 + C3 <= 1
    R1: x + y - 20 C3 <= 0
    Bounds
    x <= 10
    y <= 10
    Binaries
    C2 C3
    End

     

    Now add a constraint x + y ≤ 5 and see how the translation changes:

    m.addConstr(x+y <= 5)

    presolved.lp

    Subject To
     R0: C2 + C3 <= 1
     R1: x + y - 5 C3 <= 0
    Bounds
     x <= 5
     y <= 5
    Binaries
     C2 C3
    End

    - Riley

    0
  • spineda
    • Gurobi-versary
    • Conversationalist
    • First Question

    Thanks a lot Riley, that example is very illustrative and I finally understand what Gurobi is doing internally. 

    0
  • spineda
    • Gurobi-versary
    • Conversationalist
    • First Question

    Thanks again. I’ve now run all simulations with the indicator constraints, using the same bounds I had for the other methods, and the results have improved significantly. So it makes more sense to use that as a benchmark for my results.

    While implementing this approach, I came across another question related to my problem. I have the relation y = z * x, where y and x are continuous variables and z is binary. I have bounds for both y and x depending on the value of z, but I also have tighter bounds for x that are only valid when z = 0. Is there a way to include this information directly in Gurobi? Or should I just add another indicator constraint to enforce the tighter bounds when z = 0?

    0
  • Riley Clement
    • Gurobi Staff

    No problem!

    You can use an indicator constraint or you can linearize it yourself with

    x ≤ (1-z) ub_1 + z ub_2

    where ub_1 is the tighter bound, and ub_2 is the looser one.

    0
  • spineda
    • Gurobi-versary
    • Conversationalist
    • First Question

    Ok, I did that. I was expecting the indicator formulation to consistently outperform the big-M reformulation when using the same bounds, but that’s not always the case. In fact, I implemented three different formulations: the non-linear formulation y = z * x, the indicator formulation (z = 0 ⇒ y = 0, z = 1 ⇒ y = x), and the big-M formulation. Surprisingly, depending on the instance, any of the three can result in the lowest CPU time.

    Maybe my implementation could be optimized further, but I was wondering if this kind of behavior is expected in Gurobi?

    0
  • Riley Clement
    • Gurobi Staff

    Are you comparing each instance with many values of Seed?

    These articles will be of use:

     

     

    0
  • spineda
    • Gurobi-versary
    • Conversationalist
    • First Question

    No, instances differ because some of the parameters are different.

    0

Please sign in to leave a comment.