From 4bf30b4546de5bee4208438fc361582d70321536 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Thu, 18 Dec 2025 20:41:35 +1300 Subject: [PATCH 1/4] Add two-stage solve for EpsilonConstraint --- src/algorithms/EpsilonConstraint.jl | 39 +++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/algorithms/EpsilonConstraint.jl b/src/algorithms/EpsilonConstraint.jl index 6442cea..20833d9 100644 --- a/src/algorithms/EpsilonConstraint.jl +++ b/src/algorithms/EpsilonConstraint.jl @@ -107,22 +107,40 @@ function minimize_multiobjective!( MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2) # Add epsilon constraint variables = MOI.get(inner, MOI.ListOfVariableIndices()) - bound = right - ε - constant = MOI.constant(f1, Float64) - ci = MOI.Utilities.normalize_and_add_constraint( - model, + bound_1 = right - ε + constant_1 = MOI.constant(f1, Float64) + ci_1 = MOI.Utilities.normalize_and_add_constraint( + inner, f1, - MOI.LessThan{Float64}(bound); - allow_modify_function = true, + MOI.LessThan(bound_1), ) - bound -= constant + yN = max(solution_1[1].y[2], solution_2[1].y[2]) + constant_2 = MOI.constant(f2, Float64) + ci_2 = MOI.Utilities.normalize_and_add_constraint( + inner, + f2, + MOI.LessThan(yN) + ) + bound_1 -= constant_1 status = MOI.OPTIMAL for _ in 3:n_points if (ret = _check_premature_termination(model)) !== nothing status = ret break end - MOI.set(model, MOI.ConstraintSet(), ci, MOI.LessThan{Float64}(bound)) + # First-stage solve: minimize f₂: f₂ <= bound_1 + MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2) + MOI.set(inner, MOI.ConstraintSet(), ci_1, MOI.LessThan(bound_1)) + MOI.set(inner, MOI.ConstraintSet(), ci_2, MOI.LessThan(yN - constant_2)) + optimize_inner!(model) + if !_is_scalar_status_optimal(model) + break + end + # First-stage solve: minimize f₁: f₂ <= f₂^* + f_2_star = MOI.get(inner, MOI.ObjectiveValue())::Float64 + MOI.set(inner, MOI.ObjectiveFunction{typeof(f1)}(), f1) + bound_2 = f_2_star - constant_2 + MOI.set(inner, MOI.ConstraintSet(), ci_2, MOI.LessThan(bound_2)) optimize_inner!(model) if !_is_scalar_status_optimal(model) break @@ -132,8 +150,9 @@ function minimize_multiobjective!( if isempty(solutions) || !(Y ≈ solutions[end].y) push!(solutions, SolutionPoint(X, Y)) end - bound = min(Y[1] - constant - ε, bound - ε) + bound_1 = min(Y[1] - constant_1 - ε, bound_1 - ε) end - MOI.delete(model, ci) + MOI.delete(inner, ci_1) + MOI.delete(inner, ci_2) return status, solutions end From b76d084712548ea193d225fa85c3730ba478b015 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Thu, 18 Dec 2025 21:06:24 +1300 Subject: [PATCH 2/4] Update --- src/algorithms/EpsilonConstraint.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/algorithms/EpsilonConstraint.jl b/src/algorithms/EpsilonConstraint.jl index 20833d9..7a95431 100644 --- a/src/algorithms/EpsilonConstraint.jl +++ b/src/algorithms/EpsilonConstraint.jl @@ -116,11 +116,8 @@ function minimize_multiobjective!( ) yN = max(solution_1[1].y[2], solution_2[1].y[2]) constant_2 = MOI.constant(f2, Float64) - ci_2 = MOI.Utilities.normalize_and_add_constraint( - inner, - f2, - MOI.LessThan(yN) - ) + ci_2 = + MOI.Utilities.normalize_and_add_constraint(inner, f2, MOI.LessThan(yN)) bound_1 -= constant_1 status = MOI.OPTIMAL for _ in 3:n_points From 2ed12a015e04850fb6df1d976ab7ef245915dcdc Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 19 Dec 2025 09:19:52 +1300 Subject: [PATCH 3/4] Update --- src/algorithms/EpsilonConstraint.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/EpsilonConstraint.jl b/src/algorithms/EpsilonConstraint.jl index 7a95431..0293054 100644 --- a/src/algorithms/EpsilonConstraint.jl +++ b/src/algorithms/EpsilonConstraint.jl @@ -133,7 +133,7 @@ function minimize_multiobjective!( if !_is_scalar_status_optimal(model) break end - # First-stage solve: minimize f₁: f₂ <= f₂^* + # Second-stage solve: minimize f₁: f₂ <= f₂^* f_2_star = MOI.get(inner, MOI.ObjectiveValue())::Float64 MOI.set(inner, MOI.ObjectiveFunction{typeof(f1)}(), f1) bound_2 = f_2_star - constant_2 From 5853d191701d642395b8c126bd7d3ff626e8b74d Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 19 Dec 2025 09:37:53 +1300 Subject: [PATCH 4/4] Update --- src/algorithms/EpsilonConstraint.jl | 39 ++++++++++++----------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/algorithms/EpsilonConstraint.jl b/src/algorithms/EpsilonConstraint.jl index 0293054..648c034 100644 --- a/src/algorithms/EpsilonConstraint.jl +++ b/src/algorithms/EpsilonConstraint.jl @@ -105,49 +105,42 @@ function minimize_multiobjective!( solutions = SolutionPoint[only(solution_1), only(solution_2)] f1, f2 = MOI.Utilities.eachscalar(f) MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2) - # Add epsilon constraint - variables = MOI.get(inner, MOI.ListOfVariableIndices()) - bound_1 = right - ε - constant_1 = MOI.constant(f1, Float64) - ci_1 = MOI.Utilities.normalize_and_add_constraint( - inner, - f1, - MOI.LessThan(bound_1), - ) - yN = max(solution_1[1].y[2], solution_2[1].y[2]) - constant_2 = MOI.constant(f2, Float64) + # Add epsilon constraints + u_1 = right + c_1 = MOI.constant(f1, Float64) + ci_1 = + MOI.Utilities.normalize_and_add_constraint(inner, f1, MOI.LessThan(u_1)) + u_2 = max(solution_1[1].y[2], solution_2[1].y[2]) + c_2 = MOI.constant(f2, Float64) ci_2 = - MOI.Utilities.normalize_and_add_constraint(inner, f2, MOI.LessThan(yN)) - bound_1 -= constant_1 + MOI.Utilities.normalize_and_add_constraint(inner, f2, MOI.LessThan(u_2)) + variables = MOI.get(inner, MOI.ListOfVariableIndices()) status = MOI.OPTIMAL for _ in 3:n_points if (ret = _check_premature_termination(model)) !== nothing status = ret break end - # First-stage solve: minimize f₂: f₂ <= bound_1 + # First-stage solve: minimize f₂: f₁ <= u₁ - ε MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2) - MOI.set(inner, MOI.ConstraintSet(), ci_1, MOI.LessThan(bound_1)) - MOI.set(inner, MOI.ConstraintSet(), ci_2, MOI.LessThan(yN - constant_2)) + MOI.set(inner, MOI.ConstraintSet(), ci_1, MOI.LessThan(u_1 - c_1 - ε)) + MOI.set(inner, MOI.ConstraintSet(), ci_2, MOI.LessThan(u_2 - c_2)) optimize_inner!(model) if !_is_scalar_status_optimal(model) break end # Second-stage solve: minimize f₁: f₂ <= f₂^* - f_2_star = MOI.get(inner, MOI.ObjectiveValue())::Float64 + f_2_opt = MOI.get(inner, MOI.ObjectiveValue())::Float64 MOI.set(inner, MOI.ObjectiveFunction{typeof(f1)}(), f1) - bound_2 = f_2_star - constant_2 - MOI.set(inner, MOI.ConstraintSet(), ci_2, MOI.LessThan(bound_2)) + MOI.set(inner, MOI.ConstraintSet(), ci_2, MOI.LessThan(f_2_opt - c_2)) optimize_inner!(model) if !_is_scalar_status_optimal(model) break end X, Y = _compute_point(model, variables, f) _log_subproblem_solve(model, Y) - if isempty(solutions) || !(Y ≈ solutions[end].y) - push!(solutions, SolutionPoint(X, Y)) - end - bound_1 = min(Y[1] - constant_1 - ε, bound_1 - ε) + push!(solutions, SolutionPoint(X, Y)) + u_1 = Y[1] end MOI.delete(inner, ci_1) MOI.delete(inner, ci_2)