Skip to content

Commit

Permalink
Fix and add tests for constant offset in objective function (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Nov 29, 2023
1 parent bc0239d commit cab13bf
Show file tree
Hide file tree
Showing 14 changed files with 77 additions and 40 deletions.
20 changes: 13 additions & 7 deletions src/algorithms/Chalmet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ function _solve_constrained_model(
f = MOI.Utilities.scalarize(model.f)
g = sum(1.0 * fi for fi in f)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(g)}(), g)
constraints = [
MOI.add_constraint(model.inner, f[1], MOI.LessThan(rhs[1] - 1))
MOI.add_constraint(model.inner, f[2], MOI.LessThan(rhs[2] - 1))
]
sets = MOI.LessThan.(rhs .- 1)
c = MOI.Utilities.normalize_and_add_constraint.(model.inner, f, sets)
MOI.optimize!(model.inner)
MOI.delete.(model, constraints)
MOI.delete.(model, c)
status = MOI.get(model.inner, MOI.TerminationStatus())
if !_is_scalar_status_optimal(status)
return status, nothing
Expand Down Expand Up @@ -74,7 +72,11 @@ function optimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
end
_, y1[2] = _compute_point(model, variables, f2)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
y1_constraint = MOI.add_constraint(model.inner, f2, MOI.LessThan(y1[2]))
y1_constraint = MOI.Utilities.normalize_and_add_constraint(
model.inner,
f2,
MOI.LessThan(y1[2]),
)
MOI.optimize!(model.inner)
x1, y1[1] = _compute_point(model, variables, f1)
MOI.delete(model.inner, y1_constraint)
Expand All @@ -90,7 +92,11 @@ function optimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
return MOI.OPTIMAL, [solutions]
end
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
y2_constraint = MOI.add_constraint(model.inner, f1, MOI.LessThan(y2[1]))
y2_constraint = MOI.Utilities.normalize_and_add_constraint(
model.inner,
f1,
MOI.LessThan(y2[1]),
)
MOI.optimize!(model.inner)
x2, y2[2] = _compute_point(model, variables, f2)
MOI.delete(model.inner, y2_constraint)
Expand Down
13 changes: 10 additions & 3 deletions src/algorithms/EpsilonConstraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,14 @@ function optimize_multiobjective!(
else
MOI.GreaterThan{Float64}, left
end
ci = MOI.add_constraint(model, f1, SetType(bound))
constant = MOI.constant(f1, Float64)
ci = MOI.Utilities.normalize_and_add_constraint(
model,
f1,
SetType(bound);
allow_modify_function = true,
)
bound -= constant
status = MOI.OPTIMAL
for _ in 1:n_points
if _time_limit_exceeded(model, start_time)
Expand All @@ -121,9 +128,9 @@ function optimize_multiobjective!(
push!(solutions, SolutionPoint(X, Y))
end
if sense == MOI.MIN_SENSE
bound = min(Y[1] - ε, bound - ε)
bound = min(Y[1] - constant - ε, bound - ε)
else
bound = max(Y[1] + ε, bound + ε)
bound = max(Y[1] - constant + ε, bound + ε)
end
end
MOI.delete(model, ci)
Expand Down
3 changes: 2 additions & 1 deletion src/algorithms/Hierarchical.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ function optimize_multiobjective!(algorithm::Hierarchical, model::Optimizer)
else
MOI.GreaterThan(Y[i] - rtol * abs(Y[i]))
end
push!(constraints, MOI.add_constraint(model, fi, set))
ci = MOI.Utilities.normalize_and_add_constraint(model, fi, set)
push!(constraints, ci)
end
end
X, Y = _compute_point(model, variables, model.f)
Expand Down
13 changes: 10 additions & 3 deletions src/algorithms/KirlikSayin.jl
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ function optimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
)
for (i, f_i) in enumerate(scalars)
if i != k
ci = MOI.add_constraint(model.inner, f_i, SetType(ε[i] + δ))
ci = MOI.Utilities.normalize_and_add_constraint(
model.inner,
f_i,
SetType(ε[i] + δ),
)
push!(ε_constraints, ci)
end
end
Expand All @@ -168,8 +172,11 @@ function optimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
sum_f = sum(1.0 * s for s in scalars)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(sum_f)}(), sum_f)
# Constraint to eliminate weak dominance
zₖ_constraint =
MOI.add_constraint(model.inner, scalars[k], MOI.EqualTo(zₖ))
zₖ_constraint = MOI.Utilities.normalize_and_add_constraint(
model.inner,
scalars[k],
MOI.EqualTo(zₖ),
)
MOI.optimize!(model.inner)
MOI.delete.(model, ε_constraints)
MOI.delete(model, zₖ_constraint)
Expand Down
3 changes: 2 additions & 1 deletion src/algorithms/Lexicographic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ function _solve_in_sequence(
else
MOI.GreaterThan(Y - rtol * abs(Y))
end
push!(constraints, MOI.add_constraint(model, f, set))
ci = MOI.Utilities.normalize_and_add_constraint(model, f, set)
push!(constraints, ci)
end
for c in constraints
MOI.delete(model, c)
Expand Down
9 changes: 6 additions & 3 deletions src/algorithms/TambyVanderpooten.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function optimize_multiobjective!(
ε_constraints = Any[]
for (i, f_i) in enumerate(scalars)
if i != k
ci = MOI.add_constraint(
ci = MOI.Utilities.normalize_and_add_constraint(
model.inner,
f_i,
MOI.LessThan{Float64}(u[i] - 1),
Expand Down Expand Up @@ -171,8 +171,11 @@ function optimize_multiobjective!(
y_k = MOI.get(model.inner, MOI.ObjectiveValue())
sum_f = sum(1.0 * s for s in scalars)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(sum_f)}(), sum_f)
y_k_constraint =
MOI.add_constraint(model.inner, scalars[k], MOI.EqualTo(y_k))
y_k_constraint = MOI.Utilities.normalize_and_add_constraint(
model.inner,
scalars[k],
MOI.EqualTo(y_k),
)
MOI.optimize!(model.inner)
if !_is_scalar_status_optimal(model)
return status, nothing
Expand Down
8 changes: 4 additions & 4 deletions test/algorithms/Chalmet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function test_knapsack_max()
MOI.VectorAffineTerm(i, MOI.ScalarAffineTerm(C[i, j], x[j])) for
i in 1:2 for j in 1:n
],
[0.0, 0.0],
[1.0, 0.0],
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
Expand All @@ -109,9 +109,9 @@ function test_knapsack_max()
0 1 1 1 1 0 1 0 1 1
]
Y_N = Float64[
2854 4636
3394 3817
3042 4627
2855 4636
3395 3817
3043 4627
]
N = MOI.get(model, MOI.ResultCount())
x_sol = hcat([MOI.get(model, MOI.VariablePrimal(i), x) for i in 1:N]...)
Expand Down
12 changes: 6 additions & 6 deletions test/algorithms/Dichotomy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function test_moi_bolp_1()
model,
"""
variables: x, y
minobjective: [2 * x + y, x + 3 * y]
minobjective: [2 * x + y + 1, x + 3 * y]
c1: x + y >= 1.0
c2: 0.5 * x + y >= 0.75
c3: x >= 0.0
Expand All @@ -60,15 +60,15 @@ c4: y >= 0.25
@test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
@test MOI.get(model, MOI.ResultCount()) == 3
X = [[0.0, 1.0], [0.5, 0.5], [1.0, 0.25]]
Y = [[1.0, 3.0], [1.5, 2.0], [2.25, 1.75]]
Y = [[2.0, 3.0], [2.5, 2.0], [3.25, 1.75]]
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
@test MOI.get(model, MOI.ObjectiveValue(i)) == Y[i]
@test MOI.get(model, MOI.VariablePrimal(i), x) == X[i][1]
@test MOI.get(model, MOI.VariablePrimal(i), y) == X[i][2]
end
@test MOI.get(model, MOI.ObjectiveBound()) == [1.0, 1.75]
@test MOI.get(model, MOI.ObjectiveBound()) == [2.0, 1.75]
return
end

Expand All @@ -83,7 +83,7 @@ function test_moi_bolp_1_maximize()
model,
"""
variables: x, y
maxobjective: [-2.0 * x + -1.0 * y, -1.0 * x + -3.0 * y]
maxobjective: [-2.0 * x + -1.0 * y, -1.0 * x + -3.0 * y + 0.5]
c1: x + y >= 1.0
c2: 0.5 * x + y >= 0.75
c3: x >= 0.0
Expand All @@ -96,15 +96,15 @@ c4: y >= 0.25
@test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMAL
@test MOI.get(model, MOI.ResultCount()) == 3
X = [[0.0, 1.0], [0.5, 0.5], [1.0, 0.25]]
Y = [-[1.0, 3.0], -[1.5, 2.0], -[2.25, 1.75]]
Y = [-[1.0, 2.5], -[1.5, 1.5], -[2.25, 1.25]]
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
@test MOI.get(model, MOI.ObjectiveValue(i)) == Y[i]
@test MOI.get(model, MOI.VariablePrimal(i), x) == X[i][1]
@test MOI.get(model, MOI.VariablePrimal(i), y) == X[i][2]
end
@test MOI.get(model, MOI.ObjectiveBound()) == -[1.0, 1.75]
@test MOI.get(model, MOI.ObjectiveBound()) == -[1.0, 1.25]
return
end

Expand Down
3 changes: 2 additions & 1 deletion test/algorithms/DominguezRios.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function test_knapsack_min_p3()
MOI.VectorAffineTerm(i, MOI.ScalarAffineTerm(-C[i, j], x[j]))
for i in 1:p for j in 1:n
],
fill(0.0, p),
ones(p),
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
Expand All @@ -77,6 +77,7 @@ function test_knapsack_min_p3()
-2997 -3539 -3509
-2518 -3866 -3191
]
Y_N .+= 1
N = MOI.get(model, MOI.ResultCount())
x_sol = hcat([MOI.get(model, MOI.VariablePrimal(i), x) for i in 1:N]...)
@test isapprox(sort(x_sol; dims = 1), sort(X_E'; dims = 1); atol = 1e-6)
Expand Down
19 changes: 10 additions & 9 deletions test/algorithms/EpsilonConstraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function test_biobjective_knapsack()
Float64,
[sum(1.0 * p[i] * x[i] for i in 1:length(w)) for p in [p1, p2]]...,
)
f.constants[1] = 1.0
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.add_constraint(
model,
Expand All @@ -48,15 +49,15 @@ function test_biobjective_knapsack()
)
MOI.optimize!(model)
results = Dict(
[955, 906] => [2, 3, 5, 6, 9, 10, 11, 14, 15, 16, 17],
[949, 915] => [1, 2, 5, 6, 8, 9, 10, 11, 15, 16, 17],
[948, 939] => [1, 2, 3, 5, 6, 8, 10, 11, 15, 16, 17],
[943, 940] => [2, 3, 5, 6, 8, 9, 10, 11, 15, 16, 17],
[936, 942] => [1, 2, 3, 5, 6, 10, 11, 12, 15, 16, 17],
[935, 947] => [2, 5, 6, 8, 9, 10, 11, 12, 15, 16, 17],
[934, 971] => [2, 3, 5, 6, 8, 10, 11, 12, 15, 16, 17],
[927, 972] => [2, 3, 5, 6, 8, 9, 10, 11, 12, 16, 17],
[918, 983] => [2, 3, 4, 5, 6, 8, 10, 11, 12, 16, 17],
[956, 906] => [2, 3, 5, 6, 9, 10, 11, 14, 15, 16, 17],
[950, 915] => [1, 2, 5, 6, 8, 9, 10, 11, 15, 16, 17],
[949, 939] => [1, 2, 3, 5, 6, 8, 10, 11, 15, 16, 17],
[944, 940] => [2, 3, 5, 6, 8, 9, 10, 11, 15, 16, 17],
[937, 942] => [1, 2, 3, 5, 6, 10, 11, 12, 15, 16, 17],
[936, 947] => [2, 5, 6, 8, 9, 10, 11, 12, 15, 16, 17],
[935, 971] => [2, 3, 5, 6, 8, 10, 11, 12, 15, 16, 17],
[928, 972] => [2, 3, 5, 6, 8, 9, 10, 11, 12, 16, 17],
[919, 983] => [2, 3, 4, 5, 6, 8, 10, 11, 12, 16, 17],
)
@test MOI.get(model, MOI.ResultCount()) == 9
for i in 1:MOI.get(model, MOI.ResultCount())
Expand Down
4 changes: 4 additions & 0 deletions test/algorithms/Hierarchical.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,15 @@ function test_knapsack()
MOI.add_constraint.(model, x, MOI.LessThan(1.0))
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
f = MOI.Utilities.operate(vcat, Float64, P * x...)
f.constants[4] = 1_000.0
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.add_constraint(model, sum(1.0 * x[i] for i in 1:4), MOI.LessThan(2.0))
MOI.optimize!(model)
@test MOI.get(model, MOI.ResultCount()) == 1
x_sol = MOI.get(model, MOI.VariablePrimal(), x)
@test (x_sol, [0.9, 0, 0.9, 0.2]; atol = 1e-3)
y_sol = MOI.get(model, MOI.ObjectiveValue())
@test (y_sol, P * x_sol .+ [0.0, 0.0, 0.0, 1_000.0]; atol = 1e-4)
return
end

Expand Down
3 changes: 2 additions & 1 deletion test/algorithms/KirlikSayin.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function test_knapsack_min_p3()
MOI.VectorAffineTerm(i, MOI.ScalarAffineTerm(-C[i, j], x[j]))
for i in 1:p for j in 1:n
],
fill(0.0, p),
ones(p),
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
Expand All @@ -74,6 +74,7 @@ function test_knapsack_min_p3()
-2518 -3866 -3191
-2854 -4636 -3076
]
Y_N .+= 1
N = MOI.get(model, MOI.ResultCount())
x_sol = hcat([MOI.get(model, MOI.VariablePrimal(i), x) for i in 1:N]...)
@test isapprox(sort(x_sol; dims = 1), sort(X_E'; dims = 1); atol = 1e-6)
Expand Down
4 changes: 4 additions & 0 deletions test/algorithms/Lexicographic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ function test_knapsack()
MOI.add_constraint.(model, x, MOI.LessThan(1.0))
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
f = MOI.Utilities.operate(vcat, Float64, P * x...)
f.constants[4] = 1_000.0
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.add_constraint(model, sum(1.0 * x[i] for i in 1:4), MOI.LessThan(2.0))
MOI.optimize!(model)
@test MOI.get(model, MOI.ResultCount()) == 1
x_sol = MOI.get(model, MOI.VariablePrimal(), x)
@test (x_sol, [0.9, 1, 0, 0.1]; atol = 1e-3)
y_sol = MOI.get(model, MOI.ObjectiveValue())
@test (y_sol, P * x_sol .+ [0.0, 0.0, 0.0, 1_000.0]; atol = 1e-4)
return
end

Expand Down
3 changes: 2 additions & 1 deletion test/algorithms/TambyVanderpooten.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function test_knapsack_min_p3()
MOI.VectorAffineTerm(i, MOI.ScalarAffineTerm(-C[i, j], x[j]))
for i in 1:p for j in 1:n
],
fill(0.0, p),
ones(p),
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
Expand All @@ -74,6 +74,7 @@ function test_knapsack_min_p3()
-2518 -3866 -3191
-2854 -4636 -3076
]
Y_N .+= 1
N = MOI.get(model, MOI.ResultCount())
x_sol = hcat([MOI.get(model, MOI.VariablePrimal(i), x) for i in 1:N]...)'
y_sol = hcat([MOI.get(model, MOI.ObjectiveValue(i)) for i in 1:N]...)'
Expand Down

0 comments on commit cab13bf

Please sign in to comment.