Skip to content

Commit 49e403d

Browse files
committed
Add a warning when lexicographic used with default args and 5+ objectives
1 parent 0be8f2f commit 49e403d

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

src/MultiObjectiveAlgorithms.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ Controls whether to return the lexicographic solution for all permutations of
364364
the scalar objectives (when `true`), or only the solution corresponding to the
365365
lexicographic solution of the original objective function (when `false`).
366366
367-
Defaults to true`.
367+
Defaults to `true`.
368368
"""
369369
struct LexicographicAllPermutations <: AbstractAlgorithmAttribute end
370370

src/algorithms/Lexicographic.jl

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ point on the frontier, corresponding to solving each objective in order.
2525
"""
2626
mutable struct Lexicographic <: AbstractAlgorithm
2727
rtol::Vector{Float64}
28-
all_permutations::Bool
28+
all_permutations::Union{Nothing,Bool}
2929

3030
function Lexicographic(; all_permutations::Union{Nothing,Bool} = nothing)
3131
if all_permutations !== nothing
@@ -35,7 +35,7 @@ mutable struct Lexicographic <: AbstractAlgorithm
3535
"option to `$all_permutations` instead.",
3636
)
3737
end
38-
return new(Float64[], default(LexicographicAllPermutations()))
38+
return new(Float64[], nothing)
3939
end
4040
end
4141

@@ -67,9 +67,35 @@ end
6767
function optimize_multiobjective!(algorithm::Lexicographic, model::Optimizer)
6868
start_time = time()
6969
sequence = 1:MOI.output_dimension(model.f)
70-
if !MOI.get(algorithm, LexicographicAllPermutations())
70+
perm = MOI.get(algorithm, LexicographicAllPermutations())
71+
if !something(perm, default(LexicographicAllPermutations()))
7172
return _solve_in_sequence(algorithm, model, sequence, start_time)
7273
end
74+
if perm === nothing && length(sequence) >= 5
75+
o, n = length(sequence), factorial(length(sequence))
76+
@warn(
77+
"""
78+
The `MOA.Lexicographic` algorithm has been called with the default
79+
option for `MOA.LexicographicAllPermutations()`.
80+
81+
Because there are $o objectives, the algorithm will solve all
82+
$(o)! = $n permutations of the objectives.
83+
84+
To solve only a single sequence corresponding to the lexicographic
85+
order of the objective function, set `MOA.LexicographicAllPermutations()`
86+
to `false`:
87+
```julia
88+
set_attribute(model, MOA.LexicographicAllPermutations(), false)
89+
```
90+
91+
To disable this warning, explicitly opt-in to all permutations by
92+
setting it to `true`:
93+
```julia
94+
set_attribute(model, MOA.LexicographicAllPermutations(), true)
95+
```
96+
""",
97+
)
98+
end
7399
solutions = SolutionPoint[]
74100
status = MOI.OPTIMAL
75101
for sequence in Combinatorics.permutations(sequence)

test/algorithms/Lexicographic.jl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,55 @@ function test_knapsack_time_limit()
198198
return
199199
end
200200

201+
function test_knapsack_5_objectives()
202+
P = Float64[
203+
1 0 0 0;
204+
0 1 0 0;
205+
0 0 1 0;
206+
0 0 0 1;
207+
1 1 1 1;
208+
]
209+
model = MOA.Optimizer(HiGHS.Optimizer)
210+
MOI.set(model, MOA.Algorithm(), MOA.Lexicographic())
211+
MOI.set(model, MOI.Silent(), true)
212+
x = MOI.add_variables(model, 4)
213+
MOI.add_constraint.(model, x, MOI.GreaterThan(0.0))
214+
MOI.add_constraint.(model, x, MOI.LessThan(1.0))
215+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
216+
f = MOI.Utilities.operate(vcat, Float64, P * x...)
217+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
218+
MOI.add_constraint(model, sum(1.0 * x[i] for i in 1:4), MOI.LessThan(2.0))
219+
@test_logs (:warn,) MOI.optimize!(model)
220+
@test MOI.get(model, MOI.ResultCount()) == 6
221+
results = [
222+
[0, 0, 1, 1, 2] => [0, 0, 1, 1],
223+
[0, 1, 0, 1, 2] => [0, 1, 0, 1],
224+
[0, 1, 1, 0, 2] => [0, 1, 1, 0],
225+
[1, 0, 0, 1, 2] => [1, 0, 0, 1],
226+
[1, 0, 1, 0, 2] => [1, 0, 1, 0],
227+
[1, 1, 0, 0, 2] => [1, 1, 0, 0],
228+
]
229+
for i in 1:MOI.get(model, MOI.ResultCount())
230+
X = round.(Int, MOI.get(model, MOI.VariablePrimal(i), x))
231+
Y = round.(Int, MOI.get(model, MOI.ObjectiveValue(i)))
232+
@test results[i] == (Y => X)
233+
end
234+
MOI.set(model, MOA.LexicographicAllPermutations(), true)
235+
@test_nowarn MOI.optimize!(model)
236+
for i in 1:MOI.get(model, MOI.ResultCount())
237+
X = round.(Int, MOI.get(model, MOI.VariablePrimal(i), x))
238+
Y = round.(Int, MOI.get(model, MOI.ObjectiveValue(i)))
239+
@test results[i] == (Y => X)
240+
end
241+
MOI.set(model, MOA.LexicographicAllPermutations(), false)
242+
@test_nowarn MOI.optimize!(model)
243+
@test MOI.get(model, MOI.ResultCount()) == 1
244+
X = round.(Int, MOI.get(model, MOI.VariablePrimal(1), x))
245+
Y = round.(Int, MOI.get(model, MOI.ObjectiveValue(1)))
246+
@test ([1, 1, 0, 0, 2] => [1, 1, 0, 0]) == (Y => X)
247+
return
248+
end
249+
201250
end # module TestLexicographic
202251

203252
TestLexicographic.run_tests()

0 commit comments

Comments
 (0)