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.)

using JuMP
import Gurobi
softlimit = 5
hardlimit = 100
model = direct_model(
Gurobi.Optimizer(
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
@info("Executing callback")
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)")
Gurobi.terminate(backend(model).inner)
end
return
end
set_optimizer_attribute(model, "TimeLimit", hardlimit)
MOI.set(model, Gurobi.CallbackFunction(), softtime)
GC.enable(false) # You might want this
optimize!(model)
GC.enable(true) # You might want this

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