From d0142869ad830ddb94ddaf75931c7b478ba4e53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 18 Jul 2019 19:12:47 -0600 Subject: [PATCH 1/7] Define add_constrained_variable --- docs/src/apimanual.md | 32 +++++++- docs/src/apireference.md | 18 ++++- src/Test/UnitTests/basic_constraint_tests.jl | 3 - src/constraints.jl | 20 ++++- src/variables.jl | 81 ++++++++++++++++++++ 5 files changed, 146 insertions(+), 8 deletions(-) diff --git a/docs/src/apimanual.md b/docs/src/apimanual.md index 66a854ff54..dbb9cf52e3 100644 --- a/docs/src/apimanual.md +++ b/docs/src/apimanual.md @@ -929,7 +929,9 @@ If ``\mathcal{C}_i`` is a vector set, the discussion remains valid with ``y_i(\frac{1}{2}x^TQ_ix + a_i^T x + b_i)`` replaced with the scalar product between `y_i` and the vector of scalar-valued quadratic functions. -### Constraint bridges +### Automatic reformulation + +#### Constraint reformulation A constraint often possess different equivalent formulations, but a solver may only support one of them. It would be duplicate work to implement rewritting rules in every solver wrapper for every different formulation of the constraint to express it in the form supported by the solver. @@ -966,6 +968,34 @@ model = MyPackage.Optimizer() MOI.set(model, MyPackage.PrintLevel(), 0) ``` +### Supported constrained variables and constraints + +The solver interface should only implement support for constrained variables +or constraints that directly map to a structure exploited by the solver +algorithm. There is no need to add support for additional types, this is +handled by the [Automatic reformulation](@ref). Furthermore, this allows +[`supports_constraint`](@ref) to indicate which types are exploited by the +solver and hence allow layers such as [`Bridges.LazyBridgeOptimizer`](@ref) +to accurately select the most appropriate transformations. + +As [`add_constrained_variable`](@ref) (resp. [`add_constrained_variables`](@ref)) +falls back to [`add_variable`](@ref) (resp. [`add_variables`](@ref)) followed by +[`add_constraint`](@ref), there is no need to implement this function +if `model` supports creating free variables and supports transforming free +variables into variables in `set`. However, + +* if `model` does not support creating free variables, it should only implement + `add_constrained_variable` and not [`add_variable`](@ref) nor + [`add_constraint`](@ref) for [`SingleVariable`](@ref)-in-`typeof(set)`. + In addition, it should implement `supports_constraint(::Optimizer, + ::Type{VectorOfVariables}, ::Type{Reals})` and return `false` so that free + variables are bridged, see [`supports_constraint`](@ref). +* if `model` supports free variables but does not support transforming free + variables into variables in `set`, then it should implement both + [`add_variable`](@ref) and `add_constraint_variable` but should not implement + any method for [`add_constraint`](@ref) for + [`SingleVariable`](@ref)-in-`typeof(set)`. + ### Implementing copy Avoid storing extra copies of the problem when possible. This means that solver diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 9fa351a6a5..e07c6e5c1c 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -182,11 +182,25 @@ delete(::ModelLike, ::Index) ### Variables -Functions for adding variables. For deleting, see index types section. +*Free variables* are created with with [`add_variable`](@ref) or +[`add_variables`](@ref) and *constrained variables* are created with +[`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref). +Note that free variables can be constrained after being created using +[`add_constraint`](@ref) with the [`SingleVariable`](@ref) or +[`VectorOfVariables`](@ref). However, they need to be added with +[`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref) to +allow Variable bridges to be used. +Note further that free variables that are constrained after being created using +[`add_constraint`](@ref) will be copied by [`copy_to`](@ref) with +[`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref) by the +[`Utilities.CachingOptimizer`](@ref). +For deleting, see index types section. ```@docs -add_variables add_variable +add_variables +add_constrained_variable +add_constrained_variables ``` List of attributes associated with variables. [category AbstractVariableAttribute] diff --git a/src/Test/UnitTests/basic_constraint_tests.jl b/src/Test/UnitTests/basic_constraint_tests.jl index 0f099c2331..3456e22e7f 100644 --- a/src/Test/UnitTests/basic_constraint_tests.jl +++ b/src/Test/UnitTests/basic_constraint_tests.jl @@ -30,7 +30,6 @@ const BasicConstraintTests = Dict( (MOI.VectorOfVariables, MOI.SOS1{Float64}) => ( dummy_vectorofvariables, 2, MOI.SOS1([1.0, 2.0]) ), (MOI.VectorOfVariables, MOI.SOS2{Float64}) => ( dummy_vectorofvariables, 2, MOI.SOS2([1.0, 2.0]) ), - (MOI.VectorOfVariables, MOI.Reals) => ( dummy_vectorofvariables, 2, MOI.Reals(2) ), (MOI.VectorOfVariables, MOI.Zeros) => ( dummy_vectorofvariables, 2, MOI.Zeros(2) ), (MOI.VectorOfVariables, MOI.Nonpositives) => ( dummy_vectorofvariables, 2, MOI.Nonpositives(2) ), (MOI.VectorOfVariables, MOI.Nonnegatives) => ( dummy_vectorofvariables, 2, MOI.Nonnegatives(2) ), @@ -60,7 +59,6 @@ const BasicConstraintTests = Dict( (MOI.ScalarQuadraticFunction{Float64}, MOI.EqualTo{Float64}) => ( dummy_scalar_quadratic, 1, MOI.EqualTo(1.0) ), (MOI.ScalarQuadraticFunction{Float64}, MOI.Interval{Float64}) => ( dummy_scalar_quadratic, 1, MOI.Interval(1.0, 2.0) ), - (MOI.VectorAffineFunction{Float64}, MOI.Reals) => ( dummy_vector_affine, 2, MOI.Reals(2) ), (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => ( dummy_vector_affine, 2, MOI.Zeros(2) ), (MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => ( dummy_vector_affine, 2, MOI.Nonpositives(2) ), (MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => ( dummy_vector_affine, 2, MOI.Nonnegatives(2) ), @@ -68,7 +66,6 @@ const BasicConstraintTests = Dict( (MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => ( dummy_vector_affine, 3, MOI.SecondOrderCone(3) ), (MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => ( dummy_vector_affine, 3, MOI.RotatedSecondOrderCone(3) ), - (MOI.VectorQuadraticFunction{Float64}, MOI.Reals) => ( dummy_vector_quadratic, 2, MOI.Reals(2) ), (MOI.VectorQuadraticFunction{Float64}, MOI.Zeros) => ( dummy_vector_quadratic, 2, MOI.Zeros(2) ), (MOI.VectorQuadraticFunction{Float64}, MOI.Nonpositives) => ( dummy_vector_quadratic, 2, MOI.Nonpositives(2) ), (MOI.VectorQuadraticFunction{Float64}, MOI.Nonnegatives) => ( dummy_vector_quadratic, 2, MOI.Nonnegatives(2) ), diff --git a/src/constraints.jl b/src/constraints.jl index f288827410..98c548d6dd 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -8,10 +8,26 @@ is, `copy_to(model, src)` does not throw [`UnsupportedConstraint`](@ref) when `src` contains `F`-in-`S` constraints. If `F`-in-`S` constraints are only not supported in specific circumstances, e.g. `F`-in-`S` constraints cannot be combined with another type of constraint, it should still return `true`. -""" + + supports_constraint(model::ModelLike, ::Type{VectorOfVariables}, ::Type{Reals})::Bool + +Return a `Bool` indicating whether `model` supports free variables. That is, +`copy_to(model, src)` does error when `src` contains variables that are not +constrained by any [`SingleVariable`](@ref) or [`VectorOfVariables`](@ref) +constraint. By default, this method returns `true` so it should only be +implemented if `model` does not support free variables. + +Note that free variables are not explicitly set to be free by calling +[`add_constraint`](@ref) with the set [`Reals`](@ref), instead, free variables +are created with [`add_variable`](@ref) and [`add_variables`](@ref). +If `model` does not support free variables, it should not implement +[`add_variable`](@ref) nor [`add_variables`](@ref) but should implement +this method and return `false`. This allows free variables to be bridged as the +sum of a nonnegative and a nonpositive variables. +""" # Implemented as only one method to avoid ambiguity function supports_constraint(model::ModelLike, F::Type{<:AbstractFunction}, S::Type{<:AbstractSet}) - return false + return F == VectorOfVariables && S == Reals end """ diff --git a/src/variables.jl b/src/variables.jl index 0b8438596d..ba5c8e3fc5 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -33,3 +33,84 @@ A [`AddVariableNotAllowed`](@ref) error is thrown if adding variables cannot be done in the current state of the model `model`. """ add_variable(model::ModelLike) = throw(AddVariableNotAllowed()) + +""" + add_constrained_variable( + model::ModelLike, + set::AbstractScalarSet + )::Tuple{MOI.VariableIndex, + MOI.ConstraintIndex{MOI.SingleVariable, typeof(set)}} + +Add to `model` a scalar variable constrained to belong to `set`, returning the +index of the variable created and the index of the constraint constraining the +variable to belong to `set`. + +By default, this function falls back to creating a free variable with +[`add_variable`](@ref) and then constraining it to belong to `set` with +[`add_constraint`](@ref). +""" +function add_constrained_variable(model::ModelLike, set::AbstractScalarSet) + variable = add_variable(model) + constraint = add_constraint(model, SingleVariable(variable), set) + return variable, constraint +end + +""" + add_constrained_variables( + model::ModelLike, + sets:AbstractVector{<:AbstractScalarSet} + )::Tuple{Vector{MOI.VariableIndex}, + Vector{MOI.ConstraintIndex{MOI.SingleVariable, eltype(sets)}}} + +Add to `model` scalar variables constrained to belong to `sets`, returning the +indices of the variables created and the indices of the constraints constraining +the variables to belong to each set in `sets`. That is, if it returns `variables` +and `constraints`, `constraints[i]` is the index of the constraint constraining +`variable[i]` to belong to `sets[i]`. + +By default, this function falls back to calling +[`add_constrained_variable`](@ref) on each set. +""" +function add_constrained_variables(model::ModelLike, sets::AbstractVector{<:AbstractScalarSet}) + variables = Vector{VariableIndex}(undef, length(sets)) + constraints = Vector{ConstraintIndex{SingleVariable, eltype(sets)}}(undef, length(sets)) + for (i, set) in enumerate(sets) + variables[i], constraints[i] = add_constrained_variable(model, set) + end + return variables, constraints +end + +""" + add_constrained_variables( + model::ModelLike, + set::AbstractVectorSet + )::Tuple{Vector{MOI.VariableIndex}, + MOI.ConstraintIndex{MOI.VectorOfVariables, typeof(set)}} + +Add to `model` a vector of variables constrained to belong to `set`, returning +the indices of the variables created and the index of the constraint +constraining the vector of variables to belong to `set`. + +By default, this function falls back to creating free variables with +[`add_variables`](@ref) and then constraining it to belong to `set` with +[`add_constraint`](@ref). Therefore, there is no need to implement this function +if `model` supports creating free variables and supports transforming free +variables into variables in `set`. However, + +* if `model` does not support creating variables, it should only implement + `add_constrained_variables` and not [`add_variable`](@ref) nor + [`add_constraint`](@ref) for [`VectorOfVariables`](@ref)-in-`typeof(set)`. + In addition, it should implement `supports_constraint(::Optimizer, + ::Type{VectorOfVariables}, ::Type{Reals})` and return `false` so that free + variables are bridged, see [`supports_constraint`](@ref). +* if `model` supports free variables but does not support transforming free + variables into variables in `set`, then it should implement both + [`add_variable`](@ref) and `add_constraint_variables` but should not implement + any method for [`add_constraint`](@ref) for + [`VectorOfVariables`](@ref)-in-`typeof(set)`. +""" +function add_constrained_variables(model::ModelLike, set::AbstractVectorSet) + variables = add_variables(model, dimension(set)) + constraint = add_constraint(model, VectorOfVariables(variables), set) + return variables, constraint +end From 3ba9e2ced52639b471b58c69468da9de0eca9576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 18 Jul 2019 21:19:08 -0600 Subject: [PATCH 2/7] Address comments --- docs/src/apimanual.md | 24 +++++++++--------------- src/variables.jl | 16 +--------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/docs/src/apimanual.md b/docs/src/apimanual.md index dbb9cf52e3..8e96fba67f 100644 --- a/docs/src/apimanual.md +++ b/docs/src/apimanual.md @@ -971,30 +971,24 @@ MOI.set(model, MyPackage.PrintLevel(), 0) ### Supported constrained variables and constraints The solver interface should only implement support for constrained variables +(see [`add_constrained_variable`](@ref)/[`add_constrained_variable`](@ref)) or constraints that directly map to a structure exploited by the solver algorithm. There is no need to add support for additional types, this is handled by the [Automatic reformulation](@ref). Furthermore, this allows [`supports_constraint`](@ref) to indicate which types are exploited by the -solver and hence allow layers such as [`Bridges.LazyBridgeOptimizer`](@ref) +solver and hence allows layers such as [`Bridges.LazyBridgeOptimizer`](@ref) to accurately select the most appropriate transformations. As [`add_constrained_variable`](@ref) (resp. [`add_constrained_variables`](@ref)) falls back to [`add_variable`](@ref) (resp. [`add_variables`](@ref)) followed by [`add_constraint`](@ref), there is no need to implement this function -if `model` supports creating free variables and supports transforming free -variables into variables in `set`. However, - -* if `model` does not support creating free variables, it should only implement - `add_constrained_variable` and not [`add_variable`](@ref) nor - [`add_constraint`](@ref) for [`SingleVariable`](@ref)-in-`typeof(set)`. - In addition, it should implement `supports_constraint(::Optimizer, - ::Type{VectorOfVariables}, ::Type{Reals})` and return `false` so that free - variables are bridged, see [`supports_constraint`](@ref). -* if `model` supports free variables but does not support transforming free - variables into variables in `set`, then it should implement both - [`add_variable`](@ref) and `add_constraint_variable` but should not implement - any method for [`add_constraint`](@ref) for - [`SingleVariable`](@ref)-in-`typeof(set)`. +if `model` supports creating free variables. However, if `model` does not +support creating free variables, then it should only implement +[`add_constrained_variable`](@ref) and not [`add_variable`](@ref) nor +[`add_constraint`](@ref) for [`SingleVariable`](@ref)-in-`typeof(set)`. +In addition, it should implement `supports_constraint(::Optimizer, +::Type{VectorOfVariables}, ::Type{Reals})` and return `false` so that free +variables are bridged, see [`supports_constraint`](@ref). ### Implementing copy diff --git a/src/variables.jl b/src/variables.jl index ba5c8e3fc5..f37f9e49a4 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -93,21 +93,7 @@ constraining the vector of variables to belong to `set`. By default, this function falls back to creating free variables with [`add_variables`](@ref) and then constraining it to belong to `set` with -[`add_constraint`](@ref). Therefore, there is no need to implement this function -if `model` supports creating free variables and supports transforming free -variables into variables in `set`. However, - -* if `model` does not support creating variables, it should only implement - `add_constrained_variables` and not [`add_variable`](@ref) nor - [`add_constraint`](@ref) for [`VectorOfVariables`](@ref)-in-`typeof(set)`. - In addition, it should implement `supports_constraint(::Optimizer, - ::Type{VectorOfVariables}, ::Type{Reals})` and return `false` so that free - variables are bridged, see [`supports_constraint`](@ref). -* if `model` supports free variables but does not support transforming free - variables into variables in `set`, then it should implement both - [`add_variable`](@ref) and `add_constraint_variables` but should not implement - any method for [`add_constraint`](@ref) for - [`VectorOfVariables`](@ref)-in-`typeof(set)`. +[`add_constraint`](@ref). """ function add_constrained_variables(model::ModelLike, set::AbstractVectorSet) variables = add_variables(model, dimension(set)) From 1c04d5651f2a33e28800c517dd04bc9e2ed1ddb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 18 Jul 2019 21:45:45 -0600 Subject: [PATCH 3/7] Address comments --- docs/src/apimanual.md | 2 +- docs/src/apireference.md | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/src/apimanual.md b/docs/src/apimanual.md index 8e96fba67f..0bf04755d0 100644 --- a/docs/src/apimanual.md +++ b/docs/src/apimanual.md @@ -971,7 +971,7 @@ MOI.set(model, MyPackage.PrintLevel(), 0) ### Supported constrained variables and constraints The solver interface should only implement support for constrained variables -(see [`add_constrained_variable`](@ref)/[`add_constrained_variable`](@ref)) +(see [`add_constrained_variable`](@ref)/[`add_constrained_variables`](@ref)) or constraints that directly map to a structure exploited by the solver algorithm. There is no need to add support for additional types, this is handled by the [Automatic reformulation](@ref). Furthermore, this allows diff --git a/docs/src/apireference.md b/docs/src/apireference.md index e07c6e5c1c..76b2ff2bd4 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -182,16 +182,17 @@ delete(::ModelLike, ::Index) ### Variables -*Free variables* are created with with [`add_variable`](@ref) or -[`add_variables`](@ref) and *constrained variables* are created with -[`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref). -Note that free variables can be constrained after being created using +*Free variables* are the variables created with [`add_variable`](@ref) or +[`add_variables`](@ref) and never constrained while *constrained variables* are +the variables either created with [`add_constrained_variable`](@ref) or +[`add_constrained_variables`](@ref) or created with [`add_variable`](@ref) or +[`add_variables`](@ref) and constrained after being created using [`add_constraint`](@ref) with the [`SingleVariable`](@ref) or [`VectorOfVariables`](@ref). However, they need to be added with [`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref) to allow Variable bridges to be used. -Note further that free variables that are constrained after being created using -[`add_constraint`](@ref) will be copied by [`copy_to`](@ref) with +Note further that variables that are constrained after being created using +[`add_constraint`](@ref) may be copied by [`copy_to`](@ref) with [`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref) by the [`Utilities.CachingOptimizer`](@ref). For deleting, see index types section. From 9d8f0d505fd13c8fe7e162990d08082e78216068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Jul 2019 17:22:49 -0600 Subject: [PATCH 4/7] Address comments --- docs/src/apireference.md | 8 +++++++- src/constraints.jl | 6 ++++-- src/variables.jl | 6 ++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 76b2ff2bd4..70ffd609aa 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -195,7 +195,13 @@ Note further that variables that are constrained after being created using [`add_constraint`](@ref) may be copied by [`copy_to`](@ref) with [`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref) by the [`Utilities.CachingOptimizer`](@ref). -For deleting, see index types section. +More precisely, the attributes do not distinguish constraints on variables +created with `add_constrained_variable(s)` or `add_variable(s)`/`add_constraint`. +When the model is copied, if a variable is constrained in several sets, +the implementation of [`copy_to`](@ref) can determine whether it is added +using [`add_variable`](@ref) or [`add_constrained_variable`](@ref) with one +of the sets. The rest of the constraints on the variables are added +with [`add_constraint`](@ref). For deleting, see [Index types](@ref). ```@docs add_variable diff --git a/src/constraints.jl b/src/constraints.jl index 98c548d6dd..90450983d2 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -12,10 +12,12 @@ combined with another type of constraint, it should still return `true`. supports_constraint(model::ModelLike, ::Type{VectorOfVariables}, ::Type{Reals})::Bool Return a `Bool` indicating whether `model` supports free variables. That is, -`copy_to(model, src)` does error when `src` contains variables that are not +`copy_to(model, src)` does not error when `src` contains variables that are not constrained by any [`SingleVariable`](@ref) or [`VectorOfVariables`](@ref) constraint. By default, this method returns `true` so it should only be -implemented if `model` does not support free variables. +implemented if `model` does not support free variables. For instance, if a +solver requires all variables to be nonnegative, it should implement this +method and return `false` free variables cannot be copied to the solver. Note that free variables are not explicitly set to be free by calling [`add_constraint`](@ref) with the set [`Reals`](@ref), instead, free variables diff --git a/src/variables.jl b/src/variables.jl index f37f9e49a4..7d6c13c71e 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -47,7 +47,8 @@ variable to belong to `set`. By default, this function falls back to creating a free variable with [`add_variable`](@ref) and then constraining it to belong to `set` with -[`add_constraint`](@ref). +[`add_constraint`](@ref), which turns the free variable into a constrained +variable. """ function add_constrained_variable(model::ModelLike, set::AbstractScalarSet) variable = add_variable(model) @@ -93,7 +94,8 @@ constraining the vector of variables to belong to `set`. By default, this function falls back to creating free variables with [`add_variables`](@ref) and then constraining it to belong to `set` with -[`add_constraint`](@ref). +[`add_constraint`](@ref), which turns the free variables into constrained +variables. """ function add_constrained_variables(model::ModelLike, set::AbstractVectorSet) variables = add_variables(model, dimension(set)) From ab0bc7fb388c56147db29d5ccc053ee7f96bbd89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Jul 2019 17:55:11 -0600 Subject: [PATCH 5/7] Add missing because --- src/constraints.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constraints.jl b/src/constraints.jl index 90450983d2..36ca0fbe22 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -17,7 +17,7 @@ constrained by any [`SingleVariable`](@ref) or [`VectorOfVariables`](@ref) constraint. By default, this method returns `true` so it should only be implemented if `model` does not support free variables. For instance, if a solver requires all variables to be nonnegative, it should implement this -method and return `false` free variables cannot be copied to the solver. +method and return `false` because free variables cannot be copied to the solver. Note that free variables are not explicitly set to be free by calling [`add_constraint`](@ref) with the set [`Reals`](@ref), instead, free variables From b7709629dc68560e62ff14093a3c3b34b103850d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 20 Jul 2019 17:52:43 -0600 Subject: [PATCH 6/7] Change def of constrained variables --- docs/src/apireference.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 70ffd609aa..4b0968fcc6 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -183,15 +183,12 @@ delete(::ModelLike, ::Index) ### Variables *Free variables* are the variables created with [`add_variable`](@ref) or -[`add_variables`](@ref) and never constrained while *constrained variables* are -the variables either created with [`add_constrained_variable`](@ref) or -[`add_constrained_variables`](@ref) or created with [`add_variable`](@ref) or -[`add_variables`](@ref) and constrained after being created using -[`add_constraint`](@ref) with the [`SingleVariable`](@ref) or -[`VectorOfVariables`](@ref). However, they need to be added with -[`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref) to -allow Variable bridges to be used. -Note further that variables that are constrained after being created using +[`add_variables`](@ref) while *constrained variables* are +the variables created with [`add_constrained_variable`](@ref) or +[`add_constrained_variables`](@ref). Adding constrained variables instead of +constraining free variables with [`add_constraint`](@ref) allows Variable +bridges to be used. +Note further that free variables that are constrained with [`add_constraint`](@ref) may be copied by [`copy_to`](@ref) with [`add_constrained_variable`](@ref) or [`add_constrained_variables`](@ref) by the [`Utilities.CachingOptimizer`](@ref). From f791021e1c17820063360547c39687c14f9d6917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 20 Jul 2019 23:26:13 -0600 Subject: [PATCH 7/7] Remove outdated desc --- src/variables.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/variables.jl b/src/variables.jl index 7d6c13c71e..f37f9e49a4 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -47,8 +47,7 @@ variable to belong to `set`. By default, this function falls back to creating a free variable with [`add_variable`](@ref) and then constraining it to belong to `set` with -[`add_constraint`](@ref), which turns the free variable into a constrained -variable. +[`add_constraint`](@ref). """ function add_constrained_variable(model::ModelLike, set::AbstractScalarSet) variable = add_variable(model) @@ -94,8 +93,7 @@ constraining the vector of variables to belong to `set`. By default, this function falls back to creating free variables with [`add_variables`](@ref) and then constraining it to belong to `set` with -[`add_constraint`](@ref), which turns the free variables into constrained -variables. +[`add_constraint`](@ref). """ function add_constrained_variables(model::ModelLike, set::AbstractVectorSet) variables = add_variables(model, dimension(set))