|
| 1 | +using MatrixAlgebraKit |
| 2 | +using Test |
| 3 | +using TestExtras |
| 4 | +using StableRNGs |
| 5 | +using MatrixAlgebraKit: diagview |
| 6 | +using LinearAlgebra |
| 7 | +using LinearAlgebra: exp |
| 8 | + |
| 9 | +BLASFloats = (Float32, Float64, ComplexF32, ComplexF64) |
| 10 | +GenericFloats = (Float16, ComplexF16, BigFloat, Complex{BigFloat}) |
| 11 | + |
| 12 | +@testset "logarithm! for T = $T" for T in BLASFloats |
| 13 | + rng = StableRNG(123) |
| 14 | + m = 54 |
| 15 | + |
| 16 | + # spectrum inside a disk around 1, away from the negative real axis and zero |
| 17 | + A = LinearAlgebra.I + LinearAlgebra.normalize!(randn(rng, T, m, m)) |
| 18 | + Ac = copy(A) |
| 19 | + logA = LinearAlgebra.log(A) |
| 20 | + |
| 21 | + logA2 = @constinferred logarithm(A) |
| 22 | + @test logA ≈ logA2 |
| 23 | + @test exp(logA2) ≈ A |
| 24 | + @test A == Ac |
| 25 | + |
| 26 | + algs = (MatrixFunctionViaLA(), MatrixFunctionViaEig(LAPACK_Simple())) |
| 27 | + @testset "algorithm $alg" for alg in algs |
| 28 | + logA2 = @constinferred logarithm(A, alg) |
| 29 | + @test logA ≈ logA2 |
| 30 | + @test A == Ac |
| 31 | + end |
| 32 | + |
| 33 | + @test_throws DomainError logarithm(A; alg = MatrixFunctionViaEigh(LAPACK_QRIteration())) |
| 34 | + |
| 35 | + # roundtrip with exponential |
| 36 | + @test logarithm(exponential(logA)) ≈ logA |
| 37 | +end |
| 38 | + |
| 39 | +@testset "logarithm! for hermitian T = $T" for T in BLASFloats |
| 40 | + rng = StableRNG(123) |
| 41 | + m = 54 |
| 42 | + |
| 43 | + X = randn(rng, T, m, m) |
| 44 | + A = Matrix(LinearAlgebra.Hermitian(X * X' + one(real(T)) * LinearAlgebra.I)) |
| 45 | + Ac = copy(A) |
| 46 | + logA = LinearAlgebra.log(LinearAlgebra.Hermitian(A)) |
| 47 | + |
| 48 | + algs = ( |
| 49 | + MatrixFunctionViaLA(), MatrixFunctionViaEigh(LAPACK_QRIteration()), |
| 50 | + MatrixFunctionViaEig(LAPACK_Simple()), |
| 51 | + ) |
| 52 | + @testset "algorithm $alg" for alg in algs |
| 53 | + logA2 = @constinferred logarithm(A, alg) |
| 54 | + @test logA ≈ logA2 |
| 55 | + @test A == Ac |
| 56 | + end |
| 57 | +end |
| 58 | + |
| 59 | +@testset "logarithm! domain handling for T = $T" for T in (Float32, Float64) |
| 60 | + rng = StableRNG(123) |
| 61 | + m = 4 |
| 62 | + |
| 63 | + X = randn(rng, T, m, m) |
| 64 | + V = Matrix(LinearAlgebra.qr(X).Q) |
| 65 | + A = V * LinearAlgebra.Diagonal(T[-1, 1, 2, 3]) * V' |
| 66 | + A = (A + A') / 2 |
| 67 | + |
| 68 | + # negative eigenvalue: DomainError for real input, complex principal value otherwise |
| 69 | + @test_throws DomainError logarithm(A) |
| 70 | + @test_throws DomainError logarithm(A; alg = MatrixFunctionViaEigh(LAPACK_QRIteration())) |
| 71 | + @test_throws DomainError logarithm(A; alg = MatrixFunctionViaEig(LAPACK_Simple())) |
| 72 | + |
| 73 | + logA = @constinferred logarithm(complex.(A)) |
| 74 | + @test exp(logA) ≈ A |
| 75 | + |
| 76 | + # (numerically) zero eigenvalue: no logarithm exists |
| 77 | + Asing = V * LinearAlgebra.Diagonal(T[0, 1, 2, 3]) * V' |
| 78 | + Asing = (Asing + Asing') / 2 |
| 79 | + @test_throws DomainError logarithm(Asing; alg = MatrixFunctionViaEigh(LAPACK_QRIteration())) |
| 80 | + @test_throws DomainError logarithm(Asing; alg = MatrixFunctionViaEig(LAPACK_Simple())) |
| 81 | + @test_throws DomainError logarithm(complex.(Asing); alg = MatrixFunctionViaEig(LAPACK_Simple())) |
| 82 | +end |
| 83 | + |
| 84 | +@testset "logarithm! for Diagonal{$T}" for T in (BLASFloats..., GenericFloats...) |
| 85 | + rng = StableRNG(123) |
| 86 | + m = 54 |
| 87 | + |
| 88 | + data = T <: Real ? (abs.(randn(rng, T, m)) .+ one(T)) : (randn(rng, T, m) .+ 4 * one(T)) |
| 89 | + A = Diagonal(data) |
| 90 | + Ac = copy(A) |
| 91 | + logA = LinearAlgebra.log(A) |
| 92 | + |
| 93 | + logA2 = @constinferred logarithm(A) |
| 94 | + @test logA2 isa Diagonal |
| 95 | + @test logA ≈ logA2 |
| 96 | + @test A == Ac |
| 97 | + |
| 98 | + if T <: Real |
| 99 | + @test_throws DomainError logarithm(Diagonal(-data)) |
| 100 | + end |
| 101 | + @test_throws DomainError logarithm(Diagonal(zeros(T, m))) |
| 102 | +end |
0 commit comments