Skip to content

Commit 3a857fe

Browse files
authored
Add Bridges.ListOfNonstandardBridges attribute (#666)
* Add Bridges.ToAdd attribute * Rename to ListOfNonstandardBridges * Fix * Doc fix * Fix doc * Refer to instantiate in docs
1 parent 697ec4b commit 3a857fe

File tree

6 files changed

+145
-17
lines changed

6 files changed

+145
-17
lines changed

docs/src/submodules/Bridges/reference.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Bridges.add_bridge
2525
Bridges.remove_bridge
2626
Bridges.has_bridge
2727
Bridges.full_bridge_optimizer
28+
Bridges.ListOfNonstandardBridges
2829
Bridges.debug_supports_constraint
2930
Bridges.debug_supports
3031
Bridges.bridged_variable_function
@@ -59,9 +60,10 @@ Bridges.inverse_adjoint_map_function
5960

6061
```@docs
6162
Bridges.Constraint.FlipSignBridge
63+
Bridges.Constraint.AbstractToIntervalBridge
6264
Bridges.Constraint.GreaterToIntervalBridge
63-
Bridges.Constraint.GreaterToLessBridge
6465
Bridges.Constraint.LessToIntervalBridge
66+
Bridges.Constraint.GreaterToLessBridge
6567
Bridges.Constraint.LessToGreaterBridge
6668
Bridges.Constraint.NonnegToNonposBridge
6769
Bridges.Constraint.NonposToNonnegBridge

src/Bridges/Bridges.jl

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,115 @@ include("debug.jl")
2323
"""
2424
full_bridge_optimizer(model::MOI.ModelLike, ::Type{T}) where {T}
2525
26-
Returns a `LazyBridgeOptimizer` bridging `model` for every bridge defined in
27-
this package and for the coefficient type `T`.
26+
Returns a [`LazyBridgeOptimizer`](@ref) bridging `model` for every bridge
27+
defined in this package (see below for the few exceptions) and for the
28+
coefficient type `T` in addition to the bridges in the list returned by
29+
`MOI.get(model, MOI.Bridges.ListOfNonstandardBridges{T}())`.
30+
31+
See also [`ListOfNonstandardBridges`](@ref).
32+
33+
!!! note
34+
The following bridges are not added by `full_bridge_optimizer` except if
35+
they are in the list returned by `MOI.get(model, MOI.Bridges.ListOfNonstandardBridges{T}())`
36+
(see the docstrings of the corresponding bridge for the reason they are not
37+
added):
38+
* [`Constraint.SOCtoNonConvexQuadBridge`](@ref),
39+
[`Constraint.RSOCtoNonConvexQuadBridge`](@ref) and
40+
[`Constraint.SOCtoPSDBridge`](@ref).
41+
* The subtypes of [`Constraint.AbstractToIntervalBridge`](@ref) (i.e.
42+
[`Constraint.GreaterToIntervalBridge`](@ref) and
43+
[`Constraint.LessToIntervalBridge`](@ref)) if `T` is not a subtype of
44+
`AbstractFloat`.
2845
"""
2946
function full_bridge_optimizer(model::MOI.ModelLike, ::Type{T}) where {T}
3047
bridged_model = LazyBridgeOptimizer(model)
48+
for BT in MOI.get(model, ListOfNonstandardBridges{T}())
49+
add_bridge(bridged_model, BT)
50+
end
3151
Variable.add_all_bridges(bridged_model, T)
3252
Constraint.add_all_bridges(bridged_model, T)
3353
Objective.add_all_bridges(bridged_model, T)
3454
return bridged_model
3555
end
3656

57+
"""
58+
ListOfNonstandardBridges{T}() <: MOI.AbstractOptimizerAttribute
59+
60+
Any optimizer can be wrapped in a [`LazyBridgeOptimizer`](@ref) using
61+
[`full_bridge_optimizer`](@ref). However, by default [`LazyBridgeOptimizer`](@ref)
62+
uses a limited set of bridges that are:
63+
64+
1. implemented in `MOI.Bridges`
65+
2. generally applicable for all optimizers.
66+
67+
For some optimizers however, it is useful to add additional bridges, such as
68+
those that are implemented in external packages (e.g., within the solver package
69+
itself) or only apply in certain circumstances (e.g.,
70+
[`Constraint.SOCtoNonConvexQuadBridge`](@ref)).
71+
72+
Such optimizers should implement the `ListOfNonstandardBridges` attribute to
73+
return a vector of bridge types that are added by [`full_bridge_optimizer`](@ref)
74+
in addition to the list of default bridges.
75+
76+
Note that optimizers implementing `ListOfNonstandardBridges` may require
77+
package-specific functions or sets to be used if the non-standard bridges
78+
are not added. Therefore, you are recommended to use
79+
`model = MOI.instantiate(Package.Optimizer; with_bridge_type = T)` instead of
80+
`model = MOI.instantiate(Package.Optimizer)`. See
81+
[`MathOptInterface.instantiate`](@ref).
82+
83+
## Examples
84+
85+
### An optimizer using a non-default bridge in `MOI.Bridges`
86+
87+
Solvers supporting [`MOI.ScalarQuadraticFunction`](@ref) can support
88+
[`MOI.SecondOrderCone`](@ref) and [`MOI.RotatedSecondOrderCone`](@ref) by
89+
defining:
90+
```julia
91+
function MOI.get(::MyQuadraticOptimizer, ::ListOfNonstandardBridges{Float64})
92+
return Type[
93+
MOI.Bridges.Constraint.SOCtoNonConvexQuadBridge{Float64},
94+
MOI.Bridges.Constraint.RSOCtoNonConvexQuadBridge{Float64},
95+
]
96+
end
97+
```
98+
99+
### An optimizer defining an internal bridge
100+
101+
Suppose an optimizer can exploit specific structure of a constraint, e.g., it
102+
can exploit the structure of the matrix `A` in the linear system of equations
103+
`A * x = b`.
104+
105+
The optimizer can define the function:
106+
```julia
107+
struct MatrixAffineFunction{T} <: MOI.AbstractVectorFunction
108+
A::SomeStructuredMatrixType{T}
109+
b::Vector{T}
110+
end
111+
```
112+
and then a bridge
113+
```julia
114+
struct MatrixAffineFunctionBridge{T} <: MOI.Constraint.AbstractBridge
115+
# ...
116+
end
117+
# ...
118+
```
119+
from `VectorAffineFunction{T}` to the `MatrixAffineFunction`. Finally, it
120+
defines:
121+
```julia
122+
function MOI.get(::Optimizer{T}, ::ListOfNonstandardBridges{T}) where {T}
123+
return Type[MatrixAffineFunctionBridge{T}]
124+
end
125+
```
126+
"""
127+
struct ListOfNonstandardBridges{T} <: MOI.AbstractOptimizerAttribute end
128+
129+
attribute_value_type(::ListOfNonstandardBridges) = Vector{Type}
130+
131+
MOI.is_copyable(::ListOfNonstandardBridges) = false
132+
133+
MOI.get_fallback(model::MOI.ModelLike, ::ListOfNonstandardBridges) = Type[]
134+
37135
print_num_bridges(io::IO, ::Variable.EmptyMap) = nothing
38136

39137
print_num_bridges(io::IO, ::Constraint.EmptyMap) = nothing

src/Bridges/Constraint/ltgt_to_interval.jl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ Bridge a `F`-in-`Interval` constraint into an `F`-in-`Interval{T}` constraint wh
99
1010
The `F`-in-`Interval{T}` constraint is stored in the `constraint`
1111
field by convention.
12-
It is required that T be a AbstractFloat type because otherwise
13-
typemin and typemax would either be not implemented (e.g. BigInt)
14-
or would not give infinite value (e.g. Int).
12+
13+
!!! warning
14+
It is required that `T` be a `AbstractFloat` type because otherwise
15+
typemin and typemax would either be not implemented (e.g. BigInt)
16+
or would not give infinite value (e.g. Int). For this reason,
17+
this bridge is only added to
18+
[`MathOptInterface.Bridges.full_bridge_optimizer`](@ref).
19+
20+
when `T` is a subtype of `AbstractFloat`.
1521
"""
1622
abstract type AbstractToIntervalBridge{
1723
T<:AbstractFloat,

src/Bridges/Constraint/soc_to_nonconvex_quad.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ is equivalent to
1616
```
1717
with ``t \\ge 0``. (3)
1818
19-
*WARNING* This transformation starts from a convex constraint (1) and creates a
20-
non-convex constraint (2), because the Q matrix associated with the constraint 2
21-
has one negative eigenvalue. This might be wrongly interpreted by a solver.
22-
Some solvers can look at (2) and understand that it is a second order cone, but
23-
this is not a general rule.
24-
For these reasons this bridge is not automatically added by [`MOI.Bridges.full_bridge_optimizer`](@ref).
25-
Care is recommended when adding this bridge to a optimizer.
19+
!!! warning
20+
This transformation starts from a convex constraint (1) and creates a
21+
non-convex constraint (2), because the Q matrix associated with the constraint (2)
22+
has one negative eigenvalue. This might be wrongly interpreted by a solver.
23+
Some solvers can look at (2) and understand that it is a second order cone, but
24+
this is not a general rule.
25+
For these reasons this bridge is not automatically added by [`MOI.Bridges.full_bridge_optimizer`](@ref).
26+
Care is recommended when adding this bridge to a optimizer.
2627
"""
2728
struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T}
2829
quad::CI{MOI.ScalarQuadraticFunction{T},MOI.LessThan{T}}

src/Bridges/Constraint/soc_to_psd.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ which is equivalent to
4545
t^2 & > x^\\top x
4646
\\end{align*}
4747
```
48-
This bridge is not added by default by [`MOI.Bridges.full_bridge_optimizer`](@ref)
49-
as bridging second order cone constraints to semidefinite constraints can be
50-
achieved by the [`SOCtoRSOCBridge`](@ref) followed by the [`RSOCtoPSDBridge`](@ref)
51-
while creating a smaller semidefinite constraint.
48+
49+
!!! warning
50+
This bridge is not added by default by [`MOI.Bridges.full_bridge_optimizer`](@ref)
51+
as bridging second order cone constraints to semidefinite constraints can be
52+
achieved by the [`SOCtoRSOCBridge`](@ref) followed by the [`RSOCtoPSDBridge`](@ref)
53+
while creating a smaller semidefinite constraint.
5254
"""
5355
struct SOCtoPSDBridge{T,F,G} <: SetMapBridge{
5456
T,

test/Bridges/lazy_bridge_optimizer.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,25 @@ function test_wrong_coefficient()
19561956
return
19571957
end
19581958

1959+
struct OptimizerWithBridgeListOfNonstandardBridges <: MOI.AbstractOptimizer end
1960+
struct BridgeListOfNonstandardBridges{T} <:
1961+
MOI.Bridges.Constraint.AbstractBridge end
1962+
function MOI.get(
1963+
::OptimizerWithBridgeListOfNonstandardBridges,
1964+
::MOI.Bridges.ListOfNonstandardBridges{T},
1965+
) where {T}
1966+
return [BridgeListOfNonstandardBridges{T}]
1967+
end
1968+
1969+
function test_toadd()
1970+
b = MOI.Bridges.full_bridge_optimizer(
1971+
OptimizerWithBridgeListOfNonstandardBridges(),
1972+
Int,
1973+
)
1974+
@test MOI.Bridges.has_bridge(b, BridgeListOfNonstandardBridges{Int})
1975+
@test !MOI.Bridges.has_bridge(b, BridgeListOfNonstandardBridges{Float64})
1976+
end
1977+
19591978
end # module
19601979

19611980
TestBridgesLazyBridgeOptimizer.runtests()

0 commit comments

Comments
 (0)