Skip to content

Commit 2447365

Browse files
andreasnoackKristofferC
authored andcommitted
Use a stable inverse for Tridiagonal and SymTridiagonal (#29667)
* Use a stable inverse for Tridiagonal and SymTridiagonal Fixes #29630 * Fix ldlt in the complex case (cherry picked from commit 00bb4f2)
1 parent 1f92410 commit 2447365

File tree

3 files changed

+25
-38
lines changed

3 files changed

+25
-38
lines changed

stdlib/LinearAlgebra/src/ldlt.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ julia> S
4848
⋅ 0.545455 3.90909
4949
```
5050
"""
51-
function ldlt!(S::SymTridiagonal{T,V}) where {T<:Real,V}
51+
function ldlt!(S::SymTridiagonal{T,V}) where {T,V}
5252
n = size(S,1)
5353
d = S.dv
5454
e = S.ev
5555
@inbounds @simd for i = 1:n-1
5656
e[i] /= d[i]
57-
d[i+1] -= abs2(e[i])*d[i]
57+
d[i+1] -= e[i]^2*d[i]
5858
end
5959
return LDLt{T,SymTridiagonal{T,V}}(S)
6060
end

stdlib/LinearAlgebra/src/tridiag.jl

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -320,44 +320,13 @@ function Base.replace_in_print_matrix(A::SymTridiagonal, i::Integer, j::Integer,
320320
i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s)
321321
end
322322

323-
#Implements the inverse using the recurrence relation between principal minors
323+
# Implements the determinant using principal minors
324324
# a, b, c are assumed to be the subdiagonal, diagonal, and superdiagonal of
325325
# a tridiagonal matrix.
326326
#Reference:
327327
# R. Usmani, "Inversion of a tridiagonal Jacobi matrix",
328328
# Linear Algebra and its Applications 212-213 (1994), pp.413-414
329329
# doi:10.1016/0024-3795(94)90414-6
330-
function inv_usmani(a::V, b::V, c::V) where {T,V<:AbstractVector{T}}
331-
@assert !has_offset_axes(a, b, c)
332-
n = length(b)
333-
θ = zeros(T, n+1) #principal minors of A
334-
θ[1] = 1
335-
n>=1 && (θ[2] = b[1])
336-
for i=2:n
337-
θ[i+1] = b[i]*θ[i]-a[i-1]*c[i-1]*θ[i-1]
338-
end
339-
φ = zeros(T, n+1)
340-
φ[n+1] = 1
341-
n>=1 && (φ[n] = b[n])
342-
for i=n-1:-1:1
343-
φ[i] = b[i]*φ[i+1]-a[i]*c[i]*φ[i+2]
344-
end
345-
α = Matrix{T}(undef, n, n)
346-
for i=1:n, j=1:n
347-
sign = (i+j)%2==0 ? (+) : (-)
348-
if i<j
349-
α[i,j]=(sign)(prod(c[i:j-1]))*θ[i]*φ[j+1]/θ[n+1]
350-
elseif i==j
351-
α[i,i]= θ[i]*φ[i+1]/θ[n+1]
352-
else #i>j
353-
α[i,j]=(sign)(prod(a[j:i-1]))*θ[j]*φ[i+1]/θ[n+1]
354-
end
355-
end
356-
α
357-
end
358-
359-
#Implements the determinant using principal minors
360-
#Inputs and reference are as above for inv_usmani()
361330
function det_usmani(a::V, b::V, c::V) where {T,V<:AbstractVector{T}}
362331
@assert !has_offset_axes(a, b, c)
363332
n = length(b)
@@ -366,13 +335,12 @@ function det_usmani(a::V, b::V, c::V) where {T,V<:AbstractVector{T}}
366335
return θa
367336
end
368337
θb = b[1]
369-
for i=2:n
370-
θb, θa = b[i]*θb-a[i-1]*c[i-1]*θa, θb
338+
for i in 2:n
339+
θb, θa = b[i]*θb - a[i-1]*c[i-1]*θa, θb
371340
end
372341
return θb
373342
end
374343

375-
inv(A::SymTridiagonal) = inv_usmani(A.ev, A.dv, A.ev)
376344
det(A::SymTridiagonal) = det_usmani(A.ev, A.dv, A.ev)
377345

378346
function getindex(A::SymTridiagonal{T}, i::Integer, j::Integer) where T
@@ -651,7 +619,6 @@ end
651619
==(A::Tridiagonal, B::SymTridiagonal) = (A.dl==A.du==B.ev) && (A.d==B.dv)
652620
==(A::SymTridiagonal, B::Tridiagonal) = (B.dl==B.du==A.ev) && (B.d==A.dv)
653621

654-
inv(A::Tridiagonal) = inv_usmani(A.dl, A.d, A.du)
655622
det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du)
656623

657624
AbstractMatrix{T}(M::Tridiagonal) where {T} = Tridiagonal{T}(M)

stdlib/LinearAlgebra/test/tridiag.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,4 +391,24 @@ end
391391
"LinearAlgebra.LU{Float64,LinearAlgebra.Tridiagonal{Float64,SparseArrays.SparseVector")
392392
end
393393

394+
@testset "Issue 29630" begin
395+
function central_difference_discretization(N; dfunc = x -> 12x^2 - 2N^2,
396+
dufunc = x -> N^2 + 4N*x,
397+
dlfunc = x -> N^2 - 4N*x,
398+
bfunc = x -> 114^-x * (1 + 3x),
399+
b0 = 0, bf = 57/ℯ,
400+
x0 = 0, xf = 1)
401+
h = 1/N
402+
d, du, dl, b = map(dfunc, (x0+h):h:(xf-h)), map(dufunc, (x0+h):h:(xf-2h)),
403+
map(dlfunc, (x0+2h):h:(xf-h)), map(bfunc, (x0+h):h:(xf-h))
404+
b[1] -= dlfunc(x0)*b0 # subtract the boundary term
405+
b[end] -= dufunc(xf)*bf # subtract the boundary term
406+
Tridiagonal(dl, d, du), b
407+
end
408+
409+
A90, b90 = central_difference_discretization(90)
410+
411+
@test A90\b90 inv(A90)*b90
412+
end
413+
394414
end # module TestTridiagonal

0 commit comments

Comments
 (0)