• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37929

11 Oct 2024 05:57AM UTC coverage: 87.748% (-0.02%) from 87.764%
#37929

push

local

web-flow
Remove warning from c when binding is ambiguous (#56103)

78875 of 89888 relevant lines covered (87.75%)

16976924.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.59
/stdlib/LinearAlgebra/src/tridiag.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
#### Specialized matrix types ####
4

5
## (complex) symmetric tridiagonal matrices
6
struct SymTridiagonal{T, V<:AbstractVector{T}} <: AbstractMatrix{T}
7
    dv::V                        # diagonal
8
    ev::V                        # superdiagonal
9
    function SymTridiagonal{T, V}(dv, ev) where {T, V<:AbstractVector{T}}
5✔
10
        require_one_based_indexing(dv, ev)
2,446✔
11
        if !(length(dv) - 1 <= length(ev) <= length(dv))
2,446✔
12
            throw(DimensionMismatch(lazy"subdiagonal has wrong length. Has length $(length(ev)), but should be either $(length(dv) - 1) or $(length(dv))."))
5✔
13
        end
14
        new{T, V}(dv, ev)
2,441✔
15
    end
16
end
17

18
"""
19
    SymTridiagonal(dv::V, ev::V) where V <: AbstractVector
20

21
Construct a symmetric tridiagonal matrix from the diagonal (`dv`) and first
22
sub/super-diagonal (`ev`), respectively. The result is of type `SymTridiagonal`
23
and provides efficient specialized eigensolvers, but may be converted into a
24
regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short).
25

26
For `SymTridiagonal` block matrices, the elements of `dv` are symmetrized.
27
The argument `ev` is interpreted as the superdiagonal. Blocks from the
28
subdiagonal are (materialized) transpose of the corresponding superdiagonal blocks.
29

30
# Examples
31
```jldoctest
32
julia> dv = [1, 2, 3, 4]
33
4-element Vector{Int64}:
34
 1
35
 2
36
 3
37
 4
38

39
julia> ev = [7, 8, 9]
40
3-element Vector{Int64}:
41
 7
42
 8
43
 9
44

45
julia> SymTridiagonal(dv, ev)
46
4×4 SymTridiagonal{Int64, Vector{Int64}}:
47
 1  7  ⋅  ⋅
48
 7  2  8  ⋅
49
 ⋅  8  3  9
50
 ⋅  ⋅  9  4
51

52
julia> A = SymTridiagonal(fill([1 2; 3 4], 3), fill([1 2; 3 4], 2));
53

54
julia> A[1,1]
55
2×2 Symmetric{Int64, Matrix{Int64}}:
56
 1  2
57
 2  4
58

59
julia> A[1,2]
60
2×2 Matrix{Int64}:
61
 1  2
62
 3  4
63

64
julia> A[2,1]
65
2×2 Matrix{Int64}:
66
 1  3
67
 2  4
68
```
69
"""
70
SymTridiagonal(dv::V, ev::V) where {T,V<:AbstractVector{T}} = SymTridiagonal{T}(dv, ev)
2,278✔
71
SymTridiagonal{T}(dv::V, ev::V) where {T,V<:AbstractVector{T}} = SymTridiagonal{T,V}(dv, ev)
2,441✔
72
function SymTridiagonal{T}(dv::AbstractVector, ev::AbstractVector) where {T}
27✔
73
    d = convert(AbstractVector{T}, dv)::AbstractVector{T}
45✔
74
    e = convert(AbstractVector{T}, ev)::AbstractVector{T}
42✔
75
    typeof(d) == typeof(e) ?
30✔
76
        SymTridiagonal{T}(d, e) :
77
        throw(ArgumentError("diagonal vectors needed to be convertible to same type"))
78
end
79
SymTridiagonal(d::AbstractVector{T}, e::AbstractVector{S}) where {T,S} =
15✔
80
    SymTridiagonal{promote_type(T, S)}(d, e)
81

82
"""
83
    SymTridiagonal(A::AbstractMatrix)
84

85
Construct a symmetric tridiagonal matrix from the diagonal and first superdiagonal
86
of the symmetric matrix `A`.
87

88
# Examples
89
```jldoctest
90
julia> A = [1 2 3; 2 4 5; 3 5 6]
91
3×3 Matrix{Int64}:
92
 1  2  3
93
 2  4  5
94
 3  5  6
95

96
julia> SymTridiagonal(A)
97
3×3 SymTridiagonal{Int64, Vector{Int64}}:
98
 1  2  ⋅
99
 2  4  5
100
 ⋅  5  6
101

102
julia> B = reshape([[1 2; 2 3], [1 2; 3 4], [1 3; 2 4], [1 2; 2 3]], 2, 2);
103

104
julia> SymTridiagonal(B)
105
2×2 SymTridiagonal{Matrix{Int64}, Vector{Matrix{Int64}}}:
106
 [1 2; 2 3]  [1 3; 2 4]
107
 [1 2; 3 4]  [1 2; 2 3]
108
```
109
"""
110
function SymTridiagonal(A::AbstractMatrix)
527✔
111
    checksquare(A)
527✔
112
    du = diag(A, 1)
527✔
113
    d  = diag(A)
527✔
114
    dl = diag(A, -1)
527✔
115
    if all(((x, y),) -> x == transpose(y), zip(du, dl)) && all(issymmetric, d)
1,656✔
116
        SymTridiagonal(d, du)
522✔
117
    else
118
        throw(ArgumentError("matrix is not symmetric; cannot convert to SymTridiagonal"))
5✔
119
    end
120
end
121

122
SymTridiagonal{T,V}(S::SymTridiagonal{T,V}) where {T,V<:AbstractVector{T}} = S
16✔
123
SymTridiagonal{T,V}(S::SymTridiagonal) where {T,V<:AbstractVector{T}} =
4✔
124
    SymTridiagonal(convert(V, S.dv)::V, convert(V, S.ev)::V)
125
SymTridiagonal{T}(S::SymTridiagonal{T}) where {T} = S
1✔
126
SymTridiagonal{T}(S::SymTridiagonal) where {T} =
12✔
127
    SymTridiagonal(convert(AbstractVector{T}, S.dv)::AbstractVector{T},
128
                    convert(AbstractVector{T}, S.ev)::AbstractVector{T})
129
SymTridiagonal(S::SymTridiagonal) = S
5✔
130

131
AbstractMatrix{T}(S::SymTridiagonal) where {T} = SymTridiagonal{T}(S)
7✔
132
AbstractMatrix{T}(S::SymTridiagonal{T}) where {T} = copy(S)
×
133

134
function Matrix{T}(M::SymTridiagonal) where T
1,923✔
135
    n = size(M, 1)
1,923✔
136
    Mf = Matrix{T}(undef, n, n)
3,843✔
137
    n == 0 && return Mf
1,923✔
138
    if haszero(T) # optimized path for types with zero(T) defined
1,920✔
139
        n > 2 && fill!(Mf, zero(T))
107,363✔
140
        @inbounds for i = 1:n-1
1,906✔
141
            Mf[i,i] = symmetric(M.dv[i], :U)
10,765✔
142
            Mf[i+1,i] = transpose(M.ev[i])
10,765✔
143
            Mf[i,i+1] = M.ev[i]
10,765✔
144
        end
19,638✔
145
        Mf[n,n] = symmetric(M.dv[n], :U)
1,906✔
146
    else
147
        copyto!(Mf, M)
28✔
148
    end
149
    return Mf
1,920✔
150
end
151
Matrix(M::SymTridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M)
1,852✔
152
Array(M::SymTridiagonal) = Matrix(M)
217✔
153

154
size(A::SymTridiagonal) = (n = length(A.dv); (n, n))
14,115✔
155
axes(M::SymTridiagonal) = (ax = axes(M.dv, 1); (ax, ax))
177,788✔
156

157
similar(S::SymTridiagonal, ::Type{T}) where {T} = SymTridiagonal(similar(S.dv, T), similar(S.ev, T))
278✔
158
similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(S.dv, T, dims)
1,362✔
159

160
# copyto! for matching axes
161
_copyto_banded!(dest::SymTridiagonal, src::SymTridiagonal) =
190✔
162
    (copyto!(dest.dv, src.dv); copyto!(dest.ev, _evview(src)); dest)
95✔
163

164
#Elementary operations
165
for func in (:conj, :copy, :real, :imag)
166
    @eval ($func)(M::SymTridiagonal) = SymTridiagonal(($func)(M.dv), ($func)(M.ev))
92✔
167
end
168

169
transpose(S::SymTridiagonal) = S
262✔
170
adjoint(S::SymTridiagonal{<:Number}) = SymTridiagonal(vec(adjoint(S.dv)), vec(adjoint(S.ev)))
273✔
171
adjoint(S::SymTridiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) =
4✔
172
    SymTridiagonal(adjoint(parent(S.dv)), adjoint(parent(S.ev)))
173

174
permutedims(S::SymTridiagonal) = S
25✔
175
function permutedims(S::SymTridiagonal, perm)
10✔
176
    Base.checkdims_perm(axes(S), axes(S), perm)
15✔
177
    NTuple{2}(perm) == (2, 1) ? permutedims(S) : S
10✔
178
end
179
Base.copy(S::Adjoint{<:Any,<:SymTridiagonal}) = SymTridiagonal(map(x -> copy.(adjoint.(x)), (S.parent.dv, S.parent.ev))...)
×
180

181
ishermitian(S::SymTridiagonal) = isreal(S.dv) && isreal(_evview(S))
20✔
182
issymmetric(S::SymTridiagonal) = true
×
183

184
tr(S::SymTridiagonal) = sum(symmetric, S.dv)
7✔
185

186
_diagiter(M::SymTridiagonal{<:Number}) = M.dv
37✔
187
_diagiter(M::SymTridiagonal) = (symmetric(x, :U) for x in M.dv)
3✔
188
_eviter_transposed(M::SymTridiagonal{<:Number}) = _evview(M)
12✔
189
_eviter_transposed(M::SymTridiagonal) = (transpose(x) for x in _evview(M))
1✔
190

191
function diag(M::SymTridiagonal, n::Integer=0)
114✔
192
    # every branch call similar(..., ::Int) to make sure the
193
    # same vector type is returned independent of n
194
    v = similar(M.dv, max(0, length(M.dv)-abs(n)))
205✔
195
    if n == 0
102✔
196
        return copyto!(v, _diagiter(M))
40✔
197
    elseif n == 1
62✔
198
        return copyto!(v, _evview(M))
26✔
199
    elseif n == -1
36✔
200
        return copyto!(v, _eviter_transposed(M))
13✔
201
    else
202
        for i in eachindex(v)
23✔
203
            v[i] = M[BandIndex(n,i)]
11✔
204
        end
11✔
205
    end
206
    return v
21✔
207
end
208

209
+(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv+B.dv, _evview(A)+_evview(B))
61✔
210
-(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv-B.dv, _evview(A)-_evview(B))
59✔
211
-(A::SymTridiagonal) = SymTridiagonal(-A.dv, -A.ev)
16✔
212
*(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv*B, A.ev*B)
6✔
213
*(B::Number, A::SymTridiagonal) = SymTridiagonal(B*A.dv, B*A.ev)
6✔
214
function rmul!(A::SymTridiagonal, x::Number)
3✔
215
    if size(A,1) > 2
3✔
216
        # ensure that zeros are preserved on scaling
217
        y = A[3,1] * x
2✔
218
        iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ",
3✔
219
            lazy"the tridiagonal band to a nonzero value ($y)")))
