Skip to content

Commit 154e90f

Browse files
authored
Merge pull request #695 from JuliaOpt/bl/cachingoptattr
Add Silent and implement setting optimizer attributes in caching and mock optimizers
2 parents 001b4c0 + 78ed02b commit 154e90f

File tree

5 files changed

+79
-9
lines changed

5 files changed

+79
-9
lines changed

docs/src/apireference.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,11 @@ AbstractOptimizer
8484
optimize!
8585
```
8686

87-
List of attributes optimizers attributes
87+
List of optimizers attributes
8888

8989
```@docs
9090
SolverName
91+
Silent
9192
```
9293

9394
List of attributes useful for optimizers

src/Utilities/cachingoptimizer.jl

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ function reset_optimizer(m::CachingOptimizer, optimizer::MOI.AbstractOptimizer)
8080
@assert MOI.is_empty(optimizer)
8181
m.optimizer = optimizer
8282
m.state = EMPTY_OPTIMIZER
83+
for attr in MOI.get(m.model_cache, MOI.ListOfOptimizerAttributesSet())
84+
value = MOI.get(m.model_cache, attr)
85+
optimizer_value = attribute_value_map(m.model_to_optimizer_map, value)
86+
MOI.set(m.optimizer, attr, optimizer_value)
87+
end
8388
return
8489
end
8590

@@ -501,16 +506,24 @@ function MOI.get(m::CachingOptimizer, IdxT::Type{<:MOI.Index}, name::String)
501506
return MOI.get(m.model_cache, IdxT, name)
502507
end
503508

504-
# TODO: MOI.set for MOI.AbstractOptimizerAttribute.
509+
function MOI.set(model::CachingOptimizer, attr::MOI.AbstractOptimizerAttribute,
510+
value)
511+
optimizer_value = attribute_value_map(model.model_to_optimizer_map, value)
512+
if model.optimizer !== nothing
513+
MOI.set(model.optimizer, attr, optimizer_value)
514+
end
515+
MOI.set(model.model_cache, attr, value)
516+
end
505517

506518
function MOI.get(model::CachingOptimizer, attr::MOI.AbstractOptimizerAttribute)
507519
if state(model) == NO_OPTIMIZER
520+
# TODO: Copyable attributes (e.g., `Silent`, `TimeLimit`) should also be
521+
# stored in the cache so we could return the value stored in the cache
522+
# instead. However, for non-copyable attributes( e.g. `SolverName`) the
523+
# error is appropriate.
508524
error("Cannot query $(attr) from caching optimizer because no " *
509525
"optimizer is attached.")
510526
end
511-
# TODO: Copyable attributes (e.g., TimeLimit) could also be stored in the
512-
# cache. When MOI.set is implemented for MOI.AbstractOptimizerAttribute,
513-
# make sure this case is handled correctly.
514527
return attribute_value_map(model.optimizer_to_model_map,
515528
MOI.get(model.optimizer, attr))
516529
end

src/Utilities/mockoptimizer.jl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,9 @@ MOI.set(mock::MockOptimizer, ::MockModelAttribute, value::Integer) = (mock.attri
151151
function MOI.supports(mock::MockOptimizer, attr::MOI.AbstractModelAttribute)
152152
return MOI.supports(mock.inner_model, attr)
153153
end
154-
function MOI.set(mock::MockOptimizer, attr::MOI.AbstractModelAttribute, value)
154+
function MOI.set(mock::MockOptimizer,
155+
attr::Union{MOI.AbstractModelAttribute,
156+
MOI.AbstractOptimizerAttribute}, value)
155157
MOI.set(mock.inner_model, attr, value)
156158
end
157159
MOI.set(mock::MockOptimizer, attr::MOI.ObjectiveFunction, value) = MOI.set(mock.inner_model, attr, xor_variables(value))
@@ -164,7 +166,11 @@ MOI.set(mock::MockOptimizer, ::MockConstraintAttribute, idx::MOI.ConstraintIndex
164166
MOI.set(mock::MockOptimizer, ::MOI.ConstraintDual, idx::MOI.ConstraintIndex, value) = (mock.condual[xor_index(idx)] = value)
165167
MOI.set(mock::MockOptimizer, ::MOI.ConstraintBasisStatus, idx::MOI.ConstraintIndex, value) = (mock.con_basis[xor_index(idx)] = value)
166168

167-
MOI.get(mock::MockOptimizer, attr::MOI.AbstractModelAttribute) = MOI.get(mock.inner_model, attr)
169+
function MOI.get(mock::MockOptimizer,
170+
attr::Union{MOI.AbstractModelAttribute,
171+
MOI.AbstractOptimizerAttribute})
172+
return MOI.get(mock.inner_model, attr)
173+
end
168174
MOI.get(mock::MockOptimizer, attr::Union{MOI.ListOfVariableIndices,
169175
MOI.ListOfConstraintIndices}) = xor_index.(MOI.get(mock.inner_model, attr))
170176
MOI.get(mock::MockOptimizer, attr::MOI.ObjectiveFunction) = xor_variables(MOI.get(mock.inner_model, attr))

src/attributes.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,27 @@ An optimizer attribute for the string identifying the solver/optimizer.
337337
"""
338338
struct SolverName <: AbstractOptimizerAttribute end
339339

340+
"""
341+
Silent
342+
343+
An optimizer attribute for silencing the output of an optimizer. When `set`
344+
to `true`, it takes precedence over any other attribute controlling verbosity
345+
and requires the solver to produce no output. The default value is `false`
346+
which has no effect. In this case the verbosity is controlled by other
347+
attributes.
348+
349+
## Note
350+
351+
Every optimizer should have verbosity on by default. For instance, if a solver
352+
has a solver-specific log level attribute, the MOI implementation should set it
353+
to `1` by default. If the user sets `Silent` to `true`, then the log level
354+
should be set to `0`, even if the user specifically sets a value of log level.
355+
If the value of `Silent` is `false` then the log level set to the solver is the
356+
value given by the user for this solver-specific parameter or `1` if none is
357+
given.
358+
"""
359+
struct Silent <: AbstractOptimizerAttribute end
360+
340361
## Model attributes
341362

342363
"""

test/Utilities/cachingoptimizer.jl

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,45 @@
3030
exception = ErrorException(
3131
"Cannot query $(attr) from caching optimizer because no optimizer" *
3232
" is attached.")
33-
@test_throws Exception MOI.get(model, MOI.SolverName())
33+
@test_throws exception MOI.get(model, attr)
34+
attr = MOI.Silent()
35+
exception = ErrorException(
36+
"Cannot query $(attr) from caching optimizer because no optimizer" *
37+
" is attached.")
38+
@test_throws exception MOI.get(model, attr)
3439

3540
attr = MOI.ResultCount()
3641
exception = ErrorException(
3742
"Cannot query $(attr) from caching optimizer because no optimizer" *
3843
" is attached.")
39-
@test_throws Exception MOI.get(model, MOI.ResultCount())
44+
@test_throws exception MOI.get(model, attr)
4045
end
4146
end
4247

48+
@testset "Copyable solver attributes" begin
49+
cache = MOIU.UniversalFallback(ModelForCachingOptimizer{Float64}())
50+
cached = MOIU.CachingOptimizer(cache, MOIU.MANUAL)
51+
MOI.set(cached, MOI.Silent(), true)
52+
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(ModelForMock{Float64}()))
53+
MOIU.reset_optimizer(cached, mock)
54+
@test MOI.get(mock, MOI.Silent())
55+
@test MOI.get(cached, MOI.Silent())
56+
MOI.set(cached, MOI.Silent(), false)
57+
@test !MOI.get(mock, MOI.Silent())
58+
@test !MOI.get(cached, MOI.Silent())
59+
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(ModelForMock{Float64}()))
60+
MOIU.reset_optimizer(cached, mock)
61+
@test !MOI.get(mock, MOI.Silent())
62+
@test !MOI.get(cached, MOI.Silent())
63+
MOI.set(cached, MOI.Silent(), true)
64+
@test MOI.get(mock, MOI.Silent())
65+
@test MOI.get(cached, MOI.Silent())
66+
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(ModelForMock{Float64}()))
67+
MOIU.reset_optimizer(cached, mock)
68+
@test MOI.get(mock, MOI.Silent())
69+
@test MOI.get(cached, MOI.Silent())
70+
end
71+
4372
@testset "CachingOptimizer MANUAL mode" begin
4473
m = MOIU.CachingOptimizer(ModelForCachingOptimizer{Float64}(), MOIU.MANUAL)
4574
@test MOIU.state(m) == MOIU.NO_OPTIMIZER

0 commit comments

Comments
 (0)