Skip to content

Commit b6bf829

Browse files
authored
Add SubproblemCount attribute (#135)
1 parent 515f7cc commit b6bf829

21 files changed

+92
-26
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ the solution process.
8585
* `MOA.SolutionLimit()`
8686
* `MOI.TimeLimitSec()`
8787

88+
Query the number of scalar subproblems that were solved using
89+
90+
* `MOA.SubproblemCount()`
91+
8892
## Ideal point
8993

9094
By default, MOA will compute the ideal point, which can be queried using the

ext/MultiObjectiveAlgorithmsPolyhedraExt.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function MOA.minimize_multiobjective!(
5353
yI, yUB = zeros(n), zeros(n)
5454
for (i, f_i) in enumerate(scalars)
5555
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
56-
MOI.optimize!(model.inner)
56+
MOA.optimize_inner!(model)
5757
status = MOI.get(model.inner, MOI.TerminationStatus())
5858
if !MOA._is_scalar_status_optimal(model)
5959
return status, nothing
@@ -63,7 +63,7 @@ function MOA.minimize_multiobjective!(
6363
yI[i] = Y[i]
6464
anchors[Y] = X
6565
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
66-
MOI.optimize!(model.inner)
66+
MOA.optimize_inner!(model)
6767
status = MOI.get(model.inner, MOI.TerminationStatus())
6868
if !MOA._is_scalar_status_optimal(model)
6969
MOA._warn_on_nonfinite_anti_ideal(algorithm, MOI.MIN_SENSE, i)
@@ -113,7 +113,7 @@ function MOA.minimize_multiobjective!(
113113
# would not terminate when precision is set to 0
114114
new_f = sum(w[i] * (scalars[i] + u[i]) for i in 1:n) # w' * (f(x) + u)
115115
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
116-
MOI.optimize!(model.inner)
116+
MOA.optimize_inner!(model)
117117
status = MOI.get(model.inner, MOI.TerminationStatus())
118118
if !MOA._is_scalar_status_optimal(model)
119119
return status, nothing

src/MultiObjectiveAlgorithms.jl

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
123123
solve_time::Float64
124124
ideal_point::Vector{Float64}
125125
compute_ideal_point::Bool
126+
subproblem_count::Int
126127

127128
function Optimizer(optimizer_factory)
128129
return new(
@@ -135,6 +136,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
135136
NaN,
136137
Float64[],
137138
default(ComputeIdealPoint()),
139+
0,
138140
)
139141
end
140142
end
@@ -146,6 +148,7 @@ function MOI.empty!(model::Optimizer)
146148
model.termination_status = MOI.OPTIMIZE_NOT_CALLED
147149
model.solve_time = NaN
148150
empty!(model.ideal_point)
151+
model.subproblem_count = 0
149152
return
150153
end
151154

@@ -394,6 +397,20 @@ end
394397

395398
MOI.get(model::Optimizer, ::ComputeIdealPoint) = model.compute_ideal_point
396399

400+
### SubproblemCount
401+
402+
"""
403+
SubproblemCount <: AbstractModelAttribute -> Int
404+
405+
A result attribute for querying the total number of subproblem solves by an
406+
algorithm.
407+
"""
408+
struct SubproblemCount <: MOI.AbstractModelAttribute end
409+
410+
MOI.is_set_by_optimize(::SubproblemCount) = true
411+
412+
MOI.get(model::Optimizer, ::SubproblemCount) = model.subproblem_count
413+
397414
### RawOptimizerAttribute
398415

399416
function MOI.supports(model::Optimizer, attr::MOI.RawOptimizerAttribute)
@@ -573,6 +590,18 @@ function MOI.delete(model::Optimizer, ci::MOI.ConstraintIndex)
573590
return
574591
end
575592

593+
"""
594+
optimize_inner!(model::Optimizer)
595+
596+
A function that must be called instead of `MOI.optimize!(model.inner)` because
597+
it also increments the `subproblem_count`.
598+
"""
599+
function optimize_inner!(model::Optimizer)
600+
MOI.optimize!(model.inner)
601+
model.subproblem_count += 1
602+
return
603+
end
604+
576605
function _compute_ideal_point(model::Optimizer, start_time)
577606
for (i, f) in enumerate(MOI.Utilities.eachscalar(model.f))
578607
if _time_limit_exceeded(model, start_time)
@@ -582,7 +611,7 @@ function _compute_ideal_point(model::Optimizer, start_time)
582611
continue # The algorithm already updated this information
583612
end
584613
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f)
585-
MOI.optimize!(model.inner)
614+
optimize_inner!(model)
586615
status = MOI.get(model.inner, MOI.TerminationStatus())
587616
if _is_scalar_status_optimal(status)
588617
model.ideal_point[i] = MOI.get(model.inner, MOI.ObjectiveValue())
@@ -619,6 +648,7 @@ function MOI.optimize!(model::Optimizer)
619648
start_time = time()
620649
empty!(model.solutions)
621650
model.termination_status = MOI.OPTIMIZE_NOT_CALLED
651+
model.subproblem_count = 0
622652
if model.f === nothing
623653
model.termination_status = MOI.INVALID_MODEL
624654
empty!(model.ideal_point)

src/algorithms/Chalmet.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function _solve_constrained_model(
2929
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(g)}(), g)
3030
sets = MOI.LessThan.(rhs .- 1)
3131
c = MOI.Utilities.normalize_and_add_constraint.(model.inner, f, sets)
32-
MOI.optimize!(model.inner)
32+
optimize_inner!(model)
3333
status = MOI.get(model.inner, MOI.TerminationStatus())
3434
if !_is_scalar_status_optimal(status)
3535
MOI.delete.(model, c)
@@ -54,7 +54,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
5454
f1, f2 = MOI.Utilities.scalarize(model.f)
5555
y1, y2 = zeros(2), zeros(2)
5656
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f2)}(), f2)
57-
MOI.optimize!(model.inner)
57+
optimize_inner!(model)
5858
status = MOI.get(model.inner, MOI.TerminationStatus())
5959
if !_is_scalar_status_optimal(status)
6060
return status, nothing
@@ -66,7 +66,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
6666
f2,
6767
MOI.LessThan(y1[2]),
6868
)
69-
MOI.optimize!(model.inner)
69+
optimize_inner!(model)
7070
status = MOI.get(model.inner, MOI.TerminationStatus())
7171
if !_is_scalar_status_optimal(status)
7272
return status, nothing
@@ -75,7 +75,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
7575
MOI.delete(model.inner, y1_constraint)
7676
push!(solutions, SolutionPoint(x1, y1))
7777
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f1)}(), f1)
78-
MOI.optimize!(model.inner)
78+
optimize_inner!(model)
7979
status = MOI.get(model.inner, MOI.TerminationStatus())
8080
if !_is_scalar_status_optimal(status)
8181
return status, nothing
@@ -90,7 +90,7 @@ function minimize_multiobjective!(algorithm::Chalmet, model::Optimizer)
9090
f1,
9191
MOI.LessThan(y2[1]),
9292
)
93-
MOI.optimize!(model.inner)
93+
optimize_inner!(model)
9494
status = MOI.get(model.inner, MOI.TerminationStatus())
9595
if !_is_scalar_status_optimal(status)
9696
return status, nothing

