diff --git a/src/algorithms/DominguezRios.jl b/src/algorithms/DominguezRios.jl index b88f842..815dc12 100644 --- a/src/algorithms/DominguezRios.jl +++ b/src/algorithms/DominguezRios.jl @@ -67,13 +67,12 @@ end function _select_next_box(L::Vector{Vector{_DominguezRiosBox}}, k::Int) p = length(L) - if any(.!isempty.(L)) + @assert any(!isempty(l) for l in L) + k = k % p + 1 + while isempty(L[k]) k = k % p + 1 - while isempty(L[k]) - k = k % p + 1 - end - i = argmax([B.priority for B in L[k]]) end + i = argmax([B.priority for B in L[k]]) return i, k end @@ -190,7 +189,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer) end i, k = _select_next_box(L, k) B = L[k][i] - # We're goign to scale `w` here by `scale` instead of the usual + # We're going to scale `w` here by `scale` instead of the usual # `1 / max(...)`. It will show up in a few places bbelow. w = scale ./ max.(1, B.u - yI) constraints = [ @@ -221,6 +220,13 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer) else deleteat!(L[k], i) end + else + # In theory, this shouldn't happen, because this subproblem is meant + # to always be feasible. However, in some of our testing, HiGHS will + # fail and return something like OTHER_ERROR (e.g., because the + # numerics are challenging). Rather than error completely, let's + # just skip this box. + deleteat!(L[k], i) end MOI.delete.(model.inner, constraints) end diff --git a/test/algorithms/DominguezRios.jl b/test/algorithms/DominguezRios.jl index e931b60..e9970d0 100644 --- a/test/algorithms/DominguezRios.jl +++ b/test/algorithms/DominguezRios.jl @@ -38,10 +38,12 @@ end function test_vOptLib_runtests() model = MOA.Optimizer(HiGHS.Optimizer) - MOI.set(model, MOA.Algorithm(), MOA.DominguezRios()) - MOI.set(model, MOI.Silent(), true) - # TODO(odow): it doesn't terminate - # vOptLib.run_tests(model) + for (presolve, complete) in ["off" => true, "choose" => false] + MOI.set(model, MOI.RawOptimizerAttribute("presolve"), presolve) + MOI.set(model, MOA.Algorithm(), MOA.DominguezRios()) + MOI.set(model, MOI.Silent(), true) + vOptLib.run_tests(model; complete) + end return end diff --git a/test/vOptLib.jl b/test/vOptLib.jl index 1c1c483..74b6af9 100644 --- a/test/vOptLib.jl +++ b/test/vOptLib.jl @@ -9,19 +9,19 @@ using Test import JSON import MathOptInterface as MOI -function run_tests(model::MOI.ModelLike) +function run_tests(model::MOI.ModelLike; kwargs...) for name in names(@__MODULE__; all = true) if startswith("$name", "test_") @testset "$name" begin MOI.empty!(model) - getfield(@__MODULE__, name)(model) + getfield(@__MODULE__, name)(model; kwargs...) end end end return end -function _test_vOptLib_instance(model, instance) +function _test_vOptLib_instance(model, instance; complete::Bool = true) root = joinpath(dirname(@__DIR__), "instances") src = MOI.FileFormats.MOF.Model() MOI.read_from_file(src, joinpath(root, "models", instance * ".mof.json")) @@ -38,7 +38,8 @@ function _test_vOptLib_instance(model, instance) end push!(solutions[Y], convert(Vector{Int}, sol["X"])) end - @test MOI.get(model, MOI.ResultCount()) >= length(solutions) + min_solutions = complete ? length(solutions) : 1 + @test MOI.get(model, MOI.ResultCount()) >= min_solutions for i in 1:MOI.get(model, MOI.ResultCount()) Y = round.(Int, MOI.get(model, MOI.ObjectiveValue(i))) @test haskey(solutions, Y) @@ -48,12 +49,20 @@ function _test_vOptLib_instance(model, instance) return end -test_vOptLib_2KP50_11(model) = _test_vOptLib_instance(model, "2KP50-11") +function test_vOptLib_2KP50_11(model; kwargs...) + return _test_vOptLib_instance(model, "2KP50-11"; kwargs...) +end -test_vOptLib_2KP50_50(model) = _test_vOptLib_instance(model, "2KP50-50") +function test_vOptLib_2KP50_50(model; kwargs...) + return _test_vOptLib_instance(model, "2KP50-50"; kwargs...) +end -test_vOptLib_2KP50_92(model) = _test_vOptLib_instance(model, "2KP50-92") +function test_vOptLib_2KP50_92(model; kwargs...) + return _test_vOptLib_instance(model, "2KP50-92"; kwargs...) +end -test_vOptLib_2KP100_50(model) = _test_vOptLib_instance(model, "2KP100-50") +function test_vOptLib_2KP100_50(model; kwargs...) + return _test_vOptLib_instance(model, "2KP100-50"; kwargs...) +end end # module vOptLib