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

JuliaLang / julia / #37807

14 Jun 2024 05:48AM UTC coverage: 87.487% (+0.01%) from 87.473%
#37807

push

local

web-flow
Move error comment line in scoping example (#54790)

Continuation of #54500.
I realized the erroring line is one lower.

77155 of 88190 relevant lines covered (87.49%)

16355099.22 hits per line

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

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

3
## Triangular
4

5
# could be renamed to Triangular when that name has been fully deprecated
6
"""
7
    AbstractTriangular
8

9
Supertype of triangular matrix types such as [`LowerTriangular`](@ref), [`UpperTriangular`](@ref),
10
[`UnitLowerTriangular`](@ref) and [`UnitUpperTriangular`](@ref).
11
"""
12
abstract type AbstractTriangular{T} <: AbstractMatrix{T} end
13

14
# First loop through all methods that don't need special care for upper/lower and unit diagonal
15
for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, :UnitUpperTriangular)
16
    @eval begin
17
        struct $t{T,S<:AbstractMatrix{T}} <: AbstractTriangular{T}
18
            data::S
19

20
            function $t{T,S}(data) where {T,S<:AbstractMatrix{T}}
18✔
21
                require_one_based_indexing(data)
101,705✔
22
                checksquare(data)
101,706✔
23
                new{T,S}(data)
101,704✔
24
            end
25
        end
26
        $t(A::$t) = A
1,949✔
27
        $t{T}(A::$t{T}) where {T} = A
60✔
28
        $t(A::AbstractMatrix) = $t{eltype(A), typeof(A)}(A)
101,671✔
29
        $t{T}(A::AbstractMatrix) where {T} = $t(convert(AbstractMatrix{T}, A))
28✔
30
        $t{T}(A::$t) where {T} = $t(convert(AbstractMatrix{T}, A.data))
3,616✔
31

32
        AbstractMatrix{T}(A::$t) where {T} = $t{T}(A)
3,584✔
33
        AbstractMatrix{T}(A::$t{T}) where {T} = copy(A)
×
34

35
        size(A::$t) = size(A.data)
265,129✔
36
        axes(A::$t) = axes(A.data)
1,047,872✔
37

38
        # For A<:AbstractTriangular, similar(A[, neweltype]) should yield a matrix with the same
39
        # triangular type and underlying storage type as A. The following method covers these cases.
40
        similar(A::$t, ::Type{T}) where {T} = $t(similar(parent(A), T))
6,832✔
41
        # On the other hand, similar(A, [neweltype,] shape...) should yield a matrix of the underlying
42
        # storage type of A (not wrapped in a triangular type). The following method covers these cases.
43
        similar(A::$t, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(A), T, dims)
34,799✔
44

45
        copy(A::$t) = $t(copy(A.data))
1✔
46
        Base.unaliascopy(A::$t) = $t(Base.unaliascopy(A.data))
2,348✔
47

48
        real(A::$t{<:Real}) = A
85✔
49
        real(A::$t{<:Complex}) = (B = real(A.data); $t(B))
×
50
        real(A::$t{<:Complex, <:StridedMaybeAdjOrTransMat}) = $t(real.(A))
372✔
51
    end
52
end
53

54
"""
55
    LowerTriangular(A::AbstractMatrix)
56

57
Construct a `LowerTriangular` view of the matrix `A`.
58

59
# Examples
60
```jldoctest
61
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
62
3×3 Matrix{Float64}:
63
 1.0  2.0  3.0
64
 4.0  5.0  6.0
65
 7.0  8.0  9.0
66

67
julia> LowerTriangular(A)
68
3×3 LowerTriangular{Float64, Matrix{Float64}}:
69
 1.0   ⋅    ⋅
70
 4.0  5.0   ⋅
71
 7.0  8.0  9.0
72
```
73
"""
74
LowerTriangular
75
"""
76
    UpperTriangular(A::AbstractMatrix)
77

78
Construct an `UpperTriangular` view of the matrix `A`.
79

80
# Examples
81
```jldoctest
82
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
83
3×3 Matrix{Float64}:
84
 1.0  2.0  3.0
85
 4.0  5.0  6.0
86
 7.0  8.0  9.0
87

88
julia> UpperTriangular(A)
89
3×3 UpperTriangular{Float64, Matrix{Float64}}:
90
 1.0  2.0  3.0
91
  ⋅   5.0  6.0
92
  ⋅    ⋅   9.0
93
```
94
"""
95
UpperTriangular
96
"""
97
    UnitLowerTriangular(A::AbstractMatrix)
98

99
Construct a `UnitLowerTriangular` view of the matrix `A`.
100
Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref)
101
of `A` on its diagonal.
102

103
# Examples
104
```jldoctest
105
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
106
3×3 Matrix{Float64}:
107
 1.0  2.0  3.0
108
 4.0  5.0  6.0
109
 7.0  8.0  9.0
110

111
julia> UnitLowerTriangular(A)
112
3×3 UnitLowerTriangular{Float64, Matrix{Float64}}:
113
 1.0   ⋅    ⋅
114
 4.0  1.0   ⋅
115
 7.0  8.0  1.0
116
```
117
"""
118
UnitLowerTriangular
119
"""
120
    UnitUpperTriangular(A::AbstractMatrix)
121

122
Construct an `UnitUpperTriangular` view of the matrix `A`.
123
Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref)
124
of `A` on its diagonal.
125

126
# Examples
127
```jldoctest
128
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
129
3×3 Matrix{Float64}:
130
 1.0  2.0  3.0
131
 4.0  5.0  6.0
132
 7.0  8.0  9.0
133

134
julia> UnitUpperTriangular(A)
135
3×3 UnitUpperTriangular{Float64, Matrix{Float64}}:
136
 1.0  2.0  3.0
137
  ⋅   1.0  6.0
138
  ⋅    ⋅   1.0
139
```
140
"""
141
UnitUpperTriangular
142

143
const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}}
144
const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}}
145
const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}}
146

147
uppertriangular(M) = UpperTriangular(M)
804✔
148
lowertriangular(M) = LowerTriangular(M)
212✔
149

150
uppertriangular(U::UpperOrUnitUpperTriangular) = U
17✔
151
lowertriangular(U::LowerOrUnitLowerTriangular) = U
1✔
152

153
Base.dataids(A::UpperOrLowerTriangular) = Base.dataids(A.data)
37,125✔
154

155
imag(A::UpperTriangular) = UpperTriangular(imag(A.data))
1✔
156
imag(A::LowerTriangular) = LowerTriangular(imag(A.data))
1✔
157
imag(A::UpperTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) = imag.(A)
12✔
158
imag(A::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}) = imag.(A)
10✔
159
function imag(A::UnitLowerTriangular)
9✔
160
    L = LowerTriangular(A.data)
9✔
161
    Lim = similar(L) # must be mutable to set diagonals to zero
9✔
162
    Lim .= imag.(L)
9✔
163
    for i in 1:size(Lim,1)
9✔
164
        Lim[i,i] = zero(Lim[i,i])
67✔
165
    end
125✔
166
    return Lim
9✔
167
end
168
function imag(A::UnitUpperTriangular)
9✔
169
    U = UpperTriangular(A.data)
9✔
170
    Uim = similar(U) # must be mutable to set diagonals to zero
9✔
171
    Uim .= imag.(U)
9✔
172
    for i in 1:size(Uim,1)
9✔
173
        Uim[i,i] = zero(Uim[i,i])
67✔
174
    end
125✔
175
    return Uim
9✔
176
end
177

178
parent(A::UpperOrLowerTriangular) = A.data
193,609✔
179

180
# For strided matrices, we may only loop over the filled triangle
181
copy(A::UpperOrLowerTriangular{<:Any, <:StridedMaybeAdjOrTransMat}) = copyto!(similar(A), A)
5,254✔
182

183
# then handle all methods that requires specific handling of upper/lower and unit diagonal
184

185
function full!(A::LowerTriangular)
7✔
186
    B = A.data
49✔
187
    tril!(B)
49✔
188
    B
49✔
189
end
190
function full!(A::UnitLowerTriangular)
19✔
191
    B = A.data
49✔
192
    tril!(B)
49✔
193
    for i = 1:size(A,1)
49✔
194
        B[i,i] = oneunit(eltype(B))
441✔
195
    end
833✔
196
    B
49✔
197
end
198
function full!(A::UpperTriangular)
7✔
199
    B = A.data
49✔
200
    triu!(B)
49✔
201
    B
49✔
202
end
203
function full!(A::UnitUpperTriangular)
19✔
204
    B = A.data
49✔
205
    triu!(B)
49✔
206
    for i = 1:size(A,1)
49✔
207
        B[i,i] = oneunit(eltype(B))
441✔
208
    end
833✔
209
    B
49✔
210
end
211

212
Base.isassigned(A::UnitLowerTriangular, i::Int, j::Int) =
313,718✔
213
    i > j ? isassigned(A.data, i, j) : true
214
Base.isassigned(A::LowerTriangular, i::Int, j::Int) =
54✔
215
    i >= j ? isassigned(A.data, i, j) : true
216
Base.isassigned(A::UnitUpperTriangular, i::Int, j::Int) =
313,618✔
217
    i < j ? isassigned(A.data, i, j) : true
218
Base.isassigned(A::UpperTriangular, i::Int, j::Int) =
2,710✔
219
    i <= j ? isassigned(A.data, i, j) : true
220

221
Base.isstored(A::UnitLowerTriangular, i::Int, j::Int) =
×
222
    i > j ? Base.isstored(A.data, i, j) : false
223
Base.isstored(A::LowerTriangular, i::Int, j::Int) =
×
224
    i >= j ? Base.isstored(A.data, i, j) : false
225
Base.isstored(A::UnitUpperTriangular, i::Int, j::Int) =
×
226
    i < j ? Base.isstored(A.data, i, j) : false
227
Base.isstored(A::UpperTriangular, i::Int, j::Int) =
×
228
    i <= j ? Base.isstored(A.data, i, j) : false
229

230
@propagate_inbounds getindex(A::UnitLowerTriangular{T}, i::Int, j::Int) where {T} =
2,870,066✔
231
    i > j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
232
@propagate_inbounds getindex(A::LowerTriangular, i::Int, j::Int) =
3,176,886✔
233
    i >= j ? A.data[i,j] : _zero(A.data,j,i)
234
@propagate_inbounds getindex(A::UnitUpperTriangular{T}, i::Int, j::Int) where {T} =
2,884,931✔
235
    i < j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
236
@propagate_inbounds getindex(A::UpperTriangular, i::Int, j::Int) =
3,673,107✔
237
    i <= j ? A.data[i,j] : _zero(A.data,j,i)
238

239
_zero_triangular_half_str(::Type{<:UpperOrUnitUpperTriangular}) = "lower"
510✔
240
_zero_triangular_half_str(::Type{<:LowerOrUnitLowerTriangular}) = "upper"
509✔
241

242
@noinline function throw_nonzeroerror(T, @nospecialize(x), i, j)
1,019✔
243
    Ts = _zero_triangular_half_str(T)
1,019✔
244
    Tn = nameof(T)
1,019✔
245
    throw(ArgumentError(
1,019✔
246
        lazy"cannot set index in the $Ts triangular part ($i, $j) of an $Tn matrix to a nonzero value ($x)"))
247
end
248
@noinline function throw_nononeerror(T, @nospecialize(x), i, j)
128✔
249
    Tn = nameof(T)
128✔
250
    throw(ArgumentError(
128✔
251
        lazy"cannot set index on the diagonal ($i, $j) of an $Tn matrix to a non-unit value ($x)"))
252
end
253

254
@propagate_inbounds function setindex!(A::UpperTriangular, x, i::Integer, j::Integer)
819✔
255
    if i > j
29,153✔
256
        iszero(x) || throw_nonzeroerror(typeof(A), x, i, j)
3,270✔
257
    else
258
        A.data[i,j] = x
26,140✔
259
    end
260
    return A
28,896✔
261
end
262

263
@propagate_inbounds function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer)
882✔
264
    if i > j
1,658✔
265
        iszero(x) || throw_nonzeroerror(typeof(A), x, i, j)
1,101✔
266
    elseif i == j
810✔
267
        x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j)
216✔
268
    else
269
        A.data[i,j] = x
594✔
270
    end
271
    return A
1,342✔
272
end
273

274
@propagate_inbounds function setindex!(A::LowerTriangular, x, i::Integer, j::Integer)
819✔
275
    if i < j
3,243✔
276
        iszero(x) || throw_nonzeroerror(typeof(A), x, i, j)
1,400✔
277
    else
278
        A.data[i,j] = x
2,099✔
279
    end
280
    return A
2,987✔
281
end
282

283
@propagate_inbounds function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer)
882✔
284
    if i < j
1,659✔
285
        iszero(x) || throw_nonzeroerror(typeof(A), x, i, j)
1,100✔
286
    elseif i == j
812✔
287
        x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j)
216✔
288
    else
289
        A.data[i,j] = x
596✔
290
    end
291
    return A
1,343✔
292
end
293

294
@noinline function throw_setindex_structuralzero_error(T, @nospecialize(x))
×
295
    Ts = _zero_triangular_half_str(T)
×
296
    Tn = nameof(T)
×
297
    throw(ArgumentError(
×
298
        lazy"cannot set indices in the $Ts triangular part of an $Tn matrix to a nonzero value ($x)"))
×
299
end
300

301
@inline function fill!(A::UpperTriangular, x)
302
    iszero(x) || throw_setindex_structuralzero_error(typeof(A), x)
405✔
303
    for col in axes(A,2), row in firstindex(A,1):col
405✔
304
        @inbounds A.data[row, col] = x
1,516✔
305
    end
1,972✔
306
    A
405✔
307
end
308
@inline function fill!(A::LowerTriangular, x)
309
    iszero(x) || throw_setindex_structuralzero_error(typeof(A), x)
252✔
310
    for col in axes(A,2), row in col:lastindex(A,1)
252✔
311
        @inbounds A.data[row, col] = x
1,057✔
312
    end
1,360✔
313
    A
252✔
314
end
315

316
Base._reverse(A::UpperOrUnitUpperTriangular, dims::Integer) = reverse!(Matrix(A); dims)
8✔
317
Base._reverse(A::UpperTriangular, ::Colon) = LowerTriangular(reverse(A.data))
1✔
318
Base._reverse(A::UnitUpperTriangular, ::Colon) = UnitLowerTriangular(reverse(A.data))
1✔
319
Base._reverse(A::LowerOrUnitLowerTriangular, dims) = reverse!(Matrix(A); dims)
8✔
320
Base._reverse(A::LowerTriangular, ::Colon) = UpperTriangular(reverse(A.data))
1✔
321
Base._reverse(A::UnitLowerTriangular, ::Colon) = UnitUpperTriangular(reverse(A.data))
1✔
322

323
## structured matrix methods ##
324
function Base.replace_in_print_matrix(A::Union{UpperTriangular,UnitUpperTriangular},
158,162✔
325
                                      i::Integer, j::Integer, s::AbstractString)
326
    return i <= j ? s : Base.replace_with_centered_mark(s)
158,162✔
327
end
328
function Base.replace_in_print_matrix(A::Union{LowerTriangular,UnitLowerTriangular},
156,884✔
329
                                      i::Integer, j::Integer, s::AbstractString)
330
    return i >= j ? s : Base.replace_with_centered_mark(s)
156,884✔
331
end
332

333
Base.@constprop :aggressive function istril(A::Union{LowerTriangular,UnitLowerTriangular}, k::Integer=0)
42✔
334
    k >= 0 && return true
102✔
335
    return _istril(A, k)
×
336
end
337
Base.@constprop :aggressive function istriu(A::Union{UpperTriangular,UnitUpperTriangular}, k::Integer=0)
49✔
338
    k <= 0 && return true
1,044✔
339
    return _istriu(A, k)
×
340
end
341
istril(A::Adjoint, k::Integer=0) = istriu(A.parent, -k)
6,808✔
342
istril(A::Transpose, k::Integer=0) = istriu(A.parent, -k)
1,487✔
343
istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k)
6,819✔
344
istriu(A::Transpose, k::Integer=0) = istril(A.parent, -k)
1,487✔
345

346
function tril!(A::UpperTriangular{T}, k::Integer=0) where {T}
35✔
347
    n = size(A,1)
35✔
348
    if k < 0
35✔
349
        fill!(A.data, zero(T))
1,134✔
350
        return A
14✔
351
    elseif k == 0
21✔
352
        for j in 1:n, i in 1:j-1
7✔
353
            A.data[i,j] = zero(T)
252✔
354
        end
308✔
355
        return A
7✔
356
    else
357
        return UpperTriangular(tril!(A.data,k))
14✔
358
    end
359
end
360
function triu!(A::UpperTriangular, k::Integer=0)
132✔
361
    n = size(A,1)
194✔
362
    if k > 0
132✔
363
        for j in 1:n, i in max(1,j-k+1):j
