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.
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
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
Consider the classic:
julia> 0.3 == 10 / 3
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.