Skip to content

Commit 33ec305

Browse files
authored
[Bridges.Constraint] add bridges in alphabetical order (#2596)
1 parent 461006f commit 33ec305

File tree

6 files changed

+140
-68
lines changed

6 files changed

+140
-68
lines changed

src/Bridges/Constraint/Constraint.jl

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -29,84 +29,113 @@ Add all bridges defined in the `Bridges.Constraint` submodule to `model`.
2929
The coefficient type used is `T`.
3030
"""
3131
function add_all_bridges(model, ::Type{T}) where {T}
32-
if T <: AbstractFloat
32+
MOI.Bridges.add_bridge(model, AllDifferentToCountDistinctBridge{T})
33+
MOI.Bridges.add_bridge(model, BinPackingToMILPBridge{T})
34+
MOI.Bridges.add_bridge(model, CircuitToMILPBridge{T})
35+
MOI.Bridges.add_bridge(model, ComplexNormInfinityToSecondOrderConeBridge{T})
36+
MOI.Bridges.add_bridge(model, CountAtLeastToCountBelongsBridge{T})
37+
MOI.Bridges.add_bridge(model, CountBelongsToMILPBridge{T})
38+
MOI.Bridges.add_bridge(model, CountDistinctToMILPBridge{T})
39+
MOI.Bridges.add_bridge(model, CountGreaterThanToMILPBridge{T})
40+
# * ExponentialConeToScalarNonlinearFunctionBridge{T}
41+
# This bridge is not added by default because it starts with a convex
42+
# conic constraint and adds a nonlinear constraint that local NLP
43+
# solvers like Ipopt can struggle with because of log(x) when x is 0.
44+
# In addition, the bridge does not support ConstraintDual.
45+
# * FunctionConversionBridge{T}
46+
# This bridge is not added because, even though it is not abstract, it
47+
# is highly parameterized, and parameterized versions such as
48+
# ScalarFunctionizeBridge are added.
49+
MOI.Bridges.add_bridge(model, GeoMeanBridge{T})
50+
MOI.Bridges.add_bridge(model, GeoMeanToPowerBridge{T})
51+
MOI.Bridges.add_bridge(model, GeoMeantoRelEntrBridge{T})
52+
if T <: AbstractFloat # See note in docstring of AbstractToIntervalBridge
3353
MOI.Bridges.add_bridge(model, GreaterToIntervalBridge{T})
34-
MOI.Bridges.add_bridge(model, LessToIntervalBridge{T})
3554
end
3655
MOI.Bridges.add_bridge(model, GreaterToLessBridge{T})
56+
MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T})
57+
MOI.Bridges.add_bridge(model, IndicatorActiveOnFalseBridge{T})
58+
MOI.Bridges.add_bridge(model, IndicatorGreaterToLessThanBridge{T})
59+
MOI.Bridges.add_bridge(model, IndicatorLessToGreaterThanBridge{T})
60+
# * IndicatorSetMapBridge{T}
61+
# This bridge is not added because, even though it is not abstract, it
62+
# is highly parameterized, and parameterized versions such as
63+
# IndicatorGreaterToLessThanBridge are added.
64+
MOI.Bridges.add_bridge(model, IndicatorSOS1Bridge{T})
65+
MOI.Bridges.add_bridge(model, IndicatorToMILPBridge{T})
66+
# * InequalityToComplementsBridge{T}
67+
# This bridge is not added because of a bug in Convex.jl:
68+
# https://github.com/jump-dev/Convex.jl/blob/ca5324217575af263bfeee20b3e0526bed051887/src/MOI_wrapper.jl#L119-L133
69+
# It is also really useful only to PATHSolver.jl, which could add this
70+
# to MOI.ListOfRequiredBridges.
71+
MOI.Bridges.add_bridge(model, IntegerToZeroOneBridge{T})
3772
MOI.Bridges.add_bridge(model, LessToGreaterBridge{T})
73+
if T <: AbstractFloat # See note in docstring of AbstractToIntervalBridge
74+
MOI.Bridges.add_bridge(model, LessToIntervalBridge{T})
75+
end
76+
MOI.Bridges.add_bridge(model, LogDetBridge{T})
3877
MOI.Bridges.add_bridge(model, NonnegToNonposBridge{T})
3978
MOI.Bridges.add_bridge(model, NonposToNonnegBridge{T})
40-
MOI.Bridges.add_bridge(model, ScalarizeBridge{T})
41-
MOI.Bridges.add_bridge(model, VectorizeBridge{T})
42-
MOI.Bridges.add_bridge(model, ScalarSlackBridge{T})
43-
MOI.Bridges.add_bridge(model, VectorSlackBridge{T})
44-
MOI.Bridges.add_bridge(model, ScalarFunctionizeBridge{T})
45-
MOI.Bridges.add_bridge(model, VectorFunctionizeBridge{T})
46-
MOI.Bridges.add_bridge(model, ToScalarQuadraticBridge{T})
47-
MOI.Bridges.add_bridge(model, ToVectorQuadraticBridge{T})
48-
MOI.Bridges.add_bridge(model, ToScalarNonlinearBridge{T})
49-
MOI.Bridges.add_bridge(model, SplitHyperRectangleBridge{T})
50-
MOI.Bridges.add_bridge(model, SplitIntervalBridge{T})
51-
MOI.Bridges.add_bridge(model, SplitComplexEqualToBridge{T})
52-
MOI.Bridges.add_bridge(model, SplitComplexZerosBridge{T})
53-
MOI.Bridges.add_bridge(model, QuadtoSOCBridge{T})
54-
# We do not add `(R)SOCtoNonConvexQuad` because it starts with a convex
55-
# conic constraint and generate a non-convex constraint (in the QCP
56-
# interpretation).
5779
MOI.Bridges.add_bridge(model, NormInfinityBridge{T})
80+
MOI.Bridges.add_bridge(model, NormInfinityConeToNormConeBridge{T})
81+
MOI.Bridges.add_bridge(model, NormNuclearBridge{T})
5882
MOI.Bridges.add_bridge(model, NormOneBridge{T})
59-
MOI.Bridges.add_bridge(model, GeoMeantoRelEntrBridge{T})
60-
MOI.Bridges.add_bridge(model, GeoMeanBridge{T})
61-
MOI.Bridges.add_bridge(model, GeoMeanToPowerBridge{T})
62-
MOI.Bridges.add_bridge(model, NormToPowerBridge{T})
6383
MOI.Bridges.add_bridge(model, NormOneConeToNormConeBridge{T})
64-
MOI.Bridges.add_bridge(model, SecondOrderConeToNormConeBridge{T})
65-
MOI.Bridges.add_bridge(model, NormInfinityConeToNormConeBridge{T})
66-
MOI.Bridges.add_bridge(model, ComplexNormInfinityToSecondOrderConeBridge{T})
67-
MOI.Bridges.add_bridge(model, RelativeEntropyBridge{T})
84+
# * NormSpecialCaseBridge{T}
85+
# This bridge is not added because, even though it is not abstract, it
86+
# is highly parameterized, and parameterized versions such as
87+
# NormOneConeToNormConeBridge are added.
6888
MOI.Bridges.add_bridge(model, NormSpectralBridge{T})
69-
MOI.Bridges.add_bridge(model, NormNuclearBridge{T})
70-
MOI.Bridges.add_bridge(model, HermitianToSymmetricPSDBridge{T})
71-
MOI.Bridges.add_bridge(model, SquareBridge{T})
72-
MOI.Bridges.add_bridge(model, SetDotScalingBridge{T})
73-
MOI.Bridges.add_bridge(model, SetDotInverseScalingBridge{T})
74-
MOI.Bridges.add_bridge(model, LogDetBridge{T})
89+
MOI.Bridges.add_bridge(model, NormToPowerBridge{T})
90+
# * NumberConversionBridge{T}
91+
# This bridge is not added by default because it would silently enable
92+
# models with mixed precision. In most cases, this is a bug in the
93+
# user's code, so we leave this bridge as opt-in.
94+
MOI.Bridges.add_bridge(model, QuadtoSOCBridge{T})
95+
MOI.Bridges.add_bridge(model, ReifiedAllDifferentToCountDistinctBridge{T})
96+
MOI.Bridges.add_bridge(model, ReifiedCountDistinctToMILPBridge{T})
97+
MOI.Bridges.add_bridge(model, RelativeEntropyBridge{T})
7598
MOI.Bridges.add_bridge(model, RootDetBridge{T})
76-
MOI.Bridges.add_bridge(model, RSOCtoSOCBridge{T})
77-
MOI.Bridges.add_bridge(model, SOCtoRSOCBridge{T})
78-
# We do not add `SOCtoPSDBridge` as transforming the `SOC` to `RSOC` and
79-
# then to `PSD` produces a smaller SDP constraint.
80-
# MOI.Bridges.add_bridge(model, SOCtoPSDBridge{T})
99+
# * RSOCtoNonConvexQuadBridge{T}
100+
# This bridge is not added by default because it starts with a convex
101+
# conic constraint and generate a non-convex constraint (in the QCP
102+
# interpretation).
81103
MOI.Bridges.add_bridge(model, RSOCtoPSDBridge{T})
82-
MOI.Bridges.add_bridge(model, IndicatorActiveOnFalseBridge{T})
83-
MOI.Bridges.add_bridge(model, IndicatorSOS1Bridge{T})
84-
MOI.Bridges.add_bridge(model, IndicatorLessToGreaterThanBridge{T})
85-
MOI.Bridges.add_bridge(model, IndicatorGreaterToLessThanBridge{T})
104+
MOI.Bridges.add_bridge(model, RSOCtoSOCBridge{T})
105+
MOI.Bridges.add_bridge(model, ScalarFunctionizeBridge{T})
106+
MOI.Bridges.add_bridge(model, ScalarizeBridge{T})
107+
MOI.Bridges.add_bridge(model, ScalarSlackBridge{T})
108+
MOI.Bridges.add_bridge(model, SecondOrderConeToNormConeBridge{T})
86109
MOI.Bridges.add_bridge(model, SemiToBinaryBridge{T})
87-
MOI.Bridges.add_bridge(model, ZeroOneBridge{T})
88-
MOI.Bridges.add_bridge(model, IntegerToZeroOneBridge{T})
89-
MOI.Bridges.add_bridge(model, InequalityToComplementsBridge{T})
90-
# Do not add by default
91-
# MOI.Bridges.add_bridge(model, NumberConversionBridge{T})
92-
# Constraint programming bridges
93-
MOI.Bridges.add_bridge(model, AllDifferentToCountDistinctBridge{T})
94-
MOI.Bridges.add_bridge(model, ReifiedAllDifferentToCountDistinctBridge{T})
95-
MOI.Bridges.add_bridge(model, BinPackingToMILPBridge{T})
96-
MOI.Bridges.add_bridge(model, CircuitToMILPBridge{T})
97-
MOI.Bridges.add_bridge(model, CountAtLeastToCountBelongsBridge{T})
98-
MOI.Bridges.add_bridge(model, CountBelongsToMILPBridge{T})
99-
MOI.Bridges.add_bridge(model, CountDistinctToMILPBridge{T})
100-
MOI.Bridges.add_bridge(model, ReifiedCountDistinctToMILPBridge{T})
101-
MOI.Bridges.add_bridge(model, CountGreaterThanToMILPBridge{T})
102-
MOI.Bridges.add_bridge(model, TableToMILPBridge{T})
110+
# * SetConversionBridge{T}
111+
# This bridge is not added because, even though it is not abstract, it
112+
# is highly parameterized, and it intended for use by MOI extensions.
113+
MOI.Bridges.add_bridge(model, SetDotInverseScalingBridge{T})
114+
MOI.Bridges.add_bridge(model, SetDotScalingBridge{T})
115+
# * SOCtoNonConvexQuadBridge{T}
116+
# This bridge is not added by default because it starts with a convex
117+
# conic constraint and generate a non-convex constraint (in the QCP
118+
# interpretation).
119+
# * SOCtoPSDBridge{T}
120+
# This bridge is not added because transforming the `SOC` to `RSOC` and
121+
# then to `PSD` produces a smaller SDP constraint. `RSOCtoPSDBridge` is
122+
# added by default.
123+
MOI.Bridges.add_bridge(model, SOCtoRSOCBridge{T})
103124
MOI.Bridges.add_bridge(model, SOS1ToMILPBridge{T})
104125
MOI.Bridges.add_bridge(model, SOS2ToMILPBridge{T})
105-
MOI.Bridges.add_bridge(model, IndicatorToMILPBridge{T})
106-
MOI.Bridges.add_bridge(
107-
model,
108-
ExponentialConeToScalarNonlinearFunctionBridge{T},
109-
)
126+
MOI.Bridges.add_bridge(model, SplitComplexEqualToBridge{T})
127+
MOI.Bridges.add_bridge(model, SplitComplexZerosBridge{T})
128+
MOI.Bridges.add_bridge(model, SplitHyperRectangleBridge{T})
129+
MOI.Bridges.add_bridge(model, SplitIntervalBridge{T})
130+
MOI.Bridges.add_bridge(model, SquareBridge{T})
131+
MOI.Bridges.add_bridge(model, TableToMILPBridge{T})
132+
MOI.Bridges.add_bridge(model, ToScalarNonlinearBridge{T})
133+
MOI.Bridges.add_bridge(model, ToScalarQuadraticBridge{T})
134+
MOI.Bridges.add_bridge(model, ToVectorQuadraticBridge{T})
135+
MOI.Bridges.add_bridge(model, VectorFunctionizeBridge{T})
136+
MOI.Bridges.add_bridge(model, VectorizeBridge{T})
137+
MOI.Bridges.add_bridge(model, VectorSlackBridge{T})
138+
MOI.Bridges.add_bridge(model, ZeroOneBridge{T})
110139
return
111140
end
112141

src/Bridges/Constraint/bridges/SOCtoPSDBridge.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ end
7777
const SOCtoPSD{T,OT<:MOI.ModelLike} =
7878
SingleBridgeOptimizer{SOCtoPSDBridge{T},OT}
7979

80+
# This bridge destorys a lot of structure and adding PSD variables is almost
81+
# always undesirable. We give this bridge a high cost so that it is used only if
82+
# necessary.
83+
MOI.Bridges.bridging_cost(::Type{<:SOCtoPSDBridge}) = 10.0
84+
8085
function concrete_bridge_type(
8186
::Type{<:SOCtoPSDBridge{T}},
8287
G::Type{<:MOI.AbstractVectorFunction},
@@ -186,6 +191,11 @@ end
186191
const RSOCtoPSD{T,OT<:MOI.ModelLike} =
187192
SingleBridgeOptimizer{RSOCtoPSDBridge{T},OT}
188193

194+
# This bridge destorys a lot of structure and adding PSD variables is almost
195+
# always undesirable. We give this bridge a high cost so that it is used only if
196+
# necessary.
197+
MOI.Bridges.bridging_cost(::Type{<:RSOCtoPSDBridge}) = 10.0
198+
189199
function concrete_bridge_type(
190200
::Type{<:RSOCtoPSDBridge{T}},
191201
G::Type{<:MOI.AbstractVectorFunction},

src/Bridges/Variable/bridges/RSOCtoPSDBridge.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ const RSOCtoPSD{T,OT<:MOI.ModelLike} =
5757
SingleBridgeOptimizer{RSOCtoPSDBridge{T},OT}
5858

5959
# This bridge destorys a lot of structure and adding PSD variables is almost
60-
# always undesirable. We give this bridge an arbitrarily hight cost so that it
61-
# is used only if necessary.
60+
# always undesirable. We give this bridge a high cost so that it is used only if
61+
# necessary.
6262
MOI.Bridges.bridging_cost(::Type{<:RSOCtoPSDBridge}) = 10.0
6363

6464
function bridge_constrained_variable(

test/Bridges/Constraint/SOCtoPSDBridge.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,26 @@ function test_runtests()
211211
return
212212
end
213213

214+
function test_bridging_cost_SOCtoPSD()
215+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
216+
model = MOI.Bridges.Constraint.SOCtoPSD{Float64}(inner)
217+
x = MOI.add_variables(model, 3)
218+
c = MOI.add_constraint(model, x, MOI.SecondOrderCone(3))
219+
bridge = model.map[c]
220+
MOI.Bridges.bridging_cost(typeof(bridge)) == 10.0
221+
return
222+
end
223+
224+
function test_bridging_cost_RSOCtoPSD()
225+
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
226+
model = MOI.Bridges.Constraint.RSOCtoPSD{Float64}(inner)
227+
x = MOI.add_variables(model, 3)
228+
c = MOI.add_constraint(model, x, MOI.RotatedSecondOrderCone(3))
229+
bridge = model.map[c]
230+
MOI.Bridges.bridging_cost(typeof(bridge)) == 10.0
231+
return
232+
end
233+
214234
end # module
215235

216236
TestConstraintSOCtoPSD.runtests()

test/Bridges/bridge_optimizer.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,20 @@ function test_double_deletion_scalar()
567567
# careful not to delete the second one twice, see https://github.com/jump-dev/MathOptInterface.jl/issues/1231
568568
model =
569569
MOI.instantiate(AffineOnlyModel{Float64}, with_bridge_type = Float64)
570+
# If LessToGreaterBridge exists, this goes from:
571+
# VariableIndex(x) in LessThan(1.0)
572+
# ScalarAffineFunction(-1.0x) in GreaterThan(-1.0) (LessToGreater)
573+
# ScalarAffineFunction(-1.0x) in Interval(-1.0, Inf) (GreaterToInterval)
574+
# we want instead
575+
# VariableIndex(x) in LessThan(1.0)
576+
# VariableIndex(x) in Interval(-Inf, 1.0) (LessToInterval)
577+
# ScalarAffineFunction(1.0x) in Interval(-Inf, 1.0) (FunctionConversion)
578+
# To check that we handle the LessThan and Interval sets on the same
579+
# VariableIndex correctly.
580+
MOI.Bridges.remove_bridge(
581+
model,
582+
MOI.Bridges.Constraint.LessToGreaterBridge{Float64},
583+
)
570584
x = MOI.add_variable(model)
571585
c = MOI.add_constraint(model, x, MOI.LessThan(1.0))
572586
# Need to test the bridging to make sure it's not functionized first as otherwise,

test/Bridges/lazy_bridge_optimizer.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,7 +1704,6 @@ function test_MOI_runtests_No_RSOCModel()
17041704
return
17051705
end
17061706

1707-
# Test that RSOCtoPSD is used instead of RSOC+SOCtoPSD as it is a shortest path.
17081707
function test_bridge_selection()
17091708
mock = MOI.Utilities.MockOptimizer(NoRSOCModel{Float64}())
17101709
bridged_mock = MOI.Bridges.LazyBridgeOptimizer(mock)
@@ -1751,13 +1750,13 @@ function test_bridge_selection()
17511750
bridged_mock,
17521751
MOI.VectorOfVariables,
17531752
MOI.RotatedSecondOrderCone,
1754-
) == MOI.Bridges.Constraint.RSOCtoPSDBridge{
1753+
) == MOI.Bridges.Constraint.RSOCtoSOCBridge{
17551754
Float64,
17561755
MOI.VectorAffineFunction{Float64},
17571756
MOI.VectorOfVariables,
17581757
}
17591758
@test MOI.Bridges.bridge(bridged_mock, c) isa
1760-
MOI.Bridges.Constraint.RSOCtoPSDBridge
1759+
MOI.Bridges.Constraint.RSOCtoSOCBridge
17611760
@test bridged_mock.graph.constraint_dist[MOI.Bridges.node(
17621761
bridged_mock,
17631762
MOI.VectorOfVariables,

0 commit comments

Comments
 (0)