From 767d52160ddeb671db4895684c924b01bfdcea03 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 3 May 2024 12:11:03 +1200 Subject: [PATCH 1/5] Support vector-valued constraints in normalized_coefficient --- src/variables.jl | 27 ++++++++++++++++++++++++++- test/test_variable.jl | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/variables.jl b/src/variables.jl index 79be0447fc1..2079b4d9447 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -2801,7 +2801,7 @@ end Return the coefficient associated with `variable` in `constraint` after JuMP has normalized the constraint into its standard form. -See also [`set_normalized_coefficient`](@ref). +See also [`set_normalized_coefficient`](@ref) and [`set_normalized_coefficients`](@ref).. ## Example @@ -2816,6 +2816,15 @@ con : 5 x ≤ 2 julia> normalized_coefficient(con, x) 5.0 + +julia> @constraint(model, con_vec, [x, 2x + 1, 3] >= 0) +[x, 2 x + 1, 3] ∈ MathOptInterface.Nonnegatives(3) + +julia> normalized_coefficient(con_vec, x) +3-element Vector{Float64}: + 1.0 + 2.0 + 0.0 ``` """ function normalized_coefficient( @@ -2825,6 +2834,13 @@ function normalized_coefficient( return coefficient(constraint_object(constraint).func, variable) end +function normalized_coefficient( + constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, + variable::AbstractVariableRef, +) where {F<:Union{MOI.VectorAffineFunction,MOI.VectorQuadraticFunction}} + return coefficient.(constraint_object(constraint).func, variable) +end + """ normalized_coefficient( constraint::ConstraintRef, @@ -2863,6 +2879,15 @@ function normalized_coefficient( return coefficient(con.func, variable_1, variable_2) end +function normalized_coefficient( + constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, + variable_1::AbstractVariableRef, + variable_2::AbstractVariableRef, +) where {F<:MOI.VectorQuadraticFunction} + con = constraint_object(constraint) + return coefficient.(con.func, variable_1, variable_2) +end + ### ### Error messages for common incorrect usages ### diff --git a/test/test_variable.jl b/test/test_variable.jl index 651e9db71ee..d2cf63aff47 100644 --- a/test/test_variable.jl +++ b/test/test_variable.jl @@ -1626,4 +1626,27 @@ function test_variable_one() return end +function test_variable_normalized_coefficient_vector() + model = Model() + @variable(model, x[1:3]) + A = [4 5 0; 0 6 7; 8 0 0; 9 10 11] + @constraint(model, c, A * x >= 0) + for i in 1:3 + @test normalized_coefficient(c, x[i]) == A[:, i] + end + return +end + +function test_variable_normalized_coefficient_vector_quadratic() + model = Model() + @variable(model, x[1:3]) + A = [4 5 0; 0 6 7; 8 0 0; 9 10 11] + @constraint(model, c, A * x.^2 >= 0) + for i in 1:3, j in 1:3 + b = ifelse(i == j, A[:, i], zeros(4)) + @test normalized_coefficient(c, x[i], x[j]) == b + end + return +end + end # module TestVariable From 46e6c1fd594499cc2f93062f551d82cdcb3442fb Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 3 May 2024 13:02:42 +1200 Subject: [PATCH 2/5] Update --- src/variables.jl | 2 +- test/test_variable.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/variables.jl b/src/variables.jl index 2079b4d9447..cfaa3bd4ab9 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -2818,7 +2818,7 @@ julia> normalized_coefficient(con, x) 5.0 julia> @constraint(model, con_vec, [x, 2x + 1, 3] >= 0) -[x, 2 x + 1, 3] ∈ MathOptInterface.Nonnegatives(3) +con_vec : [x, 2 x + 1, 3] ∈ MathOptInterface.Nonnegatives(3) julia> normalized_coefficient(con_vec, x) 3-element Vector{Float64}: diff --git a/test/test_variable.jl b/test/test_variable.jl index d2cf63aff47..42cbca4bdf6 100644 --- a/test/test_variable.jl +++ b/test/test_variable.jl @@ -1641,7 +1641,7 @@ function test_variable_normalized_coefficient_vector_quadratic() model = Model() @variable(model, x[1:3]) A = [4 5 0; 0 6 7; 8 0 0; 9 10 11] - @constraint(model, c, A * x.^2 >= 0) + @constraint(model, c, A * (x .^ 2) >= 0) for i in 1:3, j in 1:3 b = ifelse(i == j, A[:, i], zeros(4)) @test normalized_coefficient(c, x[i], x[j]) == b From 3642b465c59b9e61d79c826a9192b3a298939b35 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 6 May 2024 16:13:43 +1200 Subject: [PATCH 3/5] Deprecate set_normalized_coefficients --- docs/src/manual/constraints.md | 6 +++--- src/variables.jl | 24 ++++++++++++++++++++---- test/test_constraint.jl | 12 ++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/docs/src/manual/constraints.md b/docs/src/manual/constraints.md index 2462ed0ce5e..f03d0805023 100644 --- a/docs/src/manual/constraints.md +++ b/docs/src/manual/constraints.md @@ -843,7 +843,7 @@ julia> normalized_coefficient(con, x[1], x[2]) ### Vector constraints To modify the coefficients of a vector-valued constraint, use -[`set_normalized_coefficients`](@ref). +[`set_normalized_coefficient`](@ref). ```jldoctest julia> model = Model(); @@ -853,12 +853,12 @@ x julia> @constraint(model, con, [2x + 3x, 4x] in MOI.Nonnegatives(2)) con : [5 x, 4 x] ∈ MathOptInterface.Nonnegatives(2) -julia> set_normalized_coefficients(con, x, [(1, 3.0)]) +julia> set_normalized_coefficient(con, x, [(1, 3.0)]) julia> con con : [3 x, 4 x] ∈ MathOptInterface.Nonnegatives(2) -julia> set_normalized_coefficients(con, x, [(1, 2.0), (2, 5.0)]) +julia> set_normalized_coefficient(con, x, [(1, 2.0), (2, 5.0)]) julia> con con : [2 x, 5 x] ∈ MathOptInterface.Nonnegatives(2) diff --git a/src/variables.jl b/src/variables.jl index cfaa3bd4ab9..435cdbdda44 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -2624,7 +2624,7 @@ function set_normalized_coefficient( end """ - set_normalized_coefficients( + set_normalized_coefficient( con_ref::ConstraintRef, variable::AbstractVariableRef, new_coefficients::Vector{Tuple{Int64,T}}, @@ -2648,13 +2648,13 @@ x julia> @constraint(model, con, [2x + 3x, 4x] in MOI.Nonnegatives(2)) con : [5 x, 4 x] ∈ MathOptInterface.Nonnegatives(2) -julia> set_normalized_coefficients(con, x, [(1, 2.0), (2, 5.0)]) +julia> set_normalized_coefficient(con, x, [(1, 2.0), (2, 5.0)]) julia> con con : [2 x, 5 x] ∈ MathOptInterface.Nonnegatives(2) ``` """ -function set_normalized_coefficients( +function set_normalized_coefficient( constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, variable::AbstractVariableRef, new_coefficients::Vector{Tuple{Int64,T}}, @@ -2669,6 +2669,22 @@ function set_normalized_coefficients( return end +""" + set_normalized_coefficients( + constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, + variable::AbstractVariableRef, + new_coefficients::Vector{Tuple{Int64,T}}, + ) where {T,F<:Union{MOI.VectorAffineFunction{T},MOI.VectorQuadraticFunction{T}}} + +A deprecated method that now redirects to [`set_normalized_coefficient`](@ref). +""" +function set_normalized_coefficients( + constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, + variable::AbstractVariableRef, + new_coefficients::Vector{Tuple{Int64,T}}, +) where {T,F<:Union{MOI.VectorAffineFunction{T},MOI.VectorQuadraticFunction{T}}} + return set_normalized_coefficient(constraint, variable, new_coefficients) +end """ set_normalized_coefficient( constraint::ConstraintRef, @@ -2801,7 +2817,7 @@ end Return the coefficient associated with `variable` in `constraint` after JuMP has normalized the constraint into its standard form. -See also [`set_normalized_coefficient`](@ref) and [`set_normalized_coefficients`](@ref).. +See also [`set_normalized_coefficient`](@ref). ## Example diff --git a/test/test_constraint.jl b/test/test_constraint.jl index 08c89342fbc..c466f632bdc 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1009,6 +1009,18 @@ function test_change_coefficient_batch() end function test_change_coefficients_vector_function() + model = Model() + @variable(model, x) + @constraint(model, con, [2x + 3x, 4x] in MOI.Nonnegatives(2)) + @test isequal_canonical(constraint_object(con).func, [5.0x, 4.0x]) + set_normalized_coefficient(con, x, [(Int64(1), 3.0)]) + @test isequal_canonical(constraint_object(con).func, [3.0x, 4.0x]) + set_normalized_coefficient(con, x, [(Int64(1), 2.0), (Int64(2), 5.0)]) + @test isequal_canonical(constraint_object(con).func, [2.0x, 5.0x]) + return +end + +function test_change_coefficients_vector_function_DEPRECATED() model = Model() @variable(model, x) @constraint(model, con, [2x + 3x, 4x] in MOI.Nonnegatives(2)) From 130b7227d4711acece690b16f875a4b104f937c6 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 8 May 2024 10:23:41 +1200 Subject: [PATCH 4/5] Update --- src/variables.jl | 40 +++++++++++++++++++++++++++++++--------- test/test_variable.jl | 10 ++++++---- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/variables.jl b/src/variables.jl index 435cdbdda44..5803509cb2e 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -2837,10 +2837,9 @@ julia> @constraint(model, con_vec, [x, 2x + 1, 3] >= 0) con_vec : [x, 2 x + 1, 3] ∈ MathOptInterface.Nonnegatives(3) julia> normalized_coefficient(con_vec, x) -3-element Vector{Float64}: - 1.0 - 2.0 - 0.0 +2-element Vector{Tuple{Int64, Float64}}: + (1, 1.0) + (2, 2.0) ``` """ function normalized_coefficient( @@ -2853,8 +2852,15 @@ end function normalized_coefficient( constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, variable::AbstractVariableRef, -) where {F<:Union{MOI.VectorAffineFunction,MOI.VectorQuadraticFunction}} - return coefficient.(constraint_object(constraint).func, variable) +) where {T,F<:Union{MOI.VectorAffineFunction{T},MOI.VectorQuadraticFunction{T}}} + ret = Tuple{Int,T}[] + for (i, fi) in enumerate(constraint_object(constraint).func) + c = coefficient(fi, variable) + if !iszero(c) + push!(ret, (i, c)) + end + end + return ret end """ @@ -2884,6 +2890,16 @@ julia> normalized_coefficient(con, x[1], x[1]) julia> normalized_coefficient(con, x[1], x[2]) 3.0 + +julia> @constraint(model, con_vec, x.^2 <= [1, 2]) +con_vec : [x[1]² - 1, x[2]² - 2] ∈ MathOptInterface.Nonpositives(2) + +julia> normalized_coefficient(con_vec, x[1], x[1]) +1-element Vector{Tuple{Int64, Float64}}: + (1, 1.0) + +julia> normalized_coefficient(con_vec, x[1], x[2]) +Tuple{Int64, Float64}[] ``` """ function normalized_coefficient( @@ -2899,9 +2915,15 @@ function normalized_coefficient( constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, variable_1::AbstractVariableRef, variable_2::AbstractVariableRef, -) where {F<:MOI.VectorQuadraticFunction} - con = constraint_object(constraint) - return coefficient.(con.func, variable_1, variable_2) +) where {T,F<:MOI.VectorQuadraticFunction{T}} + ret = Tuple{Int,T}[] + for (i, fi) in enumerate(constraint_object(constraint).func) + c = coefficient(fi, variable_1, variable_2) + if !iszero(c) + push!(ret, (i, c)) + end + end + return ret end ### diff --git a/test/test_variable.jl b/test/test_variable.jl index 42cbca4bdf6..e744ae85723 100644 --- a/test/test_variable.jl +++ b/test/test_variable.jl @@ -1629,10 +1629,11 @@ end function test_variable_normalized_coefficient_vector() model = Model() @variable(model, x[1:3]) - A = [4 5 0; 0 6 7; 8 0 0; 9 10 11] + A = Float64[4 5 0; 0 6 7; 8 0 0; 9 10 11] + sparse(y) = filter!(!iszero ∘ last, collect(enumerate(y))) @constraint(model, c, A * x >= 0) for i in 1:3 - @test normalized_coefficient(c, x[i]) == A[:, i] + @test normalized_coefficient(c, x[i]) == sparse(A[:, i]) end return end @@ -1640,11 +1641,12 @@ end function test_variable_normalized_coefficient_vector_quadratic() model = Model() @variable(model, x[1:3]) - A = [4 5 0; 0 6 7; 8 0 0; 9 10 11] + A = Float64[4 5 0; 0 6 7; 8 0 0; 9 10 11] + sparse(y) = filter!(!iszero ∘ last, collect(enumerate(y))) @constraint(model, c, A * (x .^ 2) >= 0) for i in 1:3, j in 1:3 b = ifelse(i == j, A[:, i], zeros(4)) - @test normalized_coefficient(c, x[i], x[j]) == b + @test normalized_coefficient(c, x[i], x[j]) == sparse(b) end return end From c3b4c3cff71fc945ad69ec7eee66bc67453db4ec Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 8 May 2024 10:26:23 +1200 Subject: [PATCH 5/5] Simplify --- src/variables.jl | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/variables.jl b/src/variables.jl index 5803509cb2e..ddd73f9a6b1 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -2853,14 +2853,8 @@ function normalized_coefficient( constraint::ConstraintRef{<:AbstractModel,<:MOI.ConstraintIndex{F}}, variable::AbstractVariableRef, ) where {T,F<:Union{MOI.VectorAffineFunction{T},MOI.VectorQuadraticFunction{T}}} - ret = Tuple{Int,T}[] - for (i, fi) in enumerate(constraint_object(constraint).func) - c = coefficient(fi, variable) - if !iszero(c) - push!(ret, (i, c)) - end - end - return ret + c = coefficient.(constraint_object(constraint).func, variable) + return filter!(!iszero ∘ last, collect(enumerate(c))) end """ @@ -2916,14 +2910,9 @@ function normalized_coefficient( variable_1::AbstractVariableRef, variable_2::AbstractVariableRef, ) where {T,F<:MOI.VectorQuadraticFunction{T}} - ret = Tuple{Int,T}[] - for (i, fi) in enumerate(constraint_object(constraint).func) - c = coefficient(fi, variable_1, variable_2) - if !iszero(c) - push!(ret, (i, c)) - end - end - return ret + f = constraint_object(constraint).func + c = coefficient.(f, variable_1, variable_2) + return filter!(!iszero ∘ last, collect(enumerate(c))) end ###