Skip to content

Commit

Permalink
adding ObjectiveLimit attribute (#2257)
Browse files Browse the repository at this point in the history
  • Loading branch information
matbesancon authored Sep 5, 2023
1 parent 8c7335a commit 7baa563
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/src/manual/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,4 @@ The following attributes are available:
* [`SolverVersion`](@ref)
* [`SolveTimeSec`](@ref)
* [`TimeLimitSec`](@ref)
* [`ObjectiveLimit`](@ref)
1 change: 1 addition & 0 deletions docs/src/reference/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ SolverName
SolverVersion
Silent
TimeLimitSec
ObjectiveLimit
RawOptimizerAttribute
NumberOfThreads
RawSolver
Expand Down
1 change: 1 addition & 0 deletions docs/src/tutorials/implementing.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ method for each attribute.
| [`Name`](@ref) | Yes | Yes | Yes |
| [`Silent`](@ref) | Yes | Yes | Yes |
| [`TimeLimitSec`](@ref) | Yes | Yes | Yes |
| [`ObjectiveLimit`](@ref) | Yes | Yes | Yes |
| [`RawOptimizerAttribute`](@ref) | Yes | Yes | Yes |
| [`NumberOfThreads`](@ref) | Yes | Yes | Yes |
| [`AbsoluteGapTolerance`](@ref) | Yes | Yes | Yes |
Expand Down
26 changes: 26 additions & 0 deletions src/Test/test_attribute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,32 @@ function setup_test(
return
end

function test_attribute_ObjectiveLimit(model::MOI.AbstractOptimizer, ::Config)
@requires MOI.supports(model, MOI.ObjectiveLimit())
# Get the current value to restore it at the end of the test
value = MOI.get(model, MOI.ObjectiveLimit())
MOI.set(model, MOI.ObjectiveLimit(), 0.0)
@test MOI.get(model, MOI.ObjectiveLimit()) == 0.0
MOI.set(model, MOI.ObjectiveLimit(), nothing)
@test MOI.get(model, MOI.ObjectiveLimit()) === nothing
MOI.set(model, MOI.ObjectiveLimit(), 1.0)
@test MOI.get(model, MOI.ObjectiveLimit()) == 1.0
MOI.set(model, MOI.ObjectiveLimit(), value)
@test value == MOI.get(model, MOI.ObjectiveLimit()) # Equality should hold
_test_attribute_value_type(model, MOI.ObjectiveLimit())
return
end
test_attribute_ObjectiveLimit(::MOI.ModelLike, ::Config) = nothing

function setup_test(
::typeof(test_attribute_ObjectiveLimit),
model::MOIU.MockOptimizer,
::Config,
)
MOI.set(model, MOI.ObjectiveLimit(), nothing)
return
end

"""
test_attribute_AbsoluteGapTolerance(model::MOI.AbstractOptimizer, config::Config)
Expand Down
17 changes: 17 additions & 0 deletions src/attributes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,23 @@ struct TimeLimitSec <: AbstractOptimizerAttribute end

attribute_value_type(::TimeLimitSec) = Union{Nothing,Float64}

"""
ObjectiveLimit()
An optimizer attribute for setting a limit on the objective value.
The provided limit must be a `Union{Real,Nothing}`.
When `set` to `nothing`, the limit reverts to the solver's default.
The default value is `nothing`.
The solver may stop when the [`ObjectiveValue`](@ref) is better (lower for
minimization, higher for maximization) than the `ObjectiveLimit`. If stopped,
the [`TerminationStatus`](@ref) should be `OBJECTIVE_LIMIT`.
"""
struct ObjectiveLimit <: AbstractOptimizerAttribute end

"""
RawOptimizerAttribute(name::String)
Expand Down
9 changes: 9 additions & 0 deletions test/Utilities/cachingoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ function test_default_attributes()
MOI.SolverName(),
MOI.Silent(),
MOI.TimeLimitSec(),
MOI.ObjectiveLimit(),
MOI.NumberOfThreads(),
MOI.ResultCount(),
)
Expand All @@ -209,22 +210,28 @@ function test_copyable_solver_attributes()
cached = MOIU.CachingOptimizer(cache, MOIU.MANUAL)
MOI.set(cached, MOI.Silent(), true)
MOI.set(cached, MOI.TimeLimitSec(), 0.0)
MOI.set(cached, MOI.ObjectiveLimit(), 42.0)
MOI.set(cached, MOI.NumberOfThreads(), 1)
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}()))
MOIU.reset_optimizer(cached, mock)
@test MOI.get(mock, MOI.Silent())
@test MOI.get(cached, MOI.Silent())
@test MOI.get(mock, MOI.TimeLimitSec()) == 0.0
@test MOI.get(cached, MOI.TimeLimitSec()) == 0.0
@test MOI.get(mock, MOI.ObjectiveLimit()) == 42.0
@test MOI.get(cached, MOI.ObjectiveLimit()) == 42.0
@test MOI.get(mock, MOI.NumberOfThreads()) == 1
@test MOI.get(cached, MOI.NumberOfThreads()) == 1
MOI.set(cached, MOI.Silent(), false)
MOI.set(cached, MOI.TimeLimitSec(), 1.0)
MOI.set(cached, MOI.ObjectiveLimit(), 1.0)
MOI.set(cached, MOI.NumberOfThreads(), 2)
@test !MOI.get(mock, MOI.Silent())
@test !MOI.get(cached, MOI.Silent())
@test MOI.get(mock, MOI.TimeLimitSec()) 1.0
@test MOI.get(mock, MOI.ObjectiveLimit()) 1.0
@test MOI.get(cached, MOI.TimeLimitSec()) 1.0
@test MOI.get(cached, MOI.ObjectiveLimit()) 1.0
@test MOI.get(mock, MOI.NumberOfThreads()) == 2
@test MOI.get(cached, MOI.NumberOfThreads()) == 2
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}()))
Expand All @@ -233,6 +240,8 @@ function test_copyable_solver_attributes()
@test !MOI.get(cached, MOI.Silent())
@test MOI.get(mock, MOI.TimeLimitSec()) 1.0
@test MOI.get(cached, MOI.TimeLimitSec()) 1.0
@test MOI.get(mock, MOI.ObjectiveLimit()) 1.0
@test MOI.get(cached, MOI.ObjectiveLimit()) 1.0
@test MOI.get(mock, MOI.NumberOfThreads()) == 2
@test MOI.get(cached, MOI.NumberOfThreads()) == 2
MOI.set(cached, MOI.Silent(), true)
Expand Down
1 change: 1 addition & 0 deletions test/Utilities/universalfallback.jl
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ function test_missing_attribute()
model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
@test MOI.get(model, MOI.Test.UnknownModelAttribute()) === nothing
@test MOI.get(model, MOI.TimeLimitSec()) === nothing
@test MOI.get(model, MOI.ObjectiveLimit()) === nothing
return
end

Expand Down

0 comments on commit 7baa563

Please sign in to comment.