Skip to content

Commit

Permalink
Fix ObjectiveBound and RelativeGap for LP models (#229)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Sep 23, 2024
1 parent b4eb587 commit b92cb13
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 7 deletions.
17 changes: 10 additions & 7 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2103,22 +2103,25 @@ function _dual_objective_contribution(l, x, u, d)
end

function MOI.get(model::Optimizer, ::MOI.ObjectiveBound)
# TODO(odow): there is a bug in HiGHS where it reports incorrect values for
# mip_dual_bound in the LP case. As a work-around, just return the most
# optimistic of the primal and dual values.
if model.solution.dual_solution_status != kHighsSolutionStatusNone
return MOI.get(model, MOI.DualObjectiveValue())
end
p = Ref{Cdouble}()
Highs_getDoubleInfoValue(model, "mip_dual_bound", p)
sense = _sense_corrector(model)
primal = Highs_getObjectiveValue(model)
return sense == -1 ? max(primal, -p[]) : min(primal, p[])
return p[]
end

function MOI.get(model::Optimizer, ::MOI.SolveTimeSec)
return Highs_getRunTime(model)
end

function MOI.get(model::Optimizer, ::MOI.RelativeGap)
p = Ref{Cdouble}(0)
if model.solution.dual_solution_status != kHighsSolutionStatusNone
primal = MOI.get(model, MOI.ObjectiveValue())
dual = MOI.get(model, MOI.DualObjectiveValue())
return abs(primal - dual) / max(1, abs(primal))
end
p = Ref{Cdouble}(0.0)
ret = Highs_getDoubleInfoValue(model, "mip_gap", p)
_check_ret(ret)
return p[]
Expand Down
19 changes: 19 additions & 0 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,25 @@ function test_DualObjectiveValue_int()
return
end

function test_continuous_objective_bound()
model = HiGHS.Optimizer()
MOI.set(model, MOI.Silent(), true)
MOI.set(model, MOI.RawOptimizerAttribute("solver"), "ipm")
MOI.set(model, MOI.RawOptimizerAttribute("run_crossover"), "off")
MOI.set(model, MOI.RawOptimizerAttribute("presolve"), "off")
x = MOI.add_variables(model, 3)
c = MOI.add_constraint.(model, 1.0 .* x, MOI.EqualTo.(1.0:3.0))
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
f = 1.0 * x[1] + x[2] + x[3]
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.optimize!(model)
primal = MOI.get(model, MOI.ObjectiveValue())
dual = MOI.get(model, MOI.DualObjectiveValue())
@test MOI.get(model, MOI.ObjectiveBound()) == dual
@test 0 < MOI.get(model, MOI.RelativeGap()) <= 1e-6
return
end

end # module

TestMOIHighs.runtests()

0 comments on commit b92cb13

Please sign in to comment.