diff --git a/docs/src/reference/constraints.md b/docs/src/reference/constraints.md index 3020293cb28..f385820dc12 100644 --- a/docs/src/reference/constraints.md +++ b/docs/src/reference/constraints.md @@ -6,6 +6,7 @@ More information can be found in the [Constraints](@ref) section of the manual. @constraint @constraints @SDconstraint +@SDconstraints name(::ConstraintRef{Model,<:JuMP._MOICON}) set_name(::ConstraintRef{Model,<:JuMP._MOICON}, ::String) @@ -47,8 +48,6 @@ index(::ConstraintRef) optimizer_index(::ConstraintRef{Model}) constraint_object -jump_function -moi_function moi_set function_string diff --git a/docs/src/reference/expressions.md b/docs/src/reference/expressions.md index 73d08dec7af..3616deb5974 100644 --- a/docs/src/reference/expressions.md +++ b/docs/src/reference/expressions.md @@ -4,9 +4,26 @@ More information can be found in the [Expressions](@ref) section of the manual. ```@docs @expression +@expressions + +GenericAffExpr +AffExpr +GenericQuadExpr +QuadExpr +UnorderedPair + add_to_expression! -drop_zeros! constant +drop_zeros! +isequal_canonical linear_terms +map_coefficients +map_coefficients_inplace! quad_terms +variable_ref_type + +jump_function +jump_function_type +moi_function +moi_function_type ``` diff --git a/docs/src/reference/models.md b/docs/src/reference/models.md index 44d6e39586e..343c24a4546 100644 --- a/docs/src/reference/models.md +++ b/docs/src/reference/models.md @@ -45,4 +45,5 @@ copy_extension_data Base.copy(::AbstractModel) operator_warn +error_if_direct_mode ``` diff --git a/docs/src/reference/nonlinear.md b/docs/src/reference/nonlinear.md index 6890db1f56d..9520af52fa4 100644 --- a/docs/src/reference/nonlinear.md +++ b/docs/src/reference/nonlinear.md @@ -5,7 +5,9 @@ manual. ```@docs @NLconstraint +@NLconstraints @NLexpression +@NLexpressions @NLobjective @NLparameter value(::JuMP.NonlinearParameter) diff --git a/docs/src/reference/objectives.md b/docs/src/reference/objectives.md index 9c1837b7443..44b4aa42b0e 100644 --- a/docs/src/reference/objectives.md +++ b/docs/src/reference/objectives.md @@ -5,14 +5,14 @@ More information can be found in the [Objectives](@ref) section of the manual. ```@docs @objective -objective_sense -set_objective_sense - objective_function objective_function_type -set_objective_function +objective_sense +set_objective set_objective_coefficient +set_objective_function +set_objective_sense objective_function_string show_objective_function_summary diff --git a/src/aff_expr.jl b/src/aff_expr.jl index 3075d6a8f99..44f7f9ef679 100644 --- a/src/aff_expr.jl +++ b/src/aff_expr.jl @@ -76,19 +76,62 @@ end ############################################################################# -# GenericAffExpr -# ∑ aᵢ xᵢ + c + +""" + mutable struct GenericAffExpr{CoefType,VarType} <: AbstractJuMPScalar + constant::CoefType + terms::OrderedDict{VarType,CoefType} + end + +An expression type representing an affine expression of the form: +``\\sum a_i x_i + c``. + +## Fields + + * `.constant`: the constant `c` in the expression. + * `.terms`: an `OrderedDict`, with keys of `VarType` and values of `CoefType` + describing the sparse vector `a`. +""" mutable struct GenericAffExpr{CoefType,VarType} <: AbstractJuMPScalar constant::CoefType terms::OrderedDict{VarType,CoefType} end +""" + variable_ref_type(::GenericAffExpr{C, V}) where {C, V} + +A helper function used internally by JuMP and some JuMP extensions. Returns the +variable type `V` from a [`GenericAffExpr`](@ref) +""" variable_ref_type(::GenericAffExpr{C, V}) where {C, V} = V +""" + GenericAffExpr(constant::V, kv::AbstractArray{Pair{K,V}}) where {K,V} + +Create a [`GenericAffExpr`](@ref) by passing a constant and a vector of pairs. + +## Examples + +```jldoctest; setup=:(using JuMP; model = Model(); @variable(model, x)) +julia> GenericAffExpr(1.0, [x => 1.0]) +x + 1 +""" function GenericAffExpr(constant::V, kv::AbstractArray{Pair{K,V}}) where {K,V} return GenericAffExpr{V,K}(constant, _new_ordered_dict(K, V, kv)) end +""" + GenericAffExpr(constant::V, kv::Vararg{Pair{K,V},N}) where {K,V,N} + +Create a [`GenericAffExpr`](@Ref) by passing a constant and pairs of additional +arguments. + +## Examples + +```jldoctest; setup=:(using JuMP; model = Model(); @variable(model, x)) +julia> GenericAffExpr(1.0, x => 1.0) +x + 1 +""" function GenericAffExpr(constant::V, kv::Vararg{Pair{K,V},N}) where {K,V,N} return GenericAffExpr{V,K}(constant, _new_ordered_dict(K, V, kv...)) end @@ -131,6 +174,27 @@ function _affine_coefficient(f::GenericAffExpr{C, V}, variable::V) where {C, V} return get(f.terms, variable, zero(C)) end +""" + map_coefficients_inplace!(f::Function, a::GenericAffExpr) + +Apply `f` to the coefficients and constant term of an [`GenericAffExpr`](@ref) +`a` and update them in-place. + +See also: [`map_coefficients`](@ref) + +## Example + +```jldoctest; setup=:(using JuMP; model = Model(); @variable(model, x)) +julia> a = GenericAffExpr(1.0, x => 1.0) +x + 1 + +julia> map_coefficients_inplace!(c -> 2 * c, a) +2 x + 2 + +julia> a +2 x + 2 +``` +""" function map_coefficients_inplace!(f::Function, a::GenericAffExpr) # The iterator remains valid if existing elements are updated. for (coef, var) in linear_terms(a) @@ -140,6 +204,27 @@ function map_coefficients_inplace!(f::Function, a::GenericAffExpr) return a end +""" + map_coefficients(f::Function, a::GenericAffExpr) + +Apply `f` to the coefficients and constant term of an [`GenericAffExpr`](@ref) +`a` and return a new expression. + +See also: [`map_coefficients_inplace!`](@ref) + +## Example + +```jldoctest; setup=:(using JuMP; model = Model(); @variable(model, x)) +julia> a = GenericAffExpr(1.0, x => 1.0) +x + 1 + +julia> map_coefficients(c -> 2 * c, a) +2 x + 2 + +julia> a +x + 1 +``` +""" function map_coefficients(f::Function, a::GenericAffExpr) return map_coefficients_inplace!(f, copy(a)) end @@ -295,9 +380,19 @@ function SparseArrays.dropzeros(aff::GenericAffExpr) return result end -# Check if two AffExprs are equal after dropping zeros and disregarding the -# order. Mostly useful for testing. -function isequal_canonical(aff::GenericAffExpr{C,V}, other::GenericAffExpr{C,V}) where {C,V} +""" + isequal_canonical( + aff::GenericAffExpr{C,V}, + other::GenericAffExpr{C,V} + ) where {C,V} + +Return `true` if `aff` is equal to `other` after dropping zeros and disregarding +the order. Mainly useful for testing. +""" +function isequal_canonical( + aff::GenericAffExpr{C,V}, + other::GenericAffExpr{C,V} +) where {C,V} aff_nozeros = dropzeros(aff) other_nozeros = dropzeros(other) # Note: This depends on equality of OrderedDicts ignoring order. @@ -312,7 +407,12 @@ function Base.convert(::Type{GenericAffExpr{T,V}}, v::_Constant) where {T,V} return GenericAffExpr{T,V}(convert(T, _constant_to_number(v))) end -# Alias for (Float64, VariableRef), the specific GenericAffExpr used by JuMP +""" + AffExpr + +Alias for `GenericAffExpr{Float64,VariableRef}`, the specific +[`GenericAffExpr`](@ref) used by JuMP. +""" const AffExpr = GenericAffExpr{Float64,VariableRef} # Check all coefficients are finite, i.e. not NaN, not Inf, not -Inf @@ -358,12 +458,48 @@ function MOI.ScalarAffineFunction(a::AffExpr) for t in linear_terms(a)] return MOI.ScalarAffineFunction(terms, a.constant) end + +""" + moi_function(x) + +Given a JuMP object `x`, return the MathOptInterface equivalent. + +See also: [`jump_function`](@ref). +""" +function moi_function end + +""" + moi_function_type(::Type{T}) where {T} + +Given a JuMP object type `T`, return the MathOptInterface equivalent. + +See also: [`jump_function_type`](@ref). +""" +function moi_function_type end + +""" + jump_function(x) + +Given an MathOptInterface object `x`, return the JuMP equivalent. + +See also: [`moi_function`](@ref). +""" +function jump_function end + +""" + jump_function_type(::Type{T}) where {T} + +Given an MathOptInterface object type `T`, return the JuMP equivalent. + +See also: [`moi_function_type`](@ref). +""" +function jump_function_type end + moi_function(a::GenericAffExpr) = MOI.ScalarAffineFunction(a) function moi_function_type(::Type{<:GenericAffExpr{T}}) where T return MOI.ScalarAffineFunction{T} end - function AffExpr(m::Model, f::MOI.ScalarAffineFunction) aff = AffExpr() for t in f.terms diff --git a/src/constraints.jl b/src/constraints.jl index 1b7cfae11f5..dd213acdf09 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -340,7 +340,7 @@ end Return the function of the constraint `constraint` in the function-in-set form as a `AbstractJuMPScalar` or `Vector{AbstractJuMPScalar}`. """ -function jump_function end +jump_function(constraint::AbstractConstraint) = constraint.func """ moi_function(constraint::AbstractConstraint) @@ -362,7 +362,7 @@ Return the set of the constraint `constraint` in the function-in-set form as a Returns the MOI set of dimension `dim` corresponding to the JuMP set `s`. """ -function moi_set end +moi_set(constraint::AbstractConstraint) = constraint.set """ constraint_object(con_ref::ConstraintRef) @@ -385,8 +385,6 @@ struct ScalarConstraint{F <: AbstractJuMPScalar, set::S end -jump_function(constraint::ScalarConstraint) = constraint.func -moi_set(constraint::ScalarConstraint) = constraint.set reshape_set(set::MOI.AbstractScalarSet, ::ScalarShape) = set shape(::ScalarConstraint) = ScalarShape() @@ -429,8 +427,6 @@ function VectorConstraint(func::Vector{<:AbstractJuMPScalar}, VectorConstraint(func, set, VectorShape()) end -jump_function(constraint::VectorConstraint) = constraint.func -moi_set(constraint::VectorConstraint) = constraint.set reshape_set(set::MOI.AbstractVectorSet, ::VectorShape) = set shape(con::VectorConstraint) = con.shape function constraint_object(con_ref::ConstraintRef{Model, _MOICON{FuncType, SetType}}) where diff --git a/src/macros.jl b/src/macros.jl index 320c2f1bf63..eca4c3d106d 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -707,34 +707,125 @@ end # Doc strings for the auto-generated macro pluralizations @doc """ - @constraints(m, args...) + @constraints(model, args...) -adds groups of constraints at once, in the same fashion as @constraint. The model must be the first argument, and multiple constraints can be added on multiple lines wrapped in a `begin ... end` block. For example: +Adds groups of constraints at once, in the same fashion as the +[`@constraint`](@ref) macro. - @constraints(m, begin - x >= 1 - y - w <= 2 - sum_to_one[i=1:3], z[i] + y == 1 - end) +The model must be the first argument, and multiple constraints can be added on +multiple lines wrapped in a `begin ... end` block. + +# Examples + +```julia +@constraints(model, begin + x >= 1 + y - w <= 2 + sum_to_one[i=1:3], z[i] + y == 1 +end) +``` """ :(@constraints) @doc """ - @variables(m, args...) + @variables(model, args...) + +Adds multiple variables to model at once, in the same fashion as the +[`@variable`](@ref) macro. -Adds multiple variables to model at once, in the same fashion as `@variable` -macro. The model must be the first argument, and multiple variables can be added -on multiple lines wrapped in a `begin ... end` block. For example: +The model must be the first argument, and multiple variables can be added on +multiple lines wrapped in a `begin ... end` block. - @variables(m, begin - x - y[i = 1:2] >= 0, (start = i) - z, Bin, (start = 0, base_name = "Z") - end) +# Examples + +```julia +@variables(model, begin + x + y[i = 1:2] >= 0, (start = i) + z, Bin, (start = 0, base_name = "Z") +end) +``` !!! note - Keyword arguments must be contained within parentheses (refer to the example above). + Keyword arguments must be contained within parentheses (refer to the example + above). """ :(@variables) +@doc """ + @expressions(model, args...) + +Adds multiple expressions to model at once, in the same fashion as the +[`@expression`](@ref) macro. + +The model must be the first argument, and multiple variables can be added on +multiple lines wrapped in a `begin ... end` block. + +# Examples + +```julia +@expressions(model, begin + my_expr, x^2 + y^2 + my_expr_1[i = 1:2], a[i] - z[i] +end) +``` +""" :(@expressions) + +@doc """ + @SDconstraints(model, args...) + +Adds multiple semi-definite constraints to model at once, in the same fashion as +the [`@SDconstraint`](@ref) macro. + +The model must be the first argument, and multiple constraints can be added on +multiple lines wrapped in a `begin ... end` block. + +# Examples + +```julia +@SDconstraints(model, begin + A * x >= b + b - C * y >= 0 +end) +``` +""" :(@SDconstraints) + +@doc """ + @NLconstraints(model, args...) + +Adds multiple nonlinear constraints to model at once, in the same fashion as +the [`@NLconstraint`](@ref) macro. + +The model must be the first argument, and multiple variables can be added on +multiple lines wrapped in a `begin ... end` block. + +# Examples + +```julia +@NLconstraints(model, begin + t >= sqrt(x^2 + y^2) + [i = 1:2], z[i] <= log(a[i]) +end) +``` +""" :(@NLconstraints) + +@doc """ + @NLexpressions(model, args...) + +Adds multiple nonlinear expressions to model at once, in the same fashion as the +[`@NLexpression`](@ref) macro. + +The model must be the first argument, and multiple variables can be added on +multiple lines wrapped in a `begin ... end` block. + +# Examples + +```julia +@NLexpressions(model, begin + my_expr, sqrt(x^2 + y^2) + my_expr_1[i = 1:2], log(a[i]) - z[i] +end) +``` +""" :(@NLexpressions) + """ _moi_sense(_error::Function, sense) diff --git a/src/objective.jl b/src/objective.jl index a7cda1279c7..e42a5639a65 100644 --- a/src/objective.jl +++ b/src/objective.jl @@ -123,6 +123,26 @@ function set_objective_function(model::AbstractModel, func) error("The objective function `$(func)` is not supported by JuMP.") end +""" + set_objective(model::AbstractModel, sense::MOI.OptimizationSense, func) + +The functional equivalent of the [`@objective`](@ref) macro. + +Sets the objective sense and objective function simultaneously, and is +equivalent to: +```julia +set_objective_sense(model, sense) +set_objective_function(model, func) +``` + +## Examples + +```jldoctest; setup=:(using JuMP) +model = Model() +@variable(model, x) +set_objective(model, MOI.MIN_SENSE, x) +``` +""" function set_objective(model::AbstractModel, sense::MOI.OptimizationSense, func) set_objective_sense(model, sense) set_objective_function(model, func) diff --git a/src/optimizer_interface.jl b/src/optimizer_interface.jl index 21f4c49f970..61214d4b43d 100644 --- a/src/optimizer_interface.jl +++ b/src/optimizer_interface.jl @@ -3,6 +3,15 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. +""" + error_if_direct_mode(model::Model, func::Symbol) + +Errors if `model` is in direct mode during a call from the function named +`func`. + +Used internally within JuMP, or by JuMP extensions who do not want to support +models in direct mode. +""" function error_if_direct_mode(model::Model, func::Symbol) if mode(model) == DIRECT error("The `$func` function is not supported in DIRECT mode.") diff --git a/src/quad_expr.jl b/src/quad_expr.jl index efb5347fb48..724534a678e 100644 --- a/src/quad_expr.jl +++ b/src/quad_expr.jl @@ -16,6 +16,11 @@ ############################################################################# +""" + UnorderedPair(a::T, b::T) + +A wrapper type used by [`GenericQuadExpr`](@ref) with fields `.a` and `.b`. +""" struct UnorderedPair{T} a::T b::T @@ -26,14 +31,47 @@ function Base.isequal(p1::UnorderedPair, p2::UnorderedPair) return (p1.a == p2.a && p1.b == p2.b) || (p1.a == p2.b && p1.b == p2.a) end -# GenericQuadExpr -# ∑qᵢⱼ xᵢⱼ + ∑ aᵢ xᵢ + c +""" + mutable struct GenericQuadExpr{CoefType,VarType} <: AbstractJuMPScalar + aff::GenericAffExpr{CoefType,VarType} + terms::OrderedDict{UnorderedPair{VarType}, CoefType} + end + +An expression type representing an quadratic expression of the form: +``\\sum q_{i,j} x_i x_j + \\sum a_i x_i + c``. + +## Fields + + * `.aff`: an [`GenericAffExpr`](@ref) representing the affine portion of the + expression. + * `.terms`: an `OrderedDict`, with keys of `UnorderedPair{VarType}` and + values of `CoefType`, describing the sparse list of terms `q`. +""" mutable struct GenericQuadExpr{CoefType,VarType} <: AbstractJuMPScalar aff::GenericAffExpr{CoefType,VarType} terms::OrderedDict{UnorderedPair{VarType}, CoefType} end -function GenericQuadExpr(aff::GenericAffExpr{V,K}, kv::AbstractArray{Pair{UnorderedPair{K},V}}) where {K,V} +""" + GenericQuadExpr( + aff::GenericAffExpr{V,K}, + kv::AbstractArray{Pair{UnorderedPair{K},V}} + ) where {K,V} + +Create a [`GenericQuadExpr`](@ref) by passing a [`GenericAffExpr`](@ref) and a +vector of ([`UnorderedPair`](@ref), coefficient) pairs. + +## Example + +```jldoctest; setup=:(using JuMP; model = Model(); @variable(model, x)) +julia> GenericQuadExpr(GenericAffExpr(1.0, x => 2.0), [UnorderedPair(x, x) => 3.0]) +3 x² + 2 x + 1 +``` +""" +function GenericQuadExpr( + aff::GenericAffExpr{V,K}, + kv::AbstractArray{Pair{UnorderedPair{K},V}} +) where {K,V} return GenericQuadExpr{V,K}(aff, _new_ordered_dict(UnorderedPair{K}, V, kv)) end @@ -78,6 +116,27 @@ function drop_zeros!(expr::GenericQuadExpr) return end +""" + map_coefficients_inplace!(f::Function, a::GenericQuadExpr) + +Apply `f` to the coefficients and constant term of an [`GenericQuadExpr`](@ref) +`a` and update them in-place. + +See also: [`map_coefficients`](@ref) + +## Example + +```jldoctest; setup=:(using JuMP; model = Model(); @variable(model, x)) +julia> a = @expression(model, x^2 + x + 1) +x² + x + 1 + +julia> map_coefficients_inplace!(c -> 2 * c, a) +2 x² + 2 x + 2 + +julia> a +2 x² + 2 x + 2 +``` +""" function map_coefficients_inplace!(f::Function, q::GenericQuadExpr) # The iterator remains valid if existing elements are updated. for (key, value) in q.terms @@ -87,6 +146,27 @@ function map_coefficients_inplace!(f::Function, q::GenericQuadExpr) return q end +""" + map_coefficients(f::Function, a::GenericQuadExpr) + +Apply `f` to the coefficients and constant term of an [`GenericQuadExpr`](@ref) +`a` and return a new expression. + +See also: [`map_coefficients_inplace!`](@ref) + +## Example + +```jldoctest; setup=:(using JuMP; model = Model(); @variable(model, x)) +julia> a = @expression(model, x^2 + x + 1) +x² + x + 1 + +julia> map_coefficients(c -> 2 * c, a) +2 x² + 2 x + 2 + +julia> a +x² + x + 1 +``` +""" function map_coefficients(f::Function, q::GenericQuadExpr) return map_coefficients_inplace!(f, copy(q)) end @@ -319,6 +399,12 @@ function isequal_canonical(quad::GenericQuadExpr{CoefType,VarType}, other::Gener end # Alias for (Float64, VariableRef) +""" + QuadExpr + +An alias for `GenericQuadExpr{Float64,VariableRef}`, the specific + [`GenericQuadExpr`](@ref) used by JuMP. +""" const QuadExpr = GenericQuadExpr{Float64,VariableRef} function Base.convert(::Type{GenericQuadExpr{C, V}}, v::Union{_Constant,AbstractVariableRef,GenericAffExpr}) where {C, V} return GenericQuadExpr(convert(GenericAffExpr{C, V}, v)) @@ -359,7 +445,6 @@ function moi_function_type(::Type{<:GenericQuadExpr{T}}) where T return MOI.ScalarQuadraticFunction{T} end - function QuadExpr(m::Model, f::MOI.ScalarQuadraticFunction) quad = QuadExpr(AffExpr(m, MOI.ScalarAffineFunction(f.affine_terms, f.constant))) diff --git a/src/variables.jl b/src/variables.jl index 97ed12558fa..a5f238c6161 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -142,13 +142,13 @@ end """ check_belongs_to_model(func::AbstractJuMPScalar, model::AbstractModel) -Throw `VariableNotOwned` if the `owner_model` of one of the variables of the -function `func` is not `model`. +Throw [`VariableNotOwned`](@ref) if the [`owner_model`](@ref) of one of the +variables of the function `func` is not `model`. check_belongs_to_model(constraint::AbstractConstraint, model::AbstractModel) -Throw `VariableNotOwned` if the `owner_model` of one of the variables of the -constraint `constraint` is not `model`. +Throw [`VariableNotOwned`](@ref) if the [`owner_model`](@ref) of one of the +variables of the constraint `constraint` is not `model`. """ function check_belongs_to_model end