220
    end
221
    A.dv .*= x
2✔
222
    _evview(A) .*= x
2✔
223
    return A
2✔
224
end
225
function lmul!(x::Number, B::SymTridiagonal)
3✔
226
    if size(B,1) > 2
3✔
227
        # ensure that zeros are preserved on scaling
228
        y = x * B[3,1]
2✔
229
        iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ",
3✔
230
            lazy"the tridiagonal band to a nonzero value ($y)")))
231
    end
232
    @. B.dv = x * B.dv
2✔
233
    ev = _evview(B)
2✔
234
    @. ev = x * ev
2✔
235
    return B
2✔
236
end
237
/(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv/B, A.ev/B)
6✔
238
\(B::Number, A::SymTridiagonal) = SymTridiagonal(B\A.dv, B\A.ev)
1✔
239
==(A::SymTridiagonal{<:Number}, B::SymTridiagonal{<:Number}) =
85✔
240
    (A.dv == B.dv) && (_evview(A) == _evview(B))
241
==(A::SymTridiagonal, B::SymTridiagonal) =
16✔
242
    size(A) == size(B) && all(i -> A[i,i] == B[i,i], axes(A, 1)) && (_evview(A) == _evview(B))
52✔
243

244
function dot(x::AbstractVector, S::SymTridiagonal, y::AbstractVector)
40✔
245
    require_one_based_indexing(x, y)
40✔
246
    nx, ny = length(x), length(y)
40✔
247
    (nx == size(S, 1) == ny) || throw(DimensionMismatch("dot"))
40✔
248
    if nx ≤ 1
40✔
249
        nx == 0 && return dot(zero(eltype(x)), zero(eltype(S)), zero(eltype(y)))
25✔
250
        return dot(x[1], S.dv[1], y[1])
10✔
251
    end
252
    dv, ev = S.dv, S.ev
15✔
253
    @inbounds begin
15✔
254
        x₀ = x[1]
15✔
255
        x₊ = x[2]
15✔
256
        sub = transpose(ev[1])
15✔
257
        r = dot(adjoint(dv[1])*x₀ + adjoint(sub)*x₊, y[1])
15✔
258
        for j in 2:nx-1
15✔
259
            x₋, x₀, x₊ = x₀, x₊, x[j+1]
130✔
260
            sup, sub = transpose(sub), transpose(ev[j])
130✔
261
            r += dot(adjoint(sup)*x₋ + adjoint(dv[j])*x₀ + adjoint(sub)*x₊, y[j])
130✔
262
        end
245✔
263
        r += dot(adjoint(transpose(sub))*x₀ + adjoint(dv[nx])*x₊, y[nx])
15✔
264
    end
