Callback not being called when it should

395 views
Skip to first unread message

Fabien Tricoire

unread,
Jul 10, 2015, 8:59:41 AM7/10/15
to gur...@googlegroups.com
Hi,

I work in a setting where cuts are added from a callback, when where = MIPSOL. After generating a cut this way and adding it to the model, the status changes to OPTIMAL and my cut callback is not called ever again with where = MIPSOL. The model is then considered to be optimal and the solution process is over (meaning that the call to optimize() is over). However, the cut callback should still be called: there are still cuts to add that weren't added previously. What am I missing? Why is the cut callback not called?

So the chronology is as follows:
1) callback is called with where = MIPSOL
2) callback generates lazy cut
3) callback is called again several times but only with where = MIP, MESSAGE or POLLING.
4) model is considered optimal (supposedly after having solved the LP again with the new cut)
5) optimize() returns

However at stage 4 the callback should be called again with where = MIPSOL: possibly some new cuts would make the current solution suboptimal (which is exactly my situation).

For the model to be really optimal, we should first have the callback be called with where = MIPSOL, then have this callback add no cut at all. For some reason this does not happen. What could that reason be?

The Gurobi version I use is 6.0.4. This is a similar problem to the one mentioned here: https://groups.google.com/d/msg/gurobi/dYePF7JsNlA/hZsiCsUo0gcJ
However that older thread does not solve the issue and no solution is offered. I also tried posting as a comment there but it does not seem to work.

cheers,
Fabien Tricoire

Ed Rothberg

unread,
Jul 14, 2015, 2:38:39 PM7/14/15
to gur...@googlegroups.com, fabien....@gmail.com

We're not sure exactly why, but lazy constraints are probably our #1 source of spurious bug reports.  We had a few minor bugs in our lazy constraints early on, but I'd estimate that >95% of all reports of problems with lazy constraints have turned out to be in user code.

The one piece of advice I can give: make certain that the constraint you are adding truly cuts off the reported solution.  A constraint that doesn't actually cut off the solution would give the symptoms you describe.

Ed


Fabien Tricoire

unread,
Jul 16, 2015, 9:32:11 AM7/16/15
to gur...@googlegroups.com
Thank you for your reply. In the mean time I managed to reproduce this bug (I believe) with a short code sample, which I post below. I would expect this code to add cuts until counter = 10, but it actually stops at 6. If you believe this is not a bug then I would be glad to know what I am doing wrong, so that I can fix it.

Code starts below.

------------------------------------------------------------------------------------------------------------------
from gurobipy import *

def myCallback(model, where):
    global y, counter
    if where == GRB.Callback.MIPSOL:
        print '*** In callback! where = MIPSOL'
        if counter < counterLimit:
            model.cbLazy(y >= counter)
            counter += 1
            print 'added cut! counter =', counter

counter = 1
counterLimit = 5

model = Model('test')

model.setParam('Outputflag', 0)
model.setParam('LazyConstraints', 1)
model.setParam('Threads', 1)

x = model.addVar(vtype=GRB.BINARY, name='x')
y = model.addVar(vtype=GRB.CONTINUOUS, name='y')

model.update()

model.setObjective(x+y, GRB.MINIMIZE)

model.optimize(myCallback)

counterLimit = 10

model.optimize(myCallback)
------------------------------------------------------------------------------------------------------------------

kind regards,
Fabien Tricoire

Renan Garcia

unread,
Jul 19, 2015, 9:14:52 AM7/19/15
to gur...@googlegroups.com
I would expect this code to add cuts until counter = 10, but it actually stops at 6.

Stopping earlier than counter = 10 is expected behavior for your code. By setting counterLimit = 5 before the first call to optimize(), the code adds only the following constraints:

  y >= 1
  y >= 2
  y >= 3
  y >= 4

Thus, when Gurobi presents the solution (0,4) in a subsequent MIPSOL callback, it declines to cut it off since counter >= counter limit, and it is accepted as a feasible solution. Since you don't change the model in any way before the second call to optimize(), that (0,4) solution is still loaded for that model. Therefore, the root LP bound and the best feasible bound both equal 4 at the beginning of that solve and Gurobi concludes the problem is optimal. If you think any solution presented by a MIPSOL callback is infeasible, you must cut it off then and there because you won't get another chance to do so.

Fabien Tricoire

unread,
Jul 20, 2015, 11:13:57 AM7/20/15
to gur...@googlegroups.com
Thank you for your answer. I understand what you mean but it doesn't actually explain what happens: the callback is called again during the second call to optimize(), and a new cut is added: y>= 6. Following your explanation this should not happen. The cut is added, so the model is modified by this cut. Only after that does optimize() stop, even though the model has just been modified by the new cut. So what is the explanation for that?

Renan Garcia

unread,
Jul 20, 2015, 4:18:09 PM7/20/15
to gur...@googlegroups.com
New lazy constraints added during a call to Model.optimize() will never invalidate existing incumbent solutions. If you want to invalidate them before a call to Model.optimize(), modify the model with non-lazy constraints to cut them off or use the Model.reset() method.

--

---
You received this message because you are subscribed to the Google Groups "Gurobi Optimization" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gurobi+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages