From 7966f481de984c348355cd0a2ef8bebc691b3056 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Sat, 21 Dec 2024 14:17:46 +1300 Subject: [PATCH 1/9] [Bridges.Constraint] add bridges in alphabetical order --- src/Bridges/Constraint/Constraint.jl | 146 ++++++++++++++++----------- 1 file changed, 85 insertions(+), 61 deletions(-) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 743c360c67..996934c359 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -30,83 +30,107 @@ The coefficient type used is `T`. """ function add_all_bridges(model, ::Type{T}) where {T} if T <: AbstractFloat + # Out of order: put these bridges at the start for backwards + # compatibility. MOI.Bridges.add_bridge(model, GreaterToIntervalBridge{T}) MOI.Bridges.add_bridge(model, LessToIntervalBridge{T}) end + MOI.Bridges.add_bridge(model, AllDifferentToCountDistinctBridge{T}) + MOI.Bridges.add_bridge(model, BinPackingToMILPBridge{T}) + MOI.Bridges.add_bridge(model, CircuitToMILPBridge{T}) + MOI.Bridges.add_bridge(model, ComplexNormInfinityToSecondOrderConeBridge{T}) + MOI.Bridges.add_bridge(model, CountAtLeastToCountBelongsBridge{T}) + MOI.Bridges.add_bridge(model, CountBelongsToMILPBridge{T}) + MOI.Bridges.add_bridge(model, CountDistinctToMILPBridge{T}) + MOI.Bridges.add_bridge(model, CountGreaterThanToMILPBridge{T}) + MOI.Bridges.add_bridge( + model, + ExponentialConeToScalarNonlinearFunctionBridge{T}, + ) + # * FunctionConversionBridge{T} + # This bridge is not added because, even though it is not abstract, it + # is highly parameterized, and parameterized versions such as + # ScalarFunctionizeBridge are added. + MOI.Bridges.add_bridge(model, GeoMeanBridge{T}) + MOI.Bridges.add_bridge(model, GeoMeanToPowerBridge{T}) + MOI.Bridges.add_bridge(model, GeoMeantoRelEntrBridge{T}) MOI.Bridges.add_bridge(model, GreaterToLessBridge{T}) + MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T}) + MOI.Bridges.add_bridge(model, IndicatorActiveOnFalseBridge{T}) + MOI.Bridges.add_bridge(model, IndicatorGreaterToLessThanBridge{T}) + MOI.Bridges.add_bridge(model, IndicatorLessToGreaterThanBridge{T}) + # * IndicatorSetMapBridge{T} + # This bridge is not added because, even though it is not abstract, it + # is highly parameterized, and parameterized versions such as + # IndicatorGreaterToLessThanBridge are added. + MOI.Bridges.add_bridge(model, IndicatorSOS1Bridge{T}) + MOI.Bridges.add_bridge(model, IndicatorToMILPBridge{T}) + MOI.Bridges.add_bridge(model, InequalityToComplementsBridge{T}) + MOI.Bridges.add_bridge(model, IntegerToZeroOneBridge{T}) MOI.Bridges.add_bridge(model, LessToGreaterBridge{T}) + MOI.Bridges.add_bridge(model, LogDetBridge{T}) MOI.Bridges.add_bridge(model, NonnegToNonposBridge{T}) MOI.Bridges.add_bridge(model, NonposToNonnegBridge{T}) - MOI.Bridges.add_bridge(model, ScalarizeBridge{T}) - MOI.Bridges.add_bridge(model, VectorizeBridge{T}) - MOI.Bridges.add_bridge(model, ScalarSlackBridge{T}) - MOI.Bridges.add_bridge(model, VectorSlackBridge{T}) - MOI.Bridges.add_bridge(model, ScalarFunctionizeBridge{T}) - MOI.Bridges.add_bridge(model, VectorFunctionizeBridge{T}) - MOI.Bridges.add_bridge(model, ToScalarQuadraticBridge{T}) - MOI.Bridges.add_bridge(model, ToVectorQuadraticBridge{T}) - MOI.Bridges.add_bridge(model, ToScalarNonlinearBridge{T}) - MOI.Bridges.add_bridge(model, SplitHyperRectangleBridge{T}) - MOI.Bridges.add_bridge(model, SplitIntervalBridge{T}) - MOI.Bridges.add_bridge(model, SplitComplexEqualToBridge{T}) - MOI.Bridges.add_bridge(model, SplitComplexZerosBridge{T}) - MOI.Bridges.add_bridge(model, QuadtoSOCBridge{T}) - # We do not add `(R)SOCtoNonConvexQuad` because it starts with a convex - # conic constraint and generate a non-convex constraint (in the QCP - # interpretation). MOI.Bridges.add_bridge(model, NormInfinityBridge{T}) + MOI.Bridges.add_bridge(model, NormInfinityConeToNormConeBridge{T}) + MOI.Bridges.add_bridge(model, NormNuclearBridge{T}) MOI.Bridges.add_bridge(model, NormOneBridge{T}) - MOI.Bridges.add_bridge(model, GeoMeantoRelEntrBridge{T}) - MOI.Bridges.add_bridge(model, GeoMeanBridge{T}) - MOI.Bridges.add_bridge(model, GeoMeanToPowerBridge{T}) - MOI.Bridges.add_bridge(model, NormToPowerBridge{T}) MOI.Bridges.add_bridge(model, NormOneConeToNormConeBridge{T}) - MOI.Bridges.add_bridge(model, SecondOrderConeToNormConeBridge{T}) - MOI.Bridges.add_bridge(model, NormInfinityConeToNormConeBridge{T}) - MOI.Bridges.add_bridge(model, ComplexNormInfinityToSecondOrderConeBridge{T}) - MOI.Bridges.add_bridge(model, RelativeEntropyBridge{T}) + # * NormSpecialCaseBridge{T} + # This bridge is not added because, even though it is not abstract, it + # is highly parameterized, and parameterized versions such as + # NormOneConeToNormConeBridge are added. MOI.Bridges.add_bridge(model, NormSpectralBridge{T}) - MOI.Bridges.add_bridge(model, NormNuclearBridge{T}) - MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T}) - MOI.Bridges.add_bridge(model, SquareBridge{T}) - MOI.Bridges.add_bridge(model, SetDotScalingBridge{T}) - MOI.Bridges.add_bridge(model, SetDotInverseScalingBridge{T}) - MOI.Bridges.add_bridge(model, LogDetBridge{T}) + MOI.Bridges.add_bridge(model, NormToPowerBridge{T}) + # * NumberConversionBridge{T} + # This bridge is not added by default because it would silently enable + # models with mixed precision. In most cases, this is a bug in the + # user's code, so we leave this bridge as opt-in. + MOI.Bridges.add_bridge(model, QuadtoSOCBridge{T}) + MOI.Bridges.add_bridge(model, ReifiedAllDifferentToCountDistinctBridge{T}) + MOI.Bridges.add_bridge(model, ReifiedCountDistinctToMILPBridge{T}) + MOI.Bridges.add_bridge(model, RelativeEntropyBridge{T}) MOI.Bridges.add_bridge(model, RootDetBridge{T}) - MOI.Bridges.add_bridge(model, RSOCtoSOCBridge{T}) - MOI.Bridges.add_bridge(model, SOCtoRSOCBridge{T}) - # We do not add `SOCtoPSDBridge` as transforming the `SOC` to `RSOC` and - # then to `PSD` produces a smaller SDP constraint. - # MOI.Bridges.add_bridge(model, SOCtoPSDBridge{T}) + # * RSOCtoNonConvexQuadBridge{T} + # This bridge is not added by default because it starts with a convex + # conic constraint and generate a non-convex constraint (in the QCP + # interpretation). MOI.Bridges.add_bridge(model, RSOCtoPSDBridge{T}) - MOI.Bridges.add_bridge(model, IndicatorActiveOnFalseBridge{T}) - MOI.Bridges.add_bridge(model, IndicatorSOS1Bridge{T}) - MOI.Bridges.add_bridge(model, IndicatorLessToGreaterThanBridge{T}) - MOI.Bridges.add_bridge(model, IndicatorGreaterToLessThanBridge{T}) + MOI.Bridges.add_bridge(model, RSOCtoSOCBridge{T}) + MOI.Bridges.add_bridge(model, ScalarFunctionizeBridge{T}) + MOI.Bridges.add_bridge(model, ScalarizeBridge{T}) + MOI.Bridges.add_bridge(model, ScalarSlackBridge{T}) + MOI.Bridges.add_bridge(model, SecondOrderConeToNormConeBridge{T}) MOI.Bridges.add_bridge(model, SemiToBinaryBridge{T}) - MOI.Bridges.add_bridge(model, ZeroOneBridge{T}) - MOI.Bridges.add_bridge(model, IntegerToZeroOneBridge{T}) - MOI.Bridges.add_bridge(model, InequalityToComplementsBridge{T}) - # Do not add by default - # MOI.Bridges.add_bridge(model, NumberConversionBridge{T}) - # Constraint programming bridges - MOI.Bridges.add_bridge(model, AllDifferentToCountDistinctBridge{T}) - MOI.Bridges.add_bridge(model, ReifiedAllDifferentToCountDistinctBridge{T}) - MOI.Bridges.add_bridge(model, BinPackingToMILPBridge{T}) - MOI.Bridges.add_bridge(model, CircuitToMILPBridge{T}) - MOI.Bridges.add_bridge(model, CountAtLeastToCountBelongsBridge{T}) - MOI.Bridges.add_bridge(model, CountBelongsToMILPBridge{T}) - MOI.Bridges.add_bridge(model, CountDistinctToMILPBridge{T}) - MOI.Bridges.add_bridge(model, ReifiedCountDistinctToMILPBridge{T}) - MOI.Bridges.add_bridge(model, CountGreaterThanToMILPBridge{T}) - MOI.Bridges.add_bridge(model, TableToMILPBridge{T}) + # * SetConversionBridge{T} + # This bridge is not added because, even though it is not abstract, it + # is highly parameterized, and it intended for use by MOI extensions. + MOI.Bridges.add_bridge(model, SetDotInverseScalingBridge{T}) + MOI.Bridges.add_bridge(model, SetDotScalingBridge{T}) + # * SOCtoNonConvexQuadBridge{T} + # This bridge is not added by default because it starts with a convex + # conic constraint and generate a non-convex constraint (in the QCP + # interpretation). + # * SOCtoPSDBridge{T} + # This bridge is not added because transforming the `SOC` to `RSOC` and + # then to `PSD` produces a smaller SDP constraint. `RSOCtoPSDBridge` is + # added by default. + MOI.Bridges.add_bridge(model, SOCtoRSOCBridge{T}) MOI.Bridges.add_bridge(model, SOS1ToMILPBridge{T}) MOI.Bridges.add_bridge(model, SOS2ToMILPBridge{T}) - MOI.Bridges.add_bridge(model, IndicatorToMILPBridge{T}) - MOI.Bridges.add_bridge( - model, - ExponentialConeToScalarNonlinearFunctionBridge{T}, - ) + MOI.Bridges.add_bridge(model, SplitComplexEqualToBridge{T}) + MOI.Bridges.add_bridge(model, SplitComplexZerosBridge{T}) + MOI.Bridges.add_bridge(model, SplitHyperRectangleBridge{T}) + MOI.Bridges.add_bridge(model, SplitIntervalBridge{T}) + MOI.Bridges.add_bridge(model, SquareBridge{T}) + MOI.Bridges.add_bridge(model, TableToMILPBridge{T}) + MOI.Bridges.add_bridge(model, ToScalarNonlinearBridge{T}) + MOI.Bridges.add_bridge(model, ToScalarQuadraticBridge{T}) + MOI.Bridges.add_bridge(model, ToVectorQuadraticBridge{T}) + MOI.Bridges.add_bridge(model, VectorFunctionizeBridge{T}) + MOI.Bridges.add_bridge(model, VectorizeBridge{T}) + MOI.Bridges.add_bridge(model, VectorSlackBridge{T}) + MOI.Bridges.add_bridge(model, ZeroOneBridge{T}) return end From d54d3a0f761e59f843a9480f83ac41e17d999fe4 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 31 Dec 2024 10:28:54 +1300 Subject: [PATCH 2/9] Update --- src/Bridges/Constraint/Constraint.jl | 17 +++++++---------- test/Bridges/bridge_optimizer.jl | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 996934c359..9b59b72745 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -29,12 +29,6 @@ Add all bridges defined in the `Bridges.Constraint` submodule to `model`. The coefficient type used is `T`. """ function add_all_bridges(model, ::Type{T}) where {T} - if T <: AbstractFloat - # Out of order: put these bridges at the start for backwards - # compatibility. - MOI.Bridges.add_bridge(model, GreaterToIntervalBridge{T}) - MOI.Bridges.add_bridge(model, LessToIntervalBridge{T}) - end MOI.Bridges.add_bridge(model, AllDifferentToCountDistinctBridge{T}) MOI.Bridges.add_bridge(model, BinPackingToMILPBridge{T}) MOI.Bridges.add_bridge(model, CircuitToMILPBridge{T}) @@ -43,10 +37,11 @@ function add_all_bridges(model, ::Type{T}) where {T} MOI.Bridges.add_bridge(model, CountBelongsToMILPBridge{T}) MOI.Bridges.add_bridge(model, CountDistinctToMILPBridge{T}) MOI.Bridges.add_bridge(model, CountGreaterThanToMILPBridge{T}) - MOI.Bridges.add_bridge( - model, - ExponentialConeToScalarNonlinearFunctionBridge{T}, - ) + # * ExponentialConeToScalarNonlinearFunctionBridge{T} + # This bridge is not added by default because it starts with a convex + # conic constraint and adds a nonlinear constraint that local NLP + # solvers like Ipopt can struggle with because of log(x) when x is 0. + # In addition, the bridge does not support ConstraintDual. # * FunctionConversionBridge{T} # This bridge is not added because, even though it is not abstract, it # is highly parameterized, and parameterized versions such as @@ -54,6 +49,7 @@ function add_all_bridges(model, ::Type{T}) where {T} MOI.Bridges.add_bridge(model, GeoMeanBridge{T}) MOI.Bridges.add_bridge(model, GeoMeanToPowerBridge{T}) MOI.Bridges.add_bridge(model, GeoMeantoRelEntrBridge{T}) + MOI.Bridges.add_bridge(model, GreaterToIntervalBridge{T}) MOI.Bridges.add_bridge(model, GreaterToLessBridge{T}) MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T}) MOI.Bridges.add_bridge(model, IndicatorActiveOnFalseBridge{T}) @@ -68,6 +64,7 @@ function add_all_bridges(model, ::Type{T}) where {T} MOI.Bridges.add_bridge(model, InequalityToComplementsBridge{T}) MOI.Bridges.add_bridge(model, IntegerToZeroOneBridge{T}) MOI.Bridges.add_bridge(model, LessToGreaterBridge{T}) + MOI.Bridges.add_bridge(model, LessToIntervalBridge{T}) MOI.Bridges.add_bridge(model, LogDetBridge{T}) MOI.Bridges.add_bridge(model, NonnegToNonposBridge{T}) MOI.Bridges.add_bridge(model, NonposToNonnegBridge{T}) diff --git a/test/Bridges/bridge_optimizer.jl b/test/Bridges/bridge_optimizer.jl index 7871eafe1f..7ee72de240 100644 --- a/test/Bridges/bridge_optimizer.jl +++ b/test/Bridges/bridge_optimizer.jl @@ -567,6 +567,20 @@ function test_double_deletion_scalar() # careful not to delete the second one twice, see https://github.com/jump-dev/MathOptInterface.jl/issues/1231 model = MOI.instantiate(AffineOnlyModel{Float64}, with_bridge_type = Float64) + # If LessToGreaterBridge exists, this goes from: + # VariableIndex(x) in LessThan(1.0) + # ScalarAffineFunction(-1.0x) in GreaterThan(-1.0) (LessToGreater) + # ScalarAffineFunction(-1.0x) in Interval(-1.0, Inf) (GreaterToInterval) + # we want instead + # VariableIndex(x) in LessThan(1.0) + # VariableIndex(x) in Interval(-Inf, 1.0) (LessToInterval) + # ScalarAffineFunction(1.0x) in Interval(-Inf, 1.0) (FunctionConversion) + # To check that we handle the LessThan and Interval sets on the same + # VariableIndex correctly. + MOI.Bridges.remove_bridge( + model, + MOI.Bridges.Constraint.LessToGreaterBridge{Float64}, + ) x = MOI.add_variable(model) c = MOI.add_constraint(model, x, MOI.LessThan(1.0)) # Need to test the bridging to make sure it's not functionized first as otherwise, From 10e897d7ebafe2b73647bd663ba59f677417ef5e Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 31 Dec 2024 10:45:38 +1300 Subject: [PATCH 3/9] Update --- src/Bridges/Constraint/Constraint.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index 9b59b72745..ab9cd8afcc 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -49,7 +49,9 @@ function add_all_bridges(model, ::Type{T}) where {T} MOI.Bridges.add_bridge(model, GeoMeanBridge{T}) MOI.Bridges.add_bridge(model, GeoMeanToPowerBridge{T}) MOI.Bridges.add_bridge(model, GeoMeantoRelEntrBridge{T}) - MOI.Bridges.add_bridge(model, GreaterToIntervalBridge{T}) + if T <: AbstractFloat # See note in docstring of AbstractToIntervalBridge + MOI.Bridges.add_bridge(model, GreaterToIntervalBridge{T}) + end MOI.Bridges.add_bridge(model, GreaterToLessBridge{T}) MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T}) MOI.Bridges.add_bridge(model, IndicatorActiveOnFalseBridge{T}) @@ -64,7 +66,9 @@ function add_all_bridges(model, ::Type{T}) where {T} MOI.Bridges.add_bridge(model, InequalityToComplementsBridge{T}) MOI.Bridges.add_bridge(model, IntegerToZeroOneBridge{T}) MOI.Bridges.add_bridge(model, LessToGreaterBridge{T}) - MOI.Bridges.add_bridge(model, LessToIntervalBridge{T}) + if T <: AbstractFloat # See note in docstring of AbstractToIntervalBridge + MOI.Bridges.add_bridge(model, LessToIntervalBridge{T}) + end MOI.Bridges.add_bridge(model, LogDetBridge{T}) MOI.Bridges.add_bridge(model, NonnegToNonposBridge{T}) MOI.Bridges.add_bridge(model, NonposToNonnegBridge{T}) From 77e13623feaef049e1d38c141a6f62e2adb61198 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 31 Dec 2024 13:50:05 +1300 Subject: [PATCH 4/9] Update Constraint.jl --- src/Bridges/Constraint/Constraint.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index ab9cd8afcc..164a6009e3 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -63,7 +63,11 @@ function add_all_bridges(model, ::Type{T}) where {T} # IndicatorGreaterToLessThanBridge are added. MOI.Bridges.add_bridge(model, IndicatorSOS1Bridge{T}) MOI.Bridges.add_bridge(model, IndicatorToMILPBridge{T}) - MOI.Bridges.add_bridge(model, InequalityToComplementsBridge{T}) + # * InequalityToComplementsBridge{T} + # This bridge is not added because of a bug in Convex.jl: + # https://github.com/jump-dev/Convex.jl/blob/ca5324217575af263bfeee20b3e0526bed051887/src/MOI_wrapper.jl#L119-L133 + # It is also really useful only to PATHSolver.jl, which could add this + # to MOI.ListOfRequiredBridges. MOI.Bridges.add_bridge(model, IntegerToZeroOneBridge{T}) MOI.Bridges.add_bridge(model, LessToGreaterBridge{T}) if T <: AbstractFloat # See note in docstring of AbstractToIntervalBridge From f0abce071e9c01b531f58340e298e27cae488d54 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 3 Jan 2025 11:18:33 +1300 Subject: [PATCH 5/9] Add high cost --- src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl b/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl index df695beb7d..7056ffe732 100644 --- a/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl +++ b/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl @@ -77,6 +77,11 @@ end const SOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoPSDBridge{T},OT} +# This bridge destorys a lot of structure and adding PSD variables is almost +# always undesirable. We give this bridge an arbitrarily hight cost so that it +# is used only if necessary. +bridging_cost(::Type{<:SOCtoPSDBridge}) = 10.0 + function concrete_bridge_type( ::Type{<:SOCtoPSDBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, @@ -186,6 +191,11 @@ end const RSOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T},OT} +# This bridge destorys a lot of structure and adding PSD variables is almost +# always undesirable. We give this bridge an arbitrarily hight cost so that it +# is used only if necessary. +bridging_cost(::Type{<:RSOCtoPSDBridge}) = 10.0 + function concrete_bridge_type( ::Type{<:RSOCtoPSDBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, From 93c543f330c00c3351cbd46ece0a57a58ad3e13b Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 3 Jan 2025 17:02:55 +1300 Subject: [PATCH 6/9] Add test --- test/Bridges/Constraint/SOCtoPSDBridge.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/Bridges/Constraint/SOCtoPSDBridge.jl b/test/Bridges/Constraint/SOCtoPSDBridge.jl index 441b4937a4..3f91df871b 100644 --- a/test/Bridges/Constraint/SOCtoPSDBridge.jl +++ b/test/Bridges/Constraint/SOCtoPSDBridge.jl @@ -211,6 +211,26 @@ function test_runtests() return end +function test_bridging_cost_SOCtoPSD() + inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Constraint.SOCtoPSD{Float64}(inner) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint(model, x, MOI.SecondOrderCone(3)) + bridge = model.map[c] + MOI.Bridges.bridging_cost(typeof(bridge)) == 10.0 + return +end + +function test_bridging_cost_RSOCtoPSD() + inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + model = MOI.Bridges.Constraint.RSOCtoPSD{Float64}(inner) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint(model, x, MOI.RotatedSecondOrderCone(3)) + bridge = model.map[c] + MOI.Bridges.bridging_cost(typeof(bridge)) == 10.0 + return +end + end # module TestConstraintSOCtoPSD.runtests() From f11fbfa7704ed47772091d35b1643922efe629b9 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Sat, 4 Jan 2025 11:33:03 +1300 Subject: [PATCH 7/9] Apply suggestions from code review --- src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl b/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl index 7056ffe732..9bb9a01c41 100644 --- a/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl +++ b/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl @@ -80,7 +80,7 @@ const SOCtoPSD{T,OT<:MOI.ModelLike} = # This bridge destorys a lot of structure and adding PSD variables is almost # always undesirable. We give this bridge an arbitrarily hight cost so that it # is used only if necessary. -bridging_cost(::Type{<:SOCtoPSDBridge}) = 10.0 +MOI.Bridges.bridging_cost(::Type{<:SOCtoPSDBridge}) = 10.0 function concrete_bridge_type( ::Type{<:SOCtoPSDBridge{T}}, @@ -194,7 +194,7 @@ const RSOCtoPSD{T,OT<:MOI.ModelLike} = # This bridge destorys a lot of structure and adding PSD variables is almost # always undesirable. We give this bridge an arbitrarily hight cost so that it # is used only if necessary. -bridging_cost(::Type{<:RSOCtoPSDBridge}) = 10.0 +MOI.Bridges.bridging_cost(::Type{<:RSOCtoPSDBridge}) = 10.0 function concrete_bridge_type( ::Type{<:RSOCtoPSDBridge{T}}, From fabb24b7cd91681df2e24bd8378ac129f7fca323 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 6 Jan 2025 08:33:39 +1300 Subject: [PATCH 8/9] Update --- .../Constraint/bridges/SOCtoPSDBridge.jl | 8 +-- .../Variable/bridges/RSOCtoPSDBridge.jl | 4 +- test/Bridges/lazy_bridge_optimizer.jl | 62 ------------------- 3 files changed, 6 insertions(+), 68 deletions(-) diff --git a/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl b/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl index 9bb9a01c41..32133a17ff 100644 --- a/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl +++ b/src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl @@ -78,8 +78,8 @@ const SOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoPSDBridge{T},OT} # This bridge destorys a lot of structure and adding PSD variables is almost -# always undesirable. We give this bridge an arbitrarily hight cost so that it -# is used only if necessary. +# always undesirable. We give this bridge a high cost so that it is used only if +# necessary. MOI.Bridges.bridging_cost(::Type{<:SOCtoPSDBridge}) = 10.0 function concrete_bridge_type( @@ -192,8 +192,8 @@ const RSOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T},OT} # This bridge destorys a lot of structure and adding PSD variables is almost -# always undesirable. We give this bridge an arbitrarily hight cost so that it -# is used only if necessary. +# always undesirable. We give this bridge a high cost so that it is used only if +# necessary. MOI.Bridges.bridging_cost(::Type{<:RSOCtoPSDBridge}) = 10.0 function concrete_bridge_type( diff --git a/src/Bridges/Variable/bridges/RSOCtoPSDBridge.jl b/src/Bridges/Variable/bridges/RSOCtoPSDBridge.jl index 5fe932df84..780be3102a 100644 --- a/src/Bridges/Variable/bridges/RSOCtoPSDBridge.jl +++ b/src/Bridges/Variable/bridges/RSOCtoPSDBridge.jl @@ -57,8 +57,8 @@ const RSOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T},OT} # This bridge destorys a lot of structure and adding PSD variables is almost -# always undesirable. We give this bridge an arbitrarily hight cost so that it -# is used only if necessary. +# always undesirable. We give this bridge a high cost so that it is used only if +# necessary. MOI.Bridges.bridging_cost(::Type{<:RSOCtoPSDBridge}) = 10.0 function bridge_constrained_variable( diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index ce174c10d7..b14b8b9ca8 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -1704,68 +1704,6 @@ function test_MOI_runtests_No_RSOCModel() return end -# Test that RSOCtoPSD is used instead of RSOC+SOCtoPSD as it is a shortest path. -function test_bridge_selection() - mock = MOI.Utilities.MockOptimizer(NoRSOCModel{Float64}()) - bridged_mock = MOI.Bridges.LazyBridgeOptimizer(mock) - - MOI.Bridges.add_bridge( - bridged_mock, - MOI.Bridges.Constraint.SplitIntervalBridge{Float64}, - ) - MOI.Bridges.add_bridge( - bridged_mock, - MOI.Bridges.Constraint.RSOCtoPSDBridge{Float64}, - ) - MOI.Bridges.add_bridge( - bridged_mock, - MOI.Bridges.Constraint.SOCtoPSDBridge{Float64}, - ) - MOI.Bridges.add_bridge( - bridged_mock, - MOI.Bridges.Constraint.RSOCtoSOCBridge{Float64}, - ) - @test !(MOI.supports_constraint( - bridged_mock, - MOI.VectorAffineFunction{Float64}, - MOI.LogDetConeTriangle, - )) - x = MOI.add_variables(bridged_mock, 3) - err = MOI.UnsupportedConstraint{ - MOI.VectorAffineFunction{Float64}, - MOI.LogDetConeTriangle, - }() - @test_throws err begin - MOI.Bridges.bridge_type( - bridged_mock, - MOI.VectorAffineFunction{Float64}, - MOI.LogDetConeTriangle, - ) - end - c = MOI.add_constraint( - bridged_mock, - MOI.VectorOfVariables(x), - MOI.RotatedSecondOrderCone(3), - ) - @test MOI.Bridges.bridge_type( - bridged_mock, - MOI.VectorOfVariables, - MOI.RotatedSecondOrderCone, - ) == MOI.Bridges.Constraint.RSOCtoPSDBridge{ - Float64, - MOI.VectorAffineFunction{Float64}, - MOI.VectorOfVariables, - } - @test MOI.Bridges.bridge(bridged_mock, c) isa - MOI.Bridges.Constraint.RSOCtoPSDBridge - @test bridged_mock.graph.constraint_dist[MOI.Bridges.node( - bridged_mock, - MOI.VectorOfVariables, - MOI.RotatedSecondOrderCone, - ).index] == 1 - return -end - function test_supports() mock = MOI.Utilities.MockOptimizer(NoRSOCModel{Float64}()) full_bridged_mock = MOI.Bridges.full_bridge_optimizer(mock, Float64) From a1df3594af5b413705884fce64b37f07df99a24d Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 6 Jan 2025 08:36:27 +1300 Subject: [PATCH 9/9] Update --- test/Bridges/lazy_bridge_optimizer.jl | 61 +++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/test/Bridges/lazy_bridge_optimizer.jl b/test/Bridges/lazy_bridge_optimizer.jl index b14b8b9ca8..af5148d039 100644 --- a/test/Bridges/lazy_bridge_optimizer.jl +++ b/test/Bridges/lazy_bridge_optimizer.jl @@ -1704,6 +1704,67 @@ function test_MOI_runtests_No_RSOCModel() return end +function test_bridge_selection() + mock = MOI.Utilities.MockOptimizer(NoRSOCModel{Float64}()) + bridged_mock = MOI.Bridges.LazyBridgeOptimizer(mock) + + MOI.Bridges.add_bridge( + bridged_mock, + MOI.Bridges.Constraint.SplitIntervalBridge{Float64}, + ) + MOI.Bridges.add_bridge( + bridged_mock, + MOI.Bridges.Constraint.RSOCtoPSDBridge{Float64}, + ) + MOI.Bridges.add_bridge( + bridged_mock, + MOI.Bridges.Constraint.SOCtoPSDBridge{Float64}, + ) + MOI.Bridges.add_bridge( + bridged_mock, + MOI.Bridges.Constraint.RSOCtoSOCBridge{Float64}, + ) + @test !(MOI.supports_constraint( + bridged_mock, + MOI.VectorAffineFunction{Float64}, + MOI.LogDetConeTriangle, + )) + x = MOI.add_variables(bridged_mock, 3) + err = MOI.UnsupportedConstraint{ + MOI.VectorAffineFunction{Float64}, + MOI.LogDetConeTriangle, + }() + @test_throws err begin + MOI.Bridges.bridge_type( + bridged_mock, + MOI.VectorAffineFunction{Float64}, + MOI.LogDetConeTriangle, + ) + end + c = MOI.add_constraint( + bridged_mock, + MOI.VectorOfVariables(x), + MOI.RotatedSecondOrderCone(3), + ) + @test MOI.Bridges.bridge_type( + bridged_mock, + MOI.VectorOfVariables, + MOI.RotatedSecondOrderCone, + ) == MOI.Bridges.Constraint.RSOCtoSOCBridge{ + Float64, + MOI.VectorAffineFunction{Float64}, + MOI.VectorOfVariables, + } + @test MOI.Bridges.bridge(bridged_mock, c) isa + MOI.Bridges.Constraint.RSOCtoSOCBridge + @test bridged_mock.graph.constraint_dist[MOI.Bridges.node( + bridged_mock, + MOI.VectorOfVariables, + MOI.RotatedSecondOrderCone, + ).index] == 1 + return +end + function test_supports() mock = MOI.Utilities.MockOptimizer(NoRSOCModel{Float64}()) full_bridged_mock = MOI.Bridges.full_bridge_optimizer(mock, Float64)