From ad64448b488c7d2f33764900b20e2331f05d53be Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 27 Mar 2025 10:17:01 +1300 Subject: [PATCH 1/4] [docs] improve the docstrings of attributes --- src/attributes.jl | 587 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 450 insertions(+), 137 deletions(-) diff --git a/src/attributes.jl b/src/attributes.jl index 025d20e7ab..30d48ae798 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -690,10 +690,11 @@ end HeuristicSolution(callback_data) Heuristically obtained feasible solution. The solution is submitted as -`variables, values` where `values[i]` gives the value of `variables[i]`, -similarly to [`set`](@ref). The [`submit`](@ref) call returns a -[`HeuristicSolutionStatus`](@ref) indicating whether the provided solution -was accepted or rejected. +`(variables, values)::Tuple{Vector{MOI.VariableIndex},Vector{T}}`, where +`values[i]` gives the value of `variables[i]`. + +The [`submit`](@ref) call returns a [`HeuristicSolutionStatus`](@ref) indicating +whether the provided solution was accepted or rejected. This can be submitted only from the [`HeuristicCallback`](@ref). The field `callback_data` is a solver-specific callback type that is passed as the @@ -710,13 +711,15 @@ end Constraint `func`-to-`set` suggested to help the solver detect the solution given by [`CallbackVariablePrimal`](@ref) as infeasible. The cut is submitted -as `func, set`. +as the tuple `(func, set)::Tuple{MOI.AbstractFunction,MOI.AbstractSet}`. + Typically [`CallbackVariablePrimal`](@ref) will violate integrality constraints, and a cut would be of the form [`ScalarAffineFunction`](@ref)-in-[`LessThan`](@ref) -or [`ScalarAffineFunction`](@ref)-in-[`GreaterThan`](@ref). Note that, as -opposed to [`LazyConstraint`](@ref), the provided constraint cannot modify the -feasible set, the constraint should be redundant, for example, it may be a consequence -of affine and integrality constraints. +or [`ScalarAffineFunction`](@ref)-in-[`GreaterThan`](@ref). + +Note that, as opposed to [`LazyConstraint`](@ref), the provided constraint +must not modify the feasible set. The constraint should be redundant, for +example, it may be a consequence of affine and integrality constraints. This can be submitted only from the [`UserCutCallback`](@ref). The field `callback_data` is a solver-specific callback type that is passed as the @@ -758,11 +761,20 @@ end An Enum for the value of the [`CallbackNodeStatus`](@ref) attribute. """, CallbackNodeStatusCode, - "The primal solution available from [`CallbackVariablePrimal`](@ref) is integer feasible.", + """ + The primal solution available from [`CallbackVariablePrimal`](@ref) is + integer feasible. + """, CALLBACK_NODE_STATUS_INTEGER, - "The primal solution available from [`CallbackVariablePrimal`](@ref) is integer infeasible.", + """ + The primal solution available from [`CallbackVariablePrimal`](@ref) is + integer infeasible. + """, CALLBACK_NODE_STATUS_FRACTIONAL, - "The status of the primal solution available from [`CallbackVariablePrimal`](@ref) is unknown.", + """ + The status of the primal solution available from [`CallbackVariablePrimal`](@ref) + is unknown. + """, CALLBACK_NODE_STATUS_UNKNOWN, ) @@ -786,15 +798,35 @@ attribute_value_type(::CallbackNodeStatus) = CallbackNodeStatusCode """ ListOfOptimizerAttributesSet() -An optimizer attribute for the `Vector{AbstractOptimizerAttribute}` of all -optimizer attributes that were set. +An [`AbstractOptimizerAttribute`](@ref) for the `Vector{AbstractOptimizerAttribute}` +of all optimizer attributes that were set. + +## Implementation + +Optimizers should implement the following methods: +``` +MOI.get( + ::Optimizer, + ::MOI.ListOfOptimizerAttributesSet, +)::Vector{MOI.AbstractOptimizerAttribute} +``` + +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct ListOfOptimizerAttributesSet <: AbstractOptimizerAttribute end """ SolverName() -An optimizer attribute for the string identifying the solver/optimizer. +An [`AbstractOptimizerAttribute`](@ref) for the string identifying the solver. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.SolverName)::String +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct SolverName <: AbstractOptimizerAttribute end @@ -803,17 +835,27 @@ attribute_value_type(::SolverName) = String """ SolverVersion() -An optimizer attribute for the string identifying the version of the solver. +An [`AbstractOptimizerAttribute`](@ref) for the string identifying the version +of the solver. -!!! note +## Versioning systems + +For solvers supporting [semantic versioning](https://semver.org), the +[`SolverVersion`](@ref) should be a string of the form "vMAJOR.MINOR.PATCH", so +that it can be converted to a Julia `VersionNumber` (for example, +`VersionNumber("v1.2.3")). - For solvers supporting [semantic versioning](https://semver.org), the - `SolverVersion` should be a string of the form "vMAJOR.MINOR.PATCH", so that - it can be converted to a Julia `VersionNumber` (for example, `VersionNumber("v1.2.3")). +We do not require Semantic Versioning because some solvers use alternate +versioning systems. For example, CPLEX uses Calendar Versioning, so +[`SolverVersion`](@ref) will return a string like `"202001"`. - We do not require Semantic Versioning because some solvers use alternate - versioning systems. For example, CPLEX uses Calendar Versioning, so - `SolverVersion` will return a string like `"202001"`. +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.SolverVersion)::String +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct SolverVersion <: AbstractOptimizerAttribute end @@ -822,11 +864,20 @@ attribute_value_type(::SolverVersion) = String """ Silent() -An optimizer attribute for silencing the output of an optimizer. When `set` -to `true`, it takes precedence over any other attribute controlling verbosity -and requires the solver to produce no output. The default value is `false` -which has no effect. In this case the verbosity is controlled by other -attributes. +An [`AbstractOptimizerAttribute`](@ref) for silencing the output of an +optimizer. + +When `set` to `true`, this attribute takes precedence over any other attribute +controlling verbosity and requires the optimizer to produce no output. + +The default value is `false` which has no effect. In this case the verbosity is +controlled by other optimizer-specific attributes. + +## Value and default + +The provided value must be a `Bool`. + +The default value is `false`. ## Note @@ -837,6 +888,15 @@ should be set to `0`, even if the user specifically sets a value of log level. If the value of `Silent` is `false` then the log level set to the solver is the value given by the user for this solver-specific parameter or `1` if none is given. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.Silent)::Bool +MOI.set(::Optimizer, ::MOI.Silent, ::Bool)::Nothing +MOI.supports(::Optimizer, ::MOI.Silent)::Bool +``` """ struct Silent <: AbstractOptimizerAttribute end @@ -845,9 +905,36 @@ attribute_value_type(::Silent) = Bool """ TimeLimitSec() -An optimizer attribute for setting a time limit (in seconds) for an -optimization. When `set` to `nothing`, it deactivates the solver time limit. The -default value is `nothing`. +An [`AbstractOptimizerAttribute`](@ref) for setting a time limit (in seconds) +for a call to [`optimize!`](@ref). + +## Value and default + +The provided limit must be a `Union{Nothing,Real}`. + +When `set` to `nothing`, it deactivates the time limit. + +The default value is `nothing`. + +## TerminationStatus + +The optimizer may stop when the [`SolveTimeSec`](@ref) is larger than the +[`TimeLimitSec`](@ref). If stopped because of this limit, the +[`TerminationStatus`](@ref) must be [`TIME_LIMIT`](@ref). + +Note that most optimizers do not strictly respect a time limit. Instead, they +terminate at the first convenient time after the time limit has been exceeded. +Thus, you may find that the [`SolveTimeSec`](@ref) exceeds the [`TimeLimitSec`](@ref) +by a few seconds. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.TimeLimitSec)::Union{Nothing,Float64} +MOI.set(::Optimizer, ::MOI.TimeLimitSec, ::Union{Nothing,Real})::Nothing +MOI.supports(::Optimizer, ::MOI.TimeLimitSec)::Bool +``` """ struct TimeLimitSec <: AbstractOptimizerAttribute end @@ -856,27 +943,42 @@ attribute_value_type(::TimeLimitSec) = Union{Nothing,Float64} """ ObjectiveLimit() -An optimizer attribute for setting a limit on the objective value. +An [`AbstractOptimizerAttribute`](@ref) for setting a limit on the objective +value. -The provided limit must be a `Union{Real,Nothing}`. +## Value and default + +The provided limit must be a `Union{Nothing,Real}`. When `set` to `nothing`, the limit reverts to the solver's default. The default value is `nothing`. +## TerminationStatus + The solver may stop when the [`ObjectiveValue`](@ref) is better (lower for minimization, higher for maximization) than the `ObjectiveLimit`. If stopped, -the [`TerminationStatus`](@ref) should be [`OBJECTIVE_LIMIT`](@ref). +because of this limit, the [`TerminationStatus`](@ref) should be +[`OBJECTIVE_LIMIT`](@ref). + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.ObjectiveLimit)::Union{Nothing,Float64} +MOI.set(::Optimizer, ::MOI.ObjectiveLimit, ::Union{Nothing,Real})::Nothing +MOI.supports(::Optimizer, ::MOI.ObjectiveLimit)::Bool +``` """ struct ObjectiveLimit <: AbstractOptimizerAttribute end """ SolutionLimit() -An optimizer attribute for setting a limit on the number of available feasible -solutions. +An [`AbstractOptimizerAttribute`](@ref) for setting a limit on the number of +available feasible solutions. -## Default values +## Value and default The provided limit must be a `Union{Nothing,Int}`. @@ -887,13 +989,22 @@ The default value is `nothing`. ## Termination criteria The solver may stop when the [`ResultCount`](@ref) is larger than or equal to -the `SolutionLimit`. If stopped because of this attribute, the +the [`SolutionLimit`](@ref). If stopped because of this attribute, the [`TerminationStatus`](@ref) must be [`SOLUTION_LIMIT`](@ref). ## Solution quality The quality of the available solutions is solver-dependent. The set of resulting solutions is not guaranteed to contain an optimal solution. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.SolutionLimit)::Union{Nothing,Int} +MOI.set(::Optimizer, ::MOI.SolutionLimit, ::Union{Nothing,Int})::Nothing +MOI.supports(::Optimizer, ::MOI.SolutionLimit)::Bool +``` """ struct SolutionLimit <: AbstractOptimizerAttribute end @@ -902,10 +1013,10 @@ attribute_value_type(::SolutionLimit) = Union{Nothing,Int} """ NodeLimit() -An optimizer attribute for setting a limit on the number of branch-and-bound -nodes explored by a mixed-integer program (MIP) solver. +An [`AbstractOptimizerAttribute`](@ref) for setting a limit on the number of +branch-and-bound nodes explored by a mixed-integer program (MIP) solver. -## Default values +## Value and default The provided limit must be a `Union{Nothing,Int}`. @@ -916,8 +1027,17 @@ The default value is `nothing`. ## Termination criteria The solver may stop when the [`NodeCount`](@ref) is larger than or equal to -the `NodeLimit`. If stopped because of this attribute, the +the [`NodeLimit`](@ref). If stopped because of this attribute, the [`TerminationStatus`](@ref) must be [`NODE_LIMIT`](@ref). + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.NodeLimit)::Union{Nothing,Int} +MOI.set(::Optimizer, ::MOI.NodeLimit, ::Union{Nothing,Int})::Nothing +MOI.supports(::Optimizer, ::MOI.NodeLimit)::Bool +``` """ struct NodeLimit <: AbstractOptimizerAttribute end @@ -926,7 +1046,17 @@ attribute_value_type(::NodeLimit) = Union{Nothing,Int} """ RawOptimizerAttribute(name::String) -An optimizer attribute for the solver-specific parameter identified by `name`. +An [`AbstractOptimizerAttribute`](@ref) for the solver-specific parameter +identified by `name`. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.RawOptimizerAttribute)::Any +MOI.set(::Optimizer, ::MOI.RawOptimizerAttribute, ::Any)::Nothing +MOI.supports(::Optimizer, ::MOI.RawOptimizerAttribute)::Bool +``` """ struct RawOptimizerAttribute <: AbstractOptimizerAttribute name::String @@ -935,9 +1065,25 @@ end """ NumberOfThreads() -An optimizer attribute for setting the number of threads used for an -optimization. When set to `nothing` uses solver default. Values are positive -integers. The default value is `nothing`. +An [`AbstractOptimizerAttribute`](@ref) for setting the number of threads used +for an optimization. + +## Value and default + +The provided value must be `nothing` or a positive `Int`. + +When `set` to `nothing`, the value reverts to the solver's default. + +The default value is `nothing`. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.NumberOfThreads)::Union{Nothing,Int} +MOI.set(::Optimizer, ::MOI.NumberOfThreads, ::Union{Nothing,Int})::Nothing +MOI.supports(::Optimizer, ::MOI.NumberOfThreads)::Bool +``` """ struct NumberOfThreads <: AbstractOptimizerAttribute end @@ -946,32 +1092,38 @@ attribute_value_type(::NumberOfThreads) = Union{Nothing,Int} """ RelativeGapTolerance() -An optimizer attribute for setting the relative gap tolerance for an -optimization. This is an _optimizer_ attribute, and should be set before calling -[`optimize!`](@ref). When set to `nothing` (if supported), uses solver default. +An [`AbstractOptimizerAttribute`](@ref) for setting the relative gap tolerance +for an optimization. + +## Definition + +The mathematical definition of "relative gap" and its allowed range are +solver-dependent. Typically, solvers expect a value between `0.0` and `1.0`. + +## Value and default + +The provided value must be a `Union{Nothing,Float64}`. + +When set to `nothing`, the limit reverts to the solver's default. + +## TerminationStatus + +The optimizer may stop when the [`RelativeGap`](@ref) is smaller than the +[`RelativeGapTolerance`](@ref). If stopped because of this limit, the +[`TerminationStatus`](@ref) may be [`OPTIMAL`](@ref). -If you are looking for the relative gap of the current best solution, see -[`RelativeGap`](@ref). If no limit nor issue is encountered during the -optimization, the value of [`RelativeGap`](@ref) should be at most as large as -`RelativeGapTolerance`. +## Implementation +Optimizers should implement the following methods: ```julia -# Before optimizing: set relative gap tolerance -# set 0.1% relative gap tolerance -MOI.set(model, MOI.RelativeGapTolerance(), 1e-3) -MOI.optimize!(model) - -# After optimizing (assuming all went well) -# The relative gap tolerance has not changed... -MOI.get(model, MOI.RelativeGapTolerance()) # returns 1e-3 -# ... and the relative gap of the obtained solution is smaller or equal to the -# tolerance -MOI.get(model, MOI.RelativeGap()) # should return something ≤ 1e-3 +MOI.get(::Optimizer, ::MOI.RelativeGapTolerance)::Union{Nothing,Float64} +MOI.set( + ::Optimizer, + ::MOI.RelativeGapTolerance, + ::Union{Nothing,Float64}, +)::Nothing +MOI.supports(::Optimizer, ::MOI.RelativeGapTolerance)::Bool ``` - -!!! warning - The mathematical definition of "relative gap", and its allowed range, are - solver-dependent. Typically, solvers expect a value between `0.0` and `1.0`. """ struct RelativeGapTolerance <: AbstractOptimizerAttribute end @@ -980,19 +1132,41 @@ attribute_value_type(::RelativeGapTolerance) = Union{Nothing,Float64} """ AbsoluteGapTolerance() -An optimizer attribute for setting the absolute gap tolerance for an -optimization. This is an _optimizer_ attribute, and should be set before calling -[`optimize!`](@ref). When set to `nothing` (if supported), uses solver default. +An [`AbstractOptimizerAttribute`](@ref) for setting the absolute gap tolerance +for an optimization. -To set a _relative_ gap tolerance, see [`RelativeGapTolerance`](@ref). +## Definition -!!! warning - The mathematical definition of "absolute gap", and its treatment during the - optimization, are solver-dependent. However, assuming no other limit nor - issue is encountered during the optimization, most solvers that implement - this attribute will stop once ``|f - b| ≤ g_{abs}``, where ``b`` is the best - bound, ``f`` is the best feasible objective value, and ``g_{abs}`` is the - absolute gap. +The mathematical definition of "absolute gap" and its allowed range are +solver-dependent. However, most solvers that implement this attribute will stop +once ``|f - b| ≤ g_{abs}``, where ``b`` is the best bound, ``f`` is the best +feasible objective value, and ``g_{abs}`` is the absolute gap. + +## Value and default + +The provided value must be a `Union{Nothing,Float64}`. + +When set to `nothing`, the limit reverts to the solver's default. + +## TerminationStatus + +The optimizer may stop when the absolute difference between [`ObjectiveValue`](@ref) +and [`ObjectiveBound`](@ref) is smaller than the [`AbsoluteGapTolerance`](@ref). +If stopped because of this limit, the [`TerminationStatus`](@ref) may be +[`OPTIMAL`](@ref). + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.AbsoluteGapTolerance)::Union{Nothing,Float64} +MOI.set( + ::Optimizer, + ::MOI.AbsoluteGapTolerance, + ::Union{Nothing,Float64}, +)::Nothing +MOI.supports(::Optimizer, ::MOI.AbsoluteGapTolerance)::Bool +``` """ struct AbsoluteGapTolerance <: AbstractOptimizerAttribute end @@ -1133,7 +1307,7 @@ struct UserCutCallback <: AbstractCallback end """ ListOfModelAttributesSet() -A model attribute for the `Vector{AbstractModelAttribute}` of all model +An [`AbstractModelAttribute`](@ref) for the `Vector{AbstractModelAttribute}` of all model attributes `attr` such that: 1. `is_copyable(attr)` returns `true`, and @@ -1144,8 +1318,18 @@ struct ListOfModelAttributesSet <: AbstractModelAttribute end """ Name() -A model attribute for the string identifying the model. It has a default value -of `""` if not set`. +An [`AbstractModelAttribute`](@ref) for the string identifying the model. + +It has a default value of `""` if not set. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.Name)::String +MOI.set(::Optimizer, ::MOI.Name, ::String)::Nothing +MOI.supports(::Optimizer, ::MOI.Name)::Bool +``` """ struct Name <: AbstractModelAttribute end @@ -1169,8 +1353,8 @@ attribute_value_type(::Name) = String """ ObjectiveSense() -A model attribute for the objective sense of the objective function, which -must be an [`OptimizationSense`](@ref). +An [`AbstractModelAttribute`](@ref) for the objective sense of the objective +function, which must be an [`OptimizationSense`](@ref). The default is [`FEASIBILITY_SENSE`](@ref). @@ -1182,8 +1366,17 @@ attribute. That is, if you first set [`ObjectiveFunction`](@ref) and then set be passed to the solver. In addition, some reformulations of [`ObjectiveFunction`](@ref) via bridges rely -on the value of `ObjectiveSense`. Therefore, you should set `ObjectiveSense` -before setting [`ObjectiveFunction`](@ref). +on the value of [`ObjectiveSense`](@ref). Therefore, you should set +[`ObjectiveSense`](@ref) before setting [`ObjectiveFunction`](@ref). + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.ObjectiveSense)::MOI.OptimizationSense +MOI.set(::Optimizer, ::MOI.ObjectiveSense, ::MOI.OptimizationSense)::Nothing +MOI.supports(::Optimizer, ::MOI.ObjectiveSense)::Bool +``` """ struct ObjectiveSense <: AbstractModelAttribute end @@ -1192,7 +1385,16 @@ attribute_value_type(::ObjectiveSense) = OptimizationSense """ NumberOfVariables() -A model attribute for the number of variables in the model. +An [`AbstractModelAttribute`](@ref) for the number of variables in the model. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.NumberOfVariables)::Int64 +``` + +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct NumberOfVariables <: AbstractModelAttribute end @@ -1201,18 +1403,45 @@ attribute_value_type(::NumberOfVariables) = Int64 """ ListOfVariableIndices() -A model attribute for the `Vector{VariableIndex}` of all variable indices -present in the model (that is, of length equal to the value of -[`NumberOfVariables`](@ref) in the order in which they were added. +An [`AbstractModelAttribute`](@ref) for querying the `Vector{MOI.VariableIndex}` +of all [`MOI.VariableIndex`] present in the model. + +## Order + +The variables must be returned in the order in which they were added to the +model. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.ListOfVariableIndices)::Vector{MOI.VariableIndex} +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct ListOfVariableIndices <: AbstractModelAttribute end """ ListOfConstraintIndices{F,S}() -A model attribute for the `Vector{ConstraintIndex{F,S}}` of all constraint -indices of type `F`-in-`S` in the model (that is, of length equal to the value of -[`NumberOfConstraints{F,S}`](@ref)) in the order in which they were added. +An [`AbstractModelAttribute`](@ref) for the `Vector{MOI.ConstraintIndex{F,S}}` +of all constraint indices of type `F`-in-`S` in the model. + +## Order + +The constraints must be returned in the order in which they were added to the +model. + +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.ListOfConstraintIndices{F,S}, +)::Vector{MOI.ConstraintIndex{F,S}} where {F<:MOI.AbstractFunction,MOI.AbstractSet} +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct ListOfConstraintIndices{F,S} <: AbstractModelAttribute end @@ -1233,8 +1462,19 @@ end """ NumberOfConstraints{F,S}() -A model attribute for the number of constraints of the type `F`-in-`S` present -in the model. +An [`AbstractModelAttribute`](@ref) for querying the number of constraints of +the type `F`-in-`S` present in the model. + +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.NumberOfConstraints{F,S}, +)::Int64 where {F<:MOI.AbstractFunction,MOI.AbstractSet} +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct NumberOfConstraints{F,S} <: AbstractModelAttribute end @@ -1254,25 +1494,48 @@ end """ ListOfConstraintTypesPresent() -A model attribute for the list of tuples of the form `(F,S)`, where `F` is a -function type and `S` is a set type indicating that the attribute -[`NumberOfConstraints{F,S}`](@ref) has a value greater than zero. +An [`AbstractModelAttribute`](@ref) for the list of tuples of the form `(F, S)`, +indicating that the attribute [`NumberOfConstraints{F,S}`](@ref) has a value +greater than zero. + +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.ListOfConstraintTypesPresent, +)::Vector{Tuple{Type,Type}} +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct ListOfConstraintTypesPresent <: AbstractModelAttribute end """ ObjectiveFunction{F<:AbstractScalarFunction}() -A model attribute for the objective function which has a type +An [`AbstractModelAttribute`](@ref) for the objective function which has a type `F<:AbstractScalarFunction`. -`F` should be guaranteed to be equivalent but not necessarily identical to the -function type provided by the user. +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.ObjectiveFunction{F}, +)::F where {F<:MOI.AbstractFunction} +function MOI.set( + ::Optimizer, + ::MOI.ObjectiveFunction{F}, + ::F, +)::F where {F<:MOI.AbstractFunction} +MOI.supports(::Optimizer, ::MOI.ObjectiveFunction{<:MOI.AbstractFunction})::Bool +``` -Throws an `InexactError` if the objective function cannot be converted to `F`, -for example, the objective function is quadratic and `F` is -`ScalarAffineFunction{Float64}` or it has non-integer coefficient and `F` is -`ScalarAffineFunction{Int}`. +When implementing `get`, `F` may to be equivalent but not necessarily identical +to the function type set by the user. If the objective function cannot be +converted to `F`, an `InexactError` must be thrown. """ struct ObjectiveFunction{F<:AbstractFunction} <: AbstractModelAttribute end @@ -1281,8 +1544,19 @@ attribute_value_type(::ObjectiveFunction{F}) where {F} = F """ ObjectiveFunctionType() -A model attribute for the type `F` of the objective function set using the -[`ObjectiveFunction{F}`](@ref) attribute. +An [`AbstractModelAttribute`](@ref) for the type `F` of the objective function +set using the [`ObjectiveFunction{F}`](@ref) attribute. + +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.ObjectiveFunctionType, +)::Type{<:MOI.AbstractFunction} +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). ## Example @@ -1307,17 +1581,37 @@ attribute_value_type(::ObjectiveFunctionType) = Type{<:AbstractFunction} """ ObjectiveValue(result_index::Int = 1) -A model attribute for the objective value of the primal solution `result_index`. +An [`AbstractModelAttribute`](@ref) for the objective value of the primal +solution `result_index`. + +## PrimalStatus + +Before quering this attribute you should first check [`PrimalStatus`](@ref) to +confirm that a primal solution is avaiable. + +If the [`PrimalStatus`](@ref) is [`NO_SOLUTION`](@ref) the result of querying +this attribute is undefined. + +## `result_index` + +The optimizer may return multiple primal solutions. See [`ResultCount`](@ref) +for information on how the results are ordered. If the solver does not have a primal value for the objective because the `result_index` is beyond the available solutions (whose number is indicated by the [`ResultCount`](@ref) attribute), getting this attribute must throw a -[`ResultIndexBoundsError`](@ref). Otherwise, if the result is unavailable for -another reason (for instance, only a dual solution is available), the result is -undefined. Users should first check [`PrimalStatus`](@ref) before accessing the -`ObjectiveValue` attribute. +[`ResultIndexBoundsError`](@ref). -See [`ResultCount`](@ref) for information on how the results are ordered. +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.ObjectiveValue, +)::Union{T,Vector{T}} where {T<:Real} +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct ObjectiveValue <: AbstractModelAttribute result_index::Int @@ -1327,18 +1621,37 @@ end """ DualObjectiveValue(result_index::Int = 1) -A model attribute for the value of the objective function of the dual problem -for the `result_index`th dual result. +An [`AbstractModelAttribute`](@ref) for the value of the objective function of +the dual solution `result_index`. + +## DualStatus + +Before quering this attribute you should first check [`DualStatus`](@ref) to +confirm that a dual solution is avaiable. + +If the [`DualStatus`](@ref) is [`NO_SOLUTION`](@ref) the result of querying +this attribute is undefined. + +## `result_index` + +The optimizer may return multiple dual solutions. See [`ResultCount`](@ref) +for information on how the results are ordered. If the solver does not have a dual value for the objective because the `result_index` is beyond the available solutions (whose number is indicated by the [`ResultCount`](@ref) attribute), getting this attribute must throw a -[`ResultIndexBoundsError`](@ref). Otherwise, if the result is unavailable for -another reason (for instance, only a primal solution is available), the result -is undefined. Users should first check [`DualStatus`](@ref) before accessing the -`DualObjectiveValue` attribute. +[`ResultIndexBoundsError`](@ref). -See [`ResultCount`](@ref) for information on how the results are ordered. +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.DualObjectiveValue, +)::Union{T,Vector{T}} where {T<:Real} +``` +They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). """ struct DualObjectiveValue <: AbstractModelAttribute result_index::Int @@ -1348,14 +1661,14 @@ end """ ObjectiveBound() -A model attribute for the best known bound on the optimal objective value. +An [`AbstractModelAttribute`](@ref) for the best known bound on the optimal objective value. """ struct ObjectiveBound <: AbstractModelAttribute end """ RelativeGap() -A model attribute for the final relative optimality gap. +An [`AbstractModelAttribute`](@ref) for the final relative optimality gap. !!! warning The definition of this gap is solver-dependent. However, most solvers @@ -1370,7 +1683,7 @@ attribute_value_type(::RelativeGap) = Float64 """ SolveTimeSec() -A model attribute for the total elapsed solution time (in seconds) as reported +An [`AbstractModelAttribute`](@ref) for the total elapsed solution time (in seconds) as reported by the optimizer. """ struct SolveTimeSec <: AbstractModelAttribute end @@ -1380,7 +1693,7 @@ attribute_value_type(::SolveTimeSec) = Float64 """ SimplexIterations() -A model attribute for the cumulative number of simplex iterations during the +An [`AbstractModelAttribute`](@ref) for the cumulative number of simplex iterations during the optimization process. For a mixed-integer program (MIP), the return value is the total simplex @@ -1393,7 +1706,7 @@ attribute_value_type(::SimplexIterations) = Int64 """ BarrierIterations() -A model attribute for the cumulative number of barrier iterations while solving +An [`AbstractModelAttribute`](@ref) for the cumulative number of barrier iterations while solving a problem. """ struct BarrierIterations <: AbstractModelAttribute end @@ -1403,7 +1716,7 @@ attribute_value_type(::BarrierIterations) = Int64 """ NodeCount() -A model attribute for the total number of branch-and-bound nodes explored while +An [`AbstractModelAttribute`](@ref) for the total number of branch-and-bound nodes explored while solving a mixed-integer program (MIP). """ struct NodeCount <: AbstractModelAttribute end @@ -1413,7 +1726,7 @@ attribute_value_type(::NodeCount) = Int64 """ RawSolver() -A model attribute for the object that may be used to access a solver-specific +An [`AbstractModelAttribute`](@ref) for the object that may be used to access a solver-specific API for this optimizer. """ struct RawSolver <: AbstractModelAttribute end @@ -1421,7 +1734,7 @@ struct RawSolver <: AbstractModelAttribute end """ ResultCount() -A model attribute for the number of results available. +An [`AbstractModelAttribute`](@ref) for the number of results available. ## Order of solutions @@ -1475,7 +1788,7 @@ attribute_value_type(::ResultCount) = Int """ ConflictStatus() -A model attribute for the [`ConflictStatusCode`](@ref) explaining why the +An [`AbstractModelAttribute`](@ref) for the [`ConflictStatusCode`](@ref) explaining why the conflict refiner stopped when computing the conflict. """ struct ConflictStatus <: AbstractModelAttribute end @@ -1485,7 +1798,7 @@ attribute_value_type(::ConflictStatus) = ConflictStatusCode """ ListOfVariableAttributesSet() -A model attribute for the `Vector{AbstractVariableAttribute}` of all variable +An [`AbstractModelAttribute`](@ref) for the `Vector{AbstractVariableAttribute}` of all variable attributes `attr` such that 1) `is_copyable(attr)` returns `true` and 2) the attribute was set to variables. """ @@ -1494,7 +1807,7 @@ struct ListOfVariableAttributesSet <: AbstractModelAttribute end """ ListOfVariablesWithAttributeSet(attr::AbstractVariableAttribute) -A model attribute for the `Vector{VariableIndex}` of all variables with the +An [`AbstractModelAttribute`](@ref) for the `Vector{VariableIndex}` of all variables with the attribute `attr` set. The returned list may not be minimal, so some elements may have their default @@ -1638,7 +1951,7 @@ attribute_value_type(::VariableBasisStatus) = BasisStatusCode """ ListOfConstraintAttributesSet{F, S}() -A model attribute for the `Vector{AbstractConstraintAttribute}` of all +An [`AbstractModelAttribute`](@ref) for the `Vector{AbstractConstraintAttribute}` of all constraint attributes `attr` such that: 1. `is_copyable(attr)` returns `true` and @@ -1654,7 +1967,7 @@ struct ListOfConstraintAttributesSet{F,S} <: AbstractModelAttribute end """ ListOfConstraintsWithAttributeSet{F,S}(attr:AbstractConstraintAttribute) -A model attribute for the `Vector{ConstraintIndex{F,S}}` of all constraints with +An [`AbstractModelAttribute`](@ref) for the `Vector{ConstraintIndex{F,S}}` of all constraints with the attribute `attr` set. The returned list may not be minimal, so some elements may have their default @@ -2288,7 +2601,7 @@ end """ TerminationStatus() -A model attribute for the [`TerminationStatusCode`](@ref) explaining why the +An [`AbstractModelAttribute`](@ref) for the [`TerminationStatusCode`](@ref) explaining why the optimizer stopped. """ struct TerminationStatus <: AbstractModelAttribute end @@ -2298,7 +2611,7 @@ attribute_value_type(::TerminationStatus) = TerminationStatusCode """ RawStatusString() -A model attribute for a solver specific string explaining why the optimizer +An [`AbstractModelAttribute`](@ref) for a solver specific string explaining why the optimizer stopped. """ struct RawStatusString <: AbstractModelAttribute end @@ -2373,7 +2686,7 @@ attribute_value_type(::RawStatusString) = String """ PrimalStatus(result_index::Int = 1) -A model attribute for the [`ResultStatusCode`](@ref) of the primal result +An [`AbstractModelAttribute`](@ref) for the [`ResultStatusCode`](@ref) of the primal result `result_index`. If `result_index` is omitted, it defaults to 1. @@ -2393,7 +2706,7 @@ attribute_value_type(::PrimalStatus) = ResultStatusCode """ DualStatus(result_index::Int = 1) -A model attribute for the [`ResultStatusCode`](@ref) of the dual result +An [`AbstractModelAttribute`](@ref) for the [`ResultStatusCode`](@ref) of the dual result `result_index`. If `result_index` is omitted, it defaults to 1. From 075187cea3a234aa6d8fa78b90fc3000be965271 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 27 Mar 2025 10:54:48 +1300 Subject: [PATCH 2/4] Update --- src/attributes.jl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/attributes.jl b/src/attributes.jl index 30d48ae798..10f6bce2c3 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -811,7 +811,7 @@ MOI.get( )::Vector{MOI.AbstractOptimizerAttribute} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ListOfOptimizerAttributesSet <: AbstractOptimizerAttribute end @@ -826,7 +826,7 @@ Optimizers should implement the following methods: ```julia MOI.get(::Optimizer, ::MOI.SolverName)::String ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct SolverName <: AbstractOptimizerAttribute end @@ -855,7 +855,7 @@ Optimizers should implement the following methods: ```julia MOI.get(::Optimizer, ::MOI.SolverVersion)::String ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct SolverVersion <: AbstractOptimizerAttribute end @@ -1394,7 +1394,7 @@ Optimizers should implement the following methods: MOI.get(::Optimizer, ::MOI.NumberOfVariables)::Int64 ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct NumberOfVariables <: AbstractModelAttribute end @@ -1417,7 +1417,7 @@ Optimizers should implement the following methods: ```julia MOI.get(::Optimizer, ::MOI.ListOfVariableIndices)::Vector{MOI.VariableIndex} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ListOfVariableIndices <: AbstractModelAttribute end @@ -1441,7 +1441,7 @@ function MOI.get( ::MOI.ListOfConstraintIndices{F,S}, )::Vector{MOI.ConstraintIndex{F,S}} where {F<:MOI.AbstractFunction,MOI.AbstractSet} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ListOfConstraintIndices{F,S} <: AbstractModelAttribute end @@ -1474,7 +1474,7 @@ function MOI.get( ::MOI.NumberOfConstraints{F,S}, )::Int64 where {F<:MOI.AbstractFunction,MOI.AbstractSet} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct NumberOfConstraints{F,S} <: AbstractModelAttribute end @@ -1507,7 +1507,7 @@ function MOI.get( ::MOI.ListOfConstraintTypesPresent, )::Vector{Tuple{Type,Type}} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ListOfConstraintTypesPresent <: AbstractModelAttribute end @@ -1556,7 +1556,7 @@ function MOI.get( ::MOI.ObjectiveFunctionType, )::Type{<:MOI.AbstractFunction} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). ## Example @@ -1611,7 +1611,7 @@ function MOI.get( ::MOI.ObjectiveValue, )::Union{T,Vector{T}} where {T<:Real} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ObjectiveValue <: AbstractModelAttribute result_index::Int @@ -1651,7 +1651,7 @@ function MOI.get( ::MOI.DualObjectiveValue, )::Union{T,Vector{T}} where {T<:Real} ``` -They should not implement [`MOI.set`](@ref) or [`MOI.supports`](@ref). +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct DualObjectiveValue <: AbstractModelAttribute result_index::Int From 256a1723997f1d4a781ce2e224fbc8c95a887b9e Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 27 Mar 2025 14:48:37 +1300 Subject: [PATCH 3/4] Update --- src/attributes.jl | 342 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 291 insertions(+), 51 deletions(-) diff --git a/src/attributes.jl b/src/attributes.jl index 10f6bce2c3..12f78f2136 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -2062,25 +2062,51 @@ struct ConstraintDualStart <: AbstractConstraintAttribute end A constraint attribute for the assignment to some constraint's primal value in result `result_index`. -If the constraint is `f(x) in S`, then in most cases the `ConstraintPrimal` is -the value of `f`, evaluated at the corresponding [`VariablePrimal`](@ref) +## Definition + +If the constraint is ``f(x) \\in S``, then in most cases the [`ConstraintPrimal`](@ref) +is the value of ``f``, evaluated at the corresponding [`VariablePrimal`](@ref) solution. -However, some conic solvers reformulate `b - Ax in S` to `s = b - Ax, s in S`. -These solvers may return the value of `s` for `ConstraintPrimal`, rather than -`b - Ax`. (Although these are constrained by an equality constraint, due to -numerical tolerances they may not be identical.) +However, some conic solvers reformulate ``b - Ax \\in S`` to ``s = b - Ax`` and +``s \\in S``. These solvers may return the value of `s` for [`ConstraintPrimal`](@ref), +rather than `b - Ax`. (Although these are constrained by an equality constraint, +due to numerical tolerances they may not be identical.) + +## PrimalStatus + +Before quering this attribute you should first check [`PrimalStatus`](@ref) to +confirm that a primal solution is avaiable. + +If the [`PrimalStatus`](@ref) is [`NO_SOLUTION`](@ref) the result of querying +this attribute is undefined. + +## `result_index` + +The optimizer may return multiple primal solutions. See [`ResultCount`](@ref) +for information on how the results are ordered. If the solver does not have a primal value for the constraint because the `result_index` is beyond the available solutions (whose number is indicated by the [`ResultCount`](@ref) attribute), getting this attribute must throw a -[`ResultIndexBoundsError`](@ref). Otherwise, if the result is unavailable for -another reason (for instance, only a dual solution is available), the result is -undefined. Users should first check [`PrimalStatus`](@ref) before accessing the -`ConstraintPrimal` attribute. +[`ResultIndexBoundsError`](@ref). -If `result_index` is omitted, it is 1 by default. See [`ResultCount`](@ref) for -information on how the results are ordered. +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.ConstraintPrimal, + ::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction} +)::T +function MOI.get( + ::Optimizer, + ::MOI.ConstraintPrimal, + ::MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} +)::Vector{T} +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ConstraintPrimal <: AbstractConstraintAttribute result_index::Int @@ -2091,17 +2117,42 @@ end ConstraintDual(result_index::Int = 1) A constraint attribute for the assignment to some constraint's dual value in -result `result_index`. If `result_index` is omitted, it is 1 by default. +result `result_index`. + +## DualStatus + +Before quering this attribute you should first check [`DualStatus`](@ref) to +confirm that a dual solution is avaiable. + +If the [`DualStatus`](@ref) is [`NO_SOLUTION`](@ref) the result of querying +this attribute is undefined. + +## `result_index` + +The optimizer may return multiple dual solutions. See [`ResultCount`](@ref) +for information on how the results are ordered. -If the solver does not have a dual value for the variable because the +If the solver does not have a dual value for the constraint because the `result_index` is beyond the available solutions (whose number is indicated by the [`ResultCount`](@ref) attribute), getting this attribute must throw a -[`ResultIndexBoundsError`](@ref). Otherwise, if the result is unavailable for -another reason (for instance, only a primal solution is available), the result is -undefined. Users should first check [`DualStatus`](@ref) before accessing the -`ConstraintDual` attribute. +[`ResultIndexBoundsError`](@ref). -See [`ResultCount`](@ref) for information on how the results are ordered. +## Implementation + +Optimizers should implement the following methods: +```julia +function MOI.get( + ::Optimizer, + ::MOI.ConstraintDual, + ::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction} +)::T +function MOI.get( + ::Optimizer, + ::MOI.ConstraintDual, + ::MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} +)::Vector{T} +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ConstraintDual <: AbstractConstraintAttribute result_index::Int @@ -2111,9 +2162,10 @@ end """ ConstraintBasisStatus(result_index::Int = 1) -A constraint attribute for the `BasisStatusCode` of some constraint in result -`result_index`, with respect to an available optimal solution basis. If -`result_index` is omitted, it is 1 by default. +A constraint attribute for the [`BasisStatusCode`](@ref) of some constraint in +result `result_index`, with respect to an available optimal solution basis. + +If `result_index` is omitted, it is 1 by default. If the solver does not have a basis status for the constraint because the `result_index` is beyond the available solutions (whose number is indicated by @@ -2160,15 +2212,46 @@ A constraint attribute for a canonical representation of the Getting this attribute is guaranteed to return a function that is equivalent but not necessarily identical to the function provided by the user. -By default, `MOI.get(model, MOI.CanonicalConstraintFunction(), ci)` fallbacks to -`MOI.Utilities.canonical(MOI.get(model, MOI.ConstraintFunction(), ci))`. +## Fallback + +By default, `MOI.get(model, MOI.CanonicalConstraintFunction(), ci)` falls back +to `MOI.Utilities.canonical(MOI.get(model, MOI.ConstraintFunction(), ci))`. + However, if `model` knows that the constraint function is canonical then it can implement a specialized method that directly return the function without calling [`Utilities.canonical`](@ref). Therefore, the value returned **cannot** be assumed to be a copy of the function stored in `model`. + Moreover, [`Utilities.Model`](@ref) checks with [`Utilities.is_canonical`](@ref) whether the function stored internally is already canonical and if it's the case, then it returns the function stored internally instead of a copy. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variable(model); + +julia> c = MOI.add_constraint(model, 1.0 * x + 1.0 * x, MOI.GreaterThan(0.0)); + +julia> MOI.get(model, MOI.CanonicalConstraintFunction(), c) +0.0 + 2.0 MOI.VariableIndex(1) +``` + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get( + ::Optimizer, + ::MOI.CanonicalConstraintFunction, + ::MOI.ConstraintIndex{F,S}, +)::F where {F,S} +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct CanonicalConstraintFunction <: AbstractConstraintAttribute end @@ -2202,6 +2285,47 @@ the constraint. It is guaranteed to be equivalent but not necessarily identical to the function provided by the user. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variable(model); + +julia> c = MOI.add_constraint(model, 1.0 * x, MOI.GreaterThan(0.0)); + +julia> MOI.get(model, MOI.ConstraintFunction(), c) +0.0 + 1.0 MOI.VariableIndex(1) + +julia> MOI.set(model, MOI.ConstraintFunction(), c, 2.0 * x) + +julia> MOI.get(model, MOI.ConstraintFunction(), c) +0.0 + 2.0 MOI.VariableIndex(1) +``` + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get( + ::Optimizer, + ::MOI.ConstraintFunction, + ::MOI.ConstraintIndex{F,S}, +)::F where {F,S} +``` +If the optimizer supports modifying an existing function, it should implement: +```julia +MOI.set( + ::Optimizer, + ::MOI.ConstraintFunction, + ::MOI.ConstraintIndex{F,S}, + ::F, +)::Nothing where {F,S} +``` +It should not implement [`supports`](@ref). """ struct ConstraintFunction <: AbstractConstraintAttribute end @@ -2244,6 +2368,47 @@ end A constraint attribute for the [`AbstractSet`](@ref) object used to define the constraint. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variable(model); + +julia> c = MOI.add_constraint(model, x, MOI.GreaterThan(0.0)); + +julia> MOI.get(model, MOI.ConstraintSet(), c) +MathOptInterface.GreaterThan{Float64}(0.0) + +julia> MOI.set(model, MOI.ConstraintSet(), c, MOI.GreaterThan(1.0)) + +julia> MOI.get(model, MOI.ConstraintSet(), c) +MathOptInterface.GreaterThan{Float64}(1.0) +``` + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get( + ::Optimizer, + ::MOI.ConstraintSet, + ::MOI.ConstraintIndex{F,S}, +)::S where {F,S} +``` +If the optimizer supports modifying an existing set, it should implement: +```julia +MOI.set( + ::Optimizer, + ::MOI.ConstraintSet, + ::MOI.ConstraintIndex{F,S}, + ::S, +)::Nothing where {F,S} +``` +It should not implement [`supports`](@ref). """ struct ConstraintSet <: AbstractConstraintAttribute end @@ -2410,7 +2575,19 @@ end ListOfSupportedNonlinearOperators() <: AbstractModelAttribute When queried with [`get`](@ref), return a `Vector{Symbol}` listing the operators -supported by the model. +supported by the model. These operators may appear in the `head` field of +[`ScalarNonlinearFunction`](@ref). + +## Implementation + +Optimizers should implement the following methods: +``` +MOI.get( + ::Optimizer, + ::MOI.ListOfSupportedNonlinearOperators, +)::Vector{Symbol} +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ListOfSupportedNonlinearOperators <: AbstractOptimizerAttribute end @@ -2587,10 +2764,26 @@ struct ListOfSupportedNonlinearOperators <: AbstractOptimizerAttribute end """ AutomaticDifferentiationBackend() <: AbstractOptimizerAttribute -An [`AbstractOptimizerAttribute`](@ref) for setting the automatic differentiation -backend used by the solver. +An [`AbstractOptimizerAttribute`](@ref) for setting the automatic +differentiation backend used by the solver. The value must be a subtype of [`Nonlinear.AbstractAutomaticDifferentiation`](@ref). + +## Implementation + +Optimizers should implement the following methods: +``` +MOI.get( + ::Optimizer, + ::MOI.AutomaticDifferentiationBackend, +)::MOI.Nonlinear.AbstractAutomaticDifferentiation +MOI.set( + ::Optimizer, + ::MOI.AutomaticDifferentiationBackend, + ::MOI.Nonlinear.AbstractAutomaticDifferentiation, +)::Nothing +MOI.supports(::Optimizer, ::MOI.AutomaticDifferentiationBackend)::Bool +``` """ struct AutomaticDifferentiationBackend <: AbstractOptimizerAttribute end @@ -2601,8 +2794,16 @@ end """ TerminationStatus() -An [`AbstractModelAttribute`](@ref) for the [`TerminationStatusCode`](@ref) explaining why the -optimizer stopped. +An [`AbstractModelAttribute`](@ref) for the [`TerminationStatusCode`](@ref) +explaining why the optimizer stopped. + +## Implementation + +Optimizers should implement the following methods: +``` +MOI.get(::Optimizer, ::MOI.TerminationStatus)::MOI.TerminationStatusCode +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct TerminationStatus <: AbstractModelAttribute end @@ -2611,8 +2812,16 @@ attribute_value_type(::TerminationStatus) = TerminationStatusCode """ RawStatusString() -An [`AbstractModelAttribute`](@ref) for a solver specific string explaining why the optimizer -stopped. +An [`AbstractModelAttribute`](@ref) for a solver specific string explaining why +the optimizer stopped. + +## Implementation + +Optimizers should implement the following methods: +``` +MOI.get(::Optimizer, ::MOI.RawStatusString)::MOI.String +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct RawStatusString <: AbstractModelAttribute end @@ -2639,32 +2848,32 @@ attribute_value_type(::RawStatusString) = String """ The result vector is an infeasibility certificate. - If the [`PrimalStatus`](@ref) is `INFEASIBILITY_CERTIFICATE`, then the - primal result vector is a certificate of dual infeasibility. + If the [`PrimalStatus`](@ref) is [`INFEASIBILITY_CERTIFICATE`](@ref), then + the primal result vector is a certificate of dual infeasibility. - If the [`DualStatus`](@ref) is `INFEASIBILITY_CERTIFICATE`, then the dual - result vector is a proof of primal infeasibility. + If the [`DualStatus`](@ref) is [`INFEASIBILITY_CERTIFICATE`](@ref), then the + dual result vector is a proof of primal infeasibility. """, INFEASIBILITY_CERTIFICATE, """ The result satisfies a relaxed criterion for a certificate of infeasibility. - If the [`PrimalStatus`](@ref) is `NEARLY_INFEASIBILITY_CERTIFICATE`, then - the primal result vector is a certificate of dual infeasibility. + If the [`PrimalStatus`](@ref) is [`NEARLY_INFEASIBILITY_CERTIFICATE`](@ref), + then the primal result vector is a certificate of dual infeasibility. - If the [`DualStatus`](@ref) is `NEARLY_INFEASIBILITY_CERTIFICATE`, then the - dual result vector is a proof of primal infeasibility. + If the [`DualStatus`](@ref) is [`NEARLY_INFEASIBILITY_CERTIFICATE`](@ref), + then the dual result vector is a proof of primal infeasibility. """, NEARLY_INFEASIBILITY_CERTIFICATE, """ The result vector is an ill-posed certificate; see [this article](https://arxiv.org/abs/1408.4685) for details. - If the [`PrimalStatus`](@ref) is `REDUCTION_CERTIFICATE`, then the primal - result vector is a proof that the dual problem is ill-posed. + If the [`PrimalStatus`](@ref) is [`REDUCTION_CERTIFICATE`](@ref), then the + primal result vector is a proof that the dual problem is ill-posed. - If the [`DualStatus`](@ref) is `REDUCTION_CERTIFICATE`, then the dual result - vector is a proof that the primal is ill-posed. + If the [`DualStatus`](@ref) is [`REDUCTION_CERTIFICATE`](@ref), then the + dual result vector is a proof that the primal is ill-posed. """, REDUCTION_CERTIFICATE, """ @@ -2686,8 +2895,10 @@ attribute_value_type(::RawStatusString) = String """ PrimalStatus(result_index::Int = 1) -An [`AbstractModelAttribute`](@ref) for the [`ResultStatusCode`](@ref) of the primal result -`result_index`. +An [`AbstractModelAttribute`](@ref) for the [`ResultStatusCode`](@ref) of the +primal result `result_index`. + +## `result_index` If `result_index` is omitted, it defaults to 1. @@ -2695,6 +2906,14 @@ See [`ResultCount`](@ref) for information on how the results are ordered. If `result_index` is larger than the value of [`ResultCount`](@ref) then [`NO_SOLUTION`](@ref) is returned. + +## Implementation + +Optimizers should implement the following methods: +``` +MOI.get(::Optimizer, ::MOI.PrimalStatus)::MOI.ResultStatusCode +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct PrimalStatus <: AbstractModelAttribute result_index::Int @@ -2706,15 +2925,23 @@ attribute_value_type(::PrimalStatus) = ResultStatusCode """ DualStatus(result_index::Int = 1) -An [`AbstractModelAttribute`](@ref) for the [`ResultStatusCode`](@ref) of the dual result -`result_index`. +An [`AbstractModelAttribute`](@ref) for the [`ResultStatusCode`](@ref) of the +dual result `result_index`. -If `result_index` is omitted, it defaults to 1. +## `result_index` See [`ResultCount`](@ref) for information on how the results are ordered. If `result_index` is larger than the value of [`ResultCount`](@ref) then [`NO_SOLUTION`](@ref) is returned. + +## Implementation + +Optimizers should implement the following methods: +``` +MOI.get(::Optimizer, ::MOI.PrimalStatus)::MOI.ResultStatusCode +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct DualStatus <: AbstractModelAttribute result_index::Int @@ -2764,7 +2991,8 @@ the optimization. If an attribute can be set by the user, define [`is_copyable`](@ref) instead. -An attribute cannot be both [`is_copyable`](@ref) and `is_set_by_optimize`. +An attribute cannot be both [`is_copyable`](@ref) and +[`is_set_by_optimize`](@ref). ## Default fallback @@ -2773,8 +3001,20 @@ attributes that are set by [`optimize!`](@ref). ## Undefined behavior -Querying the value of the attribute that `is_set_by_optimize` before a call to -[`optimize!`](@ref) is undefined and depends on solver-specific behavior. +Querying the value of the attribute that [`is_set_by_optimize`](@ref) before a +call to [`optimize!`](@ref) is undefined and depends on solver-specific behavior. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> MOI.is_set_by_optimize(MOI.ObjectiveValue()) +true + +julia> MOI.is_set_by_optimize(MOI.VariableName()) +false +``` """ is_set_by_optimize(::AnyAttribute) = false From 458887b191aad3e831d6596cc0775b8a54e26c1c Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 27 Mar 2025 15:14:27 +1300 Subject: [PATCH 4/4] Update --- src/attributes.jl | 360 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 289 insertions(+), 71 deletions(-) diff --git a/src/attributes.jl b/src/attributes.jl index 12f78f2136..e7305ae3a3 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -1436,7 +1436,7 @@ model. Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ListOfConstraintIndices{F,S}, )::Vector{MOI.ConstraintIndex{F,S}} where {F<:MOI.AbstractFunction,MOI.AbstractSet} @@ -1469,7 +1469,7 @@ the type `F`-in-`S` present in the model. Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.NumberOfConstraints{F,S}, )::Int64 where {F<:MOI.AbstractFunction,MOI.AbstractSet} @@ -1502,7 +1502,7 @@ greater than zero. Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ListOfConstraintTypesPresent, )::Vector{Tuple{Type,Type}} @@ -1521,11 +1521,11 @@ An [`AbstractModelAttribute`](@ref) for the objective function which has a type Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ObjectiveFunction{F}, )::F where {F<:MOI.AbstractFunction} -function MOI.set( +MOI.set( ::Optimizer, ::MOI.ObjectiveFunction{F}, ::F, @@ -1551,7 +1551,7 @@ set using the [`ObjectiveFunction{F}`](@ref) attribute. Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ObjectiveFunctionType, )::Type{<:MOI.AbstractFunction} @@ -1606,7 +1606,7 @@ the [`ResultCount`](@ref) attribute), getting this attribute must throw a Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ObjectiveValue, )::Union{T,Vector{T}} where {T<:Real} @@ -1646,7 +1646,7 @@ the [`ResultCount`](@ref) attribute), getting this attribute must throw a Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.DualObjectiveValue, )::Union{T,Vector{T}} where {T<:Real} @@ -1693,11 +1693,19 @@ attribute_value_type(::SolveTimeSec) = Float64 """ SimplexIterations() -An [`AbstractModelAttribute`](@ref) for the cumulative number of simplex iterations during the -optimization process. +An [`AbstractModelAttribute`](@ref) for the cumulative number of simplex +iterations while solving a problem. For a mixed-integer program (MIP), the return value is the total simplex iterations for all nodes. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.SimplexIterations)::Int64 +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct SimplexIterations <: AbstractModelAttribute end @@ -1706,8 +1714,19 @@ attribute_value_type(::SimplexIterations) = Int64 """ BarrierIterations() -An [`AbstractModelAttribute`](@ref) for the cumulative number of barrier iterations while solving -a problem. +An [`AbstractModelAttribute`](@ref) for the cumulative number of barrier +iterations while solving a problem. + +For a mixed-integer program (MIP), the return value is the total barrier +iterations for all nodes. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.BarrierIterations)::Int64 +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct BarrierIterations <: AbstractModelAttribute end @@ -1716,8 +1735,16 @@ attribute_value_type(::BarrierIterations) = Int64 """ NodeCount() -An [`AbstractModelAttribute`](@ref) for the total number of branch-and-bound nodes explored while -solving a mixed-integer program (MIP). +An [`AbstractModelAttribute`](@ref) for the total number of branch-and-bound +nodes explored while solving a mixed-integer program (MIP). + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.NodeCount)::Int64 +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct NodeCount <: AbstractModelAttribute end @@ -1726,8 +1753,16 @@ attribute_value_type(::NodeCount) = Int64 """ RawSolver() -An [`AbstractModelAttribute`](@ref) for the object that may be used to access a solver-specific -API for this optimizer. +An [`AbstractModelAttribute`](@ref) for the object that may be used to access a +solver-specific API for this optimizer. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.RawSolver)::Any +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct RawSolver <: AbstractModelAttribute end @@ -1742,7 +1777,7 @@ A number of attributes contain an index, `result_index`, which is used to refer to one of the available results. Thus, `result_index` must be an integer between `1` and the number of available results. -As a general rule, the first result (`result_index=1`) is the most important +As a general rule, the first result (`result_index = 1`) is the most important result (for example, an optimal solution or an infeasibility certificate). Other results will typically be alternate solutions that the solver found during the search for the first result. @@ -1758,6 +1793,14 @@ If a primal or dual infeasibility certificate is available, that is, and the corresponding [`PrimalStatus`](@ref) or [`DualStatus`](@ref) is [`INFEASIBILITY_CERTIFICATE`](@ref), then the first result must be a certificate. Other results may be alternate certificates, or infeasible points. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.ResultCount)::Int +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ResultCount <: AbstractModelAttribute end @@ -1788,8 +1831,16 @@ attribute_value_type(::ResultCount) = Int """ ConflictStatus() -An [`AbstractModelAttribute`](@ref) for the [`ConflictStatusCode`](@ref) explaining why the -conflict refiner stopped when computing the conflict. +An [`AbstractModelAttribute`](@ref) for the [`ConflictStatusCode`](@ref) +explaining why [`compute_conflict!`] stopped when computing the conflict. + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.ConflictStatus)::MOI.ConflictStatusCode +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ConflictStatus <: AbstractModelAttribute end @@ -1798,17 +1849,30 @@ attribute_value_type(::ConflictStatus) = ConflictStatusCode """ ListOfVariableAttributesSet() -An [`AbstractModelAttribute`](@ref) for the `Vector{AbstractVariableAttribute}` of all variable -attributes `attr` such that 1) `is_copyable(attr)` returns `true` and 2) the -attribute was set to variables. +An [`AbstractModelAttribute`](@ref) for the `Vector{AbstractVariableAttribute}` +of all variable attributes `attr` such that: + + 1. `is_copyable(attr)` returns `true` + 2. the attribute was set for at least one variable in the model + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get( + ::Optimizer, + ::MOI.ListOfVariableAttributesSet, +)::Vector{MOI.AbstractVariableAttribute} +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ListOfVariableAttributesSet <: AbstractModelAttribute end """ ListOfVariablesWithAttributeSet(attr::AbstractVariableAttribute) -An [`AbstractModelAttribute`](@ref) for the `Vector{VariableIndex}` of all variables with the -attribute `attr` set. +An [`AbstractModelAttribute`](@ref) for the `Vector{MOI.VariableIndex}` of all +variables with the attribute `attr` set. The returned list may not be minimal, so some elements may have their default value set. @@ -1817,6 +1881,17 @@ value set. This is an optional attribute to implement. The default fallback is to get [`ListOfVariableIndices`](@ref). + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get( + ::Optimizer, + ::MOI.ListOfVariablesWithAttributeSet{<:MOI.AbstractVariableAttribute}, +)::Vector{MOI.VarialbeIndex} +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct ListOfVariablesWithAttributeSet{A} <: AbstractModelAttribute attr::A @@ -1832,10 +1907,44 @@ end """ VariableName() -A variable attribute for a string identifying the variable. It is *valid* for -two variables to have the same name; however, variables with duplicate names -cannot be looked up using [`get`](@ref). It has a default value of `""` if not -set`. +An [`AbstractVariableAttribute`](@ref) for a `String` identifying the variable. + +The default name is `""` if not set by the user. + +## Duplicate names + +Two variables may have the same name; however, variables with duplicate names +cannot be looked up using [`get`](@ref). + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variable(model); + +julia> MOI.supports(model, MOI.VariableName(), MOI.VariableIndex) +true + +julia> MOI.get(model, MOI.VariableName(), x) +"" + +julia> MOI.set(model, MOI.VariableName(), x, "x") + +julia> MOI.get(model, MOI.VariableName(), x) +"x" +``` + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.VariableName, ::MOI.VariableIndex)::String +MOI.set(::Optimizer, ::MOI.VariableName, ::MOI.VariableIndex, ::String)::Nothing +MOI.supports(::Optimizer, ::MOI.VariableName, ::Type{MOI.VariableIndex})::Bool +``` """ struct VariableName <: AbstractVariableAttribute end @@ -1844,27 +1953,73 @@ attribute_value_type(::VariableName) = String """ VariablePrimalStart() -A variable attribute for the initial assignment to some primal variable's value -that the optimizer may use to warm-start the solve. +An [`AbstractVariableAttribute`](@ref) for the initial assignment to the +variable's primal value that the optimizer may use to warm-start the solve. + May be a number or `nothing` (unset). + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()); + +julia> x = MOI.add_variable(model); + +julia> MOI.supports(model, MOI.VariablePrimalStart(), MOI.VariableIndex) +true + +julia> MOI.get(model, MOI.VariablePrimalStart(), x) + +julia> MOI.set(model, MOI.VariablePrimalStart(), x, 1.0) + +julia> MOI.get(model, MOI.VariablePrimalStart(), x) +1.0 +``` + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.VariablePrimalStart, ::MOI.VariableIndex)::Union{Nothing,T} +MOI.set(::Optimizer, ::MOI.VariablePrimalStart, ::MOI.VariableIndex, ::Union{Nothing,T})::Nothing +MOI.supports(::Optimizer, ::MOI.VariablePrimalStart, ::Type{MOI.VariableIndex})::Bool +``` """ struct VariablePrimalStart <: AbstractVariableAttribute end """ VariablePrimal(result_index::Int = 1) -A variable attribute for the assignment to some primal variable's value in -result `result_index`. If `result_index` is omitted, it is 1 by default. +An [`AbstractVariableAttribute`](@ref) for the variable's primal value in result +`result_index`. + +## PrimalStatus + +Before quering this attribute you should first check [`PrimalStatus`](@ref) to +confirm that a primal solution is avaiable. + +If the [`PrimalStatus`](@ref) is [`NO_SOLUTION`](@ref) the result of querying +this attribute is undefined. + +## `result_index` + +The optimizer may return multiple primal solutions. See [`ResultCount`](@ref) +for information on how the results are ordered. If the solver does not have a primal value for the variable because the `result_index` is beyond the available solutions (whose number is indicated by the [`ResultCount`](@ref) attribute), getting this attribute must throw a -[`ResultIndexBoundsError`](@ref). Otherwise, if the result is unavailable for -another reason (for instance, only a dual solution is available), the result is -undefined. Users should first check [`PrimalStatus`](@ref) before accessing the -`VariablePrimal` attribute. +[`ResultIndexBoundsError`](@ref). -See [`ResultCount`](@ref) for information on how the results are ordered. +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.VariablePrimal, ::MOI.VariableIndex)::T +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct VariablePrimal <: AbstractVariableAttribute result_index::Int @@ -1874,8 +2029,8 @@ end """ CallbackVariablePrimal(callback_data) -A variable attribute for the assignment to some primal variable's value during -the callback identified by `callback_data`. +An [`AbstractVariableAttribute`](@ref) for the assignment to the variable's +primal value during the callback identified by `callback_data`. """ struct CallbackVariablePrimal{CallbackDataType} <: AbstractVariableAttribute callback_data::CallbackDataType @@ -1928,18 +2083,34 @@ is_set_by_optimize(::CallbackVariablePrimal) = true """ VariableBasisStatus(result_index::Int = 1) -A variable attribute for the [`BasisStatusCode`](@ref) of a variable in result -`result_index`, with respect to an available optimal solution basis. +An [`AbstractVariableAttribute`](@ref) for the [`BasisStatusCode`](@ref) of the +variable in result `result_index`, with respect to a basic solution. + +## PrimalStatus + +Before quering this attribute you should first check [`PrimalStatus`](@ref) to +confirm that a primal solution is avaiable. + +If the [`PrimalStatus`](@ref) is [`NO_SOLUTION`](@ref) the result of querying +this attribute is undefined. -If the solver does not have a basis status for the variable because the +## `result_index` + +The optimizer may return multiple primal solutions. See [`ResultCount`](@ref) +for information on how the results are ordered. + +If the solver does not have a primal value for the variable because the `result_index` is beyond the available solutions (whose number is indicated by the [`ResultCount`](@ref) attribute), getting this attribute must throw a -[`ResultIndexBoundsError`](@ref). Otherwise, if the result is unavailable for -another reason (for instance, only a dual solution is available), the result is -undefined. Users should first check [`PrimalStatus`](@ref) before accessing the -`VariableBasisStatus` attribute. +[`ResultIndexBoundsError`](@ref). -See [`ResultCount`](@ref) for information on how the results are ordered. +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.VariableBasisStatus, ::MOI.VariableIndex)::MOI.BasisStatusCode +``` +They should not implement [`set`](@ref) or [`supports`](@ref). """ struct VariableBasisStatus <: AbstractVariableAttribute result_index::Int @@ -1997,17 +2168,62 @@ end """ ConstraintName() -A constraint attribute for a string identifying the constraint. +An [`AbstractConstraintAttribute`](@ref) for a `String` identifying the +constraint. -It is *valid* for constraints variables to have the same name; however, -constraints with duplicate names cannot be looked up using [`get`](@ref), -regardless of whether they have the same `F`-in-`S` type. +The default name is `""` if not set by the user. -`ConstraintName` has a default value of `""` if not set. +## Duplicate names -## Notes +Two constraints may have the same name; however, constraints with duplicate +names cannot be looked up using [`get`](@ref), regardless of whether they have +the same `F`-in-`S` type. + +## VariableIndex connstraints + +You should _not_ implement [`ConstraintName`](@ref) for [`VariableIndex`](@ref) +constraints. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI -You should _not_ implement `ConstraintName` for `VariableIndex` constraints. +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variable(model); + +julia> c = MOI.add_constraint(model, 1.0 * x, MOI.EqualTo(1.0)); + +julia> MOI.supports(model, MOI.ConstraintName(), typeof(c)) +true + +julia> MOI.get(model, MOI.ConstraintName(), c) +"" + +julia> MOI.set(model, MOI.ConstraintName(), c, "c") + +julia> MOI.get(model, MOI.ConstraintName(), c) +"c" + +julia> MOI.get(model, MOI.ConstraintIndex, "c") +MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.EqualTo{Float64}}(1) + +julia> F, S = MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}; + +julia> MOI.get(model, MOI.ConstraintIndex{F,S}, "c") +MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.EqualTo{Float64}}(1) +``` + +## Implementation + +Optimizers should implement the following methods: +```julia +MOI.get(::Optimizer, ::MOI.ConstraintName, ::MOI.ConstraintIndex)::String +MOI.set(::Optimizer, ::MOI.ConstraintName, ::MOI.ConstraintIndex, ::String)::Nothing +MOI.supports(::Optimizer, ::MOI.ConstraintName, ::Type{<:MOI.ConstraintIndex})::Bool +MOI.get(::Optimizer, ::MOI.ConstraintIndex, ::MOI.ConstraintIndex, ::String)::MOI.ConstraintIndex +``` """ struct ConstraintName <: AbstractConstraintAttribute end @@ -2037,8 +2253,9 @@ end """ ConstraintPrimalStart() -A constraint attribute for the initial assignment to some constraint's -[`ConstraintPrimal`](@ref) that the optimizer may use to warm-start the solve. +An [`AbstractConstraintAttribute`](@ref) for the initial assignment to the +constraint's [`ConstraintPrimal`](@ref) that the optimizer may use to warm-start +the solve. May be `nothing` (unset), a number for [`AbstractScalarFunction`](@ref), or a vector for [`AbstractVectorFunction`](@ref). @@ -2048,8 +2265,9 @@ struct ConstraintPrimalStart <: AbstractConstraintAttribute end """ ConstraintDualStart() -A constraint attribute for the initial assignment to some constraint's -[`ConstraintDual`](@ref) that the optimizer may use to warm-start the solve. +An [`AbstractConstraintAttribute`](@ref) for the initial assignment to the +constraint's [`ConstraintDual`](@ref) that the optimizer may use to warm-start +the solve. May be `nothing` (unset), a number for [`AbstractScalarFunction`](@ref), or a vector for [`AbstractVectorFunction`](@ref). @@ -2059,7 +2277,7 @@ struct ConstraintDualStart <: AbstractConstraintAttribute end """ ConstraintPrimal(result_index::Int = 1) -A constraint attribute for the assignment to some constraint's primal value in +An [`AbstractConstraintAttribute`](@ref) for the constraint's primal value in result `result_index`. ## Definition @@ -2095,12 +2313,12 @@ the [`ResultCount`](@ref) attribute), getting this attribute must throw a Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ConstraintPrimal, ::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction} )::T -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ConstraintPrimal, ::MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} @@ -2116,7 +2334,7 @@ end """ ConstraintDual(result_index::Int = 1) -A constraint attribute for the assignment to some constraint's dual value in +An [`AbstractConstraintAttribute`](@ref) for the constraint's dual value in result `result_index`. ## DualStatus @@ -2141,12 +2359,12 @@ the [`ResultCount`](@ref) attribute), getting this attribute must throw a Optimizers should implement the following methods: ```julia -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ConstraintDual, ::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction} )::T -function MOI.get( +MOI.get( ::Optimizer, ::MOI.ConstraintDual, ::MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} @@ -2162,8 +2380,8 @@ end """ ConstraintBasisStatus(result_index::Int = 1) -A constraint attribute for the [`BasisStatusCode`](@ref) of some constraint in -result `result_index`, with respect to an available optimal solution basis. +An [`AbstractConstraintAttribute`](@ref) for the [`BasisStatusCode`](@ref) of +the constraint in result `result_index`, with respect to a basic solution. If `result_index` is omitted, it is 1 by default. @@ -2206,7 +2424,7 @@ end """ CanonicalConstraintFunction() -A constraint attribute for a canonical representation of the +An [`AbstractConstraintAttribute`](@ref) for a canonical representation of the [`AbstractFunction`](@ref) object used to define the constraint. Getting this attribute is guaranteed to return a function that is equivalent but @@ -2280,8 +2498,8 @@ end """ ConstraintFunction() -A constraint attribute for the [`AbstractFunction`](@ref) object used to define -the constraint. +An [`AbstractConstraintAttribute`](@ref) for the [`AbstractFunction`](@ref) +object used to define the constraint. It is guaranteed to be equivalent but not necessarily identical to the function provided by the user. @@ -2366,8 +2584,8 @@ end """ ConstraintSet() -A constraint attribute for the [`AbstractSet`](@ref) object used to define the -constraint. +An [`AbstractConstraintAttribute`](@ref) for the [`AbstractSet`](@ref) object +used to define the constraint. ## Example