Skip to content

Commit a7c2ce8

Browse files
dkarraschfredrikekre
authored andcommitted
fix eigen in the diagonal 2x2-case (#524)
1 parent 8c89f41 commit a7c2ce8

File tree

2 files changed

+60
-32
lines changed

2 files changed

+60
-32
lines changed

src/eigen.jl

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -133,52 +133,73 @@ end
133133

134134
@inline function _eig(::Size{(2,2)}, A::LinearAlgebra.RealHermSymComplexHerm{T}, permute, scale) where {T <: Real}
135135
a = A.data
136+
TA = eltype(A)
137+
138+
@inbounds if A.uplo == 'U'
139+
if a[3] == 0 # A is diagonal
140+
A11 = a[1]
141+
A22 = a[4]
142+
if A11 < A22
143+
vals = SVector(A11, A22)
144+
vecs = @SMatrix [convert(TA, 1) convert(TA, 0);
145+
convert(TA, 0) convert(TA, 1)]
146+
else # A22 <= A11
147+
vals = SVector(A22, A11)
148+
vecs = @SMatrix [convert(TA, 0) convert(TA, 1);
149+
convert(TA, 1) convert(TA, 0)]
150+
end
151+
else # A is not diagonal
152+
t_half = real(a[1] + a[4]) / 2
153+
d = real(a[1] * a[4] - a[3]' * a[3]) # Should be real
136154

137-
if A.uplo == 'U'
138-
@inbounds t_half = real(a[1] + a[4])/2
139-
@inbounds d = real(a[1]*a[4] - a[3]'*a[3]) # Should be real
140-
141-
tmp2 = t_half*t_half - d
142-
tmp2 < 0 ? tmp = zero(tmp2) : tmp = sqrt(tmp2) # Numerically stable for identity matrices, etc.
143-
vals = SVector(t_half - tmp, t_half + tmp)
155+
tmp2 = t_half * t_half - d
156+
tmp = tmp2 < 0 ? zero(tmp2) : sqrt(tmp2) # Numerically stable for identity matrices, etc.
157+
vals = SVector(t_half - tmp, t_half + tmp)
144158

145-
@inbounds if a[3] == 0
146-
vecs = SMatrix{2,2,eltype(A)}(I)
147-
else
148-
@inbounds v11 = vals[1]-a[4]
149-
@inbounds n1 = sqrt(v11'*v11 + a[3]'*a[3])
159+
v11 = vals[1] - a[4]
160+
n1 = sqrt(v11' * v11 + a[3]' * a[3])
150161
v11 = v11 / n1
151-
@inbounds v12 = a[3]' / n1
162+
v12 = a[3]' / n1
152163

153-
@inbounds v21 = vals[2]-a[4]
154-
@inbounds n2 = sqrt(v21'*v21 + a[3]'*a[3])
164+
v21 = vals[2] - a[4]
165+
n2 = sqrt(v21' * v21 + a[3]' * a[3])
155166
v21 = v21 / n2
156-
@inbounds v22 = a[3]' / n2
167+
v22 = a[3]' / n2
157168

158169
vecs = @SMatrix [ v11 v21 ;
159170
v12 v22 ]
160171
end
161-
return (vals,vecs)
162-
else
163-
@inbounds t_half = real(a[1] + a[4])/2
164-
@inbounds d = real(a[1]*a[4] - a[2]'*a[2]) # Should be real
172+
return (vals, vecs)
173+
else # A.uplo == 'L'
174+
if a[2] == 0 # A is diagonal
175+
A11 = a[1]
176+
A22 = a[4]
177+
if A11 < A22
178+
vals = SVector(A11, A22)
179+
vecs = @SMatrix [convert(TA, 1) convert(TA, 0);
180+
convert(TA, 0) convert(TA, 1)]
181+
else # A22 <= A11
182+
vals = SVector(A22, A11)
183+
vecs = @SMatrix [convert(TA, 0) convert(TA, 1);
184+
convert(TA, 1) convert(TA, 0)]
185+
end
186+
else # A is not diagonal
187+
t_half = real(a[1] + a[4]) / 2
188+
d = real(a[1] * a[4] - a[2]' * a[2]) # Should be real
165189

166-
tmp2 = t_half*t_half - d
167-
tmp2 < 0 ? tmp = zero(tmp2) : tmp = sqrt(tmp2) # Numerically stable for identity matrices, etc.
168-
vals = SVector(t_half - tmp, t_half + tmp)
190+
tmp2 = t_half * t_half - d
191+
tmp = tmp2 < 0 ? zero(tmp2) : sqrt(tmp2) # Numerically stable for identity matrices, etc.
192+
vals = SVector(t_half - tmp, t_half + tmp)
169193

170-
@inbounds if a[2] == 0
171-
vecs = SMatrix{2,2,eltype(A)}(I)
172-
else
173-
@inbounds v11 = vals[1]-a[4]
174-
@inbounds n1 = sqrt(v11'*v11 + a[2]'*a[2])
194+
v11 = vals[1] - a[4]
195+
n1 = sqrt(v11' * v11 + a[2]' * a[2])
175196
v11 = v11 / n1
176-
@inbounds v12 = a[2] / n1
197+
v12 = a[2] / n1
177198

178-
@inbounds v21 = vals[2]-a[4]
179-
@inbounds n2 = sqrt(v21'*v21 + a[2]'*a[2])
199+
v21 = vals[2] - a[4]
200+
n2 = sqrt(v21' * v21 + a[2]' * a[2])
180201
v21 = v21 / n2
181-
@inbounds v22 = a[2] / n2
202+
v22 = a[2] / n2
182203

183204
vecs = @SMatrix [ v11 v21 ;
184205
v12 v22 ]

test/eigen.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ using StaticArrays, Test, LinearAlgebra
6767
@test vals::SVector sort(m_d)
6868
@test eigvals(m) sort(m_d)
6969
@test eigvals(Hermitian(m)) sort(m_d)
70+
71+
# issue #523
72+
for (i, j) in ((1, 2), (2, 1)), uplo in (:U, :L)
73+
A = SMatrix{2,2,Float64}((i, 0, 0, j))
74+
E = eigen(Symmetric(A, uplo))
75+
@test eigvecs(E) * SDiagonal(eigvals(E)) * eigvecs(E)' A
76+
end
7077
end
7178

7279
@testset "3×3" for i = 1:100

0 commit comments

Comments
 (0)