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
43 changes: 22 additions & 21 deletions ext/MultiObjectiveAlgorithmsPolyhedraExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ end
function MOA.minimize_multiobjective!(
algorithm::MOA.Sandwiching,
model::MOA.Optimizer,
inner::MOI.ModelLike,
f::MOI.AbstractVectorFunction,
)
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
@assert MOI.get(inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
solutions = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
n = MOI.output_dimension(model.f)
scalars = MOI.Utilities.scalarize(model.f)
variables = MOI.get(inner, MOI.ListOfVariableIndices())
n = MOI.output_dimension(f)
scalars = MOI.Utilities.scalarize(f)
status = MOI.OPTIMAL
δ_OPS_optimizer = MOI.instantiate(model.optimizer_factory)
if MOI.supports(δ_OPS_optimizer, MOI.Silent())
Expand All @@ -52,26 +54,26 @@ function MOA.minimize_multiobjective!(
anchors = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
yI, yUB = zeros(n), zeros(n)
for (i, f_i) in enumerate(scalars)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
MOI.set(inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
MOA.optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !MOA._is_scalar_status_optimal(model)
return status, nothing
end
X, Y = MOA._compute_point(model, variables, model.f)
X, Y = MOA._compute_point(model, variables, f)
model.ideal_point[i] = Y[i]
yI[i] = Y[i]
anchors[Y] = X
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOI.set(inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOA.optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !MOA._is_scalar_status_optimal(model)
MOA._warn_on_nonfinite_anti_ideal(algorithm, MOI.MIN_SENSE, i)
return status, nothing
end
_, Y = MOA._compute_point(model, variables, f_i)
yUB[i] = Y
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
e_i = Float64.(1:n .== i)
MOI.add_constraint(
δ_OPS_optimizer,
Expand All @@ -86,14 +88,13 @@ function MOA.minimize_multiobjective!(
end
IPS = [yUB, keys(anchors)...]
merge!(solutions, anchors)
u = MOI.add_variables(model.inner, n)
u = MOI.add_variables(inner, n)
u_constraints = [ # u_i >= 0 for all i = 1:n
MOI.add_constraint(model.inner, u_i, MOI.GreaterThan{Float64}(0))
for u_i in u
MOI.add_constraint(inner, u_i, MOI.GreaterThan{Float64}(0)) for u_i in u
]
f_constraints = [ # f_i + u_i <= yUB_i for all i = 1:n
MOI.Utilities.normalize_and_add_constraint(
model.inner,
inner,
scalars[i] + u[i],
MOI.LessThan(yUB[i]),
) for i in 1:n
Expand All @@ -113,14 +114,14 @@ function MOA.minimize_multiobjective!(
end
# would not terminate when precision is set to 0
new_f = sum(w[i] * (scalars[i] + u[i]) for i in 1:n) # w' * (f(x) + u)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
MOI.set(inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
MOA.optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !MOA._is_scalar_status_optimal(model)
return status, nothing
end
β̄ = MOI.get(model.inner, MOI.ObjectiveValue())
X, Y = MOA._compute_point(model, variables, model.f)
β̄ = MOI.get(inner, MOI.ObjectiveValue())
X, Y = MOA._compute_point(model, variables, f)
solutions[Y] = X
MOI.add_constraint(
δ_OPS_optimizer,
Expand All @@ -130,9 +131,9 @@ function MOA.minimize_multiobjective!(
IPS = push!(IPS, Y)
H = _halfspaces(IPS)
end
MOI.delete.(model.inner, f_constraints)
MOI.delete.(model.inner, u_constraints)
MOI.delete.(model.inner, u)
MOI.delete.(inner, f_constraints)
MOI.delete.(inner, u_constraints)
MOI.delete.(inner, u)
return status, [MOA.SolutionPoint(X, Y) for (Y, X) in solutions]
end

Expand Down
4 changes: 3 additions & 1 deletion src/MultiObjectiveAlgorithms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,9 @@ This function is part of the public developer API. You should not call it from
user-facing code. You may use it when implementing new algorithms in third-party
packages.
"""
function minimize_multiobjective! end
function minimize_multiobjective!(alg::AbstractAlgorithm, model::Optimizer)
return minimize_multiobjective!(alg, model, model.inner, model.f)
end

"""
optimize_multiobjective!(
Expand Down
60 changes: 34 additions & 26 deletions src/algorithms/Chalmet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,66 +26,73 @@ This algorithm is restricted to problems with:
struct Chalmet <: AbstractAlgorithm end

function _solve_constrained_model(
model::Optimizer,
::Chalmet,
model::Optimizer,
inner::MOI.ModelLike,
f::MOI.AbstractVectorFunction,
rhs::Vector{Float64},
)
f = MOI.Utilities.scalarize(model.f)
g = sum(1.0 * fi for fi in f)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(g)}(), g)
f_scalars = MOI.Utilities.scalarize(model.f)
g = MOI.Utilities.operate(+, Float64, f_scalars...)
MOI.set(inner, MOI.ObjectiveFunction{typeof(g)}(), g)
sets = MOI.LessThan.(rhs .- 1)
c = MOI.Utilities.normalize_and_add_constraint.(model.inner, f, sets)
c = MOI.Utilities.normalize_and_add_constraint.(inner, f_scalars, sets)
optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !_is_scalar_status_optimal(status)
_log_subproblem_solve(model, "subproblem not optimal")
MOI.delete.(model, c)
return status, nothing
end
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
X, Y = _compute_point(model, variables, model.f)
variables = MOI.get(inner, MOI.ListOfVariableIndices())
X, Y = _compute_point(model, variables, f)
_log_subproblem_solve(model, Y)
MOI.delete.(model, c)
return status, SolutionPoint(X, Y)
end

function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
if MOI.output_dimension(model.f) != 2
function minimize_multiobjective!(
algorithm::Chalmet,
model::Optimizer,
inner::MOI.ModelLike,
f::MOI.AbstractVectorFunction,
)
@assert MOI.get(inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
if MOI.output_dimension(f) != 2
error("Chalmet requires exactly two objectives")
end
solutions = SolutionPoint[]
E = Tuple{Int,Int}[]
Q = Tuple{Int,Int}[]
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
f1, f2 = MOI.Utilities.scalarize(model.f)
variables = MOI.get(inner, MOI.ListOfVariableIndices())
f1, f2 = MOI.Utilities.scalarize(f)
y1, y2 = zeros(2), zeros(2)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !_is_scalar_status_optimal(status)
return status, solutions
end
_, y1[2] = _compute_point(model, variables, f2)
_log_subproblem_solve(model, variables)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
MOI.set(inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
y1_constraint = MOI.Utilities.normalize_and_add_constraint(
model.inner,
inner,
f2,
MOI.LessThan(y1[2]),
)
optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !_is_scalar_status_optimal(status)
return status, solutions
end
x1, y1[1] = _compute_point(model, variables, f1)
_log_subproblem_solve(model, y1)
MOI.delete(model.inner, y1_constraint)
MOI.delete(inner, y1_constraint)
push!(solutions, SolutionPoint(x1, y1))
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
MOI.set(inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !_is_scalar_status_optimal(status)
return status, solutions
end
Expand All @@ -94,20 +101,20 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
if y2[1] ≈ solutions[1].y[1]
return MOI.OPTIMAL, solutions
end
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
y2_constraint = MOI.Utilities.normalize_and_add_constraint(
model.inner,
inner,
f1,
MOI.LessThan(y2[1]),
)
optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !_is_scalar_status_optimal(status)
return status, solutions
end
x2, y2[2] = _compute_point(model, variables, f2)
_log_subproblem_solve(model, y2)
MOI.delete(model.inner, y2_constraint)
MOI.delete(inner, y2_constraint)
push!(solutions, SolutionPoint(x2, y2))
push!(Q, (1, 2))
t = 3
Expand All @@ -118,7 +125,8 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
r, s = pop!(Q)
yr, ys = solutions[r].y, solutions[s].y
rhs = [max(yr[1], ys[1]), max(yr[2], ys[2])]
status, solution = _solve_constrained_model(model, algorithm, rhs)
status, solution =
_solve_constrained_model(algorithm, model, inner, f, rhs)
if !_is_scalar_status_optimal(status)
push!(E, (r, s))
continue
Expand Down
39 changes: 26 additions & 13 deletions src/algorithms/Dichotomy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,37 +61,49 @@ function MOI.get(alg::Dichotomy, attr::SolutionLimit)
end

function _solve_weighted_sum(
model::Optimizer,
::Dichotomy,
model::Optimizer,
inner::MOI.ModelLike,
f::MOI.AbstractVectorFunction,
weights::Vector{Float64},
)
f = _scalarise(model.f, weights)
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f)
f_scalar = _scalarise(f, weights)
MOI.set(inner, MOI.ObjectiveFunction{typeof(f_scalar)}(), f_scalar)
optimize_inner!(model)
status = MOI.get(model.inner, MOI.TerminationStatus())
status = MOI.get(inner, MOI.TerminationStatus())
if !_is_scalar_status_optimal(status)
return status, nothing
end
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
X, Y = _compute_point(model, variables, model.f)
variables = MOI.get(inner, MOI.ListOfVariableIndices())
X, Y = _compute_point(model, variables, f)
_log_subproblem_solve(model, Y)
return status, SolutionPoint(X, Y)
end

function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer)
if MOI.output_dimension(model.f) > 2
error("Only scalar or bi-objective problems supported.")
end
if MOI.output_dimension(model.f) == 1
return optimize_multiobjective!(algorithm, model, model.inner, model.f)
end

function optimize_multiobjective!(
algorithm::Dichotomy,
model::Optimizer,
inner::MOI.ModelLike,
f::MOI.AbstractVectorFunction,
)
if MOI.output_dimension(f) == 1
if (ret = _check_premature_termination(model)) !== nothing
return ret, nothing
end
status, solution = _solve_weighted_sum(model, algorithm, [1.0])
status, solution =
_solve_weighted_sum(algorithm, model, inner, f, [1.0])
return status, [solution]
elseif MOI.output_dimension(f) > 2
error("Only scalar or bi-objective problems supported.")
end
solutions = Dict{Float64,SolutionPoint}()
for (i, w) in (1 => 1.0, 2 => 0.0)
status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w])
status, solution =
_solve_weighted_sum(algorithm, model, inner, f, [w, 1.0 - w])
if !_is_scalar_status_optimal(status)
return status, nothing
end
Expand All @@ -113,7 +125,8 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer)
(a, b) = popfirst!(queue)
y_d = solutions[a].y .- solutions[b].y
w = y_d[2] / (y_d[2] - y_d[1])
status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w])
status, solution =
_solve_weighted_sum(algorithm, model, inner, f, [w, 1.0 - w])
if !_is_scalar_status_optimal(status)
break # Exit the solve with some error.
elseif solution ≈ solutions[a] || solution ≈ solutions[b]
Expand Down
Loading
Loading