28✔
364
            A.data[i,j] = zero(eltype(A))
756✔
365
        end
980✔
366
    end
367
    return A
132✔
368
end
369

370
function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where {T}
35✔
371
    n = size(A,1)
35✔
372
    if k < 0
35✔
373
        fill!(A.data, zero(T))
1,134✔
374
        return UpperTriangular(A.data)
14✔
375
    elseif k == 0
21✔
376
        fill!(A.data, zero(T))
567✔
377
        for i in diagind(A)
14✔
378
            A.data[i] = oneunit(T)
63✔
379
        end
119✔
380
        return UpperTriangular(A.data)
7✔
381
    else
382
        for i in diagind(A)
28✔
383
            A.data[i] = oneunit(T)
126✔
384
        end
238✔
385
        return UpperTriangular(tril!(A.data,k))
14✔
386
    end
387
end
388

389
function triu!(A::UnitUpperTriangular, k::Integer=0)
35✔
390
    for i in diagind(A)
70✔
391
        A.data[i] = oneunit(eltype(A))
315✔
392
    end
595✔
393
    return triu!(UpperTriangular(A.data), k)
35✔
394
end
395

396
function triu!(A::LowerTriangular{T}, k::Integer=0) where {T}
35✔
397
    n = size(A,1)
35✔
398
    if k > 0
35✔
399
        fill!(A.data, zero(T))
1,134✔
400
        return A
14✔
401
    elseif k == 0
21✔
402
        for j in 1:n, i in j+1:n
14✔
403
            A.data[i,j] = zero(T)
252✔
404
        end
308✔
405
        return A
7✔
406
    else
407
        return LowerTriangular(triu!(A.data, k))
14✔
408
    end
409
end
410

411
function tril!(A::LowerTriangular, k::Integer=0)
70✔
412
    n = size(A,1)
70✔
413
    if k < 0
70✔
414
        for j in 1:n, i in j:min(j-k-1,n)
28✔
415
            A.data[i, j] = zero(eltype(A))
756✔
416
        end
980✔
417
    end
418
    A
70✔
419
end
420

421
function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T
35✔
422
    n = size(A,1)
35✔
423
    if k > 0
35✔
424
        fill!(A.data, zero(T))
1,134✔
425
        return LowerTriangular(A.data)
14✔
426
    elseif k == 0
21✔
427
        fill!(A.data, zero(T))
567✔
428
        for i in diagind(A)
14✔
429
            A.data[i] = oneunit(T)
63✔
430
        end
119✔
431
        return LowerTriangular(A.data)
7✔
432
    else
433
        for i in diagind(A)
28✔
434
            A.data[i] = oneunit(T)
126✔
435
        end
238✔
436
        return LowerTriangular(triu!(A.data, k))
14✔
437
    end
438
end
439

440
function tril!(A::UnitLowerTriangular, k::Integer=0)
35✔
441
    for i in diagind(A)
70✔
442
        A.data[i] = oneunit(eltype(A))
315✔
443
    end
595✔
444
    return tril!(LowerTriangular(A.data), k)
35✔
445
end
446

447
adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data))
4,546✔
448
adjoint(A::UpperTriangular) = LowerTriangular(adjoint(A.data))
6,334✔
449
adjoint(A::UnitLowerTriangular) = UnitUpperTriangular(adjoint(A.data))
4,513✔
450
adjoint(A::UnitUpperTriangular) = UnitLowerTriangular(adjoint(A.data))
4,311✔
451
transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data))
4,249✔
452
transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data))
5,775✔
453
transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data))
4,755✔
454
transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data))
4,290✔
455

456
transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L', false, true))
22✔
457
transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L', false, false))
22✔
458
transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U', false, true))
22✔
459
transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U', false, false))
22✔
460
adjoint!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true, true))
22✔
461
adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true, false))
22✔
462
adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true, true))
22✔
463
adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true, false))
22✔
464

465
diag(A::LowerTriangular) = diag(A.data)
71✔
466
diag(A::UnitLowerTriangular) = fill(oneunit(eltype(A)), size(A,1))
159✔
467
diag(A::UpperTriangular) = diag(A.data)
168✔
468
diag(A::UnitUpperTriangular) = fill(oneunit(eltype(A)), size(A,1))
321✔
469

470
# Unary operations
471
-(A::LowerTriangular) = LowerTriangular(-A.data)
1✔
472
-(A::UpperTriangular) = UpperTriangular(-A.data)
1✔
473
function -(A::UnitLowerTriangular)
11✔
474
    Adata = A.data
11✔
475
    Anew = similar(Adata) # must be mutable, even if Adata is not
22✔
476
    @. Anew = -Adata
22✔
477
    for i = 1:size(A, 1)
11✔
478
        Anew[i, i] = -A[i, i]
77✔
479
    end
143✔
480
    LowerTriangular(Anew)
11✔
481
end
482
function -(A::UnitUpperTriangular)
11✔
483
    Adata = A.data
11✔
484
    Anew = similar(Adata) # must be mutable, even if Adata is not
22✔
485
    @. Anew = -Adata
22✔
486
    for i = 1:size(A, 1)
11✔
487
        Anew[i, i] = -A[i, i]
77✔
488
    end
143✔
489
    UpperTriangular(Anew)
11✔
490
end
491

492
# use broadcasting if the parents are strided, where we loop only over the triangular part
493
for TM in (:LowerTriangular, :UpperTriangular)
494
    @eval -(A::$TM{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast(-, A)
205✔
495
end
496

497
tr(A::LowerTriangular) = tr(A.data)
7✔
498
tr(A::UnitLowerTriangular) = size(A, 1) * oneunit(eltype(A))
7✔
499
tr(A::UpperTriangular) = tr(A.data)
7✔
500
tr(A::UnitUpperTriangular) = size(A, 1) * oneunit(eltype(A))
7✔
501

502
for T in (:UpperOrUnitUpperTriangular, :LowerOrUnitLowerTriangular)
503
    @eval @propagate_inbounds function copyto!(dest::$T, U::$T)
166✔
504
        if axes(dest) != axes(U)
14,694✔
505
            @invoke copyto!(dest::AbstractArray, U::AbstractArray)
20✔
506
        else
507
            _copyto!(dest, U)
7,342✔
508
        end
509
        return dest
7,340✔
510
    end
511
end
512

513
# copy and scale
514
for (T, UT) in ((:UpperTriangular, :UnitUpperTriangular), (:LowerTriangular, :UnitLowerTriangular))
515
    @eval @inline function _copyto!(A::$T, B::$T)
516
        @boundscheck checkbounds(A, axes(B)...)
6,688✔
517
        copytrito!(parent(A), parent(B), uplo_char(A))
6,688✔
518
        return A
6,688✔
519
    end
520
    @eval @inline function _copyto!(A::$UT, B::$T)
521
        for dind in diagind(A, IndexStyle(A))
8✔
522
            if A[dind] != B[dind]
16✔
523
                throw_nononeerror(typeof(A), B[dind], Tuple(dind)...)
2✔
524
            end
525
        end
26✔
526
        _copyto!($T(parent(A)), B)
2✔
527
        return A
2✔
528
    end
529
end
530
@inline function _copyto!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular)
531
    @boundscheck checkbounds(A, axes(B)...)
338✔
532
    n = size(B,1)
338✔
533
    B2 = Base.unalias(A, B)
338✔
534
    for j = 1:n
338✔
535
        for i = 1:j-1
2,896✔
536
            @inbounds parent(A)[i,j] = parent(B2)[i,j]
11,211✔
537
        end
19,864✔
538
        if A isa UpperTriangular # copy diagonal
2,896✔
539
            @inbounds parent(A)[j,j] = B2[j,j]
12✔
540
        end
541
    end
5,454✔
542
    return A
338✔
543
end
544
@inline function _copyto!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular)
545
    @boundscheck checkbounds(A, axes(B)...)
314✔
546
    n = size(B,1)
314✔
547
    B2 = Base.unalias(A, B)
314✔
548
    for j = 1:n
314✔
549
        if A isa LowerTriangular # copy diagonal
2,680✔
550
            @inbounds parent(A)[j,j] = B2[j,j]
12✔
551
        end
552
        for i = j+1:n
2,994✔
553
            @inbounds parent(A)[i,j] = parent(B2)[i,j]
10,347✔
554
        end
18,328✔
555
    end
5,046✔
556
    return A
314✔
557
end
558

559
_triangularize!(::UpperOrUnitUpperTriangular) = triu!
×
560
_triangularize!(::LowerOrUnitLowerTriangular) = tril!
×
561

562
@propagate_inbounds function copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular)
8✔
563
    if axes(dest) != axes(U)
54,868✔
564
        @invoke copyto!(dest::StridedMatrix, U::AbstractArray)
×
565
    else
566
        _copyto!(dest, U)
27,434✔
567
    end
568
    return dest
27,434✔
569
end
570
@propagate_inbounds function _copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular)
571
    copytrito!(dest, parent(U), U isa UpperOrUnitUpperTriangular ? 'U' : 'L')
8,199✔
572
    copytrito!(dest, U, U isa UpperOrUnitUpperTriangular ? 'L' : 'U')
8,199✔
573
    return dest
8,199✔
574
end
575
@propagate_inbounds function _copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular{<:Any, <:StridedMatrix})
576
    U2 = Base.unalias(dest, U)
19,235✔
577
    copyto_unaliased!(dest, U2)
98,076✔
578
    return dest
19,235✔
579
end
580
# for strided matrices, we explicitly loop over the arrays to improve cache locality
581
# This fuses the copytrito! for the two halves
582
@inline function copyto_unaliased!(dest::StridedMatrix, U::UpperOrUnitUpperTriangular{<:Any, <:StridedMatrix})
583
    @boundscheck checkbounds(dest, axes(U)...)
13,578✔
584
    isunit = U isa UnitUpperTriangular
13,578✔
585
    for col in axes(dest,2)
13,578✔
586
        for row in 1:col-isunit
65,428✔
587
            @inbounds dest[row,col] = U.data[row,col]
261,196✔
588
        end
459,072✔
589
        for row in col+!isunit:size(U,1)
76,892✔
590
            @inbounds dest[row,col] = U[row,col]
216,884✔
591
        end
379,804✔
592
    end
117,284✔
593
    return dest
13,578✔
594
end
595
@inline function copyto_unaliased!(dest::StridedMatrix, L::LowerOrUnitLowerTriangular{<:Any, <:StridedMatrix})
596
    @boundscheck checkbounds(dest, axes(L)...)
5,657✔
597
    isunit = L isa UnitLowerTriangular
5,657✔
598
    for col in axes(dest,2)
5,657✔
599
        for row in 1:col-!isunit
32,636✔
600
            @inbounds dest[row,col] = L[row,col]
130,245✔
601
        end
231,553✔
602
        for row in col+isunit:size(L,1)
34,588✔
603
            @inbounds dest[row,col] = L.data[row,col]
143,203✔
604
        end
255,722✔
605
    end
59,621✔
606
    return dest
5,657✔
607
end
608

609
@inline _rscale_add!(A::AbstractTriangular, B::AbstractTriangular, C::Number, alpha::Number, beta::Number) =
620✔
610
    @stable_muladdmul _triscale!(A, B, C, MulAddMul(alpha, beta))
611
@inline _lscale_add!(A::AbstractTriangular, B::Number, C::AbstractTriangular, alpha::Number, beta::Number) =
26✔
612
    @stable_muladdmul _triscale!(A, B, C, MulAddMul(alpha, beta))
613

614
function checksize1(A, B)
1,311✔
615
    szA, szB = size(A), size(B)
1,311✔
616
    szA == szB || throw(DimensionMismatch(lazy"size of A, $szA, does not match size of B, $szB"))
1,319✔
617
    checksquare(B)
1,303✔
618
end
619

620
function _triscale!(A::UpperTriangular, B::UpperTriangular, c::Number, _add)
595✔
621
    n = checksize1(A, B)
1,001✔
622
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
1,000✔
623
    for j = 1:n
1,000✔
624
        for i = 1:j
3,473✔
625
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
9,744✔
626
        end
16,015✔
627
    end
5,946✔
628
    return A
1,000✔
629
end
630
function _triscale!(A::UpperTriangular, c::Number, B::UpperTriangular, _add)
1✔
631
    n = checksize1(A, B)
40✔
632
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
39✔
633
    for j = 1:n
39✔
634
        for i = 1:j
383✔
635
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
2,075✔
636
        end
3,767✔
637
    end
727✔
638
    return A
39✔
639
end
640
function _triscale!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular, c::Number, _add)
12✔
641
    n = checksize1(A, B)
14✔
642
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
13✔
643
    for j = 1:n
9✔
644
        @inbounds _modify!(_add, c, A, (j,j))
69✔
645
        for i = 1:(j - 1)
69✔
646
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
258✔
647
        end
456✔
648
    end
129✔
649
    return A
9✔
650
end
651
function _triscale!(A::UpperOrUnitUpperTriangular, c::Number, B::UnitUpperTriangular, _add)
12✔
652
    n = checksize1(A, B)
12✔
653
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
11✔
654
    for j = 1:n
7✔
655
        @inbounds _modify!(_add, c, A, (j,j))
63✔
656
        for i = 1:(j - 1)
63✔
657
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
252✔
658
        end
448✔
659
    end
119✔
660
    return A
7✔
661
end
662
function _triscale!(A::LowerTriangular, B::LowerTriangular, c::Number, _add)
1✔
663
    n = checksize1(A, B)
178✔
664
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
177✔
665
    for j = 1:n
177✔
666
        for i = j:n
741✔
667
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
2,927✔
668
        end
5,113✔
669
    end
1,305✔
670
    return A
177✔
671
end
672
function _triscale!(A::LowerTriangular, c::Number, B::LowerTriangular, _add)
1✔
673
    n = checksize1(A, B)
40✔
674
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
39✔
675
    for j = 1:n
39✔
676
        for i = j:n
383✔
677
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
2,075✔
678
        end
3,767✔
679
    end
727✔
680
    return A
39✔
681
end
682
function _triscale!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular, c::Number, _add)
12✔
683
    n = checksize1(A, B)
14✔
684
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
13✔
685
    for j = 1:n
9✔
686
        @inbounds _modify!(_add, c, A, (j,j))
69✔
687
        for i = (j + 1):n
78✔
688
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
258✔
689
        end
456✔
690
    end
129✔
691
    return A
9✔
692
end
693
function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriangular, _add)
12✔
694
    n = checksize1(A, B)
12✔
695
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
11✔
696
    for j = 1:n
7✔
697
        @inbounds _modify!(_add, c, A, (j,j))
63✔
698
        for i = (j + 1):n
70✔
699
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
252✔
700
        end
448✔
701
    end
119✔
702
    return A
7✔
703
end
704

705
rmul!(A::UpperOrLowerTriangular, c::Number) = @inline _triscale!(A, A, c, MulAddMul())
587✔
706
lmul!(c::Number, A::UpperOrLowerTriangular) = @inline _triscale!(A, c, A, MulAddMul())
78✔
707

708
function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector)
42✔
709
    require_one_based_indexing(x, y)
42✔
710
    m = size(A, 1)
42✔
711
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
712
    if iszero(m)
42✔
713
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
714
    end
715
    x₁ = x[1]
42✔
716
    r = dot(x₁, A[1,1], y[1])
42✔
717
    @inbounds for j in 2:m
42✔
718
        yj = y[j]
336✔
719
        if !iszero(yj)
336✔
720
            temp = adjoint(A[1,j]) * x₁
336✔
721
            @simd for i in 2:j
336✔
722
                temp += adjoint(A[i,j]) * x[i]
1,512✔
723
            end
724
            r += dot(temp, yj)
336✔
725
        end
726
    end
630✔
727
    return r
42✔
728
end
729
function dot(x::AbstractVector, A::UnitUpperTriangular, y::AbstractVector)
42✔
730
    require_one_based_indexing(x, y)
42✔
731
    m = size(A, 1)
42✔
732
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
733
    if iszero(m)
42✔
734
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
735
    end
736
    x₁ = first(x)
42✔
737
    r = dot(x₁, y[1])
42✔
738
    @inbounds for j in 2:m
42✔
739
        yj = y[j]
336✔
740
        if !iszero(yj)
336✔
741
            temp = adjoint(A[1,j]) * x₁
336✔
742
            @simd for i in 2:j-1
336✔
743
                temp += adjoint(A[i,j]) * x[i]
1,176✔
744
            end
745
            r += dot(temp, yj)
420✔
746
            r += dot(x[j], yj)
336✔
747
        end
748
    end
630✔
749
    return r
42✔
750
end
751
function dot(x::AbstractVector, A::LowerTriangular, y::AbstractVector)
42✔
752
    require_one_based_indexing(x, y)
42✔
753
    m = size(A, 1)
42✔
754
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
755
    if iszero(m)
