Skip to content

Commit ea8e858

Browse files
authored
Test Diagonal functions against non-dense matrices (#1316)
In the `Diagonal` tests, we often compare results of function evaluations with that for the corresponding dense matrix. E.g., with `D = Diagonal(::Vector)` and `DM = Matrix(D)`, ```julia @test log(Diagonal(abs.(D.diag))) ≈ log(abs.(DM)) atol=n^3*eps(relty) ``` The problem with this is that, in the dense method `log(::Matrix)`, we check if the matrix is diagonal, and dispatch to the `Diagonal` methods for performance. This makes this test useless, as we are comparing identical methods. In this PR, I've changed the type of `DM` to be `Hermitian` for real matrices, and `UpperTriangular` for complex ones. Both of these types have special methods for `log`, at least. The `Hermitian` matrices have special methods for all the trigonometric functions, and for the complex matrices, we wrap these in a custom wrapper `NotDiagonal` that avoids the `isdiag` paths. This wrapper lies about the structure, but it should be ok for the testing purposes as we ensure that we are comparing different paths.
1 parent 2d35f07 commit ea8e858

File tree

1 file changed

+23
-7
lines changed

1 file changed

+23
-7
lines changed

test/diagonal.jl

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ using .Main.ImmutableArrays
2727
const n=12 # Size of matrix problem to test
2828
Random.seed!(1)
2929

30+
# wrapper to avoid dispatching to diagonal methods
31+
struct NotDiagonal{T,A<:AbstractMatrix{T}} <: AbstractMatrix{T}
32+
a :: A
33+
end
34+
Base.size(N::NotDiagonal) = size(N.a)
35+
Base.getindex(N::NotDiagonal, i::Int, j::Int) = N.a[i, j]
36+
LinearAlgebra.isdiag(N::NotDiagonal) = false # this contradicts `getindex`
37+
LinearAlgebra.ishermitian(N::NotDiagonal) = ishermitian(N.a)
38+
LinearAlgebra.istriu(N::NotDiagonal) = istriu(N.a)
39+
LinearAlgebra.istril(N::NotDiagonal) = istril(N.a)
40+
3041
@testset for relty in (Float32, Float64, BigFloat), elty in (relty, Complex{relty})
3142
dd=convert(Vector{elty}, randn(n))
3243
vv=convert(Vector{elty}, randn(n))
@@ -37,13 +48,18 @@ Random.seed!(1)
3748
UU+=im*convert(Matrix{elty}, randn(n,n))
3849
end
3950
D = Diagonal(dd)
40-
DM = Matrix(Diagonal(dd))
51+
M = Matrix(D)
52+
# we can't directly compare with a Matrix, since the dense methods often dispatch
53+
# to Diagonal ones. We therefore compare with other structured matrix types
54+
# which have their own implementations.
55+
# We wrap the complex matrices in NotDiagonal to avoid falling back to Diagonal methods
56+
DM = elty <: Real ? Hermitian(M) : NotDiagonal(UpperTriangular(M))
4157

4258
@testset "constructor" begin
4359
for x in (dd, GenericArray(dd))
44-
@test Diagonal(x)::Diagonal{elty,typeof(x)} == DM
60+
@test Diagonal(x)::Diagonal{elty,typeof(x)} == M
4561
@test Diagonal(x).diag === x
46-
@test Diagonal{elty}(x)::Diagonal{elty,typeof(x)} == DM
62+
@test Diagonal{elty}(x)::Diagonal{elty,typeof(x)} == M
4763
@test Diagonal{elty}(x).diag === x
4864
@test Diagonal{elty}(D) === D
4965
end
@@ -83,9 +99,9 @@ Random.seed!(1)
8399
@test typeof(convert(Diagonal{ComplexF32},D)) <: Diagonal{ComplexF32}
84100
@test typeof(convert(AbstractMatrix{ComplexF32},D)) <: Diagonal{ComplexF32}
85101

86-
@test Array(real(D)) == real(DM)
87-
@test Array(abs.(D)) == abs.(DM)
88-
@test Array(imag(D)) == imag(DM)
102+
@test Array(real(D)) == real(M)
103+
@test Array(abs.(D)) == abs.(M)
104+
@test Array(imag(D)) == imag(M)
89105

90106
@test parent(D) == dd
91107
@test D[1,1] == dd[1]
@@ -227,7 +243,7 @@ Random.seed!(1)
227243
if elty <: Real
228244
@test Array(abs.(D)^a) abs.(DM)^a
229245
else
230-
@test Array(D^a) DM^a
246+
@test Array(D^a) DM^a rtol=max(eps(relty), 1e-15) # TODO: improve precision
231247
end
232248
@test Diagonal(1:100)^2 == Diagonal((1:100).^2)
233249
p = 3

0 commit comments

Comments
 (0)