I’ve got a MIP problem in JuMP, for which I would like the branch-and-bound to terminate as soon as the incumbent reaches a particular value, or as soon as the lower bound reaches a (different) particular value.
I’ve found an example of how this can be done in Python (using callbacks) on Gurobi’s website.
My understanding is that JuMP’s support for callbacks is limited, but certain solver-specific callbacks can be implemented. Before I spend too much time reading through source code, I would like to know if this is possible, and if any examples exist that could be used as a starting point.
Ideally, something like this. But I’m getting a segfault when I try to
terminate. (Edit: I think this may have been due to Julia’s GC mixing with the early terminate. I can’t reproduce if I turn it off.)
softlimit = 5
hardlimit = 100
model = direct_model(
OutputFlag = 0,
Cuts = 0,
Presolve = 0,
PreCrush = 1,
Heuristics = 0,
@variable(model, 0 <= x <= 2.5, Int)
@variable(model, 0 <= y <= 2.5, Int)
@objective(model, Max, y)
function softtime(cb_data, cb_where)
if cb_where == Gurobi.CB_MIP
runtime = Gurobi.cbget_runtime(cb_data, cb_where)
objbst = Gurobi.cbget_mip_objbst(cb_data, cb_where)
objbnd = Gurobi.cbget_mip_objbnd(cb_data, cb_where)
gap = abs((objbst - objbnd) / objbst)
@info("Terminating at $(runtime) with $(gap)")
set_optimizer_attribute(model, "TimeLimit", hardlimit)
MOI.set(model, Gurobi.CallbackFunction(), softtime)
GC.enable(false) # You might want this
GC.enable(true) # You might want this
I ran your code (both with and without the GC enabled) and it seemed to work fine, and I didn’t get a segmentation fault.
Was there a reason for the optimizer settings (no cuts, presolve or heuristics etc.)?
That’s just so it triggers the callback on a small problem. Otherwise the optimal solution is found at the root node before the branch and bound routine (and callbacks) starts to get called.
I’ve got this working in my application; thanks again. However, for some reason, I wasn’t able to directly access the
inner field of my model and had to use
It’s for this reason I used
direct_model(Gurobi.Optimizer()) instead of
In the second case, you have to fight your way through the layers of bridging and caching optimizers to reach