From 65c66f0d7450ef03ab5bbc2d05327d7ec4c15b36 Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 9 Nov 2021 11:26:20 +1300 Subject: [PATCH 1/2] General edit of the docs --- docs/make.jl | 12 +- docs/src/background/duality.md | 9 +- docs/src/background/motivation.md | 30 ++-- docs/src/index.md | 8 +- docs/src/manual/constraints.md | 10 +- docs/src/manual/models.md | 2 +- docs/src/manual/solutions.md | 2 +- docs/src/manual/variables.md | 4 +- docs/src/reference/errors.md | 10 +- docs/src/reference/standard_form.md | 23 +-- docs/src/submodules/Bridges/implementation.md | 11 +- docs/src/submodules/FileFormats/overview.md | 9 +- docs/src/submodules/Test/overview.md | 4 +- docs/src/submodules/Utilities/overview.md | 4 +- docs/src/tutorials/bridging_constraint.md | 144 +++++++++--------- docs/src/tutorials/example.md | 5 +- docs/src/tutorials/implementing.md | 19 ++- docs/src/tutorials/mathprogbase.md | 4 +- 18 files changed, 165 insertions(+), 145 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 6aab1f9764..64b21f5c0d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,12 +15,10 @@ const _PDF = findfirst(isequal("--pdf"), ARGS) !== nothing || _IS_GITHUB_ACTIONS # ============================================================================== const _PAGES = [ - "Introduction" => "index.md", - "Background" => [ + "Introduction" => [ + "index.md", "background/motivation.md", - "background/duality.md", - "background/naming_conventions.md", - ], + ] "Tutorials" => [ "tutorials/example.md", "tutorials/implementing.md", @@ -37,6 +35,10 @@ const _PAGES = [ "manual/solutions.md", "manual/modification.md", ], + "Background" => [ + "background/duality.md", + "background/naming_conventions.md", + ], "API Reference" => [ "reference/standard_form.md", "reference/models.md", diff --git a/docs/src/background/duality.md b/docs/src/background/duality.md index 5f6b4f1586..4d3bce587e 100644 --- a/docs/src/background/duality.md +++ b/docs/src/background/duality.md @@ -53,11 +53,10 @@ and the dual is a minimization problem in standard conic form: \end{align} ``` -A linear inequality constraint ``a^T x + b \ge c`` should be interpreted as -``a^T x + b - c \in \mathbb{R}_+``, and similarly ``a^T x + b \le c`` should be -interpreted as ``a^T x + b - c \in \mathbb{R}_-``. Variable-wise constraints -should be interpreted as affine constraints with the appropriate identity -mapping in place of ``A_i``. +A linear inequality constraint ``a^T x + b \ge c`` is equivalent to +``a^T x + b - c \in \mathbb{R}_+``, and ``a^T x + b \le c`` is equivalent to +``a^T x + b - c \in \mathbb{R}_-``. Variable-wise constraints are affine +constraints with the appropriate identity mapping in place of ``A_i``. For the special case of minimization LPs, the MOI primal form can be stated as: ```math diff --git a/docs/src/background/motivation.md b/docs/src/background/motivation.md index 252341ac07..db333524f6 100644 --- a/docs/src/background/motivation.md +++ b/docs/src/background/motivation.md @@ -1,20 +1,24 @@ # Motivation -MOI has been designed to replace [MathProgBase](https://github.com/JuliaOpt/MathProgBase.jl), -which was been used by modeling packages such as [JuMP](https://github.com/jump-dev/JuMP.jl) -and [Convex.jl](https://github.com/jump-dev/Convex.jl). +MathOptInterface (MOI) is a replacement for +[MathProgBase](https://github.com/JuliaOpt/MathProgBase.jl), +the first-generation abstraction layer for mathematical optimization previously +used by +[JuMP](https://github.com/jump-dev/JuMP.jl) and +[Convex.jl](https://github.com/jump-dev/Convex.jl). -This second-generation abstraction layer addresses a number of limitations of -MathProgBase. +To address a number of limitations of MathProgBase, MOI is designed to: -MOI is designed to: -- Be simple and extensible, unifying linear, quadratic, and conic optimization, - and seamlessly facilitate extensions to essentially arbitrary constraints and - functions (e.g., indicator constraints, complementarity constraints, and - piecewise-linear functions) -- Be fast by allowing access to a solver's in-memory representation of a problem - without writing intermediate files (when possible) and by using multiple - dispatch and avoiding requiring containers of nonconcrete types +- Be simple and extensible + * unifying linear, quadratic, and conic optimization, + * seamlessly facilitating extensions to essentially arbitrary constraints and + functions (e.g., indicator constraints, complementarity constraints, and + piecewise-linear functions) +- Be fast + * by allowing access to a solver's in-memory representation of a problem + without writing intermediate files (when possible) + * by using multiple dispatch and avoiding requiring containers of nonconcrete + types - Allow a solver to return multiple results (e.g., a pool of solutions) - Allow a solver to return extra arbitrary information via attributes (e.g., variable- and constraint-wise membership in an irreducible inconsistent subset diff --git a/docs/src/index.md b/docs/src/index.md index d3298c34d7..302ae38aa5 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,5 +1,7 @@ # Introduction +Welcome to the documentation for MathOptInterface. + !!! note This documentation is also available in PDF format: [MathOptInterface.pdf](MathOptInterface.pdf). @@ -24,14 +26,14 @@ solver-specific APIs. Having a high-level overview of how this documentation is structured will help you know where to look for certain things. -* The **Background** section contains articles on the motivation and theory - behind MathOptInterface. Look here if you want to understand _why_, rather - than _how_. * The **Tutorials** section contains articles on how to use and implement the MathOptInteraface API. Look here if you want to write a model in MOI, or write an interface to a new solver. * The **Manual** contains short code-snippets that explain how to use the MOI API. Look here for more details on particular areas of MOI. +* The **Background** section contains articles on the theory + behind MathOptInterface. Look here if you want to understand _why_, rather + than _how_. * The **API Reference** contains a complete list of functions and types that comprise the MOI API. Look here is you want to know how to use (or implement) a particular function. diff --git a/docs/src/manual/constraints.md b/docs/src/manual/constraints.md index c6fd2c8355..6c38a19bae 100644 --- a/docs/src/manual/constraints.md +++ b/docs/src/manual/constraints.md @@ -18,8 +18,8 @@ julia> c = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Nonnegatives( MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Nonnegatives}(1) ``` -[`add_constraint`](@ref) returns a [`ConstraintIndex`](@ref) type, which should -be used to refer to the added constraint in other calls. +[`add_constraint`](@ref) returns a [`ConstraintIndex`](@ref) type, which is used +to refer to the added constraint in other calls. Check if a [`ConstraintIndex`](@ref) is valid using [`is_valid`](@ref). @@ -114,13 +114,13 @@ nonpositive) real numbers. By convention, solvers are not expected to support nonzero constant terms in the [`ScalarAffineFunction`](@ref)s the first four rows above, because they are -redundant with the parameters of the sets. For example, ``2x + 1 \le 2`` should -be encoded as ``2x \le 1``. +redundant with the parameters of the sets. For example, encode ``2x + 1 \le 2`` +as ``2x \le 1``. Constraints with [`VariableIndex`](@ref) in [`LessThan`](@ref), [`GreaterThan`](@ref), [`EqualTo`](@ref), or [`Interval`](@ref) sets have a natural interpretation as variable bounds. As such, it is typically not natural to impose multiple lower- -or upper-bounds on the same variable, and the solver interfaces should throw +or upper-bounds on the same variable, and the solver interfaces will throw respectively [`LowerBoundAlreadySet`](@ref) or [`UpperBoundAlreadySet`](@ref). Moreover, adding two [`VariableIndex`](@ref) constraints on the same variable diff --git a/docs/src/manual/models.md b/docs/src/manual/models.md index 23a948e9dd..a73dc55c30 100644 --- a/docs/src/manual/models.md +++ b/docs/src/manual/models.md @@ -11,7 +11,7 @@ DocTestFilters = [r"MathOptInterface|MOI"] The most significant part of MOI is the definition of the **model API** that is used to specify an instance of an optimization problem (e.g., by adding -variables and constraints). Objects that implement the model API should inherit +variables and constraints). Objects that implement the model API must inherit from the [`ModelLike`](@ref) abstract type. Notably missing from the model API is the method to solve an optimization diff --git a/docs/src/manual/solutions.md b/docs/src/manual/solutions.md index b1e2840cd7..cd559bfc14 100644 --- a/docs/src/manual/solutions.md +++ b/docs/src/manual/solutions.md @@ -38,7 +38,7 @@ else end ``` -After checking the [`TerminationStatus`](@ref), one should typically check +After checking the [`TerminationStatus`](@ref), check [`ResultCount`](@ref). This attribute returns the number of results that the solver has available to return. *A result is defined as a primal-dual pair, but either the primal or the dual may be missing from the result.* While the diff --git a/docs/src/manual/variables.md b/docs/src/manual/variables.md index 4b02ad3f6c..251b568c23 100644 --- a/docs/src/manual/variables.md +++ b/docs/src/manual/variables.md @@ -17,8 +17,8 @@ Use [`add_variable`](@ref) to add a single variable. julia> x = MOI.add_variable(model) MathOptInterface.VariableIndex(1) ``` -[`add_variable`](@ref) returns a [`VariableIndex`](@ref) type, which should be -used to refer to the added variable in other calls. +[`add_variable`](@ref) returns a [`VariableIndex`](@ref) type, which is used to +refer to the added variable in other calls. Check if a [`VariableIndex`](@ref) is valid using [`is_valid`](@ref). ```jldoctest variables diff --git a/docs/src/reference/errors.md b/docs/src/reference/errors.md index 17e6b388c5..d7aa2727a5 100644 --- a/docs/src/reference/errors.md +++ b/docs/src/reference/errors.md @@ -11,19 +11,19 @@ DocTestFilters = [r"MathOptInterface|MOI"] When an MOI call fails on a model, precise errors should be thrown when possible instead of simply calling `error` with a message. The docstrings for the -respective methods describe the errors that the implementation should thrown in +respective methods describe the errors that the implementation should throw in certain situations. This error-reporting system allows code to distinguish between internal errors (that should be shown to the user) and unsupported operations which may have automatic workarounds. -When an invalid index is used in an MOI call, an [`InvalidIndex`](@ref) should -be thrown: +When an invalid index is used in an MOI call, an [`InvalidIndex`](@ref) is +thrown: ```@docs InvalidIndex ``` -When an invalid result index is used to retrieve an attribute, a -[`ResultIndexBoundsError`](@ref) should be thrown: +When an invalid result index is used to retrieve an attribute, a +[`ResultIndexBoundsError`](@ref) is thrown: ```@docs ResultIndexBoundsError check_result_index_bounds diff --git a/docs/src/reference/standard_form.md b/docs/src/reference/standard_form.md index 06d22e8422..89c58dc093 100644 --- a/docs/src/reference/standard_form.md +++ b/docs/src/reference/standard_form.md @@ -99,16 +99,21 @@ Complements ## Matrix sets Matrix sets are vectorized in order to be subtypes of -[`AbstractVectorSet`](@ref). For sets of symmetric matrices, storing both the -`(i, j)` and `(j, i)` elements is redundant so there exists the +[`AbstractVectorSet`](@ref). + +For sets of symmetric matrices, storing both the +`(i, j)` and `(j, i)` elements is redundant. Use the [`AbstractSymmetricMatrixSetTriangle`](@ref) set to represent only the -vectorization of the upper triangular part of the matrix. When the matrix -of expressions constrained to be in the set is not symmetric and hence -the `(i, j)` and `(j, i)` elements should be constrained to be symmetric, -the [`AbstractSymmetricMatrixSetSquare`](@ref) set can be used. The -[`Bridges.Constraint.SquareBridge`](@ref) can transform a set from the square -form to the [`triangular_form`](@ref) by adding appropriate constraints if -the `(i, j)` and `(j, i)` expressions are different. +vectorization of the upper triangular part of the matrix. + +When the matrix of expressions constrained to be in the set is not symmetric, +and hence additional constraints are needed to force the equality of the +`(i, j)` and `(j, i)` elements, use the +[`AbstractSymmetricMatrixSetSquare`](@ref) set. + +The [`Bridges.Constraint.SquareBridge`](@ref) can transform a set from the +square form to the [`triangular_form`](@ref) by adding appropriate constraints +if the `(i, j)` and `(j, i)` expressions are different. ```@docs AbstractSymmetricMatrixSetTriangle diff --git a/docs/src/submodules/Bridges/implementation.md b/docs/src/submodules/Bridges/implementation.md index df7e87f8b5..740bd5af16 100644 --- a/docs/src/submodules/Bridges/implementation.md +++ b/docs/src/submodules/Bridges/implementation.md @@ -9,24 +9,25 @@ DocTestFilters = [r"MathOptInterface|MOI"] # Bridge interface -A bridge should implement the following functions to be usable by a bridge optimizer: +To be usable by a bridge optimizer, a bridge must implement the following +functions: ```@docs Bridges.added_constrained_variable_types Bridges.added_constraint_types ``` -Additionally, variable bridges should implement: +Additionally, variable bridges must implement: ```@docs Bridges.Variable.supports_constrained_variable Bridges.Variable.concrete_bridge_type Bridges.Variable.bridge_constrained_variable ``` -constraint bridges should implement: +constraint bridges must implement: ```@docs supports_constraint(::Type{<:Bridges.Constraint.AbstractBridge}, ::Type{<:AbstractFunction}, ::Type{<:AbstractSet}) Bridges.Constraint.concrete_bridge_type Bridges.Constraint.bridge_constraint ``` -and objective bridges should implement: +and objective bridges must implement: ```@docs Bridges.set_objective_function_type Bridges.Objective.concrete_bridge_type @@ -36,7 +37,7 @@ Bridges.Objective.bridge_objective When querying the [`NumberOfVariables`](@ref), [`NumberOfConstraints`](@ref) [`ListOfVariableIndices`](@ref), and [`ListOfConstraintIndices`](@ref), the variables and constraints created by the bridges in the underlying model are -hidden by the bridge optimizer. For this purpose, the bridge should provide +hidden by the bridge optimizer. For this purpose, the bridge must provide access to the variables and constraints it has created by implementing the following methods of [`get`](@ref): ```@docs diff --git a/docs/src/submodules/FileFormats/overview.md b/docs/src/submodules/FileFormats/overview.md index 6561b68d42..6c30d8ee88 100644 --- a/docs/src/submodules/FileFormats/overview.md +++ b/docs/src/submodules/FileFormats/overview.md @@ -160,7 +160,7 @@ filename. In some cases `src` may contain constraints that are not supported by the file format (e.g., the CBF format supports integer variables but not binary). If so, -you should copy `src` to a bridged model using [`Bridges.full_bridge_optimizer`](@ref): +copy `src` to a bridged model using [`Bridges.full_bridge_optimizer`](@ref): ```julia src = MOI.Utilities.Model{Float64}() x = MOI.add_variable(model) @@ -170,9 +170,10 @@ bridged = MOI.Bridges.full_bridge_optimizer(dest, Float64) MOI.copy_to(bridged, src) MOI.write_to_file(dest, "my_model.cbf") ``` -You should also note that even after bridging, it may still not be possible to -write the model to file because of unsupported constraints (e.g., PSD variables -in the LP file format). +!!! note + Even after bridging, it may still not be possible to write the model to file + because of unsupported constraints (e.g., PSD variables in the LP file + format). ## Read and write to `io` diff --git a/docs/src/submodules/Test/overview.md b/docs/src/submodules/Test/overview.md index d979e3bf01..a7ed21f0f5 100644 --- a/docs/src/submodules/Test/overview.md +++ b/docs/src/submodules/Test/overview.md @@ -200,9 +200,7 @@ machine). **Step 3** Since the double-optimize error involves solving an optimization problem, -add a new test to [src/Test/UnitTests/solve.jl](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Test/UnitTests/solve.jl). - -The test should be something like +add a new test to [src/Test/UnitTests/solve.jl](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Test/UnitTests/solve.jl): ```julia """ test_unit_optimize!_twice(model::MOI.ModelLike, config::Config) diff --git a/docs/src/submodules/Utilities/overview.md b/docs/src/submodules/Utilities/overview.md index a6f19b64c9..216f0841f6 100644 --- a/docs/src/submodules/Utilities/overview.md +++ b/docs/src/submodules/Utilities/overview.md @@ -53,7 +53,7 @@ defined outside of MOI (but still known at compile time), we provide the The `@model` macro takes a name (for a new type, which must not exist yet), eight tuples specifying the types of constraints that are supported, and then a -`Bool` indicating the type should be a subtype of `MOI.AbstractOptimizer` (if +`Bool` indicating the type is a subtype of `MOI.AbstractOptimizer` (if `true`) or `MOI.ModelLike` (if `false`). The eight tuples are in the following order: @@ -66,7 +66,7 @@ The eight tuples are in the following order: 7. Un-typed vector functions, e.g., [`VectorOfVariables`](@ref) 8. Typed vector functions, e.g., [`VectorAffineFunction`](@ref) -The tuples can contain more than one element. Typed-sets should be speficied +The tuples can contain more than one element. Typed-sets must be specified without their type parameter, i.e., `MOI.LessThan`, not `MOI.LessThan{Float64}`. Here is an example: diff --git a/docs/src/tutorials/bridging_constraint.md b/docs/src/tutorials/bridging_constraint.md index 2d07660e43..5273e78043 100644 --- a/docs/src/tutorials/bridging_constraint.md +++ b/docs/src/tutorials/bridging_constraint.md @@ -14,19 +14,19 @@ expressed in the formalism `Function`-in-`Set`. ## Preliminaries -First, decide on the set you want to bridge. Then, study its properties: the -most important one is whether the set is scalar or vector, which impacts the -dimensionality of the functions that can be used with the set. +First, decide on the set you want to bridge. Then, study its properties: the +most important one is whether the set is scalar or vector, which impacts the +dimensionality of the functions that can be used with the set. -* A scalar function only has one dimension. MOI defines three types of scalar - functions: a variable ([`VariableIndex`](@ref)), an affine function - ([`ScalarAffineFunction`](@ref)), or a quadratic function +* A scalar function only has one dimension. MOI defines three types of scalar + functions: a variable ([`VariableIndex`](@ref)), an affine function + ([`ScalarAffineFunction`](@ref)), or a quadratic function ([`ScalarQuadraticFunction`](@ref)). -* A vector function has several dimensions (at least one). MOI defines three - types of vector functions: several variables ([`VectorOfVariables`](@ref)), - an affine function ([`VectorAffineFunction`](@ref)), or a quadratic function - ([`VectorQuadraticFunction`](@ref)). The main difference with scalar functions - is that the order of dimensions can be very important: for instance, in an +* A vector function has several dimensions (at least one). MOI defines three + types of vector functions: several variables ([`VectorOfVariables`](@ref)), + an affine function ([`VectorAffineFunction`](@ref)), or a quadratic function + ([`VectorQuadraticFunction`](@ref)). The main difference with scalar functions + is that the order of dimensions can be very important: for instance, in an indicator constraint ([`Indicator`](@ref)), the first dimension indicates whether the constraint about the second dimension is active. @@ -34,53 +34,53 @@ To explain how to implement a bridge, we present the example of [`Bridges.Constraint.FlipSignBridge`](@ref). This bridge maps `<=` ([`LessThan`](@ref)) constraints to `>=` ([`GreaterThan`](@ref)) constraints. This corresponds to reversing the sign of the inequality. We focus on scalar -affine functions (we disregard the cases of a single variable or of quadratic -functions). This example is a simplified version of the code included +affine functions (we disregard the cases of a single variable or of quadratic +functions). This example is a simplified version of the code included in MOI. ## Four mandatory parts in a constraint bridge The first part of a constraint bridge is a new concrete subtype of -[`Bridges.Constraint.AbstractBridge`](@ref). This type must have fields to -store all the new variables and constraints that the bridge will add. +[`Bridges.Constraint.AbstractBridge`](@ref). This type must have fields to +store all the new variables and constraints that the bridge will add. Typically, these types are parametrized by the type of the coefficients in the model. -Then, three sets of functions must be defined: +Then, three sets of functions must be defined: 1. [`Bridges.Constraint.bridge_constraint`](@ref): this function implements the bridge and creates the required variables and constraints. -2. [`supports_constraint`](@ref): these functions should return `true` when the - combination of function and set is supported by the bridge. By default, the - base implementation always returns `false` and the bridge does not have to +2. [`supports_constraint`](@ref): these functions must return `true` when the + combination of function and set is supported by the bridge. By default, the + base implementation always returns `false` and the bridge does not have to provide this implementation. -3. [`Bridges.added_constrained_variable_types`](@ref) and - [`Bridges.added_constraint_types`](@ref): these functions return the types +3. [`Bridges.added_constrained_variable_types`](@ref) and + [`Bridges.added_constraint_types`](@ref): these functions return the types of variables and constraints that this bridge adds. They are used to compute - the set of other bridges that are required to use the one you are defining, + the set of other bridges that are required to use the one you are defining, if need be. -More functions can be implemented, for instance to retrieve properties from the +More functions can be implemented, for instance to retrieve properties from the bridge or deleting a bridged constraint. ### 1. Structure for the bridge A typical `struct` behind a bridge depends on the type of the coefficients that -are used for the model (typically `Float64`, but coefficients might also be +are used for the model (typically `Float64`, but coefficients might also be integers or complex numbers). -This structure must hold a reference to all the variables and the constraints +This structure must hold a reference to all the variables and the constraints that are created as part of the bridge. -The type of this structure is used throughout MOI as an identifier for the +The type of this structure is used throughout MOI as an identifier for the bridge. It is passed as argument to most functions related to bridges. The best practice is to have the name of this type end with `Bridge`. -In our example, the bridge should be able to map any +In our example, the bridge maps any `ScalarAffineFunction{T}`-in-`LessThan{T}` constraint to a single `ScalarAffineFunction{T}`-in-`GreaterThan{T}` constraint. The affine function -has coefficients of type `T`. The bridge is parametrized with `T`, so that the +has coefficients of type `T`. The bridge is parametrized with `T`, so that the constraint that the bridge creates also has coefficients of type `T`. ```julia @@ -92,16 +92,16 @@ end ### 2. Bridge creation The function [`Bridges.Constraint.bridge_constraint`](@ref) is called whenever -the bridge should be instantiated for a specific model, with the given function -and set. The arguments to `bridge_constraint` are similar to +the bridge is instantiated for a specific model, with the given function +and set. The arguments to `bridge_constraint` are similar to [`add_constraint`](@ref), with the exception of the first argument: it is the -`Type` of the struct defined in the first step (for our example, +`Type` of the struct defined in the first step (for our example, `Type{SignBridge{T}}`). `bridge_constraint` returns an instance of the struct defined in the first step. the first step. -In our example, the bridge constraint could be defined as: +In our example, the bridge constraint could be defined as: ```julia function Bridges.Constraint.bridge_constraint( @@ -113,7 +113,7 @@ function Bridges.Constraint.bridge_constraint( # Create the variables and constraints required for the bridge. con = add_constraint(model, -f, GreaterThan(-s.upper)) - # Return an instance of the bridge type with a reference to all the + # Return an instance of the bridge type with a reference to all the # variables and constraints that were created in this function. return SignBridge(con) end @@ -124,7 +124,7 @@ end The function [`supports_constraint`](@ref) determines whether the bridge type supports a given combination of function and set. -This function must closely match `bridge_constraint`, because it will not be +This function must closely match `bridge_constraint`, because it will not be called if `supports_constraint` returns `false`. ```julia @@ -142,12 +142,12 @@ end ### 4. Metadata about the bridge To determine whether a bridge can be used, MOI uses a shortest-path algorithm -that uses the variable types and the constraints that the bridge can create. -This information is communicated from the bridge to MOI using the functions -[`Bridges.added_constrained_variable_types`](@ref) and -[`Bridges.added_constraint_types`](@ref). Both return lists of tuples: -either a list of 1-tuples containing the variable types (typically, `ZeroOne` -or `Integer`) or a list of 2-tuples contained the functions and sets (like +that uses the variable types and the constraints that the bridge can create. +This information is communicated from the bridge to MOI using the functions +[`Bridges.added_constrained_variable_types`](@ref) and +[`Bridges.added_constraint_types`](@ref). Both return lists of tuples: +either a list of 1-tuples containing the variable types (typically, `ZeroOne` +or `Integer`) or a list of 2-tuples contained the functions and sets (like `ScalarAffineFunction{T}`-`GreaterThan`). For our example, the bridge does not create any constrained variables, and @@ -155,7 +155,7 @@ only `ScalarAffineFunction{T}`-in-`GreaterThan{T}` constraints: ```julia function Bridges.added_constrained_variable_types(::Type{SignBridge{T}}) where {T} - # The bridge does not create variables, return an empty list of tuples: + # The bridge does not create variables, return an empty list of tuples: return Tuple{Type}[] end @@ -167,27 +167,27 @@ function Bridges.added_constraint_types(::Type{SignBridge{T}}) where {T} end ``` -A bridge that creates binary variables would rather have this definition of +A bridge that creates binary variables would rather have this definition of `added_constrained_variable_types`: ```julia function Bridges.added_constrained_variable_types(::Type{SomeBridge{T}}) where {T} - # The bridge only creates binary variables: + # The bridge only creates binary variables: return Tuple{Type}[(ZeroOne,)] end ``` !!! warning - If you declare the creation of constrained variables in - `added_constrained_variable_types`, the corresponding constraint type - `VariableIndex` should not be indicated in `added_constraint_types`. - This would restrict the use of the bridge to solvers that can add such a - constraint after the variable is created. + If you declare the creation of constrained variables in + `added_constrained_variable_types`, the corresponding constraint type + `VariableIndex` must not be indicated in `added_constraint_types`. + This would restrict the use of the bridge to solvers that can add such a + constraint after the variable is created. More concretely, *if* you declare in `added_constrained_variable_types` that - your bridge creates binary variables (`ZeroOne`), *and if* you never add such - a constraint afterward (you do not call - `add_constraint(model, var, ZeroOne())`), then you should + your bridge creates binary variables (`ZeroOne`), *and if* you never add such + a constraint afterward (you do not call + `add_constraint(model, var, ZeroOne())`), then you must *not* list `(VariableIndex, ZeroOne)` in `added_constraint_types`. Typically, the function [`Bridges.Constraint.concrete_bridge_type`](@ref) does @@ -195,12 +195,12 @@ not have to be defined for most bridges. ## Bridge registration -For a bridge to be used by MOI, it must be known by MOI. +For a bridge to be used by MOI, it must be known by MOI. ### `SingleBridgeOptimizer` -The first way to do so is to create a single-bridge optimizer. This type of -optimizer wraps another optimizer and adds the possibility to use only one +The first way to do so is to create a single-bridge optimizer. This type of +optimizer wraps another optimizer and adds the possibility to use only one bridge. It is especially useful when unit testing bridges. It is common practice to use the same name as the type defined for the bridge @@ -211,8 +211,8 @@ const Sign{T,OT<: ModelLike} = SingleBridgeOptimizer{SignBridge{T}, OT} ``` -In the context of unit tests, this bridge is used in conjunction with a -[`Utilities.MockOptimizer`](@ref): +In the context of unit tests, this bridge is used in conjunction with a +[`Utilities.MockOptimizer`](@ref): ```julia mock = Utilities.MockOptimizer( @@ -223,10 +223,10 @@ bridged_mock = Sign{Float64}(mock) ### New bridge for a `LazyBridgeOptimizer` -Typical user-facing models for MOI are based on -[`Bridges.LazyBridgeOptimizer`](@ref). For instance, this type of model is -returned by [`Bridges.full_bridge_optimizer`](@ref). These models can be -added more bridges by using [`Bridges.add_bridge`](@ref): +Typical user-facing models for MOI are based on +[`Bridges.LazyBridgeOptimizer`](@ref). For instance, this type of model is +returned by [`Bridges.full_bridge_optimizer`](@ref). These models can be +added more bridges by using [`Bridges.add_bridge`](@ref): ```julia inner_optimizer = Utilities.Model{Float64}() @@ -238,11 +238,11 @@ Bridges.add_bridge(optimizer, SignBridge{Float64}) ### Attribute retrieval -Like models, bridges have attributes that can be retrieved using [`get`](@ref) +Like models, bridges have attributes that can be retrieved using [`get`](@ref) and [`set`](@ref). The most important ones are the number of variables and constraints, but also the lists of variables and constraints. -In our example, we only have one constraint and only have to implement the +In our example, we only have one constraint and only have to implement the [`NumberOfConstraints`](@ref) and [`ListOfConstraintIndices`](@ref) attributes: ```julia @@ -267,16 +267,16 @@ function get( end ``` -You must implement one such pair of functions for each type of constraint the +You must implement one such pair of functions for each type of constraint the bridge adds to the model. !!! warning - Avoid returning a list from the bridge object without copying it. Users - should be able to change the contents of the returned list without altering + Avoid returning a list from the bridge object without copying it. Users + must be able to change the contents of the returned list without altering the bridge object. -For variables, the situation is simpler. If your bridge creates new variables, -you should implement the [`NumberOfVariables`](@ref) and +For variables, the situation is simpler. If your bridge creates new variables, +you must implement the [`NumberOfVariables`](@ref) and [`ListOfVariableIndices`](@ref) attributes. However, these attributes do not have parameters, unlike their constraint counterparts. Only two functions suffice: @@ -298,13 +298,13 @@ end ### Model modifications -To avoid copying the model when the user request to change a constraint, MOI -provides [`modify`](@ref). Bridges can also implement this API to allow +To avoid copying the model when the user request to change a constraint, MOI +provides [`modify`](@ref). Bridges can also implement this API to allow certain changes, such as coefficient changes. In our case, a modification of a coefficient in the original constraint -(i.e. replacing the value of the coefficient of a variable in the affine -function) should be transmitted to the constraint created by the bridge, +(i.e. replacing the value of the coefficient of a variable in the affine +function) must be transmitted to the constraint created by the bridge, but with a sign change. ```julia @@ -324,7 +324,7 @@ end ### Bridge deletion -When a bridge is deleted, the constraints it added should be deleted too. +When a bridge is deleted, the constraints it added must be deleted too. ```julia function delete(model::ModelLike, bridge::SignBridge) diff --git a/docs/src/tutorials/example.md b/docs/src/tutorials/example.md index 29b420c929..03f203e4d5 100644 --- a/docs/src/tutorials/example.md +++ b/docs/src/tutorials/example.md @@ -9,7 +9,8 @@ DocTestFilters = [r"MathOptInterface|MOI"] # Solving a problem using MathOptInterface -In this example, we want to solve a binary-constrained knapsack problem: +In this tutorial we demonstrate how to use MathOptInterface to solve the +binary-constrained knapsack problem: ```math \begin{aligned} \max \; & c^\top x \\ @@ -18,6 +19,8 @@ s.t. \; & w^\top x \le C \\ \end{aligned} ``` +## Required packages + Load the MathOptInterface module and define the shorthand `MOI`: ```julia using MathOptInterface diff --git a/docs/src/tutorials/implementing.md b/docs/src/tutorials/implementing.md index 3a7f137c73..e4755cf3bc 100644 --- a/docs/src/tutorials/implementing.md +++ b/docs/src/tutorials/implementing.md @@ -12,7 +12,7 @@ DocTestFilters = [r"MathOptInterface|MOI"] This guide outlines the basic steps to implement an interface to MathOptInterface for a new solver. -!!! warning +!!! danger Implementing an interface to MathOptInterface for a new solver is a lot of work. Before starting, we recommend that you join the [Developer chatroom](https://gitter.im/JuliaOpt/JuMP-dev) and explain a @@ -48,6 +48,9 @@ For example: ## Preliminaries +Before starting on your wrapper, you should do some background research and make +the solver accessible via Julia. + ### Decide if MathOptInterface is right for you The first step in writing a wrapper is to decide whether implementing an @@ -627,6 +630,8 @@ If your solver accepts primal or dual warm-starts, implement: ## Other tips +Here are some other points to be aware of when writing your wrapper. + ### Unsupported constraints at runtime In some cases, your solver may support a particular type of constraint (e.g., @@ -652,7 +657,7 @@ Only throw if the constraints conflict. It is okay to add ### Expect duplicate coefficients -Solvers should expect that functions such as [`ScalarAffineFunction`](@ref) and +Solvers must expect that functions such as [`ScalarAffineFunction`](@ref) and [`VectorQuadraticFunction`](@ref) may contain duplicate coefficents. For example, @@ -663,14 +668,14 @@ coefficients aggregated together. ### Don't modify user-data -All data passed to the solver should be copied immediately to internal data -structures. Solvers may not modify any input vectors and should assume that -input vectors may be modified by users in the future. +All data passed to the solver must be copied immediately to internal data +structures. Solvers may not modify any input vectors and must assume that +input vectors will not be modified by users in the future. This applies, for example, to the `terms` vector in [`ScalarAffineFunction`](@ref). Vectors returned to the user, e.g., via [`ObjectiveFunction`](@ref) or -[`ConstraintFunction`](@ref) attributes, should not be modified by the solver +[`ConstraintFunction`](@ref) attributes, must not be modified by the solver afterwards. The in-place version of [`get!`](@ref) can be used by users to avoid extra copies in this case. @@ -681,7 +686,7 @@ API for setting coefficients in existing constraints when adding a new variable, it is possible to queue modifications and new variables and then call the solver's API once all of the new coefficients are known. -## Extra: solver-specific attributes +### Solver-specific attributes You don't need to restrict yourself to the attributes defined in the MathOptInterface.jl package. diff --git a/docs/src/tutorials/mathprogbase.md b/docs/src/tutorials/mathprogbase.md index c937aaf42f..6751568bd1 100644 --- a/docs/src/tutorials/mathprogbase.md +++ b/docs/src/tutorials/mathprogbase.md @@ -7,8 +7,8 @@ However, it is not a direct replacement. MathOptInterface is more extensive than MathProgBase which may make its implementation seem daunting at first. -There are however numerous utilities in MathOptInterface that should hopefully -make the implementation as simple or simpler than MathProgBase. +There are however numerous utilities in MathOptInterface that +the simplify implementation process. For more information, read [Implementing a solver interface](@ref). From 9c1a544f2fcd43123527274114c499e2fc71e2e4 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 9 Nov 2021 13:18:25 +1300 Subject: [PATCH 2/2] Update make.jl --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 64b21f5c0d..2948c7b1c0 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -18,7 +18,7 @@ const _PAGES = [ "Introduction" => [ "index.md", "background/motivation.md", - ] + ], "Tutorials" => [ "tutorials/example.md", "tutorials/implementing.md",