Get MIPGap in Gurobi callbacks JuMP 0.21

Hi

I’ve implemented a model with callbacks in Gurobi and experienced that cbget_mipsol_obj and cbget_mipsol_objbst returns the same value.

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0  660.03417    0    2          -  660.03417      -     -  156s
cbget_mipsol_obj(cb_data, cb_where) = 1063.9392486610595
cbget_mipsol_objbst(cb_data, cb_where) = 1063.9392486610595
cbget_mipsol_solcnt(cb_data, cb_where) = 0

H    0     0                    1063.9392487  660.03417  38.0%     -  173s
cbget_mipsol_obj(cb_data, cb_where) = 755.5376433127967
cbget_mipsol_objbst(cb_data, cb_where) = 755.5376433127967
cbget_mipsol_solcnt(cb_data, cb_where) = 1

where the first line is the output from Gurobi and next three lines are printed in the callback function:

function optimality_callback(cb_data::Gurobi.CallbackData, cb_where::Int32)
		cb_where != Gurobi.CB_MIPSOL && return

		@show cbget_mipsol_obj(cb_data, cb_where)
		@show cbget_mipsol_objbst(cb_data, cb_where)
		@show cbget_mipsol_solcnt(cb_data, cb_where)
		
		... Making cuts ...
end

The cut is submitted to the model by

MOI.set(mdl_mstr, MOI.RawParameter("LazyConstraints"), 1)
MOI.set(mdl_mstr, Gurobi.CallbackFunction(), optimality_callback)

I would think that cbget_mipsol_objbst returns the objective of the best feasible MIP solution, while cbget_mipsol_obj returns the objective of the current suggested solution.

How can I obtain the objective of the best feasible solution in callbacks?

Ultimately I’d like to obtain the MIPGap, and it seems like get_dblattr(cb_data.model, "MIPGap") doesn’t work in callbacks:

Gurobi.GurobiError(10005, "Unable to retrieve attribute 'MIPGap'")
get_dblattr(::Gurobi.Model, ::String) at grb_attrs.jl:28
...

I’m using versions

JuMP v0.21.1
Gurobi v0.7.6

Per the Gurobi documentation, https://www.gurobi.com/documentation/9.0/refman/cb_codes.html#sec:CallbackCodes

MIPSOL_OBJ	    MIPSOL	double	Objective value for new solution.
MIPSOL_OBJBST	MIPSOL	double	Current best objective.
MIPSOL_OBJBND	MIPSOL	double	Current best objective bound.

So cbget_mipsol_objbst does return the best feasible MIP solution, and cbget_mipsol_obj does return the current solution. Your callback is just being called at new integer solutions that are the best found so far.

Actually I meant the best feasible after the callback has been called for a given solution. The same value as appears under “Incumbent” in the Gurobi output. I guess it would be possible to check feasibility within the callback and store the value from there. Thank you for taking the time to answer @odow.

cbget_mipsol_objbst is the objective of the new incumbent. Your callback checks that you are at a MIPSOL node.

I suggest you read the Gurobi documentation to understand where the callbacks can get called from: https://www.gurobi.com/documentation/9.0/refman/cb_codes.html