From eb45452846aaf15e0d8b56dd314bcaf243b254f8 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 4 Nov 2024 16:15:43 +1300 Subject: [PATCH 1/3] Improve various docstrings by converting to jldoctest --- src/Benchmarks/Benchmarks.jl | 49 +++++--- src/Bridges/Bridges.jl | 2 +- src/Bridges/Variable/bridge.jl | 6 +- src/Nonlinear/evaluator.jl | 54 ++++++--- src/Nonlinear/model.jl | 124 ++++++++++++++----- src/Test/Test.jl | 29 +++-- src/Utilities/functions.jl | 41 +++++-- src/Utilities/model.jl | 4 +- src/Utilities/penalty_relaxation.jl | 4 +- src/Utilities/product_of_sets.jl | 36 +++--- src/Utilities/set_dot.jl | 2 +- src/Utilities/sets.jl | 6 +- src/attributes.jl | 29 +++-- src/constraints.jl | 48 ++++++-- src/indextypes.jl | 13 +- src/modifications.jl | 178 +++++++++++++++++++--------- src/variables.jl | 23 +++- 17 files changed, 454 insertions(+), 194 deletions(-) diff --git a/src/Benchmarks/Benchmarks.jl b/src/Benchmarks/Benchmarks.jl index 2acf35f86b..a711b8f262 100644 --- a/src/Benchmarks/Benchmarks.jl +++ b/src/Benchmarks/Benchmarks.jl @@ -22,15 +22,16 @@ arguments, and returns a new instance of the optimizer you wish to benchmark. Use `exclude` to exclude a subset of benchmarks. -### Examples +## Example ```julia -suite() do - GLPK.Optimizer() -end -suite(exclude = [r"delete"]) do - Gurobi.Optimizer(OutputFlag=0) -end +julia> MOI.Benchmarks.suite() do + return GLPK.Optimizer() + end + +julia> MOI.Benchmarks.suite(; exclude = [r"delete"]) do + return Gurobi.Optimizer() + end ``` """ function suite(new_model::Function; exclude::Vector{Regex} = Regex[]) @@ -49,11 +50,21 @@ Run all benchmarks in `suite` and save to files called `name` in `directory`. Extra `kwargs` are based to `BenchmarkTools.run`. -### Examples +## Example ```julia -my_suite = suite(() -> GLPK.Optimizer()) -create_baseline(my_suite, "glpk_master"; directory = "/tmp", verbose = true) +julia> import MathOptInterface as MOI + +julia> import GLPK + +julia> my_suite = MOI.Benchmarks.suite(() -> GLPK.Optimizer()); + +julia> MOI.Benchmarks.create_baseline( + my_suite, + "glpk_master"; + directory = "/tmp", + verbose = true, + ) ``` """ function create_baseline( @@ -86,13 +97,21 @@ A report summarizing the comparison is written to `report_filename` in Extra `kwargs` are based to `BenchmarkTools.run`. -### Examples +## Example ```julia -my_suite = suite(() -> GLPK.Optimizer()) -compare_against_baseline( - my_suite, "glpk_master"; directory = "/tmp", verbose = true -) +julia> import MathOptInterface as MOI + +julia> import GLPK + +julia> my_suite = MOI.Benchmarks.suite(() -> GLPK.Optimizer()); + +julia> MOI.Benchmarks.compare_against_baseline( + my_suite, + "glpk_master"; + directory = "/tmp", + verbose = true, + ) ``` """ function compare_against_baseline( diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index 32b4da2948..740dfee5bf 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -90,7 +90,7 @@ are not added. Therefore, you are recommended to use `model = MOI.instantiate(Package.Optimizer)`. See [`MOI.instantiate`](@ref). -## Examples +## Example ### An optimizer using a non-default bridge in `MOI.Bridges` diff --git a/src/Bridges/Variable/bridge.jl b/src/Bridges/Variable/bridge.jl index d7d06effad..30990e536c 100644 --- a/src/Bridges/Variable/bridge.jl +++ b/src/Bridges/Variable/bridge.jl @@ -71,13 +71,15 @@ Return the concrete type of the bridge supporting variables in `S` constraints. This function can only be called if `MOI.supports_constrained_variable(BT, S)` is `true`. -## Examples +## Example As a variable in [`MOI.GreaterThan`](@ref) is bridged into variables in [`MOI.Nonnegatives`](@ref) by the [`VectorizeBridge`](@ref): -```jldoctest; setup=:(import MathOptInterface as MOI) +```jldoctest +julia> import MathOptInterface as MOI + julia> MOI.Bridges.Variable.concrete_bridge_type( MOI.Bridges.Variable.VectorizeBridge{Float64}, MOI.GreaterThan{Float64}, diff --git a/src/Nonlinear/evaluator.jl b/src/Nonlinear/evaluator.jl index b9d8e5d844..17416208ce 100644 --- a/src/Nonlinear/evaluator.jl +++ b/src/Nonlinear/evaluator.jl @@ -13,20 +13,46 @@ end Return the 1-indexed value of the constraint index `c` in `evaluator`. -## Examples - -```julia -model = Model() -x = MOI.VariableIndex(1) -c1 = add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) -c2 = add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) -evaluator = Evaluator(model) -MOI.initialize(evaluator, Symbol[]) -ordinal_index(evaluator, c2) # Returns 2 -delete(model, c1) -evaluator = Evaluator(model) -MOI.initialize(evaluator, Symbol[]) -ordinal_index(model, c2) # Returns 1 +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Nonlinear.Model() +A Nonlinear.Model with: + 0 objectives + 0 parameters + 0 expressions + 0 constraints + +julia> x = MOI.VariableIndex(1) +MOI.VariableIndex(1) + +julia> c1 = MOI.Nonlinear.add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) +MathOptInterface.Nonlinear.ConstraintIndex(1) + +julia> c2 = MOI.Nonlinear.add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) +MathOptInterface.Nonlinear.ConstraintIndex(2) + +julia> evaluator = MOI.Nonlinear.Evaluator(model) +Nonlinear.Evaluator with available features: + * :ExprGraph + +julia> MOI.initialize(evaluator, Symbol[]) + +julia> MOI.Nonlinear.ordinal_index(evaluator, c2) # Returns 2 +2 + +julia> MOI.Nonlinear.delete(model, c1) + +julia> evaluator = MOI.Nonlinear.Evaluator(model) +Nonlinear.Evaluator with available features: + * :ExprGraph + +julia> MOI.initialize(evaluator, Symbol[]) + +julia> MOI.Nonlinear.ordinal_index(evaluator, c2) # Returns 1 +1 ``` """ function ordinal_index(evaluator::Evaluator, c::ConstraintIndex) diff --git a/src/Nonlinear/model.jl b/src/Nonlinear/model.jl index feb33c7e27..7e222706bb 100644 --- a/src/Nonlinear/model.jl +++ b/src/Nonlinear/model.jl @@ -49,14 +49,26 @@ function of `model`. To remove the objective, pass `nothing`. -## Examples - -```julia -model = Model() -x = MOI.VariableIndex(1) -set_objective(model, :(\$x^2 + 1)) -set_objective(model, x) -set_objective(model, nothing) +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Nonlinear.Model() +A Nonlinear.Model with: + 0 objectives + 0 parameters + 0 expressions + 0 constraints + +julia> x = MOI.VariableIndex(1) +MOI.VariableIndex(1) + +julia> MOI.Nonlinear.set_objective(model, :(\$x^2 + 1)) + +julia> MOI.Nonlinear.set_objective(model, x) + +julia> MOI.Nonlinear.set_objective(model, nothing) ``` """ function set_objective(model::Model, obj) @@ -77,13 +89,19 @@ Parse `expr` into a [`Expression`](@ref) and add to `model`. Returns an `expr` must be a type that is supported by [`parse_expression`](@ref). -## Examples +## Example -```julia -model = Model() -x = MOI.VariableIndex(1) -ex = add_expression(model, :(\$x^2 + 1)) -set_objective(model, :(sqrt(\$ex))) +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Nonlinear.Model(); + +julia> x = MOI.VariableIndex(1); + +julia> ex = MOI.Nonlinear.add_expression(model, :(\$x^2 + 1)) +MathOptInterface.Nonlinear.ExpressionIndex(1) + +julia> MOI.Nonlinear.set_objective(model, :(sqrt(\$ex))) ``` """ function add_expression(model::Model, expr) @@ -111,12 +129,17 @@ Parse `func` and `set` into a [`Constraint`](@ref) and add to `model`. Returns a [`ConstraintIndex`](@ref) that can be used to delete the constraint or query solution information. -## Examples +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Nonlinear.Model(); -```julia -model = Model() -x = MOI.VariableIndex(1) -c = add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) +julia> x = MOI.VariableIndex(1); + +julia> c = MOI.Nonlinear.add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) +MathOptInterface.Nonlinear.ConstraintIndex(1) ``` """ function add_constraint( @@ -141,13 +164,39 @@ end Delete the constraint index `c` from `model`. -## Examples +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Nonlinear.Model() +A Nonlinear.Model with: + 0 objectives + 0 parameters + 0 expressions + 0 constraints + +julia> x = MOI.VariableIndex(1) +MOI.VariableIndex(1) + +julia> c = MOI.Nonlinear.add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) +MathOptInterface.Nonlinear.ConstraintIndex(1) + +julia> model +A Nonlinear.Model with: + 0 objectives + 0 parameters + 0 expressions + 1 constraint -```julia -model = Model() -x = MOI.VariableIndex(1) -c = add_constraint(model, :(\$x^2), MOI.LessThan(1.0)) -delete(model, c) +julia> MOI.Nonlinear.delete(model, c) + +julia> model +A Nonlinear.Model with: + 0 objectives + 0 parameters + 0 expressions + 0 constraints ``` """ function delete(model::Model, c::ConstraintIndex) @@ -170,13 +219,26 @@ Add a new parameter to `model` with the default value `value`. Returns a [`ParameterIndex`](@ref) that can be interpolated into other input expressions and used to modify the value of the parameter. -## Examples +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Nonlinear.Model() +A Nonlinear.Model with: + 0 objectives + 0 parameters + 0 expressions + 0 constraints + +julia> x = MOI.VariableIndex(1) +MOI.VariableIndex(1) + +julia> p = MOI.Nonlinear.add_parameter(model, 1.2) +MathOptInterface.Nonlinear.ParameterIndex(1) -```julia -model = Model() -x = MOI.VariableIndex(1) -p = add_parameter(model, 1.2) -c = add_constraint(model, :(\$x^2 - \$p), MOI.LessThan(0.0)) +julia> c = MOI.Nonlinear.add_constraint(model, :(\$x^2 - \$p), MOI.LessThan(0.0)) +MathOptInterface.Nonlinear.ConstraintIndex(1) ``` """ function add_parameter(model::Model, value::Float64) diff --git a/src/Test/Test.jl b/src/Test/Test.jl index 44ea0376ba..bc79a5c05f 100644 --- a/src/Test/Test.jl +++ b/src/Test/Test.jl @@ -77,21 +77,24 @@ Return an object that is used to configure various tests. - `MOI.VariableName` to skip setting variable names - `MOI.ConstraintName` to skip setting constraint names -## Examples +## Example For a nonlinear solver that finds local optima and does not support finding dual variables or constraint names: -```julia -Config( - Float64; - optimal_status = MOI.LOCALLY_SOLVED, - exclude = Any[ - MOI.ConstraintDual, - MOI.VariableName, - MOI.ConstraintName, - MOI.delete, - ], -) + +```jldoctest +julia> import MathOptInterface as MOI + +julia> config = MOI.Test.Config( + Float64; + optimal_status = MOI.LOCALLY_SOLVED, + exclude = Any[ + MOI.ConstraintDual, + MOI.VariableName, + MOI.ConstraintName, + MOI.delete, + ], + ); ``` """ function Config( @@ -306,7 +309,7 @@ Check that the condition `x` is `true`. Otherwise, throw an [`RequirementUnmet`] error to indicate that the model does not support something required by the test function. -## Examples +## Example ```julia @requires MOI.supports(model, MOI.Silent()) diff --git a/src/Utilities/functions.jl b/src/Utilities/functions.jl index be8cef5acf..85cda9e0df 100644 --- a/src/Utilities/functions.jl +++ b/src/Utilities/functions.jl @@ -1025,11 +1025,22 @@ Returns the function in a canonical form, that is, The output of `canonical` can be assumed to be a copy of `f`, even for `VectorOfVariables`. -## Examples +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> x, y, z = MOI.VariableIndex.(1:3); -If `x` (resp. `y`, `z`) is `VariableIndex(1)` (resp. 2, 3). The canonical -representation of `ScalarAffineFunction([y, x, z, x, z], [2, 1, 3, -2, -3], 5)` -is `ScalarAffineFunction([x, y], [-1, 2], 5)`. +julia> f = MOI.ScalarAffineFunction( + MOI.ScalarAffineTerm.(Float64[2, 1, 3, -2, -3], [y, x, z, x, z]), + 5.0, + ) +5.0 + 2.0 MOI.VariableIndex(2) + 1.0 MOI.VariableIndex(1) + 3.0 MOI.VariableIndex(3) - 2.0 MOI.VariableIndex(1) - 3.0 MOI.VariableIndex(3) + +julia> MOI.Utilities.canonical(f) +5.0 - 1.0 MOI.VariableIndex(1) + 2.0 MOI.VariableIndex(2) +``` """ function canonical(f::MOI.AbstractFunction) g = copy(f) @@ -1144,12 +1155,24 @@ Return a `Bool` indicating whether the function `f` is approximately zero using This function assumes that `f` does not contain any duplicate terms, you might want to first call [`canonical`](@ref) if that is not guaranteed. -For instance, given -```julia -f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1, -1], [x, x]), 0) + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> x = MOI.VariableIndex(1) +MOI.VariableIndex(1) + +julia> f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1, -1], [x, x]), 0) +(0) + (1) MOI.VariableIndex(1) - (1) MOI.VariableIndex(1) + +julia> MOI.Utilities.isapprox_zero(f, 1e-8) +false + +julia> MOI.Utilities.isapprox_zero(MOI.Utilities.canonical(f), 1e-8) +true ``` -then `isapprox_zero(f)` is `false` but `isapprox_zero(MOIU.canonical(f))` is -`true`. """ function isapprox_zero end diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index 6586b95e42..0cf349def1 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -674,7 +674,7 @@ which is a subtype of [`MOI.AbstractOptimizer`](@ref), otherwise, it is a * The sets supported with [`MOI.VariableIndex`](@ref) cannot be controlled from the macro; use [`UniversalFallback`](@ref) to support more sets. -## Examples +## Example The model describing a linear program would be: ```julia @@ -840,7 +840,7 @@ const LessThanIndicatorZero{T} = An implementation of `ModelLike` that supports all functions and sets defined in MOI. It is parameterized by the coefficient type. -## Examples +## Example ```jldoctest julia> import MathOptInterface as MOI diff --git a/src/Utilities/penalty_relaxation.jl b/src/Utilities/penalty_relaxation.jl index e0bc093f59..dd51c88c63 100644 --- a/src/Utilities/penalty_relaxation.jl +++ b/src/Utilities/penalty_relaxation.jl @@ -32,7 +32,7 @@ When `S` is [`MOI.LessThan`](@ref) or [`MOI.GreaterThan`](@ref), we omit `y` or [`MOI.ScalarAffineFunction`](@ref). In an optimal solution, query the value of this function to compute the violation of the constraint. -## Examples +## Example ```jldoctest; setup=:(import MathOptInterface as MOI) julia> model = MOI.Utilities.Model{Float64}(); @@ -187,7 +187,7 @@ cannot be modified in-place. To modify variable bounds, rewrite them as linear constraints. -## Examples +## Example ```jldoctest; setup=:(import MathOptInterface as MOI) julia> model = MOI.Utilities.Model{Float64}(); diff --git a/src/Utilities/product_of_sets.jl b/src/Utilities/product_of_sets.jl index a1c1bc932e..48de49cc7e 100644 --- a/src/Utilities/product_of_sets.jl +++ b/src/Utilities/product_of_sets.jl @@ -55,14 +55,16 @@ Generate a new [`MixOfScalarSets`](@ref) subtype. ## Example -```julia -@mix_of_scalar_sets( - MixedIntegerLinearProgramSets, - MOI.GreaterThan{T}, - MOI.LessThan{T}, - MOI.EqualTo{T}, - MOI.Integer, -) +```jldoctest +julia> import MathOptInterface as MOI + +julia> MOI.Utilities.@mix_of_scalar_sets( + MixedIntegerLinearProgramSets, + MOI.GreaterThan{T}, + MOI.LessThan{T}, + MOI.EqualTo{T}, + MOI.Integer, + ) ``` """ macro mix_of_scalar_sets(name, set_types...) @@ -153,14 +155,16 @@ Generate a new [`OrderedProductOfSets`](@ref) subtype. ## Example -```julia -@product_of_sets( - LinearOrthants, - MOI.Zeros, - MOI.Nonnegatives, - MOI.Nonpositives, - MOI.ZeroOne, -) +```jldoctest +julia> import MathOptInterface as MOI + +julia> MOI.Utilities.@product_of_sets( + LinearOrthants, + MOI.Zeros, + MOI.Nonnegatives, + MOI.Nonpositives, + MOI.ZeroOne, + ) ``` """ macro product_of_sets(name, set_types...) diff --git a/src/Utilities/set_dot.jl b/src/Utilities/set_dot.jl index d0f3718083..31378da1fe 100644 --- a/src/Utilities/set_dot.jl +++ b/src/Utilities/set_dot.jl @@ -245,7 +245,7 @@ Vector `s` of scaling for the entries of the vectorized form of a vector `x` in `set` and `y` in `MOI.dual_set(set)` such that `MOI.Utilities.set_dot(x, y) = LinearAlgebra.dot(s .* x, s .* y)`. -## Examples +## Example Combined with `LinearAlgebra`, this vector can be used to scale a [`MOI.AbstractVectorFunction`](@ref). diff --git a/src/Utilities/sets.jl b/src/Utilities/sets.jl index 562d3f36c8..5e5907a2a3 100644 --- a/src/Utilities/sets.jl +++ b/src/Utilities/sets.jl @@ -21,12 +21,12 @@ else end ``` -### Note for developers +## Note for developers Only define this function if it makes sense and you have implemented [`supports_shift_constant`](@ref) to return `true`. -## Examples +## Example ```jldoctest julia> import MathOptInterface as MOI @@ -50,7 +50,7 @@ Return `true` if [`shift_constant`](@ref) is defined for set `S`. See also [`shift_constant`](@ref). -## Examples +## Example ```jldoctest julia> import MathOptInterface as MOI diff --git a/src/attributes.jl b/src/attributes.jl index c6c3d4c930..e0f079e327 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -653,7 +653,7 @@ This can be submitted only from the [`LazyConstraintCallback`](@ref). The field `callback_data` is a solver-specific callback type that is passed as the argument to the feasible solution callback. -## Examples +## Example Suppose `x` and `y` are [`VariableIndex`](@ref)s of `optimizer`. To add a `LazyConstraint` for `2x + 3y <= 1`, write @@ -1056,7 +1056,7 @@ The current primal solution is accessed through attributes will throw [`OptimizeInProgress`](@ref) as discussed in [`AbstractCallback`](@ref). -## Examples +## Example ```julia x = MOI.add_variables(optimizer, 8) @@ -1085,7 +1085,7 @@ The current primal solution is accessed through [`CallbackVariablePrimal`](@ref) Trying to access other result attributes will throw [`OptimizeInProgress`](@ref) as discussed in [`AbstractCallback`](@ref). -## Examples +## Example ```julia x = MOI.add_variables(optimizer, 8) @@ -1114,7 +1114,7 @@ The infeasible solution is accessed through [`CallbackVariablePrimal`](@ref). Trying to access other result attributes will throw [`OptimizeInProgress`](@ref) as discussed in [`AbstractCallback`](@ref). -## Examples +## Example ```julia x = MOI.add_variables(optimizer, 8) @@ -1283,13 +1283,20 @@ attribute_value_type(::ObjectiveFunction{F}) where {F} = F A model attribute for the type `F` of the objective function set using the [`ObjectiveFunction{F}`](@ref) attribute. -## Examples +## Example -In the following code, `attr` should be equal to `MOI.VariableIndex`: -```julia -x = MOI.add_variable(model) -MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), x) -attr = MOI.get(model, MOI.ObjectiveFunctionType()) +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variable(model) +MOI.VariableIndex(1) + +julia> MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), x) + +julia> MOI.get(model, MOI.ObjectiveFunctionType()) +MathOptInterface.VariableIndex ``` """ struct ObjectiveFunctionType <: AbstractModelAttribute end @@ -2018,7 +2025,7 @@ have the form: respect to `x[i]` and then `x[j]`. `H` is initialized to the zero matrix, so you do not need to set any zero elements. -## Examples +## Example ```jldoctest julia> import MathOptInterface as MOI diff --git a/src/constraints.jl b/src/constraints.jl index 03550dca77..480ba10118 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -312,29 +312,55 @@ function Base.showerror(io::IO, err::UpperBoundAlreadySet{S1,S2}) where {S1,S2} end """ -## Transform Constraint Set + transform( + model::ModelLike, + c::ConstraintIndex{F,S1}, + newset::S2, + )::ConstraintIndex{F,S2} - transform(model::ModelLike, c::ConstraintIndex{F,S1}, newset::S2)::ConstraintIndex{F,S2} +Replace the set in constraint `c` with `newset`. -Replace the set in constraint `c` with `newset`. The constraint index `c` -will no longer be valid, and the function returns a new constraint index with -the correct type. +The constraint index `c` will no longer be valid, and the function returns a new +constraint index with the correct type. Solvers may only support a subset of constraint transforms that they perform efficiently (for example, changing from a `LessThan` to `GreaterThan` set). In addition, set modification (where `S1 = S2`) should be performed via the `modify` function. - Typically, the user should delete the constraint and add a new one. -### Examples +## 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.LessThan(2.0)); + +julia> print(model) +Feasibility + +Subject to: + +ScalarAffineFunction{Float64}-in-LessThan{Float64} + 0.0 + 1.0 v[1] <= 2.0 + +julia> c2 = MOI.transform(model, c, MOI.GreaterThan(0.0)) + +julia> print(model) +Feasibility + +Subject to: -If `c` is a `ConstraintIndex{ScalarAffineFunction{Float64},LessThan{Float64}}`, +ScalarAffineFunction{Float64}-in-GreaterThan{Float64} + 0.0 + 1.0 v[1] >= 0.0 -```julia -c2 = transform(model, c, GreaterThan(0.0)) -transform(model, c, LessThan(0.0)) # errors +julia> MOI.is_valid(model, c) +false ``` """ function transform end diff --git a/src/indextypes.jl b/src/indextypes.jl index 76bc70815d..ce795a705b 100644 --- a/src/indextypes.jl +++ b/src/indextypes.jl @@ -46,14 +46,19 @@ struct VariableIndex <: AbstractScalarFunction end """ - ConstraintIndex{F, S} + ConstraintIndex{F,S} A type-safe wrapper for `Int64` for use in referencing `F`-in-`S` constraints in a model. + The parameter `F` is the type of the function in the constraint, and the -parameter `S` is the type of set in the constraint. To allow for deletion, -indices need not be consecutive. Indices within a constraint type (that is, `F`-in-`S`) -must be unique, but non-unique indices across different constraint types are allowed. +parameter `S` is the type of set in the constraint. + +To allow for deletion, indices need not be consecutive. + +Indices within a constraint type (that is, `F`-in-`S`) must be unique, but +non-unique indices across different constraint types are allowed. + If `F` is [`VariableIndex`](@ref) then the index is equal to the index of the variable. That is for an `index::ConstraintIndex{VariableIndex}`, we always have diff --git a/src/modifications.jl b/src/modifications.jl index c402c898c2..e0dfd5ed4a 100644 --- a/src/modifications.jl +++ b/src/modifications.jl @@ -53,9 +53,11 @@ struct ModifyObjectiveNotAllowed{C<:AbstractFunctionModification} <: change::C message::String end + function ModifyObjectiveNotAllowed(change::AbstractFunctionModification) return ModifyObjectiveNotAllowed(change, "") end + function throw_modify_not_allowed(::ObjectiveFunction, args...) return throw(ModifyObjectiveNotAllowed(args...)) end @@ -65,39 +67,48 @@ function operation_name(err::ModifyObjectiveNotAllowed) end """ -## Constraint Function - - modify(model::ModelLike, ci::ConstraintIndex, change::AbstractFunctionModification) + modify( + model::ModelLike, + ci::ConstraintIndex, + change::AbstractFunctionModification, + ) Apply the modification specified by `change` to the function of constraint `ci`. An [`ModifyConstraintNotAllowed`](@ref) error is thrown if modifying constraints is not supported by the model `model`. -### Examples +## Example -```julia -modify(model, ci, ScalarConstantChange(10.0)) -``` +```jldoctest +julia> import MathOptInterface as MOI -## Objective Function +julia> model = MOI.Utilities.Model{Float64}(); - modify(model::ModelLike, ::ObjectiveFunction, change::AbstractFunctionModification) +julia> x = MOI.add_variable(model); -Apply the modification specified by `change` to the objective function of -`model`. To change the function completely, call `set` instead. +julia> ci = MOI.add_constraint(model, 1.0 * x, MOI.EqualTo(1.0)); -An [`ModifyObjectiveNotAllowed`](@ref) error is thrown if modifying -objectives is not supported by the model `model`. +julia> MOI.modify(model, ci, MOI.ScalarConstantChange(10.0)) -### Examples +julia> print(model) +Feasibility -```julia -modify(model, ObjectiveFunction{ScalarAffineFunction{Float64}}(), ScalarConstantChange(10.0)) -``` +Subject to: -## Multiple modifications in Constraint Functions +ScalarAffineFunction{Float64}-in-EqualTo{Float64} + 10.0 + 1.0 v[1] == 1.0 +``` +""" +function modify( + model::ModelLike, + ci::ConstraintIndex, + change::AbstractFunctionModification, +) + return throw_modify_not_allowed(ci, change) +end +""" modify( model::ModelLike, cis::AbstractVector{<:ConstraintIndex}, @@ -109,55 +120,29 @@ Apply multiple modifications specified by `changes` to the functions of constrai A [`ModifyConstraintNotAllowed`](@ref) error is thrown if modifying constraints is not supported by `model`. -### Examples +## Example -```julia -modify( - model, - [ci, ci], - [ - ScalarCoefficientChange{Float64}(VariableIndex(1), 1.0), - ScalarCoefficientChange{Float64}(VariableIndex(2), 0.5), - ], -) -``` +```jldoctest +julia> import MathOptInterface as MOI -## Multiple modifications in the Objective Function +julia> model = MOI.Utilities.Model{Float64}(); - modify( - model::ModelLike, - attr::ObjectiveFunction, - changes::AbstractVector{<:AbstractFunctionModification}, - ) +julia> x = MOI.add_variables(model, 2); -Apply multiple modifications specified by `changes` to the functions of constraints `cis`. +julia> ci = MOI.add_constraint.(model, 1.0 .* x, MOI.EqualTo(1.0)); -A [`ModifyObjectiveNotAllowed`](@ref) error is thrown if modifying -objective coefficients is not supported by `model`. +julia> MOI.modify(model, ci, MOI.ScalarCoefficientChange.(x, [2.0, 0.5])) -### Examples +julia> print(model) +Feasibility -```julia -modify( - model, - ObjectiveFunction{ScalarAffineFunction{Float64}}(), - [ - ScalarCoefficientChange{Float64}(VariableIndex(1), 1.0), - ScalarCoefficientChange{Float64}(VariableIndex(2), 0.5), - ], -) +Subject to: + +ScalarAffineFunction{Float64}-in-EqualTo{Float64} + 0.0 + 2.0 v[1] == 1.0 + 0.0 + 0.5 v[2] == 1.0 ``` """ -function modify end - -function modify( - model::ModelLike, - ci::ConstraintIndex, - change::AbstractFunctionModification, -) - return throw_modify_not_allowed(ci, change) -end - function modify( model::ModelLike, cis::AbstractVector{<:ConstraintIndex}, @@ -170,6 +155,42 @@ function modify( return end +""" + modify(model::ModelLike, ::ObjectiveFunction, change::AbstractFunctionModification) + +Apply the modification specified by `change` to the objective function of +`model`. To change the function completely, call `set` instead. + +An [`ModifyObjectiveNotAllowed`](@ref) error is thrown if modifying +objectives is not supported by the model `model`. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variable(model); + +julia> MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + +julia> f = 1.0 * x; + +julia> attr = MOI.ObjectiveFunction{typeof(f)}() +MathOptInterface.ObjectiveFunction{MathOptInterface.ScalarAffineFunction{Float64}}() + +julia> MOI.set(model, attr, f) + +julia> MOI.modify(model, attr, MOI.ScalarConstantChange(10.0)) + +julia> print(model) +Minimize ScalarAffineFunction{Float64}: + 10.0 + 1.0 v[1] + +Subject to: +``` +""" function modify( model::ModelLike, attr::ObjectiveFunction, @@ -178,6 +199,47 @@ function modify( return throw_modify_not_allowed(attr, change) end +""" + modify( + model::ModelLike, + attr::ObjectiveFunction, + changes::AbstractVector{<:AbstractFunctionModification}, + ) + +Apply multiple modifications specified by `changes` to the functions of constraints `cis`. + +A [`ModifyObjectiveNotAllowed`](@ref) error is thrown if modifying +objective coefficients is not supported by `model`. + +## Example + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> x = MOI.add_variables(model, 2); + +julia> MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + +julia> f = 1.0 * x[1] + 1.0 * x[2]; + +julia> attr = MOI.ObjectiveFunction{typeof(f)}() +MathOptInterface.ObjectiveFunction{MathOptInterface.ScalarAffineFunction{Float64}}() + +julia> MOI.set(model, attr, f) + +julia> MOI.modify(model, attr, MOI.ScalarCoefficientChange.(x, [2.0, 0.5])) + +julia> print(model) +Minimize ScalarAffineFunction{Float64}: + 0.0 + 2.0 v[1] + 0.5 v[2] + +Subject to: +``` +""" function modify( model::ModelLike, attr::ObjectiveFunction, diff --git a/src/variables.jl b/src/variables.jl index 1187c6900c..0660bc16ec 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -16,6 +16,7 @@ An error indicating that variables cannot be added to the model. struct AddVariableNotAllowed <: NotAllowedError message::String # Human-friendly explanation why the attribute cannot be set end + AddVariableNotAllowed() = AddVariableNotAllowed("") operation_name(::AddVariableNotAllowed) = "Adding variables" @@ -25,8 +26,18 @@ operation_name(::AddVariableNotAllowed) = "Adding variables" Add `n` scalar variables to the model, returning a vector of variable indices. -A [`AddVariableNotAllowed`](@ref) error is thrown if adding variables cannot be +An [`AddVariableNotAllowed`](@ref) error is thrown if adding variables cannot be done in the current state of the model `model`. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> MOI.add_variables(model, 2) +``` """ function add_variables(model::ModelLike, n) return VariableIndex[add_variable(model) for _ in 1:n] @@ -39,6 +50,16 @@ Add a scalar variable to the model, returning a variable index. A [`AddVariableNotAllowed`](@ref) error is thrown if adding variables cannot be done in the current state of the model `model`. + +## Example + +```jldoctest +julia> import MathOptInterface as MOI + +julia> model = MOI.Utilities.Model{Float64}(); + +julia> MOI.add_variables(model, 2) +``` """ add_variable(model::ModelLike) = throw(AddVariableNotAllowed()) From a5a9509f171460355c830ed12ce19c25caa128a5 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 4 Nov 2024 16:32:50 +1300 Subject: [PATCH 2/3] Update --- src/constraints.jl | 4 ++-- src/variables.jl | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/constraints.jl b/src/constraints.jl index 480ba10118..6625dfd5cc 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -335,9 +335,9 @@ Typically, the user should delete the constraint and add a new one. ```jldoctest julia> import MathOptInterface as MOI -julia> model = MOI.Utilities.Model{Float64}() +julia> model = MOI.Utilities.Model{Float64}(); -julia> x = MOI.add_variable(model) +julia> x = MOI.add_variable(model); julia> c = MOI.add_constraint(model, 1.0 * x, MOI.LessThan(2.0)); diff --git a/src/variables.jl b/src/variables.jl index 0660bc16ec..8bbbcc3db4 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -37,6 +37,9 @@ julia> import MathOptInterface as MOI julia> model = MOI.Utilities.Model{Float64}(); julia> MOI.add_variables(model, 2) +2-element Vector{MathOptInterface.VariableIndex}: + MOI.VariableIndex(1) + MOI.VariableIndex(2) ``` """ function add_variables(model::ModelLike, n) @@ -58,7 +61,8 @@ julia> import MathOptInterface as MOI julia> model = MOI.Utilities.Model{Float64}(); -julia> MOI.add_variables(model, 2) +julia> x = MOI.add_variable(model) +MOI.VariableIndex(1) ``` """ add_variable(model::ModelLike) = throw(AddVariableNotAllowed()) From 1f1b77da188c525c4421015188a904b37fb8b201 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 4 Nov 2024 16:41:54 +1300 Subject: [PATCH 3/3] Update --- src/constraints.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constraints.jl b/src/constraints.jl index 6625dfd5cc..979fae8308 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -350,6 +350,7 @@ ScalarAffineFunction{Float64}-in-LessThan{Float64} 0.0 + 1.0 v[1] <= 2.0 julia> c2 = MOI.transform(model, c, MOI.GreaterThan(0.0)) +MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}(1) julia> print(model) Feasibility