From 90b109c92bc644dc66be023a956a9579a707bd01 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 14 Aug 2025 15:59:47 +1200 Subject: [PATCH 1/4] Add support for interrupting during the solve loops --- src/MultiObjectiveAlgorithms.jl | 17 +++++++++++++++++ src/algorithms/Chalmet.jl | 10 ++++++---- src/algorithms/Dichotomy.jl | 10 ++++++++-- src/algorithms/DominguezRios.jl | 3 +++ src/algorithms/EpsilonConstraint.jl | 3 +++ src/algorithms/KirlikSayin.jl | 6 +++++- src/algorithms/Lexicographic.jl | 3 +++ src/algorithms/RandomWeighting.jl | 2 ++ src/algorithms/TambyVanderpooten.jl | 3 +++ 9 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/MultiObjectiveAlgorithms.jl b/src/MultiObjectiveAlgorithms.jl index 7a178ae..6f90203 100644 --- a/src/MultiObjectiveAlgorithms.jl +++ b/src/MultiObjectiveAlgorithms.jl @@ -644,7 +644,24 @@ function optimize_multiobjective!( return minimize_multiobjective!(algorithm, model) end +function _check_interrupt() + try + reenable_sigint(() -> nothing) + catch ex + if ex isa InterruptException + return true + end + rethrow(ex) + end + return false +end + function MOI.optimize!(model::Optimizer) + disable_sigint(() -> _optimize!(model)) + return +end + +function _optimize!(model::Optimizer) start_time = time() empty!(model.solutions) model.termination_status = MOI.OPTIMIZE_NOT_CALLED diff --git a/src/algorithms/Chalmet.jl b/src/algorithms/Chalmet.jl index b4e61cf..b9e446b 100644 --- a/src/algorithms/Chalmet.jl +++ b/src/algorithms/Chalmet.jl @@ -57,7 +57,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, nothing + return status, solutions end _, y1[2] = _compute_point(model, variables, f2) MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1) @@ -69,7 +69,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, nothing + return status, solutions end x1, y1[1] = _compute_point(model, variables, f1) MOI.delete(model.inner, y1_constraint) @@ -78,7 +78,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, nothing + return status, solutions end _, y2[1] = _compute_point(model, variables, f1) if y2[1] ≈ solutions[1].y[1] @@ -93,7 +93,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, nothing + return status, solutions end x2, y2[2] = _compute_point(model, variables, f2) MOI.delete(model.inner, y2_constraint) @@ -103,6 +103,8 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) while !isempty(Q) if _time_limit_exceeded(model, start_time) return MOI.TIME_LIMIT, solutions + elseif _check_interrupt() + return MOI.INTERRUPTED, solutions end r, s = pop!(Q) yr, ys = solutions[r].y, solutions[s].y diff --git a/src/algorithms/Dichotomy.jl b/src/algorithms/Dichotomy.jl index d357056..1157668 100644 --- a/src/algorithms/Dichotomy.jl +++ b/src/algorithms/Dichotomy.jl @@ -79,6 +79,8 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer) if MOI.output_dimension(model.f) == 1 if _time_limit_exceeded(model, start_time) return MOI.TIME_LIMIT, nothing + elseif _check_interrupt() + return MOI.INTERRUPTED, nothing end status, solution = _solve_weighted_sum(model, algorithm, [1.0]) return status, [solution] @@ -87,6 +89,8 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer) for (i, w) in (1 => 1.0, 2 => 0.0) if _time_limit_exceeded(model, start_time) return MOI.TIME_LIMIT, nothing + elseif _check_interrupt() + return MOI.INTERRUPTED, nothing end status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w]) if !_is_scalar_status_optimal(status) @@ -106,14 +110,16 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer) if _time_limit_exceeded(model, start_time) status = MOI.TIME_LIMIT break + elseif _check_interrupt() + status = MOI.INTERRUPTED + break end (a, b) = popfirst!(queue) y_d = solutions[a].y .- solutions[b].y w = y_d[2] / (y_d[2] - y_d[1]) status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w]) if !_is_scalar_status_optimal(status) - # Exit the solve with some error. - return status, nothing + break # Exit the solve with some error. elseif solution ≈ solutions[a] || solution ≈ solutions[b] # We have found an existing solution. We're free to prune (a, b) # from the search space. diff --git a/src/algorithms/DominguezRios.jl b/src/algorithms/DominguezRios.jl index 566b582..e55fceb 100644 --- a/src/algorithms/DominguezRios.jl +++ b/src/algorithms/DominguezRios.jl @@ -186,6 +186,9 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer) if _time_limit_exceeded(model, start_time) status = MOI.TIME_LIMIT break + elseif _check_interrupt() + status = MOI.INTERRUPTED + break end i, k = _select_next_box(L, k) B = L[k][i] diff --git a/src/algorithms/EpsilonConstraint.jl b/src/algorithms/EpsilonConstraint.jl index e1ed5f0..a8689ae 100644 --- a/src/algorithms/EpsilonConstraint.jl +++ b/src/algorithms/EpsilonConstraint.jl @@ -114,6 +114,9 @@ function minimize_multiobjective!( if _time_limit_exceeded(model, start_time) status = MOI.TIME_LIMIT break + elseif _check_interrupt() + status = MOI.INTERRUPTED + break end MOI.set(model, MOI.ConstraintSet(), ci, MOI.LessThan{Float64}(bound)) optimize_inner!(model) diff --git a/src/algorithms/KirlikSayin.jl b/src/algorithms/KirlikSayin.jl index c5f242e..41a665b 100644 --- a/src/algorithms/KirlikSayin.jl +++ b/src/algorithms/KirlikSayin.jl @@ -118,7 +118,11 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer) status = MOI.OPTIMAL while !isempty(L) if _time_limit_exceeded(model, start_time) - return MOI.TIME_LIMIT, solutions + status = MOI.TIME_LIMIT + break + elseif _check_interrupt() + status = MOI.INTERRUPTED + break end max_volume_index = argmax([_volume(Rᵢ, _project(yI, k)) for Rᵢ in L]) uᵢ = L[max_volume_index].u diff --git a/src/algorithms/Lexicographic.jl b/src/algorithms/Lexicographic.jl index 5f105a9..2b7a0e6 100644 --- a/src/algorithms/Lexicographic.jl +++ b/src/algorithms/Lexicographic.jl @@ -127,6 +127,9 @@ function _solve_in_sequence( if _time_limit_exceeded(model, start_time) status = MOI.TIME_LIMIT break + elseif _check_interrupt() + status = MOI.INTERRUPTED + break end f = scalars[i] MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f) diff --git a/src/algorithms/RandomWeighting.jl b/src/algorithms/RandomWeighting.jl index 2d2f8ef..f35658c 100644 --- a/src/algorithms/RandomWeighting.jl +++ b/src/algorithms/RandomWeighting.jl @@ -62,6 +62,8 @@ function optimize_multiobjective!(algorithm::RandomWeighting, model::Optimizer) while length(solutions) < MOI.get(algorithm, SolutionLimit()) if _time_limit_exceeded(model, start_time) return MOI.TIME_LIMIT, filter_nondominated(sense, solutions) + elseif _check_interrupt() + return MOI.INTERRUPTED, filter_nondominated(sense, solutions) end weights = rand(P) f = _scalarise(model.f, weights) diff --git a/src/algorithms/TambyVanderpooten.jl b/src/algorithms/TambyVanderpooten.jl index 0bd5b32..d88a314 100644 --- a/src/algorithms/TambyVanderpooten.jl +++ b/src/algorithms/TambyVanderpooten.jl @@ -126,6 +126,9 @@ function minimize_multiobjective!( if _time_limit_exceeded(model, start_time) status = MOI.TIME_LIMIT break + elseif _check_interrupt() + status = MOI.INTERRUPTED + break end k, u = _select_search_zone(U_N, yI, yN) MOI.set( From 36ed96f7dcef3b6f09d5888246cf594be8fe91e2 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 14 Aug 2025 16:13:41 +1200 Subject: [PATCH 2/4] Update --- ext/MultiObjectiveAlgorithmsPolyhedraExt.jl | 5 +-- src/MultiObjectiveAlgorithms.jl | 38 ++++++++++----------- src/algorithms/Chalmet.jl | 10 +++--- src/algorithms/Dichotomy.jl | 19 ++++------- src/algorithms/DominguezRios.jl | 11 +++--- src/algorithms/EpsilonConstraint.jl | 7 ++-- src/algorithms/KirlikSayin.jl | 7 ++-- src/algorithms/Lexicographic.jl | 7 ++-- src/algorithms/RandomWeighting.jl | 7 ++-- src/algorithms/TambyVanderpooten.jl | 7 ++-- 10 files changed, 47 insertions(+), 71 deletions(-) diff --git a/ext/MultiObjectiveAlgorithmsPolyhedraExt.jl b/ext/MultiObjectiveAlgorithmsPolyhedraExt.jl index d61a6df..39b6fd5 100644 --- a/ext/MultiObjectiveAlgorithmsPolyhedraExt.jl +++ b/ext/MultiObjectiveAlgorithmsPolyhedraExt.jl @@ -101,8 +101,9 @@ function MOA.minimize_multiobjective!( H = _halfspaces(IPS) count = 0 while !isempty(H) - if MOA._time_limit_exceeded(model, start_time) - status = MOI.TIME_LIMIT + ret = MOA._check_premature_termination(model, start_time) + if ret !== nothing + status = ret break end count += 1 diff --git a/src/MultiObjectiveAlgorithms.jl b/src/MultiObjectiveAlgorithms.jl index 6f90203..c4f9394 100644 --- a/src/MultiObjectiveAlgorithms.jl +++ b/src/MultiObjectiveAlgorithms.jl @@ -185,21 +185,6 @@ function MOI.set(model::Optimizer, ::MOI.TimeLimitSec, ::Nothing) return end -function _time_limit_exceeded(model::Optimizer, start_time::Float64) - time_limit = MOI.get(model, MOI.TimeLimitSec()) - if time_limit === nothing - return false - end - time_remaining = time_limit - (time() - start_time) - if time_remaining <= 0 - return true - end - if MOI.supports(model.inner, MOI.TimeLimitSec()) - MOI.set(model.inner, MOI.TimeLimitSec(), time_remaining) - end - return false -end - ### SolveTimeSec function MOI.get(model::Optimizer, ::MOI.SolveTimeSec) @@ -604,7 +589,7 @@ end function _compute_ideal_point(model::Optimizer, start_time) for (i, f) in enumerate(MOI.Utilities.eachscalar(model.f)) - if _time_limit_exceeded(model, start_time) + if _check_premature_termination(model, start_time) !== nothing return end if !isnan(model.ideal_point[i]) @@ -644,16 +629,29 @@ function optimize_multiobjective!( return minimize_multiobjective!(algorithm, model) end -function _check_interrupt() +function _check_premature_termination(model::Optimizer, start_time::Float64) try - reenable_sigint(() -> nothing) + return reenable_sigint() do + time_limit = MOI.get(model, MOI.TimeLimitSec()) + if time_limit === nothing + return + end + time_remaining = time_limit - (time() - start_time) + if time_remaining <= 0 + return MOI.TIME_LIMIT + end + if MOI.supports(model.inner, MOI.TimeLimitSec()) + MOI.set(model.inner, MOI.TimeLimitSec(), time_remaining) + end + return + end catch ex if ex isa InterruptException - return true + return MOI.INTERRUPTED end rethrow(ex) end - return false + return nothing # no termination end function MOI.optimize!(model::Optimizer) diff --git a/src/algorithms/Chalmet.jl b/src/algorithms/Chalmet.jl index b9e446b..f7a6e5f 100644 --- a/src/algorithms/Chalmet.jl +++ b/src/algorithms/Chalmet.jl @@ -100,11 +100,11 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) push!(solutions, SolutionPoint(x2, y2)) push!(Q, (1, 2)) t = 3 + status = MOI.OPTIMAL while !isempty(Q) - if _time_limit_exceeded(model, start_time) - return MOI.TIME_LIMIT, solutions - elseif _check_interrupt() - return MOI.INTERRUPTED, solutions + if (ret = _check_premature_termination(model, start_time)) !== nothing + status = ret + break end r, s = pop!(Q) yr, ys = solutions[r].y, solutions[s].y @@ -118,5 +118,5 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) append!(Q, [(r, t), (t, s)]) t += 1 end - return MOI.OPTIMAL, solutions + return status, solutions end diff --git a/src/algorithms/Dichotomy.jl b/src/algorithms/Dichotomy.jl index 1157668..ba74728 100644 --- a/src/algorithms/Dichotomy.jl +++ b/src/algorithms/Dichotomy.jl @@ -77,20 +77,16 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer) error("Only scalar or bi-objective problems supported.") end if MOI.output_dimension(model.f) == 1 - if _time_limit_exceeded(model, start_time) - return MOI.TIME_LIMIT, nothing - elseif _check_interrupt() - return MOI.INTERRUPTED, nothing + if (ret = _check_premature_termination(model, start_time)) !== nothing + return ret, nothing end status, solution = _solve_weighted_sum(model, algorithm, [1.0]) return status, [solution] end solutions = Dict{Float64,SolutionPoint}() for (i, w) in (1 => 1.0, 2 => 0.0) - if _time_limit_exceeded(model, start_time) - return MOI.TIME_LIMIT, nothing - elseif _check_interrupt() - return MOI.INTERRUPTED, nothing + if (ret = _check_premature_termination(model, start_time)) !== nothing + return ret, nothing end status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w]) if !_is_scalar_status_optimal(status) @@ -107,11 +103,8 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer) limit = MOI.get(algorithm, SolutionLimit()) status = MOI.OPTIMAL while length(queue) > 0 && length(solutions) < limit - if _time_limit_exceeded(model, start_time) - status = MOI.TIME_LIMIT - break - elseif _check_interrupt() - status = MOI.INTERRUPTED + if (ret = _check_premature_termination(model, start_time)) !== nothing + status = ret break end (a, b) = popfirst!(queue) diff --git a/src/algorithms/DominguezRios.jl b/src/algorithms/DominguezRios.jl index e55fceb..b28a251 100644 --- a/src/algorithms/DominguezRios.jl +++ b/src/algorithms/DominguezRios.jl @@ -57,8 +57,8 @@ function _p_partition( ẑ = max.(z, B.l) ret = _DominguezRiosBox[] for i in 1:length(z) - new_l = vcat(B.l[1:i], ẑ[i+1:end]) - new_u = vcat(B.u[1:i-1], ẑ[i], B.u[i+1:end]) + new_l = vcat(B.l[1:i], ẑ[(i+1):end]) + new_u = vcat(B.u[1:(i-1)], ẑ[i], B.u[(i+1):end]) new_priority = _reduced_scaled_priority(new_l, new_u, i, ẑ, yI, yN) push!(ret, _DominguezRiosBox(new_l, new_u, new_priority)) end @@ -183,11 +183,8 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer) k = 0 status = MOI.OPTIMAL while any(!isempty(l) for l in L) - if _time_limit_exceeded(model, start_time) - status = MOI.TIME_LIMIT - break - elseif _check_interrupt() - status = MOI.INTERRUPTED + if (ret = _check_premature_termination(model, start_time)) !== nothing + status = ret break end i, k = _select_next_box(L, k) diff --git a/src/algorithms/EpsilonConstraint.jl b/src/algorithms/EpsilonConstraint.jl index a8689ae..74dc5b0 100644 --- a/src/algorithms/EpsilonConstraint.jl +++ b/src/algorithms/EpsilonConstraint.jl @@ -111,11 +111,8 @@ function minimize_multiobjective!( bound -= constant status = MOI.OPTIMAL for _ in 3:n_points - if _time_limit_exceeded(model, start_time) - status = MOI.TIME_LIMIT - break - elseif _check_interrupt() - status = MOI.INTERRUPTED + if (ret = _check_premature_termination(model, start_time)) !== nothing + status = ret break end MOI.set(model, MOI.ConstraintSet(), ci, MOI.LessThan{Float64}(bound)) diff --git a/src/algorithms/KirlikSayin.jl b/src/algorithms/KirlikSayin.jl index 41a665b..0fb3e4b 100644 --- a/src/algorithms/KirlikSayin.jl +++ b/src/algorithms/KirlikSayin.jl @@ -117,11 +117,8 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer) L = [_Rectangle(_project(yI, k), _project(yN, k))] status = MOI.OPTIMAL while !isempty(L) - if _time_limit_exceeded(model, start_time) - status = MOI.TIME_LIMIT - break - elseif _check_interrupt() - status = MOI.INTERRUPTED + if (ret = _check_premature_termination(model, start_time)) !== nothing + status = ret break end max_volume_index = argmax([_volume(Rᵢ, _project(yI, k)) for Rᵢ in L]) diff --git a/src/algorithms/Lexicographic.jl b/src/algorithms/Lexicographic.jl index 2b7a0e6..049c8b6 100644 --- a/src/algorithms/Lexicographic.jl +++ b/src/algorithms/Lexicographic.jl @@ -124,11 +124,8 @@ function _solve_in_sequence( solution = SolutionPoint[] status = MOI.OPTIMAL for i in sequence - if _time_limit_exceeded(model, start_time) - status = MOI.TIME_LIMIT - break - elseif _check_interrupt() - status = MOI.INTERRUPTED + if (ret = _check_premature_termination(model, start_time)) !== nothing + status = ret break end f = scalars[i] diff --git a/src/algorithms/RandomWeighting.jl b/src/algorithms/RandomWeighting.jl index f35658c..6563b0a 100644 --- a/src/algorithms/RandomWeighting.jl +++ b/src/algorithms/RandomWeighting.jl @@ -60,10 +60,9 @@ function optimize_multiobjective!(algorithm::RandomWeighting, model::Optimizer) # * then the outer loop goes again while length(solutions) < MOI.get(algorithm, SolutionLimit()) while length(solutions) < MOI.get(algorithm, SolutionLimit()) - if _time_limit_exceeded(model, start_time) - return MOI.TIME_LIMIT, filter_nondominated(sense, solutions) - elseif _check_interrupt() - return MOI.INTERRUPTED, filter_nondominated(sense, solutions) + ret = _check_premature_termination(model, start_time) + if ret !== nothing + return ret, filter_nondominated(sense, solutions) end weights = rand(P) f = _scalarise(model.f, weights) diff --git a/src/algorithms/TambyVanderpooten.jl b/src/algorithms/TambyVanderpooten.jl index d88a314..48128b4 100644 --- a/src/algorithms/TambyVanderpooten.jl +++ b/src/algorithms/TambyVanderpooten.jl @@ -123,11 +123,8 @@ function minimize_multiobjective!( U_N[yN] = [[_get_child(yN, yI, k)] for k in 1:n] status = MOI.OPTIMAL while !isempty(U_N) - if _time_limit_exceeded(model, start_time) - status = MOI.TIME_LIMIT - break - elseif _check_interrupt() - status = MOI.INTERRUPTED + if (ret = _check_premature_termination(model, start_time)) !== nothing + status = ret break end k, u = _select_search_zone(U_N, yI, yN) From c9be8b0f5700be92f57cefed0ed1abb57299d312 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 14 Aug 2025 16:17:47 +1200 Subject: [PATCH 3/4] Update --- src/algorithms/Chalmet.jl | 14 ++++++-------- src/algorithms/Dichotomy.jl | 3 ++- src/algorithms/DominguezRios.jl | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/algorithms/Chalmet.jl b/src/algorithms/Chalmet.jl index f7a6e5f..77d4a80 100644 --- a/src/algorithms/Chalmet.jl +++ b/src/algorithms/Chalmet.jl @@ -57,7 +57,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, solutions + return status, nothing end _, y1[2] = _compute_point(model, variables, f2) MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1) @@ -69,7 +69,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, solutions + return status, nothing end x1, y1[1] = _compute_point(model, variables, f1) MOI.delete(model.inner, y1_constraint) @@ -78,7 +78,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, solutions + return status, nothing end _, y2[1] = _compute_point(model, variables, f1) if y2[1] ≈ solutions[1].y[1] @@ -93,18 +93,16 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) - return status, solutions + return status, nothing end x2, y2[2] = _compute_point(model, variables, f2) MOI.delete(model.inner, y2_constraint) push!(solutions, SolutionPoint(x2, y2)) push!(Q, (1, 2)) t = 3 - status = MOI.OPTIMAL while !isempty(Q) if (ret = _check_premature_termination(model, start_time)) !== nothing - status = ret - break + return ret, solutions end r, s = pop!(Q) yr, ys = solutions[r].y, solutions[s].y @@ -118,5 +116,5 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer) append!(Q, [(r, t), (t, s)]) t += 1 end - return status, solutions + return MOI.OPTIMAL, solutions end diff --git a/src/algorithms/Dichotomy.jl b/src/algorithms/Dichotomy.jl index ba74728..47aec29 100644 --- a/src/algorithms/Dichotomy.jl +++ b/src/algorithms/Dichotomy.jl @@ -112,7 +112,8 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer) w = y_d[2] / (y_d[2] - y_d[1]) status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w]) if !_is_scalar_status_optimal(status) - break # Exit the solve with some error. + # Exit the solve with some error. + return status, nothing elseif solution ≈ solutions[a] || solution ≈ solutions[b] # We have found an existing solution. We're free to prune (a, b) # from the search space. diff --git a/src/algorithms/DominguezRios.jl b/src/algorithms/DominguezRios.jl index b28a251..8ca05d9 100644 --- a/src/algorithms/DominguezRios.jl +++ b/src/algorithms/DominguezRios.jl @@ -57,8 +57,8 @@ function _p_partition( ẑ = max.(z, B.l) ret = _DominguezRiosBox[] for i in 1:length(z) - new_l = vcat(B.l[1:i], ẑ[(i+1):end]) - new_u = vcat(B.u[1:(i-1)], ẑ[i], B.u[(i+1):end]) + new_l = vcat(B.l[1:i], ẑ[i+1:end]) + new_u = vcat(B.u[1:i-1], ẑ[i], B.u[i+1:end]) new_priority = _reduced_scaled_priority(new_l, new_u, i, ẑ, yI, yN) push!(ret, _DominguezRiosBox(new_l, new_u, new_priority)) end From 64bcf62d1facf8e070d67cef8a0f441adb6ee92b Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 14 Aug 2025 16:49:55 +1200 Subject: [PATCH 4/4] Update --- src/MultiObjectiveAlgorithms.jl | 28 +++++++++++++++------------- test/test_model.jl | 9 +++++++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/MultiObjectiveAlgorithms.jl b/src/MultiObjectiveAlgorithms.jl index c4f9394..8c2fb3b 100644 --- a/src/MultiObjectiveAlgorithms.jl +++ b/src/MultiObjectiveAlgorithms.jl @@ -629,13 +629,21 @@ function optimize_multiobjective!( return minimize_multiobjective!(algorithm, model) end -function _check_premature_termination(model::Optimizer, start_time::Float64) +function _check_interrupt(f) try - return reenable_sigint() do - time_limit = MOI.get(model, MOI.TimeLimitSec()) - if time_limit === nothing - return - end + return reenable_sigint(f) + catch ex + if !(ex isa InterruptException) + rethrow(ex) + end + return MOI.INTERRUPTED + end +end + +function _check_premature_termination(model::Optimizer, start_time::Float64) + return _check_interrupt() do + time_limit = MOI.get(model, MOI.TimeLimitSec()) + if time_limit !== nothing time_remaining = time_limit - (time() - start_time) if time_remaining <= 0 return MOI.TIME_LIMIT @@ -643,15 +651,9 @@ function _check_premature_termination(model::Optimizer, start_time::Float64) if MOI.supports(model.inner, MOI.TimeLimitSec()) MOI.set(model.inner, MOI.TimeLimitSec(), time_remaining) end - return end - catch ex - if ex isa InterruptException - return MOI.INTERRUPTED - end - rethrow(ex) + return end - return nothing # no termination end function MOI.optimize!(model::Optimizer) diff --git a/test/test_model.jl b/test/test_model.jl index 3c1f310..db61d86 100644 --- a/test/test_model.jl +++ b/test/test_model.jl @@ -225,6 +225,15 @@ function test_SubproblemCount() return end +function test_check_interrupt() + function _test_check_interrupt(err) + return disable_sigint(() -> MOA._check_interrupt(() -> throw(err))) + end + @test _test_check_interrupt(InterruptException()) == MOI.INTERRUPTED + @test_throws ArgumentError("") _test_check_interrupt(ArgumentError("")) + return +end + end # module TestModel.run_tests()