42✔
756
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
757
    end
758
    r = zero(typeof(dot(first(x), first(A), first(y))))
42✔
759
    @inbounds for j in 1:m
42✔
760
        yj = y[j]
378✔
761
        if !iszero(yj)
378✔
762
            temp = adjoint(A[j,j]) * x[j]
378✔
763
            @simd for i in j+1:m
420✔
764
                temp += adjoint(A[i,j]) * x[i]
1,512✔
765
            end
766
            r += dot(temp, yj)
378✔
767
        end
768
    end
714✔
769
    return r
42✔
770
end
771
function dot(x::AbstractVector, A::UnitLowerTriangular, y::AbstractVector)
42✔
772
    require_one_based_indexing(x, y)
42✔
773
    m = size(A, 1)
42✔
774
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
775
    if iszero(m)
42✔
776
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
777
    end
778
    r = zero(typeof(dot(first(x), first(y))))
42✔
779
    @inbounds for j in 1:m
42✔
780
        yj = y[j]
378✔
781
        if !iszero(yj)
378✔
782
            temp = x[j]
378✔
783
            @simd for i in j+1:m
420✔
784
                temp += adjoint(A[i,j]) * x[i]
1,876✔
785
            end
786
            r += dot(temp, yj)
491✔
787
        end
788
    end
714✔
789
    return r
42✔
790
end
791

792
fillstored!(A::LowerTriangular, x)     = (fillband!(A.data, x, 1-size(A,1), 0); A)
1✔
793
fillstored!(A::UnitLowerTriangular, x) = (fillband!(A.data, x, 1-size(A,1), -1); A)
1✔
794
fillstored!(A::UpperTriangular, x)     = (fillband!(A.data, x, 0, size(A,2)-1); A)
1✔
795
fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); A)
×
796

797
# Binary operations
798
+(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data + B.data)
17✔
799
+(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data + B.data)
11✔
800
+(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data + triu(B.data, 1) + I)
6✔
801
+(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data + tril(B.data, -1) + I)
1✔
802
+(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) + B.data + I)
6✔
803
+(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) + B.data + I)
1✔
804
+(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I)
1✔
805
+(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I)
1✔
806
+(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) + copyto!(similar(parent(B)), B)
406✔
807

808
-(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data - B.data)
19✔
809
-(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data - B.data)
7✔
810
-(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data - triu(B.data, 1) - I)
2✔
811
-(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data - tril(B.data, -1) - I)
1✔
812
-(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) - B.data + I)
2✔
813
-(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) - B.data + I)
1✔
814
-(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) - triu(B.data, 1))
1✔
815
-(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) - tril(B.data, -1))
1✔
816
-(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B)
396✔
817

818
# use broadcasting if the parents are strided, where we loop only over the triangular part
819
for op in (:+, :-)
820
    for TM1 in (:LowerTriangular, :UnitLowerTriangular), TM2 in (:LowerTriangular, :UnitLowerTriangular)
821
        @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B)
798✔
822
    end
823
    for TM1 in (:UpperTriangular, :UnitUpperTriangular), TM2 in (:UpperTriangular, :UnitUpperTriangular)
824
        @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B)
1,180✔
825
    end
826
end
827

828
function kron(A::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
51✔
829
    C = UpperTriangular(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B)))
51✔
830
    return kron!(C, A, B)
51✔
831
end
832
function kron(A::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
51✔
833
    C = LowerTriangular(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B)))
51✔
834
    return kron!(C, A, B)
51✔
835
end
836

837
function kron!(C::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, A::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
838
    size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!"))
51✔
839
    _triukron!(C.data, A.data, B.data)
51✔
840
    return C
51✔
841
end
842
function kron!(C::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, A::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
843
    size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!"))
51✔
844
    _trilkron!(C.data, A.data, B.data)
51✔
845
    return C
51✔
846
end
847

848
function _triukron!(C, A, B)
51✔
849
    n_A = size(A, 1)
51✔
850
    n_B = size(B, 1)
51✔
851
    @inbounds for j = 1:n_A
51✔
852
        jnB = (j - 1) * n_B
445✔
853
        for i = 1:(j-1)
445✔
854
            Aij = A[i, j]
1,766✔
855
            inB = (i - 1) * n_B
1,766✔
856
            for l = 1:n_B
1,766✔
857
                for k = 1:l
15,880✔
858
                    C[inB+k, jnB+l] = Aij * B[k, l]
79,386✔
859
                end
142,892✔
860
                for k = 1:(l-1)
15,880✔
861
                    C[inB+l, jnB+k] = zero(eltype(C))
63,506✔
862
                end
112,898✔
863
            end
29,994✔
864
        end
3,138✔
865
        Ajj = A[j, j]
445✔
866
        for l = 1:n_B
445✔
867
            for k = 1:l
3,977✔
868
                C[jnB+k, jnB+l] = Ajj * B[k, l]
19,857✔
869
            end
35,737✔
870
        end
7,509✔
871
    end
445✔
872
end
873

874
function _trilkron!(C, A, B)
51✔
875
    n_A = size(A, 1)
51✔
876
    n_B = size(B, 1)
51✔
877
    @inbounds for j = 1:n_A
51✔
878
        jnB = (j - 1) * n_B
445✔
879
        Ajj = A[j, j]
445✔
880
        for l = 1:n_B
445✔
881
            for k = l:n_B
3,977✔
882
                C[jnB+k, jnB+l] = Ajj * B[k, l]
19,857✔
883
            end
35,737✔
884
        end
7,509✔
885
        for i = (j+1):n_A
496✔
886
            Aij = A[i, j]
1,766✔
887
            inB = (i - 1) * n_B
1,766✔
888
            for l = 1:n_B
1,766✔
889
                for k = l:n_B
15,880✔
890
                    C[inB+k, jnB+l] = Aij * B[k, l]
79,386✔
891
                end
142,892✔
892
                for k = (l+1):n_B
17,646✔
893
                    C[inB+l, jnB+k] = zero(eltype(C))
63,506✔
894
                end
112,898✔
895
            end
29,994✔
896
        end
3,138✔
897
    end
445✔
898
end
899

900
######################
901
# BlasFloat routines #
902
######################
903

904
# which triangle to use of the underlying data
905
uplo_char(::UpperOrUnitUpperTriangular) = 'U'
×
906
uplo_char(::LowerOrUnitLowerTriangular) = 'L'
×
907
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:AdjOrTrans}) = 'L'
×
908
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:AdjOrTrans}) = 'U'
×
909
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'U'
×
910
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'L'
×
911
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'U'
×
912
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'L'
×
913

914
isunit_char(::UpperTriangular) = 'N'
×
915
isunit_char(::UnitUpperTriangular) = 'U'
×
916
isunit_char(::LowerTriangular) = 'N'
×
917
isunit_char(::UnitLowerTriangular) = 'U'
×
918

919
lmul!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B)
168✔
920

921
# generic fallback for AbstractTriangular matrices outside of the four subtypes provided here
922
_trimul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) =
×
923
    lmul!(A, copyto!(C, B))
924
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) =
×
925
    lmul!(A, copyto!(C, B))
926
_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) =
×
927
    rmul!(copyto!(C, A), B)
928
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractTriangular) =
×
929
    lmul!(A, copyto!(C, B))
930
# redirect for UpperOrLowerTriangular
931
_trimul!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVector) =
3,862✔
932
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
933
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractMatrix) =
5,982✔
934
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
935
_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) =
7,318✔
936
    generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
937
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) =
12,189✔
938
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
939
# disambiguation with AbstractTriangular
940
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) =
×
941
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
942
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) =
×
943
    generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
944

945
lmul!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _trimul!(B, A, B)
792✔
946
rmul!(A::AbstractMatrix, B::AbstractTriangular)   = @inline _trimul!(A, A, B)
552✔
947

948

949
for TC in (:AbstractVector, :AbstractMatrix)
950
    @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number)
951
        if isone(alpha) && iszero(beta)
3,606✔
952
            return _trimul!(C, A, B)
2,976✔
953
        else
954
            return @stable_muladdmul generic_matvecmul!(C, 'N', A, B, MulAddMul(alpha, beta))
726✔
955
        end
956
    end
957
end
958
for (TA, TB) in ((:AbstractTriangular, :AbstractMatrix),
959
                    (:AbstractMatrix, :AbstractTriangular),
960
                    (:AbstractTriangular, :AbstractTriangular)
961
                )
962
    @eval @inline function _mul!(C::AbstractMatrix, A::$TA, B::$TB, alpha::Number, beta::Number)
963
        if isone(alpha) && iszero(beta)
26,570✔
964
            return _trimul!(C, A, B)
23,376✔
965
        else
966
            return generic_matmatmul!(C, 'N', 'N', A, B, alpha, beta)
3,194✔
967
        end
968
    end
969
end
970

971
ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = _ldiv!(C, A, B)
15,767✔
972
# generic fallback for AbstractTriangular, directs to 2-arg [l/r]div!
973
_ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) =
×
974
    ldiv!(A, copyto!(C, B))
975
_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) =
×
976
    rdiv!(copyto!(C, A), B)
977
# redirect for UpperOrLowerTriangular to generic_*div!
978
_ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) =
27,835✔
979
    generic_trimatdiv!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
980
_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) =
7,324✔
981
    generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
982

983
ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _ldiv!(B, A, B)
12,068✔
984
rdiv!(A::AbstractMatrix, B::AbstractTriangular)   = @inline _rdiv!(A, A, B)
1,920✔
985

986
# preserve triangular structure in in-place multiplication/division
987
for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular),
988
                        (:UpperTriangular, :UpperTriangular, :UnitUpperTriangular),
989
                        (:UpperTriangular, :UnitUpperTriangular, :UpperTriangular),
990
                        (:UnitUpperTriangular, :UnitUpperTriangular, :UnitUpperTriangular),
991
                        (:LowerTriangular, :LowerTriangular, :LowerTriangular),
992
                        (:LowerTriangular, :LowerTriangular, :UnitLowerTriangular),
993
                        (:LowerTriangular, :UnitLowerTriangular, :LowerTriangular),
994
                        (:UnitLowerTriangular, :UnitLowerTriangular, :UnitLowerTriangular))
995
    @eval begin
996
        function _trimul!(C::$cty, A::$aty, B::$bty)
997
            _trimul!(parent(C), A, B)
1,734✔
998
            return C
1,734✔
999
        end
1000
        function _ldiv!(C::$cty, A::$aty, B::$bty)
1001
            _ldiv!(parent(C), A, B)
666✔
1002
            return C
666✔
1003
        end
1004
        function _rdiv!(C::$cty, A::$aty, B::$bty)
1005
            _rdiv!(parent(C), A, B)
1,705✔
1006
            return C
1,705✔
1007
        end
1008
    end
1009
end
1010

1011
for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'),
1012
                            (:UnitLowerTriangular, 'L', 'U'),
1013
                            (:UpperTriangular, 'U', 'N'),
1014
                            (:UnitUpperTriangular, 'U', 'U'))
1015
    @eval begin
1016
        # Matrix inverse
1017
        inv!(A::$t{T,S}) where {T<:BlasFloat,S<:StridedMatrix} =
16✔
1018
            $t{T,S}(LAPACK.trtri!($uploc, $isunitc, A.data))
1019

1020
        function inv(A::$t{T}) where {T}
533✔
1021
            S = typeof(inv(oneunit(T)))
533✔
1022
            if S <: BlasFloat || S === T # i.e. A is unitless
533✔
1023
                $t(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A))))
464✔
1024
            else
1025
                J = (one(T)*I)(size(A, 1))
650✔
1026
                $t(ldiv!(similar(A, S, size(A)), A, J))
69✔
1027
            end
1028
        end
1029

1030
        # Error bounds for triangular solve
1031
        errorbounds(A::$t{T,<:StridedMatrix}, X::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
160✔
1032
            LAPACK.trrfs!($uploc, 'N', $isunitc, A.data, B, X)
1033

1034
        # Condition numbers
1035
        function cond(A::$t{<:BlasFloat,<:StridedMatrix}, p::Real=2)
144✔
1036
            checksquare(A)
144✔
1037
            if p == 1
144✔
1038
                return inv(LAPACK.trcon!('O', $uploc, $isunitc, A.data))
64✔
1039
            elseif p == Inf
80✔
1040
                return inv(LAPACK.trcon!('I', $uploc, $isunitc, A.data))
64✔
1041
            else # use fallback
1042
                return cond(copyto!(similar(parent(A)), A), p)
16✔
1043
            end
1044
        end
1045
    end
1046
end
1047

1048
# multiplication
1049
generic_trimatmul!(c::StridedVector{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, b::AbstractVector{T}) where {T<:BlasFloat} =
1,016✔
1050
    BLAS.trmv!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, c === b ? c : copyto!(c, b))
1051
generic_trimatmul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} =
5,700✔
1052
    BLAS.trmm!('L', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), A, C === B ? C : copyto!(C, B))
1053
generic_mattrimul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} =
1,678✔
1054
    BLAS.trmm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A))
1055
# division
1056
generic_trimatdiv!(C::StridedVecOrMat{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} =
5,565✔
1057
    LAPACK.trtrs!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, C === B ? C : copyto!(C, B))
1058
generic_mattridiv!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} =
2,532✔
1059
    BLAS.trsm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A))
1060

1061
errorbounds(A::AbstractTriangular{T}, X::AbstractVecOrMat{T}, B::AbstractVecOrMat{T}) where {T<:Union{BigFloat,Complex{BigFloat}}} =
×
1062
    error("not implemented yet! Please submit a pull request.")
1063
function errorbounds(A::AbstractTriangular{TA}, X::AbstractVecOrMat{TX}, B::AbstractVecOrMat{TB}) where {TA<:Number,TX<:Number,TB<:Number}
128✔
1064
    TAXB = promote_type(TA, TB, TX, Float32)
128✔
1065
    errorbounds(convert(AbstractMatrix{TAXB}, A), convert(AbstractArray{TAXB}, X), convert(AbstractArray{TAXB}, B))
128✔
1066
end
1067

1068
# Eigensystems
1069
## Notice that trecv works for quasi-triangular matrices and therefore the lower sub diagonal must be zeroed before calling the subroutine
1070
function eigvecs(A::UpperTriangular{<:BlasFloat,<:StridedMatrix})
1✔
1071
    LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
6✔
1072
end
1073
function eigvecs(A::UnitUpperTriangular{<:BlasFloat,<:StridedMatrix})
5✔
1074
    for i = 1:size(A, 1)
5✔
1075
        A.data[i,i] = 1
45✔
1076
    end
85✔
1077
    LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
5✔
1078
end
1079
function eigvecs(A::LowerTriangular{<:BlasFloat,<:StridedMatrix})
5✔
1080
    LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)'))
5✔
1081
end
1082
function eigvecs(A::UnitLowerTriangular{<:BlasFloat,<:StridedMatrix})
5✔
1083
    for i = 1:size(A, 1)
5✔
1084
        A.data[i,i] = 1
45✔
1085
    end
85✔
1086
    LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)'))
5✔
1087
end
1088

1089
####################
1090
# Generic routines #
1091
####################
1092

1093
for (t, unitt) in ((UpperTriangular, UnitUpperTriangular),
1094
                   (LowerTriangular, UnitLowerTriangular))
1095
    tstrided = t{<:Any, <:StridedMaybeAdjOrTransMat}
1096
    @eval begin
1097
        (*)(A::$t, x::Number) = $t(A.data*x)
4✔
1098
        (*)(A::$tstrided, x::Number) = A .* x
72✔
1099

1100
        function (*)(A::$unitt, x::Number)
24✔
1101
            B = $t(A.data)*x
24✔
1102
            for i = 1:size(A, 1)
24✔
1103
                B.data[i,i] = x
188✔
1104
            end
352✔
1105
            return B
24✔
1106
        end
1107

1108
        (*)(x::Number, A::$t) = $t(x*A.data)
×
1109
        (*)(x::Number, A::$tstrided) = x .* A
846✔
1110

1111
        function (*)(x::Number, A::$unitt)
40✔
1112
            B = x*$t(A.data)
40✔
1113
            for i = 1:size(A, 1)
40✔
1114
                B.data[i,i] = x
332✔
1115
            end
624✔
1116
            return B
40✔
1117
        end
1118

1119
        (/)(A::$t, x::Number) = $t(A.data/x)
2✔
1120
        (/)(A::$tstrided, x::Number) = A ./ x
112✔
1121

1122
        function (/)(A::$unitt, x::Number)
18✔
1123
            B = $t(A.data)/x
18✔
1124
            invx = inv(x)
18✔
1125
            for i = 1:size(A, 1)
18✔
1126
                B.data[i,i] = invx
134✔
1127
            end
250✔
1128
            return B
18✔
1129
        end
1130

1131
        (\)(x::Number, A::$t) = $t(x\A.data)
×
1132
        (\)(x::Number, A::$tstrided) = x .\ A
