|
| 1 | +using StaticArrays, Base.Test |
| 2 | + |
| 3 | +""" |
| 4 | +Create an almost singular `Matrix{Float64}` of size `N×N`. |
| 5 | +
|
| 6 | +The first `rank` columns are chosen randomly, followed by `N-rank` linear |
| 7 | +combinations with random weights to create an `N×N` singular matrix `S`. |
| 8 | +A noise matrix of norm `ϵ*norm(S)` is added. |
| 9 | +
|
| 10 | +The condition number of this matrix should be approximately ϵ/eps(Float64) |
| 11 | +""" |
| 12 | +function almost_singular_matrix(N, rank, ϵ) |
| 13 | + B = rand(N,rank) |
| 14 | + weights = rand(rank, N-rank) ./ rank |
| 15 | + S = [B B*weights] |
| 16 | + S = S ./ norm(S) |
| 17 | + noise = rand(N,N) |
| 18 | + S + ϵ*norm(S)/norm(noise) * noise |
| 19 | +end |
| 20 | + |
1 | 21 | @testset "Matrix inverse" begin
|
2 | 22 | @test inv(@SMatrix [2])::SMatrix ≈ @SMatrix [0.5]
|
3 | 23 | @test inv(@SMatrix [1 2; 2 1])::SMatrix ≈ [-1/3 2/3; 2/3 -1/3]
|
4 | 24 | @test inv(@SMatrix [1 2 0; 2 1 0; 0 0 1])::SMatrix ≈ [-1/3 2/3 0; 2/3 -1/3 0; 0 0 1]
|
5 |
| - m = randn(Float64, 4,4) + eye(4) # well conditioned |
6 |
| - @test inv(SMatrix{4,4}(m))::StaticMatrix ≈ inv(m) |
7 | 25 |
|
| 26 | + m = randn(Float64, 10,10) + eye(10) # well conditioned |
| 27 | + @test inv(SMatrix{10,10}(m))::StaticMatrix ≈ inv(m) |
| 28 | + |
| 29 | + |
| 30 | + # Unsigned versions |
8 | 31 | @test inv(@SMatrix [0x01 0x02; 0x02 0x01])::SMatrix ≈ [-1/3 2/3; 2/3 -1/3]
|
9 | 32 | @test inv(@SMatrix [0x01 0x02 0x00; 0x02 0x01 0x00; 0x00 0x00 0x01])::SMatrix ≈ [-1/3 2/3 0; 2/3 -1/3 0; 0 0 1]
|
10 |
| - |
| 33 | + |
11 | 34 | m = triu(randn(Float64, 4,4) + eye(4))
|
12 | 35 | @test inv(SMatrix{4,4}(m))::StaticMatrix ≈ inv(m)
|
13 | 36 | m = tril(randn(Float64, 4,4) + eye(4))
|
14 | 37 | @test inv(SMatrix{4,4}(m))::StaticMatrix ≈ inv(m)
|
15 | 38 | end
|
| 39 | + |
| 40 | + |
| 41 | +@testset "Matrix inverse 4x4" begin |
| 42 | + # A random well-conditioned matrix generated with `rand(1:10,4,4)` |
| 43 | + m = Float64[8 4 3 1; |
| 44 | + 9 1 9 8; |
| 45 | + 5 0 2 1; |
| 46 | + 7 5 7 2] |
| 47 | + sm = SMatrix{4,4}(m) |
| 48 | + @test isapprox(inv(sm)::StaticMatrix, inv(m), rtol=2e-15) |
| 49 | + |
| 50 | + # Poorly conditioned matrix; almost_singular_matrix(4, 3, 1e-7) |
| 51 | + m = [ |
| 52 | + 2.83056817904263402e-01 1.26822318692296848e-01 2.59665505365002547e-01 1.24524798964590747e-01 |
| 53 | + 4.29869029098094768e-01 2.29977256378012789e-03 4.63841570354149135e-01 1.51975221831027241e-01 |
| 54 | + 2.33883312850939995e-01 7.69469213907006122e-02 3.16525493661056589e-01 1.06181613512460249e-01 |
| 55 | + 1.63327582123091175e-01 1.70552365412100865e-01 4.55720450934200327e-01 1.23299968650232419e-01 |
| 56 | + ] |
| 57 | + sm = SMatrix{4,4}(m) |
| 58 | + @test norm(Matrix(sm*inv(sm) - eye(4))) < 10*norm(m*inv(m) - eye(4)) |
| 59 | + @test norm(Matrix(inv(sm)*sm - eye(4))) < 10*norm(inv(m)*m - eye(4)) |
| 60 | + |
| 61 | + # Poorly conditioned matrix, generated by fuzz testing with |
| 62 | + # almost_singular_matrix(N, 3, 1e-7) |
| 63 | + # Case where the 4x4 algorithm fares badly compared to Base inv |
| 64 | + m = [1.80589503332920231e-02 2.37595944073211218e-01 3.80770755039433806e-01 5.70474286930408372e-02 |
| 65 | + 2.17494123374967520e-02 2.94069278741667661e-01 2.57334651147732463e-01 4.02348559939922357e-02 |
| 66 | + 3.42945636322714076e-01 3.29329508494837497e-01 2.25635541033857107e-01 6.03912636987153917e-02 |
| 67 | + 4.77828437366344727e-01 4.86406974015710591e-01 1.95415684569693188e-01 6.74775080892497797e-02] |
| 68 | + sm = SMatrix{4,4}(m) |
| 69 | + @test_broken norm(Matrix(inv(sm)*sm - eye(4))) < 10*norm(inv(m)*m - eye(4)) |
| 70 | + @test_broken norm(Matrix(sm*inv(sm) - eye(4))) < 10*norm(m*inv(m) - eye(4)) |
| 71 | +end |
| 72 | + |
| 73 | + |
| 74 | +#------------------------------------------------------------------------------- |
| 75 | +# More comprehensive but qualitiative testing for inv() accuracy |
| 76 | +#= |
| 77 | +using PyPlot |
| 78 | +
|
| 79 | +inv_residual(A::AbstractMatrix) = norm(A*inv(A) - eye(size(A,1))) |
| 80 | +
|
| 81 | +""" |
| 82 | + plot_residuals(N, rank, ϵ) |
| 83 | +
|
| 84 | +Plot `inv_residual(::StaticMatrix)` vs `inv_residual(::Matrix)` |
| 85 | +
|
| 86 | +""" |
| 87 | +function plot_residuals(N, rank, ϵ) |
| 88 | + A_residuals = [] |
| 89 | + SA_residuals = [] |
| 90 | + for i=1:10000 |
| 91 | + A = almost_singular_matrix(N, rank, ϵ) |
| 92 | + SA = SMatrix{N,N}(A) |
| 93 | + SA_residual = norm(Matrix(SA*inv(SA) - eye(N))) |
| 94 | + A_residual = norm(A*inv(A) - eye(N)) |
| 95 | + push!(SA_residuals, SA_residual) |
| 96 | + push!(A_residuals, A_residual) |
| 97 | + #= if SA_residual/A_residual > 10000 =# |
| 98 | + #= @printf("%10e %.4f\n", SA_residual, SA_residual/A_residual) =# |
| 99 | + #= println("[") =# |
| 100 | + #= for i=1:4 =# |
| 101 | + #= for j=1:4 =# |
| 102 | + #= @printf("%.17e ", A[i,j]) =# |
| 103 | + #= end =# |
| 104 | + #= println() =# |
| 105 | + #= end =# |
| 106 | + #= println("]") =# |
| 107 | + #= end =# |
| 108 | + end |
| 109 | + loglog(A_residuals, SA_residuals, ".", markersize=1.5) |
| 110 | +end |
| 111 | +
|
| 112 | +# Plot the accuracy of inv implementations for almost singular matrices of |
| 113 | +# various rank |
| 114 | +clf() |
| 115 | +N = 4 |
| 116 | +title("inv() accuracy for poorly conditioned $(N)x$(N) - Base vs block decomposition") |
| 117 | +labels = [] |
| 118 | +for i in N:-1:1 |
| 119 | + plot_residuals(N, i, 1e-7) |
| 120 | + push!(labels, "rank $i") |
| 121 | +end |
| 122 | +xlabel("Residual norm: `inv(::Array)`") |
| 123 | +ylabel("Residual norm: `inv(::StaticArray)`") |
| 124 | +legend(labels) |
| 125 | +=# |
0 commit comments