From c16286f21606cca0c63a96efe523d93e73be2ad8 Mon Sep 17 00:00:00 2001 From: mtanneau Date: Mon, 11 Jul 2022 16:30:03 -0400 Subject: [PATCH 01/13] Add RelativeGapTolerance optimizer attribute --- src/attributes.jl | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/attributes.jl b/src/attributes.jl index b532778fee..2afff25bbc 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -819,6 +819,37 @@ 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!`. + +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` 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} + ### Callbacks """ From bbea76c7381103a4ae763cc56bd4211c19b265ce Mon Sep 17 00:00:00 2001 From: mtanneau <9593025+mtanneau@users.noreply.github.com> Date: Tue, 12 Jul 2022 09:03:23 -0400 Subject: [PATCH 02/13] Fix indent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benoît Legat --- src/attributes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index 2afff25bbc..57159262a4 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -826,7 +826,7 @@ An optimizer attribute for setting the relative gap tolerance for an optimizatio This is an _optimizer_ attribute, and should be set before calling `optimize!`. 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, +[RelativeGap](@ref). If no limit nor issue is encountered during the optimization, the value of `RelativeGap` should be at most as large as `RelativeGapTolerance`. ```julia From 812838b2adc96bc14c2667b893dfad728f33a203 Mon Sep 17 00:00:00 2001 From: mtanneau <9593025+mtanneau@users.noreply.github.com> Date: Tue, 12 Jul 2022 09:03:30 -0400 Subject: [PATCH 03/13] Fix indent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benoît Legat --- src/attributes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index 57159262a4..33e8ae5616 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -827,7 +827,7 @@ This is an _optimizer_ attribute, and should be set before calling `optimize!`. 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` should be at most as large as `RelativeGapTolerance`. +the value of `RelativeGap` should be at most as large as `RelativeGapTolerance`. ```julia # Before optimizing: set relative gap tolerance From 7a7802c997b6fa0889c6e4a57dac6b74fb22cd97 Mon Sep 17 00:00:00 2001 From: mtanneau <9593025+mtanneau@users.noreply.github.com> Date: Tue, 12 Jul 2022 09:04:53 -0400 Subject: [PATCH 04/13] Remove empty line --- src/attributes.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index 33e8ae5616..42a372d516 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -844,7 +844,6 @@ 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 From 4141b162bdb75aab50945e953dbab2d2659c8951 Mon Sep 17 00:00:00 2001 From: mtanneau Date: Tue, 12 Jul 2022 09:45:50 -0400 Subject: [PATCH 05/13] Add AbsoluteGapTolerance attribute --- src/attributes.jl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index 42a372d516..d69dc2e856 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -826,7 +826,7 @@ An optimizer attribute for setting the relative gap tolerance for an optimizatio This is an _optimizer_ attribute, and should be set before calling `optimize!`. 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, +[`RelativeGap`](@ref). If no limit nor issue is encountered during the optimization, the value of `RelativeGap` should be at most as large as `RelativeGapTolerance`. ```julia @@ -849,6 +849,25 @@ 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!`. + +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 """ From 4404cb167ac67a41f93453ecac150dc38de966a0 Mon Sep 17 00:00:00 2001 From: mtanneau <9593025+mtanneau@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:53:21 -0400 Subject: [PATCH 06/13] Add ref in docstring Co-authored-by: Oscar Dowson --- src/attributes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index d69dc2e856..052858c6e3 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -823,7 +823,7 @@ 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!`. +This is an _optimizer_ attribute, and should be set before calling [`optimize!`](@ref). 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, From 1de7fe8cfe3298e4e5e8146e4b16c4440ff19fec Mon Sep 17 00:00:00 2001 From: mtanneau <9593025+mtanneau@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:53:28 -0400 Subject: [PATCH 07/13] Add ref in docstring Co-authored-by: Oscar Dowson --- src/attributes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index 052858c6e3..d3ff8b68db 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -827,7 +827,7 @@ This is an _optimizer_ attribute, and should be set before calling [`optimize!`] 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` should be at most as large as `RelativeGapTolerance`. +the value of [`RelativeGap`](@ref) should be at most as large as `RelativeGapTolerance`. ```julia # Before optimizing: set relative gap tolerance From a26c74d398cbd6c6b450f8a77ab11c0d77898798 Mon Sep 17 00:00:00 2001 From: mtanneau <9593025+mtanneau@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:53:37 -0400 Subject: [PATCH 08/13] Add ref in docstring Co-authored-by: Oscar Dowson --- src/attributes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index d3ff8b68db..d753626f59 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -853,7 +853,7 @@ 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!`. +This is an _optimizer_ attribute, and should be set before calling [`optimize!`](@ref). To set a _relative_ gap tolerance, see [`RelativeGapTolerance``](@ref). From 0638b6bd495bbd911d8c9cca9955b344461caaa6 Mon Sep 17 00:00:00 2001 From: mtanneau <9593025+mtanneau@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:53:47 -0400 Subject: [PATCH 09/13] Fix typo Co-authored-by: Oscar Dowson --- src/attributes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes.jl b/src/attributes.jl index d753626f59..7a31e93cd8 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -855,7 +855,7 @@ attribute_value_type(::RelativeGapTolerance) = Union{Nothing,Float64} 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). -To set a _relative_ gap tolerance, see [`RelativeGapTolerance``](@ref). +To set a _relative_ gap tolerance, see [`RelativeGapTolerance`](@ref). !!! warning The mathematical definition of "absolute gap", and its treatment during the optimization, From 5495327b6456b214cb95ddd3e301491b68f61b0e Mon Sep 17 00:00:00 2001 From: mtanneau Date: Tue, 12 Jul 2022 17:00:20 -0400 Subject: [PATCH 10/13] Update docs --- docs/src/reference/models.md | 2 ++ docs/src/tutorials/implementing.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/src/reference/models.md b/docs/src/reference/models.md index d75f1325f5..ffb231833e 100644 --- a/docs/src/reference/models.md +++ b/docs/src/reference/models.md @@ -74,6 +74,8 @@ TimeLimitSec RawOptimizerAttribute NumberOfThreads RawSolver +AbsoluteGapTolerance +RelativeGapTolerance ``` List of attributes useful for optimizers diff --git a/docs/src/tutorials/implementing.md b/docs/src/tutorials/implementing.md index a77e8fc51d..f474e4bf31 100644 --- a/docs/src/tutorials/implementing.md +++ b/docs/src/tutorials/implementing.md @@ -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 From 84a2dc496838d6ed0611c5270281fe468f9ed745 Mon Sep 17 00:00:00 2001 From: mtanneau Date: Tue, 12 Jul 2022 17:05:03 -0400 Subject: [PATCH 11/13] Add attribute test --- src/Test/test_attribute.jl | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/Test/test_attribute.jl b/src/Test/test_attribute.jl index e0b04e1074..05d88c81e1 100644 --- a/src/Test/test_attribute.jl +++ b/src/Test/test_attribute.jl @@ -190,6 +190,66 @@ 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) + @test MOI.get(model, MOI.AbsoluteGapTolerance()) == 100 + 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) From fd5fec8754becc428423a8c8af06f426204af819 Mon Sep 17 00:00:00 2001 From: mtanneau Date: Tue, 12 Jul 2022 17:55:27 -0400 Subject: [PATCH 12/13] Fix formatting --- src/Test/test_attribute.jl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Test/test_attribute.jl b/src/Test/test_attribute.jl index 05d88c81e1..a8f98c0caf 100644 --- a/src/Test/test_attribute.jl +++ b/src/Test/test_attribute.jl @@ -196,7 +196,10 @@ end Test that the [`MOI.AbsoluteGapTolerance`](@ref) attribute is implemented for `model`. """ -function test_attribute_AbsoluteGapTolerance(model::MOI.AbstractOptimizer, ::Config) +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()) @@ -226,7 +229,10 @@ end Test that the [`MOI.RelativeGapTolerance`](@ref) attribute is implemented for `model`. """ -function test_attribute_RelativeGapTolerance(model::MOI.AbstractOptimizer, ::Config) +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()) From bc94dbf68538774c06a471539ed6d7c489a6dd21 Mon Sep 17 00:00:00 2001 From: Mathieu Tanneau Date: Thu, 14 Jul 2022 08:34:18 -0400 Subject: [PATCH 13/13] Update docs and fix value type in test --- src/Test/test_attribute.jl | 4 ++-- src/attributes.jl | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Test/test_attribute.jl b/src/Test/test_attribute.jl index a8f98c0caf..a4751c7ba1 100644 --- a/src/Test/test_attribute.jl +++ b/src/Test/test_attribute.jl @@ -205,8 +205,8 @@ function test_attribute_AbsoluteGapTolerance( 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) - @test MOI.get(model, MOI.AbsoluteGapTolerance()) == 100 + 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()) diff --git a/src/attributes.jl b/src/attributes.jl index 7a31e93cd8..b8b13c9605 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -824,6 +824,7 @@ attribute_value_type(::NumberOfThreads) = Union{Nothing,Int} 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, @@ -854,6 +855,7 @@ attribute_value_type(::RelativeGapTolerance) = Union{Nothing,Float64} 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).