36✔
1133

1134
        function (\)(x::Number, A::$unitt)
18✔
1135
            B = x\$t(A.data)
18✔
1136
            invx = inv(x)
18✔
1137
            for i = 1:size(A, 1)
18✔
1138
                B.data[i,i] = invx
134✔
1139
            end
250✔
1140
            return B
18✔
1141
        end
1142
    end
1143
end
1144

1145
## Generic triangular multiplication
1146
function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat)
15,095✔
1147
    require_one_based_indexing(C, A, B)
15,095✔
1148
    m, n = size(B, 1), size(B, 2)
15,095✔
1149
    N = size(A, 1)
15,095✔
1150
    if m != N
15,095✔
1151
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $(size(A,1)), has size $m"))
2,472✔
1152
    end
1153
    mc, nc = size(C, 1), size(C, 2)
12,623✔
1154
    if mc != N || nc != n
25,246✔
1155
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($N,$n)"))
×
1156
    end
1157
    oA = oneunit(eltype(A))
12,623✔
1158
    unit = isunitc == 'U'
12,623✔
1159
    @inbounds if uploc == 'U'
12,623✔
1160
        if tfun === identity
7,054✔
1161
            for j in 1:n
3,548✔
1162
                for i in 1:m
19,020✔
1163
                    Cij = (unit ? oA : A[i,i]) * B[i,j]
159,763✔
1164
                    for k in i + 1:m
178,067✔
1165
                        Cij += A[i,k] * B[k,j]
629,739✔
1166
                    end
1,116,635✔
1167
                    C[i,j] = Cij
159,047✔
1168
                end
299,074✔
1169
            end
34,492✔
1170
        else # tfun in (transpose, adjoint)
1171
            for j in 1:n
3,506✔
1172
                for i in m:-1:1
48,729✔
1173
                    Cij = (unit ? oA : tfun(A[i,i])) * B[i,j]
212,536✔
1174
                    for k in 1:i - 1
210,898✔
1175
                        Cij += tfun(A[k,i]) * B[k,j]
836,868✔
1176
                    end
1,481,515✔
1177
                    C[i,j] = Cij
210,898✔
1178
                end
397,431✔
1179
            end
45,224✔
1180
        end
1181
    else # uploc == 'L'
1182
        if tfun === identity
5,569✔
1183
            for j in 1:n
2,494✔
1184
                for i in m:-1:1
33,693✔
1185
                    Cij = (unit ? oA : A[i,i]) * B[i,j]
149,398✔
1186
                    for k in 1:i - 1
147,298✔
1187
                        Cij += A[i,k] * B[k,j]
590,517✔
1188
                    end
1,042,183✔
1189
                    C[i,j] = Cij
147,298✔
1190
                end
277,749✔
1191
            end
16,847✔
1192
        else # tfun in (transpose, adjoint)
1193
            for j in 1:n
3,075✔
1194
                for i in 1:m
23,755✔
1195
                    Cij = (unit ? oA : tfun(A[i,i])) * B[i,j]
214,626✔
1196
                    for k in i + 1:m
234,331✔
1197
                        Cij += tfun(A[k,i]) * B[k,j]
842,409✔
1198
                    end
1,481,797✔
1199
                    C[i,j] = Cij
210,576✔
1200
                end
397,397✔
1201
            end
23,755✔
1202
        end
1203
    end
1204
    return C
12,623✔
1205
end
1206
# conjugate cases
1207
function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat)
22✔
1208
    A = parent(xA)
22✔
1209
    require_one_based_indexing(C, A, B)
22✔
1210
    m, n = size(B, 1), size(B, 2)
22✔
1211
    N = size(A, 1)
22✔
1212
    if m != N
22✔
1213
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $(size(A,1)), has size $m"))
×
1214
    end
1215
    mc, nc = size(C, 1), size(C, 2)
22✔
1216
    if mc != N || nc != n
44✔
1217
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($N,$n)"))
×
1218
    end
1219
    oA = oneunit(eltype(A))
22✔
1220
    unit = isunitc == 'U'
22✔
1221
    @inbounds if uploc == 'U'
22✔
1222
        for j in 1:n
11✔
1223
            for i in 1:m
11✔
1224
                Cij = (unit ? oA : conj(A[i,i])) * B[i,j]
4,040✔
1225
                for k in i + 1:m
4,051✔
1226
                    Cij += conj(A[i,k]) * B[k,j]
1,998,147✔
1227
                end
3,992,265✔
1228
                C[i,j] = Cij
4,040✔
1229
            end
8,069✔
1230
        end
11✔
1231
    else # uploc == 'L'
1232
        for j in 1:n
11✔
1233
            for i in m:-1:1
20✔
1234
                Cij = (unit ? oA : conj(A[i,i])) * B[i,j]
4,040✔
1235
                for k in 1:i - 1
4,040✔
1236
                    Cij += conj(A[i,k]) * B[k,j]
1,998,147✔
1237
                end
3,992,265✔
1238
                C[i,j] = Cij
4,040✔
1239
            end
8,069✔
1240
        end
11✔
1241
    end
1242
    return C
22✔
1243
end
1244

1245
function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix)
5,640✔
1246
    require_one_based_indexing(C, A, B)
5,640✔
1247
    m, n = size(A, 1), size(A, 2)
5,640✔
1248
    N = size(B, 1)
5,640✔
1249
    if n != N
5,640✔
1250
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $N"))
2,472✔
1251
    end
1252
    mc, nc = size(C, 1), size(C, 2)
3,168✔
1253
    if mc != m || nc != N
6,336✔
1254
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($m,$N)"))
×
1255
    end
1256
    oB = oneunit(eltype(B))
3,168✔
1257
    unit = isunitc == 'U'
3,168✔
1258
    @inbounds if uploc == 'U'
3,168✔
1259
        if tfun === identity
1,715✔
1260
            for i in 1:m
692✔
1261
                for j in n:-1:1
8,236✔
1262
                    Cij = A[i,j] * (unit ? oB : B[j,j])
51,494✔
1263
                    for k in 1:j - 1
33,364✔
1264
                        Cij += A[i,k] * B[k,j]
133,754✔
1265
                    end
238,262✔
1266
                    C[i,j] = Cij
33,364✔
1267
                end
62,610✔
1268
            end
7,544✔
1269
        else # tfun in (transpose, adjoint)
1270
            for i in 1:m
1,023✔
1271
                for j in 1:n
6,417✔
1272
                    Cij = A[i,j] * (unit ? oB : tfun(B[j,j]))
74,157✔
1273
                    for k in j + 1:n
58,896✔
1274
                        Cij += A[i,k] * tfun(B[j,k])
207,813✔
1275
                    end
369,564✔
1276
                    C[i,j] = Cij
52,479✔
1277
                end
98,541✔
1278
            end
11,811✔
1279
        end
1280
    else # uploc == 'L'
1281
        if tfun === identity
1,453✔
1282
            for i in 1:m
539✔
1283
                for j in 1:n
3,830✔
1284
                    Cij = A[i,j] * (unit ? oB : B[j,j])
47,958✔
1285
                    for k in j + 1:n
36,154✔
1286
                        Cij += A[i,k] * B[k,j]
129,150✔
1287
                    end
229,806✔
1288
                    C[i,j] = Cij
32,324✔
1289
                end
60,818✔
1290
            end
3,830✔
1291
        else # tfun in (transpose, adjoint)
1292
            for i in 1:m
914✔
1293
                for j in n:-1:1
13,044✔
1294
                    Cij = A[i,j] * (unit ? oB : tfun(B[j,j]))
74,448✔
1295
                    for k in 1:j - 1
54,306✔
1296
                        Cij += A[i,k] * tfun(B[j,k])
212,772✔
1297
                    end
377,760✔
1298
                    C[i,j] = Cij
54,306✔
1299
                end
102,090✔
1300
            end
6,522✔
1301
        end
1302
    end
1303
    return C
3,168✔
1304
end
1305
# conjugate cases
1306
function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans)
×
1307
    B = parent(xB)
×
1308
    require_one_based_indexing(C, A, B)
×
1309
    m, n = size(A, 1), size(A, 2)
×
1310
    N = size(B, 1)
×
1311
    if n != N
×
1312
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $N"))
×
1313
    end
1314
    mc, nc = size(C, 1), size(C, 2)
×
1315
    if mc != m || nc != N
×
1316
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($m,$N)"))
×
1317
    end
1318
    oB = oneunit(eltype(B))
×
1319
    unit = isunitc == 'U'
×
1320
    @inbounds if uploc == 'U'
×
1321
        for i in 1:m
×
1322
            for j in n:-1:1
×
1323
                Cij = A[i,j] * (unit ? oB : conj(B[j,j]))
×
1324
                for k in 1:j - 1
×
1325
                    Cij += A[i,k] * conj(B[k,j])
×
1326
                end
×
1327
                C[i,j] = Cij
×
1328
            end
×
1329
        end
×
1330
    else # uploc == 'L'
1331
        for i in 1:m
×
1332
            for j in 1:n
×
1333
                Cij = A[i,j] * (unit ? oB : conj(B[j,j]))
×
1334
                for k in j + 1:n
×
1335
                    Cij += A[i,k] * conj(B[k,j])
×
1336
                end
×
1337
                C[i,j] = Cij
×
1338
            end
×
1339
        end
×
1340
    end
1341
    return C
×
1342
end
1343

1344
#Generic solver using naive substitution
1345

1346
@inline _ustrip(a) = oneunit(a) \ a
284,264✔
1347
@inline _ustrip(a::Union{AbstractFloat,Integer,Complex,Rational}) = a
2,312,496✔
1348

1349
# manually hoisting b[j] significantly improves performance as of Dec 2015
1350
# manually eliding bounds checking significantly improves performance as of Dec 2015
1351
# replacing repeated references to A.data[j,j] with [Ajj = A.data[j,j] and references to Ajj]
1352
# does not significantly impact performance as of Dec 2015
1353
# in the transpose and conjugate transpose naive substitution variants,
1354
# accumulating in z rather than b[j,k] significantly improves performance as of Dec 2015
1355
function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat)
16,291✔
1356
    require_one_based_indexing(C, A, B)
16,291✔
1357
    mA, nA = size(A)
16,291✔
1358
    m, n = size(B, 1), size(B,2)
16,291✔
1359
    if nA != m
16,291✔
1360
        throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal"))
228✔
1361
    end
1362
    if size(C) != size(B)
32,126✔
1363
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of right hand side, $(size(B))"))
×
1364
    end
1365
    iszero(mA) && return C
16,063✔
1366
    oA = oneunit(eltype(A))
16,057✔
1367
    @inbounds if uploc == 'U'
16,057✔
1368
        if isunitc == 'N'
8,227✔
1369
            if tfun === identity
5,883✔
1370
                for k in 1:n
3,216✔
1371
                    amm = A[m,m]
20,059✔
1372
                    iszero(amm) && throw(SingularException(m))
19,990✔
1373
                    Cm = C[m,k] = amm \ B[m,k]
19,952✔
1374
                    # fill C-column
1375
                    for i in m-1:-1:1
39,843✔
1376
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm
167,052✔
1377
                    end
314,249✔
1378
                    for j in m-1:-1:1
39,843✔
1379
                        ajj = A[j,j]
167,673✔
1380
                        iszero(ajj) && throw(SingularException(j))
167,052✔
1381
                        Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
167,052✔
1382
                        for i in j-1:-1:1
314,310✔
1383
                            C[i,k] -= _ustrip(A[i,j]) * Cj
709,486✔
1384
                        end
1,271,775✔
1385
                    end
314,249✔
1386
                end
36,726✔
1387
            else # tfun in (adjoint, transpose)
1388
                for k in 1:n
2,667✔
1389
                    for j in 1:m
14,097✔
1390
                        ajj = A[j,j]
131,682✔
1391
                        iszero(ajj) && throw(SingularException(j))
130,302✔
1392
                        Bj = B[j,k]
130,302✔
1393
                        for i in 1:j-1
130,302✔
1394
                            Bj -= tfun(A[i,j]) * C[i,k]
664,215✔
1395
                        end
963,741✔
1396
                        C[j,k] = tfun(ajj) \ Bj
130,302✔
1397
                    end
246,507✔
1398
                end
25,527✔
1399
            end
1400
        else # isunitc == 'U'
1401
            if tfun === identity
2,344✔
1402
                for k in 1:n
1,202✔
1403
                    Cm = C[m,k] = oA \ B[m,k]
5,551✔
1404
                    # fill C-column
1405
                    for i in m-1:-1:1
11,099✔
1406
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm
45,695✔
1407
                    end
85,839✔
1408
                    for j in m-1:-1:1
11,099✔
1409
                        Cj = C[j,k]
45,695✔
1410
                        for i in 1:j-1
45,695✔
1411
                            C[i,k] -= _ustrip(A[i,j]) * Cj
166,196✔
1412
                        end
292,248✔
1413
                    end
85,839✔
1414
                end
5,551✔
1415
            else # tfun in (adjoint, transpose)
1416
                for k in 1:n
1,142✔
1417
                    for j in 1:m
2,422✔
1418
                        Bj = B[j,k]
22,704✔
1419
                        for i in 1:j-1
22,704✔
1420
                            Bj -= tfun(A[i,j]) * C[i,k]
114,146✔
1421
                        end
170,410✔
1422
                        C[j,k] = oA \ Bj
22,704✔
1423
                    end
42,986✔
1424
                end
2,422✔
1425
            end
1426
        end
1427
    else # uploc == 'L'
1428
        if isunitc == 'N'
7,830✔
1429
            if tfun === identity
4,807✔
1430
                for k in 1:n
2,461✔
1431
                    a11 = A[1,1]
15,898✔
1432
                    iszero(a11) && throw(SingularException(1))
15,829✔
1433
                    C1 = C[1,k] = a11 \ B[1,k]
15,791✔
1434
                    # fill C-column
1435
                    for i in 2:m
15,791✔
1436
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1
130,092✔
1437
                    end
244,393✔
1438
                    for j in 2:m
15,791✔
1439
                        ajj = A[j,j]
130,713✔
1440
                        iszero(ajj) && throw(SingularException(j))
130,092✔
1441
                        Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
130,092✔
1442
                        for i in j+1:m
145,883✔
1443
                            C[i,k] -= _ustrip(A[i,j]) * Cj
473,472✔
1444
                        end
832,643✔
1445
                    end
244,393✔
1446
                end
15,791✔
1447
            else # tfun in (adjoint, transpose)
1448
                for k in 1:n
2,346✔
1449
                    for j in m:-1:1
25,606✔
1450
                        ajj = A[j,j]
119,252✔
1451
                        iszero(ajj) && throw(SingularException(j))
117,872✔
1452
                        Bj = B[j,k]
117,897✔
1453
                        for i in j+1:m
130,675✔
1454
                            Bj -= tfun(A[i,j]) * C[i,k]
607,891✔
1455
                        end
864,957✔
1456
                        C[j,k] = tfun(ajj) \ Bj
117,872✔
1457
                    end
222,941✔
1458
                end
12,803✔
1459
            end
1460
        else # isunitc == 'U'
1461
            if tfun === identity
3,023✔
1462
                for k in 1:n
1,611✔
1463
                    C1 = C[1,k] = oA \ B[1,k]
7,402✔
1464
                    # fill C-column
1465
                    for i in 2:m
7,402✔
1466
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1
64,790✔
1467
                    end
122,274✔
1468
                    for j in 2:m
7,402✔
1469
                        Cj = C[j,k]
64,790✔
1470
                        for i in j+1:m
72,096✔
1471
                            C[i,k] -= _ustrip(A[i,j]) * Cj
340,362✔
1472
                        end
623,240✔
1473
                    end
122,274✔
1474
                end
7,402✔
1475
            else # tfun in (adjoint, transpose)
1476
                for k in 1:n
1,412✔
1477
                    for j in m:-1:1
6,366✔
1478
                        Bj = B[j,k]
29,134✔
1479
                        for i in j+1:m
32,317✔
1480
                            Bj -= tfun(A[i,j]) * C[i,k]
141,091✔
1481
                        end
215,823✔
1482
                        C[j,k] = oA \ Bj
29,134✔
1483
                    end
55,085✔
1484
                end
3,183✔
1485
            end
1486
        end
1487
    end
1488
    return C
15,981✔
1489
end
1490
# conjugate cases
1491
function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat)
166✔
1492
    A = parent(xA)
166✔
1493
    require_one_based_indexing(C, A, B)
166✔
1494
    mA, nA = size(A)
166✔
1495
    m, n = size(B, 1), size(B,2)
166✔
1496
    if nA != m
166✔
1497
        throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal"))
×
1498
    end
1499
    if size(C) != size(B)
332✔
1500
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of right hand side, $(size(B))"))
×
1501
    end
1502
    iszero(mA) && return C
166✔
1503
    oA = oneunit(eltype(A))
164✔
1504
    @inbounds if uploc == 'U'
