diff --git a/src/JuMP.jl b/src/JuMP.jl index 5d0764f4dcc..e2bf709bbc1 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -598,13 +598,30 @@ end set_string_names_on_creation(model::GenericModel, value::Bool) Set the default argument of the `set_string_name` keyword in the -[`@variable`](@ref) and [`@constraint`](@ref) macros to `value`. This is used to -determine whether to assign `String` names to all variables and constraints in -`model`. +[`@variable`](@ref) and [`@constraint`](@ref) macros to `value`. + +The `set_string_name` keyword is used to determine whether to assign `String` +names to all variables and constraints in `model`. By default, `value` is `true`. However, for larger models calling `set_string_names_on_creation(model, false)` can improve performance at the cost of reducing the readability of printing and solver log messages. + +## Example + +```jldoctest +julia> import HiGHS + +julia> model = Model(HiGHS.Optimizer); + +julia> set_string_names_on_creation(model) +true + +julia> set_string_names_on_creation(model, false) + +julia> set_string_names_on_creation(model) +false +``` """ function set_string_names_on_creation(model::GenericModel, value::Bool) model.set_string_names_on_creation = value @@ -1206,6 +1223,43 @@ Base.iterate(::AbstractJuMPScalar, state) = nothing Base.isempty(::AbstractJuMPScalar) = false Base.length(::AbstractJuMPScalar) = 1 +""" + isequal_canonical( + x::T, + y::T + ) where {T<:AbstractJuMPScalar,AbstractArray{<:AbstractJuMPScalar}} + +Return `true` if `x` is equal to `y` after dropping zeros and disregarding +the order. + +This method is mainly useful for testing, because fallbacks like `x == y` do not +account for valid mathematical comparisons like `x[1] + 0 x[2] + 1 == x[1] + 1`. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x[1:2]); + +julia> a = x[1] + 1.0 +x[1] + 1 + +julia> b = x[1] + x[2] + 1.0 +x[1] + x[2] + 1 + +julia> add_to_expression!(b, -1.0, x[2]) +x[1] + 0 x[2] + 1 + +julia> a == b +false + +julia> isequal_canonical(a, b) +true +``` +""" +function isequal_canonical end + # Check if two arrays of AbstractJuMPScalars are equal. Useful for testing. function isequal_canonical( x::AbstractArray{<:AbstractJuMPScalar}, diff --git a/src/aff_expr.jl b/src/aff_expr.jl index e0edcdf6ec1..2e49a418139 100644 --- a/src/aff_expr.jl +++ b/src/aff_expr.jl @@ -629,15 +629,6 @@ function SparseArrays.dropzeros(aff::GenericAffExpr) return result end -""" - 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}, diff --git a/src/callbacks.jl b/src/callbacks.jl index 71d950da8fd..42d947216af 100644 --- a/src/callbacks.jl +++ b/src/callbacks.jl @@ -13,6 +13,33 @@ Return an [`MOI.CallbackNodeStatusCode`](@ref) enum, indicating if the current primal solution available from [`callback_value`](@ref) is integer feasible. + +## Example + +```jldoctest; filter=r"CALLBACK_NODE_STATUS_.+" +julia> import GLPK + +julia> model = Model(GLPK.Optimizer); + +julia> @variable(model, x <= 10, Int); + +julia> @objective(model, Max, x); + +julia> function my_callback_function(cb_data) + status = callback_node_status(cb_data, model) + println("Status is: ", status) + return + end +my_callback_function (generic function with 1 method) + +julia> set_attribute(model, GLPK.CallbackFunction(), my_callback_function) + +julia> optimize!(model) +Status is: CALLBACK_NODE_STATUS_UNKNOWN +Status is: CALLBACK_NODE_STATUS_UNKNOWN +Status is: CALLBACK_NODE_STATUS_INTEGER +Status is: CALLBACK_NODE_STATUS_INTEGER +``` """ function callback_node_status(cb_data, model::GenericModel) # TODO(odow): @@ -29,11 +56,41 @@ end """ callback_value(cb_data, x::GenericVariableRef) + callback_value(cb_data, x::Union{GenericAffExpr,GenericQuadExpr}) -Return the primal solution of a variable inside a callback. +Return the primal solution of `x` inside a callback. `cb_data` is the argument to the callback function, and the type is dependent on the solver. + +Use [`callback_node_status`](@ref) to check whether a solution is available. + +## Example + +```jldoctest +julia> import GLPK + +julia> model = Model(GLPK.Optimizer); + +julia> @variable(model, x <= 10, Int); + +julia> @objective(model, Max, x); + +julia> function my_callback_function(cb_data) + status = callback_node_status(cb_data, model) + if status == MOI.CALLBACK_NODE_STATUS_INTEGER + println("Solution is: ", callback_value(cb_data, x)) + end + return + end +my_callback_function (generic function with 1 method) + +julia> set_attribute(model, GLPK.CallbackFunction(), my_callback_function) + +julia> optimize!(model) +Solution is: 10.0 +Solution is: 10.0 +``` """ function callback_value(cb_data, x::GenericVariableRef) # TODO(odow): @@ -52,15 +109,6 @@ function callback_value(cb_data, x::GenericVariableRef) ) end -""" - callback_value(cb_data, expr::Union{GenericAffExpr, GenericQuadExpr}) - -Return the primal solution of an affine or quadratic expression inside a -callback by getting the value for each variable appearing in the expression. - -`cb_data` is the argument to the callback function, and the type is dependent on -the solver. -""" function callback_value(cb_data, expr::Union{GenericAffExpr,GenericQuadExpr}) return value(expr) do x return callback_value(cb_data, x) diff --git a/src/constraints.jl b/src/constraints.jl index 57d22236d11..53e4a42c55a 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -33,6 +33,19 @@ end index(cr::ConstraintRef)::MOI.ConstraintIndex Return the index of the constraint that corresponds to `cr` in the MOI backend. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x); + +julia> @constraint(model, c, x >= 0); + +julia> index(c) +MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}(1) +``` """ index(cr::ConstraintRef) = cr.index @@ -60,6 +73,7 @@ julia> MOI.get(model_new, MOI.ConstraintName(), c) ERROR: ConstraintNotOwned{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.GreaterThan{Float64}}, ScalarShape}}(c : x ≥ 0) Stacktrace: [...] +``` """ struct ConstraintNotOwned{C<:ConstraintRef} <: Exception constraint_ref::C @@ -487,6 +501,9 @@ end constraint_ref_with_index(model::AbstractModel, index::MOI.ConstraintIndex) Return a `ConstraintRef` of `model` corresponding to `index`. + +This function is a helper function used internally by JuMP and some JuMP +extensions. It should not need to be called in user-code. """ function constraint_ref_with_index( model::AbstractModel, @@ -497,6 +514,7 @@ function constraint_ref_with_index( ) return ConstraintRef(model, index, ScalarShape()) end + function constraint_ref_with_index( model::AbstractModel, index::MOI.ConstraintIndex{ @@ -607,7 +625,25 @@ end """ is_valid(model::GenericModel, con_ref::ConstraintRef{<:AbstractModel}) -Return `true` if `constraint_ref` refers to a valid constraint in `model`. +Return `true` if `con_ref` refers to a valid constraint in `model`. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x); + +julia> @constraint(model, c, 2 * x <= 1); + +julia> is_valid(model, c) +true + +julia> model_2 = Model(); + +julia> is_valid(model_2, c) +false +``` """ function is_valid(model::GenericModel, con_ref::ConstraintRef{<:AbstractModel}) return ( @@ -1357,7 +1393,7 @@ true julia> dual(c) -2.0 -```` +``` """ function dual( con_ref::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex}; diff --git a/src/macros/@variable.jl b/src/macros/@variable.jl index 13de6117a37..e9e73ed9300 100644 --- a/src/macros/@variable.jl +++ b/src/macros/@variable.jl @@ -401,6 +401,15 @@ end Given an (in)equality symbol `T`, return a new `Val` object with the opposite (in)equality symbol. + +This function is intended for use in JuMP extensions. + +## Example + +```jldoctest +julia> reverse_sense(Val(:>=)) +Val{:<=}() +``` """ function reverse_sense end reverse_sense(::Val{:<=}) = Val(:>=) diff --git a/src/nlp.jl b/src/nlp.jl index 3d5e2f55c43..e77e3bd3158 100644 --- a/src/nlp.jl +++ b/src/nlp.jl @@ -263,11 +263,6 @@ function add_nonlinear_parameter(model::Model, value::Real) return NonlinearParameter(model, p.value) end -""" - index(p::NonlinearParameter)::MOI.Nonlinear.ParameterIndex - -Return the index of the nonlinear parameter associated with `p`. -""" index(p::NonlinearParameter) = MOI.Nonlinear.ParameterIndex(p.index) """ @@ -372,11 +367,6 @@ function MOI.Nonlinear.parse_expression( return MOI.Nonlinear.parse_expression(model, expr, index, parent) end -""" - index(ex::NonlinearExpression)::MOI.Nonlinear.ExpressionIndex - -Return the index of the nonlinear expression associated with `ex`. -""" index(ex::NonlinearExpression) = MOI.Nonlinear.ExpressionIndex(ex.index) """ diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index 53e62fc8110..6393ea297e0 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -160,6 +160,19 @@ _parens(::MIME"text/latex") = "\\left(", "\\right)", "{", "}", "\\textsf" Return the string that should be printed for the operator `op` when [`function_string`](@ref) is called with `mime` and `x`. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x[1:2], Bin); + +julia> f = @expression(model, x[1] || x[2]); + +julia> op_string(MIME("text/plain"), f, Val(:||)) +"||" +``` """ op_string(::MIME, ::GenericNonlinearExpr, ::Val{op}) where {op} = string(op) op_string(::MIME"text/latex", ::GenericNonlinearExpr, ::Val{:&&}) = "\\wedge" diff --git a/src/optimizer_interface.jl b/src/optimizer_interface.jl index f487b5bfb64..04f7840df0f 100644 --- a/src/optimizer_interface.jl +++ b/src/optimizer_interface.jl @@ -370,6 +370,19 @@ Errors if `model` is in direct mode during a call from the function named Used internally within JuMP, or by JuMP extensions who do not want to support models in direct mode. + +## Example + +```jldoctest +julia> import HiGHS + +julia> model = direct_model(HiGHS.Optimizer()); + +julia> error_if_direct_mode(model, :foo) +ERROR: The `foo` function is not supported in DIRECT mode. +Stacktrace: +[...] +``` """ function error_if_direct_mode(model::GenericModel, func::Symbol) if mode(model) == DIRECT @@ -507,6 +520,28 @@ If `ignore_optimize_hook == true`, the optimize hook is ignored and the model is solved as if the hook was not set. Keyword arguments `kwargs` are passed to the `optimize_hook`. An error is thrown if `optimize_hook` is `nothing` and keyword arguments are provided. + +## Example + +```jldoctest +julia> import HiGHS + +julia> model = Model(HiGHS.Optimizer); + +julia> set_silent(model) + +julia> function my_optimize_hook(model; foo) + println("Hook called with foo = ", foo) + return optimize!(model; ignore_optimize_hook = true) + end +my_optimize_hook (generic function with 1 method) + +julia> set_optimize_hook(model, my_optimize_hook) +my_optimize_hook (generic function with 1 method) + +julia> optimize!(model; foo = 2) +Hook called with foo = 2 +``` """ function optimize!( model::GenericModel; @@ -590,13 +625,41 @@ end """ compute_conflict!(model::GenericModel) -Compute a conflict if the model is infeasible. If an optimizer has not -been set yet (see [`set_optimizer`](@ref)), a [`NoOptimizer`](@ref) -error is thrown. +Compute a conflict if the model is infeasible. + +The conflict is also called the Irreducible Infeasible Subsystem (IIS). + +If an optimizer has not been set yet (see [`set_optimizer`](@ref)), a +[`NoOptimizer`](@ref) error is thrown. -The status of the conflict can be checked with the `MOI.ConflictStatus` +The status of the conflict can be checked with the [`MOI.ConflictStatus`](@ref) model attribute. Then, the status for each constraint can be queried with -the `MOI.ConstraintConflictStatus` attribute. +the [`MOI.ConstraintConflictStatus`](@ref) attribute. + +See also: [`copy_conflict`](@ref) + +## Example + +```julia +julia> using JuMP + +julia> model = Model(Gurobi.Optimizer); + +julia> set_silent(model) + +julia> @variable(model, x >= 0); + +julia> @constraint(model, c1, x >= 2); + +julia> @constraint(model, c2, x <= 1); + +julia> optimize!(model) + +julia> compute_conflict!(model) + +julia> get_attribute(model, MOI.ConflictStatus()) +CONFLICT_FOUND::ConflictStatusCode = 3 +``` """ function compute_conflict!(model::GenericModel) if mode(model) != DIRECT && MOIU.state(backend(model)) == MOIU.NO_OPTIMIZER diff --git a/src/print.jl b/src/print.jl index b2bc8a7d619..648ea32734d 100644 --- a/src/print.jl +++ b/src/print.jl @@ -238,6 +238,19 @@ end show_objective_function_summary(io::IO, model::AbstractModel) Write to `io` a summary of the objective function type. + +## Extensions + +`AbstractModel`s should implement this method. + +## Example + +```jldoctest +julia> model = Model(); + +julia> show_objective_function_summary(stdout, model) +Objective function type: AffExpr +``` """ function show_objective_function_summary(io::IO, model::GenericModel) nlobj = _nlp_objective_function(model) @@ -254,6 +267,21 @@ end show_constraints_summary(io::IO, model::AbstractModel) Write to `io` a summary of the number of constraints. + +## Extensions + +`AbstractModel`s should implement this method. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x >= 0); + +julia> show_constraints_summary(stdout, model) +`VariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 1 constraint +``` """ function show_constraints_summary(io::IO, model::GenericModel) for (F, S) in list_of_constraint_types(model) @@ -272,7 +300,20 @@ end Print a summary of the optimizer backing `model`. +## Extensions + `AbstractModel`s should implement this method. + +## Example + +```jldoctest +julia> model = Model(); + +julia> show_backend_summary(stdout, model) +Model mode: AUTOMATIC +CachingOptimizer state: NO_OPTIMIZER +Solver name: No optimizer attached. +``` """ function show_backend_summary(io::IO, model::GenericModel) model_mode = mode(model) @@ -412,6 +453,19 @@ end model_string(mode::MIME, model::AbstractModel) Return a `String` representation of `model` given the `mode`. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x >= 0); + +julia> print(model_string(MIME("text/plain"), model)) +Feasibility +Subject to + x ≥ 0 +``` """ function model_string(mode::MIME, model::AbstractModel) if mode == MIME("text/latex") @@ -424,6 +478,19 @@ end objective_function_string(mode, model::AbstractModel)::String Return a `String` describing the objective function of the model. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x); + +julia> @objective(model, Min, 2 * x); + +julia> objective_function_string(MIME("text/plain"), model) +"2 x" +``` """ function objective_function_string(mode, model::GenericModel) nlobj = _nlp_objective_function(model) @@ -475,6 +542,21 @@ end constraints_string(mode, model::AbstractModel)::Vector{String} Return a list of `String`s describing each constraint of the model. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x >= 0); + +julia> @constraint(model, c, 2 * x <= 1); + +julia> constraints_string(MIME("text/plain"), model) +2-element Vector{String}: + "c : 2 x ≤ 1" + "x ≥ 0" +``` """ function constraints_string(mode, model::GenericModel) strings = String[ @@ -641,6 +723,17 @@ end ) Return a `String` representing the function `func` using print mode `mode`. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x); + +julia> function_string(MIME("text/plain"), 2 * x + 1) +"2 x + 1" +``` """ function function_string(mode::MIME"text/plain", v::AbstractVariableRef) var_name = name(v) @@ -845,6 +938,18 @@ end Return a `String` representing the membership to the set `set` using print mode `mode`. + +## Extensions + +JuMP extensions may extend this method for new `set` types to improve the +legibility of their printing. + +## Example + +```jldoctest +julia> in_set_string(MIME("text/plain"), MOI.Interval(1.0, 2.0)) +"∈ [1, 2]" +``` """ function in_set_string end @@ -918,6 +1023,19 @@ end ) Return a string representation of the constraint `ref`, given the `mode`. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x); + +julia> @constraint(model, c, 2 * x <= 1); + +julia> constraint_string(MIME("text/plain"), c) +"c : 2 x ≤ 1" +``` """ function constraint_string(mode::MIME, ref::ConstraintRef; in_math_mode = false) return constraint_string( diff --git a/src/sd.jl b/src/sd.jl index 5f33a0fe824..e61f111391f 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -76,17 +76,18 @@ struct HermitianMatrixSpace end PSDCone Positive semidefinite cone object that can be used to constrain a square matrix -to be positive semidefinite in the [`@constraint`](@ref) macro. If the matrix -has type `Symmetric` then the columns vectorization (the vector obtained by -concatenating the columns) of its upper triangular part is constrained to belong -to the `MOI.PositiveSemidefiniteConeTriangle` set, otherwise its column -vectorization is constrained to belong to the -`MOI.PositiveSemidefiniteConeSquare` set. +to be positive semidefinite in the [`@constraint`](@ref) macro. + +If the matrix has type `Symmetric` then the columns vectorization (the vector +obtained by concatenating the columns) of its upper triangular part is +constrained to belong to the [`MOI.PositiveSemidefiniteConeTriangle`](@ref) set, +otherwise its column vectorization is constrained to belong to the +[`MOI.PositiveSemidefiniteConeSquare`](@ref) set. ## Example -Consider the following example: -```jldoctest PSDCone +Non-symmetric case: +```jldoctest julia> model = Model(); julia> @variable(model, x); @@ -109,12 +110,20 @@ julia> jump_function(constraint_object(cref)) julia> moi_set(constraint_object(cref)) MathOptInterface.PositiveSemidefiniteConeSquare(2) ``` -We see in the output of the last command that the vectorization of the matrix -is constrained to belong to the `PositiveSemidefiniteConeSquare`. + +Symmetric case: ```jldoctest PSDCone julia> using LinearAlgebra # For Symmetric +julia> model = Model(); + +julia> @variable(model, x); + +julia> a = [x 2x; 2x x]; + +julia> b = [1 2; 2 4]; + julia> cref = @constraint(model, Symmetric(a - b) in PSDCone()) [x - 1 2 x - 2 2 x - 2 x - 4] ∈ PSDCone() @@ -128,9 +137,6 @@ julia> jump_function(constraint_object(cref)) julia> moi_set(constraint_object(cref)) MathOptInterface.PositiveSemidefiniteConeTriangle(2) ``` -As we see in the output of the last command, the vectorization of only the upper -triangular part of the matrix is constrained to belong to the -`PositiveSemidefiniteConeSquare`. """ struct PSDCone end @@ -362,45 +368,6 @@ function value( ) end -""" - build_constraint( - error_fn::Function, - Q::LinearAlgebra.Symmetric{V, M}, - ::PSDCone, - ) where {V<:AbstractJuMPScalar,M<:AbstractMatrix{V}} - -Return a [`VectorConstraint`](@ref) of shape [`SymmetricMatrixShape`](@ref) -constraining the matrix `Q` to be positive semidefinite. - -This function is used by the [`@constraint`](@ref) macros as follows: -```jldoctest -julia> import LinearAlgebra - -julia> model = Model(); - -julia> @variable(model, Q[1:2, 1:2]); - -julia> @constraint(model, LinearAlgebra.Symmetric(Q) in PSDCone()) -[Q[1,1] Q[1,2] - Q[1,2] Q[2,2]] ∈ PSDCone() -``` - -The form above is usually used when the entries of `Q` are affine or quadratic -expressions, but it can also be used when the entries are variables to get the -reference of the semidefinite constraint, for example, -```jldoctest -julia> model = Model(); - -julia> @variable(model, Q[1:2, 1:2], Symmetric) -2×2 LinearAlgebra.Symmetric{VariableRef, Matrix{VariableRef}}: - Q[1,1] Q[1,2] - Q[1,2] Q[2,2] - -julia> @constraint(model, Q in PSDCone()) -[Q[1,1] Q[1,2] - Q[1,2] Q[2,2]] ∈ PSDCone() -``` -""" function build_constraint( error_fn::Function, Q::LinearAlgebra.Symmetric{V,M}, diff --git a/src/shapes.jl b/src/shapes.jl index 62d1472db70..840de3d13c0 100644 --- a/src/shapes.jl +++ b/src/shapes.jl @@ -102,22 +102,69 @@ function reshape_vector end shape(c::AbstractConstraint)::AbstractShape Return the shape of the constraint `c`. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x[1:2]); + +julia> c = @constraint(model, x[2] <= 1); + +julia> shape(constraint_object(c)) +ScalarShape() + +julia> d = @constraint(model, x in SOS1()); + +julia> shape(constraint_object(d)) +VectorShape() +``` """ function shape end """ - ScalarShape + ScalarShape() + +An [`AbstractShape`](@ref) that represents scalar constraints. + +## Example + +```jldoctest +julia> model = Model(); -Shape of scalar constraints. +julia> @variable(model, x[1:2]); + +julia> c = @constraint(model, x[2] <= 1); + +julia> shape(constraint_object(c)) +ScalarShape() +``` """ struct ScalarShape <: AbstractShape end + reshape_vector(α, ::ScalarShape) = α """ - VectorShape + VectorShape() + +An [`AbstractShape`](@ref) that represents vector-valued constraints. + +## Example -Vector for which the vectorized form corresponds exactly to the vector given. +```jldoctest +julia> model = Model(); + +julia> @variable(model, x[1:2]); + +julia> c = @constraint(model, x in SOS1()); + +julia> shape(constraint_object(c)) +VectorShape() +``` """ struct VectorShape <: AbstractShape end + reshape_vector(vectorized_form, ::VectorShape) = vectorized_form + vectorize(x, ::VectorShape) = x diff --git a/src/variables.jl b/src/variables.jl index 76d4a354f1f..89f649580ef 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -334,15 +334,28 @@ function Base.showerror(io::IO, err::VariableNotOwned) end """ - check_belongs_to_model(func::AbstractJuMPScalar, model::AbstractModel) + check_belongs_to_model(x::AbstractJuMPScalar, model::AbstractModel) + check_belongs_to_model(x::AbstractConstraint, model::AbstractModel) -Throw [`VariableNotOwned`](@ref) if the [`owner_model`](@ref) of one of the -variables of the function `func` is not `model`. +Throw [`VariableNotOwned`](@ref) if the [`owner_model`](@ref) of `x` is not +`model`. - check_belongs_to_model(constraint::AbstractConstraint, model::AbstractModel) +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x); + +julia> check_belongs_to_model(x, model) -Throw [`VariableNotOwned`](@ref) if the [`owner_model`](@ref) of one of the -variables of the constraint `constraint` is not `model`. +julia> model_2 = Model(); + +julia> check_belongs_to_model(x, model_2) +ERROR: VariableNotOwned{VariableRef}(x): the variable x cannot be used in this model because +it belongs to a different model. +[...] +``` """ function check_belongs_to_model end @@ -350,6 +363,7 @@ function check_belongs_to_model(v::AbstractVariableRef, model::AbstractModel) if owner_model(v) !== model throw(VariableNotOwned(v)) end + return end Base.iszero(::GenericVariableRef) = false @@ -2052,9 +2066,7 @@ function JuMP.add_variable( end ``` but adds the variables with `MOI.add_constrained_variable(model, variable.set)` -instead. See [the MOI documentation](https://jump.dev/MathOptInterface.jl/v0.9.3/apireference/#Variables-1) -for the difference between adding the variables with `MOI.add_constrained_variable` -and adding them with `MOI.add_variable` and adding the constraint separately. +instead. """ struct VariableConstrainedOnCreation{ S<:MOI.AbstractScalarSet,