Skip to content

Commit da4c7cf

Browse files
authored
Add function barriers to all algorithms (#171)
1 parent 067521e commit da4c7cf

11 files changed

+198
-138
lines changed

ext/MultiObjectiveAlgorithmsPolyhedraExt.jl

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ end
3737
function MOA.minimize_multiobjective!(
3838
algorithm::MOA.Sandwiching,
3939
model::MOA.Optimizer,
40+
inner::MOI.ModelLike,
41+
f::MOI.AbstractVectorFunction,
4042
)
41-
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
43+
@assert MOI.get(inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
4244
solutions = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
43-
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
44-
n = MOI.output_dimension(model.f)
45-
scalars = MOI.Utilities.scalarize(model.f)
45+
variables = MOI.get(inner, MOI.ListOfVariableIndices())
46+
n = MOI.output_dimension(f)
47+
scalars = MOI.Utilities.scalarize(f)
4648
status = MOI.OPTIMAL
4749
δ_OPS_optimizer = MOI.instantiate(model.optimizer_factory)
4850
if MOI.supports(δ_OPS_optimizer, MOI.Silent())
@@ -52,26 +54,26 @@ function MOA.minimize_multiobjective!(
5254
anchors = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
5355
yI, yUB = zeros(n), zeros(n)
5456
for (i, f_i) in enumerate(scalars)
55-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
57+
MOI.set(inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
5658
MOA.optimize_inner!(model)
57-
status = MOI.get(model.inner, MOI.TerminationStatus())
59+
status = MOI.get(inner, MOI.TerminationStatus())
5860
if !MOA._is_scalar_status_optimal(model)
5961
return status, nothing
6062
end
61-
X, Y = MOA._compute_point(model, variables, model.f)
63+
X, Y = MOA._compute_point(model, variables, f)
6264
model.ideal_point[i] = Y[i]
6365
yI[i] = Y[i]
6466
anchors[Y] = X
65-
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
67+
MOI.set(inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
6668
MOA.optimize_inner!(model)
67-
status = MOI.get(model.inner, MOI.TerminationStatus())
69+
status = MOI.get(inner, MOI.TerminationStatus())
6870
if !MOA._is_scalar_status_optimal(model)
6971
MOA._warn_on_nonfinite_anti_ideal(algorithm, MOI.MIN_SENSE, i)
7072
return status, nothing
7173
end
7274
_, Y = MOA._compute_point(model, variables, f_i)
7375
yUB[i] = Y
74-
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
76+
MOI.set(inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
7577
e_i = Float64.(1:n .== i)
7678
MOI.add_constraint(
7779
δ_OPS_optimizer,
@@ -86,14 +88,13 @@ function MOA.minimize_multiobjective!(
8688
end
8789
IPS = [yUB, keys(anchors)...]
8890
merge!(solutions, anchors)
89-
u = MOI.add_variables(model.inner, n)
91+
u = MOI.add_variables(inner, n)
9092
u_constraints = [ # u_i >= 0 for all i = 1:n
91-
MOI.add_constraint(model.inner, u_i, MOI.GreaterThan{Float64}(0))
92-
for u_i in u
93+
MOI.add_constraint(inner, u_i, MOI.GreaterThan{Float64}(0)) for u_i in u
9394
]
9495
f_constraints = [ # f_i + u_i <= yUB_i for all i = 1:n
9596
MOI.Utilities.normalize_and_add_constraint(
96-
model.inner,
97+
inner,
9798
scalars[i] + u[i],
9899
MOI.LessThan(yUB[i]),
99100
) for i in 1:n
@@ -113,14 +114,14 @@ function MOA.minimize_multiobjective!(
113114
end
114115
# would not terminate when precision is set to 0
115116
new_f = sum(w[i] * (scalars[i] + u[i]) for i in 1:n) # w' * (f(x) + u)
116-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
117+
MOI.set(inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
117118
MOA.optimize_inner!(model)
118-
status = MOI.get(model.inner, MOI.TerminationStatus())
119+
status = MOI.get(inner, MOI.TerminationStatus())
119120
if !MOA._is_scalar_status_optimal(model)
120121
return status, nothing
121122
end
122-
β̄ = MOI.get(model.inner, MOI.ObjectiveValue())
123-
X, Y = MOA._compute_point(model, variables, model.f)
123+
β̄ = MOI.get(inner, MOI.ObjectiveValue())
124+
X, Y = MOA._compute_point(model, variables, f)
124125
solutions[Y] = X
125126
MOI.add_constraint(
126127
δ_OPS_optimizer,
@@ -130,9 +131,9 @@ function MOA.minimize_multiobjective!(
130131
IPS = push!(IPS, Y)
131132
H = _halfspaces(IPS)
132133
end
133-
MOI.delete.(model.inner, f_constraints)
134-
MOI.delete.(model.inner, u_constraints)
135-
MOI.delete.(model.inner, u)
134+
MOI.delete.(inner, f_constraints)
135+
MOI.delete.(inner, u_constraints)
136+
MOI.delete.(inner, u)
136137
return status, [MOA.SolutionPoint(X, Y) for (Y, X) in solutions]
137138
end
138139

src/MultiObjectiveAlgorithms.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,9 @@ This function is part of the public developer API. You should not call it from
701701
user-facing code. You may use it when implementing new algorithms in third-party
702702
packages.
703703
"""
704-
function minimize_multiobjective! end
704+
function minimize_multiobjective!(alg::AbstractAlgorithm, model::Optimizer)
705+
return minimize_multiobjective!(alg, model, model.inner, model.f)
706+
end
705707

706708
"""
707709
optimize_multiobjective!(

src/algorithms/Chalmet.jl

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,66 +26,73 @@ This algorithm is restricted to problems with:
2626
struct Chalmet <: AbstractAlgorithm end
2727

2828
function _solve_constrained_model(
29-
model::Optimizer,
3029
::Chalmet,
30+
model::Optimizer,
31+
inner::MOI.ModelLike,
32+
f::MOI.AbstractVectorFunction,
3133
rhs::Vector{Float64},
3234
)
33-
f = MOI.Utilities.scalarize(model.f)
34-
g = sum(1.0 * fi for fi in f)
35-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(g)}(), g)
35+
f_scalars = MOI.Utilities.scalarize(model.f)
36+
g = MOI.Utilities.operate(+, Float64, f_scalars...)
37+
MOI.set(inner, MOI.ObjectiveFunction{typeof(g)}(), g)
3638
sets = MOI.LessThan.(rhs .- 1)
37-
c = MOI.Utilities.normalize_and_add_constraint.(model.inner, f, sets)
39+
c = MOI.Utilities.normalize_and_add_constraint.(inner, f_scalars, sets)
3840
optimize_inner!(model)
39-
status = MOI.get(model.inner, MOI.TerminationStatus())
41+
status = MOI.get(inner, MOI.TerminationStatus())
4042
if !_is_scalar_status_optimal(status)
4143
_log_subproblem_solve(model, "subproblem not optimal")
4244
MOI.delete.(model, c)
4345
return status, nothing
4446
end
45-
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
46-
X, Y = _compute_point(model, variables, model.f)
47+
variables = MOI.get(inner, MOI.ListOfVariableIndices())
48+
X, Y = _compute_point(model, variables, f)
4749
_log_subproblem_solve(model, Y)
4850
MOI.delete.(model, c)
4951
return status, SolutionPoint(X, Y)
5052
end
5153

52-
function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
53-
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
54-
if MOI.output_dimension(model.f) != 2
54+
function minimize_multiobjective!(
55+
algorithm::Chalmet,
56+
model::Optimizer,
57+
inner::MOI.ModelLike,
58+
f::MOI.AbstractVectorFunction,
59+
)
60+
@assert MOI.get(inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
61+
if MOI.output_dimension(f) != 2
5562
error("Chalmet requires exactly two objectives")
5663
end
5764
solutions = SolutionPoint[]
5865
E = Tuple{Int,Int}[]
5966
Q = Tuple{Int,Int}[]
60-
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
61-
f1, f2 = MOI.Utilities.scalarize(model.f)
67+
variables = MOI.get(inner, MOI.ListOfVariableIndices())
68+
f1, f2 = MOI.Utilities.scalarize(f)
6269
y1, y2 = zeros(2), zeros(2)
63-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
70+
MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
6471
optimize_inner!(model)
65-
status = MOI.get(model.inner, MOI.TerminationStatus())
72+
status = MOI.get(inner, MOI.TerminationStatus())
6673
if !_is_scalar_status_optimal(status)
6774
return status, solutions
6875
end
6976
_, y1[2] = _compute_point(model, variables, f2)
7077
_log_subproblem_solve(model, variables)
71-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
78+
MOI.set(inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
7279
y1_constraint = MOI.Utilities.normalize_and_add_constraint(
73-
model.inner,
80+
inner,
7481
f2,
7582
MOI.LessThan(y1[2]),
7683
)
7784
optimize_inner!(model)
78-
status = MOI.get(model.inner, MOI.TerminationStatus())
85+
status = MOI.get(inner, MOI.TerminationStatus())
7986
if !_is_scalar_status_optimal(status)
8087
return status, solutions
8188
end
8289
x1, y1[1] = _compute_point(model, variables, f1)
8390
_log_subproblem_solve(model, y1)
84-
MOI.delete(model.inner, y1_constraint)
91+
MOI.delete(inner, y1_constraint)
8592
push!(solutions, SolutionPoint(x1, y1))
86-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
93+
MOI.set(inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
8794
optimize_inner!(model)
88-
status = MOI.get(model.inner, MOI.TerminationStatus())
95+
status = MOI.get(inner, MOI.TerminationStatus())
8996
if !_is_scalar_status_optimal(status)
9097
return status, solutions
9198
end
@@ -94,20 +101,20 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
94101
if y2[1] solutions[1].y[1]
95102
return MOI.OPTIMAL, solutions
96103
end
97-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
104+
MOI.set(inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
98105
y2_constraint = MOI.Utilities.normalize_and_add_constraint(
99-
model.inner,
106+
inner,
100107
f1,
101108
MOI.LessThan(y2[1]),
102109
)
103110
optimize_inner!(model)
104-
status = MOI.get(model.inner, MOI.TerminationStatus())
111+
status = MOI.get(inner, MOI.TerminationStatus())
105112
if !_is_scalar_status_optimal(status)
106113
return status, solutions
107114
end
108115
x2, y2[2] = _compute_point(model, variables, f2)
109116
_log_subproblem_solve(model, y2)
110-
MOI.delete(model.inner, y2_constraint)
117+
MOI.delete(inner, y2_constraint)
111118
push!(solutions, SolutionPoint(x2, y2))
112119
push!(Q, (1, 2))
113120
t = 3
@@ -118,7 +125,8 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
118125
r, s = pop!(Q)
119126
yr, ys = solutions[r].y, solutions[s].y
120127
rhs = [max(yr[1], ys[1]), max(yr[2], ys[2])]
121-
status, solution = _solve_constrained_model(model, algorithm, rhs)
128+
status, solution =
129+
_solve_constrained_model(algorithm, model, inner, f, rhs)
122130
if !_is_scalar_status_optimal(status)
123131
push!(E, (r, s))
124132
continue

src/algorithms/Dichotomy.jl

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,37 +61,49 @@ function MOI.get(alg::Dichotomy, attr::SolutionLimit)
6161
end
6262

6363
function _solve_weighted_sum(
64-
model::Optimizer,
6564
::Dichotomy,
65+
model::Optimizer,
66+
inner::MOI.ModelLike,
67+
f::MOI.AbstractVectorFunction,
6668
weights::Vector{Float64},
6769
)
68-
f = _scalarise(model.f, weights)
69-
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f)
70+
f_scalar = _scalarise(f, weights)
71+
MOI.set(inner, MOI.ObjectiveFunction{typeof(f_scalar)}(), f_scalar)
7072
optimize_inner!(model)
71-
status = MOI.get(model.inner, MOI.TerminationStatus())
73+
status = MOI.get(inner, MOI.TerminationStatus())
7274
if !_is_scalar_status_optimal(status)
7375
return status, nothing
7476
end
75-
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
76-
X, Y = _compute_point(model, variables, model.f)
77+
variables = MOI.get(inner, MOI.ListOfVariableIndices())
78+
X, Y = _compute_point(model, variables, f)
7779
_log_subproblem_solve(model, Y)
7880
return status, SolutionPoint(X, Y)
7981
end
8082

8183
function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer)
82-
if MOI.output_dimension(model.f) > 2
83-
error("Only scalar or bi-objective problems supported.")
84-
end
85-
if MOI.output_dimension(model.f) == 1
84+
return optimize_multiobjective!(algorithm, model, model.inner, model.f)
85+
end
86+
87+
function optimize_multiobjective!(
88+
algorithm::Dichotomy,
89+
model::Optimizer,
90+
inner::MOI.ModelLike,
91+
f::MOI.AbstractVectorFunction,
92+
)
93+
if MOI.output_dimension(f) == 1
8694
if (ret = _check_premature_termination(model)) !== nothing
8795
return ret, nothing
8896
end
89-
status, solution = _solve_weighted_sum(model, algorithm, [1.0])
97+
status, solution =
98+
_solve_weighted_sum(algorithm, model, inner, f, [1.0])
9099
return status, [solution]
100+
elseif MOI.output_dimension(f) > 2
101+
error("Only scalar or bi-objective problems supported.")
91102
end
92103
solutions = Dict{Float64,SolutionPoint}()
93104
for (i, w) in (1 => 1.0, 2 => 0.0)
94-
status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w])
105+
status, solution =
106+
_solve_weighted_sum(algorithm, model, inner, f, [w, 1.0 - w])
95107
if !_is_scalar_status_optimal(status)
96108
return status, nothing
97109
end
@@ -113,7 +125,8 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer)
113125
(a, b) = popfirst!(queue)
114126
y_d = solutions[a].y .- solutions[b].y
115127
w = y_d[2] / (y_d[2] - y_d[1])
116-
status, solution = _solve_weighted_sum(model, algorithm, [w, 1.0 - w])
128+
status, solution =
129+
_solve_weighted_sum(algorithm, model, inner, f, [w, 1.0 - w])
117130
if !_is_scalar_status_optimal(status)
118131
break # Exit the solve with some error.
119132
elseif solution solutions[a] || solution solutions[b]

0 commit comments

Comments
 (0)