164✔
1505
        if isunitc == 'N'
82✔
1506
            for k in 1:n
82✔
1507
                amm = conj(A[m,m])
742✔
1508
                iszero(amm) && throw(SingularException(m))
742✔
1509
                Cm = C[m,k] = amm \ B[m,k]
742✔
1510
                # fill C-column
1511
                for i in m-1:-1:1
1,484✔
1512
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm
5,976✔
1513
                end
11,210✔
1514
                for j in m-1:-1:1
1,484✔
1515
                    ajj = conj(A[j,j])
5,976✔
1516
                    iszero(ajj) && throw(SingularException(j))
5,976✔
1517
                    Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
5,976✔
1518
                    for i in j-1:-1:1
11,210✔
1519
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
21,096✔
1520
                    end
36,958✔
1521
                end
11,210✔
1522
            end
1,402✔
1523
        else # isunitc == 'U'
1524
            for k in 1:n
×
1525
                Cm = C[m,k] = oA \ B[m,k]
×
1526
                # fill C-column
1527
                for i in m-1:-1:1
×
1528
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm
×
1529
                end
×
1530
                for j in m-1:-1:1
×
1531
                    Cj = C[j,k]
×
1532
                    for i in 1:j-1
×
1533
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
×
1534
                    end
×
1535
                end
×
1536
            end
×
1537
        end
1538
    else # uploc == 'L'
1539
        if isunitc == 'N'
82✔
1540
            for k in 1:n
82✔
1541
                a11 = conj(A[1,1])
742✔
1542
                iszero(a11) && throw(SingularException(1))
742✔
1543
                C1 = C[1,k] = a11 \ B[1,k]
742✔
1544
                # fill C-column
1545
                for i in 2:m
742✔
1546
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1
5,976✔
1547
                end
11,210✔
1548
                for j in 2:m
742✔
1549
                    ajj = conj(A[j,j])
5,976✔
1550
                    iszero(ajj) && throw(SingularException(j))
5,976✔
1551
                    Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
5,976✔
1552
                    for i in j+1:m
6,718✔
1553
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
21,096✔
1554
                    end
36,958✔
1555
                end
11,210✔
1556
            end
742✔
1557
        else # isunitc == 'U'
1558
            for k in 1:n
×
1559
                C1 = C[1,k] = oA \ B[1,k]
×
1560
                # fill C-column
1561
                for i in 2:m
×
1562
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1
×
1563
                end
×
1564
                for j in 1:m
×
1565
                    Cj = C[j,k]
×
1566
                    for i in j+1:m
×
1567
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
×
1568
                    end
×
1569
                end
×
1570
            end
×
1571
        end
1572
    end
1573
    return C
164✔
1574
end
1575

1576
function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix)
4,790✔
1577
    require_one_based_indexing(C, A, B)
4,790✔
1578
    m, n = size(A)
4,790✔
1579
    if size(B, 1) != n
4,790✔
1580
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))"))
2,028✔
1581
    end
1582
    if size(C) != size(A)
5,524✔
1583
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of left hand side, $(size(A))"))
×
1584
    end
1585
    oB = oneunit(eltype(B))
2,762✔
1586
    unit = isunitc == 'U'
2,762✔
1587
    @inbounds if uploc == 'U'
2,762✔
1588
        if tfun === identity
1,385✔
1589
            for i in 1:m
1,041✔
1590
                for j in 1:n
9,621✔
1591
                    Aij = A[i,j]
89,129✔
1592
                    for k in 1:j - 1
89,129✔
1593
                        Aij -= C[i,k]*B[k,j]
463,262✔
1594
                    end
659,024✔
1595
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
134,825✔
1596
                    C[i,j] = Aij / (unit ? oB : B[j,j])
89,129✔
1597
                end
168,637✔
1598
            end
18,201✔
1599
        else # tfun in (adjoint, transpose)
1600
            for i in 1:m
344✔
1601
                for j in n:-1:1
6,240✔
1602
                    Aij = A[i,j]
28,320✔
1603
                    for k in j + 1:n
31,440✔
1604
                        Aij -= C[i,k]*tfun(B[j,k])
140,688✔
1605
                    end
203,760✔
1606
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
43,680✔
1607
                    C[i,j] = Aij / (unit ? oB : tfun(B[j,j]))
28,320✔
1608
                end
53,520✔
1609
            end
5,896✔
1610
        end
1611
    else # uploc == 'L'
1612
        if tfun === identity
1,377✔
1613
            for i in 1:m
1,033✔
1614
                for j in n:-1:1
19,082✔
1615
                    Aij = A[i,j]
88,329✔
1616
                    for k in j + 1:n
97,870✔
1617
                        Aij -= C[i,k]*B[k,j]
459,662✔
1618
                    end
652,544✔
1619
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
133,625✔
1620
                    C[i,j] = Aij / (unit ? oB : B[j,j])
88,329✔
1621
                end
167,117✔
1622
            end
9,541✔
1623
        else # tfun in (adjoint, transpose)
1624
            for i in 1:m
344✔
1625
                for j in 1:n
3,120✔
1626
                    Aij = A[i,j]
28,320✔
1627
                    for k in 1:j - 1
28,320✔
1628
                        Aij -= C[i,k]*tfun(B[j,k])
140,688✔
1629
                    end
203,760✔
1630
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
43,680✔
1631
                    C[i,j] = Aij / (unit ? oB : tfun(B[j,j]))
28,320✔
1632
                end
53,520✔
1633
            end
3,120✔
1634
        end
1635
    end
1636
    return C
2,762✔
1637
end
1638
function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans)
2✔
1639
    B = parent(xB)
2✔
1640
    require_one_based_indexing(C, A, B)
2✔
1641
    m, n = size(A)
2✔
1642
    if size(B, 1) != n
2✔
1643
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))"))
×
1644
    end
1645
    if size(C) != size(A)
4✔
1646
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of left hand side, $(size(A))"))
×
1647
    end
1648
    oB = oneunit(eltype(B))
2✔
1649
    unit = isunitc == 'U'
2✔
1650
    if uploc == 'U'
2✔
1651
        @inbounds for i in 1:m
2✔
1652
            for j in 1:n
20✔
1653
                Aij = A[i,j]
×
1654
                for k in 1:j - 1
×
1655
                    Aij -= C[i,k]*conj(B[k,j])
×
1656
                end
×
1657
                unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1658
                C[i,j] = Aij / (unit ? oB : conj(B[j,j]))
×
1659
            end
×
1660
        end
38✔
1661
    else # uploc == 'L'
1662
        @inbounds for i in 1:m
×
1663
            for j in n:-1:1
×
1664
                Aij = A[i,j]
×
1665
                for k in j + 1:n
×
1666
                    Aij -= C[i,k]*conj(B[k,j])
×
1667
                end
×
1668
                unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1669
                C[i,j] = Aij / (unit ? oB : conj(B[j,j]))
×
1670
            end
×
1671
        end
×
1672
    end
1673
    return C
2✔
1674
end
1675

1676
# these are needed because we don't keep track of left- and right-multiplication in tritrimul!
1677
rmul!(A::UpperTriangular, B::UpperTriangular)     = UpperTriangular(rmul!(triu!(A.data), B))
12✔
1678
rmul!(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(rmul!(triu!(A.data), B))
12✔
1679
rmul!(A::LowerTriangular, B::LowerTriangular)     = LowerTriangular(rmul!(tril!(A.data), B))
12✔
1680
rmul!(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(rmul!(tril!(A.data), B))
12✔
1681

1682
# Promotion
1683
## Promotion methods in matmul don't apply to triangular multiplication since
1684
## it is inplace. Hence we have to make very similar definitions, but without
1685
## allocation of a result array. For multiplication and unit diagonal division
1686
## the element type doesn't have to be stable under division whereas that is
1687
## necessary in the general triangular solve problem.
1688

1689
_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA<:Integer,TB<:Integer} =
404✔
1690
    promote_op(matprod, TA, TB)
1691
_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA,TB} =
6,368✔
1692
    promote_op(op, TA, TB)
1693
## The general promotion methods
1694
for mat in (:AbstractVector, :AbstractMatrix)
1695
    ### Left division with triangle to the left hence rhs cannot be transposed. No quotients.
1696
    @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat)
3,677✔
1697
        require_one_based_indexing(B)
4,235✔
1698
        TAB = _inner_type_promotion(\, eltype(A), eltype(B))
4,235✔
1699
        ldiv!(similar(B, TAB, size(B)), A, B)
4,793✔
1700
    end
1701
    ### Left division with triangle to the left hence rhs cannot be transposed. Quotients.
1702
    @eval function \(A::Union{UpperTriangular,LowerTriangular}, B::$mat)
4,341✔
1703
        require_one_based_indexing(B)
10,706✔
1704
        TAB = promote_op(\, eltype(A), eltype(B))
10,706✔
1705
        ldiv!(similar(B, TAB, size(B)), A, B)
17,071✔
1706
    end
1707
    ### Right division with triangle to the right hence lhs cannot be transposed. No quotients.
1708
    @eval function /(A::$mat, B::Union{UnitUpperTriangular, UnitLowerTriangular})
1,979✔
1709
        require_one_based_indexing(A)
2,537✔
1710
        TAB = _inner_type_promotion(/, eltype(A), eltype(B))
2,537✔
1711
        _rdiv!(similar(A, TAB, size(A)), A, B)
3,095✔
1712
    end
1713
    ### Right division with triangle to the right hence lhs cannot be transposed. Quotients.
1714
    @eval function /(A::$mat, B::Union{UpperTriangular,LowerTriangular})
2,005✔
1715
        require_one_based_indexing(A)
2,603✔
1716
        TAB = promote_op(/, eltype(A), eltype(B))
2,603✔
1717
        _rdiv!(similar(A, TAB, size(A)), A, B)
3,201✔
1718
    end
1719
end
1720

1721
## Some Triangular-Triangular cases. We might want to write tailored methods
1722
## for these cases, but I'm not sure it is worth it.
1723
for f in (:*, :\)
1724
    @eval begin
1725
        ($f)(A::LowerTriangular, B::LowerTriangular) =
741✔
1726
            LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix))
1727
        ($f)(A::LowerTriangular, B::UnitLowerTriangular) =
672✔
1728
            LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix))
1729
        ($f)(A::UnitLowerTriangular, B::LowerTriangular) =
627✔
1730
            LowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix))
1731
        ($f)(A::UnitLowerTriangular, B::UnitLowerTriangular) =
616✔
1732
            UnitLowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix))
1733
        ($f)(A::UpperTriangular, B::UpperTriangular) =
2,600✔
1734
            UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix))
1735
        ($f)(A::UpperTriangular, B::UnitUpperTriangular) =
672✔
1736
            UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix))
1737
        ($f)(A::UnitUpperTriangular, B::UpperTriangular) =
627✔
1738
            UpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix))
1739
        ($f)(A::UnitUpperTriangular, B::UnitUpperTriangular) =
621✔
1740
            UnitUpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix))
1741
    end
1742
end
1743
(/)(A::LowerTriangular, B::LowerTriangular) =
123✔
1744
    LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular))
1745
(/)(A::LowerTriangular, B::UnitLowerTriangular) =
117✔
1746
    LowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular))
1747
(/)(A::UnitLowerTriangular, B::LowerTriangular) =
98✔
1748
    LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular))
1749
(/)(A::UnitLowerTriangular, B::UnitLowerTriangular) =
106✔
1750
    UnitLowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular))
1751
(/)(A::UpperTriangular, B::UpperTriangular) =
167✔
1752
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular))
1753
(/)(A::UpperTriangular, B::UnitUpperTriangular) =
117✔
1754
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular))
1755
(/)(A::UnitUpperTriangular, B::UpperTriangular) =
98✔
1756
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular))
1757
(/)(A::UnitUpperTriangular, B::UnitUpperTriangular) =
106✔
1758
    UnitUpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular))
1759

1760
# Complex matrix power for upper triangular factor, see:
1761
#   Higham and Lin, "A Schur-Padé algorithm for fractional powers of a Matrix",
1762
#     SIAM J. Matrix Anal. & Appl., 32 (3), (2011) 1056–1078.
1763
#   Higham and Lin, "An improved Schur-Padé algorithm for fractional powers of
1764
#     a matrix and their Fréchet derivatives", SIAM. J. Matrix Anal. & Appl.,
1765
#     34(3), (2013) 1341–1360.
1766
function powm!(A0::UpperTriangular, p::Real)
64✔
1767
    if abs(p) >= 1
64✔
1768
        throw(ArgumentError(lazy"p must be a real number in (-1,1), got $p"))
2✔
1769
    end
1770

1771
    normA0 = opnorm(A0, 1)
62✔
1772
    rmul!(A0, 1/normA0)
62✔
1773

1774
    theta = [1.53e-5, 2.25e-3, 1.92e-2, 6.08e-2, 1.25e-1, 2.03e-1, 2.84e-1]
434✔
1775
    n = checksquare(A0)
62✔
1776

1777
    A, m, s = invsquaring(A0, theta)
62✔
1778
    A = I - A
62✔
1779

1780
    # Compute accurate diagonal of I - T
1781
    sqrt_diag!(A0, A, s)
62✔
1782
    for i = 1:n
62✔
1783
        A[i, i] = -A[i, i]
214✔
1784
    end
366✔
1785
    # Compute the Padé approximant
1786
    c = 0.5 * (p - m) / (2 * m - 1)
62✔
1787
    triu!(A)
63✔
1788
    S = c * A
63✔
1789
    Stmp = similar(S)
62✔
1790
    for j = m-1:-1:1
124✔
1791
        j4 = 4 * j
266✔
1792
        c = (-p - j) / (j4 + 2)
266✔
1793
        for i = 1:n
266✔
1794
            @inbounds S[i, i] = S[i, i] + 1
940✔
1795
        end
1,614✔
1796
        copyto!(Stmp, S)
266✔
1797
        mul!(S, A, c)
532✔
1798
        ldiv!(Stmp, S)
270✔
1799

1800
        c = (p - j) / (j4 - 2)
266✔
1801
        for i = 1:n
266✔
1802
            @inbounds S[i, i] = S[i, i] + 1
940✔
1803
        end
1,614✔
1804
        copyto!(Stmp, S)
266✔
1805
        mul!(S, A, c)
532✔
1806
        ldiv!(Stmp, S)
270✔
1807
    end
470✔
1808
    for i = 1:n
62✔
1809
        S[i, i] = S[i, i] + 1
214✔
1810
    end
366✔
1811
    copyto!(Stmp, S)
62✔
1812
    mul!(S, A, -p)
124✔
1813
    ldiv!(Stmp, S)
63✔
1814
    for i = 1:n
62✔
1815
        @inbounds S[i, i] = S[i, i] + 1
214✔
1816
    end
366✔
1817

1818
    blockpower!(A0, S, p/(2^s))
62✔
1819
    for m = 1:s
62✔
1820
        mul!(Stmp.data, S, S)
492✔
1821
        copyto!(S, Stmp)
246✔
1822
        blockpower!(A0, S, p/(2^(s-m)))
246✔
1823
    end
430✔
1824
    rmul!(S, normA0^p)
124✔
1825
    return S
62✔
1826
end
1827
powm(A::LowerTriangular, p::Real) = copy(transpose(powm!(copy(transpose(A)), p::Real)))
2✔
1828

1829
# Complex matrix logarithm for the upper triangular factor, see:
1830
#   Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for
1831
#     the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169.
1832
#   Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix
1833
#     logarithm and estimating the condition number", SIAM J. Sci. Comput.,
1834
#     35(4), (2013), C394–C410.
1835
#
1836
# Based on the code available at http://eprints.ma.man.ac.uk/1851/02/logm.zip,
1837
# Copyright (c) 2011, Awad H. Al-Mohy and Nicholas J. Higham
1838
# Julia version relicensed with permission from original authors
1839
log(A::UpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A)
256✔
1840
log(A::UnitUpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A)
26✔
1841
log(A::LowerTriangular) = copy(transpose(log(copy(transpose(A)))))
4✔
1842
log(A::UnitLowerTriangular) = copy(transpose(log(copy(transpose(A)))))
4✔
1843

1844
function log_quasitriu(A0::AbstractMatrix{T}) where T<:BlasFloat
295✔
1845
    # allocate real A if log(A) will be real and complex A otherwise
1846
    n = checksquare(A0)
295✔
1847
    if isreal(A0) && (!istriu(A0) || !any(x -> real(x) < zero(real(T)), diag(A0)))
1,235✔
1848
        A = T <: Complex ? real(A0) : copy(A0)
90✔
1849
    else
1850
        A = T <: Complex ? copy(A0) : complex(A0)
205✔
1851
    end
1852
    if A0 isa UnitUpperTriangular
295✔
1853
        A = UpperTriangular(parent(A))
26✔
1854
        @inbounds for i in 1:n
