LazyConstraintCallback not called at every solution?

The MathOptInterface documentation states that:

there is no guarantee that the callback is called at every feasible primal solution.

Is there a way to get around this using Gurobi? I have run into a problem where only a part of the necessary LazyConstraints get added before the callback function is never called again.

Example execution could be: finding an optimal solution → calling my callback function → adding a violated lazy constraint → finding new optimal solution

At this point Gurobi terminates, having found “the optimal solution”. However if the callback function was called again, it would find a new violated constraint and have to resolve the problem.

1 Like

Do you have a reproducible example? This sounds like a bug. The final solution should be feasible for all possible lazy constraints

I do have an example but it is quite lengthy. Luckily most of the code needn’t be understood except for the LazyConstraintCallback part. I tried to explain the problem in a bit more detail in the README file. Here is a link to the repository with the code.

I think that the final solution is indeed feasible for all lazy constraints that are added. However, I add one violated constraint at a time (a single callback function call), and at some point the callback function is just not called anymore, and the solution process terminates having found a solution that satisfies all constraints added so far.

I didn’t run your code, but there are a few things that stick out. This is probably a bug in your code, not in Gurobi.jl or in Gurobi.

The most likely are lines like this:

Gurobi may return solutions which are feasible only up to a certain tolerance. So even though x_opt is binary, it may return 0.999999.

Use isapprox instead:

isapprox(x_opt[current_feat, current_splitpoint_index, 1; atol = 1e-5)

Note that this applies to programming more generally. It is never safe to compare floating point numbers with == and !=.

Consider the classic:

julia> 0.3 == 10 / 3
false

You should always use isapprox with some tolerance.

Thank you very much. I rounded the current optimal solutions before using the values and set the callback to run only at integer solutions (before it was at all nodes). Now the solution is as expected.

1 Like