diff --git a/docs/make.jl b/docs/make.jl index c45d431c78..1a35a2ea8e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -63,6 +63,7 @@ const _PAGES = [ ], "Bridges" => [ "Overview" => "submodules/Bridges/overview.md", + "Implementation" => "submodules/Bridges/implementation.md", "List of bridges" => "submodules/Bridges/list_of_bridges.md", "API Reference" => "submodules/Bridges/reference.md", ], diff --git a/docs/src/submodules/Bridges/implementation.md b/docs/src/submodules/Bridges/implementation.md new file mode 100644 index 0000000000..3782edd7a4 --- /dev/null +++ b/docs/src/submodules/Bridges/implementation.md @@ -0,0 +1,123 @@ +```@meta +CurrentModule = MathOptInterface +DocTestSetup = quote + import MathOptInterface as MOI +end +DocTestFilters = [r"MathOptInterface|MOI"] +``` + +# 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](@ref) 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](@ref). + +If you need help or advice, please contact the [Developer Chatroom](https://jump.dev/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`](@ref) or +[`Bridges.Variable.SetMapBridge`](@ref) 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`](@ref), which require knowledge of the +variable bounds. + +Bridges requiring information from other parts of the model should implement +[`Bridges.final_touch`](@ref) and [`Bridges.needs_final_touch`](@ref). + +During the bridge's construction, store the function and set and make no changes +to the underlying model. Then, in [`Bridges.final_touch`](@ref), query the +additional information and add the reformulated problem to the `model`. + +When implementing, you must consider that: + + * [`Bridges.final_touch`](@ref) 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 a `final_touch` bridge. Therefore, you should implement + `final_touch` only if necessary, and we recommend that you contact the + [Developer Chatroom](https://jump.dev/chatroom/) for advice before doing so. + +## Testing + +Use the [`Bridges.runtests`](@ref) 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: +```jldoctest +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 to `Float64`. + * `variable_start` and `constraint_start` are used as the values to set the + [`VariablePrimalStart`](@ref) and [`ConstraintPrimalStart`](@ref) attributes + to. They default to `1.2`. If you use a different `eltype`, you must set + appropriate starting values of the same type. The default `1.2` was chosen to + minimize the risk that the starting point is undefined, which could happen + for common situations like `0.0` and `1.0`. The tests associated with the + starting values do not necessarily check for correctness, only that they can + be `set` and `get` 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 to `false`. + +Here is an example: + +```jldoctest +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) +``` diff --git a/docs/src/submodules/Bridges/reference.md b/docs/src/submodules/Bridges/reference.md index 44d9a1855e..90adb6f4df 100644 --- a/docs/src/submodules/Bridges/reference.md +++ b/docs/src/submodules/Bridges/reference.md @@ -21,6 +21,7 @@ get(::Bridges.AbstractBridge, ::ListOfConstraintIndices) Bridges.needs_final_touch Bridges.final_touch Bridges.bridging_cost +Bridges.runtests ``` ## Constraint bridge API diff --git a/docs/src/submodules/Utilities/reference.md b/docs/src/submodules/Utilities/reference.md index c555d6c92f..9fd6a1af7f 100644 --- a/docs/src/submodules/Utilities/reference.md +++ b/docs/src/submodules/Utilities/reference.md @@ -79,6 +79,7 @@ Utilities.default_copy_to Utilities.IndexMap Utilities.identity_index_map Utilities.ModelFilter +Utilities.loadfromstring! ``` ## Penalty relaxation