Skip to content

Commit a60994e

Browse files
authored
Add RelativeGapTolerance and AbsoluteGapTolerance optimizer attributes (#1944)
1 parent b34036e commit a60994e

File tree

4 files changed

+121
-0
lines changed

4 files changed

+121
-0
lines changed

docs/src/reference/models.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ TimeLimitSec
7474
RawOptimizerAttribute
7575
NumberOfThreads
7676
RawSolver
77+
AbsoluteGapTolerance
78+
RelativeGapTolerance
7779
```
7880

7981
List of attributes useful for optimizers

docs/src/tutorials/implementing.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ method for each attribute.
338338
| [`TimeLimitSec`](@ref) | Yes | Yes | Yes |
339339
| [`RawOptimizerAttribute`](@ref) | Yes | Yes | Yes |
340340
| [`NumberOfThreads`](@ref) | Yes | Yes | Yes |
341+
| [`AbsoluteGapTolerance`](@ref) | Yes | Yes | Yes |
342+
| [`RelativeGapTolerance`](@ref) | Yes | Yes | Yes |
341343

342344
For example:
343345
```julia

src/Test/test_attribute.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,72 @@ function setup_test(
190190
return
191191
end
192192

193+
"""
194+
test_attribute_AbsoluteGapTolerance(model::MOI.AbstractOptimizer, config::Config)
195+
196+
Test that the [`MOI.AbsoluteGapTolerance`](@ref) attribute is implemented for
197+
`model`.
198+
"""
199+
function test_attribute_AbsoluteGapTolerance(
200+
model::MOI.AbstractOptimizer,
201+
::Config,
202+
)
203+
@requires MOI.supports(model, MOI.AbsoluteGapTolerance())
204+
# Get the current value to restore it at the end of the test
205+
value = MOI.get(model, MOI.AbsoluteGapTolerance())
206+
MOI.set(model, MOI.AbsoluteGapTolerance(), 1e-2)
207+
@test MOI.get(model, MOI.AbsoluteGapTolerance()) == 1e-2
208+
MOI.set(model, MOI.AbsoluteGapTolerance(), 100.0)
209+
@test MOI.get(model, MOI.AbsoluteGapTolerance()) == 100.0
210+
MOI.set(model, MOI.AbsoluteGapTolerance(), value)
211+
@test value == MOI.get(model, MOI.AbsoluteGapTolerance())
212+
_test_attribute_value_type(model, MOI.AbsoluteGapTolerance())
213+
return
214+
end
215+
test_attribute_AbsoluteGapTolerance(::MOI.ModelLike, ::Config) = nothing
216+
217+
function setup_test(
218+
::typeof(test_attribute_AbsoluteGapTolerance),
219+
model::MOIU.MockOptimizer,
220+
::Config,
221+
)
222+
MOI.set(model, MOI.AbsoluteGapTolerance(), nothing)
223+
return
224+
end
225+
226+
"""
227+
test_attribute_RelativeGapTolerance(model::MOI.AbstractOptimizer, config::Config)
228+
229+
Test that the [`MOI.RelativeGapTolerance`](@ref) attribute is implemented for
230+
`model`.
231+
"""
232+
function test_attribute_RelativeGapTolerance(
233+
model::MOI.AbstractOptimizer,
234+
::Config,
235+
)
236+
@requires MOI.supports(model, MOI.RelativeGapTolerance())
237+
# Get the current value to restore it at the end of the test
238+
value = MOI.get(model, MOI.RelativeGapTolerance())
239+
MOI.set(model, MOI.RelativeGapTolerance(), 1e-2)
240+
@test MOI.get(model, MOI.RelativeGapTolerance()) == 1e-2
241+
MOI.set(model, MOI.RelativeGapTolerance(), 5e-5)
242+
@test MOI.get(model, MOI.RelativeGapTolerance()) == 5e-5
243+
MOI.set(model, MOI.RelativeGapTolerance(), value)
244+
@test value == MOI.get(model, MOI.RelativeGapTolerance())
245+
_test_attribute_value_type(model, MOI.RelativeGapTolerance())
246+
return
247+
end
248+
test_attribute_RelativeGapTolerance(::MOI.ModelLike, ::Config) = nothing
249+
250+
function setup_test(
251+
::typeof(test_attribute_RelativeGapTolerance),
252+
model::MOIU.MockOptimizer,
253+
::Config,
254+
)
255+
MOI.set(model, MOI.RelativeGapTolerance(), nothing)
256+
return
257+
end
258+
193259
"""
194260
test_attribute_after_empty(model::MOI.AbstractOptimizer, config::Config)
195261

src/attributes.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,57 @@ struct NumberOfThreads <: AbstractOptimizerAttribute end
819819

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

822+
"""
823+
RelativeGapTolerance()
824+
825+
An optimizer attribute for setting the relative gap tolerance for an optimization.
826+
This is an _optimizer_ attribute, and should be set before calling [`optimize!`](@ref).
827+
When set to `nothing` (if supported), uses solver default.
828+
829+
If you are looking for the relative gap of the current best solution, see
830+
[`RelativeGap`](@ref). If no limit nor issue is encountered during the optimization,
831+
the value of [`RelativeGap`](@ref) should be at most as large as `RelativeGapTolerance`.
832+
833+
```julia
834+
# Before optimizing: set relative gap tolerance
835+
MOI.set(model, MOI.RelativeGapTolerance(), 1e-3) # set 0.1% relative gap tolerance
836+
MOI.optimize!(model)
837+
838+
# After optimizing (assuming all went well)
839+
# The relative gap tolerance has not changed...
840+
MOI.get(model, MOI.RelativeGapTolerance()) # returns 1e-3
841+
# ... and the relative gap of the obtained solution is smaller or equal to the tolerance
842+
MOI.get(model, MOI.RelativeGap()) # should return something ≤ 1e-3
843+
```
844+
845+
!!! warning
846+
The mathematical definition of "relative gap", and its allowed range, are solver-dependent.
847+
Typically, solvers expect a value between `0` and `1`.
848+
"""
849+
struct RelativeGapTolerance <: AbstractOptimizerAttribute end
850+
851+
attribute_value_type(::RelativeGapTolerance) = Union{Nothing,Float64}
852+
853+
"""
854+
AbsoluteGapTolerance()
855+
856+
An optimizer attribute for setting the absolute gap tolerance for an optimization.
857+
This is an _optimizer_ attribute, and should be set before calling [`optimize!`](@ref).
858+
When set to `nothing` (if supported), uses solver default.
859+
860+
To set a _relative_ gap tolerance, see [`RelativeGapTolerance`](@ref).
861+
862+
!!! warning
863+
The mathematical definition of "absolute gap", and its treatment during the optimization,
864+
are solver-dependent. However, assuming no other limit nor issue is encountered during the
865+
optimization, most solvers that implement this attribute will stop once ``|f - b| ≤ g_{abs}``,
866+
where ``b`` is the best bound, ``f`` is the best feasible objective value, and ``g_{abs}``
867+
is the absolute gap.
868+
"""
869+
struct AbsoluteGapTolerance <: AbstractOptimizerAttribute end
870+
871+
attribute_value_type(::AbsoluteGapTolerance) = Union{Nothing,Float64}
872+
822873
### Callbacks
823874

824875
"""

0 commit comments

Comments
 (0)