265
    return r
15✔
266
end
267

268
(\)(T::SymTridiagonal, B::AbstractVecOrMat) = ldlt(T)\B
19✔
269

270
# division with optional shift for use in shifted-Hessenberg solvers (hessenberg.jl):
271
ldiv!(A::SymTridiagonal, B::AbstractVecOrMat; shift::Number=false) = ldiv!(ldlt(A, shift=shift), B)
90✔
272
rdiv!(B::AbstractVecOrMat, A::SymTridiagonal; shift::Number=false) = rdiv!(B, ldlt(A, shift=shift))
90✔
273

274
eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}) = Eigen(LAPACK.stegr!('V', A.dv, A.ev)...)
22✔
275
eigen(A::SymTridiagonal{T}) where T = eigen!(copymutable_oftype(A, eigtype(T)))
38✔
276

277
eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, irange::UnitRange) =
4✔
278
    Eigen(LAPACK.stegr!('V', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)...)
279
eigen(A::SymTridiagonal{T}, irange::UnitRange) where T =
4✔
280
    eigen!(copymutable_oftype(A, eigtype(T)), irange)
281

282
eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, vl::Real, vu::Real) =
4✔
283
    Eigen(LAPACK.stegr!('V', 'V', A.dv, A.ev, vl, vu, 0, 0)...)
284
eigen(A::SymTridiagonal{T}, vl::Real, vu::Real) where T =
4✔
285
    eigen!(copymutable_oftype(A, eigtype(T)), vl, vu)
286

287
eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}) = LAPACK.stev!('N', A.dv, A.ev)[1]
15✔
288
eigvals(A::SymTridiagonal{T}) where T = eigvals!(copymutable_oftype(A, eigtype(T)))
16✔
289

290
eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, irange::UnitRange) =
2✔
291
    LAPACK.stegr!('N', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)[1]
292
eigvals(A::SymTridiagonal{T}, irange::UnitRange) where T =
4✔
293
    eigvals!(copymutable_oftype(A, eigtype(T)), irange)
294

295
eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, vl::Real, vu::Real) =
3✔
296
    LAPACK.stegr!('N', 'V', A.dv, A.ev, vl, vu, 0, 0)[1]
297
eigvals(A::SymTridiagonal{T}, vl::Real, vu::Real) where T =
6✔
298
    eigvals!(copymutable_oftype(A, eigtype(T)), vl, vu)
299

300
#Computes largest and smallest eigenvalue
301
eigmax(A::SymTridiagonal) = eigvals(A, size(A, 1):size(A, 1))[1]
1✔
302
eigmin(A::SymTridiagonal) = eigvals(A, 1:1)[1]
1✔
303

304
#Compute selected eigenvectors only corresponding to particular eigenvalues
305
"""
306
    eigvecs(A::SymTridiagonal[, eigvals]) -> Matrix
307

308
Return a matrix `M` whose columns are the eigenvectors of `A`. (The `k`th eigenvector can
309
be obtained from the slice `M[:, k]`.)
310

311
If the optional vector of eigenvalues `eigvals` is specified, `eigvecs`
312
returns the specific corresponding eigenvectors.
313

314
# Examples
315
```jldoctest
316
julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.])
317
3×3 SymTridiagonal{Float64, Vector{Float64}}:
318
 1.0  2.0   ⋅
319
 2.0  2.0  3.0
320
  ⋅   3.0  1.0
321

322
julia> eigvals(A)
323
3-element Vector{Float64}:
324
 -2.1400549446402604
325
  1.0000000000000002
326
  5.140054944640259
327

328
julia> eigvecs(A)
329
3×3 Matrix{Float64}:
330
  0.418304  -0.83205      0.364299
331
 -0.656749  -7.39009e-16  0.754109
332
  0.627457   0.5547       0.546448
333

334
julia> eigvecs(A, [1.])
335
3×1 Matrix{Float64}:
336
  0.8320502943378438
337
  4.263514128092366e-17
338
 -0.5547001962252291
339
```
340
"""
341
eigvecs(A::SymTridiagonal{<:BlasFloat,<:StridedVector}, eigvals::Vector{<:Real}) = LAPACK.stein!(A.dv, A.ev, eigvals)
4✔
342

343
function svdvals!(A::SymTridiagonal)
×
344
    vals = eigvals!(A)
×
345
    return sort!(map!(abs, vals, vals); rev=true)
×
346
end
347

348
# tril and triu
349

350
Base.@constprop :aggressive function istriu(M::SymTridiagonal, k::Integer=0)
35✔
351
    if k <= -1
69✔
352
        return true
19✔
353
    elseif k == 0
40✔
354
        return iszero(_evview(M))
25✔
355
    else # k >= 1
356
        return iszero(_evview(M)) && iszero(M.dv)
15✔
357
    end
358
end
359
Base.@constprop :aggressive istril(M::SymTridiagonal, k::Integer) = istriu(M, -k)
22✔
360
iszero(M::SymTridiagonal) =  iszero(_evview(M)) && iszero(M.dv)
30✔
361
isone(M::SymTridiagonal) =  iszero(_evview(M)) && all(isone, M.dv)
26✔
362
isdiag(M::SymTridiagonal) =  iszero(_evview(M))
46✔
363

364

365
function tril!(M::SymTridiagonal{T}, k::Integer=0) where T
55✔
366
    n = length(M.dv)
60✔
367
    if !(-n - 1 <= k <= n - 1)
50✔
368
        throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ",
10✔
369
            lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix")))
370
    elseif k < -1
40✔
371
        fill!(M.ev, zero(T))
110✔
372
        fill!(M.dv, zero(T))
120✔
373
        return Tridiagonal(M.ev,M.dv,copy(M.ev))
10✔
374
    elseif k == -1
30✔
375
        fill!(M.dv, zero(T))
120✔
376
        return Tridiagonal(M.ev,M.dv,zero(M.ev))
10✔
377
    elseif k == 0
20✔
378
        return Tridiagonal(M.ev,M.dv,zero(M.ev))
10✔
379
    else # if k >= 1
380
        return Tridiagonal(M.ev,M.dv,copy(M.ev))
10✔
381
    end
382
end
383

384
function triu!(M::SymTridiagonal{T}, k::Integer=0) where T
55✔
385
    n = length(M.dv)
60✔
386
    if !(-n + 1 <= k <= n + 1)
50✔
387
        throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ",
10✔
388
            lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix")))
389
    elseif k > 1
40✔
390
        fill!(M.ev, zero(T))
110✔
391
        fill!(M.dv, zero(T))
120✔
392
        return Tridiagonal(M.ev,M.dv,copy(M.ev))
10✔
393
    elseif k == 1
30✔
394
        fill!(M.dv, zero(T))
120✔
395
        return Tridiagonal(zero(M.ev),M.dv,M.ev)
10✔
396
    elseif k == 0
20✔
397
        return Tridiagonal(zero(M.ev),M.dv,M.ev)
10✔
398
    else # if k <= -1
399
        return Tridiagonal(M.ev,M.dv,copy(M.ev))
10✔
400
    end
401
end
402

403
###################
404
# Generic methods #
405
###################
406

