Skip to content

Commit 4e097b6

Browse files
committed
Update
1 parent 39f9696 commit 4e097b6

12 files changed

+82
-55
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Ipopt = "1"
2121
JSON = "1"
2222
MathOptInterface = "1.19"
2323
Polyhedra = "0.8"
24-
Printf = "1.11.0"
24+
Printf = "1.10.0"
2525
Test = "1"
2626
julia = "1.10"
2727

ext/MultiObjectiveAlgorithmsPolyhedraExt.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ function MOA.minimize_multiobjective!(
3939
model::MOA.Optimizer,
4040
)
4141
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
42-
start_time = time()
4342
solutions = Dict{Vector{Float64},Dict{MOI.VariableIndex,Float64}}()
4443
variables = MOI.get(model.inner, MOI.ListOfVariableIndices())
4544
n = MOI.output_dimension(model.f)
@@ -102,7 +101,7 @@ function MOA.minimize_multiobjective!(
102101
H = _halfspaces(IPS)
103102
count = 0
104103
while !isempty(H)
105-
ret = MOA._check_premature_termination(model, start_time)
104+
ret = MOA._check_premature_termination(model)
106105
if ret !== nothing
107106
status = ret
108107
break

src/MultiObjectiveAlgorithms.jl

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
172172
termination_status::MOI.TerminationStatusCode
173173
silent::Bool
174174
time_limit_sec::Union{Nothing,Float64}
175+
start_time::Float64
175176
solve_time::Float64
176177
ideal_point::Vector{Float64}
177178
compute_ideal_point::Bool
@@ -192,6 +193,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
192193
false,
193194
nothing,
194195
NaN,
196+
NaN,
195197
Float64[],
196198
_default(ComputeIdealPoint()),
197199
0,
@@ -206,6 +208,7 @@ function MOI.empty!(model::Optimizer)
206208
empty!(model.solutions)
207209
model.termination_status = MOI.OPTIMIZE_NOT_CALLED
208210
model.solve_time = NaN
211+
model.start_time = NaN
209212
empty!(model.ideal_point)
210213
model.subproblem_count = 0
211214
return
@@ -665,24 +668,56 @@ function optimize_inner!(model::Optimizer)
665668
return
666669
end
667670

671+
"""
672+
_log_solution(model::Optimizer, variables::Vector{MOI.VariableIndex})
673+
674+
Log the solution. We don't have a pre-computed point, so compute one from the
675+
variable values.
676+
"""
677+
function _log_solution(model::Optimizer, variables::Vector{MOI.VariableIndex})
678+
if !model.silent
679+
_, Y = _compute_point(model, variables, model.f)
680+
_log_solution(model, Y)
681+
end
682+
return
683+
end
684+
685+
"""
686+
_log_solution(model::Optimizer, variables::Vector{MOI.VariableIndex})
687+
688+
Log the solution. We have a pre-computed point.
689+
"""
668690
function _log_solution(model::Optimizer, Y)
669-
if model.silent
670-
return
691+
if !model.silent
692+
print(_format(model.subproblem_count))
693+
for y in Y
694+
print(" ", _format(y))
695+
end
696+
println(" ", _format(time() - model.start_time))
671697
end
672-
print(_format(model.subproblem_count))
673-
for y in Y
674-
print(" ", _format(y))
698+
return
699+
end
700+
"""
701+
_log_solution(model::Optimizer, msg::String)
702+
703+
Log the solution. Assume the subproblem failed to solve.
704+
"""
705+
function _log_solution(model::Optimizer, msg::String)
706+
if !model.silent
707+
print(_format(model.subproblem_count), " ")
708+
print(rpad(msg, 13 * MOI.output_dimension(model.f) - 3))
709+
println(" ", _format(time() - model.start_time))
675710
end
676-
println()
677711
return
678712
end
713+
679714
_format(x::Int) = Printf.@sprintf("%5d", x)
680715
_format(x::Float64) = Printf.@sprintf("% .5e", x)
681716
_format(::Nothing) = " "
682717

683-
function _compute_ideal_point(model::Optimizer, start_time)
718+
function _compute_ideal_point(model::Optimizer)
684719
for (i, f) in enumerate(MOI.Utilities.eachscalar(model.f))
685-
if _check_premature_termination(model, start_time) !== nothing
720+
if _check_premature_termination(model) !== nothing
686721
return
687722
end
688723
if !isnan(model.ideal_point[i])
@@ -767,11 +802,11 @@ function _check_interrupt(f)
767802
end
768803
end
769804

770-
function _check_premature_termination(model::Optimizer, start_time::Float64)
805+
function _check_premature_termination(model::Optimizer)
771806
return _check_interrupt() do
772807
time_limit = MOI.get(model, MOI.TimeLimitSec())
773808
if time_limit !== nothing
774-
time_remaining = time_limit - (time() - start_time)
809+
time_remaining = time_limit - (time() - model.start_time)
775810
if time_remaining <= 0
776811
return MOI.TIME_LIMIT
777812
end
@@ -789,7 +824,7 @@ function MOI.optimize!(model::Optimizer)
789824
end
790825

791826
function _optimize!(model::Optimizer)
792-
start_time = time()
827+
model.start_time = time()
793828
empty!(model.solutions)
794829
model.termination_status = MOI.OPTIMIZE_NOT_CALLED
795830
model.subproblem_count = 0
@@ -801,9 +836,9 @@ function _optimize!(model::Optimizer)
801836
if !model.silent
802837
print("Iter.")
803838
for i in 1:MOI.output_dimension(model.f)
804-
print(lpad("Obj. $i", 13))
839+
print(lpad("Obj. $i ", 13))
805840
end
806-
println()
841+
println(" Time ")
807842
end
808843
# We need to clear the ideal point prior to starting the solve. Algorithms
809844
# may update this during the solve, otherwise we will update it at the end.
@@ -819,12 +854,12 @@ function _optimize!(model::Optimizer)
819854
println("Found $(length(model.solutions)) solutions")
820855
end
821856
if MOI.get(model, ComputeIdealPoint())
822-
_compute_ideal_point(model, start_time)
857+
_compute_ideal_point(model)
823858
end
824859
if MOI.supports(model.inner, MOI.TimeLimitSec())
825860
MOI.set(model.inner, MOI.TimeLimitSec(), nothing)
826861
end
827-
model.solve_time = time() - start_time
862+
model.solve_time = time() - model.start_time
828863
return
829864
end
830865

src/algorithms/Chalmet.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ function _solve_constrained_model(
3838
optimize_inner!(model)
3939
status = MOI.get(model.inner, MOI.TerminationStatus())
4040
if !_is_scalar_status_optimal(status)
41+
_log_solution(model, "subproblem not optimal")
4142
MOI.delete.(model, c)
4243
return status, nothing
4344
end
@@ -50,7 +51,6 @@ end
5051

5152
function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
5253
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
53-
start_time = time()
5454
if MOI.output_dimension(model.f) != 2
5555
error("Chalmet requires exactly two objectives")
5656
end
@@ -67,7 +67,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
6767
return status, solutions
6868
end
6969
_, y1[2] = _compute_point(model, variables, f2)
70-
_log_solution(model, y1[2])
70+
_log_solution(model, variables)
7171
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
7272
y1_constraint = MOI.Utilities.normalize_and_add_constraint(
7373
model.inner,
@@ -80,7 +80,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
8080
return status, solutions
8181
end
8282
x1, y1[1] = _compute_point(model, variables, f1)
83-
_log_solution(model, y1[1])
83+
_log_solution(model, y1)
8484
MOI.delete(model.inner, y1_constraint)
8585
push!(solutions, SolutionPoint(x1, y1))
8686
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
@@ -90,7 +90,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
9090
return status, solutions
9191
end
9292
_, y2[1] = _compute_point(model, variables, f1)
93-
_log_solution(model, y2[1])
93+
_log_solution(model, variables)
9494
if y2[1] solutions[1].y[1]
9595
return MOI.OPTIMAL, solutions
9696
end
@@ -106,13 +106,13 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
106106
return status, solutions
107107
end
108108
x2, y2[2] = _compute_point(model, variables, f2)
109-
_log_solution(model, y2[2])
109+
_log_solution(model, y2)
110110
MOI.delete(model.inner, y2_constraint)
111111
push!(solutions, SolutionPoint(x2, y2))
112112
push!(Q, (1, 2))
113113
t = 3
114114
while !isempty(Q)
115-
if (ret = _check_premature_termination(model, start_time)) !== nothing
115+
if (ret = _check_premature_termination(model)) !== nothing
116116
return ret, solutions
117117
end
118118
r, s = pop!(Q)

src/algorithms/Dichotomy.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,11 @@ function _solve_weighted_sum(
7979
end
8080

8181
function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer)
82-
start_time = time()
8382
if MOI.output_dimension(model.f) > 2
8483
error("Only scalar or bi-objective problems supported.")
8584
end
8685
if MOI.output_dimension(model.f) == 1
87-
if (ret = _check_premature_termination(model, start_time)) !== nothing
86+
if (ret = _check_premature_termination(model)) !== nothing
8887
return ret, nothing
8988
end
9089
status, solution = _solve_weighted_sum(model, algorithm, [1.0])
@@ -107,7 +106,7 @@ function optimize_multiobjective!(algorithm::Dichotomy, model::Optimizer)
107106
limit = MOI.get(algorithm, SolutionLimit())
108107
status = MOI.OPTIMAL
109108
while length(queue) > 0 && length(solutions) < limit
110-
if (ret = _check_premature_termination(model, start_time)) !== nothing
109+
if (ret = _check_premature_termination(model)) !== nothing
111110
status = ret
112111
break
113112
end

src/algorithms/DominguezRios.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ end
150150

151151
function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
152152
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
153-
start_time = time()
154153
n = MOI.output_dimension(model.f)
155154
L = [_DominguezRiosBox[] for i in 1:n]
156155
scalars = MOI.Utilities.scalarize(model.f)
@@ -166,7 +165,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
166165
return status, nothing
167166
end
168167
_, Y = _compute_point(model, variables, f_i)
169-
_log_solution(model, Y)
168+
_log_solution(model, variables)
170169
yI[i] = Y
171170
model.ideal_point[i] = Y
172171
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
@@ -177,7 +176,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
177176
return status, nothing
178177
end
179178
_, Y = _compute_point(model, variables, f_i)
180-
_log_solution(model, Y)
179+
_log_solution(model, variables)
181180
yN[i] = Y + 1
182181
end
183182
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
@@ -192,7 +191,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
192191
k = 0
193192
status = MOI.OPTIMAL
194193
while any(!isempty(l) for l in L)
195-
if (ret = _check_premature_termination(model, start_time)) !== nothing
194+
if (ret = _check_premature_termination(model)) !== nothing
196195
status = ret
197196
break
198197
end
@@ -236,6 +235,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
236235
# fail and return something like OTHER_ERROR (e.g., because the
237236
# numerics are challenging). Rather than error completely, let's
238237
# just skip this box.
238+
_log_solution(model, "subproblem not optimal")
239239
deleteat!(L[k], i)
240240
end
241241
MOI.delete.(model.inner, constraints)

src/algorithms/EpsilonConstraint.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ function minimize_multiobjective!(
7676
model::Optimizer,
7777
)
7878
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
79-
start_time = time()
8079
if MOI.output_dimension(model.f) != 2
8180
error("EpsilonConstraint requires exactly two objectives")
8281
end
@@ -117,7 +116,7 @@ function minimize_multiobjective!(
117116
bound -= constant
118117
status = MOI.OPTIMAL
119118
for _ in 3:n_points
120-
if (ret = _check_premature_termination(model, start_time)) !== nothing
119+
if (ret = _check_premature_termination(model)) !== nothing
121120
status = ret
122121
break
123122
end

src/algorithms/Hierarchical.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,12 @@ function minimize_multiobjective!(algorithm::Hierarchical, model::Optimizer)
112112
if round == length(objective_subsets)
113113
break
114114
end
115+
if !model.silent
116+
X, Y = _compute_point(model, variables, model.f)
117+
_log_solution(model, Y)
118+
end
115119
# Add tolerance constraints
116120
X, Y = _compute_point(model, variables, new_vector_f)
117-
_log_solution(model, Y)
118121
for (i, fi) in enumerate(MOI.Utilities.eachscalar(new_vector_f))
119122
rtol = MOI.get(algorithm, ObjectiveRelativeTolerance(i))
120123
set = MOI.LessThan(Y[i] + rtol * abs(Y[i]))

src/algorithms/KirlikSayin.jl

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ end
8383

8484
function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
8585
@assert MOI.get(model.inner, MOI.ObjectiveSense()) == MOI.MIN_SENSE
86-
start_time = time()
8786
solutions = SolutionPoint[]
8887
# Problem with p objectives.
8988
# Set k = 1, meaning the nondominated points will get projected
@@ -96,8 +95,6 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
9695
# This tolerance is really important!
9796
δ = 1.0
9897
scalars = MOI.Utilities.scalarize(model.f)
99-
printing = Vector{Union{Nothing,Float64}}(undef, n)
100-
fill!(printing, nothing)
10198
# Ideal and Nadir point estimation
10299
for (i, f_i) in enumerate(scalars)
103100
# Ideal point
@@ -108,8 +105,7 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
108105
return status, nothing
109106
end
110107
_, Y = _compute_point(model, variables, f_i)
111-
printing[i] = Y
112-
_log_solution(model, printing)
108+
_log_solution(model, variables)
113109
model.ideal_point[i] = yI[i] = Y
114110
# Nadir point
115111
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
@@ -122,16 +118,14 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
122118
return status, nothing
123119
end
124120
_, Y = _compute_point(model, variables, f_i)
125-
printing[i] = Y
126-
_log_solution(model, printing)
127-
printing[i] = nothing
121+
_log_solution(model, variables)
128122
yN[i] = Y + δ
129123
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
130124
end
131125
L = [_Rectangle(_project(yI, k), _project(yN, k))]
132126
status = MOI.OPTIMAL
133127
while !isempty(L)
134-
if (ret = _check_premature_termination(model, start_time)) !== nothing
128+
if (ret = _check_premature_termination(model)) !== nothing
135129
status = ret
136130
break
137131
end
@@ -159,6 +153,7 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
159153
push!(ε_constraints, ci)
160154
end
161155
optimize_inner!(model)
156+
_log_solution(model, "auxillary subproblem")
162157
if !_is_scalar_status_optimal(model)
163158
# If this fails, it likely means that the solver experienced a
164159
# numerical error with this box. Just skip it.
@@ -179,6 +174,7 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
179174
)
180175
optimize_inner!(model)
181176
if !_is_scalar_status_optimal(model)
177+
_log_solution(model, "subproblem not optimal")
182178
# If this fails, it likely means that the solver experienced a
183179
# numerical error with this box. Just skip it.
184180
MOI.delete.(model, ε_constraints)

0 commit comments

Comments
 (0)