I will assume that we know the constant offset will be ignored in Gurobi/CPLEX.
The MPB method getobjgap() will return the gap from solver (printed in solver log), which is evaluated using the solver method without considering the constant offset.
On the JuMP side, m.objVal and m.objBound will convert it back by adding the m.obj.constant. A very strange thing is that method getobjectivevalue() returns the m.objVal rather than fetching from solver.
The inconsistency is between getobjbound() and getobjectivevalue(), where the fomer returns the bound from solver (printed in solver log) while the latter returns the m.objVal where the offset is considered. I believed that the consideration for not having m.objGap in JuMP.Model is due to the different methods used to evaluate MIP gap.
Hence, in the algorithm design, a secure way is to use m.objVal and m.objBound to evaluate the gap using a locally defined Gap, which will be referred to as true GAP (TGap). We will refer the terminated solver log gap as the solver GAP (SGap). Considering the standard CPLEX rel-Gap calculation:
|UB-LB|/(tol+|UB|), note that the offset only affects the denominator in this case. Hence, the potential of error of SGap should be,
When the objective bound and the offset are both positive/negative, SGap < TGap, which means you probably took more time to solve a problem you need.
Otherwise, it may require some more calculation (when numerically sensitive) given the absolute function lose its monotonicity if the domain is around 0.
@ccoffrin @kaarthiksundar
I will assume that we know the constant offset will be ignored in Gurobi/CPLEX.
The MPB method getobjgap() will return the gap from solver (printed in solver log), which is evaluated using the solver method without considering the constant offset.
On the JuMP side, m.objVal and m.objBound will convert it back by adding the m.obj.constant. A very strange thing is that method getobjectivevalue() returns the m.objVal rather than fetching from solver.
The inconsistency is between getobjbound() and getobjectivevalue(), where the fomer returns the bound from solver (printed in solver log) while the latter returns the m.objVal where the offset is considered. I believed that the consideration for not having m.objGap in JuMP.Model is due to the different methods used to evaluate MIP gap.
Hence, in the algorithm design, a secure way is to use m.objVal and m.objBound to evaluate the gap using a locally defined Gap, which will be referred to as true GAP (TGap). We will refer the terminated solver log gap as the solver GAP (SGap). Considering the standard CPLEX rel-Gap calculation: |UB-LB|/(tol+|UB|), note that the offset only affects the denominator in this case. Hence, the potential of error of SGap should be,