Skip to content

Commit d23472a

Browse files
authored
Fix MOA with local solvers not supporting delete (#46)
1 parent 2d96a72 commit d23472a

File tree

7 files changed

+52
-7
lines changed

7 files changed

+52
-7
lines changed

src/MultiObjectiveAlgorithms.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ abstract type AbstractAlgorithm end
9494

9595
MOI.Utilities.map_indices(::Function, x::AbstractAlgorithm) = x
9696

97+
function _instantiate_with_cache(optimizer_factory)
98+
model = MOI.instantiate(optimizer_factory)
99+
if !MOI.supports_incremental_interface(model)
100+
# A cache will already have been added
101+
return model
102+
end
103+
cache = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
104+
return MOI.Utilities.CachingOptimizer(cache, model)
105+
end
106+
97107
mutable struct Optimizer <: MOI.AbstractOptimizer
98108
inner::MOI.AbstractOptimizer
99109
algorithm::Union{Nothing,AbstractAlgorithm}
@@ -103,7 +113,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
103113

104114
function Optimizer(optimizer_factory)
105115
return new(
106-
MOI.instantiate(optimizer_factory),
116+
_instantiate_with_cache(optimizer_factory),
107117
nothing,
108118
nothing,
109119
SolutionPoint[],
@@ -489,7 +499,8 @@ function MOI.get(model::Optimizer, attr::MOI.ObjectiveBound)
489499
for (i, f) in enumerate(objectives)
490500
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f)
491501
MOI.optimize!(model.inner)
492-
if MOI.get(model.inner, MOI.TerminationStatus()) == MOI.OPTIMAL
502+
status = MOI.get(model.inner, MOI.TerminationStatus())
503+
if _is_scalar_status_optimal(status)
493504
ideal_point[i] = MOI.get(model.inner, MOI.ObjectiveValue())
494505
end
495506
end
@@ -523,6 +534,11 @@ function _is_scalar_status_optimal(status::MOI.TerminationStatusCode)
523534
return status == MOI.OPTIMAL || status == MOI.LOCALLY_SOLVED
524535
end
525536

537+
function _is_scalar_status_optimal(model::Optimizer)
538+
status = MOI.get(model.inner, MOI.TerminationStatus())
539+
return _is_scalar_status_optimal(status)
540+
end
541+
526542
for file in readdir(joinpath(@__DIR__, "algorithms"))
527543
include(joinpath(@__DIR__, "algorithms", file))
528544
end

src/algorithms/Dichotomy.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ function _solve_weighted_sum(
6464
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f)
6565
MOI.optimize!(model.inner)
6666
status = MOI.get(model.inner, MOI.TerminationStatus())
67-
status = MOI.get(model.inner, MOI.TerminationStatus())
6867
if !_is_scalar_status_optimal(status)
6968
return status, nothing
7069
end

src/algorithms/DominguezRios.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ function optimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
201201
new_f = t_max + ϵ * sum(w[i] * (scalars[i] - yI[i]) for i in 1:n)
202202
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
203203
MOI.optimize!(model.inner)
204-
if MOI.get(model.inner, MOI.TerminationStatus()) == MOI.OPTIMAL
204+
if _is_scalar_status_optimal(model)
205205
X, Y = _compute_point(model, variables, model.f)
206206
obj = MOI.get(model.inner, MOI.ObjectiveValue())
207207
if (obj < 1) && all(yI .< B.u)

src/algorithms/EpsilonConstraint.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ function optimize_multiobjective!(
107107
while true
108108
MOI.set(model, MOI.ConstraintSet(), ci, SetType(bound))
109109
MOI.optimize!(model.inner)
110-
if MOI.get(model.inner, MOI.TerminationStatus()) != MOI.OPTIMAL
110+
if !_is_scalar_status_optimal(model)
111111
break
112112
end
113113
X, Y = _compute_point(model, variables, model.f)

src/algorithms/KirlikSayin.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ function optimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
153153
end
154154
end
155155
MOI.optimize!(model.inner)
156-
if MOI.get(model.inner, MOI.TerminationStatus()) != MOI.OPTIMAL
156+
if !_is_scalar_status_optimal(model)
157157
_remove_rectangle(L, _Rectangle(_project(yI, k), uᵢ))
158158
MOI.delete.(model, ε_constraints)
159159
continue
@@ -169,7 +169,7 @@ function optimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
169169
MOI.optimize!(model.inner)
170170
MOI.delete.(model, ε_constraints)
171171
MOI.delete(model, zₖ_constraint)
172-
if MOI.get(model.inner, MOI.TerminationStatus()) != MOI.OPTIMAL
172+
if !_is_scalar_status_optimal(model)
173173
_remove_rectangle(L, _Rectangle(_project(yI, k), uᵢ))
174174
continue
175175
end

test/algorithms/EpsilonConstraint.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module TestEpsilonConstraint
88
using Test
99

1010
import HiGHS
11+
import Ipopt
1112
import MultiObjectiveAlgorithms as MOA
1213

1314
const MOI = MOA.MOI
@@ -289,6 +290,34 @@ function test_deprecated()
289290
return
290291
end
291292

293+
function test_quadratic()
294+
μ = [0.05470748600000001, 0.18257110599999998]
295+
Q = [0.00076204 0.00051972; 0.00051972 0.00546173]
296+
N = 2
297+
model = MOA.Optimizer(Ipopt.Optimizer)
298+
MOI.set(model, MOA.Algorithm(), MOA.EpsilonConstraint())
299+
MOI.set(model, MOA.SolutionLimit(), 10)
300+
MOI.set(model, MOI.Silent(), true)
301+
w = MOI.add_variables(model, N)
302+
MOI.add_constraint.(model, w, MOI.GreaterThan(0.0))
303+
MOI.add_constraint.(model, w, MOI.LessThan(1.0))
304+
MOI.add_constraint(model, sum(1.0 * w[i] for i in 1:N), MOI.EqualTo(1.0))
305+
var = sum(Q[i, j] * w[i] * w[j] for i in 1:N, j in 1:N)
306+
mean = sum(-μ[i] * w[i] for i in 1:N)
307+
f = MOI.Utilities.operate(vcat, Float64, var, mean)
308+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
309+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
310+
MOI.optimize!(model)
311+
@test MOI.get(model, MOI.ResultCount()) == 10
312+
for i in 1:MOI.get(model, MOI.ResultCount())
313+
w_sol = MOI.get(model, MOI.VariablePrimal(i), w)
314+
y = MOI.get(model, MOI.ObjectiveValue(i))
315+
@test y [w_sol' * Q * w_sol, -μ' * w_sol]
316+
end
317+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
318+
return
319+
end
320+
292321
end
293322

294323
TestEpsilonConstraint.run_tests()

test/test_model.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function test_moi_runtests()
3636
exclude = String[
3737
# Skipped beause of UniversalFallback in _mock_optimizer
3838
"test_attribute_Silent",
39+
"test_attribute_after_empty",
3940
"test_model_copy_to_UnsupportedAttribute",
4041
"test_model_copy_to_UnsupportedConstraint",
4142
"test_model_supports_constraint_ScalarAffineFunction_EqualTo",

0 commit comments

Comments
 (0)