Implementing a bridge
The easiest way to implement a bridge is to follow an existing example. There are three locations of bridges in the source code:
- Constraint bridges are stored in
src/Bridges/Constraint/bridges
- Objective bridges are stored in
src/Bridges/Objective/bridges
- Variable bridges are stored in
src/Bridges/Variable/bridges
The Implementing a constraint bridge tutorial has a more detailed guide on what is required to implement a bridge.
When opening a pull request that adds a new bridge, use the checklist Adding a new bridge.
If you need help or advice, please contact the Developer Chatroom.
SetMap bridges
For constraint and variable bridges, a common reformulation is that $f(x) \in F$ is reformulated to $g(x) \in G$. In this case, no additional variables and constraints are added, and the bridge needs only a way to map between the functions f
and g
and the sets F
and G
.
To implementation a bridge of this form, subtype the abstract type Bridges.Constraint.SetMapBridge
or Bridges.Variable.SetMapBridge
and implement the API described in the docstring of each type.
final_touch
Some bridges require information from other parts of the model. One set of examples are the various combinatorial ToMILP
bridges, such as Bridges.Constraint.SOS1ToMILPBridge
, which require knowledge of the variable bounds.
Bridges requiring information from other parts of the model should implement Bridges.final_touch
and Bridges.needs_final_touch
.
During the bridge's construction, store the function and set and make no changes to the underlying model. Then, in Bridges.final_touch
, query the additional information and add the reformulated problem to the model
.
When implementing, you must consider that:
Bridges.final_touch
may be called multiple times, so that your reformulation should be applied only if necessary. Sometimes the additional data will be the same, and sometimes it may be different.- We do not currently support
final_touch
bridges that introduce constraints which also require afinal_touch
bridge. Therefore, you should implementfinal_touch
only if necessary, and we recommend that you contact the Developer Chatroom for advice before doing so.
Testing
Use the Bridges.runtests
function to test a bridge. It takes three arguments: the type of the bridge, the input model as a string, and the output model as a string.
Here is an example:
julia> MOI.Bridges.runtests(
MOI.Bridges.Constraint.GreaterToLessBridge,
"""
variables: x
x >= 1.0
""",
"""
variables: x
-1.0 * x <= -1.0
""",
)
There are a number of other useful keyword arguments.
eltype
can be used to specify the element type of the model (and bridge). It defaults toFloat64
.variable_start
andconstraint_start
are used as the values to set theVariablePrimalStart
andConstraintPrimalStart
attributes to. They default to1.2
. If you use a differenteltype
, you must set appropriate starting values of the same type. The default1.2
was chosen to minimize the risk that the starting point is undefined, which could happen for common situations like0.0
and1.0
. The tests associated with the starting values do not necessarily check for correctness, only that they can beset
andget
to produce the same result.print_inner_model
can be used to print the reformulated output model from the bridge. This is especially helpful during debugging to see what the bridge is doing, and to spot mistakes. It defaults tofalse
.
Here is an example:
julia> MOI.Bridges.runtests(
MOI.Bridges.Constraint.GreaterToLessBridge,
"""
variables: x
x >= 1
""",
"""
variables: x
::Int: -1 * x <= -1
""";
eltype = Int,
print_inner_model = true,
variable_start = 2,
constraint_start = 2,
)
Feasibility
Subject to:
ScalarAffineFunction{Int64}-in-LessThan{Int64}
(0) - (1) x <= (-1)