src/algorithms/Dichotomy.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function _solve_weighted_sum(
6161
)
6262
f = _scalarise(model.f, weights)
6363
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f)
64-
MOI.optimize!(model.inner)
64+
optimize_inner!(model)
6565
status = MOI.get(model.inner, MOI.TerminationStatus())
6666
if !_is_scalar_status_optimal(status)
6767
return status, nothing

src/algorithms/DominguezRios.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
153153
for (i, f_i) in enumerate(scalars)
154154
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
155155
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MIN_SENSE)
156-
MOI.optimize!(model.inner)
156+
optimize_inner!(model)
157157
status = MOI.get(model.inner, MOI.TerminationStatus())
158158
if !_is_scalar_status_optimal(status)
159159
return status, nothing
@@ -162,7 +162,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
162162
yI[i] = Y
163163
model.ideal_point[i] = Y
164164
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
165-
MOI.optimize!(model.inner)
165+
optimize_inner!(model)
166166
status = MOI.get(model.inner, MOI.TerminationStatus())
167167
if !_is_scalar_status_optimal(status)
168168
_warn_on_nonfinite_anti_ideal(algorithm, MOI.MIN_SENSE, i)
@@ -206,7 +206,7 @@ function minimize_multiobjective!(algorithm::DominguezRios, model::Optimizer)
206206
# * the `ϵ` term is already covered in `w`
207207
new_f = t_max + ϵ * sum(w[i] * (scalars[i] - yI[i]) for i in 1:n)
208208
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
209-
MOI.optimize!(model.inner)
209+
optimize_inner!(model)
210210
if _is_scalar_status_optimal(model)
211211
X, Y = _compute_point(model, variables, model.f)
212212
obj = MOI.get(model.inner, MOI.ObjectiveValue())