26✔
1855
            A[i,i] = 1
234✔
1856
        end
442✔
1857
    end
1858
    Y0 = _log_quasitriu!(A0, A)
385✔
1859
    # return complex result for complex input
1860
    Y = T <: Complex ? complex(Y0) : Y0
320✔
1861

1862
    if A0 isa UpperTriangular || A0 isa UnitUpperTriangular
295✔
1863
        return UpperTriangular(Y)
282✔
1864
    else
1865
        return Y
13✔
1866
    end
1867
end
1868
# type-stable implementation of log_quasitriu
1869
# A is a copy of A0 that is overwritten while computing the result. It has the same eltype
1870
# as the result.
1871
function _log_quasitriu!(A0, A)
295✔
1872
    # Find Padé degree m and s while replacing A with A^(1/2^s)
1873
    m, s = _find_params_log_quasitriu!(A)
295✔
1874

1875
    # Compute accurate superdiagonal of A
1876
    _pow_superdiag_quasitriu!(A, A0, 0.5^s)
583✔
1877

1878
    # Compute accurate block diagonal of A
1879
    _sqrt_pow_diag_quasitriu!(A, A0, s)
295✔
1880

1881
    # Get the Gauss-Legendre quadrature points and weights
1882
    R = zeros(Float64, m, m)
10,102✔
1883
    for i = 1:m - 1
295✔
1884
        R[i,i+1] = i / sqrt((2 * i)^2 - 1)
1,399✔
1885
        R[i+1,i] = R[i,i+1]
1,399✔
1886
    end
2,512✔
1887
    x,V = eigen(R)
590✔
1888
    w = Vector{Float64}(undef, m)
590✔
1889
    for i = 1:m
295✔
1890
        x[i] = (x[i] + 1) / 2
1,694✔
1891
        w[i] = V[1,i]^2
1,694✔
1892
    end
3,093✔
1893

1894
    # Compute the Padé approximation
1895
    t = eltype(A)
295✔
1896
    n = size(A, 1)
295✔
1897
    Y = zeros(t, n, n)
10,278✔
1898
    B = similar(A)
308✔
1899
    for k = 1:m
295✔
1900
        B .= t(x[k]) .* A
1,755✔
1901
        @inbounds for i in 1:n
1,694✔
1902
            B[i,i] += 1
8,412✔
1903
        end
15,130✔
1904
        Y .+= t(w[k]) .* rdiv_quasitriu!(A, B)
3,388✔
1905
    end
3,093✔
1906

1907
    # Scale back
1908
    lmul!(2.0^s, Y)
583✔
1909

1910
    # Compute accurate diagonal and superdiagonal of log(A)
1911
    _log_diag_quasitriu!(Y, A0)
295✔
1912

1913
    return Y
295✔
1914
end
1915

1916
# Auxiliary functions for matrix logarithm and matrix power
1917

1918
# Find Padé degree m and s while replacing A with A^(1/2^s)
1919
#   Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for
1920
#     the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169.
1921
#   from Algorithm 4.1
1922
function _find_params_log_quasitriu!(A)
295✔
1923
    maxsqrt = 100
295✔
1924
    theta = [1.586970738772063e-005,
2,065✔
1925
         2.313807884242979e-003,
1926
         1.938179313533253e-002,
1927
         6.209171588994762e-002,
1928
         1.276404810806775e-001,
1929
         2.060962623452836e-001,
1930
         2.879093714241194e-001]
1931
    tmax = size(theta, 1)
295✔
1932
    n = size(A, 1)
295✔
1933
    p = 0
295✔
1934
    m = 0
295✔
1935

1936
    # Find s0, the smallest s such that the ρ(triu(A)^(1/2^s) - I) ≤ theta[tmax], where ρ(X)
1937
    # is the spectral radius of X
1938
    d = complex.(@view(A[diagind(A)]))
295✔
1939
    dm1 = d .- 1
590✔
1940
    s = 0
295✔
1941
    while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
1,384✔
1942
        d .= sqrt.(d)
1,089✔
1943
        dm1 .= d .- 1
2,178✔
1944
        s = s + 1
1,089✔
1945
    end
1,089✔
1946
    s0 = s
295✔
1947

1948
    # Compute repeated roots
1949
    for k = 1:min(s, maxsqrt)
295✔
1950
        _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A)
1,089✔
1951
    end
1,917✔
1952

1953
    # these three never needed at the same time, so reuse the same temporary
1954
    AmI = AmI4 = AmI5 = A - I
295✔
1955
    AmI2 = AmI * AmI
308✔
1956
    AmI3 = AmI2 * AmI
308✔
1957
    d2 = sqrt(opnorm(AmI2, 1))
295✔
1958
    d3 = cbrt(opnorm(AmI3, 1))
295✔
1959
    alpha2 = max(d2, d3)
295✔
1960
    foundm = false
295✔
1961
    if alpha2 <= theta[2]
295✔
1962
        m = alpha2 <= theta[1] ? 1 : 2
9✔
1963
        foundm = true
9✔
1964
    end
1965

1966
    while !foundm
633✔
1967
        more_sqrt = false
624✔
1968
        mul!(AmI4, AmI2, AmI2)
624✔
1969
        d4 = opnorm(AmI4, 1)^(1/4)
1,248✔
1970
        alpha3 = max(d3, d4)
624✔
1971
        if alpha3 <= theta[tmax]
624✔
1972
            local j
1973
            for outer j = 3:tmax
359✔
1974
                if alpha3 <= theta[j]
1,478✔
1975
                    break
359✔
1976
                end
1977
            end
1,119✔
1978
            if j <= 6
359✔
1979
                m = j
220✔
1980
                break
220✔
1981
            elseif alpha3 / 2 <= theta[5] && p < 2
139✔
1982
                more_sqrt = true
98✔
1983
                p = p + 1
98✔
1984
           end
1985
        end
1986

1987
        if !more_sqrt
404✔
1988
            mul!(AmI5, AmI3, AmI2)
306✔
1989
            d5 = opnorm(AmI5, 1)^(1/5)
612✔
1990
            alpha4 = max(d4, d5)
306✔
1991
            eta = min(alpha3, alpha4)
306✔
1992
            if eta <= theta[tmax]
306✔
1993
                j = 0
65✔
1994
                for outer j = 6:tmax
65✔
1995
                    if eta <= theta[j]
130✔
1996
                        m = j
65✔
1997
                        break
65✔
1998
                    end
1999
                end
65✔
2000
                break
65✔
2001
            end
2002
        end
2003

2004
        if s == maxsqrt
339✔
2005
            m = tmax
1✔
2006
            break
1✔
2007
        end
2008
        _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A)
338✔
2009
        copyto!(AmI, A)
440✔
2010
        for i in 1:n
338✔
2011
            @inbounds AmI[i,i] -= 1
1,726✔
2012
        end
3,114✔
2013
        mul!(AmI2, AmI, AmI)
338✔
2014
        mul!(AmI3, AmI2, AmI)
338✔
2015
        d3 = cbrt(opnorm(AmI3, 1))
338✔
2016
        s = s + 1
338✔
2017
    end
338✔
2018
    return m, s
295✔
2019
end
2020

2021
# Compute accurate diagonal of A = A0^s - I
2022
function sqrt_diag!(A0::UpperTriangular, A::UpperTriangular, s)
62✔
2023
    n = checksquare(A0)
62✔
2024
    T = eltype(A)
62✔
2025
    @inbounds for i = 1:n
62✔
2026
        a = complex(A0[i,i])
214✔
2027
        A[i,i] = _sqrt_pow(a, s)
214✔
2028
    end
214✔
2029
end
2030
# Compute accurate block diagonal of A = A0^s - I for upper quasi-triangular A0 produced
2031
# by the Schur decomposition. Diagonal is made of 1x1 and 2x2 blocks.
2032
# 2x2 blocks are real with non-negative conjugate pair eigenvalues
2033
function _sqrt_pow_diag_quasitriu!(A, A0, s)
295✔
2034
    n = checksquare(A0)
295✔
2035
    t = typeof(sqrt(zero(eltype(A))))
295✔
2036
    i = 1
295✔
2037
    @inbounds while i < n
295✔
2038
        if iszero(A0[i+1,i])  # 1x1 block
1,131✔
2039
            A[i,i] = _sqrt_pow(t(A0[i,i]), s)
1,114✔
2040
            i += 1
1,114✔
2041
        else  # real 2x2 block
2042
            @views _sqrt_pow_diag_block_2x2!(A[i:i+1,i:i+1], A0[i:i+1,i:i+1], s)
17✔
2043
            i += 2
17✔
2044
        end
2045
    end
1,131✔
2046
    if i == n  # last block is 1x1
295✔
2047
        @inbounds A[n,n] = _sqrt_pow(t(A0[n,n]), s)
288✔
2048
    end
2049
    return A
295✔
2050
end
2051
# compute a^(1/2^s)-1
2052
#   Al-Mohy, "A more accurate Briggs method for the logarithm",
2053
#      Numer. Algorithms, 59, (2012), 393–402.
2054
#   Algorithm 2
2055
function _sqrt_pow(a::Number, s)
1,616✔
2056
    T = typeof(sqrt(zero(a)))
1,616✔
2057
    s == 0 && return T(a) - 1
1,616✔
2058
    s0 = s
1,589✔
2059
    if imag(a) >= 0 && real(a) <= 0 && !iszero(a)  # angle(a) ≥ π / 2
1,589✔
2060
        a = sqrt(a)
139✔
2061
        s0 = s - 1
139✔
2062
    end
2063
    z0 = a - 1
1,589✔
2064
    a = sqrt(a)
1,589✔
2065
    r = 1 + a
1,589✔
2066
    for j = 1:s0-1
1,589✔
2067
        a = sqrt(a)
4,444✔
2068
        r = r * (1 + a)
4,444✔
2069
    end
7,315✔
2070
    return z0 / r
1,589✔
2071
end
2072
# compute A0 = A^(1/2^s)-I for 2x2 real matrices A and A0
2073
# A has non-negative conjugate pair eigenvalues
2074
# "Improved Inverse Scaling and Squaring Algorithms for the Matrix Logarithm"
2075
# SIAM J. Sci. Comput., 34(4), (2012) C153–C169. doi: 10.1137/110852553
2076
# Algorithm 5.1
2077
Base.@propagate_inbounds function _sqrt_pow_diag_block_2x2!(A, A0, s)
2078
    _sqrt_real_2x2!(A, A0)
17✔
2079
    if isone(s)
17✔
2080
        A[1,1] -= 1
×
2081
        A[2,2] -= 1
×
2082
    else
2083
        # Z = A - I
2084
        z11, z21, z12, z22 = A[1,1] - 1, A[2,1], A[1,2], A[2,2] - 1
17✔
2085
        # A = sqrt(A)
2086
        _sqrt_real_2x2!(A, A)
17✔
2087
        # P = A + I
2088
        p11, p21, p12, p22 = A[1,1] + 1, A[2,1], A[1,2], A[2,2] + 1
17✔
2089
        for i in 1:(s - 2)
17✔
2090
            # A = sqrt(A)
2091
            _sqrt_real_2x2!(A, A)
423✔
2092
            a11, a21, a12, a22 = A[1,1], A[2,1], A[1,2], A[2,2]
423✔
2093
            # P += P * A
2094
            r11 = p11*(1 + a11) + p12*a21
423✔
2095
            r22 = p21*a12 + p22*(1 + a22)
423✔
2096
            p21 = p21*(1 + a11) + p22*a21
423✔
2097
            p12 = p11*a12 + p12*(1 + a22)
423✔
2098
            p11 = r11
423✔
2099
            p22 = r22
423✔
2100
        end
829✔
2101
        # A = Z / P
2102
        c = inv(p11*p22 - p21*p12)
17✔
2103
        A[1,1] = (p22*z11 - p21*z12) * c
17✔
2104
        A[2,1] = (p22*z21 - p21*z22) * c
17✔
2105
        A[1,2] = (p11*z12 - p12*z11) * c
17✔
2106
        A[2,2] = (p11*z22 - p12*z21) * c
17✔
2107
    end
2108
    return A
17✔
2109
end
2110
# Compute accurate superdiagonal of A = A0^s - I for upper quasi-triangular A0 produced
2111
# by a Schur decomposition.
2112
# Higham and Lin, "A Schur–Padé Algorithm for Fractional Powers of a Matrix"
2113
# SIAM J. Matrix Anal. Appl., 32(3), (2011), 1056–1078.
2114
# Equation 5.6
2115
# see also blockpower for when A0 is upper triangular
2116
function _pow_superdiag_quasitriu!(A, A0, p)
295✔
2117
    n = checksquare(A0)
295✔
2118
    t = eltype(A)
295✔
2119
    k = 1
295✔
2120
    @inbounds while k < n
295✔
2121
        if !iszero(A[k+1,k])
1,124✔
2122
            k += 2
10✔
2123
            continue
10✔
2124
        end
2125
        if !(k == n - 1 || iszero(A[k+2,k+1]))
1,949✔
2126
            k += 3
7✔
2127
            continue
7✔
2128
        end
2129
        Ak = t(A0[k,k])
1,107✔
2130
        Akp1 = t(A0[k+1,k+1])
1,107✔
2131

2132
        Akp = Ak^p
1,107✔
2133
        Akp1p = Akp1^p
1,107✔
2134

2135
        if Ak == Akp1
1,107✔
2136
            A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
306✔
2137
        elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
1,590✔
2138
            A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
424✔
2139
        else
2140
            logAk = log(Ak)
377✔
2141
            logAkp1 = log(Akp1)
377✔
2142
            z = (Akp1 - Ak)/(Akp1 + Ak)
377✔
2143
            if abs(z) > 1
488✔
2144
                A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
73✔
2145
            else
2146
                w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
399✔
2147
                dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
513✔
2148
                A[k,k+1] = A0[k,k+1] * dd
304✔
2149
            end
2150
        end
2151
        k += 1
1,107✔
2152
    end
1,124✔
2153
end
2154

2155
# Compute accurate block diagonal and superdiagonal of A = log(A0) for upper
2156
# quasi-triangular A0 produced by the Schur decomposition.
2157
function _log_diag_quasitriu!(A, A0)
295✔
2158
    n = checksquare(A0)
295✔
2159
    t = eltype(A)
295✔
2160
    k = 1
295✔
2161
    @inbounds while k < n
295✔
2162
        if iszero(A0[k+1,k])  # 1x1 block
776✔
2163
            Ak = t(A0[k,k])
759✔
2164
            logAk = log(Ak)
759✔
2165
            A[k,k] = logAk
759✔
2166
            if k < n - 2 && iszero(A0[k+2,k+1])
759✔
2167
                Akp1 = t(A0[k+1,k+1])
355✔
2168
                logAkp1 = log(Akp1)
355✔
2169
                A[k+1,k+1] = logAkp1
355✔
2170
                if Ak == Akp1
355✔
2171
                    A[k,k+1] = A0[k,k+1] / Ak
114✔
2172
                elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
486✔
2173
                    A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
121✔
2174
                else
2175
                    z = (Akp1 - Ak)/(Akp1 + Ak)
120✔
2176
                    if abs(z) > 1
152✔
2177
                        A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
32✔
2178
                    else
2179
                        w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
