Skip to content
2 changes: 2 additions & 0 deletions docs/src/reference/models.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ TimeLimitSec
RawOptimizerAttribute
NumberOfThreads
RawSolver
AbsoluteGapTolerance
RelativeGapTolerance
```

List of attributes useful for optimizers
Expand Down
2 changes: 2 additions & 0 deletions docs/src/tutorials/implementing.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ method for each attribute.
| [`TimeLimitSec`](@ref) | Yes | Yes | Yes |
| [`RawOptimizerAttribute`](@ref) | Yes | Yes | Yes |
| [`NumberOfThreads`](@ref) | Yes | Yes | Yes |
| [`AbsoluteGapTolerance`](@ref) | Yes | Yes | Yes |
| [`RelativeGapTolerance`](@ref) | Yes | Yes | Yes |

For example:
```julia
Expand Down
66 changes: 66 additions & 0 deletions src/Test/test_attribute.jl
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,72 @@ function setup_test(
return
end

"""
test_attribute_AbsoluteGapTolerance(model::MOI.AbstractOptimizer, config::Config)

Test that the [`MOI.AbsoluteGapTolerance`](@ref) attribute is implemented for
`model`.
"""
function test_attribute_AbsoluteGapTolerance(
model::MOI.AbstractOptimizer,
::Config,
)
@requires MOI.supports(model, MOI.AbsoluteGapTolerance())
# Get the current value to restore it at the end of the test
value = MOI.get(model, MOI.AbsoluteGapTolerance())
MOI.set(model, MOI.AbsoluteGapTolerance(), 1e-2)
@test MOI.get(model, MOI.AbsoluteGapTolerance()) == 1e-2
MOI.set(model, MOI.AbsoluteGapTolerance(), 100.0)
@test MOI.get(model, MOI.AbsoluteGapTolerance()) == 100.0
MOI.set(model, MOI.AbsoluteGapTolerance(), value)
@test value == MOI.get(model, MOI.AbsoluteGapTolerance())
_test_attribute_value_type(model, MOI.AbsoluteGapTolerance())
return
end
test_attribute_AbsoluteGapTolerance(::MOI.ModelLike, ::Config) = nothing

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

"""
test_attribute_RelativeGapTolerance(model::MOI.AbstractOptimizer, config::Config)

Test that the [`MOI.RelativeGapTolerance`](@ref) attribute is implemented for
`model`.
"""
function test_attribute_RelativeGapTolerance(
model::MOI.AbstractOptimizer,
::Config,
)
@requires MOI.supports(model, MOI.RelativeGapTolerance())
# Get the current value to restore it at the end of the test
value = MOI.get(model, MOI.RelativeGapTolerance())
MOI.set(model, MOI.RelativeGapTolerance(), 1e-2)
@test MOI.get(model, MOI.RelativeGapTolerance()) == 1e-2
MOI.set(model, MOI.RelativeGapTolerance(), 5e-5)
@test MOI.get(model, MOI.RelativeGapTolerance()) == 5e-5
MOI.set(model, MOI.RelativeGapTolerance(), value)
@test value == MOI.get(model, MOI.RelativeGapTolerance())
_test_attribute_value_type(model, MOI.RelativeGapTolerance())
return
end
test_attribute_RelativeGapTolerance(::MOI.ModelLike, ::Config) = nothing

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

"""
test_attribute_after_empty(model::MOI.AbstractOptimizer, config::Config)

Expand Down
51 changes: 51 additions & 0 deletions src/attributes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,57 @@ struct NumberOfThreads <: AbstractOptimizerAttribute end

attribute_value_type(::NumberOfThreads) = Union{Nothing,Int}

"""
RelativeGapTolerance()

An optimizer attribute for setting the relative gap tolerance for an optimization.
This is an _optimizer_ attribute, and should be set before calling [`optimize!`](@ref).
When set to `nothing` (if supported), uses solver default.

If you are looking for the relative gap of the current best solution, see
[`RelativeGap`](@ref). If no limit nor issue is encountered during the optimization,
the value of [`RelativeGap`](@ref) should be at most as large as `RelativeGapTolerance`.

```julia
# Before optimizing: set relative gap tolerance
MOI.set(model, MOI.RelativeGapTolerance(), 1e-3) # set 0.1% relative gap tolerance
MOI.optimize!(model)

# After optimizing (assuming all went well)
# The relative gap tolerance has not changed...
MOI.get(model, MOI.RelativeGapTolerance()) # returns 1e-3
# ... and the relative gap of the obtained solution is smaller or equal to the tolerance
MOI.get(model, MOI.RelativeGap()) # should return something ≤ 1e-3
```

!!! warning
The mathematical definition of "relative gap", and its allowed range, are solver-dependent.
Typically, solvers expect a value between `0` and `1`.
"""
struct RelativeGapTolerance <: AbstractOptimizerAttribute end

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

"""
AbsoluteGapTolerance()

An optimizer attribute for setting the absolute gap tolerance for an optimization.
This is an _optimizer_ attribute, and should be set before calling [`optimize!`](@ref).
When set to `nothing` (if supported), uses solver default.

To set a _relative_ gap tolerance, see [`RelativeGapTolerance`](@ref).

!!! warning
The mathematical definition of "absolute gap", and its treatment during the optimization,
are solver-dependent. However, assuming no other limit nor issue is encountered during the
optimization, most solvers that implement this attribute will stop once ``|f - b| ≤ g_{abs}``,
where ``b`` is the best bound, ``f`` is the best feasible objective value, and ``g_{abs}``
is the absolute gap.
"""
struct AbsoluteGapTolerance <: AbstractOptimizerAttribute end

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

### Callbacks

"""
Expand Down