src/algorithms/EpsilonConstraint.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ function minimize_multiobjective!(
116116
break
117117
end
118118
MOI.set(model, MOI.ConstraintSet(), ci, MOI.LessThan{Float64}(bound))
119-
MOI.optimize!(model.inner)
119+
optimize_inner!(model)
120120
if !_is_scalar_status_optimal(model)
121121
break
122122
end

src/algorithms/Hierarchical.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ function minimize_multiobjective!(algorithm::Hierarchical, model::Optimizer)
100100
new_vector_f = objectives[indices]
101101
new_f = _scalarise(new_vector_f, weights[indices])
102102
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(new_f)}(), new_f)
103-
MOI.optimize!(model.inner)
103+
optimize_inner!(model)
104104
status = MOI.get(model.inner, MOI.TerminationStatus())
105105
if !_is_scalar_status_optimal(status)
106106
return status, nothing

src/algorithms/KirlikSayin.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
9393
for (i, f_i) in enumerate(scalars)
9494
# Ideal point
9595
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f_i)}(), f_i)
96-
MOI.optimize!(model.inner)
96+
optimize_inner!(model)
9797
status = MOI.get(model.inner, MOI.TerminationStatus())
9898
if !_is_scalar_status_optimal(status)
9999
return status, nothing
@@ -102,7 +102,7 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
102102
model.ideal_point[i] = yI[i] = Y
103103
# Nadir point
104104
MOI.set(model.inner, MOI.ObjectiveSense(), MOI.MAX_SENSE)
105-
MOI.optimize!(model.inner)
105+
optimize_inner!(model)
106106
status = MOI.get(model.inner, MOI.TerminationStatus())
107107
if !_is_scalar_status_optimal(status)
108108
# Repair ObjectiveSense before exiting
@@ -143,7 +143,7 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
143143
)
144144
push!(ε_constraints, ci)
145145
end
146-
MOI.optimize!(model.inner)
146+
optimize_inner!(model)
147147
if !_is_scalar_status_optimal(model)
148148
_remove_rectangle(L, _Rectangle(_project(yI, k), uᵢ))
149149
MOI.delete.(model, ε_constraints)
@@ -160,7 +160,7 @@ function minimize_multiobjective!(algorithm::KirlikSayin, model::Optimizer)
160160
scalars[k],
161161
MOI.EqualTo(zₖ),
162162
)
163-
MOI.optimize!(model.inner)
163+
optimize_inner!(model)
164164
if !_is_scalar_status_optimal(model)
165165
MOI.delete.(model, ε_constraints)
166166
MOI.delete(model, zₖ_constraint)

src/algorithms/Lexicographic.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function _solve_in_sequence(
104104
end
105105
f = scalars[i]
106106
MOI.set(model.inner, MOI.ObjectiveFunction{typeof(f)}(), f)
107-
MOI.optimize!(model.inner)
107+
optimize_inner!(model)
108108
status = MOI.get(model.inner, MOI.TerminationStatus())
109109
primal_status = MOI.get(model.inner, MOI.PrimalStatus())
110110
if _is_scalar_status_feasible_point(primal_status)

0 commit comments

Comments
 (0)