From d922b3fc7e643e603d7dfe0a8d93dfc64b0a9d12 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 25 Feb 2026 11:09:54 +1300 Subject: [PATCH 1/2] Fix sorting non-dominated solutions to use a tolerance --- src/MultiObjectiveAlgorithms.jl | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/MultiObjectiveAlgorithms.jl b/src/MultiObjectiveAlgorithms.jl index c7d16ef..57e9e89 100644 --- a/src/MultiObjectiveAlgorithms.jl +++ b/src/MultiObjectiveAlgorithms.jl @@ -51,8 +51,26 @@ function _dominates( end end -function _sort!(solutions::Vector{SolutionPoint}, sense::MOI.OptimizationSense) - return sort!(solutions; by = x -> x.y, rev = sense == MOI.MAX_SENSE) +# The use of `atol` when sorting is to work-around a tolerance issue that caused +# a test failure in #181 that wasn't reproducible on macOS. It happened on linux +# because of a minor version change in HiGHS. +# +# Consider two Y vectors `y1 = [22, 37, 63]` and `y2 = [22, 54, 47]`. We clearly +# want to return them in the order `y1`, `y2`, but if `y1[1] = 22+eps` then +# we'll get these "round the wrong way" from the user's perspective, even though +# it would be numerically correct. +# +# My solution is just to round these to the nearest `atol`. The main situation +# that this would be confusing is when the objective is integer and we sort +# wrongly because of 0.9999999 and 1.00000001 etc. +function _sort!( + solutions::Vector{SolutionPoint}, + sense::MOI.OptimizationSense; + atol::Float64, +) + digits = round(Int, log10(atol)) + rev = sense == MOI.MAX_SENSE + return sort!(solutions; by = p -> round.(p.y; digits), rev) end """ @@ -83,7 +101,7 @@ function filter_nondominated( push!(nondominated_solutions, candidate) end end - _sort!(nondominated_solutions, sense) + _sort!(nondominated_solutions, sense; atol) return nondominated_solutions end From 2948a40278c1c8db92bf381f53d7c0799d1661cd Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 25 Feb 2026 11:21:14 +1300 Subject: [PATCH 2/2] Update --- src/MultiObjectiveAlgorithms.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MultiObjectiveAlgorithms.jl b/src/MultiObjectiveAlgorithms.jl index 57e9e89..fdbad5d 100644 --- a/src/MultiObjectiveAlgorithms.jl +++ b/src/MultiObjectiveAlgorithms.jl @@ -68,7 +68,7 @@ function _sort!( sense::MOI.OptimizationSense; atol::Float64, ) - digits = round(Int, log10(atol)) + digits = max(0, round(Int, -log10(atol))) rev = sense == MOI.MAX_SENSE return sort!(solutions; by = p -> round.(p.y; digits), rev) end