Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ Query the number of scalar subproblems that were solved using

* `MOA.SubproblemCount()`

## Solution ordering

Results are lexicograhically ordered by their objective vectors. The order
depends on the objective sense. The first result is best.

## Ideal point

By default, MOA will compute the ideal point, which can be queried using the
Expand Down
8 changes: 5 additions & 3 deletions src/MultiObjectiveAlgorithms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ function dominates(
end
end

_sort!(solutions::Vector{SolutionPoint}) = sort!(solutions; by = x -> x.y)
function _sort!(solutions::Vector{SolutionPoint}, sense::MOI.OptimizationSense)
return sort!(solutions; by = x -> x.y, rev = sense == MOI.MAX_SENSE)
end

function filter_nondominated(
sense,
solutions::Vector{SolutionPoint};
atol::Float64 = 1e-6,
)
_sort!(solutions)
nondominated_solutions = SolutionPoint[]
for candidate in solutions
if any(test -> dominates(sense, test, candidate; atol), solutions)
Expand All @@ -60,6 +61,7 @@ function filter_nondominated(
push!(nondominated_solutions, candidate)
end
end
_sort!(nondominated_solutions, sense)
return nondominated_solutions
end

Expand Down Expand Up @@ -681,7 +683,7 @@ function _optimize!(model::Optimizer)
model.termination_status = status
if solutions !== nothing
model.solutions = solutions
_sort!(model.solutions)
_sort!(model.solutions, MOI.get(model, MOI.ObjectiveSense()))
end
if MOI.get(model, ComputeIdealPoint())
_compute_ideal_point(model, start_time)
Expand Down
1 change: 1 addition & 0 deletions test/algorithms/Chalmet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ function test_knapsack_max()
[0, 1, 1, 1, 1, 0, 1, 0, 1, 1] => [3043, 4627],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1] => [3395, 3817],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == length(results)
for (i, (x_sol, y_sol)) in enumerate(results)
@test ≈(x_sol, MOI.get(model, MOI.VariablePrimal(i), x); atol = 1e-6)
Expand Down
3 changes: 3 additions & 0 deletions test/algorithms/Dichotomy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ function test_moi_bolp_1_maximize()
@test MOI.get(model, MOI.ResultCount()) == 3
X = [[1.0, 0.25], [0.5, 0.5], [0.0, 1.0]]
Y = [[-2.25, -1.25], [-1.5, -1.5], [-1.0, -2.5]]
reverse!(X)
reverse!(Y)
for i in 1:3
@test MOI.get(model, MOI.PrimalStatus(i)) == MOI.FEASIBLE_POINT
@test MOI.get(model, MOI.DualStatus(i)) == MOI.NO_SOLUTION
Expand Down Expand Up @@ -227,6 +229,7 @@ function test_biobjective_knapsack()
[948.0, 939.0] => [1, 2, 3, 5, 6, 8, 10, 11, 15, 16, 17],
[955.0, 906.0] => [2, 3, 5, 6, 9, 10, 11, 14, 15, 16, 17],
]
reverse!(results)
for i in 1:MOI.get(model, MOI.ResultCount())
x_sol = MOI.get(model, MOI.VariablePrimal(i), x)
@test results[i][2] == findall(elt -> elt > 0.9, x_sol)
Expand Down
4 changes: 4 additions & 0 deletions test/algorithms/EpsilonConstraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function test_biobjective_knapsack()
[950, 915] => [1, 2, 5, 6, 8, 9, 10, 11, 15, 16, 17],
[956, 906] => [2, 3, 5, 6, 9, 10, 11, 14, 15, 16, 17],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == 9
for i in 1:MOI.get(model, MOI.ResultCount())
x_sol = MOI.get(model, MOI.VariablePrimal(i), x)
Expand Down Expand Up @@ -113,6 +114,7 @@ function test_biobjective_knapsack_atol()
[949, 915] => [1, 2, 5, 6, 8, 9, 10, 11, 15, 16, 17],
[955, 906] => [2, 3, 5, 6, 9, 10, 11, 14, 15, 16, 17],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == 9
for i in 1:MOI.get(model, MOI.ResultCount())
x_sol = MOI.get(model, MOI.VariablePrimal(i), x)
Expand Down Expand Up @@ -154,6 +156,7 @@ function test_biobjective_knapsack_atol_large()
[948, 939] => [1, 2, 3, 5, 6, 8, 10, 11, 15, 16, 17],
[955, 906] => [2, 3, 5, 6, 9, 10, 11, 14, 15, 16, 17],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == 4
for i in 1:MOI.get(model, MOI.ResultCount())
x_sol = MOI.get(model, MOI.VariablePrimal(i), x)
Expand Down Expand Up @@ -238,6 +241,7 @@ function test_biobjective_knapsack_min_solution_limit()
[943, 940] => [2, 3, 5, 6, 8, 9, 10, 11, 15, 16, 17],
[955, 906] => [2, 3, 5, 6, 9, 10, 11, 14, 15, 16, 17],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == 3
for i in 1:MOI.get(model, MOI.ResultCount())
x_sol = MOI.get(model, MOI.VariablePrimal(i), x)
Expand Down
2 changes: 2 additions & 0 deletions test/algorithms/Lexicographic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ function test_knapsack_default()
[1, 0, 1] => [1, 0, 0, 1],
[1, 1, 0] => [1, 1, 0, 0],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == 3
for i in 1:MOI.get(model, MOI.ResultCount())
X = round.(Int, MOI.get(model, MOI.VariablePrimal(i), x))
Expand Down Expand Up @@ -247,6 +248,7 @@ function test_knapsack_5_objectives()
[1, 0, 1, 0, 2] => [1, 0, 1, 0],
[1, 1, 0, 0, 2] => [1, 1, 0, 0],
]
reverse!(results)
for i in 1:MOI.get(model, MOI.ResultCount())
X = round.(Int, MOI.get(model, MOI.VariablePrimal(i), x))
Y = round.(Int, MOI.get(model, MOI.ObjectiveValue(i)))
Expand Down
1 change: 1 addition & 0 deletions test/algorithms/RandomWeighting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ function test_knapsack_max()
[0, 1, 1, 1, 1, 0, 1, 0, 1, 1] => [3043, 4627],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1] => [3395, 3817],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == length(results)
for (i, (x_sol, y_sol)) in enumerate(results)
@test (x_sol, MOI.get(model, MOI.VariablePrimal(i), x); atol = 1e-6)
Expand Down
6 changes: 4 additions & 2 deletions test/problems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ function test_problem_knapsack_max_p3(model)
[0, 1, 1, 1, 1, 0, 1, 0, 1, 1] => [3042, 4627, 3189],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1] => [3394, 3817, 3408],
]
reverse!(results)
N = MOI.get(model, MOI.ResultCount())
@assert N == length(results)
for i in 1:length(results)
Expand Down Expand Up @@ -223,6 +224,7 @@ function test_problem_knapsack_max_p4(model)
[0, 1, 1, 0, 1, 1, 1, 1, 1, 0] => [3152, 3232, 3596, 3382],
[1, 1, 1, 0, 1, 1, 1, 0, 0, 0] => [3269, 2320, 3059, 2891],
]
reverse!(results)
@test MOI.get(model, MOI.ResultCount()) == length(results)
for (i, (x_sol, y_sol)) in enumerate(results)
@test ≈(x_sol, MOI.get(model, MOI.VariablePrimal(i), x); atol = 1e-6)
Expand Down Expand Up @@ -368,7 +370,7 @@ function test_problem_assignment_max_p3(model)
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.optimize!(model)
results = reverse([
results = [
[0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1] => [16, 61, 47],
[0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1] => [17, 43, 71],
[0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1] => [18, 47, 67],
Expand All @@ -390,7 +392,7 @@ function test_problem_assignment_max_p3(model)
[0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0] => [43, 51, 31],
[0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0] => [45, 33, 34],
[0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0] => [50, 40, 32],
])
]
@test MOI.get(model, MOI.ResultCount()) == length(results)
@test MOI.get(model, MOA.SubproblemCount()) >= length(results)
for (i, (x_sol, y_sol)) in enumerate(results)
Expand Down
13 changes: 7 additions & 6 deletions test/test_utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ function test_filter_nondominated()
x = Dict{MOI.VariableIndex,Float64}()
solutions = [MOA.SolutionPoint(x, [0, 1]), MOA.SolutionPoint(x, [1, 0])]
@test MOA.filter_nondominated(MOI.MIN_SENSE, solutions) == solutions
@test MOA.filter_nondominated(MOI.MAX_SENSE, solutions) == solutions
@test MOA.filter_nondominated(MOI.MAX_SENSE, solutions) ==
reverse(solutions)
return
end

