|
| 1 | +include("shared.jl") |
| 2 | + |
| 3 | +@testset "CXSparse cs_cholesky" begin |
| 4 | + Random.seed!(0xCC55_F00D) |
| 5 | + |
| 6 | + @testset "SPD ($Tv, $Ti, n=$n)" for Tv in ELTYPES, |
| 7 | + Ti in IDXTYPES, n in (5, 25, 100) |
| 8 | + |
| 9 | + Mraw = _randmat(Tv, n, n) |
| 10 | + Adense = Mraw * Mraw' + Tv(n) * I |
| 11 | + A = _convert(Adense, Tv, Ti) |
| 12 | + b = _randvec(Tv, n) |
| 13 | + |
| 14 | + F = cs_cholesky(A) |
| 15 | + @test size(F) == (n, n) |
| 16 | + @test size(F, 1) == n |
| 17 | + @test size(F, 2) == n |
| 18 | + |
| 19 | + x = F \ b |
| 20 | + @test length(x) == n |
| 21 | + @test norm(A * x - b) < 1e-8 * norm(b) |
| 22 | + @test x ≈ Adense \ b rtol=1e-8 |
| 23 | + |
| 24 | + x_pre = zeros(Tv, n) |
| 25 | + @test ldiv!(x_pre, F, b) === x_pre |
| 26 | + @test x_pre ≈ x |
| 27 | + end |
| 28 | + |
| 29 | + @testset "rejects non-square ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 30 | + A = _convert(_randmat(Tv, 3, 5), Tv, Ti) |
| 31 | + @test_throws ErrorException cs_cholesky(A) |
| 32 | + end |
| 33 | + |
| 34 | + @testset "rejects non-positive-definite ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 35 | + A = _convert(Tv[-1 0; 0 -1], Tv, Ti) |
| 36 | + @test_throws ErrorException cs_cholesky(A) |
| 37 | + end |
| 38 | + |
| 39 | + @testset "rejects zero diagonal ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 40 | + A = _convert(Tv[0 0; 0 1], Tv, Ti) |
| 41 | + @test_throws ErrorException cs_cholesky(A) |
| 42 | + end |
| 43 | + |
| 44 | + @testset "dimension checks ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 45 | + A = _convert(Tv[2 0; 0 2], Tv, Ti) |
| 46 | + F = cs_cholesky(A) |
| 47 | + @test_throws DimensionMismatch (F \ Tv[1, 2, 3]) |
| 48 | + @test_throws DimensionMismatch ldiv!(zeros(Tv, 3), F, Tv[1, 2]) |
| 49 | + end |
| 50 | + |
| 51 | + @testset "n=1 trivial case ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 52 | + A = _convert(reshape(Tv[9.0], 1, 1), Tv, Ti) |
| 53 | + F = cs_cholesky(A) |
| 54 | + @test F \ Tv[18.0] ≈ Tv[2.0] |
| 55 | + end |
| 56 | + |
| 57 | + @testset "identity matrix ($Tv, $Ti, n=$n)" for Tv in ELTYPES, |
| 58 | + Ti in IDXTYPES, n in (3, 10) |
| 59 | + A = _convert(Matrix{Tv}(I, n, n), Tv, Ti) |
| 60 | + b = _randvec(Tv, n) |
| 61 | + F = cs_cholesky(A) |
| 62 | + @test F \ b ≈ b |
| 63 | + end |
| 64 | + |
| 65 | + @testset "diagonal positive matrix ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 66 | + d = Tv <: Complex ? Tv[2+0im, 3+0im, 4+0im, 1+0im] : Tv[2, 3, 4, 1] |
| 67 | + A = _convert(Diagonal(d), Tv, Ti) |
| 68 | + b = _randvec(Tv, 4) |
| 69 | + F = cs_cholesky(A) |
| 70 | + @test F \ b ≈ b ./ d |
| 71 | + end |
| 72 | + |
| 73 | + @testset "tridiagonal SPD ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 74 | + n = 25 |
| 75 | + Adense = Tridiagonal(fill(Tv(-1), n - 1), fill(Tv(4), n), fill(Tv(-1), n - 1)) |
| 76 | + A = _convert(Matrix(Adense), Tv, Ti) |
| 77 | + b = _randvec(Tv, n) |
| 78 | + F = cs_cholesky(A) |
| 79 | + @test F \ b ≈ Matrix(Adense) \ b rtol=1e-10 |
| 80 | + end |
| 81 | + |
| 82 | + @testset "Hermitian (not just real-symmetric) ($Ti)" for Ti in IDXTYPES |
| 83 | + # Hermitian PD matrix with non-trivial complex off-diagonals. |
| 84 | + Adense = ComplexF64[ |
| 85 | + 4.0+0.0im 1.0+0.5im 0.0+0.0im |
| 86 | + 1.0-0.5im 3.0+0.0im 0.5-0.25im |
| 87 | + 0.0+0.0im 0.5+0.25im 2.0+0.0im |
| 88 | + ] |
| 89 | + @assert ishermitian(Adense) |
| 90 | + A = _convert(Adense, ComplexF64, Ti) |
| 91 | + b = ComplexF64[1+0im, 2-1im, -1+0.5im] |
| 92 | + F = cs_cholesky(A) |
| 93 | + x = F \ b |
| 94 | + @test x ≈ Adense \ b rtol=1e-10 |
| 95 | + @test norm(A * x - b) < 1e-10 * norm(b) |
| 96 | + end |
| 97 | + |
| 98 | + @testset "small known-answer ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 99 | + # A = [4 2; 2 5] (SPD: dets are 4, 16) ; b = [6, 7] |
| 100 | + # det(A) = 16, x = [(5*6 - 2*7)/16, (4*7 - 2*6)/16] = [16/16, 16/16] = [1, 1] |
| 101 | + A = _convert(Tv[4 2; 2 5], Tv, Ti) |
| 102 | + b = Tv[6, 7] |
| 103 | + F = cs_cholesky(A) |
| 104 | + @test F \ b ≈ Tv[1, 1] |
| 105 | + end |
| 106 | + |
| 107 | + @testset "input matrix is not mutated by factorization or solve ($Tv, $Ti)" for |
| 108 | + Tv in ELTYPES, Ti in IDXTYPES |
| 109 | + M = _randmat(Tv, 8, 8) |
| 110 | + A = _convert(M * M' + Tv(8) * I, Tv, Ti) |
| 111 | + snap = _snapshot(A) |
| 112 | + F = cs_cholesky(A) |
| 113 | + @test _unchanged(A, snap) |
| 114 | + _ = F \ _randvec(Tv, 8) |
| 115 | + @test _unchanged(A, snap) |
| 116 | + end |
| 117 | + |
| 118 | + @testset "rhs `b` is not mutated by solve ($Tv, $Ti)" for Tv in ELTYPES, Ti in IDXTYPES |
| 119 | + M = _randmat(Tv, 6, 6) |
| 120 | + A = _convert(M * M' + Tv(6) * I, Tv, Ti) |
| 121 | + b = _randvec(Tv, 6) |
| 122 | + b_orig = copy(b) |
| 123 | + F = cs_cholesky(A) |
| 124 | + _ = F \ b |
| 125 | + @test b == b_orig |
| 126 | + _ = ldiv!(zeros(Tv, 6), F, b) |
| 127 | + @test b == b_orig |
| 128 | + end |
| 129 | + |
| 130 | + @testset "multiple solves on same factorization ($Tv, $Ti)" for |
| 131 | + Tv in ELTYPES, Ti in IDXTYPES |
| 132 | + M = _randmat(Tv, 10, 10) |
| 133 | + A = _convert(M * M' + Tv(10) * I, Tv, Ti) |
| 134 | + F = cs_cholesky(A) |
| 135 | + Adense = Matrix(A) |
| 136 | + for _ in 1:5 |
| 137 | + b = _randvec(Tv, 10) |
| 138 | + x = F \ b |
| 139 | + @test x ≈ Adense \ b rtol=1e-10 |
| 140 | + end |
| 141 | + end |
| 142 | + |
| 143 | + @testset "ldiv! returns the destination vector ($Tv, $Ti)" for |
| 144 | + Tv in ELTYPES, Ti in IDXTYPES |
| 145 | + M = _randmat(Tv, 5, 5) |
| 146 | + A = _convert(M * M' + Tv(5) * I, Tv, Ti) |
| 147 | + F = cs_cholesky(A) |
| 148 | + b = _randvec(Tv, 5) |
| 149 | + x = Vector{Tv}(undef, 5) |
| 150 | + @test ldiv!(x, F, b) === x |
| 151 | + end |
| 152 | + |
| 153 | + @testset "explicit finalize is safe and idempotent" begin |
| 154 | + M = randn(5, 5) |
| 155 | + A = sparse(M * M' + 5 * I) |
| 156 | + F = cs_cholesky(A) |
| 157 | + _ = F \ randn(5) |
| 158 | + finalize(F) |
| 159 | + finalize(F) |
| 160 | + @test F.S == C_NULL |
| 161 | + @test F.N == C_NULL |
| 162 | + end |
| 163 | +end |
0 commit comments