407
## structured matrix methods ##
408
function Base.replace_in_print_matrix(A::SymTridiagonal, i::Integer, j::Integer, s::AbstractString)
1,240✔
409
    i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s)
1,240✔
410
end
411

412
# Implements the determinant using principal minors
413
# a, b, c are assumed to be the subdiagonal, diagonal, and superdiagonal of
414
# a tridiagonal matrix.
415
#Reference:
416
#    R. Usmani, "Inversion of a tridiagonal Jacobi matrix",
417
#    Linear Algebra and its Applications 212-213 (1994), pp.413-414
418
#    doi:10.1016/0024-3795(94)90414-6
419
function det_usmani(a::V, b::V, c::V, shift::Number=0) where {T,V<:AbstractVector{T}}
15✔
420
    require_one_based_indexing(a, b, c)
51✔
421
    n = length(b)
37✔
422
    θa = oneunit(T)+zero(shift)
37✔
423
    if n == 0
37✔
424
        return θa
4✔
425
    end
426
    θb = b[1]+shift
33✔
427
    for i in 2:n
33✔
428
        θb, θa = (b[i]+shift)*θb - a[i-1]*c[i-1]*θa, θb
313✔
429
    end
593✔
430
    return θb
33✔
431
end
432

433
# det with optional diagonal shift for use with shifted Hessenberg factorizations
434
det(A::SymTridiagonal; shift::Number=false) = det_usmani(A.ev, A.dv, A.ev, shift)
58✔
435
logabsdet(A::SymTridiagonal; shift::Number=false) = logabsdet(ldlt(A; shift=shift))
30✔
436

437
@inline function Base.isassigned(A::SymTridiagonal, i::Int, j::Int)
2,480✔
438
    @boundscheck checkbounds(Bool, A, i, j) || return false
2,523✔
439
    if i == j
2,523✔
440
        return @inbounds isassigned(A.dv, i)
323✔
441
    elseif i == j + 1
2,200✔
442
        return @inbounds isassigned(A.ev, j)
286✔
443
    elseif i + 1 == j
1,914✔
444
        return @inbounds isassigned(A.ev, i)
270✔
445
    else
446
        return true
1,644✔
447
    end
448
end
449

450
@inline function Base.isstored(A::SymTridiagonal, i::Int, j::Int)
×
451
    @boundscheck checkbounds(A, i, j)
×
452
    if i == j
×
453
        return @inbounds Base.isstored(A.dv, i)
×
454
    elseif i == j + 1
×
455
        return @inbounds Base.isstored(A.ev, j)
×
456
    elseif i + 1 == j
×
457
        return @inbounds Base.isstored(A.ev, i)
×
458
    else
459
        return false
×
460
    end
461
end
462

463
@inline function getindex(A::SymTridiagonal{T}, i::Int, j::Int) where T
2,567✔
464
    @boundscheck checkbounds(A, i, j)
160,173✔
465
    if i == j
160,151✔
466
        return symmetric((@inbounds A.dv[i]), :U)::symmetric_type(eltype(A.dv))
26,991✔
467
    elseif i == j + 1
133,160✔
468
        return copy(transpose(@inbounds A.ev[j])) # materialized for type stability
19,743✔
469
    elseif i + 1 == j
113,417✔
470
        return @inbounds A.ev[i]
19,938✔
471
    else
472
        return zero(T)
93,479✔
473
    end
474
end
475

476
Base._reverse(A::SymTridiagonal, dims) = reverse!(Matrix(A); dims)
2✔
477
Base._reverse(A::SymTridiagonal, dims::Colon) = SymTridiagonal(reverse(A.dv), reverse(A.ev))
1✔
478
Base._reverse!(A::SymTridiagonal, dims::Colon) = (reverse!(A.dv); reverse!(A.ev); A)
×
479

480
@inline function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer)
40✔
481
    @boundscheck checkbounds(A, i, j)
60✔
482
    if i == j
40✔
483
        issymmetric(x) || throw(ArgumentError("cannot set a diagonal entry of a SymTridiagonal to an asymmetric value"))
26✔
484
        @inbounds A.dv[i] = x
24✔
485
    else
486
        throw(ArgumentError(lazy"cannot set off-diagonal entry ($i, $j)"))
15✔
487
    end
488
    return A
24✔
489
end
490

491
## Tridiagonal matrices ##
492
struct Tridiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T}
493
    dl::V    # sub-diagonal
494
    d::V     # diagonal
495
    du::V    # sup-diagonal
496
    du2::V   # supsup-diagonal for pivoting in LU
497
    function Tridiagonal{T,V}(dl, d, du) where {T,V<:AbstractVector{T}}
5✔
498
        require_one_based_indexing(dl, d, du)
10,768✔
499
        n = length(d)
10,768✔
500
        if (length(dl) != n-1 || length(du) != n-1) && !(length(d) == 0 && length(dl) == 0 && length(du) == 0)
21,465✔
501
            throw(ArgumentError(LazyString("cannot construct Tridiagonal from incompatible ",
5✔
502
                "lengths of subdiagonal, diagonal and superdiagonal: ",
503
                lazy"($(length(dl)), $(length(d)), $(length(du)))")))
504
        end
505
        new{T,V}(dl, d, Base.unalias(dl, du))
10,763✔
506
    end
507
    # constructor used in lu!
508
    function Tridiagonal{T,V}(dl, d, du, du2) where {T,V<:AbstractVector{T}}
509
        require_one_based_indexing(dl, d, du, du2)
299✔
510
        # length checks?
511
        new{T,V}(dl, d, Base.unalias(dl, du), du2)
299✔
512
    end
513
end
514

515
"""
516
    Tridiagonal(dl::V, d::V, du::V) where V <: AbstractVector
517

518
Construct a tridiagonal matrix from the first subdiagonal, diagonal, and first superdiagonal,
519
respectively. The result is of type `Tridiagonal` and provides efficient specialized linear
520
solvers, but may be converted into a regular matrix with
521
[`convert(Array, _)`](@ref) (or `Array(_)` for short).
522
The lengths of `dl` and `du` must be one less than the length of `d`.
523

524
!!! note
525
    The subdiagonal `dl` and the superdiagonal `du` must not be aliased to each other.
526
    If aliasing is detected, the constructor will use a copy of `du` as its argument.
527

528
# Examples
529
```jldoctest
530
julia> dl = [1, 2, 3];
531

532
julia> du = [4, 5, 6];
533

534
julia> d = [7, 8, 9, 0];
535

536
julia> Tridiagonal(dl, d, du)
537
4×4 Tridiagonal{Int64, Vector{Int64}}:
538
 7  4  ⋅  ⋅
539
 1  8  5  ⋅
540
 ⋅  2  9  6
541
 ⋅  ⋅  3  0
542
```
543
"""
544
Tridiagonal(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T,V}(dl, d, du)
15,257✔
545
Tridiagonal(dl::V, d::V, du::V, du2::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T,V}(dl, d, du, du2)
64✔
546
Tridiagonal(dl::AbstractVector{T}, d::AbstractVector{S}, du::AbstractVector{U}) where {T,S,U} =
13✔
547
    Tridiagonal{promote_type(T, S, U)}(dl, d, du)
