From 1ae61fdfa7a45e22e799945ad08873f9cbd1100e Mon Sep 17 00:00:00 2001 From: odow Date: Sun, 11 Aug 2024 14:34:44 +1200 Subject: [PATCH 1/7] Fix return type of +/-(::Symmetric, ::Hermitian) for JuMP types --- src/operators.jl | 52 +++++++++++++++++++++++++++++++++++++++++++ test/test_operator.jl | 22 ++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/operators.jl b/src/operators.jl index 25cd8f14c79..d7e1c0b3089 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -571,3 +571,55 @@ function Base.complex( ) return r + im * i end + +function Base.:+( + A::LinearAlgebra.Symmetric{V}, + B::LinearAlgebra.Hermitian, +) where { + V<:Union{ + GenericVariableRef{<:Real}, + GenericAffExpr{<:Real}, + GenericQuadExpr{<:Real}, + }, +} + return LinearAlgebra.Hermitian(A) + B +end + +function Base.:+( + A::LinearAlgebra.Hermitian, + B::LinearAlgebra.Symmetric{V}, +) where { + V<:Union{ + GenericVariableRef{<:Real}, + GenericAffExpr{<:Real}, + GenericQuadExpr{<:Real}, + }, +} + return A + LinearAlgebra.Hermitian(B) +end + +function Base.:-( + A::LinearAlgebra.Symmetric{V}, + B::LinearAlgebra.Hermitian, +) where { + V<:Union{ + GenericVariableRef{<:Real}, + GenericAffExpr{<:Real}, + GenericQuadExpr{<:Real}, + }, +} + return LinearAlgebra.Hermitian(A) - B +end + +function Base.:-( + A::LinearAlgebra.Hermitian, + B::LinearAlgebra.Symmetric{V}, +) where { + V<:Union{ + GenericVariableRef{<:Real}, + GenericAffExpr{<:Real}, + GenericQuadExpr{<:Real}, + }, +} + return A - LinearAlgebra.Hermitian(B) +end diff --git a/test/test_operator.jl b/test/test_operator.jl index d8d27270b41..1cbfeb9925a 100644 --- a/test/test_operator.jl +++ b/test/test_operator.jl @@ -689,4 +689,26 @@ function test_base_complex() return end +function test_hermitian_and_symmetric() + model = Model() + @variable(model, A[1:2, 1:2], Symmetric) + @variable(model, B[1:2, 1:2], Hermitian) + for (x, y) in ( + (A, B), + (B, A), + (1.0 * A, B), + (B, 1.0 * A), + (1.0 * A, 1.0 * B), + (1.0 * B, 1.0 * A), + (1.0 * LinearAlgebra.Symmetric(A .* A), 1.0 * B), + (1.0 * B, 1.0 * LinearAlgebra.Symmetric(A .* A)), + ) + @test x + y isa LinearAlgebra.Hermitian + @test x + y == x .+ y + @test x - y isa LinearAlgebra.Hermitian + @test x - y == x .- y + end + return +end + end From e244c54527c0ac87ab31dccf032daa6359dc34c3 Mon Sep 17 00:00:00 2001 From: odow Date: Sun, 11 Aug 2024 14:39:09 +1200 Subject: [PATCH 2/7] Update --- src/operators.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operators.jl b/src/operators.jl index d7e1c0b3089..027842d71ed 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -599,7 +599,7 @@ function Base.:+( end function Base.:-( - A::LinearAlgebra.Symmetric{V}, + A::LinearAlgebra.Symmetric{V,Matrix{V}}, B::LinearAlgebra.Hermitian, ) where { V<:Union{ @@ -613,7 +613,7 @@ end function Base.:-( A::LinearAlgebra.Hermitian, - B::LinearAlgebra.Symmetric{V}, + B::LinearAlgebra.Symmetric{V,Matrix{V}}, ) where { V<:Union{ GenericVariableRef{<:Real}, From 5036c98626deb108e17eea48f27d34c08cd42cd0 Mon Sep 17 00:00:00 2001 From: odow Date: Sun, 11 Aug 2024 14:53:57 +1200 Subject: [PATCH 3/7] Update --- src/operators.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operators.jl b/src/operators.jl index 027842d71ed..1c4ea3484ff 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -573,7 +573,7 @@ function Base.complex( end function Base.:+( - A::LinearAlgebra.Symmetric{V}, + A::LinearAlgebra.Symmetric{V,Matrix{V}}, B::LinearAlgebra.Hermitian, ) where { V<:Union{ @@ -587,7 +587,7 @@ end function Base.:+( A::LinearAlgebra.Hermitian, - B::LinearAlgebra.Symmetric{V}, + B::LinearAlgebra.Symmetric{V,Matrix{V}}, ) where { V<:Union{ GenericVariableRef{<:Real}, From c4b82f34583563780ced95b4990614821cce81b2 Mon Sep 17 00:00:00 2001 From: odow Date: Sun, 11 Aug 2024 15:20:15 +1200 Subject: [PATCH 4/7] Update --- src/operators.jl | 8 +++----- test/test_operator.jl | 12 ++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/operators.jl b/src/operators.jl index 1c4ea3484ff..98f71cddd6a 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -333,11 +333,9 @@ end function Base.:+(a::GenericAffExpr, q::GenericQuadExpr) return GenericQuadExpr(a + q.aff, copy(q.terms)) end -function Base.:-(a::GenericAffExpr, q::GenericQuadExpr) - result = -q - # This makes an unnecessary copy of aff, but it's important for a to appear - # first. - result.aff = a + result.aff +function Base.:-(a::GenericAffExpr{S}, q::GenericQuadExpr{T}) where {S,T} + result = -_copy_convert_coef(_MA.promote_operation(-, S, T), q) + add_to_expression!(result.aff, a) return result end diff --git a/test/test_operator.jl b/test/test_operator.jl index 1cbfeb9925a..8b237ff1456 100644 --- a/test/test_operator.jl +++ b/test/test_operator.jl @@ -689,6 +689,18 @@ function test_base_complex() return end +function test_aff_minus_quad() + model = Model() + @variable(model, x) + a, b = 1.0 * x, (2 + 3im) * x^2 + @test a - b == -(b - a) + @test b - a == -(a - b) + a, b = (1.0 + 2im) * x, 3 * x^2 + 4 * x + @test a - b == -(b - a) + @test b - a == -(a - b) + return +end + function test_hermitian_and_symmetric() model = Model() @variable(model, A[1:2, 1:2], Symmetric) From 53f28e37f2a428fd2a69948119b664a8ac035868 Mon Sep 17 00:00:00 2001 From: odow Date: Sun, 11 Aug 2024 15:35:41 +1200 Subject: [PATCH 5/7] Update --- test/test_operator.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_operator.jl b/test/test_operator.jl index 8b237ff1456..b29d2b357e0 100644 --- a/test/test_operator.jl +++ b/test/test_operator.jl @@ -469,7 +469,7 @@ function test_extension_basic_operators_affexpr( @test_expression_with_string aff - aff "0 x" # 4-4 AffExpr--QuadExpr @test_expression_with_string aff2 + q "2.5 y*z + 1.2 y + 7.1 x + 3.7" - @test_expression_with_string aff2 - q "-2.5 y*z + 1.2 y - 7.1 x - 1.3" + @test_expression_with_string aff2 - q "-2.5 y*z - 7.1 x + 1.2 y - 1.3" @test_expression_with_string aff2 * q "(1.2 y + 1.2) * (2.5 y*z + 7.1 x + 2.5)" @test_expression_with_string aff2 / q "(1.2 y + 1.2) / (2.5 y*z + 7.1 x + 2.5)" @test transpose(aff) === aff @@ -499,7 +499,7 @@ function test_extension_basic_operators_quadexpr( @test_expression_with_string q * 2 "5 y*z + 14.2 x + 5" @test_expression_with_string q / 2 "1.25 y*z + 3.55 x + 1.25" @test q == q - @test_expression_with_string aff2 - q "-2.5 y*z + 1.2 y - 7.1 x - 1.3" + @test_expression_with_string aff2 - q "-2.5 y*z - 7.1 x + 1.2 y - 1.3" # 4-2 QuadExpr--Variable @test_expression_with_string q + w "2.5 y*z + 7.1 x + w + 2.5" @test_expression_with_string q - w "2.5 y*z + 7.1 x - w + 2.5" From b18678afdd595794e09681c0d4bacaa4a356441c Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 12 Aug 2024 09:58:53 +1200 Subject: [PATCH 6/7] Update --- src/sd.jl | 16 ++++++++++++++++ test/test_constraint.jl | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/sd.jl b/src/sd.jl index a00b85735ac..f07b1a09d65 100644 --- a/src/sd.jl +++ b/src/sd.jl @@ -703,6 +703,22 @@ function build_constraint( return VectorConstraint(x, MOI.Zeros(length(x)), shape) end +# If we have a real-valued Hermitian matrix, then it is actually Symmetric, and +# not Complex-valued Hermitian. +function build_constraint( + error_fn::Function, + H::LinearAlgebra.Hermitian{V}, + set::Zeros, +) where { + V<:Union{ + GenericVariableRef{<:Real}, + GenericAffExpr{<:Real}, + GenericQuadExpr{<:Real}, + }, +} + return build_constraint(error_fn, LinearAlgebra.Symmetric(H), set) +end + reshape_set(s::MOI.Zeros, ::HermitianMatrixShape) = Zeros() function build_constraint(error_fn::Function, ::AbstractMatrix, ::Nonnegatives) diff --git a/test/test_constraint.jl b/test/test_constraint.jl index 282d299de9c..fa6dc9b04a7 100644 --- a/test/test_constraint.jl +++ b/test/test_constraint.jl @@ -2088,4 +2088,23 @@ function test_abstract_vector_orthants() return end +function test_real_hermitian_in_zeros() + model = Model() + @variable(model, x[1:2, 1:2], Symmetric) + c = @constraint(model, LinearAlgebra.Hermitian(x) in Zeros()) + obj = constraint_object(c) + @test obj.func == [x[1, 1], x[1, 2], x[2, 2]] + @test obj.shape == SymmetricMatrixShape(2; needs_adjoint_dual = true) + H = LinearAlgebra.Hermitian([1 2; 2 3]) + c = @constraint(model, x == H) + obj = constraint_object(c) + @test obj.func == [x[1, 1] - 1, x[1, 2] - 2, x[2, 2] - 3] + @test obj.shape == SymmetricMatrixShape(2; needs_adjoint_dual = true) + c = @constraint(model, LinearAlgebra.Hermitian(x .^ 2) in Zeros()) + obj = constraint_object(c) + @test obj.func == [x[1, 1]^2, x[1, 2]^2, x[2, 2]^2] + @test obj.shape == SymmetricMatrixShape(2; needs_adjoint_dual = true) + return +end + end # module From cb854d72b1c3a6d064a26e5435c7d1c697dce1df Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 12 Aug 2024 20:18:19 +1200 Subject: [PATCH 7/7] Update src/operators.jl --- src/operators.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/operators.jl b/src/operators.jl index 98f71cddd6a..11671393eb8 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -570,6 +570,8 @@ function Base.complex( return r + im * i end +# These methods exist in LinearAlgebra for subtypes of Real. Without them, we +# return a `Matrix` which looses the Hermitian information. function Base.:+( A::LinearAlgebra.Symmetric{V,Matrix{V}}, B::LinearAlgebra.Hermitian,