Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HermOrSym should preserve structure when scaled with Numbers. #29469

Merged
merged 1 commit into from
Oct 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions stdlib/LinearAlgebra/src/symmetric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,13 @@ mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatrix})
*(adjA::Adjoint{<:Any,<:RealHermSymComplexHerm}, B::AbstractTriangular) = adjA.parent * B
*(A::AbstractTriangular, adjB::Adjoint{<:Any,<:RealHermSymComplexHerm}) = A * adjB.parent

for T in (:Symmetric, :Hermitian), op in (:*, :/)
# Deal with an ambiguous case
@eval ($op)(A::$T, x::Bool) = ($T)(($op)(A.data, x), sym_uplo(A.uplo))
S = T == :Hermitian ? :Real : :Number
@eval ($op)(A::$T, x::$S) = ($T)(($op)(A.data, x), sym_uplo(A.uplo))
end
# Scaling with Number
*(A::Symmetric, x::Number) = Symmetric(A.data*x, sym_uplo(A.uplo))
*(x::Number, A::Symmetric) = Symmetric(x*A.data, sym_uplo(A.uplo))
*(A::Hermitian, x::Real) = Hermitian(A.data*x, sym_uplo(A.uplo))
*(x::Real, A::Hermitian) = Hermitian(x*A.data, sym_uplo(A.uplo))
/(A::Symmetric, x::Number) = Symmetric(A.data/x, sym_uplo(A.uplo))
/(A::Hermitian, x::Real) = Hermitian(A.data/x, sym_uplo(A.uplo))

function factorize(A::HermOrSym{T}) where T
TT = typeof(sqrt(oneunit(T)))
Expand Down
44 changes: 44 additions & 0 deletions stdlib/LinearAlgebra/test/symmetric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -517,4 +517,48 @@ end
@test Hermitian(A, :U)[1,1] == Hermitian(A, :L)[1,1] == real(A[1,1])
end

@testset "issue #29392: SymOrHerm scaled with Number" begin
R = rand(Float64, 2, 2); C = rand(ComplexF64, 2, 2)
# Symmetric * Real, Real * Symmetric
A = Symmetric(R); x = 2.0
@test (A * x)::Symmetric == (x * A)::Symmetric
A = Symmetric(C); x = 2.0
@test (A * x)::Symmetric == (x * A)::Symmetric
# Symmetric * Complex, Complex * Symmetrics
A = Symmetric(R); x = 2.0im
@test (A * x)::Symmetric == (x * A)::Symmetric
A = Symmetric(C); x = 2.0im
@test (A * x)::Symmetric == (x * A)::Symmetric
# Hermitian * Real, Real * Hermitian
A = Hermitian(R); x = 2.0
@test (A * x)::Hermitian == (x * A)::Hermitian
A = Hermitian(C); x = 2.0
@test (A * x)::Hermitian == (x * A)::Hermitian
# Hermitian * Complex, Complex * Hermitian
A = Hermitian(R); x = 2.0im
@test (A * x)::Matrix == (x * A)::Matrix
A = Hermitian(C); x = 2.0im
@test (A * x)::Matrix == (x * A)::Matrix
# Symmetric / Real
A = Symmetric(R); x = 2.0
@test (A / x)::Symmetric == Matrix(A) / x
A = Symmetric(C); x = 2.0
@test (A / x)::Symmetric == Matrix(A) / x
# Symmetric / Complex
A = Symmetric(R); x = 2.0im
@test (A / x)::Symmetric == Matrix(A) / x
A = Symmetric(C); x = 2.0im
@test (A / x)::Symmetric == Matrix(A) / x
# Hermitian / Real
A = Hermitian(R); x = 2.0
@test (A / x)::Hermitian == Matrix(A) / x
A = Hermitian(C); x = 2.0
@test (A / x)::Hermitian == Matrix(A) / x
# Hermitian / Complex
A = Hermitian(R); x = 2.0im
@test (A / x)::Matrix == Matrix(A) / x
A = Hermitian(C); x = 2.0im
@test (A / x)::Matrix == Matrix(A) / x
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine I'm missing something obvious: Why not preserve the structural information? :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we trying to make Symmetric/Hermitian full fledged abstract arrays? I feel like that can never be complete, there will always be something that does not return a Symmetric. Personally I just consider them dispatch wrappers, and same with Adjoint/Transpose, but I guess I am alone in this thinking.

Anyway, I guess this change was not necessary and maybe it is better to make ::Number * ::SymOrHerm preserve the structural property?

Copy link
Member

@StefanKarpinski StefanKarpinski Oct 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like that can never be complete

There are a finite number of operations defined in Base, so this can't be true. We're still in the phase where we're encountering things we missed all the time, but it's getting less and less over time and will eventually be none. If it's still a common problem in a couple of minor versions (say 1.3 or so) then, we can consider if it's just not working, but it seems premature to declare failure at this point.


end # module TestSymmetric