From 49225eae72f20ab8d33ef36230b63d2146ec660a Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 4 Jun 2024 10:10:55 +1200 Subject: [PATCH 01/10] Add set inequality syntax for symmetric matrices --- docs/src/manual/constraints.md | 38 ++++++++++++++++++++++++++++++++++ src/sd.jl | 32 ++++++++++++++++++++++++++-- test/test_constraint.jl | 20 ++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/docs/src/manual/constraints.md b/docs/src/manual/constraints.md index 4eb08ee188e..367f0f2a1e8 100644 --- a/docs/src/manual/constraints.md +++ b/docs/src/manual/constraints.md @@ -1474,6 +1474,44 @@ julia> @constraint(model, LinearAlgebra.Symmetric(X) >= 0, PSDCone()) ``` (Note that no error is thrown, even though `X` is not symmetric.) +## Symmetric matrices + +Inequalities between symmetric matrices are not supported, due to the common +ambiguity between elementwise inequalities and a [`PSDCone`](@ref) constraint. +```jldoctest symmetric_matrix +julia> model = Model(); + +julia> @variable(model, x[1:2, 1:2], Symmetric); + +julia> @variable(model, y[1:2, 1:2], Symmetric); + +julia> @constraint(model, x >= y) +ERROR: At REPL[91]:1: `@constraint(model, x >= y)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.>=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X >= 0, PSDCone())` or `@constraint(model, X >= 0, HermmitianPSDCone())`. +Stacktrace: +[...] +``` + +Instead, use the [Set inequality syntax](@ref) to specify either a [`PSDCone`](@ref), +or a elementwise inequality set like [`Nonnegatives`](@ref): + +```jldoctest symmetric_matrix +julia> @constraint(model, x >= y, PSDCone()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ PSDCone() + +julia> @constraint(model, x >= y, Nonnegatives()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonnegatives() + +julia> @constraint(model, x >= y, Nonpositives()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonpositives() + +julia> @constraint(model, x >= y, Zeros()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Zeros() +``` + ## Complementarity constraints A mixed complementarity constraint `F(x) ⟂ x` consists of finding `x` in the diff --git a/src/sd.jl b/src/sd.jl index 5457884faf3..c3e396da757 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -570,8 +570,6 @@ function build_constraint( return VectorConstraint(x, MOI.Zeros(length(x)), shape) end -reshape_set(::MOI.Zeros, ::SymmetricMatrixShape) = Zeros() - function build_constraint(error_fn::Function, ::AbstractMatrix, ::Nonnegatives) return error_fn( "Unsupported matrix in vector-valued set. Did you mean to use the " * @@ -657,3 +655,33 @@ function build_variable( x = _vectorize_variables(error_fn, variables) return VariablesConstrainedOnCreation(x, set, SymmetricMatrixShape(n)) end + +moi_set(::Nonnegatives, dim::Int) = MOI.Nonnegatives(dim) +moi_set(::Nonpositives, dim::Int) = MOI.Nonpositives(dim) +moi_set(::Zeros, dim::Int) = MOI.Zeros(dim) + +reshape_set(::MOI.Nonnegatives, ::SymmetricMatrixShape) = Nonnegatives() +reshape_set(::MOI.Nonpositives, ::SymmetricMatrixShape) = Nonpositives() +reshape_set(::MOI.Zeros, ::SymmetricMatrixShape) = Zeros() + +function build_constraint( + error_fn::Function, + f::LinearAlgebra.Symmetric, + ::Nonnegatives, + set::Union{Nonnegatives,Nonpositives,Zeros}, +) + shape = SymmetricMatrixShape(size(f, 1)) + x = vectorize(f, shape) + return VectorConstraint(x, moi_set(set, length(x)), shape) +end + +function build_constraint( + error_fn::Function, + f::LinearAlgebra.Symmetric, + ::Nonpositives, + set::Union{Nonnegatives,Nonpositives,Zeros}, +) + shape = SymmetricMatrixShape(size(f, 1)) + x = -vectorize(f, shape) + return VectorConstraint(x, moi_set(set, length(x)), shape) +end diff --git a/test/test_constraint.jl b/test/test_constraint.jl index c466f632bdc..2c8d87ed164 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1887,4 +1887,24 @@ function test_set_normalized_coefficient_quadratic_batch() return end +function test_symmetric_matrix_inequality() + model = Model() + @variable(model, x[1:2, 1:2], Symmetric) + @variable(model, y[1:2, 1:2], Symmetric) + g = [x[1, 1] - y[1, 1], x[1, 2] - y[1, 2], x[2, 2] - y[2, 2]] + for set in (Nonnegatives(), Nonpositives(), Zeros()) + c = @constraint(model, x >= y, set) + o = constraint_object(c) + @test isequal_canonical(o.func, g) + @test o.set == moi_set(set, 3) + @test o.shape == SymmetricMatrixShape(2) + c = @constraint(model, x <= y, set) + o = constraint_object(c) + @test isequal_canonical(o.func, -g) + @test o.set == moi_set(set, 3) + @test o.shape == SymmetricMatrixShape(2) + end + return +end + end # module From 6ed04ac1633101798d5bfa77d5d6079ec442b9a2 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 4 Jun 2024 10:41:01 +1200 Subject: [PATCH 02/10] Update docs/src/manual/constraints.md --- docs/src/manual/constraints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/manual/constraints.md b/docs/src/manual/constraints.md index 367f0f2a1e8..5098a48205a 100644 --- a/docs/src/manual/constraints.md +++ b/docs/src/manual/constraints.md @@ -1486,7 +1486,7 @@ julia> @variable(model, x[1:2, 1:2], Symmetric); julia> @variable(model, y[1:2, 1:2], Symmetric); julia> @constraint(model, x >= y) -ERROR: At REPL[91]:1: `@constraint(model, x >= y)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.>=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X >= 0, PSDCone())` or `@constraint(model, X >= 0, HermmitianPSDCone())`. +ERROR: At none:1: `@constraint(model, x >= y)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.>=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X >= 0, PSDCone())` or `@constraint(model, X >= 0, HermmitianPSDCone())`. Stacktrace: [...] ``` From d19d2fa0b31408e952fefb97ed144cc36fd08987 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 4 Jun 2024 11:14:26 +1200 Subject: [PATCH 03/10] Update test_constraint.jl --- test/test_constraint.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_constraint.jl b/test/test_constraint.jl index 2c8d87ed164..70307f91d2f 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1898,11 +1898,13 @@ function test_symmetric_matrix_inequality() @test isequal_canonical(o.func, g) @test o.set == moi_set(set, 3) @test o.shape == SymmetricMatrixShape(2) + @test reshape_set(o.set, o.shape) == set c = @constraint(model, x <= y, set) o = constraint_object(c) @test isequal_canonical(o.func, -g) @test o.set == moi_set(set, 3) @test o.shape == SymmetricMatrixShape(2) + @test reshape_set(o.set, o.shape) == set end return end From 4ba6c82b84359ed3bd9527a23c3411d004fe3298 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 4 Jun 2024 13:13:48 +1200 Subject: [PATCH 04/10] Update accept.txt --- docs/styles/config/vocabularies/JuMP/accept.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/styles/config/vocabularies/JuMP/accept.txt b/docs/styles/config/vocabularies/JuMP/accept.txt index ea2bdaf925f..900f3eb8deb 100644 --- a/docs/styles/config/vocabularies/JuMP/accept.txt +++ b/docs/styles/config/vocabularies/JuMP/accept.txt @@ -19,6 +19,7 @@ discretize(d|s) docstring(?s) doctest(?s) embeddable +elementwise [Ee]num(?s) errored flamegraph From 31d1ba023db9aaae0b7d244e2f2af2c20745511d Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 5 Jun 2024 14:23:44 +1200 Subject: [PATCH 05/10] Update --- docs/src/manual/constraints.md | 81 +++++++++++++--------------------- src/sd.jl | 30 +++++++++++++ src/shapes.jl | 28 ++++++++++++ 3 files changed, 89 insertions(+), 50 deletions(-) diff --git a/docs/src/manual/constraints.md b/docs/src/manual/constraints.md index 5098a48205a..75df9f219dc 100644 --- a/docs/src/manual/constraints.md +++ b/docs/src/manual/constraints.md @@ -164,27 +164,46 @@ julia> @constraint(model, A * x .>= b) 3 x[1] + 4 x[2] ≥ 6 ``` -### Vectorized matrix constraints +## Matrix inequalities -In most cases, you cannot use the non-broadcasting syntax for general matrices. -For example: +Inequalities between matrices are not supported, due to the common ambiguity +between elementwise inequalities and a [`PSDCone`](@ref) constraint. -```jldoctest +```jldoctest symmetric_matrix julia> model = Model(); -julia> @variable(model, X[1:2, 1:2]) -2×2 Matrix{VariableRef}: - X[1,1] X[1,2] - X[2,1] X[2,2] +julia> @variable(model, x[1:2, 1:2], Symmetric); + +julia> @variable(model, y[1:2, 1:2], Symmetric); -julia> @constraint(model, X >= 0) -ERROR: At none:1: `@constraint(model, X >= 0)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.>=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X >= 0, PSDCone())` or `@constraint(model, X >= 0, HermitianPSDCone())`. +julia> @constraint(model, x >= y) +ERROR: At none:1: `@constraint(model, x >= y)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.>=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X >= 0, PSDCone())` or `@constraint(model, X >= 0, HermitianPSDCone())`. Stacktrace: [...] ``` -Instead, to represent matrix inequalities you must always use the element-wise -broadcasting `.==`, `.>=`, or `.<=`, or use the [Set inequality syntax](@ref). +Instead, use the [Set inequality syntax](@ref) to specify a set like +[`PSDCone`](@ref) or [`Nonnegatives`](@ref): + +```jldoctest symmetric_matrix +julia> @constraint(model, x >= y, PSDCone()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ PSDCone() + +julia> @constraint(model, x >= y, Nonnegatives()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonnegatives() + +julia> @constraint(model, x >= y, Nonpositives()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonpositives() + +julia> @constraint(model, x >= y, Zeros()) +[x[1,1] - y[1,1] x[1,2] - y[1,2] + x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Zeros() +``` + +### Special cases There are two exceptions: if the result of the left-hand side minus the right-hand side is a `LinearAlgebra.Symmetric` matrix or a `LinearAlgebra.Hermitian` @@ -1474,44 +1493,6 @@ julia> @constraint(model, LinearAlgebra.Symmetric(X) >= 0, PSDCone()) ``` (Note that no error is thrown, even though `X` is not symmetric.) -## Symmetric matrices - -Inequalities between symmetric matrices are not supported, due to the common -ambiguity between elementwise inequalities and a [`PSDCone`](@ref) constraint. -```jldoctest symmetric_matrix -julia> model = Model(); - -julia> @variable(model, x[1:2, 1:2], Symmetric); - -julia> @variable(model, y[1:2, 1:2], Symmetric); - -julia> @constraint(model, x >= y) -ERROR: At none:1: `@constraint(model, x >= y)`: Unsupported matrix in vector-valued set. Did you mean to use the broadcasting syntax `.>=` instead? Alternatively, perhaps you are missing a set argument like `@constraint(model, X >= 0, PSDCone())` or `@constraint(model, X >= 0, HermmitianPSDCone())`. -Stacktrace: -[...] -``` - -Instead, use the [Set inequality syntax](@ref) to specify either a [`PSDCone`](@ref), -or a elementwise inequality set like [`Nonnegatives`](@ref): - -```jldoctest symmetric_matrix -julia> @constraint(model, x >= y, PSDCone()) -[x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ PSDCone() - -julia> @constraint(model, x >= y, Nonnegatives()) -[x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonnegatives() - -julia> @constraint(model, x >= y, Nonpositives()) -[x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonpositives() - -julia> @constraint(model, x >= y, Zeros()) -[x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Zeros() -``` - ## Complementarity constraints A mixed complementarity constraint `F(x) ⟂ x` consists of finding `x` in the diff --git a/src/sd.jl b/src/sd.jl index c3e396da757..2b5d38a56ee 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -685,3 +685,33 @@ function build_constraint( x = -vectorize(f, shape) return VectorConstraint(x, moi_set(set, length(x)), shape) end + +reshape_set(::MOI.Nonnegatives, ::SymmetricMatrixShape) = Nonnegatives() +reshape_set(::MOI.Nonpositives, ::SymmetricMatrixShape) = Nonpositives() +reshape_set(::MOI.Zeros, ::SymmetricMatrixShape) = Zeros() + +function build_constraint( + error_fn::Function, + f::AbstractArray, + ::Nonnegatives, + set::Union{Nonnegatives,Nonpositives,Zeros}, +) + shape = ArrayShape(size(f)) + x = vectorize(f, shape) + return VectorConstraint(x, moi_set(set, length(x)), shape) +end + +function build_constraint( + error_fn::Function, + f::AbstractArray, + ::Nonpositives, + set::Union{Nonnegatives,Nonpositives,Zeros}, +) + shape = ArrayShape(size(f)) + x = -vectorize(f, shape) + return VectorConstraint(x, moi_set(set, length(x)), shape) +end + +reshape_set(::MOI.Nonnegatives, ::ArrayShape) = Nonnegatives() +reshape_set(::MOI.Nonpositives, ::ArrayShape) = Nonpositives() +reshape_set(::MOI.Zeros, ::ArrayShape) = Zeros() diff --git a/src/shapes.jl b/src/shapes.jl index 840de3d13c0..8c507786180 100644 --- a/src/shapes.jl +++ b/src/shapes.jl @@ -168,3 +168,31 @@ struct VectorShape <: AbstractShape end reshape_vector(vectorized_form, ::VectorShape) = vectorized_form vectorize(x, ::VectorShape) = x + +""" + ArrayShape{N}(dims::NTuple{N,Int}) where {N} + +An [`AbstractShape`](@ref) that represents array-valued constraints. + +## Example + +```jldoctest +julia> model = Model(); + +julia> @variable(model, x[1:2, 1:3]); + +julia> c = @constraint(model, x >= 0, Nonnegatives()) +[x[1,1] x[1,2] x[1,3] + x[2,1] x[2,2] x[2,3]] ∈ Nonnegatives() + +julia> shape(constraint_object(c)) +ArrayShape{2}((2, 3)) +``` +""" +struct ArrayShape{N} <: AbstractShape + dims::NTuple{N,Int} +end + +reshape_vector(x, shape::ArrayShape) = reshape(x, shape.dims) + +vectorize(x, ::ArrayShape) = vec(x) From 916ab02d624e32481ca507b82a47e15e7f09cfc2 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 5 Jun 2024 14:25:50 +1200 Subject: [PATCH 06/10] Update --- src/sd.jl | 4 ++-- test/test_constraint.jl | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/sd.jl b/src/sd.jl index 2b5d38a56ee..78230944e21 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -692,7 +692,7 @@ reshape_set(::MOI.Zeros, ::SymmetricMatrixShape) = Zeros() function build_constraint( error_fn::Function, - f::AbstractArray, + f::Array, ::Nonnegatives, set::Union{Nonnegatives,Nonpositives,Zeros}, ) @@ -703,7 +703,7 @@ end function build_constraint( error_fn::Function, - f::AbstractArray, + f::Array, ::Nonpositives, set::Union{Nonnegatives,Nonpositives,Zeros}, ) diff --git a/test/test_constraint.jl b/test/test_constraint.jl index 70307f91d2f..7c8c0dfa09a 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1909,4 +1909,26 @@ function test_symmetric_matrix_inequality() return end +function test_matrix_inequality() + model = Model() + @variable(model, x[1:2, 1:3]) + @variable(model, y[1:2, 1:3]) + g = vec(x .- y) + for set in (Nonnegatives(), Nonpositives(), Zeros()) + c = @constraint(model, x >= y, set) + o = constraint_object(c) + @test isequal_canonical(o.func, g) + @test o.set == moi_set(set, 6) + @test o.shape == ArrayShape((2, 3)) + @test reshape_set(o.set, o.shape) == set + c = @constraint(model, x <= y, set) + o = constraint_object(c) + @test isequal_canonical(o.func, -g) + @test o.set == moi_set(set, 6) + @test o.shape == ArrayShape((2, 3)) + @test reshape_set(o.set, o.shape) == set + end + return +end + end # module From 9f1454adcf042edbd60b5344b6530ef7ddee58ea Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 5 Jun 2024 15:26:15 +1200 Subject: [PATCH 07/10] Update sd.jl --- src/sd.jl | 50 ++++++++++++++------------------------------------ 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/src/sd.jl b/src/sd.jl index 78230944e21..7a01b07baeb 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -660,58 +660,36 @@ moi_set(::Nonnegatives, dim::Int) = MOI.Nonnegatives(dim) moi_set(::Nonpositives, dim::Int) = MOI.Nonpositives(dim) moi_set(::Zeros, dim::Int) = MOI.Zeros(dim) +shape(f::LinearAlgebra.Symmetric) = SymmetricMatrixShape(size(f, 1)) + reshape_set(::MOI.Nonnegatives, ::SymmetricMatrixShape) = Nonnegatives() reshape_set(::MOI.Nonpositives, ::SymmetricMatrixShape) = Nonpositives() reshape_set(::MOI.Zeros, ::SymmetricMatrixShape) = Zeros() -function build_constraint( - error_fn::Function, - f::LinearAlgebra.Symmetric, - ::Nonnegatives, - set::Union{Nonnegatives,Nonpositives,Zeros}, -) - shape = SymmetricMatrixShape(size(f, 1)) - x = vectorize(f, shape) - return VectorConstraint(x, moi_set(set, length(x)), shape) -end - -function build_constraint( - error_fn::Function, - f::LinearAlgebra.Symmetric, - ::Nonpositives, - set::Union{Nonnegatives,Nonpositives,Zeros}, -) - shape = SymmetricMatrixShape(size(f, 1)) - x = -vectorize(f, shape) - return VectorConstraint(x, moi_set(set, length(x)), shape) -end +shape(f::Array) = ArrayShape(size(f)) -reshape_set(::MOI.Nonnegatives, ::SymmetricMatrixShape) = Nonnegatives() -reshape_set(::MOI.Nonpositives, ::SymmetricMatrixShape) = Nonpositives() -reshape_set(::MOI.Zeros, ::SymmetricMatrixShape) = Zeros() +reshape_set(::MOI.Nonnegatives, ::ArrayShape) = Nonnegatives() +reshape_set(::MOI.Nonpositives, ::ArrayShape) = Nonpositives() +reshape_set(::MOI.Zeros, ::ArrayShape) = Zeros() function build_constraint( error_fn::Function, - f::Array, + f::Union{Array,LinearAlgebra.Symmetric}, ::Nonnegatives, set::Union{Nonnegatives,Nonpositives,Zeros}, ) - shape = ArrayShape(size(f)) - x = vectorize(f, shape) - return VectorConstraint(x, moi_set(set, length(x)), shape) + s = shape(f) + x = vectorize(f, s) + return VectorConstraint(x, moi_set(set, length(x)), s) end function build_constraint( error_fn::Function, - f::Array, + f::Union{Array,LinearAlgebra.Symmetric}, ::Nonpositives, set::Union{Nonnegatives,Nonpositives,Zeros}, ) - shape = ArrayShape(size(f)) - x = -vectorize(f, shape) - return VectorConstraint(x, moi_set(set, length(x)), shape) + s = shape(f) + x = -vectorize(f, s) + return VectorConstraint(x, moi_set(set, length(x)), s) end - -reshape_set(::MOI.Nonnegatives, ::ArrayShape) = Nonnegatives() -reshape_set(::MOI.Nonpositives, ::ArrayShape) = Nonpositives() -reshape_set(::MOI.Zeros, ::ArrayShape) = Zeros() From f3b9858bac275b2c404835617db6414c384f5061 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 6 Jun 2024 08:51:12 +1200 Subject: [PATCH 08/10] Update --- src/sd.jl | 8 ++++---- test/test_constraint.jl | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sd.jl b/src/sd.jl index 7a01b07baeb..fdb370a5722 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -685,11 +685,11 @@ end function build_constraint( error_fn::Function, - f::Union{Array,LinearAlgebra.Symmetric}, + ::Union{Array,LinearAlgebra.Symmetric}, ::Nonpositives, set::Union{Nonnegatives,Nonpositives,Zeros}, ) - s = shape(f) - x = -vectorize(f, s) - return VectorConstraint(x, moi_set(set, length(x)), s) + return error_fn( + "The syntax `x <= y, $set` not supported. Use `y >= x, $set` instead.", + ) end diff --git a/test/test_constraint.jl b/test/test_constraint.jl index 7c8c0dfa09a..25133c9801e 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1899,12 +1899,12 @@ function test_symmetric_matrix_inequality() @test o.set == moi_set(set, 3) @test o.shape == SymmetricMatrixShape(2) @test reshape_set(o.set, o.shape) == set - c = @constraint(model, x <= y, set) - o = constraint_object(c) - @test isequal_canonical(o.func, -g) - @test o.set == moi_set(set, 3) - @test o.shape == SymmetricMatrixShape(2) - @test reshape_set(o.set, o.shape) == set + @test_throws_runtime( + ErrorException( + "In `@constraint(model, x <= y, set)`: The syntax `x <= y, $set` not supported. Use `y >= x, $set` instead.", + ), + @constraint(model, x <= y, set), + ) end return end @@ -1921,12 +1921,12 @@ function test_matrix_inequality() @test o.set == moi_set(set, 6) @test o.shape == ArrayShape((2, 3)) @test reshape_set(o.set, o.shape) == set - c = @constraint(model, x <= y, set) - o = constraint_object(c) - @test isequal_canonical(o.func, -g) - @test o.set == moi_set(set, 6) - @test o.shape == ArrayShape((2, 3)) - @test reshape_set(o.set, o.shape) == set + @test_throws_runtime( + ErrorException( + "In `@constraint(model, x <= y, set)`: The syntax `x <= y, $set` not supported. Use `y >= x, $set` instead.", + ), + @constraint(model, x <= y, set), + ) end return end From 7a9a6be9eaf20d19b3ab020c35af2c4dfbf1042f Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 6 Jun 2024 08:56:08 +1200 Subject: [PATCH 09/10] Update --- docs/src/manual/constraints.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/manual/constraints.md b/docs/src/manual/constraints.md index 75df9f219dc..e79719c4f93 100644 --- a/docs/src/manual/constraints.md +++ b/docs/src/manual/constraints.md @@ -188,19 +188,19 @@ Instead, use the [Set inequality syntax](@ref) to specify a set like ```jldoctest symmetric_matrix julia> @constraint(model, x >= y, PSDCone()) [x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ PSDCone() + ⋯ x[2,2] - y[2,2]] ∈ PSDCone() julia> @constraint(model, x >= y, Nonnegatives()) [x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonnegatives() + ⋯ x[2,2] - y[2,2]] ∈ Nonnegatives() julia> @constraint(model, x >= y, Nonpositives()) [x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Nonpositives() + ⋯ x[2,2] - y[2,2]] ∈ Nonpositives() julia> @constraint(model, x >= y, Zeros()) [x[1,1] - y[1,1] x[1,2] - y[1,2] - x[1,2] - y[1,2] x[2,2] - y[2,2]] ∈ Zeros() + ⋯ x[2,2] - y[2,2]] ∈ Zeros() ``` ### Special cases From 19c7f621acf4b7d93ea85ce3e2ad2d7ab48091ea Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 6 Jun 2024 09:19:24 +1200 Subject: [PATCH 10/10] Add tests --- test/test_constraint.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_constraint.jl b/test/test_constraint.jl index 25133c9801e..c3880002d4f 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -1891,6 +1891,8 @@ function test_symmetric_matrix_inequality() model = Model() @variable(model, x[1:2, 1:2], Symmetric) @variable(model, y[1:2, 1:2], Symmetric) + set_start_value.(x, [1 2; 2 3]) + set_start_value.(y, [6 4; 4 7]) g = [x[1, 1] - y[1, 1], x[1, 2] - y[1, 2], x[2, 2] - y[2, 2]] for set in (Nonnegatives(), Nonpositives(), Zeros()) c = @constraint(model, x >= y, set) @@ -1899,6 +1901,9 @@ function test_symmetric_matrix_inequality() @test o.set == moi_set(set, 3) @test o.shape == SymmetricMatrixShape(2) @test reshape_set(o.set, o.shape) == set + primal = value(start_value, c) + @test primal isa LinearAlgebra.Symmetric + @test primal == LinearAlgebra.Symmetric([-5.0 -2.0; -2.0 -4.0]) @test_throws_runtime( ErrorException( "In `@constraint(model, x <= y, set)`: The syntax `x <= y, $set` not supported. Use `y >= x, $set` instead.", @@ -1913,6 +1918,8 @@ function test_matrix_inequality() model = Model() @variable(model, x[1:2, 1:3]) @variable(model, y[1:2, 1:3]) + set_start_value.(x, [1 2 3; 4 5 6]) + set_start_value.(y, [7 9 11; 8 12 13]) g = vec(x .- y) for set in (Nonnegatives(), Nonpositives(), Zeros()) c = @constraint(model, x >= y, set) @@ -1921,6 +1928,9 @@ function test_matrix_inequality() @test o.set == moi_set(set, 6) @test o.shape == ArrayShape((2, 3)) @test reshape_set(o.set, o.shape) == set + primal = value(start_value, c) + @test primal isa Matrix{Float64} + @test primal == [-6.0 -7.0 -8.0; -4.0 -7.0 -7.0] @test_throws_runtime( ErrorException( "In `@constraint(model, x <= y, set)`: The syntax `x <= y, $set` not supported. Use `y >= x, $set` instead.",