548
Tridiagonal(dl::AbstractVector{T}, d::AbstractVector{S}, du::AbstractVector{U}, du2::AbstractVector{V}) where {T,S,U,V} =
×
549
    Tridiagonal{promote_type(T, S, U, V)}(dl, d, du, du2)
550
function Tridiagonal{T}(dl::AbstractVector, d::AbstractVector, du::AbstractVector) where {T}
24✔
551
    l, d, u = map(x->convert(AbstractVector{T}, x), (dl, d, du))
176✔
552
    typeof(l) == typeof(d) == typeof(u) ?
41✔
553
        Tridiagonal(l, d, u) :
554
        throw(ArgumentError("diagonal vectors needed to be convertible to same type"))
555
end
556
function Tridiagonal{T}(dl::AbstractVector, d::AbstractVector, du::AbstractVector, du2::AbstractVector) where {T}
557
    l, d, u, u2 = map(x->convert(AbstractVector{T}, x), (dl, d, du, du2))
295✔
558
    typeof(l) == typeof(d) == typeof(u) == typeof(u2) ?
59✔
559
        Tridiagonal(l, d, u, u2) :
560
        throw(ArgumentError("diagonal vectors needed to be convertible to same type"))
561
end
562

563
"""
564
    Tridiagonal(A)
565

566
Construct a tridiagonal matrix from the first sub-diagonal,
567
diagonal and first super-diagonal of the matrix `A`.
568

569
# Examples
570
```jldoctest
571
julia> A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4]
572
4×4 Matrix{Int64}:
573
 1  2  3  4
574
 1  2  3  4
575
 1  2  3  4
576
 1  2  3  4
577

578
julia> Tridiagonal(A)
579
4×4 Tridiagonal{Int64, Vector{Int64}}:
580
 1  2  ⋅  ⋅
581
 1  2  3  ⋅
582
 ⋅  2  3  4
583
 ⋅  ⋅  3  4
584
```
585
"""
586
Tridiagonal(A::AbstractMatrix) = Tridiagonal(diag(A,-1), diag(A,0), diag(A,1))
1,025✔
587

588
Tridiagonal(A::Tridiagonal) = A
38✔
589
Tridiagonal{T}(A::Tridiagonal{T}) where {T} = A
2✔
590
function Tridiagonal{T}(A::Tridiagonal) where {T}
74✔
591
    dl, d, du = map(x -> convert(AbstractVector{T}, x)::AbstractVector{T}, (A.dl, A.d, A.du))
444✔
592
    if isdefined(A, :du2)
74✔
593
        Tridiagonal{T}(dl, d, du, convert(AbstractVector{T}, A.du2)::AbstractVector{T})
59✔
594
    else
595
        Tridiagonal{T}(dl, d, du)
15✔
596
    end
597
end
598
Tridiagonal{T,V}(A::Tridiagonal{T,V}) where {T,V<:AbstractVector{T}} = A
10✔
599
function Tridiagonal{T,V}(A::Tridiagonal) where {T,V<:AbstractVector{T}}
×
600
    dl, d, du = map(x -> convert(V, x)::V, (A.dl, A.d, A.du))
×
601
    if isdefined(A, :du2)
×
602
        Tridiagonal{T,V}(dl, d, du, convert(V, A.du2)::V)
×
603
    else
604
        Tridiagonal{T,V}(dl, d, du)
×
605
    end
606
end
607

608
size(M::Tridiagonal) = (n = length(M.d); (n, n))
68,700✔
609
axes(M::Tridiagonal) = (ax = axes(M.d,1); (ax, ax))
1,738,568✔
610

611
function Matrix{T}(M::Tridiagonal) where {T}
2,849✔
612
    A = Matrix{T}(undef, size(M))
5,695✔
613
    if haszero(T) # optimized path for types with zero(T) defined
2,849✔
614
        size(A,1) > 2 && fill!(A, zero(T))
151,044✔
615
        copyto!(view(A, diagind(A)), M.d)
3,048✔
616
        copyto!(view(A, diagind(A,1)), M.du)
3,048✔
617
        copyto!(view(A, diagind(A,-1)), M.dl)
3,048✔
618
    else
619
        copyto!(A, M)
25✔
620
    end
621
    A
2,849✔
622
end
623
Matrix(M::Tridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M)
2,845✔
624
Array(M::Tridiagonal) = Matrix(M)
348✔
625

626
similar(M::Tridiagonal, ::Type{T}) where {T} = Tridiagonal(similar(M.dl, T), similar(M.d, T), similar(M.du, T))
547✔
627
similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(M.d, T, dims)
2,998✔
628

629
# Operations on Tridiagonal matrices
630
# copyto! for matching axes
631
function _copyto_banded!(dest::Tridiagonal, src::Tridiagonal)
16✔
632
    copyto!(dest.dl, src.dl)
511✔
633
    copyto!(dest.d, src.d)
514✔
634
    copyto!(dest.du, src.du)
511✔
635
    dest
261✔
636
end
637

638
#Elementary operations
639
for func in (:conj, :copy, :real, :imag)
640
    @eval function ($func)(M::Tridiagonal)
94✔
641
        Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du))
94✔
642
    end
643
end
644

645
adjoint(S::Tridiagonal{<:Number}) = Tridiagonal(vec(adjoint(S.du)), vec(adjoint(S.d)), vec(adjoint(S.dl)))
455✔
646
adjoint(S::Tridiagonal{<:Number, <:Base.ReshapedArray{<:Number,1,<:Adjoint}}) =
4✔
647
    Tridiagonal(adjoint(parent(S.du)), adjoint(parent(S.d)), adjoint(parent(S.dl)))
648
transpose(S::Tridiagonal{<:Number}) = Tridiagonal(S.du, S.d, S.dl)
449✔
649
permutedims(T::Tridiagonal) = Tridiagonal(T.du, T.d, T.dl)
25✔
650
function permutedims(T::Tridiagonal, perm)
10✔
651
    Base.checkdims_perm(axes(T), axes(T), perm)
15✔
652
    NTuple{2}(perm) == (2, 1) ? permutedims(T) : T
10✔
653
end
654
Base.copy(aS::Adjoint{<:Any,<:Tridiagonal}) = (S = aS.parent; Tridiagonal(map(x -> copy.(adjoint.(x)), (S.du, S.d, S.dl))...))
×
655
Base.copy(tS::Transpose{<:Any,<:Tridiagonal}) = (S = tS.parent; Tridiagonal(map(x -> copy.(transpose.(x)), (S.du, S.d, S.dl))...))
×
656

