From 10f7ed1972b2e5775a10235b45452026b17d82b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 May 2024 09:09:35 +0200 Subject: [PATCH 1/6] Add Variable bridges to use runtests without unbridged_variable --- src/Bridges/Bridges.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 119007c243..88a718fcf0 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -144,7 +144,7 @@ MOI.get_fallback(model::MOI.ModelLike, ::ListOfNonstandardBridges) = Type[] include("precompile.jl") -function _test_structural_identical(a::MOI.ModelLike, b::MOI.ModelLike) +function _test_structural_identical(a::MOI.ModelLike, b::MOI.ModelLike; allow_constraint_function_error::Bool = false) # Test that the variables are the same. We make the strong assumption that # the variables are added in the same order to both models. a_x = MOI.get(a, MOI.ListOfVariableIndices()) @@ -190,7 +190,15 @@ function _test_structural_identical(a::MOI.ModelLike, b::MOI.ModelLike) Test.@test MOI.supports_constraint(b, F, S) # Check that each function in `b` matches a function in `a` for ci in MOI.get(b, MOI.ListOfConstraintIndices{F,S}()) - f_b = MOI.get(b, MOI.ConstraintFunction(), ci) + try + f_b = MOI.get(b, MOI.ConstraintFunction(), ci) + catch err + if allow_constraint_function_error && err isa MOI.GetAttributeNotAllowed{MOI.ConstraintFunction} + continue + else + rethrow(err) + end + end f_b = MOI.Utilities.map_indices(x_map, f_b) s_b = MOI.get(b, MOI.ConstraintSet(), ci) # We don't care about the order that constraints are added, only @@ -253,6 +261,7 @@ function runtests( constraint_start = 1.2, eltype = Float64, print_inner_model::Bool = false, + allow_outer_constraint_function_error::Bool = false, ) # Load model and bridge it inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) @@ -267,7 +276,7 @@ function runtests( # Load a non-bridged input model, and check that getters are the same. test = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) input_fn(test) - _test_structural_identical(test, model) + _test_structural_identical(test, model; allow_constraint_function_error = allow_outer_constraint_function_error) # Load a bridged target model, and check that getters are the same. target = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) output_fn(target) From b99e03971b00d9cb929c0175891f1b2cc270ab2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 10 May 2024 09:16:26 +0200 Subject: [PATCH 2/6] Fix format --- src/Bridges/Bridges.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 88a718fcf0..64565a37aa 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -144,7 +144,11 @@ MOI.get_fallback(model::MOI.ModelLike, ::ListOfNonstandardBridges) = Type[] include("precompile.jl") -function _test_structural_identical(a::MOI.ModelLike, b::MOI.ModelLike; allow_constraint_function_error::Bool = false) +function _test_structural_identical( + a::MOI.ModelLike, + b::MOI.ModelLike; + allow_constraint_function_error::Bool = false, +) # Test that the variables are the same. We make the strong assumption that # the variables are added in the same order to both models. a_x = MOI.get(a, MOI.ListOfVariableIndices()) @@ -193,7 +197,8 @@ function _test_structural_identical(a::MOI.ModelLike, b::MOI.ModelLike; allow_co try f_b = MOI.get(b, MOI.ConstraintFunction(), ci) catch err - if allow_constraint_function_error && err isa MOI.GetAttributeNotAllowed{MOI.ConstraintFunction} + if allow_constraint_function_error && + err isa MOI.GetAttributeNotAllowed{MOI.ConstraintFunction} continue else rethrow(err) @@ -276,7 +281,11 @@ function runtests( # Load a non-bridged input model, and check that getters are the same. test = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) input_fn(test) - _test_structural_identical(test, model; allow_constraint_function_error = allow_outer_constraint_function_error) + _test_structural_identical( + test, + model; + allow_constraint_function_error = allow_outer_constraint_function_error, + ) # Load a bridged target model, and check that getters are the same. target = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) output_fn(target) From 619f6ab71d963499477bbb8d2ce4e5e070f8d399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 25 Jun 2024 14:00:33 +0200 Subject: [PATCH 3/6] Add tests --- src/Bridges/Bridges.jl | 29 ++++++++++++++++++++++------- test/Bridges/Variable/zeros.jl | 15 +++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 64565a37aa..945be822d2 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -147,7 +147,7 @@ include("precompile.jl") function _test_structural_identical( a::MOI.ModelLike, b::MOI.ModelLike; - allow_constraint_function_error::Bool = false, + cannot_unbridge::Bool = false, ) # Test that the variables are the same. We make the strong assumption that # the variables are added in the same order to both models. @@ -194,10 +194,10 @@ function _test_structural_identical( Test.@test MOI.supports_constraint(b, F, S) # Check that each function in `b` matches a function in `a` for ci in MOI.get(b, MOI.ListOfConstraintIndices{F,S}()) - try - f_b = MOI.get(b, MOI.ConstraintFunction(), ci) + f_b = try + MOI.get(b, MOI.ConstraintFunction(), ci) catch err - if allow_constraint_function_error && + if cannot_unbridge && err isa MOI.GetAttributeNotAllowed{MOI.ConstraintFunction} continue else @@ -238,6 +238,7 @@ end variable_start = 1.2, constraint_start = 1.2, eltype = Float64, + cannot_unbridge::Bool = false, ) Run a series of tests that check the correctness of `Bridge`. @@ -245,6 +246,10 @@ Run a series of tests that check the correctness of `Bridge`. `input_fn` and `output_fn` are functions such that `input_fn(model)` and `output_fn(model)` load the corresponding model into `model`. +Set `cannot_unbridge` to `true` if the bridge is a variable bridge +that does not supports [`Variables.unbridged_func`](@ref) so that +the tests allow errors that can be raised due to this. + ## Example ```jldoctest; setup=:(import MathOptInterface as MOI) @@ -266,7 +271,7 @@ function runtests( constraint_start = 1.2, eltype = Float64, print_inner_model::Bool = false, - allow_outer_constraint_function_error::Bool = false, + cannot_unbridge::Bool = false, ) # Load model and bridge it inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) @@ -284,7 +289,7 @@ function runtests( _test_structural_identical( test, model; - allow_constraint_function_error = allow_outer_constraint_function_error, + cannot_unbridge = cannot_unbridge, ) # Load a bridged target model, and check that getters are the same. target = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) @@ -309,7 +314,17 @@ function runtests( # Test ConstraintPrimalStart and ConstraintDualStart for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent()) for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) - set = MOI.get(model, MOI.ConstraintSet(), ci) + set = try + MOI.get(model, MOI.ConstraintSet(), ci) + catch err + # Could be thrown by `unbridged_function` + if cannot_unbridge && + err isa MOI.GetAttributeNotAllowed{MOI.ConstraintFunction} + continue + else + rethrow(err) + end + end for attr in (MOI.ConstraintPrimalStart(), MOI.ConstraintDualStart()) if MOI.supports(model, attr, MOI.ConstraintIndex{F,S}) MOI.set(model, attr, ci, nothing) diff --git a/test/Bridges/Variable/zeros.jl b/test/Bridges/Variable/zeros.jl index 923bc868b0..c9447f615a 100644 --- a/test/Bridges/Variable/zeros.jl +++ b/test/Bridges/Variable/zeros.jl @@ -23,6 +23,21 @@ end include("../utilities.jl") +function test_runtests() + MOI.Bridges.runtests( + MOI.Bridges.Variable.ZerosBridge, + model -> begin + x, _ = MOI.add_constrained_variables(model, MOI.Zeros(2)) + MOI.add_constraint(model, 1.0 * x[1] + 2.0 * x[2], MOI.EqualTo(3.0)) + end, + model -> begin + MOI.add_constraint(model, zero(MOI.ScalarAffineFunction{Float64}), MOI.EqualTo(3.0)) + end; + cannot_unbridge = true, + ) + return +end + function test_zeros() mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) bridged_mock = MOI.Bridges.Variable.Zeros{Float64}(mock) From 15675ebf04a2099083cf46d11eae85b40d7850a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 25 Jun 2024 14:38:59 +0200 Subject: [PATCH 4/6] Fix format --- src/Bridges/Bridges.jl | 8 ++------ test/Bridges/Variable/zeros.jl | 12 ++++++++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 945be822d2..867ce591f0 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -247,7 +247,7 @@ Run a series of tests that check the correctness of `Bridge`. and `output_fn(model)` load the corresponding model into `model`. Set `cannot_unbridge` to `true` if the bridge is a variable bridge -that does not supports [`Variables.unbridged_func`](@ref) so that +that does not supports [`Variable.unbridged_func`](@ref) so that the tests allow errors that can be raised due to this. ## Example @@ -286,11 +286,7 @@ function runtests( # Load a non-bridged input model, and check that getters are the same. test = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) input_fn(test) - _test_structural_identical( - test, - model; - cannot_unbridge = cannot_unbridge, - ) + _test_structural_identical(test, model; cannot_unbridge = cannot_unbridge) # Load a bridged target model, and check that getters are the same. target = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{eltype}()) output_fn(target) diff --git a/test/Bridges/Variable/zeros.jl b/test/Bridges/Variable/zeros.jl index c9447f615a..7dad8da30c 100644 --- a/test/Bridges/Variable/zeros.jl +++ b/test/Bridges/Variable/zeros.jl @@ -28,10 +28,18 @@ function test_runtests() MOI.Bridges.Variable.ZerosBridge, model -> begin x, _ = MOI.add_constrained_variables(model, MOI.Zeros(2)) - MOI.add_constraint(model, 1.0 * x[1] + 2.0 * x[2], MOI.EqualTo(3.0)) + MOI.add_constraint( + model, + 1.0 * x[1] + 2.0 * x[2], + MOI.EqualTo(3.0), + ) end, model -> begin - MOI.add_constraint(model, zero(MOI.ScalarAffineFunction{Float64}), MOI.EqualTo(3.0)) + MOI.add_constraint( + model, + zero(MOI.ScalarAffineFunction{Float64}), + MOI.EqualTo(3.0), + ) end; cannot_unbridge = true, ) From b0765578c84e17e83196aaa41cca878d28b00ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 25 Jun 2024 14:47:29 +0200 Subject: [PATCH 5/6] Fix --- src/Bridges/Bridges.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 867ce591f0..7add7cb2a3 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -247,7 +247,7 @@ Run a series of tests that check the correctness of `Bridge`. and `output_fn(model)` load the corresponding model into `model`. Set `cannot_unbridge` to `true` if the bridge is a variable bridge -that does not supports [`Variable.unbridged_func`](@ref) so that +that does not supports [`Variable.unbridged_function`](@ref) so that the tests allow errors that can be raised due to this. ## Example From 3d6575a8d9a0f92281ecd22e1c8120f799af8c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 25 Jun 2024 16:32:28 +0200 Subject: [PATCH 6/6] Fix --- src/Bridges/Bridges.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 7add7cb2a3..32b4da2948 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -247,7 +247,7 @@ Run a series of tests that check the correctness of `Bridge`. and `output_fn(model)` load the corresponding model into `model`. Set `cannot_unbridge` to `true` if the bridge is a variable bridge -that does not supports [`Variable.unbridged_function`](@ref) so that +for which [`Variable.unbridged_map`](@ref) returns `nothing` so that the tests allow errors that can be raised due to this. ## Example