128✔
2180
                        A[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
88✔
2181
                    end
2182
                end
2183
                k += 2
355✔
2184
            else
2185
                k += 1
404✔
2186
            end
2187
        else  # real 2x2 block
2188
            @views _log_diag_block_2x2!(A[k:k+1,k:k+1], A0[k:k+1,k:k+1])
17✔
2189
            k += 2
17✔
2190
        end
2191
    end
776✔
2192
    if k == n  # last 1x1 block
295✔
2193
        @inbounds A[n,n] = log(t(A0[n,n]))
288✔
2194
    end
2195
    return A
295✔
2196
end
2197
# compute A0 = log(A) for 2x2 real matrices A and A0, where A0 is a diagonal 2x2 block
2198
# produced by real Schur decomposition.
2199
# Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix
2200
# logarithm and estimating the condition number", SIAM J. Sci. Comput.,
2201
# 35(4), (2013), C394–C410.
2202
# Eq. 6.1
2203
Base.@propagate_inbounds function _log_diag_block_2x2!(A, A0)
2204
    a, b, c = A0[1,1], A0[1,2], A0[2,1]
17✔
2205
    # avoid underflow/overflow for large/small b and c
2206
    s = sqrt(abs(b)) * sqrt(abs(c))
17✔
2207
    θ = atan(s, a)
17✔
2208
    t = θ / s
17✔
2209
    au = abs(a)
17✔
2210
    if au > s
17✔
2211
        a1 = log1p((s / au)^2) / 2 + log(au)
7✔
2212
    else
2213
        a1 = log1p((au / s)^2) / 2 + log(s)
10✔
2214
    end
2215
    A[1,1] = a1
17✔
2216
    A[2,1] = c*t
17✔
2217
    A[1,2] = b*t
17✔
2218
    A[2,2] = a1
17✔
2219
    return A
17✔
2220
end
2221

2222
# Used only by powm at the moment
2223
# Repeatedly compute the square roots of A so that in the end its
2224
# eigenvalues are close enough to the positive real line
2225
function invsquaring(A0::UpperTriangular, theta)
62✔
2226
    require_one_based_indexing(theta)
62✔
2227
    # assumes theta is in ascending order
2228
    maxsqrt = 100
62✔
2229
    tmax = size(theta, 1)
62✔
2230
    n = checksquare(A0)
62✔
2231
    A = complex(copy(A0))
62✔
2232
    p = 0
62✔
2233
    m = 0
62✔
2234

2235
    # Compute repeated roots
2236
    d = complex(diag(A))
62✔
2237
    dm1 = d .- 1
124✔
2238
    s = 0
62✔
2239
    while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
216✔
2240
        d .= sqrt.(d)
154✔
2241
        dm1 .= d .- 1
308✔
2242
        s = s + 1
154✔
2243
    end
154✔
2244
    s0 = s
62✔
2245
    for k = 1:min(s, maxsqrt)
62✔
2246
        A = sqrt(A)
154✔
2247
    end
262✔
2248

2249
    AmI = A - I
62✔
2250
    d2 = sqrt(opnorm(AmI^2, 1))
62✔
2251
    d3 = cbrt(opnorm(AmI^3, 1))
62✔
2252
    alpha2 = max(d2, d3)
63✔
2253
    foundm = false
62✔
2254
    if alpha2 <= theta[2]
62✔
2255
        m = alpha2 <= theta[1] ? 1 : 2
×
2256
        foundm = true
×
2257
    end
2258

2259
    while !foundm
198✔
2260
        more = false
198✔
2261
        if s > s0
198✔
2262
            d3 = cbrt(opnorm(AmI^3, 1))
122✔
2263
        end
2264
        d4 = opnorm(AmI^4, 1)^(1/4)
392✔
2265
        alpha3 = max(d3, d4)
202✔
2266
        if alpha3 <= theta[tmax]
198✔
2267
            local j
2268
            for outer j = 3:tmax
150✔
2269
                if alpha3 <= theta[j]
644✔
2270
                    break
150✔
2271
                elseif alpha3 / 2 <= theta[5] && p < 2
494✔
2272
                    more = true
124✔
2273
                    p = p + 1
124✔
2274
                end
2275
            end
494✔
2276
            if j <= 6
150✔
2277
                m = j
62✔
2278
                foundm = true
62✔
2279
                break
62✔
2280
            elseif alpha3 / 2 <= theta[5] && p < 2
88✔
2281
                more = true
×
2282
                p = p + 1
×
2283
           end
2284
        end
2285

2286
        if !more
136✔
2287
            d5 = opnorm(AmI^5, 1)^(1/5)
182✔
2288
            alpha4 = max(d4, d5)
94✔
2289
            eta = min(alpha3, alpha4)
94✔
2290
            if eta <= theta[tmax]
92✔
2291
                j = 0
60✔
2292
                for outer j = 6:tmax
60✔
2293
                    if eta <= theta[j]
60✔
2294
                        m = j
14✔
2295
                        break
14✔
2296
                    end
2297
                    break
46✔
2298
                end
×
2299
            end
2300
            if s == maxsqrt
92✔
2301
                m = tmax
×
2302
                break
×
2303
            end
2304
            A = sqrt(A)
92✔
2305
            AmI = A - I
92✔
2306
            s = s + 1
92✔
2307
        end
2308
    end
136✔
2309

2310
    # Compute accurate superdiagonal of T
2311
    p = 1 / 2^s
62✔
2312
    A = complex(A)
62✔
2313
    blockpower!(A, A0, p)
62✔
2314
    return A,m,s
62✔
2315
end
2316

2317
# Compute accurate diagonal and superdiagonal of A = A0^p
2318
function blockpower!(A::UpperTriangular, A0::UpperTriangular, p)
370✔
2319
    n = checksquare(A0)
370✔
2320
    @inbounds for k = 1:n-1
370✔
2321
        Ak = complex(A0[k,k])
968✔
2322
        Akp1 = complex(A0[k+1,k+1])
968✔
2323

2324
        Akp = Ak^p
968✔
2325
        Akp1p = Akp1^p
968✔
2326

2327
        A[k,k] = Akp
968✔
2328
        A[k+1,k+1] = Akp1p
968✔
2329

2330
        if Ak == Akp1
968✔
2331
            A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
137✔
2332
        elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
1,618✔
2333
            A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
102✔
2334
        else
2335
            logAk = log(Ak)
729✔
2336
            logAkp1 = log(Akp1)
729✔
2337
            z = (Akp1 - Ak)/(Akp1 + Ak)
729✔
2338
            if abs(z) > 1
729✔
2339
                A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
×
2340
            else
2341
                w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
729✔
2342
                dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
1,458✔
2343
                A[k,k+1] = A0[k,k+1] * dd
729✔
2344
            end
2345
        end
2346
    end
968✔
2347
end
2348

2349
# Unwinding number
2350
unw(x::Real) = 0
×
2351
unw(x::Number) = ceil((imag(x) - pi) / (2 * pi))
1,972✔
2352

2353
# compute A / B for upper quasi-triangular B, possibly overwriting B
2354
function rdiv_quasitriu!(A, B)
1,694✔
2355
    n = checksquare(A)
1,694✔
2356
    AG = copy(A)
1,694✔
2357
    # use Givens rotations to annihilate 2x2 blocks
2358
    @inbounds for k in 1:(n-1)
1,694✔
2359
        s = B[k+1,k]
6,718✔
2360
        iszero(s) && continue  # 1x1 block
6,718✔
2361
        G = first(givens(B[k+1,k+1], s, k, k+1))
85✔
2362
        rmul!(B, G)
400✔
2363
        rmul!(AG, G)
85✔
2364
    end
11,758✔
2365
    return rdiv!(AG, UpperTriangular(B))
1,694✔
2366
end
2367

2368
# End of auxiliary functions for matrix logarithm and matrix power
2369

2370
sqrt(A::UpperTriangular) = sqrt_quasitriu(A)
592✔
2371
function sqrt(A::UnitUpperTriangular{T}) where T
20✔
2372
    B = A.data
20✔
2373
    n = checksquare(B)
20✔
2374
    t = typeof(sqrt(zero(T)))
20✔
2375
    R = Matrix{t}(I, n, n)
20✔
2376
    tt = typeof(oneunit(t)*oneunit(t))
20✔
2377
    half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214
22✔
2378
    @inbounds for j = 1:n
20✔
2379
        for i = j-1:-1:1
264✔
2380
            r::tt = B[i,j]
520✔
2381
            @simd for k = i+1:j-1
640✔
2382
                r -= R[i,k]*R[k,j]
1,180✔
2383
            end
2384
            iszero(r) || (R[i,j] = half*r)
1,035✔
2385
        end
914✔
2386
    end
264✔
2387
    return UnitUpperTriangular(R)
20✔
2388
end
2389
sqrt(A::LowerTriangular) = copy(transpose(sqrt(copy(transpose(A)))))
8✔
2390
sqrt(A::UnitLowerTriangular) = copy(transpose(sqrt(copy(transpose(A)))))
8✔
2391

2392
# Auxiliary functions for matrix square root
2393

2394
# square root of upper triangular or real upper quasitriangular matrix
2395
function sqrt_quasitriu(A0; blockwidth = eltype(A0) <: Complex ? 512 : 256)
1,360✔
2396
    n = checksquare(A0)
680✔
2397
    T = eltype(A0)
680✔
2398
    Tr = typeof(sqrt(real(zero(T))))
680✔
2399
    Tc = typeof(sqrt(complex(zero(T))))
680✔
2400
    if isreal(A0)
2,181✔
2401
        is_sqrt_real = true
290✔
2402
        if istriu(A0)
290✔
2403
            for i in 1:n
206✔
2404
                Aii = real(A0[i,i])
612✔
2405
                if Aii < zero(Aii)
612✔
2406
                    is_sqrt_real = false
40✔
2407
                    break
40✔
2408
                end
2409
            end
572✔
2410
        end
2411
        if is_sqrt_real
290✔
2412
            R = zeros(Tr, n, n)
27,060✔
2413
            A = real(A0)
250✔
2414
        else
2415
            R = zeros(Tc, n, n)
347✔
2416
            A = A0
40✔
2417
        end
2418
    else
2419
        A = A0
390✔
2420
        R = zeros(Tc, n, n)
390✔
2421
    end
2422
    _sqrt_quasitriu!(R, A; blockwidth=blockwidth, n=n)
925✔
2423
    Rc = eltype(A0) <: Real ? R : complex(R)
779✔
2424
    if A0 isa UpperTriangular
680✔
2425
        return UpperTriangular(Rc)
592✔
2426
    elseif A0 isa UnitUpperTriangular
88✔
2427
        return UnitUpperTriangular(Rc)
×
2428
    else
2429
        return Rc
88✔
2430
    end
2431
end
2432

2433
# in-place recursive sqrt of upper quasi-triangular matrix A from
2434
# Deadman E., Higham N.J., Ralha R. (2013) Blocked Schur Algorithms for Computing the Matrix
2435
# Square Root. Applied Parallel and Scientific Computing. PARA 2012. Lecture Notes in
2436
# Computer Science, vol 7782. https://doi.org/10.1007/978-3-642-36803-5_12
2437
function _sqrt_quasitriu!(R, A; blockwidth=64, n=checksquare(A))
4,330✔
2438
    if n ≤ blockwidth || !(eltype(R) <: BlasFloat) # base case, perform "point" algorithm
2,201✔
2439
        _sqrt_quasitriu_block!(R, A)
2,138✔
2440
    else  # compute blockwise recursion
2441
        split = div(n, 2)
31✔
2442
        iszero(A[split+1, split]) || (split += 1) # don't split 2x2 diagonal block
39✔
2443
        r1 = 1:split
31✔
2444
        r2 = (split + 1):n
31✔
2445
        n1, n2 = split, n - split
31✔
2446
        A11, A12, A22 = @views A[r1,r1], A[r1,r2], A[r2,r2]
31✔
2447
        R11, R12, R22 = @views R[r1,r1], R[r1,r2], R[r2,r2]
31✔
2448
        # solve diagonal blocks recursively
2449
        _sqrt_quasitriu!(R11, A11; blockwidth=blockwidth, n=n1)
31✔
2450
        _sqrt_quasitriu!(R22, A22; blockwidth=blockwidth, n=n2)
31✔
2451
        # solve off-diagonal block
2452
        R12 .= .- A12
62✔
2453
        _sylvester_quasitriu!(R11, R22, R12; blockwidth=blockwidth, nA=n1, nB=n2, raise=false)
31✔
2454
    end
2455
    return R
2,169✔
2456
end
2457

2458
function _sqrt_quasitriu_block!(R, A)
2459
    _sqrt_quasitriu_diag_block!(R, A)
2,138✔
2460
    _sqrt_quasitriu_offdiag_block!(R, A)
2,138✔
2461
    return R
2,138✔
2462
end
2463

2464
function _sqrt_quasitriu_diag_block!(R, A)
2,138✔
2465
    n = size(R, 1)
2,138✔
2466
    ta = eltype(R) <: Complex ? complex(eltype(A)) : eltype(A)
2,138✔
2467
    i = 1
2,138✔
2468
    @inbounds while i < n
2,138✔
2469
        if iszero(A[i + 1, i])
7,788✔
2470
            R[i, i] = sqrt(ta(A[i, i]))
7,052✔
2471
            i += 1
7,052✔
2472
        else
2473
            # This branch is never reached when A is complex triangular
2474
            @assert eltype(A) <: Real
736✔
2475
            @views _sqrt_real_2x2!(R[i:(i + 1), i:(i + 1)], A[i:(i + 1), i:(i + 1)])
736✔
2476
            i += 2
736✔
2477
        end
2478
    end
7,788✔
2479
    if i == n
2,138✔
2480
        R[n, n] = sqrt(ta(A[n, n]))
1,700✔
2481
    end
2482
    return R
2,138✔
2483
end
2484

2485
function _sqrt_quasitriu_offdiag_block!(R, A)
2,138✔
2486
    n = size(R, 1)
2,138✔
2487
    j = 1
2,138✔
2488
    @inbounds while j ≤ n
2,138✔
2489
        jsize_is_2 = j < n && !iszero(A[j + 1, j])
9,488✔
2490
        i = j - 1
9,488✔
2491
        while i > 0
76,393✔
2492
            isize_is_2 = i > 1 && !iszero(A[i, i - 1])
66,905✔
2493
            if isize_is_2
66,905✔
2494
                if jsize_is_2
831✔
2495
                    _sqrt_quasitriu_offdiag_block_2x2!(R, A, i - 1, j)
417✔
2496
                else
2497
                    _sqrt_quasitriu_offdiag_block_2x1!(R, A, i - 1, j)
417✔
2498
                end
2499
                i -= 2
831✔
2500
            else
2501
                if jsize_is_2
66,074✔
2502
                    _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j)
250✔
2503
                else
2504
                    _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j)
65,824✔
2505
                end
2506
                i -= 1
66,074✔
2507
            end
2508
        end
66,905✔
2509
        j += 2 - !jsize_is_2
9,488✔
2510
    end
9,488✔
2511
    return R
2,138✔
2512
end
2513

2514
# real square root of 2x2 diagonal block of quasi-triangular matrix from real Schur
2515
# decomposition. Eqs 6.8-6.9 and Algorithm 6.5 of
2516
# Higham, 2008, "Functions of Matrices: Theory and Computation", SIAM.
2517
Base.@propagate_inbounds function _sqrt_real_2x2!(R, A)
2518
    # in the real Schur form, A[1, 1] == A[2, 2], and A[2, 1] * A[1, 2] < 0
2519
    θ, a21, a12 = A[1, 1], A[2, 1], A[1, 2]
1,193✔
2520
    # avoid overflow/underflow of μ
2521
    # for real sqrt, |d| ≤ 2 max(|a12|,|a21|)
2522
    μ = sqrt(abs(a12)) * sqrt(abs(a21))
1,193✔
2523
    α = _real_sqrt(θ, μ)
1,307✔
2524
    c = 2α
1,193✔
2525
    R[1, 1] = α
1,193✔
2526
    R[2, 1] = a21 / c
1,193✔
2527
    R[1, 2] = a12 / c
1,193✔
2528
    R[2, 2] = α
1,193✔
2529
    return R
1,193✔
2530
end
2531

2532
# real part of square root of θ+im*μ
2533
@inline function _real_sqrt(θ, μ)
2534
    t = sqrt((abs(θ) + hypot(θ, μ)) / 2)
1,278✔
2535
    return θ ≥ 0 ? t : μ / 2t
1,307✔
2536
end
2537

2538
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j)
2539
    Rii = R[i, i]
65,824✔
2540
    Rjj = R[j, j]
65,824✔
2541
    iszero(Rii) && iszero(Rjj) && return R
65,824✔
2542
    t = eltype(R)
65,823✔
2543
    tt = typeof(zero(t)*zero(t))
65,823✔
2544
    r = tt(-A[i, j])
65,823✔
2545
    @simd for k in (i + 1):(j - 1)
72,764✔
2546
        r += R[i, k] * R[k, j]
2,992,126✔
2547
    end
2548
    iszero(r) && return R
65,823✔
2549
    R[i, j] = sylvester(Rii, Rjj, r)
56,802✔
2550
    return R
56,802✔
2551
end
2552

2553
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j)
2554
    jrange = j:(j + 1)
250✔
2555
    t = eltype(R)
250✔
2556
    tt = typeof(zero(t)*zero(t))
250✔
2557
    r1 = tt(-A[i, j])
250✔
2558
    r2 = tt(-A[i, j + 1])
250✔
2559
    @simd for k in (i + 1):(j - 1)
360✔
2560
        rik = R[i, k]
476✔
2561
        r1 += rik * R[k, j]
476✔
2562
        r2 += rik * R[k, j + 1]
476✔
2563
    end
2564
    Rjj = @view R[jrange, jrange]
250✔
2565
    Rij = @view R[i, jrange]
250✔
2566
    Rij[1] = r1
250✔
2567
    Rij[2] = r2
250✔
2568
    _sylvester_1x2!(R[i, i], Rjj, Rij)
250✔
2569
    return R
250✔
2570
end
2571

2572
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x1!(R, A, i, j)
2573
    irange = i:(i + 1)
417✔
2574
    t = eltype(R)
417✔
2575
    tt = typeof(zero(t)*zero(t))
417✔
2576
    r1 = tt(-A[i, j])
417✔
2577
    r2 = tt(-A[i + 1, j])
417✔
2578
    @simd for k in (i + 2):(j - 1)
546✔
2579
        rkj = R[k, j]
1,003✔
2580
        r1 += R[i, k] * rkj