657
ishermitian(S::Tridiagonal) = all(ishermitian, S.d) && all(Iterators.map((x, y) -> x == y', S.du, S.dl))
1,591✔
658
issymmetric(S::Tridiagonal) = all(issymmetric, S.d) && all(Iterators.map((x, y) -> x == transpose(y), S.du, S.dl))
237✔
659

660
\(A::Adjoint{<:Any,<:Tridiagonal}, B::Adjoint{<:Any,<:AbstractVecOrMat}) = copy(A) \ B
×
661

662
function diag(M::Tridiagonal, n::Integer=0)
272✔
663
    # every branch call similar(..., ::Int) to make sure the
664
    # same vector type is returned independent of n
665
    if n == 0
406✔
666
        return copyto!(similar(M.d, length(M.d)), M.d)
170✔
667
    elseif n == -1
90✔
668
        return copyto!(similar(M.dl, length(M.dl)), M.dl)
38✔
669
    elseif n == 1
52✔
670
        return copyto!(similar(M.du, length(M.du)), M.du)
33✔
671
    elseif abs(n) <= size(M,1)
19✔
672
        v = similar(M.d, size(M,1)-abs(n))
14✔
673
        for i in eachindex(v)
7✔
674
            v[i] = M[BandIndex(n,i)]
7✔
675
        end
5✔
676
        return v
5✔
677
    else
678
        throw(ArgumentError(LazyString(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ",
12✔
679
            lazy"and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix")))
680
    end
681
end
682

683
@inline function Base.isassigned(A::Tridiagonal, i::Int, j::Int)
627,296✔
684
    @boundscheck checkbounds(Bool, A, i, j) || return false
627,552✔
685
    if i == j
627,552✔
686
        return @inbounds isassigned(A.d, i)
62,808✔
687
    elseif i == j + 1
564,744✔
688
        return @inbounds isassigned(A.dl, j)
56,514✔
689
    elseif i + 1 == j
508,230✔
690
        return @inbounds isassigned(A.du, i)
56,514✔
691
    else
692
        return true
451,716✔
693
    end
694
end
695

696
@inline function Base.isstored(A::Tridiagonal, i::Int, j::Int)
×
697
    @boundscheck checkbounds(A, i, j)
×
698
    if i == j
×
699
        return @inbounds Base.isstored(A.d, i)
×
700
    elseif i == j + 1
×
701
        return @inbounds Base.isstored(A.dl, j)
×
702
    elseif i + 1 == j
×
703
        return @inbounds Base.isstored(A.du, i)
×
704
    else
705
        return false
×
706
    end
707
end
708

709
@inline function getindex(A::Tridiagonal{T}, i::Int, j::Int) where T
627,388✔
710
    @boundscheck checkbounds(A, i, j)
1,002,109✔
711
    if i == j
1,002,089✔
712
        return @inbounds A.d[i]
127,303✔
713
    elseif i == j + 1
874,786✔
714
        return @inbounds A.dl[j]
100,922✔
715
    elseif i + 1 == j
773,864✔
716
        return @inbounds A.du[i]
107,231✔
717
    else
718
        return zero(T)
666,633✔
719
    end
720
end
721

722
@inline function getindex(A::Tridiagonal{T}, b::BandIndex) where T
723
    @boundscheck checkbounds(A, b)
10,762✔
724
    if b.band == 0
10,760✔
725
        return @inbounds A.d[b.index]
3,835✔
726
    elseif b.band == -1
6,925✔
727
        return @inbounds A.dl[b.index]
3,456✔
728
    elseif b.band == 1
3,469✔
729
        return @inbounds A.du[b.index]
3,456✔
730
    else
731
        return zero(T)
13✔
732
    end
733
end
734

735
@inline function setindex!(A::Tridiagonal, x, i::Integer, j::Integer)
45✔
736
    @boundscheck checkbounds(A, i, j)
8,284✔
737
    if i == j
8,264✔
738
        @inbounds A.d[i] = x
3,028✔
739
    elseif i - j == 1
5,236✔
740
        @inbounds A.dl[j] = x
2,575✔
741
    elseif j - i == 1
2,661✔
742
        @inbounds A.du[i] = x
2,573✔
743
    elseif !iszero(x)
88✔
744
        throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off ",
7✔
745
            lazy"the tridiagonal band to a nonzero value ($x)")))
746
    end
747
    return A
8,257✔
748
end
749

750
## structured matrix methods ##
751
function Base.replace_in_print_matrix(A::Tridiagonal,i::Integer,j::Integer,s::AbstractString)
313,648✔
752
    i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s)
313,880✔
753
end
754

755
# reverse
756

757
Base._reverse(A::Tridiagonal, dims) = reverse!(Matrix(A); dims)
4✔
758
Base._reverse(A::Tridiagonal, dims::Colon) = Tridiagonal(reverse(A.du), reverse(A.d), reverse(A.dl))
2✔
759
function Base._reverse!(A::Tridiagonal, dims::Colon)
760
    n = length(A.du) # == length(A.dl), & always 1-based
2✔
761
    # reverse and swap A.dl and A.du:
762
    @inbounds for i in 1:n
2✔
763
        A.dl[i], A.du[n+1-i] = A.du[n+1-i], A.dl[i]
9✔
764
    end
16✔
765
    reverse!(A.d)
2✔
766
    return A
2✔
767
end
768

769
#tril and triu
770

771
iszero(M::Tridiagonal) = iszero(M.dl) && iszero(M.d) && iszero(M.du)
25✔
772
isone(M::Tridiagonal) = iszero(M.dl) && all(isone, M.d) && iszero(M.du)
21✔
773
Base.@constprop :aggressive function istriu(M::Tridiagonal, k::Integer=0)
38✔
774
    if k <= -1
153✔
775
        return true
15✔
776
    elseif k == 0
83✔
777
        return iszero(M.dl)
55✔
778
    elseif k == 1
28✔
779
        return iszero(M.dl) && iszero(M.d)
7✔
780
    else # k >= 2
781
        return iszero(M.dl) && iszero(M.d) && iszero(M.du)
21✔
782
    end
783
end
784
Base.@constprop :aggressive function istril(M::Tridiagonal, k::Integer=0)
38✔
785
    if k >= 1
153✔
786
        return true
15✔
787
    elseif k == 0
83✔
788
        return iszero(M.du)
55✔
789
    elseif k == -1
28✔
790
        return iszero(M.du) && iszero(M.d)
7✔
791
    else # k <= -2
792
        return iszero(M.du) && iszero(M.d) && iszero(M.dl)
21✔
793
    end
794
end
795
isdiag(M::Tridiagonal) = iszero(M.dl) && iszero(M.du)
315✔
796

797
function tril!(M::Tridiagonal{T}, k::Integer=0) where T
55✔
798
    n = length(M.d)
60✔
799
    if !(-n - 1 <= k <= n - 1)
50✔
800
        throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ",
10✔
801
            lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix")))
802
    elseif k < -1
40✔
803
        fill!(M.dl, zero(T))
110✔
804
        fill!(M.d, zero(T))
120✔
805
        fill!(M.du, zero(T))
110✔
806
    elseif k == -1
30✔
807
        fill!(M.d, zero(T))
120✔
808
        fill!(M.du, zero(T))
10✔
809
    elseif k == 0
20✔
810
        fill!(M.du, zero(T))
10✔
811
    end
812
    return M
40✔
813
end
814

815
function triu!(M::Tridiagonal{T}, k::Integer=0) where T
55✔
816
    n = length(M.d)
60✔
817
    if !(-n + 1 <= k <= n + 1)
50✔
818
        throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ",
10✔
819
            lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix")))
