From a201b5ce65237cc81f4ae8403e8c0a9aafddfd4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 29 Aug 2021 11:16:27 +0200 Subject: [PATCH 1/4] Support infinite bounds in SplitIntervalBridge --- src/Bridges/Constraint/interval.jl | 106 +++++++++++++++++---- src/Bridges/Constraint/ltgt_to_interval.jl | 4 +- 2 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/Bridges/Constraint/interval.jl b/src/Bridges/Constraint/interval.jl index 8efddccdab..6e8f03e7f3 100644 --- a/src/Bridges/Constraint/interval.jl +++ b/src/Bridges/Constraint/interval.jl @@ -1,5 +1,19 @@ _lower_set(set::MOI.Interval) = MOI.GreaterThan(set.lower) +function _lower_set(set::MOI.Interval{T}) where {T<:AbstractFloat} + if set.lower == typemin(T) + return nothing + else + return MOI.GreaterThan(set.lower) + end +end _upper_set(set::MOI.Interval) = MOI.LessThan(set.upper) +function _upper_set(set::MOI.Interval{T}) where {T<:AbstractFloat} + if set.upper == typemax(T) + return nothing + else + return MOI.LessThan(set.upper) + end +end _lower_set(set::MOI.EqualTo) = MOI.GreaterThan(set.value) _upper_set(set::MOI.EqualTo) = MOI.LessThan(set.value) _lower_set(set::MOI.Zeros) = MOI.Nonnegatives(set.dimension) @@ -25,8 +39,8 @@ struct SplitIntervalBridge{ LS<:MOI.AbstractSet, US<:MOI.AbstractSet, } <: AbstractBridge - lower::MOI.ConstraintIndex{F,LS} - upper::MOI.ConstraintIndex{F,US} + lower::Union{Nothing,MOI.ConstraintIndex{F,LS}} + upper::Union{Nothing,MOI.ConstraintIndex{F,US}} end function bridge_constraint( @@ -35,8 +49,18 @@ function bridge_constraint( f::F, set::S, ) where {T,F,S,LS,US} - lower = MOI.add_constraint(model, f, _lower_set(set)) - upper = MOI.add_constraint(model, f, _upper_set(set)) + lower_set = _lower_set(set) + if lower_set === nothing + lower = nothing + else + lower = MOI.add_constraint(model, f, _lower_set(set)) + end + upper_set = _upper_set(set) + if upper_set === nothing + upper = nothing + else + upper = MOI.add_constraint(model, f, _upper_set(set)) + end return SplitIntervalBridge{T,F,S,LS,US}(lower, upper) end @@ -85,36 +109,56 @@ function concrete_bridge_type( end function MOI.get( - ::SplitIntervalBridge{T,F,S,LS}, + bridge::SplitIntervalBridge{T,F,S,LS}, ::MOI.NumberOfConstraints{F,LS}, )::Int64 where {T,F,S,LS} - return 1 + if bridge.lower === nothing + return 0 + else + return 1 + end end function MOI.get( ::SplitIntervalBridge{T,F,S,LS,US}, ::MOI.NumberOfConstraints{F,US}, )::Int64 where {T,F,S,LS,US} - return 1 + if bridge.upper === nothing + return 0 + else + return 1 + end end function MOI.get( bridge::SplitIntervalBridge{T,F,S,LS}, ::MOI.ListOfConstraintIndices{F,LS}, ) where {T,F,S,LS} - return [bridge.lower] + if bridge.lower === nothing + return MOI.ConstraintIndex{F,LS}[] + else + return [bridge.lower] + end end function MOI.get( bridge::SplitIntervalBridge{T,F,S,LS,US}, ::MOI.ListOfConstraintIndices{F,US}, ) where {T,F,S,LS,US} - return [bridge.upper] + if bridge.upper === nothing + return MOI.ConstraintIndex{F,US}[] + else + return [bridge.upper] + end end function MOI.delete(model::MOI.ModelLike, bridge::SplitIntervalBridge) - MOI.delete(model, bridge.lower) - MOI.delete(model, bridge.upper) + if bridge.lower !== nothing + MOI.delete(model, bridge.lower) + end + if bridge.upper !== nothing + MOI.delete(model, bridge.upper) + end return end @@ -128,13 +172,23 @@ function MOI.supports( return MOI.supports(model, attr, ci_1) && MOI.supports(model, attr, ci_2) end +function _error_double_inf(attr) + error("Cannot get `$attr` for a constraint in the interval [-Inf, Inf].") +end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, bridge::SplitIntervalBridge, ) # lower and upper should give the same value - return MOI.get(model, attr, bridge.lower) + if bridge.lower !== nothing + return MOI.get(model, attr, bridge.lower) + end + if bridge.upper !== nothing + return MOI.get(model, attr, bridge.upper) + end + _error_double_inf(attr) end function MOI.set( @@ -143,8 +197,12 @@ function MOI.set( bridge::SplitIntervalBridge, value, ) - MOI.set(model, attr, bridge.lower, value) - MOI.set(model, attr, bridge.upper, value) + if bridge.lower !== nothing + MOI.set(model, attr, bridge.lower, value) + end + if bridge.upper !== nothing + MOI.set(model, attr, bridge.upper, value) + end return end @@ -157,10 +215,22 @@ end function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart}, - bridge::SplitIntervalBridge, -) - return MOI.get(model, attr, bridge.lower) + - MOI.get(model, attr, bridge.upper) + bridge::SplitIntervalBridge{T}, +) where {T} + if bridge.lower === nothing + if bridge.upper === nothing + return zero(T) + else + return MOI.get(model, attr, bridge.upper) + end + else + if bridge.upper === nothing + return MOI.get(model, attr, bridge.lower) + else + return MOI.get(model, attr, bridge.lower) + + MOI.get(model, attr, bridge.upper) + end + end end function _split_dual_start(value) diff --git a/src/Bridges/Constraint/ltgt_to_interval.jl b/src/Bridges/Constraint/ltgt_to_interval.jl index f40c281c9b..7615528fa9 100644 --- a/src/Bridges/Constraint/ltgt_to_interval.jl +++ b/src/Bridges/Constraint/ltgt_to_interval.jl @@ -12,8 +12,8 @@ field by convention. !!! warning It is required that `T` be a `AbstractFloat` type because otherwise - typemin and typemax would either be not implemented (e.g. BigInt) - or would not give infinite value (e.g. Int). For this reason, + `typemin` and `typemax` would either be not implemented (e.g. `BigInt`) + or would not give infinite value (e.g. `Int`). For this reason, this bridge is only added to [`MathOptInterface.Bridges.full_bridge_optimizer`](@ref). From d1e9ff3e9e689c9e50cbc15e5e45e5b34f6b1292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 5 Sep 2021 20:11:00 +0200 Subject: [PATCH 2/4] Add docs and tests --- src/Bridges/Constraint/interval.jl | 153 +++++++++++++++++++++++----- test/Bridges/Constraint/interval.jl | 95 +++++++++++++++++ 2 files changed, 220 insertions(+), 28 deletions(-) diff --git a/src/Bridges/Constraint/interval.jl b/src/Bridges/Constraint/interval.jl index 6e8f03e7f3..43f3d646ce 100644 --- a/src/Bridges/Constraint/interval.jl +++ b/src/Bridges/Constraint/interval.jl @@ -31,8 +31,15 @@ a `F`-in-`US` constraint where we have either: For instance, if `F` is `MOI.ScalarAffineFunction` and `S` is `MOI.Interval`, it transforms the constraint ``l ≤ ⟨a, x⟩ + α ≤ u`` into the constraints ``⟨a, x⟩ + α ≥ l`` and ``⟨a, x⟩ + α ≤ u``. + +!!! note + If `T<:AbstractFloat` and `S` is `MOI.Interval{T}` then no lower (resp. + upper) bound constraint is created if the lower (resp. upper) bound is + `typemin(T)` (resp. `typemax(T)`). Similarly, when + [`MathOptInterface.ConstraintSet`](@ref) is set, a lower or upper bound + constraint may be deleted or created accordingly. """ -struct SplitIntervalBridge{ +mutable struct SplitIntervalBridge{ T, F<:MOI.AbstractFunction, S<:MOI.AbstractSet, @@ -41,6 +48,14 @@ struct SplitIntervalBridge{ } <: AbstractBridge lower::Union{Nothing,MOI.ConstraintIndex{F,LS}} upper::Union{Nothing,MOI.ConstraintIndex{F,US}} + # To allow the user to do + # ```jl + # x = MOI.add_variable(model) + # c = MOI.add_constraint(model, x, MOI.Interval(-Inf, Inf)) + # MOI.set(model, MOI.ConstraintSet(), c, MOI.Interval(0.0, Inf)) + # ``` + # we need to store the function to create the lower bound constraint. + func::Union{Nothing,F} end function bridge_constraint( @@ -61,7 +76,12 @@ function bridge_constraint( else upper = MOI.add_constraint(model, f, _upper_set(set)) end - return SplitIntervalBridge{T,F,S,LS,US}(lower, upper) + if lower === nothing && upper === nothing + func = f + else + func = nothing + end + return SplitIntervalBridge{T,F,S,LS,US}(lower, upper, func) end function MOI.supports_constraint( @@ -120,7 +140,7 @@ function MOI.get( end function MOI.get( - ::SplitIntervalBridge{T,F,S,LS,US}, + bridge::SplitIntervalBridge{T,F,S,LS,US}, ::MOI.NumberOfConstraints{F,US}, )::Int64 where {T,F,S,LS,US} if bridge.upper === nothing @@ -173,7 +193,9 @@ function MOI.supports( end function _error_double_inf(attr) - error("Cannot get `$attr` for a constraint in the interval [-Inf, Inf].") + return error( + "Cannot get `$attr` for a constraint in the interval `[-Inf, Inf]`.", + ) end function MOI.get( @@ -188,7 +210,7 @@ function MOI.get( if bridge.upper !== nothing return MOI.get(model, attr, bridge.upper) end - _error_double_inf(attr) + return _error_double_inf(attr) end function MOI.set( @@ -255,27 +277,50 @@ function MOI.set( bridge::SplitIntervalBridge{T}, value, ) where {T} - lower, upper = _split_dual_start(value) - MOI.set(model, attr, bridge.lower, lower) - MOI.set(model, attr, bridge.upper, upper) + if bridge.lower === nothing + if bridge.upper !== nothing + MOI.set(model, attr, bridge.upper, value) + end + else + if bridge.upper === nothing + MOI.set(model, attr, bridge.lower, value) + else + lower, upper = _split_dual_start(value) + MOI.set(model, attr, bridge.lower, lower) + MOI.set(model, attr, bridge.upper, upper) + end + end return end function MOI.get( model::MOI.ModelLike, - ::MOI.ConstraintBasisStatus, + attr::MOI.ConstraintBasisStatus, bridge::SplitIntervalBridge, ) - lower_stat = MOI.get(model, MOI.ConstraintBasisStatus(), bridge.lower) - if lower_stat == MOI.NONBASIC - return MOI.NONBASIC_AT_LOWER + if bridge.upper !== nothing + upper_stat = MOI.get(model, attr, bridge.upper) + if upper_stat == MOI.NONBASIC + return MOI.NONBASIC_AT_UPPER + end end - upper_stat = MOI.get(model, MOI.ConstraintBasisStatus(), bridge.upper) - if upper_stat == MOI.NONBASIC - return MOI.NONBASIC_AT_UPPER + if bridge.lower === nothing + if bridge.upper === nothing + # The only case where the interval `[-∞, ∞]` is allowed is for + # `VariableIndex` constraints but `ConstraintBasisStatus` is not + # defined for `VariableIndex` constraints. + _error_double_inf(attr) + else + return upper_stat + end + else + lower_stat = MOI.get(model, attr, bridge.lower) + if lower_stat == MOI.NONBASIC + return MOI.NONBASIC_AT_LOWER + end + # Both statuses must be BASIC or SUPER_BASIC, so just return the lower. + return lower_stat end - # Both statuses must be BASIC or SUPER_BASIC, so just return the lower. - return lower_stat end function MOI.modify( @@ -283,8 +328,12 @@ function MOI.modify( bridge::SplitIntervalBridge, change::MOI.AbstractFunctionModification, ) - MOI.modify(model, bridge.lower, change) - MOI.modify(model, bridge.upper, change) + if bridge.lower !== nothing + MOI.modify(model, bridge.lower, change) + end + if bridge.upper !== nothing + MOI.modify(model, bridge.upper, change) + end return end @@ -294,8 +343,12 @@ function MOI.set( bridge::SplitIntervalBridge{T,F}, func::F, ) where {T,F} - MOI.set(model, MOI.ConstraintFunction(), bridge.lower, func) - MOI.set(model, MOI.ConstraintFunction(), bridge.upper, func) + if bridge.lower !== nothing + MOI.set(model, MOI.ConstraintFunction(), bridge.lower, func) + end + if bridge.upper !== nothing + MOI.set(model, MOI.ConstraintFunction(), bridge.upper, func) + end return end @@ -305,8 +358,39 @@ function MOI.set( bridge::SplitIntervalBridge{T,F,S}, change::S, ) where {T,F,S} - MOI.set(model, MOI.ConstraintSet(), bridge.lower, _lower_set(change)) - MOI.set(model, MOI.ConstraintSet(), bridge.upper, _upper_set(change)) + lower_set = _lower_set(change) + upper_set = _upper_set(change) + if lower_set === nothing && upper_set === nothing + # The constraints are going to be deleted, we store the function before + # it is lost. + bridge.func = MOI.get(model, MOI.ConstraintFunction(), bridge) + end + if bridge.lower === nothing + if lower_set !== nothing + func = MOI.get(model, MOI.ConstraintFunction(), bridge) + bridge.lower = MOI.add_constraint(model, func, lower_set) + end + else + if lower_set === nothing + MOI.delete(model, bridge.lower) + bridge.lower = nothing + else + MOI.set(model, MOI.ConstraintSet(), bridge.lower, lower_set) + end + end + if bridge.upper === nothing + if upper_set !== nothing + func = MOI.get(model, MOI.ConstraintFunction(), bridge) + bridge.upper = MOI.add_constraint(model, func, upper_set) + end + else + if upper_set === nothing + MOI.delete(model, bridge.upper) + bridge.upper = nothing + else + MOI.set(model, MOI.ConstraintSet(), bridge.upper, upper_set) + end + end return end @@ -315,7 +399,13 @@ function MOI.get( attr::MOI.ConstraintFunction, bridge::SplitIntervalBridge, ) - return MOI.get(model, attr, bridge.lower) + if bridge.lower !== nothing + return MOI.get(model, attr, bridge.lower) + end + if bridge.upper !== nothing + return MOI.get(model, attr, bridge.upper) + end + return bridge.func end function MOI.get( @@ -323,10 +413,17 @@ function MOI.get( attr::MOI.ConstraintSet, bridge::SplitIntervalBridge{T,F,MOI.Interval{T}}, ) where {T,F} - return MOI.Interval( - MOI.get(model, attr, bridge.lower).lower, - MOI.get(model, attr, bridge.upper).upper, - ) + if bridge.lower === nothing + lower = typemin(T) + else + lower = MOI.get(model, attr, bridge.lower).lower + end + if bridge.upper === nothing + upper = typemax(T) + else + upper = MOI.get(model, attr, bridge.upper).upper + end + return MOI.Interval(lower, upper) end function MOI.get( diff --git a/test/Bridges/Constraint/interval.jl b/test/Bridges/Constraint/interval.jl index 352542e8fd..3c4292e2d6 100644 --- a/test/Bridges/Constraint/interval.jl +++ b/test/Bridges/Constraint/interval.jl @@ -264,6 +264,101 @@ function test_conic_linear_VectorOfVariables() return end +function _test_interval( + mock, bridged_mock, set::MOI.Interval{T}, + ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{T}}, +) where {T} + haslb = set.lower != typemin(T) + hasub = set.upper != typemax(T) + @test MOI.Bridges.is_bridged(bridged_mock, ci) + bridge = MOI.Bridges.bridge(bridged_mock, ci) + if haslb + @test bridge.lower !== nothing + MOI.set(mock, MOI.ConstraintBasisStatus(), bridge.lower, MOI.BASIC) + else + @test bridge.lower === nothing + end + if hasub + @test bridge.upper !== nothing + MOI.set(mock, MOI.ConstraintBasisStatus(), bridge.upper, MOI.BASIC) + else + @test bridge.upper === nothing + end + @test set == MOI.get(bridged_mock, MOI.ConstraintSet(), ci) + attr = MOI.NumberOfConstraints{MOI.VariableIndex,MOI.GreaterThan{T}}() + @test 0 == MOI.get(bridged_mock, attr) + @test (haslb ? 1 : 0) == MOI.get(mock, attr) + attr = MOI.NumberOfConstraints{MOI.VariableIndex,MOI.LessThan{T}}() + @test 0 == MOI.get(bridged_mock, attr) + @test (hasub ? 1 : 0) == MOI.get(mock, attr) + attr = MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.GreaterThan{T}}() + @test isempty(MOI.get(bridged_mock, attr)) + @test (haslb ? 1 : 0) == length(MOI.get(mock, attr)) + attr = MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.LessThan{T}}() + @test isempty(MOI.get(bridged_mock, attr)) + @test (hasub ? 1 : 0) == length(MOI.get(mock, attr)) + if haslb || hasub + @test one(T) == MOI.get(bridged_mock, MOI.ConstraintPrimal(), ci) + @test MOI.BASIC == MOI.get(bridged_mock, MOI.ConstraintBasisStatus(), ci) + for attr in [MOI.ConstraintPrimalStart(), MOI.ConstraintDualStart()] + MOI.set(bridged_mock, attr, ci, T(2)) + @test T(2) == MOI.get(bridged_mock, attr, ci) + end + else + for attr in [MOI.ConstraintPrimal(), MOI.ConstraintPrimalStart(), + MOI.ConstraintBasisStatus()] + err = ErrorException( + "Cannot get `$attr` for a constraint " * + "in the interval `[-Inf, Inf]`." + ) + @test_throws err MOI.get(bridged_mock, attr, ci) + end + @test zero(T) == MOI.get(bridged_mock, MOI.ConstraintDualStart(), ci) + end + @test zero(T) == MOI.get(bridged_mock, MOI.ConstraintDual(), ci) +end + +function test_infinite_bounds(::Type{T}=Float64) where {T<:AbstractFloat} + mock = MOI.Utilities.MockOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + ) + bridged_mock = MOI.Bridges.Constraint.SplitInterval{T}(mock) + x = MOI.add_variable(bridged_mock) + MOI.set(mock, MOI.VariablePrimal(), x, one(T)) + set = MOI.Interval(typemin(T), one(T)) + ci = MOI.add_constraint(bridged_mock, x, set) + _test_interval(mock, bridged_mock, set, ci) + set = MOI.Interval(zero(T), typemax(T)) + MOI.set(bridged_mock, MOI.ConstraintSet(), ci, set) + _test_interval(mock, bridged_mock, set, ci) + set = MOI.Interval(typemin(T), typemax(T)) + MOI.set(bridged_mock, MOI.ConstraintSet(), ci, set) + _test_interval(mock, bridged_mock, set, ci) + _test_delete_bridge( + bridged_mock, + ci, + 1, + ( + (MOI.VariableIndex, MOI.GreaterThan{T}, 0), + (MOI.VariableIndex, MOI.LessThan{T}, 0), + ), + ) + ci = MOI.add_constraint(bridged_mock, x, set) + _test_interval(mock, bridged_mock, set, ci) + _test_delete_bridge( + bridged_mock, + ci, + 1, + ( + (MOI.VariableIndex, MOI.GreaterThan{T}, 0), + (MOI.VariableIndex, MOI.LessThan{T}, 0), + ), + ) + set = MOI.Interval(zero(T), typemax(T)) + ci = MOI.add_constraint(bridged_mock, x, set) + _test_interval(mock, bridged_mock, set, ci) +end + end # module TestConstraintSplitInterval.runtests() From 6bd75a7641e73fb7bfeb8ac77e3fcbb8868adeb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 5 Sep 2021 20:15:24 +0200 Subject: [PATCH 3/4] Fix formatting --- test/Bridges/Constraint/interval.jl | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/test/Bridges/Constraint/interval.jl b/test/Bridges/Constraint/interval.jl index 3c4292e2d6..85821365ab 100644 --- a/test/Bridges/Constraint/interval.jl +++ b/test/Bridges/Constraint/interval.jl @@ -29,9 +29,11 @@ function test_split_basic() bridged_mock, config, include = [ - "test_basic_$(F)_$(S)" for F in - ["VariableIndex", "ScalarAffineFunction", "ScalarQuadraticFunction"] - for S in ["Interval", "EqualTo"] + "test_basic_$(F)_$(S)" for F in [ + "VariableIndex", + "ScalarAffineFunction", + "ScalarQuadraticFunction", + ] for S in ["Interval", "EqualTo"] ], ) MOI.Test.runtests( @@ -265,7 +267,9 @@ function test_conic_linear_VectorOfVariables() end function _test_interval( - mock, bridged_mock, set::MOI.Interval{T}, + mock, + bridged_mock, + set::MOI.Interval{T}, ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{T}}, ) where {T} haslb = set.lower != typemin(T) @@ -299,17 +303,21 @@ function _test_interval( @test (hasub ? 1 : 0) == length(MOI.get(mock, attr)) if haslb || hasub @test one(T) == MOI.get(bridged_mock, MOI.ConstraintPrimal(), ci) - @test MOI.BASIC == MOI.get(bridged_mock, MOI.ConstraintBasisStatus(), ci) + @test MOI.BASIC == + MOI.get(bridged_mock, MOI.ConstraintBasisStatus(), ci) for attr in [MOI.ConstraintPrimalStart(), MOI.ConstraintDualStart()] MOI.set(bridged_mock, attr, ci, T(2)) @test T(2) == MOI.get(bridged_mock, attr, ci) end else - for attr in [MOI.ConstraintPrimal(), MOI.ConstraintPrimalStart(), - MOI.ConstraintBasisStatus()] + for attr in [ + MOI.ConstraintPrimal(), + MOI.ConstraintPrimalStart(), + MOI.ConstraintBasisStatus(), + ] err = ErrorException( "Cannot get `$attr` for a constraint " * - "in the interval `[-Inf, Inf]`." + "in the interval `[-Inf, Inf]`.", ) @test_throws err MOI.get(bridged_mock, attr, ci) end @@ -318,7 +326,7 @@ function _test_interval( @test zero(T) == MOI.get(bridged_mock, MOI.ConstraintDual(), ci) end -function test_infinite_bounds(::Type{T}=Float64) where {T<:AbstractFloat} +function test_infinite_bounds(::Type{T} = Float64) where {T<:AbstractFloat} mock = MOI.Utilities.MockOptimizer( MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), ) @@ -356,7 +364,7 @@ function test_infinite_bounds(::Type{T}=Float64) where {T<:AbstractFloat} ) set = MOI.Interval(zero(T), typemax(T)) ci = MOI.add_constraint(bridged_mock, x, set) - _test_interval(mock, bridged_mock, set, ci) + return _test_interval(mock, bridged_mock, set, ci) end end # module From 2201fbbe973a639fb35cd4ba6f6b70265233c011 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 6 Sep 2021 08:55:35 +1200 Subject: [PATCH 4/4] Update interval.jl --- test/Bridges/Constraint/interval.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/Bridges/Constraint/interval.jl b/test/Bridges/Constraint/interval.jl index 85821365ab..6a0888f73a 100644 --- a/test/Bridges/Constraint/interval.jl +++ b/test/Bridges/Constraint/interval.jl @@ -29,11 +29,9 @@ function test_split_basic() bridged_mock, config, include = [ - "test_basic_$(F)_$(S)" for F in [ - "VariableIndex", - "ScalarAffineFunction", - "ScalarQuadraticFunction", - ] for S in ["Interval", "EqualTo"] + "test_basic_$(F)_$(S)" for F in + ["VariableIndex", "ScalarAffineFunction", "ScalarQuadraticFunction"] + for S in ["Interval", "EqualTo"] ], ) MOI.Test.runtests(