Expand All @@ -34,7 +35,7 @@ function test_filter_nondominated_sort_in_order()
solutions = [MOA.SolutionPoint(x, [0, 1]), MOA.SolutionPoint(x, [1, 0])]
r_solutions = reverse(solutions)
@test MOA.filter_nondominated(MOI.MIN_SENSE, r_solutions) == solutions
@test MOA.filter_nondominated(MOI.MAX_SENSE, r_solutions) == solutions
@test MOA.filter_nondominated(MOI.MAX_SENSE, r_solutions) == r_solutions
return
end

Expand All @@ -55,7 +56,7 @@ function test_filter_nondominated_weakly_dominated()
MOA.SolutionPoint(x, [1, 0]),
]
@test MOA.filter_nondominated(MOI.MIN_SENSE, solutions) == solutions[[1, 3]]
@test MOA.filter_nondominated(MOI.MAX_SENSE, solutions) == solutions[[2, 3]]
@test MOA.filter_nondominated(MOI.MAX_SENSE, solutions) == solutions[[3, 2]]
solutions = [
MOA.SolutionPoint(x, [0, 1]),
MOA.SolutionPoint(x, [0.5, 1]),
Expand All @@ -67,7 +68,7 @@ function test_filter_nondominated_weakly_dominated()
@test MOA.filter_nondominated(MOI.MIN_SENSE, solutions) ==
solutions[[1, 4, 6]]
@test MOA.filter_nondominated(MOI.MAX_SENSE, solutions) ==
solutions[[3, 5, 6]]
solutions[[6, 5, 3]]
return
end

Expand All @@ -82,7 +83,7 @@ function test_filter_nondominated_knapsack()
]
result = solutions[[1, 3, 4]]
@test MOA.filter_nondominated(MOI.MIN_SENSE, solutions) == result
@test MOA.filter_nondominated(MOI.MAX_SENSE, solutions) == result
@test MOA.filter_nondominated(MOI.MAX_SENSE, solutions) == reverse(result)
return
end

Expand Down Expand Up @@ -115,7 +116,7 @@ function test_filter_epsilon()
solutions =
[MOA.SolutionPoint(x, [1, 1 + 9e-5]), MOA.SolutionPoint(x, [2, 1])]
new_solutions = MOA.filter_nondominated(MOI.MAX_SENSE, copy(solutions))
@test new_solutions == solutions
@test new_solutions == reverse(solutions)
solutions =
[MOA.SolutionPoint(x, [-1, -1 - 1e-6]), MOA.SolutionPoint(x, [-2, -1])]
new_solutions = MOA.filter_nondominated(MOI.MIN_SENSE, copy(solutions))
Expand Down
Loading