820
    elseif k > 1
40✔
821
        fill!(M.dl, zero(T))
110✔
822
        fill!(M.d, zero(T))
120✔
823
        fill!(M.du, zero(T))
110✔
824
    elseif k == 1
30✔
825
        fill!(M.dl, zero(T))
110✔
826
        fill!(M.d, zero(T))
10✔
827
    elseif k == 0
20✔
828
        fill!(M.dl, zero(T))
10✔
829
    end
830
    return M
40✔
831
end
832

833
tr(M::Tridiagonal) = sum(M.d)
5✔
834

835
###################
836
# Generic methods #
837
###################
838

839
+(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl+B.dl, A.d+B.d, A.du+B.du)
63✔
840
-(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl-B.dl, A.d-B.d, A.du-B.du)
70✔
841
-(A::Tridiagonal) = Tridiagonal(-A.dl, -A.d, -A.du)
18✔
842
*(A::Tridiagonal, B::Number) = Tridiagonal(A.dl*B, A.d*B, A.du*B)
20✔
843
*(B::Number, A::Tridiagonal) = Tridiagonal(B*A.dl, B*A.d, B*A.du)
11✔
844
function rmul!(T::Tridiagonal, x::Number)
15✔
845
    if size(T,1) > 2
15✔
846
        # ensure that zeros are preserved on scaling
847
        y = T[3,1] * x
10✔
848
        iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ",
11✔
849
            lazy"the tridiagonal band to a nonzero value ($y)")))
850
    end
851
    T.dl .*= x
14✔
852
    T.d .*= x
14✔
853
    T.du .*= x
14✔
854
    return T
14✔
855
end
856
function lmul!(x::Number, T::Tridiagonal)
3✔
857
    if size(T,1) > 2
3✔
858
        # ensure that zeros are preserved on scaling
859
        y = x * T[3,1]
2✔
860
        iszero(y) || throw(ArgumentError(LazyString("cannot set index (3, 1) off ",
3✔
861
            lazy"the tridiagonal band to a nonzero value ($y)")))
862
    end
863
    @. T.dl = x * T.dl
2✔
864
    @. T.d = x * T.d
2✔
865
    @. T.du = x * T.du
2✔
866
    return T
2✔
867
end
868
/(A::Tridiagonal, B::Number) = Tridiagonal(A.dl/B, A.d/B, A.du/B)
6✔
869
\(B::Number, A::Tridiagonal) = Tridiagonal(B\A.dl, B\A.d, B\A.du)
1✔
870

871
==(A::Tridiagonal, B::Tridiagonal) = (A.dl==B.dl) && (A.d==B.d) && (A.du==B.du)
448✔
872
function ==(A::Tridiagonal, B::SymTridiagonal)
63✔
873
    iseq = all(Iterators.map((x, y) -> x == transpose(y), A.du, A.dl))
891✔
874
    iseq = iseq && A.du == _evview(B)
109✔
875
    iseq && all(Iterators.map((x, y) -> x == symmetric(y, :U), A.d, B.dv))
427✔
876
end
877
==(A::SymTridiagonal, B::Tridiagonal) = B == A
27✔
878

879
det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du)
14✔
880

881
AbstractMatrix{T}(M::Tridiagonal) where {T} = Tridiagonal{T}(M)
63✔
882
AbstractMatrix{T}(M::Tridiagonal{T}) where {T} = copy(M)
×
883
Tridiagonal{T}(M::SymTridiagonal{T}) where {T} = Tridiagonal(M)
5✔
884
function SymTridiagonal{T}(M::Tridiagonal) where T
5✔
885
    if issymmetric(M)
6✔
886
        return SymTridiagonal{T}(convert(AbstractVector{T},M.d), convert(AbstractVector{T},M.dl))
5✔
887
    else
888
        throw(ArgumentError("Tridiagonal is not symmetric, cannot convert to SymTridiagonal"))
1✔
889
    end
890
end
891

892
Base._sum(A::Tridiagonal, ::Colon) = sum(A.d) + sum(A.dl) + sum(A.du)
3✔
893
function Base._sum(A::SymTridiagonal, ::Colon)
3✔
894
    se = sum(_evview(A))
5✔
895
    symmetric(sum(A.dv), :U) + se + transpose(se)
4✔
896
end
897

898
function Base._sum(A::Tridiagonal, dims::Integer)
15✔
899
    res = Base.reducedim_initarray(A, dims, zero(eltype(A)))
15✔
900
    n = length(A.d)
12✔
901
    if n == 0
12✔
902
        return res
4✔
903
    elseif n == 1
8✔
904
        res[1] = A.d[1]
4✔
905
        return res
4✔
906
    end
907
    @inbounds begin
4✔
908
        if dims == 1
4✔
909
            res[1] = A.dl[1] + A.d[1]
2✔
910
            for i = 2:n-1
2✔
911
                res[i] = A.dl[i] + A.d[i] + A.du[i-1]
2✔
912
            end
2✔
913
            res[n] = A.d[n] + A.du[n-1]
2✔
914
        elseif dims == 2
2✔
915
            res[1] = A.d[1] + A.du[1]
1✔
916
            for i = 2:n-1
1✔
917
                res[i] = A.dl[i-1] + A.d[i] + A.du[i]
1✔
918
            end
1✔
919
            res[n] = A.dl[n-1] + A.d[n]
1✔
920
        elseif dims >= 3
1✔
921
            for i = 1:n-1
1✔
922
                res[i,i+1] = A.du[i]
2✔
923
                res[i,i]   = A.d[i]
2✔
924
                res[i+1,i] = A.dl[i]
2✔
925
            end
3✔
926
            res[n,n] = A.d[n]
1✔
927
        end
928
    end
929
    res
4✔
930
end
931

932
function Base._sum(A::SymTridiagonal, dims::Integer)
17✔
933
    res = Base.reducedim_initarray(A, dims, zero(eltype(A)))
17✔
934
    n = length(A.dv)
14✔
935
    if n == 0
14✔
936
        return res
4✔
937
    elseif n == 1
10✔
938
        res[1] = A.dv[1]
4✔
939
        return res
4✔
940
    end
941
    @inbounds begin
6✔
942
        if dims == 1
6✔
943
            res[1] = transpose(A.ev[1]) + symmetric(A.dv[1], :U)
3✔
944
            for i = 2:n-1
3✔
945
                res[i] = transpose(A.ev[i]) + symmetric(A.dv[i], :U) + A.ev[i-1]
10✔
946
            end
17✔
947
            res[n] = symmetric(A.dv[n], :U) + A.ev[n-1]
3✔
948
        elseif dims == 2
3✔
949
            res[1] = symmetric(A.dv[1], :U) + A.ev[1]
2✔
950
            for i = 2:n-1
2✔
951
                res[i] = transpose(A.ev[i-1]) + symmetric(A.dv[i], :U) + A.ev[i]
9✔
952
            end
16✔
953
            res[n] = transpose(A.ev[n-1]) + symmetric(A.dv[n], :U)
2✔
954
        elseif dims >= 3
1✔
955
            for i = 1:n-1
1✔
956
                res[i,i+1] = A.ev[i]