1,003✔
2581
        r2 += R[i + 1, k] * rkj
1,003✔
2582
    end
2583
    Rii = @view R[irange, irange]
417✔
2584
    Rij = @view R[irange, j]
417✔
2585
    Rij[1] = r1
417✔
2586
    Rij[2] = r2
417✔
2587
    @views _sylvester_2x1!(Rii, R[j, j], Rij)
417✔
2588
    return R
417✔
2589
end
2590

2591
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x2!(R, A, i, j)
2592
    irange = i:(i + 1)
414✔
2593
    jrange = j:(j + 1)
414✔
2594
    t = eltype(R)
414✔
2595
    tt = typeof(zero(t)*zero(t))
414✔
2596
    for i′ in irange, j′ in jrange
414✔
2597
        Cij = tt(-A[i′, j′])
1,656✔
2598
        @simd for k in (i + 2):(j - 1)
2,332✔
2599
            Cij += R[i′, k] * R[k, j′]
3,464✔
2600
        end
2601
        R[i′, j′] = Cij
1,656✔
2602
    end
2,070✔
2603
    Rii = @view R[irange, irange]
414✔
2604
    Rjj = @view R[jrange, jrange]
414✔
2605
    Rij = @view R[irange, jrange]
414✔
2606
    if !iszero(Rij) && !all(isnan, Rij)
417✔
2607
        _sylvester_2x2!(Rii, Rjj, Rij)
411✔
2608
    end
2609
    return R
414✔
2610
end
2611

2612
# solve Sylvester's equation AX + XB = -C using blockwise recursion until the dimension of
2613
# A and B are no greater than blockwidth, based on Algorithm 1 from
2614
# Jonsson I, Kågström B. Recursive blocked algorithms for solving triangular systems—
2615
# Part I: one-sided and coupled Sylvester-type matrix equations. (2002) ACM Trans Math Softw.
2616
# 28(4), https://doi.org/10.1145/592843.592845.
2617
# specify raise=false to avoid breaking the recursion if a LAPACKException is thrown when
2618
# computing one of the blocks.
2619
function _sylvester_quasitriu!(A, B, C; blockwidth=64, nA=checksquare(A), nB=checksquare(B), raise=true)
863✔
2620
    if 1 ≤ nA ≤ blockwidth && 1 ≤ nB ≤ blockwidth
539✔
2621
        _sylvester_quasitriu_base!(A, B, C; raise=raise)
398✔
2622
    elseif nA ≥ 2nB ≥ 2
141✔
2623
        _sylvester_quasitriu_split1!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
12✔
2624
    elseif nB ≥ 2nA ≥ 2
129✔
2625
        _sylvester_quasitriu_split2!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
12✔
2626
    else
2627
        _sylvester_quasitriu_splitall!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
117✔
2628
    end
2629
    return C
499✔
2630
end
2631
function _sylvester_quasitriu_base!(A, B, C; raise=true)
796✔
2632
    try
398✔
2633
        _, scale = LAPACK.trsyl!('N', 'N', A, B, C)
398✔
2634
        rmul!(C, -inv(scale))
398✔
2635
    catch e
2636
        if !(e isa LAPACKException) || raise
296✔
2637
            throw(e)
148✔
2638
        end
2639
    end
2640
    return C
382✔
2641
end
2642
function _sylvester_quasitriu_split1!(A, B, C; nA=checksquare(A), kwargs...)
24✔
2643
    iA = div(nA, 2)
12✔
2644
    iszero(A[iA + 1, iA]) || (iA += 1)  # don't split 2x2 diagonal block
12✔
2645
    rA1, rA2 = 1:iA, (iA + 1):nA
12✔
2646
    nA1, nA2 = iA, nA-iA
12✔
2647
    A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2]
12✔
2648
    C1, C2 = @views C[rA1,:], C[rA2,:]
12✔
2649
    _sylvester_quasitriu!(A22, B, C2; nA=nA2, kwargs...)
24✔
2650
    mul!(C1, A12, C2, true, true)
8✔
2651
    _sylvester_quasitriu!(A11, B, C1; nA=nA1, kwargs...)
16✔
2652
    return C
8✔
2653
end
2654
function _sylvester_quasitriu_split2!(A, B, C; nB=checksquare(B), kwargs...)
24✔
2655
    iB = div(nB, 2)
12✔
2656
    iszero(B[iB + 1, iB]) || (iB += 1)  # don't split 2x2 diagonal block
12✔
2657
    rB1, rB2 = 1:iB, (iB + 1):nB
12✔
2658
    nB1, nB2 = iB, nB-iB
12✔
2659
    B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2]
12✔
2660
    C1, C2 = @views C[:,rB1], C[:,rB2]
12✔
2661
    _sylvester_quasitriu!(A, B11, C1; nB=nB1, kwargs...)
24✔
2662
    mul!(C2, C1, B12, true, true)
8✔
2663
    _sylvester_quasitriu!(A, B22, C2; nB=nB2, kwargs...)
16✔
2664
    return C
8✔
2665
end
2666
function _sylvester_quasitriu_splitall!(A, B, C; nA=checksquare(A), nB=checksquare(B), kwargs...)
234✔
2667
    iA = div(nA, 2)
117✔
2668
    iszero(A[iA + 1, iA]) || (iA += 1)  # don't split 2x2 diagonal block
128✔
2669
    iB = div(nB, 2)
117✔
2670
    iszero(B[iB + 1, iB]) || (iB += 1)  # don't split 2x2 diagonal block
136✔
2671
    rA1, rA2 = 1:iA, (iA + 1):nA
117✔
2672
    nA1, nA2 = iA, nA-iA
117✔
2673
    rB1, rB2 = 1:iB, (iB + 1):nB
117✔
2674
    nB1, nB2 = iB, nB-iB
117✔
2675
    A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2]
117✔
2676
    B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2]
117✔
2677
    C11, C21, C12, C22 = @views C[rA1,rB1], C[rA2,rB1], C[rA1,rB2], C[rA2,rB2]
117✔
2678
    _sylvester_quasitriu!(A22, B11, C21; nA=nA2, nB=nB1, kwargs...)
129✔
2679
    mul!(C11, A12, C21, true, true)
101✔
2680
    _sylvester_quasitriu!(A11, B11, C11; nA=nA1, nB=nB1, kwargs...)
109✔
2681
    mul!(C22, C21, B12, true, true)
101✔
2682
    _sylvester_quasitriu!(A22, B22, C22; nA=nA2, nB=nB2, kwargs...)
109✔
2683
    mul!(C12, A12, C22, true, true)
101✔
2684
    mul!(C12, C11, B12, true, true)
101✔
2685
    _sylvester_quasitriu!(A11, B22, C12; nA=nA1, nB=nB2, kwargs...)
109✔
2686
    return C
101✔
2687
end
2688

2689
# End of auxiliary functions for matrix square root
2690

2691
# Generic eigensystems
2692
eigvals(A::AbstractTriangular) = diag(A)
100✔
2693
function eigvecs(A::AbstractTriangular{T}) where T
4✔
2694
    TT = promote_type(T, Float32)
4✔
2695
    if TT <: BlasFloat
4✔
2696
        return eigvecs(convert(AbstractMatrix{TT}, A))
4✔
2697
    else
2698
        throw(ArgumentError(lazy"eigvecs type $(typeof(A)) not supported. Please submit a pull request."))
×
2699
    end
2700
end
2701
det(A::UnitUpperTriangular{T}) where {T} = one(T)
7✔
2702
det(A::UnitLowerTriangular{T}) where {T} = one(T)
7✔
2703
logdet(A::UnitUpperTriangular{T}) where {T} = zero(T)
7✔
2704
logdet(A::UnitLowerTriangular{T}) where {T} = zero(T)
7✔
2705
logabsdet(A::UnitUpperTriangular{T}) where {T} = zero(T), one(T)
7✔
2706
logabsdet(A::UnitLowerTriangular{T}) where {T} = zero(T), one(T)
7✔
2707
det(A::UpperTriangular) = prod(diag(A.data))
308✔
2708
det(A::LowerTriangular) = prod(diag(A.data))
7✔
2709
function logabsdet(A::Union{UpperTriangular{T},LowerTriangular{T}}) where T
65✔
2710
    sgn = one(T)
104✔
2711
    abs_det = zero(real(T))
104✔
2712
    @inbounds for i in 1:size(A,1)
104✔
2713
        diag_i = A.data[i,i]
664✔
2714
        sgn *= sign(diag_i)
804✔
2715
        abs_det += log(abs(diag_i))
775✔
2716
    end
1,224✔
2717
    return abs_det, sgn
104✔
2718
end
2719

2720
eigen(A::AbstractTriangular) = Eigen(eigvals(A), eigvecs(A))
20✔
2721

2722
# Generic singular systems
2723
for func in (:svd, :svd!, :svdvals)
2724
    @eval begin
2725
        ($func)(A::AbstractTriangular; kwargs...) = ($func)(copyto!(similar(parent(A)), A); kwargs...)
112✔
2726
    end
2727
end
2728

2729
factorize(A::AbstractTriangular) = A
28✔
2730

2731
# disambiguation methods: /(Adjoint of AbsVec, <:AbstractTriangular)
2732
/(u::AdjointAbsVec, A::Union{LowerTriangular,UpperTriangular}) = adjoint(adjoint(A) \ u.parent)
×
2733
/(u::AdjointAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = adjoint(adjoint(A) \ u.parent)
×
2734
# disambiguation methods: /(Transpose of AbsVec, <:AbstractTriangular)
2735
/(u::TransposeAbsVec, A::Union{LowerTriangular,UpperTriangular}) = transpose(transpose(A) \ u.parent)
×
2736
/(u::TransposeAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = transpose(transpose(A) \ u.parent)
×
2737
# disambiguation methods: /(Transpose of AbsVec, Adj/Trans of <:AbstractTriangular)
2738
for (tritype, comptritype) in ((:LowerTriangular, :UpperTriangular),
2739
                               (:UnitLowerTriangular, :UnitUpperTriangular),
2740
                               (:UpperTriangular, :LowerTriangular),
2741
                               (:UnitUpperTriangular, :UnitLowerTriangular))
2742
    @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Adjoint}) = transpose($comptritype(conj(parent(parent(A)))) \ u.parent)
×
2743
    @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Transpose}) = transpose(transpose(A) \ u.parent)
×
2744
end
2745

2746
# Cube root of a 2x2 real-valued matrix with complex conjugate eigenvalues and equal diagonal values.
2747
# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots.
2748
#   SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989).
2749
#   https://doi.org/10.1137/s0895479801392697
2750
function _cbrt_2x2!(A::AbstractMatrix{T}) where {T<:Real}
3✔
2751
    @assert checksquare(A) == 2
3✔
2752
    @inbounds begin
3✔
2753
        (A[1,1] == A[2,2]) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have equal diagonal values."))
3✔
2754
        (A[1,2]*A[2,1] < 0) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have complex conjugate eigenvalues."))
3✔
2755
        μ = sqrt(-A[1,2]*A[2,1])
3✔
2756
        r = cbrt(hypot(A[1,1], μ))
3✔
2757
        θ = atan(μ, A[1,1])
3✔
2758
        s, c = sincos(θ/3)
3✔
2759
        α, β′ = r*c, r*s/µ
3✔
2760
        A[1,1] = α
3✔
2761
        A[2,2] = α
3✔
2762
        A[1,2] = β′*A[1,2]
3✔
2763
        A[2,1] = β′*A[2,1]
3✔
2764
    end
2765
    return A
3✔
2766
end
2767

2768
# Cube root of a quasi upper triangular matrix (output of Schur decomposition)
2769
# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots.
2770
#   SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989).
2771
#   https://doi.org/10.1137/s0895479801392697
2772
@views function _cbrt_quasi_triu!(A::AbstractMatrix{T}) where {T<:Real}
3✔
2773
    m, n = size(A)
3✔
2774
    (m == n) || throw(ArgumentError("_cbrt_quasi_triu!: Matrix A must be square."))
3✔
2775
    # Cube roots of 1x1 and 2x2 diagonal blocks
2776
    i = 1
3✔
2777
    sizes = ones(Int,n)
30✔
2778
    S = zeros(T,2,n)
60✔
2779
    while i < n
27✔
2780
        if !iszero(A[i+1,i])
24✔
2781
            _cbrt_2x2!(A[i:i+1,i:i+1])
3✔
2782
            mul!(S[1:2,i:i+1], A[i:i+1,i:i+1], A[i:i+1,i:i+1])
3✔
2783
            sizes[i] = 2
3✔
2784
            sizes[i+1] = 0
3✔
2785
            i += 2
3✔
2786
        else
2787
            A[i,i] = cbrt(A[i,i])
21✔
2788
            S[1,i] = A[i,i]*A[i,i]
21✔
2789
            i += 1
21✔
2790
        end
2791
    end
24✔
2792
    if i == n
3✔
2793
        A[n,n] = cbrt(A[n,n])
3✔
2794
        S[1,n] = A[n,n]*A[n,n]
3✔
2795
    end
2796
    # Algorithm 4.3 in Reference [1]
2797
    Δ = I(4)
12✔
2798
    M_L₀ = zeros(T,4,4)
48✔
2799
    M_L₁ = zeros(T,4,4)
48✔
2800
    M_Bᵢⱼ⁽⁰⁾ = zeros(T,2,2)
12✔
2801
    M_Bᵢⱼ⁽¹⁾ = zeros(T,2,2)
12✔
2802
    for k = 1:n-1
3✔
2803
        for i = 1:n-k
27✔
2804
            if sizes[i] == 0 || sizes[i+k] == 0 continue end
255✔
2805
            k₁, k₂ = i+1+(sizes[i+1]==0), i+k-1
111✔
2806
            i₁, i₂, j₁, j₂, s₁, s₂ = i, i+sizes[i]-1, i+k, i+k+sizes[i+k]-1, sizes[i], sizes[i+k]
111✔
2807
            L₀ = M_L₀[1:s₁*s₂,1:s₁*s₂]
111✔
2808
            L₁ = M_L₁[1:s₁*s₂,1:s₁*s₂]
111✔
2809
            Bᵢⱼ⁽⁰⁾ = M_Bᵢⱼ⁽⁰⁾[1:s₁, 1:s₂]
111✔
2810
            Bᵢⱼ⁽¹⁾ = M_Bᵢⱼ⁽¹⁾[1:s₁, 1:s₂]
111✔
2811
            # Compute Bᵢⱼ⁽⁰⁾ and Bᵢⱼ⁽¹⁾
2812
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,k₁:k₂], A[k₁:k₂,j₁:j₂])
111✔
2813
            # Retrieve Rᵢ,ᵢ₊ₖ as A[i+k,i]'
2814
            mul!(Bᵢⱼ⁽¹⁾, A[i₁:i₂,k₁:k₂], A[j₁:j₂,k₁:k₂]')
111✔
2815
            # Solve Uᵢ,ᵢ₊ₖ using Reference [1, (4.10)]
2816
            kron!(L₀, Δ[1:s₂,1:s₂], S[1:s₁,i₁:i₂])
111✔
2817
            L₀ .+= kron!(L₁, A[j₁:j₂,j₁:j₂]', A[i₁:i₂,i₁:i₂])
222✔
2818
            L₀ .+= kron!(L₁, S[1:s₂,j₁:j₂]', Δ[1:s₁,1:s₁])
222✔
2819
            mul!(A[i₁:i₂,j₁:j₂], A[i₁:i₂,i₁:i₂], Bᵢⱼ⁽⁰⁾, -1.0, 1.0)
111✔
2820
            A[i₁:i₂,j₁:j₂] .-= Bᵢⱼ⁽¹⁾
222✔
2821
            ldiv!(lu!(L₀), A[i₁:i₂,j₁:j₂][:])
111✔
2822
            # Compute and store Rᵢ,ᵢ₊ₖ' in A[i+k,i]
2823
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,i₁:i₂], A[i₁:i₂,j₁:j₂], 1.0, 1.0)
111✔
2824
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,j₁:j₂], A[j₁:j₂,j₁:j₂], 1.0, 1.0)
111✔
2825
            A[j₁:j₂,i₁:i₂] .= Bᵢⱼ⁽⁰⁾'
111✔
2826
        end
243✔
2827
    end
51✔
2828
    # Make quasi triangular
2829
    for j=1:n for i=j+1+(sizes[j]==2):n A[i,j] = 0 end end
30✔
2830
    return A
3✔
2831
end
2832

2833
# Cube roots of real-valued triangular matrices
2834
cbrt(A::UpperTriangular{T}) where {T<:Real} = UpperTriangular(_cbrt_quasi_triu!(Matrix{T}(A)))
1✔
2835
cbrt(A::LowerTriangular{T}) where {T<:Real} = LowerTriangular(_cbrt_quasi_triu!(Matrix{T}(A'))')
1✔
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

© 2026 Coveralls, Inc