diff --git a/docs/src/manual/models.md b/docs/src/manual/models.md index cca29e34de..7e5b0467b2 100644 --- a/docs/src/manual/models.md +++ b/docs/src/manual/models.md @@ -62,50 +62,42 @@ functions. Consult the docstsrings of each attribute for information on what it represents. -## Model attributes +## ModelLike API -The following attributes are available for models: +The following attributes are available: + * [`ListOfConstraintAttributesSet`](@ref) + * [`ListOfConstraintIndices`](@ref) + * [`ListOfConstraintTypesPresent`](@ref) + * [`ListOfModelAttributesSet`](@ref) + * [`ListOfVariableAttributesSet`](@ref) + * [`ListOfVariableIndices`](@ref) + * [`NumberOfConstraints`](@ref) + * [`NumberOfVariables`](@ref) * [`Name`](@ref) * [`ObjectiveFunction`](@ref) * [`ObjectiveFunctionType`](@ref) * [`ObjectiveSense`](@ref) - * [`NumberOfVariables`](@ref) - * [`ListOfVariableIndices`](@ref) - * [`ListOfConstraintTypesPresent`](@ref) - * [`NumberOfConstraints`](@ref) - * [`ListOfConstraintIndices`](@ref) - * [`ListOfOptimizerAttributesSet`](@ref) - * [`ListOfModelAttributesSet`](@ref) - * [`ListOfVariableAttributesSet`](@ref) - * [`ListOfConstraintAttributesSet`](@ref) - -## Optimizer attributes - -The following attributes are available for optimizers: - - - [`SolverName`](@ref) - - [`RawOptimizerAttribute`](@ref) - - [`Silent`](@ref) - - [`TimeLimitSec`](@ref) - - [`NumberOfThreads`](@ref) - - [`RawSolver`](@ref) - -In addition, optimizers should implement the following attributes to provide -access to solutions: - - - [`TerminationStatus`](@ref) - - [`TerminationStatusCode`](@ref) - - [`PrimalStatus`](@ref) - - [`DualStatus`](@ref) - - [`ResultStatusCode`](@ref) - - [`RawStatusString`](@ref) - - [`ResultCount`](@ref) - - [`ObjectiveValue`](@ref) - - [`DualObjectiveValue`](@ref) - - [`ObjectiveBound`](@ref) - - [`RelativeGap`](@ref) - - [`SolveTimeSec`](@ref) - - [`SimplexIterations`](@ref) - - [`BarrierIterations`](@ref) - - [`NodeCount`](@ref) +## AbstractOptimizer API + +The following attributes are available: + + * [`DualStatus`](@ref) + * [`PrimalStatus`](@ref) + * [`RawStatusString`](@ref) + * [`ResultCount`](@ref) + * [`TerminationStatus`](@ref) + * [`BarrierIterations`](@ref) + * [`DualObjectiveValue`](@ref) + * [`NodeCount`](@ref) + * [`NumberOfThreads`](@ref) + * [`ObjectiveBound`](@ref) + * [`ObjectiveValue`](@ref) + * [`RelativeGap`](@ref) + * [`RawOptimizerAttribute`](@ref) + * [`RawSolver`](@ref) + * [`Silent`](@ref) + * [`SimplexIterations`](@ref) + * [`SolverName`](@ref) + * [`SolveTimeSec`](@ref) + * [`TimeLimitSec`](@ref) diff --git a/docs/src/reference/models.md b/docs/src/reference/models.md index d6956536d9..a7a0005042 100644 --- a/docs/src/reference/models.md +++ b/docs/src/reference/models.md @@ -60,6 +60,7 @@ AbstractOptimizer OptimizerWithAttributes optimize! instantiate +copy_to_and_optimize! ``` ## Optimizer attributes diff --git a/docs/src/tutorials/implementing.md b/docs/src/tutorials/implementing.md index f7217ead4b..953ee50f68 100644 --- a/docs/src/tutorials/implementing.md +++ b/docs/src/tutorials/implementing.md @@ -20,6 +20,32 @@ MathOptInterface for a new solver. not answered by this guide, please ask them in the [Developer chatroom](https://gitter.im/JuliaOpt/JuMP-dev) so we can improve this guide! +## A note on the API + +The API of MathOptInterface is large and varied. In order to support the +diversity of solvers and use-cases, we make heavy use of +[duck-typing](https://en.wikipedia.org/wiki/Duck_typing). That is, solvers are +not expected to implement the full API, nor is there a well-defined minimal +subset of what must be implemented. Instead, you should implement the API as +necessary in order to make the solver function as you require. + +The main reason for using duck-typing is that solvers work in different ways and +target different use-cases. + +For example: + + * Some solvers support incremental problem construction, support modification + after a solve, and have native support for things like variable names. + * Other solvers are "one-shot" solvers that require all of the problem data to + construct and solve the problem in a single function call. They do not + support modification or things like variable names. + * Other "solvers" are not solvers at all, but things like file readers. These + may only support functions like [`read_from_file`](@ref), and may not even + support the ability to add variables or constraints directly! + * Finally, some "solvers" are layers which take a problem as input, transform + it according to some rules, and pass the transformed problem to an inner + solver. + ## Preliminaries ### Decide if MathOptInterface is right for you @@ -51,8 +77,8 @@ has a good list of solvers, along with the problem classes they support. ### Create a low-level interface -Before writing a MathOptInterface wrapper, you first need to be able to call the solver -from Julia. +Before writing a MathOptInterface wrapper, you first need to be able to call the +solver from Julia. #### Wrapping solvers written in Julia @@ -241,9 +267,14 @@ Base.unsafe_convert(::Type{Ptr{Cvoid}}, model::Optimizer) = model.ptr ### Implement methods for `Optimizer` -Now that we have an `Optimizer`, we need to implement a few basic methods. +All `Optimizer`s must implement the following methods: -* [`empty!`](@ref) and [`is_empty`](@ref) + * [`empty!`](@ref) + * [`is_empty`](@ref) + * [`optimize!`](@ref) (or [`copy_to_and_optimize!`](@ref)) + +Other methods, detailed below, are optional or depend on how you implement the +interface. !!! tip For this and all future methods, read the docstrings to understand what each @@ -261,8 +292,7 @@ end ### Implement attributes -You also need to implement the model and optimizer attributes in the following -table. +MathOptInterface uses attributes to manage different aspects of the problem. For each attribute * [`get`](@ref) gets the current value of the attribute @@ -286,7 +316,7 @@ method for each attribute. | [`Name`](@ref) | Yes | Yes | Yes | | [`Silent`](@ref) | Yes | Yes | Yes | | [`TimeLimitSec`](@ref) | Yes | Yes | Yes | -| [`RawOptimizerAttribute`](@ref) | Yes | Yes | Yes | +| [`RawOptimizerAttribute`](@ref) | Yes | Yes | Yes | | [`NumberOfThreads`](@ref) | Yes | Yes | Yes | For example: @@ -306,7 +336,6 @@ end MOI.supports(::Optimizer, ::MOI.Silent) = true ``` - ### Define `supports_constraint` The next step is to define which constraints and objective functions you plan to @@ -410,6 +439,7 @@ In addition, you should implement the following model attributes: | [`ObjectiveFunctionType`](@ref) | Yes | No | No | | [`ObjectiveFunction`](@ref) | Yes | Yes | Yes | | [`ObjectiveSense`](@ref) | Yes | Yes | Yes | +| [`Name`](@ref) | Yes | Yes | Yes | Variable-related attributes: @@ -429,9 +459,11 @@ Constraint-related attributes: | [`ConstraintFunction`](@ref) | Yes | Yes | No | | [`ConstraintSet`](@ref) | Yes | Yes | No | -If your solver supports modifying data in-place, implement: +#### Modifications + +If your solver supports modifying data in-place, implement [`modify`](@ref) for +the following `AbstractModification`s: -* [`modify`](@ref) * [`ScalarConstantChange`](@ref) * [`ScalarCoefficientChange`](@ref) * [`VectorConstantChange`](@ref) @@ -466,8 +498,8 @@ fallback: ```julia MOI.supports_incremental_interface(::Optimizer) = true -function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kwargs...) - return MOI.Utilities.default_copy_to(dest, src; kwargs...) +function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike) + return MOI.Utilities.default_copy_to(dest, src) end ``` @@ -481,7 +513,8 @@ the `Name` attribute for variables and constraints: | [`VariableName`](@ref) | Yes | Yes | Yes | | [`ConstraintName`](@ref) | Yes | Yes | Yes | -If you implement names, you should also implement the following three methods: +If you implement names, you must also implement the following three methods: + ```julia function MOI.get(model::Optimizer, ::Type{MOI.VariableIndex}, name::String) return # The variable named `name`. @@ -536,17 +569,13 @@ Implement [`optimize!`](@ref) to solve the model: * [`optimize!`](@ref) -At a minimum, implement the following attributes to allow the user to access -solution information. +All `Optimizer`s must implement the following attributes: -* [`TerminationStatus`](@ref) -* [`PrimalStatus`](@ref) * [`DualStatus`](@ref) +* [`PrimalStatus`](@ref) * [`RawStatusString`](@ref) * [`ResultCount`](@ref) -* [`ObjectiveValue`](@ref) -* [`VariablePrimal`](@ref) -* [`SolveTimeSec`](@ref) +* [`TerminationStatus`](@ref) !!! info You only need to implement [`get`](@ref) for solution attributes. Don't @@ -558,6 +587,12 @@ solution information. designed to be used when the solver explicitly indicates that relaxed tolerances are satisfied or the returned point is infeasible, respectively. +You should also implement the following attributes: + +* [`ObjectiveValue`](@ref) +* [`SolveTimeSec`](@ref) +* [`VariablePrimal`](@ref) + !!! tip Attributes like [`VariablePrimal`](@ref) and [`ObjectiveValue`](@ref) are indexed by the result count. Use diff --git a/src/MathOptInterface.jl b/src/MathOptInterface.jl index ac6b80cef6..58970f82f0 100644 --- a/src/MathOptInterface.jl +++ b/src/MathOptInterface.jl @@ -17,12 +17,12 @@ function Base.show(io::IO, model::ModelLike) end """ - AbstractOptimizer + AbstractOptimizer <: ModelLike -Abstract supertype for objects representing an instance of an optimization problem -tied to a particular solver. This is typically a solver's in-memory representation. -In addition to `ModelLike`, `AbstractOptimizer` objects let you solve the -model and query the solution. +Abstract supertype for objects representing an instance of an optimization +problem tied to a particular solver. This is typically a solver's in-memory +representation. In addition to `ModelLike`, `AbstractOptimizer` objects let you +solve the model and query the solution. """ abstract type AbstractOptimizer <: ModelLike end @@ -82,7 +82,9 @@ function read_from_file end """ is_empty(model::ModelLike) -Returns `false` if the `model` has any model attribute set or has any variables or constraints. +Returns `false` if the `model` has any model attribute set or has any variables +or constraints. + Note that an empty model can have optimizer attributes set. """ function is_empty end @@ -90,7 +92,8 @@ function is_empty end """ empty!(model::ModelLike) -Empty the model, that is, remove all variables, constraints and model attributes but not optimizer attributes. +Empty the model, that is, remove all variables, constraints and model attributes +but not optimizer attributes. """ function empty! end