2✔
957
                res[i,i]   = symmetric(A.dv[i], :U)
2✔
958
                res[i+1,i] = transpose(A.ev[i])
2✔
959
            end
3✔
960
            res[n,n] = symmetric(A.dv[n], :U)
1✔
961
        end
962
    end
963
    res
6✔
964
end
965

966
function dot(x::AbstractVector, A::Tridiagonal, y::AbstractVector)
30✔
967
    require_one_based_indexing(x, y)
30✔
968
    nx, ny = length(x), length(y)
30✔
969
    (nx == size(A, 1) == ny) || throw(DimensionMismatch())
30✔
970
    if nx ≤ 1
30✔
971
        nx == 0 && return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
25✔
972
        return dot(x[1], A.d[1], y[1])
10✔
973
    end
974
    @inbounds begin
5✔
975
        x₀ = x[1]
5✔
976
        x₊ = x[2]
5✔
977
        dl, d, du = A.dl, A.d, A.du
5✔
978
        r = dot(adjoint(d[1])*x₀ + adjoint(dl[1])*x₊, y[1])
5✔
979
        for j in 2:nx-1
5✔
980
            x₋, x₀, x₊ = x₀, x₊, x[j+1]
50✔
981
            r += dot(adjoint(du[j-1])*x₋ + adjoint(d[j])*x₀ + adjoint(dl[j])*x₊, y[j])
50✔
982
        end
95✔
983
        r += dot(adjoint(du[nx-1])*x₀ + adjoint(d[nx])*x₊, y[nx])
5✔
984
    end
985
    return r
5✔
986
end
987

988
function cholesky(S::SymTridiagonal, ::NoPivot = NoPivot(); check::Bool = true)
42✔
989
    if !ishermitian(S)
17✔
990
        check && checkpositivedefinite(-1)
×
991
        return Cholesky(S, 'U', convert(BlasInt, -1))
×
992
    end
993
    T = choltype(S)
17✔
994
    cholesky!(Hermitian(Bidiagonal{T}(diag(S, 0), diag(S, 1), :U)), NoPivot(); check = check)
17✔
995
end
996

997
# See dgtsv.f
998
"""
999
    ldiv!(A::Tridiagonal, B::AbstractVecOrMat) -> B
1000

1001
Compute `A \\ B` in-place by Gaussian elimination with partial pivoting and store the result
1002
in `B`, returning the result. In the process, the diagonals of `A` are overwritten as well.
1003

1004
!!! compat "Julia 1.11"
1005
    `ldiv!` for `Tridiagonal` left-hand sides requires at least Julia 1.11.
1006
"""
1007
function ldiv!(A::Tridiagonal, B::AbstractVecOrMat)
12✔
1008
    LinearAlgebra.require_one_based_indexing(B)
12✔
1009
    n = size(A, 1)
12✔
1010
    if n != size(B,1)
12✔
1011
        throw(DimensionMismatch(lazy"matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows"))
×
1012
    end
1013
    nrhs = size(B, 2)
12✔
1014

1015
    # Initialize variables
1016
    dl = A.dl
12✔
1017
    d = A.d
12✔
1018
    du = A.du
12✔
1019

1020
    @inbounds begin
12✔
1021
        for i in 1:n-1
12✔
1022
            # pivot or not?
1023
            if abs(d[i]) >= abs(dl[i])
127✔
1024
                # No interchange
1025
                if d[i] != 0
74✔
1026
                    fact = dl[i]/d[i]
74✔
1027
                    d[i+1] -= fact*du[i]
74✔
1028
                    for j in 1:nrhs
74✔
1029
                        B[i+1,j] -= fact*B[i,j]
596✔
1030
                    end
1,118✔
1031
                else
1032
                    checknonsingular(i)
×
1033
                end
1034
                i < n-1 && (dl[i] = 0)
74✔
1035
            else
1036
                # Interchange
1037
                fact = d[i]/dl[i]
42✔
1038
                d[i] = dl[i]
42✔
1039
                tmp = d[i+1]
42✔
1040
                d[i+1] = du[i] - fact*tmp
42✔
1041
                du[i] = tmp
42✔
1042
                if i < n-1
42✔
1043
                    dl[i] = du[i+1]
36✔
1044
                    du[i+1] = -fact*dl[i]
36✔
1045
                end
1046
                for j in 1:nrhs
42✔
1047
                    temp = B[i,j]
168✔
1048
                    B[i,j] = B[i+1,j]
168✔
1049
                    B[i+1,j] = temp - fact*B[i+1,j]
168✔
1050
                end
168✔
1051
            end
1052
        end
220✔
1053
        iszero(d[n]) && checknonsingular(n)
12✔
1054
        # backward substitution
1055
        for j in 1:nrhs
12✔
1056
            B[n,j] /= d[n]
84✔
1057
            if n > 1
84✔
1058
                B[n-1,j] = (B[n-1,j] - du[n-1]*B[n,j])/d[n-1]
84✔
1059
            end
1060
            for i in n-2:-1:1
168✔
1061
                B[i,j] = (B[i,j] - du[i]*B[i+1,j] - dl[i]*B[i+2,j]) / d[i]
680✔
1062
            end
1,276✔
1063
        end
156✔
1064
    end
1065
    return B
12✔
1066
end
1067

1068
# combinations of Tridiagonal and Symtridiagonal
1069
# copyto! for matching axes
1070
function _copyto_banded!(A::Tridiagonal, B::SymTridiagonal)
2✔
1071
    Bev = _evview(B)
2✔
1072
    A.du .= Bev
2✔
1073
    # Broadcast identity for numbers to access the faster copyto! path
1074
    # This uses the fact that transpose(x::Number) = x and symmetric(x::Number) = x
1075
    A.dl .= (eltype(B) <: Number ? identity : transpose).(Bev)
2✔
1076
    A.d .= (eltype(B) <: Number ? identity : symmetric).(B.dv)
4✔
1077
    return A
2✔
1078
end
1079
function _copyto_banded!(A::SymTridiagonal, B::Tridiagonal)
3✔
1080
    issymmetric(B) || throw(ArgumentError("cannot copy an asymmetric Tridiagonal matrix to a SymTridiagonal"))
4✔
1081
    A.dv .= B.d
4✔
1082
    _evview(A) .= B.du
3✔
1083
    return A
2✔
1084
end
1085

1086
# display
1087
function show(io::IO, T::Tridiagonal)
2✔
1088
    print(io, "Tridiagonal(")
2✔
1089
    show(io, T.dl)
2✔
1090
    print(io, ", ")
2✔
1091
    show(io, T.d)
2✔
1092
    print(io, ", ")
2✔
1093
    show(io, T.du)
2✔
1094
    print(io, ")")
2✔
1095
end
1096
function show(io::IO, S::SymTridiagonal)
2✔
1097
    print(io, "SymTridiagonal(")
2✔
1098
    show(io, eltype(S) <: Number ? S.dv : view(S, diagind(S, IndexStyle(S))))
2✔
1099
    print(io, ", ")
2✔
1100
    show(io, S.ev)
2✔
1101
    print(io, ")")
2✔
1102
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc