Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ 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