From d7d30c708374f0a8ae736c71fae106d2b9f5fa1b Mon Sep 17 00:00:00 2001 From: Samuel Buchet Date: Wed, 1 Jul 2026 15:41:56 +0200 Subject: [PATCH 1/5] handle no solution case --- ext/Polyhedra/GeneralDichotomy.jl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/Polyhedra/GeneralDichotomy.jl b/ext/Polyhedra/GeneralDichotomy.jl index f5ab061..1358fb6 100644 --- a/ext/Polyhedra/GeneralDichotomy.jl +++ b/ext/Polyhedra/GeneralDichotomy.jl @@ -24,9 +24,8 @@ function MOA.minimize_multiobjective!( # Storage we need for the algorithm. weights, solutions = Weight[], MOA.SolutionPoint[] n_obj = MOI.output_dimension(model.f) - # First, minimize the first objective to obtain a primal feasible point. - w = zeros(Float64, n_obj) - w[1] = 1.0 + # First, minimize the combined objectives to obtain a primal feasible point. + w = ones(Float64, n_obj) status, solution = MOA._solve_weighted_sum(model, alg, w) if solution === nothing return status, nothing @@ -40,7 +39,7 @@ function MOA.minimize_multiobjective!( w[i] = 1.0 z = w' * solution.y adj_bnd = Int[-j for j in 1:n_obj if j != i] - push!(weights, Weight(w, z, adj_bnd, [1], i == 1, false)) + push!(weights, Weight(w, z, adj_bnd, [1], false, false)) end # Prevent solution duplicates: existing_sol maps an rounded objective vector # to its index in `solutions::Vector{MOA.SolutionPoint}`. @@ -59,8 +58,7 @@ function MOA.minimize_multiobjective!( end status, sol = MOA._solve_weighted_sum(model, alg, weight.w) if sol === nothing - # TODO(odow): what to do when this solve fails? - return status, solutions + return MOI.NUMERICAL_ERROR, solutions end weight.tested = true if !haskey(existing_sol, _round(sol.y; atol)) From d586717c88e61131c3c7082a2b4ba582f0fa3ca6 Mon Sep 17 00:00:00 2001 From: Samuel Buchet Date: Thu, 2 Jul 2026 10:13:34 +0200 Subject: [PATCH 2/5] test all initial weights in GeneralDichotomy to find the initial solution --- ext/Polyhedra/GeneralDichotomy.jl | 35 ++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/ext/Polyhedra/GeneralDichotomy.jl b/ext/Polyhedra/GeneralDichotomy.jl index 1358fb6..9548787 100644 --- a/ext/Polyhedra/GeneralDichotomy.jl +++ b/ext/Polyhedra/GeneralDichotomy.jl @@ -24,13 +24,25 @@ function MOA.minimize_multiobjective!( # Storage we need for the algorithm. weights, solutions = Weight[], MOA.SolutionPoint[] n_obj = MOI.output_dimension(model.f) - # First, minimize the combined objectives to obtain a primal feasible point. - w = ones(Float64, n_obj) - status, solution = MOA._solve_weighted_sum(model, alg, w) - if solution === nothing + # First, search for an initial primal feasible point. + init_sol_idx = 0 + status, solution = nothing, nothing + for i in 1:n_obj + w = zeros(Float64, n_obj) + w[i] = 1.0 + status, solution = MOA._solve_weighted_sum(model, alg, w) + if solution !== nothing + if !MOA._is_scalar_status_optimal(model) + _log_subproblem_solve(model, "subproblem not optimal") + end + init_sol_idx = i + push!(solutions, solution) + break + end + end + if length(solutions) == 0 return status, nothing end - push!(solutions, solution) # Initialize the weights. There is one weight vector for each objective, and # the weight is set to 1.0 for each objective. We use the current solution # obtained by minimizing the 1st objective as the reference. @@ -39,7 +51,9 @@ function MOA.minimize_multiobjective!( w[i] = 1.0 z = w' * solution.y adj_bnd = Int[-j for j in 1:n_obj if j != i] - push!(weights, Weight(w, z, adj_bnd, [1], false, false)) + tested = i <= init_sol_idx ? true : false + removed = i < init_sol_idx ? true : false + push!(weights, Weight(w, z, adj_bnd, [1], tested, removed)) end # Prevent solution duplicates: existing_sol maps an rounded objective vector # to its index in `solutions::Vector{MOA.SolutionPoint}`. @@ -57,10 +71,15 @@ function MOA.minimize_multiobjective!( continue end status, sol = MOA._solve_weighted_sum(model, alg, weight.w) + weight.tested = true + # the weight is skipped if there is no solution + # the procedure can continue in case of sub-optimality if sol === nothing - return MOI.NUMERICAL_ERROR, solutions + continue + end + if !MOA._is_scalar_status_optimal(model) + _log_subproblem_solve(model, "subproblem not optimal") end - weight.tested = true if !haskey(existing_sol, _round(sol.y; atol)) push!(solutions, sol) # Prepare new weight index set for the new solution's adjacency. From 997ba8cef347a50eff9cb4e02137c4bae0df9c45 Mon Sep 17 00:00:00 2001 From: smbct <18369425+smbct@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:19:33 +0200 Subject: [PATCH 3/5] Update ext/Polyhedra/GeneralDichotomy.jl Co-authored-by: Oscar Dowson --- ext/Polyhedra/GeneralDichotomy.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/Polyhedra/GeneralDichotomy.jl b/ext/Polyhedra/GeneralDichotomy.jl index 9548787..aaa23c9 100644 --- a/ext/Polyhedra/GeneralDichotomy.jl +++ b/ext/Polyhedra/GeneralDichotomy.jl @@ -52,7 +52,7 @@ function MOA.minimize_multiobjective!( z = w' * solution.y adj_bnd = Int[-j for j in 1:n_obj if j != i] tested = i <= init_sol_idx ? true : false - removed = i < init_sol_idx ? true : false + removed = i < init_sol_idx push!(weights, Weight(w, z, adj_bnd, [1], tested, removed)) end # Prevent solution duplicates: existing_sol maps an rounded objective vector From 8990d1f7b9cbab1bb4094fca9b367d9ca645a2aa Mon Sep 17 00:00:00 2001 From: smbct <18369425+smbct@users.noreply.github.com> Date: Thu, 2 Jul 2026 16:19:41 +0200 Subject: [PATCH 4/5] Update ext/Polyhedra/GeneralDichotomy.jl Co-authored-by: Oscar Dowson --- ext/Polyhedra/GeneralDichotomy.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/Polyhedra/GeneralDichotomy.jl b/ext/Polyhedra/GeneralDichotomy.jl index aaa23c9..0c999f7 100644 --- a/ext/Polyhedra/GeneralDichotomy.jl +++ b/ext/Polyhedra/GeneralDichotomy.jl @@ -51,7 +51,7 @@ function MOA.minimize_multiobjective!( w[i] = 1.0 z = w' * solution.y adj_bnd = Int[-j for j in 1:n_obj if j != i] - tested = i <= init_sol_idx ? true : false + tested = i <= init_sol_idx removed = i < init_sol_idx push!(weights, Weight(w, z, adj_bnd, [1], tested, removed)) end From 161842dba7158e43b4ee6cc0d97efc1095a4e913 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 3 Jul 2026 11:00:03 +1200 Subject: [PATCH 5/5] Update --- ext/Polyhedra/GeneralDichotomy.jl | 10 ++-------- src/algorithms/GeneralDichotomy.jl | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/ext/Polyhedra/GeneralDichotomy.jl b/ext/Polyhedra/GeneralDichotomy.jl index 4391533..a6e18a9 100644 --- a/ext/Polyhedra/GeneralDichotomy.jl +++ b/ext/Polyhedra/GeneralDichotomy.jl @@ -32,9 +32,6 @@ function MOA.minimize_multiobjective!( w[i] = 1.0 status, solution = MOA._solve_weighted_sum(model, alg, w) if solution !== nothing - if !MOA._is_scalar_status_optimal(model) - _log_subproblem_solve(model, "subproblem not optimal") - end init_sol_idx = i push!(solutions, solution) break @@ -72,14 +69,11 @@ function MOA.minimize_multiobjective!( end status, sol = MOA._solve_weighted_sum(model, alg, weight.w) weight.tested = true - # the weight is skipped if there is no solution - # the procedure can continue in case of sub-optimality + # The weight is skipped if there is no solution. The algortihm can + # continue in case of sub-optimality. if sol === nothing continue end - if !MOA._is_scalar_status_optimal(model) - _log_subproblem_solve(model, "subproblem not optimal") - end if !haskey(existing_sol, _round(sol.y; atol)) push!(solutions, sol) # Prepare new weight index set for the new solution's adjacency. diff --git a/src/algorithms/GeneralDichotomy.jl b/src/algorithms/GeneralDichotomy.jl index 9b2748e..ebf29de 100644 --- a/src/algorithms/GeneralDichotomy.jl +++ b/src/algorithms/GeneralDichotomy.jl @@ -61,6 +61,7 @@ function _solve_weighted_sum( optimize_inner!(model) status = MOI.get(model.inner, MOI.TerminationStatus()) if !_is_scalar_status_optimal(status) + _log_subproblem_solve(model, "subproblem not optimal") return status, nothing end variables = MOI.get(model.inner, MOI.ListOfVariableIndices())