Skip to content

Commit af35935

Browse files
authored
Document bridges submodule (jump-dev#1280)
1 parent 5d1b1b2 commit af35935

File tree

4 files changed

+237
-355
lines changed

4 files changed

+237
-355
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ makedocs(
3939
],
4040
"Bridges" => [
4141
"Overview" => "submodules/Bridges/overview.md",
42+
"Implementation" => "submodules/Bridges/implementation.md",
4243
"API Reference" => "submodules/Bridges/reference.md",
4344
],
4445
"FileFormats" => [
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
```@meta
2+
CurrentModule = MathOptInterface
3+
DocTestSetup = quote
4+
using MathOptInterface
5+
const MOI = MathOptInterface
6+
end
7+
DocTestFilters = [r"MathOptInterface|MOI"]
8+
```
9+
10+
# Bridge interface
11+
12+
A bridge should implement the following functions to be usable by a bridge optimizer:
13+
```@docs
14+
Bridges.added_constrained_variable_types
15+
Bridges.added_constraint_types
16+
```
17+
Additionally, variable bridges should implement:
18+
```@docs
19+
Bridges.Variable.supports_constrained_variable
20+
Bridges.Variable.concrete_bridge_type
21+
Bridges.Variable.bridge_constrained_variable
22+
```
23+
constraint bridges should implement:
24+
```@docs
25+
supports_constraint(::Type{<:Bridges.Constraint.AbstractBridge}, ::Type{<:AbstractFunction}, ::Type{<:AbstractSet})
26+
Bridges.Constraint.concrete_bridge_type
27+
Bridges.Constraint.bridge_constraint
28+
```
29+
and objective bridges should implement:
30+
```@docs
31+
Bridges.set_objective_function_type
32+
Bridges.Objective.concrete_bridge_type
33+
Bridges.Objective.bridge_objective
34+
```
35+
36+
When querying the [`NumberOfVariables`](@ref), [`NumberOfConstraints`](@ref)
37+
and [`ListOfConstraintIndices`](@ref), the variables and constraints created
38+
by the bridges in the underlying model are hidden by the bridge optimizer.
39+
For this purpose, the bridge should provide access to the variables and
40+
constraints it has creates by implemented the following methods of
41+
[`get`](@ref):
42+
```@docs
43+
get(::Bridges.Constraint.AbstractBridge, ::NumberOfVariables)
44+
get(::Bridges.Constraint.AbstractBridge, ::ListOfVariableIndices)
45+
get(::Bridges.AbstractBridge, ::NumberOfConstraints)
46+
get(::Bridges.AbstractBridge, ::ListOfConstraintIndices)
47+
```

docs/src/submodules/Bridges/overview.md

Lines changed: 163 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,16 @@ end
77
DocTestFilters = [r"MathOptInterface|MOI"]
88
```
99

10-
# The `Bridges` submodule
10+
# The Bridges submodule
11+
12+
The `Bridges` module simplifies the process of converting models between
13+
equivalent formulations.
14+
15+
!!! tip
16+
[Read our paper](https://arxiv.org/abs/2002.03447) for more details on how
17+
bridges are implemented.
18+
19+
## Why bridges?
1120

1221
A constraint can often be written in a number of equivalent formulations. For
1322
example, the constraint ``l \le a^\top x \le u``
@@ -19,63 +28,164 @@ re-formulation is to add a dummy variable `y` with the constraints ``l \le y \le
1928
([`ScalarAffineFunction`](@ref)-in-[`EqualTo`](@ref)).
2029

2130
To avoid each solver having to code these transformations manually,
22-
MathOptInterface provides *bridges*. A bridge is a small transformation from one
23-
constraint type to another (potentially collection of) constraint type. Because
24-
these bridges are included in MathOptInterface, they can be re-used by any
25-
optimizer. Some bridges also implement constraint modifications and constraint
26-
primal and dual translations.
27-
28-
For example, the `SplitIntervalBridge` defines the reformulation of a
29-
`ScalarAffineFunction`-in-`Interval` constraint into a
30-
`ScalarAffineFunction`-in-`GreaterThan` and a
31-
`ScalarAffineFunction`-in-`LessThan` constraint. `SplitInterval` is the
32-
bridge optimizer that applies the `SplitIntervalBridge` rewriting rule. Given
33-
an optimizer `optimizer` implementing `ScalarAffineFunction`-in-`GreaterThan`
34-
and `ScalarAffineFunction`-in-`LessThan`, the optimizer
35-
```jldoctest; setup=:(model = MOI.Utilities.Model{Float64}())
36-
optimizer = MOI.Bridges.Constraint.SplitInterval{Float64}(model)
37-
MOI.supports_constraint(
38-
optimizer, MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}
39-
)
40-
41-
# output
42-
43-
true
31+
MathOptInterface provides *bridges*.
32+
33+
A bridge is a small transformation from one constraint type to another
34+
(potentially collection of) constraint type.
35+
36+
Because these bridges are included in MathOptInterface, they can be re-used by
37+
any optimizer. Some bridges also implement constraint modifications and
38+
constraint primal and dual translations.
39+
40+
## The three types of bridges
41+
42+
There are three types of bridges in MathOptInterface:
43+
1. Constraint bridges
44+
2. Variable bridges
45+
3. Objective bridges
46+
47+
### Constraint bridges
48+
49+
Constraint bridges convert constraints formulated by the user into an equivalent
50+
form supported by the solver.
51+
52+
The equivalent formulation may add constraints (and possibly also variables) in
53+
the underlying model.
54+
55+
Read the [list of implemented constraint bridges](@ref constraint_bridges_ref)
56+
for more details on the types of transformations that are available.
57+
58+
### [Variable bridges](@id variable_bridges)
59+
60+
Variable bridges convert variables added by the user, either free with
61+
[`add_variable`](@ref)/[`add_variables`](@ref), or constrained with
62+
[`add_constrained_variable`](@ref)/[`add_constrained_variables`](@ref),
63+
into an equivalent form supported by the solver.
64+
65+
Te equivalent formulation may add constraints (and possibly also variables) in
66+
the underlying model.
67+
68+
Read the [list of implemented variable bridges](@ref variable_bridges_ref) for
69+
more details on the types of transformations that are available.
70+
71+
### Objective bridges
72+
73+
Objective bridges convert the [`ObjectiveFunction`](@ref) set by the user into
74+
an equivalent form supported by the solver.
75+
76+
Te equivalent formulation may add constraints (and possibly also variables) in
77+
the underlying model.
78+
79+
Read the [list of implemented objective bridges](@ref objective_bridges_ref) for
80+
more details on the types of transformations that are available.
81+
82+
## Bridges.`full_bridge_optimizer`
83+
84+
!!! tip
85+
Unless you have an advanced use-case, this is probably the only function you
86+
need to care about.
87+
88+
To enable the full power of MathOptInterface's bridges, wrap an `optimizer`
89+
in a [`Bridges.full_bridge_optimizer`](@ref).
90+
91+
```jldoctest
92+
julia> inner_optimizer = MOI.Utilities.Model{Float64}()
93+
MOIU.Model{Float64}
94+
95+
julia> optimizer = MOI.Bridges.full_bridge_optimizer(inner_optimizer, Float64)
96+
MOIB.LazyBridgeOptimizer{MOIU.Model{Float64}}
97+
with 0 variable bridges
98+
with 0 constraint bridges
99+
with 0 objective bridges
100+
with inner model MOIU.Model{Float64}
44101
```
45-
will additionally support [`ScalarAffineFunction`](@ref)-in-[`Interval`](@ref).
46-
Note that these [`Bridges.Constraint.SingleBridgeOptimizer`](@ref)s are mainly
47-
used for testing bridges.
48-
49-
It is recommended to rather use [`Bridges.full_bridge_optimizer`](@ref) which
50-
automatically selects the appropriate constraint bridges for unsupported
51-
constraints.
52-
```julia
53-
optimizer = MOI.Bridges.full_bridge_optimizer(model, Float64)
102+
103+
That's all you have to do! Use `optimizer` as normal, and bridging will happen
104+
lazily behind the scenes. By lazily, we mean that bridging will only happen if
105+
the constraint is not supported by the `inner_optimizer`.
106+
107+
!!! info
108+
Most bridges are added by default in [`Bridges.full_bridge_optimizer`](@ref).
109+
However, for technical reasons, some bridges are not added by default. Three
110+
examples include [`Bridges.Constraint.SOCtoPSDBridge`](@ref),
111+
[`Bridges.Constraint.SOCtoNonConvexQuadBridge`](@ref) and
112+
[`Bridges.Constraint.RSOCtoNonConvexQuadBridge`](@ref). See the docs of
113+
those bridges for more information.
114+
115+
## Add a single bridge
116+
117+
If you don't want to use [`Bridges.full_bridge_optimizer`](@ref), you can wrap
118+
an optimizer in a single bridge.
119+
120+
However, this will force the constraint to be bridged, even if the
121+
`inner_optimizer` supports it.
122+
123+
```jldoctest
124+
julia> inner_optimizer = MOI.Utilities.Model{Float64}()
125+
MOIU.Model{Float64}
126+
127+
julia> optimizer = MOI.Bridges.Constraint.SplitInterval{Float64}(inner_optimizer)
128+
MOIB.Constraint.SingleBridgeOptimizer{MOIB.Constraint.SplitIntervalBridge{Float64,F,S,LS,US} where US<:MOI.AbstractSet where LS<:MOI.AbstractSet where S<:MOI.AbstractSet where F<:MOI.AbstractFunction,MOIU.Model{Float64}}
129+
with 0 constraint bridges
130+
with inner model MOIU.Model{Float64}
131+
132+
julia> x = MOI.add_variable(optimizer)
133+
MOI.VariableIndex(1)
134+
135+
julia> MOI.add_constraint(optimizer, MOI.SingleVariable(x), MOI.Interval(0.0, 1.0))
136+
MathOptInterface.ConstraintIndex{MathOptInterface.SingleVariable,MathOptInterface.Interval{Float64}}(1)
137+
138+
julia> MOI.get(optimizer, MOI.ListOfConstraints())
139+
1-element Array{Tuple{DataType,DataType},1}:
140+
(MathOptInterface.SingleVariable, MathOptInterface.Interval{Float64})
141+
142+
julia> MOI.get(inner_optimizer, MOI.ListOfConstraints())
143+
2-element Array{Tuple{DataType,DataType},1}:
144+
(MathOptInterface.SingleVariable, MathOptInterface.GreaterThan{Float64})
145+
(MathOptInterface.SingleVariable, MathOptInterface.LessThan{Float64})
54146
```
55147

56-
### Variable reformulations
148+
## Bridges.LazyBridgeOptimizer
149+
150+
If you don't want to use [`Bridges.full_bridge_optimizer`](@ref), but you need
151+
more than a single bridge (or you want the bridging to happen lazily), you can
152+
manually construct a [`Bridges.LazyBridgeOptimizer`](@ref).
153+
154+
First, wrap an inner optimizer:
155+
```jldoctest lazy_bridge_optimizer
156+
julia> inner_optimizer = MOI.Utilities.Model{Float64}()
157+
MOIU.Model{Float64}
57158
58-
A variable is often created constrained in a set unsupported by the solver while
59-
it could be parametrized by variables constrained in supported sets.
60-
For example, the [`Bridges.Variable.VectorizeBridge`](@ref) defines the
61-
reformulation of a constrained variable in [`GreaterThan`](@ref) into a
62-
constrained vector of one variable in [`Nonnegatives`](@ref).
63-
The `Bridges.Variable.Vectorize` is the bridge optimizer that applies the
64-
[`Bridges.Variable.VectorizeBridge`](@ref) rewriting rule. Given an optimizer
65-
`optimizer` implementing constrained variables in [`Nonnegatives`](@ref),
66-
the optimizer
67-
```jldoctest; setup=:(model = MOI.Utilities.Model{Float64}())
68-
optimizer = MOI.Bridges.Variable.Vectorize{Float64}(model)
69-
MOI.supports_add_constrained_variable(optimizer, MOI.GreaterThan{Float64})
159+
julia> optimizer = MOI.Bridges.LazyBridgeOptimizer(inner_optimizer)
160+
MOIB.LazyBridgeOptimizer{MOIU.Model{Float64}}
161+
with 0 variable bridges
162+
with 0 constraint bridges
163+
with 0 objective bridges
164+
with inner model MOIU.Model{Float64}
165+
```
70166

71-
# output
167+
Then use [`Bridges.add_bridge`](@ref) to add individual bridges:
168+
```jldoctest lazy_bridge_optimizer
169+
julia> MOI.Bridges.add_bridge(optimizer, MOI.Bridges.Constraint.SplitIntervalBridge{Float64})
170+
Dict{Any,DataType} with 0 entries
72171
73-
true
172+
julia> MOI.Bridges.add_bridge(optimizer, MOI.Bridges.Objective.FunctionizeBridge{Float64})
173+
Dict{Any,DataType} with 0 entries
74174
```
75-
will additionally support constrained variables in [`GreaterThan`](@ref).
76-
Note that these [`Bridges.Variable.SingleBridgeOptimizer`](@ref) are mainly
77-
used for testing bridges.
78175

79-
It is recommended to rather use [`Bridges.full_bridge_optimizer`](@ref), which
80-
automatically selects the appropriate bridges for unsupported constrained
81-
variables.
176+
Now the constraints will be bridged only if needed:
177+
```jldoctest lazy_bridge_optimizer
178+
julia> x = MOI.add_variable(optimizer)
179+
MOI.VariableIndex(1)
180+
181+
julia> MOI.add_constraint(optimizer, MOI.SingleVariable(x), MOI.Interval(0.0, 1.0))
182+
MathOptInterface.ConstraintIndex{MathOptInterface.SingleVariable,MathOptInterface.Interval{Float64}}(1)
183+
184+
julia> MOI.get(optimizer, MOI.ListOfConstraints())
185+
1-element Array{Tuple{DataType,DataType},1}:
186+
(MathOptInterface.SingleVariable, MathOptInterface.Interval{Float64})
187+
188+
julia> MOI.get(inner_optimizer, MOI.ListOfConstraints())
189+
1-element Array{Tuple{DataType,DataType},1}:
190+
(MathOptInterface.SingleVariable, MathOptInterface.Interval{Float64})
191+
```

0 commit comments

Comments
 (0)