Skip to content

LinearAlgebra fails on Symmetric complex matrices #903

@Timeroot

Description

@Timeroot

I have a collection of positive-definite, symmetric, complex matrices I need to quickly solve linear systems on. Given the positive definiteness, I initially tried cholesky but that gave me the error:

MethodError: no method matching cholesky(::Symmetric{ComplexF64, Matrix{ComplexF64}})

so I figured that wasn't supported. I tried factorize figuring that would pick the right method for the job. It fell back to Bunch-Kaufman, but then gave me the error:

ArgumentError: adjoint not implemented for complex symmetric matrices

Stacktrace:
 [1] adjoint(B::BunchKaufman{ComplexF64, Matrix{ComplexF64}})
   @ LinearAlgebra /opt/julia-1.7.1/share/julia/stdlib/v1.7/LinearAlgebra/src/bunchkaufman.jl:284

Ultimately the only current workaround is to explicitly turn my Symmetric{ComplexF64} into a Matrix{ComplexF64} and then call factorize, or to directly call lu myself. Either way, this loses any optimizations that could be done using the symmetry or, even better, the positive definiteness.

Besides that BunchKaufman should work for complex symmetric matrices, Cholesky decomposition also works for PSD complex symmetric matrices; the difference is that M == L * transpose(L) instead of L * L'. Here's an implementation I made of a modified Cholesky decomposition for complex symmetric matrices:

function my_chol!(A::AbstractMatrix, ::Type{LowerTriangular})
    LinearAlgebra.require_one_based_indexing(A)
    n = LinearAlgebra.checksquare(A)
    @inbounds begin
        for k = 1:n
            Akk = A[k,k]
            for i = 1:k - 1
                Akk -= A[k,i]*A[k,i]
            end
            A[k,k] = Akk
            Akk, info = sqrt(Akk), 0
            if info != 0
                return LowerTriangular(A), info
            end
            A[k,k] = Akk
            AkkInv = inv(Akk)
            for j = 1:k - 1
                @simd for i = k + 1:n
                    A[i,k] -= A[i,j]*A[k,j]
                end
            end
            for i = k + 1:n
                A[i,k] *= AkkInv
            end
        end
     end
    return LowerTriangular(A), 0
end

(obviously the above can be cleaned up, I left it as-is to maximize similarity to existing chol! code.)

If you want to try this out, here's a snippet with two simple test cases to try:

MM = ComplexF64[1.0237067988305515 -0.03490181882982961 + 0.016873104918059822im 0.018801862132469378 + 0.03132147479849042im; -0.03490181882982961 + 0.016873104918059822im 1.156680064998401 -0.12907274302681904; 0.018801862132469378 + 0.03132147479849042im -0.12907274302681904 0.9866152863493339];
#MM = ComplexF64[1 1im; 1im 5]

show(stdout, "text/plain", MM)
println()
println("Symmetric? ",norm(MM - transpose(MM)) == 0.0)
println("Eigenvalues: ",eigen(MM).values)
LL, _ = my_chol!(copy(MM), LowerTriangular)
show(stdout, "text/plain", LL)
println()
println("Cholesky Error: ",norm(LL * transpose(LL) - MM))

It would be nice to support Symmetric{Complex} matrices.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions