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

JuliaLang / julia / #37713

12 Mar 2024 02:05AM UTC coverage: 81.372% (-5.3%) from 86.653%
#37713

push

local

web-flow
Linalg: remove unnecessary matprod_dest specializations (#53620)

These methods are not necessary, as the fallback methods for
`StructuredArrays` on line 577 and 578 do the same.

70249 of 86331 relevant lines covered (81.37%)

15860644.46 hits per line

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

80.07
/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}}
16✔
21
                require_one_based_indexing(data)
85,040✔
22
                checksquare(data)
85,041✔
23
                new{T,S}(data)
85,039✔
24
            end
25
        end
26
        $t(A::$t) = A
1,954✔
27
        $t{T}(A::$t{T}) where {T} = A
60✔
28
        $t(A::AbstractMatrix) = $t{eltype(A), typeof(A)}(A)
85,006✔
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)
148,252✔
36
        axes(A::$t) = axes(A.data)
389,631✔
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))
4,890✔
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)
31,339✔
44

45
        copy(A::$t) = $t(copy(A.data))
×
46

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

53
similar(A::UpperTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} =
180✔
54
    UpperTriangular(similar(parent(parent(A)), T))
55
similar(A::UnitUpperTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} =
76✔
56
    UnitUpperTriangular(similar(parent(parent(A)), T))
57
similar(A::LowerTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} =
533✔
58
    LowerTriangular(similar(parent(parent(A)), T))
59
similar(A::UnitLowerTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} =
72✔
60
    UnitLowerTriangular(similar(parent(parent(A)), T))
61

62

63
"""
64
    LowerTriangular(A::AbstractMatrix)
65

66
Construct a `LowerTriangular` view of the matrix `A`.
67

68
# Examples
69
```jldoctest
70
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
71
3×3 Matrix{Float64}:
72
 1.0  2.0  3.0
73
 4.0  5.0  6.0
74
 7.0  8.0  9.0
75

76
julia> LowerTriangular(A)
77
3×3 LowerTriangular{Float64, Matrix{Float64}}:
78
 1.0   ⋅    ⋅
79
 4.0  5.0   ⋅
80
 7.0  8.0  9.0
81
```
82
"""
83
LowerTriangular
84
"""
85
    UpperTriangular(A::AbstractMatrix)
86

87
Construct an `UpperTriangular` view of the matrix `A`.
88

89
# Examples
90
```jldoctest
91
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
92
3×3 Matrix{Float64}:
93
 1.0  2.0  3.0
94
 4.0  5.0  6.0
95
 7.0  8.0  9.0
2,991,474✔
96

97
julia> UpperTriangular(A)
98
3×3 UpperTriangular{Float64, Matrix{Float64}}:
99
 1.0  2.0  3.0
100
  ⋅   5.0  6.0
101
  ⋅    ⋅   9.0
102
```
103
"""
104
UpperTriangular
105
"""
106
    UnitLowerTriangular(A::AbstractMatrix)
107

108
Construct a `UnitLowerTriangular` view of the matrix `A`.
109
Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref)
476✔
110
of `A` on its diagonal.
476✔
111

476✔
112
# Examples
113
```jldoctest
114
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
115
3×3 Matrix{Float64}:
116
 1.0  2.0  3.0
117
 4.0  5.0  6.0
118
 7.0  8.0  9.0
119

120
julia> UnitLowerTriangular(A)
121
3×3 UnitLowerTriangular{Float64, Matrix{Float64}}:
122
 1.0   ⋅    ⋅
123
 4.0  1.0   ⋅
124
 7.0  8.0  1.0
125
```
126
"""
127
UnitLowerTriangular
128
"""
1,003✔
129
    UnitUpperTriangular(A::AbstractMatrix)
1,003✔
130

1,003✔
131
Construct an `UnitUpperTriangular` view of the matrix `A`.
132
Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref)
133
of `A` on its diagonal.
134

135
# Examples
136
```jldoctest
137
julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0]
1,512✔
138
3×3 Matrix{Float64}:
139
 1.0  2.0  3.0
140
 4.0  5.0  6.0
141
 7.0  8.0  9.0
142

143
julia> UnitUpperTriangular(A)
144
3×3 UnitUpperTriangular{Float64, Matrix{Float64}}:
145
 1.0  2.0  3.0
146
  ⋅   1.0  6.0
147
  ⋅    ⋅   1.0
148
```
3,464✔
149
"""
150
UnitUpperTriangular
151

152
const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}}
153
const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}}
154
const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}}
155

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

179
Array(A::AbstractTriangular) = Matrix(A)
4,113✔
180
parent(A::UpperOrLowerTriangular) = A.data
125,543✔
181

182
# For strided matrices, we may only loop over the filled triangle
183
copy(A::UpperOrLowerTriangular{<:Any, <:StridedMaybeAdjOrTransMat}) = copyto!(similar(A), A)
4,169✔
184

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

187
function Matrix{T}(A::LowerTriangular) where T
2,587✔
188
    B = Matrix{T}(undef, size(A, 1), size(A, 1))
3,914✔
189
    copyto!(B, A.data)
3,367✔
190
    tril!(B)
1,960✔
191
    B
1,960✔
192
end
193
function Matrix{T}(A::UnitLowerTriangular) where T
739✔
194
    B = Matrix{T}(undef, size(A, 1), size(A, 1))
1,478✔
195
    copyto!(B, A.data)
1,478✔
196
    tril!(B)
739✔
197
    for i = 1:size(B,1)
739✔
198
        B[i,i] = oneunit(T)
3,835✔
199
    end
8,807✔
200
    B
739✔
201
end
202
function Matrix{T}(A::UpperTriangular) where T
2,141✔
203
    B = Matrix{T}(undef, size(A, 1), size(A, 1))
4,336✔
204
    copyto!(B, A.data)
4,310✔
205
    triu!(B)
2,168✔
206
    B
2,168✔
207
end
208
function Matrix{T}(A::UnitUpperTriangular) where T
503✔
209
    B = Matrix{T}(undef, size(A, 1), size(A, 1))
1,000✔
210
    copyto!(B, A.data)
1,000✔
211
    triu!(B)
503✔
212
    for i = 1:size(B,1)
503✔
213
        B[i,i] = oneunit(T)
3,451✔
214
    end
6,405✔
215
    B
503✔
216
end
217

218
function full!(A::LowerTriangular)
7✔
219
    B = A.data
49✔
220
    tril!(B)
49✔
221
    B
49✔
222
end
223
function full!(A::UnitLowerTriangular)
19✔
224
    B = A.data
49✔
225
    tril!(B)
49✔
226
    for i = 1:size(A,1)
49✔
227
        B[i,i] = oneunit(eltype(B))
441✔
228
    end
833✔
229
    B
49✔
230
end
231
function full!(A::UpperTriangular)
7✔
232
    B = A.data
49✔
233
    triu!(B)
49✔
234
    B
49✔
235
end
236
function full!(A::UnitUpperTriangular)
19✔
237
    B = A.data
49✔
238
    triu!(B)
49✔
239
    for i = 1:size(A,1)
49✔
240
        B[i,i] = oneunit(eltype(B))
441✔
241
    end
833✔
242
    B
49✔
243
end
244

245
Base.isassigned(A::UnitLowerTriangular, i::Int, j::Int) =
376,466✔
246
    i > j ? isassigned(A.data, i, j) : true
247
Base.isassigned(A::LowerTriangular, i::Int, j::Int) =
123,924✔
248
    i >= j ? isassigned(A.data, i, j) : true
249
Base.isassigned(A::UnitUpperTriangular, i::Int, j::Int) =
376,924✔
250
    i < j ? isassigned(A.data, i, j) : true
251
Base.isassigned(A::UpperTriangular, i::Int, j::Int) =
271,717✔
252
    i <= j ? isassigned(A.data, i, j) : true
253

254
Base.isstored(A::UnitLowerTriangular, i::Int, j::Int) =
×
255
    i > j ? Base.isstored(A.data, i, j) : false
256
Base.isstored(A::LowerTriangular, i::Int, j::Int) =
×
257
    i >= j ? Base.isstored(A.data, i, j) : false
258
Base.isstored(A::UnitUpperTriangular, i::Int, j::Int) =
×
259
    i < j ? Base.isstored(A.data, i, j) : false
260
Base.isstored(A::UpperTriangular, i::Int, j::Int) =
×
261
    i <= j ? Base.isstored(A.data, i, j) : false
262

263
@propagate_inbounds getindex(A::UnitLowerTriangular{T}, i::Integer, j::Integer) where {T} =
1,458,595✔
264
    i > j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
265
@propagate_inbounds getindex(A::LowerTriangular, i::Integer, j::Integer) =
1,799,059✔
266
    i >= j ? A.data[i,j] : _zero(A.data,j,i)
267
@propagate_inbounds getindex(A::UnitUpperTriangular{T}, i::Integer, j::Integer) where {T} =
1,463,513✔
268
    i < j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
269
@propagate_inbounds getindex(A::UpperTriangular, i::Integer, j::Integer) =
2,422,890✔
270
    i <= j ? A.data[i,j] : _zero(A.data,j,i)
271

272
@propagate_inbounds function setindex!(A::UpperTriangular, x, i::Integer, j::Integer)
819✔
273
    if i > j
125,751✔
274
        iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " *
10,287✔
275
            "($i, $j) of an UpperTriangular matrix to a nonzero value ($x)"))
276
    else
277
        A.data[i,j] = x
115,719✔
278
    end
279
    return A
125,496✔
280
end
281

282
@propagate_inbounds function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer)
882✔
283
    if i > j
16,398✔
284
        iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " *
3,932✔
285
            "($i, $j) of a UnitUpperTriangular matrix to a nonzero value ($x)"))
286
    elseif i == j
12,718✔
287
        x == oneunit(x) || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " *
969✔
288
            "of a UnitUpperTriangular matrix to a non-unit value ($x)"))
289
    else
290
        A.data[i,j] = x
11,749✔
291
    end
292
    return A
16,083✔
293
end
294

295
@propagate_inbounds function setindex!(A::LowerTriangular, x, i::Integer, j::Integer)
819✔
296
    if i < j
66,828✔
297
        iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " *
21,706✔
298
            "($i, $j) of a LowerTriangular matrix to a nonzero value ($x)"))
299
    else
300
        A.data[i,j] = x
45,376✔
301
    end
302
    return A
66,574✔
303
end
304

305
@propagate_inbounds function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer)
882✔
306
    if i < j
15,354✔
307
        iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " *
3,788✔
308
            "($i, $j) of a UnitLowerTriangular matrix to a nonzero value ($x)"))
309
    elseif i == j
11,818✔
310
        x == oneunit(x) || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " *
933✔
311
            "of a UnitLowerTriangular matrix to a non-unit value ($x)"))
312
    else
313
        A.data[i,j] = x
10,885✔
314
    end
315
    return A
15,039✔
316
end
317

318
@inline function fill!(A::UpperTriangular, x)
319
    iszero(x) || throw(ArgumentError("cannot set indices in the lower triangular part " *
45✔
320
            "of an UpperTriangular matrix to a nonzero value ($x)"))
321
    for col in axes(A,2), row in firstindex(A,1):col
45✔
322
        @inbounds A.data[row, col] = x
547✔
323
    end
680✔
324
    A
45✔
325
end
326
@inline function fill!(A::LowerTriangular, x)
327
    iszero(x) || throw(ArgumentError("cannot set indices in the upper triangular part " *
109✔
328
            "of a LowerTriangular matrix to a nonzero value ($x)"))
329
    for col in axes(A,2), row in col:lastindex(A,1)
109✔
330
        @inbounds A.data[row, col] = x
931✔
331
    end
1,192✔
332
    A
109✔
333
end
334

335
## structured matrix methods ##
336
function Base.replace_in_print_matrix(A::Union{UpperTriangular,UnitUpperTriangular},
158,162✔
337
                                      i::Integer, j::Integer, s::AbstractString)
338
    return i <= j ? s : Base.replace_with_centered_mark(s)
158,162✔
339
end
340
function Base.replace_in_print_matrix(A::Union{LowerTriangular,UnitLowerTriangular},
156,884✔
341
                                      i::Integer, j::Integer, s::AbstractString)
342
    return i >= j ? s : Base.replace_with_centered_mark(s)
156,884✔
343
end
344

345
function istril(A::Union{LowerTriangular,UnitLowerTriangular}, k::Integer=0)
88✔
346
    k >= 0 && return true
102✔
347
    return _istril(A, k)
×
348
end
349
function istriu(A::Union{UpperTriangular,UnitUpperTriangular}, k::Integer=0)
691✔
350
    k <= 0 && return true
1,045✔
351
    return _istriu(A, k)
×
352
end
353
istril(A::Adjoint, k::Integer=0) = istriu(A.parent, -k)
6,766✔
354
istril(A::Transpose, k::Integer=0) = istriu(A.parent, -k)
1,487✔
355
istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k)
6,777✔
356
istriu(A::Transpose, k::Integer=0) = istril(A.parent, -k)
1,487✔
357

358
function tril!(A::UpperTriangular{T}, k::Integer=0) where {T}
35✔
359
    n = size(A,1)
35✔
360
    if k < 0
35✔
361
        fill!(A.data, zero(T))
1,134✔
362
        return A
14✔
363
    elseif k == 0
21✔
364
        for j in 1:n, i in 1:j-1
7✔
365
            A.data[i,j] = zero(T)
252✔
366
        end
308✔
367
        return A
7✔
368
    else
369
        return UpperTriangular(tril!(A.data,k))
14✔
370
    end
371
end
372
function triu!(A::UpperTriangular, k::Integer=0)
132✔
373
    n = size(A,1)
194✔
374
    if k > 0
132✔
375
        for j in 1:n, i in max(1,j-k+1):j
28✔
376
            A.data[i,j] = zero(eltype(A))
756✔
377
        end
980✔
378
    end
379
    return A
132✔
380
end
381

382
function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where {T}
35✔
383
    n = size(A,1)
35✔
384
    if k < 0
35✔
385
        fill!(A.data, zero(T))
1,134✔
386
        return UpperTriangular(A.data)
14✔
387
    elseif k == 0
21✔
388
        fill!(A.data, zero(T))
567✔
389
        for i in diagind(A)
14✔
390
            A.data[i] = oneunit(T)
63✔
391
        end
119✔
392
        return UpperTriangular(A.data)
7✔
393
    else
394
        for i in diagind(A)
28✔
395
            A.data[i] = oneunit(T)
126✔
396
        end
238✔
397
        return UpperTriangular(tril!(A.data,k))
14✔
398
    end
399
end
400

401
function triu!(A::UnitUpperTriangular, k::Integer=0)
35✔
402
    for i in diagind(A)
70✔
403
        A.data[i] = oneunit(eltype(A))
315✔
404
    end
595✔
405
    return triu!(UpperTriangular(A.data), k)
35✔
406
end
407

408
function triu!(A::LowerTriangular{T}, k::Integer=0) where {T}
35✔
409
    n = size(A,1)
35✔
410
    if k > 0
35✔
411
        fill!(A.data, zero(T))
1,134✔
412
        return A
14✔
413
    elseif k == 0
21✔
414
        for j in 1:n, i in j+1:n
14✔
415
            A.data[i,j] = zero(T)
252✔
416
        end
308✔
417
        return A
7✔
418
    else
419
        return LowerTriangular(triu!(A.data, k))
14✔
420
    end
421
end
422

423
function tril!(A::LowerTriangular, k::Integer=0)
70✔
424
    n = size(A,1)
70✔
425
    if k < 0
70✔
426
        for j in 1:n, i in j:min(j-k-1,n)
28✔
427
            A.data[i, j] = zero(eltype(A))
756✔
428
        end
980✔
429
    end
430
    A
70✔
431
end
432

433
function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T
35✔
434
    n = size(A,1)
35✔
435
    if k > 0
35✔
436
        fill!(A.data, zero(T))
1,134✔
437
        return LowerTriangular(A.data)
14✔
438
    elseif k == 0
21✔
439
        fill!(A.data, zero(T))
567✔
440
        for i in diagind(A)
14✔
441
            A.data[i] = oneunit(T)
63✔
442
        end
119✔
443
        return LowerTriangular(A.data)
7✔
444
    else
445
        for i in diagind(A)
28✔
446
            A.data[i] = oneunit(T)
126✔
447
        end
238✔
448
        return LowerTriangular(triu!(A.data, k))
14✔
449
    end
450
end
451

452
function tril!(A::UnitLowerTriangular, k::Integer=0)
35✔
453
    for i in diagind(A)
70✔
454
        A.data[i] = oneunit(eltype(A))
315✔
455
    end
595✔
456
    return tril!(LowerTriangular(A.data), k)
35✔
457
end
458

459
adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data))
4,144✔
460
adjoint(A::UpperTriangular) = LowerTriangular(adjoint(A.data))
4,953✔
461
adjoint(A::UnitLowerTriangular) = UnitUpperTriangular(adjoint(A.data))
4,108✔
462
adjoint(A::UnitUpperTriangular) = UnitLowerTriangular(adjoint(A.data))
4,008✔
463
transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data))
3,898✔
464
transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data))
4,450✔
465
transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data))
4,348✔
466
transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data))
3,991✔
467

468
transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L', false, true))
22✔
469
transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L', false, false))
22✔
470
transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U', false, true))
22✔
471
transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U', false, false))
22✔
472
adjoint!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true, true))
22✔
473
adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true, false))
22✔
474
adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true, true))
22✔
475
adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true, false))
22✔
476

477
diag(A::LowerTriangular) = diag(A.data)
68✔
478
diag(A::UnitLowerTriangular) = fill(oneunit(eltype(A)), size(A,1))
159✔
479
diag(A::UpperTriangular) = diag(A.data)
169✔
480
diag(A::UnitUpperTriangular) = fill(oneunit(eltype(A)), size(A,1))
321✔
481

482
# Unary operations
483
-(A::LowerTriangular) = LowerTriangular(-A.data)
1✔
484
-(A::UpperTriangular) = UpperTriangular(-A.data)
1✔
485
function -(A::UnitLowerTriangular)
11✔
486
    Adata = A.data
11✔
487
    Anew = similar(Adata) # must be mutable, even if Adata is not
22✔
488
    @. Anew = -Adata
22✔
489
    for i = 1:size(A, 1)
11✔
490
        Anew[i, i] = -A[i, i]
77✔
491
    end
143✔
492
    LowerTriangular(Anew)
11✔
493
end
494
function -(A::UnitUpperTriangular)
11✔
495
    Adata = A.data
11✔
496
    Anew = similar(Adata) # must be mutable, even if Adata is not
22✔
497
    @. Anew = -Adata
22✔
498
    for i = 1:size(A, 1)
11✔
499
        Anew[i, i] = -A[i, i]
77✔
500
    end
143✔
501
    UpperTriangular(Anew)
11✔
502
end
503

504
# use broadcasting if the parents are strided, where we loop only over the triangular part
505
for TM in (:LowerTriangular, :UpperTriangular)
506
    @eval -(A::$TM{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast(-, A)
205✔
507
end
508

509
tr(A::LowerTriangular) = tr(A.data)
7✔
510
tr(A::UnitLowerTriangular) = size(A, 1) * oneunit(eltype(A))
7✔
511
tr(A::UpperTriangular) = tr(A.data)
7✔
512
tr(A::UnitUpperTriangular) = size(A, 1) * oneunit(eltype(A))
7✔
513

514
# copy and scale
515
function copyto!(A::T, B::T) where {T<:Union{UpperTriangular,UnitUpperTriangular}}
4,398✔
516
    checkbounds(A, axes(B)...)
4,400✔
517
    n = size(B,1)
4,396✔
518
    for j = 1:n
4,396✔
519
        for i = 1:(isa(B, UnitUpperTriangular) ? j-1 : j)
22,653✔
520
            @inbounds A[i,j] = B[i,j]
90,446✔
521
        end
158,481✔
522
    end
40,910✔
523
    return A
4,396✔
524
end
525
function copyto!(A::T, B::T) where {T<:Union{LowerTriangular,UnitLowerTriangular}}
901✔
526
    checkbounds(A, axes(B)...)
903✔
527
    n = size(B,1)
899✔
528
    for j = 1:n
899✔
529
        for i = (isa(B, UnitLowerTriangular) ? j+1 : j):n
6,373✔
530
            @inbounds A[i,j] = B[i,j]
25,776✔
531
        end
45,623✔
532
    end
11,403✔
533
    return A
899✔
534
end
535

536
@inline _rscale_add!(A::AbstractTriangular, B::AbstractTriangular, C::Number, alpha::Number, beta::Number) =
620✔
537
    _triscale!(A, B, C, MulAddMul(alpha, beta))
538
@inline _lscale_add!(A::AbstractTriangular, B::Number, C::AbstractTriangular, alpha::Number, beta::Number) =
26✔
539
    _triscale!(A, B, C, MulAddMul(alpha, beta))
540

541
function checksize1(A, B)
1,047✔
542
    szA, szB = size(A), size(B)
1,047✔
543
    szA == szB || throw(DimensionMismatch("size of A, $szA, does not match size of B, $szB"))
1,055✔
544
    checksquare(B)
1,039✔
545
end
546

547
function _triscale!(A::UpperTriangular, B::UpperTriangular, c::Number, _add)
595✔
548
    n = checksize1(A, B)
800✔
549
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
799✔
550
    for j = 1:n
799✔
551
        for i = 1:j
3,095✔
552
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
9,213✔
553
        end
15,331✔
554
    end
5,391✔
555
    return A
799✔
556
end
557
function _triscale!(A::UpperTriangular, c::Number, B::UpperTriangular, _add)
1✔
558
    n = checksize1(A, B)
40✔
559
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
39✔
560
    for j = 1:n
39✔
561
        for i = 1:j
383✔
562
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
2,075✔
563
        end
3,767✔
564
    end
727✔
565
    return A
39✔
566
end
567
function _triscale!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular, c::Number, _add)
12✔
568
    n = checksize1(A, B)
14✔
569
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
13✔
570
    for j = 1:n
9✔
571
        @inbounds _modify!(_add, c, A, (j,j))
69✔
572
        for i = 1:(j - 1)
69✔
573
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
258✔
574
        end
456✔
575
    end
129✔
576
    return A
9✔
577
end
578
function _triscale!(A::UpperOrUnitUpperTriangular, c::Number, B::UnitUpperTriangular, _add)
12✔
579
    n = checksize1(A, B)
12✔
580
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
11✔
581
    for j = 1:n
7✔
582
        @inbounds _modify!(_add, c, A, (j,j))
63✔
583
        for i = 1:(j - 1)
63✔
584
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
252✔
585
        end
448✔
586
    end
119✔
587
    return A
7✔
588
end
589
function _triscale!(A::LowerTriangular, B::LowerTriangular, c::Number, _add)
1✔
590
    n = checksize1(A, B)
115✔
591
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
114✔
592
    for j = 1:n
114✔
593
        for i = j:n
672✔
594
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
2,909✔
595
        end
5,146✔
596
    end
1,230✔
597
    return A
114✔
598
end
599
function _triscale!(A::LowerTriangular, c::Number, B::LowerTriangular, _add)
1✔
600
    n = checksize1(A, B)
40✔
601
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
39✔
602
    for j = 1:n
39✔
603
        for i = j:n
383✔
604
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
2,075✔
605
        end
3,767✔
606
    end
727✔
607
    return A
39✔
608
end
609
function _triscale!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular, c::Number, _add)
12✔
610
    n = checksize1(A, B)
14✔
611
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
13✔
612
    for j = 1:n
9✔
613
        @inbounds _modify!(_add, c, A, (j,j))
69✔
614
        for i = (j + 1):n
78✔
615
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
258✔
616
        end
456✔
617
    end
129✔
618
    return A
9✔
619
end
620
function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriangular, _add)
12✔
621
    n = checksize1(A, B)
12✔
622
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
11✔
623
    for j = 1:n
7✔
624
        @inbounds _modify!(_add, c, A, (j,j))
63✔
625
        for i = (j + 1):n
70✔
626
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
252✔
627
        end
448✔
628
    end
119✔
629
    return A
7✔
630
end
631

632
rmul!(A::UpperOrLowerTriangular, c::Number) = @inline _triscale!(A, A, c, MulAddMul())
323✔
633
lmul!(c::Number, A::UpperOrLowerTriangular) = @inline _triscale!(A, c, A, MulAddMul())
78✔
634

635
function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector)
42✔
636
    require_one_based_indexing(x, y)
42✔
637
    m = size(A, 1)
42✔
638
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
639
    if iszero(m)
42✔
640
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
641
    end
642
    x₁ = x[1]
42✔
643
    r = dot(x₁, A[1,1], y[1])
42✔
644
    @inbounds for j in 2:m
42✔
645
        yj = y[j]
336✔
646
        if !iszero(yj)
336✔
647
            temp = adjoint(A[1,j]) * x₁
336✔
648
            @simd for i in 2:j
336✔
649
                temp += adjoint(A[i,j]) * x[i]
650
            end
651
            r += dot(temp, yj)
336✔
652
        end
653
    end
630✔
654
    return r
42✔
655
end
656
function dot(x::AbstractVector, A::UnitUpperTriangular, y::AbstractVector)
42✔
657
    require_one_based_indexing(x, y)
42✔
658
    m = size(A, 1)
42✔
659
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
660
    if iszero(m)
42✔
661
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
662
    end
663
    x₁ = first(x)
42✔
664
    r = dot(x₁, y[1])
42✔
665
    @inbounds for j in 2:m
42✔
666
        yj = y[j]
336✔
667
        if !iszero(yj)
336✔
668
            temp = adjoint(A[1,j]) * x₁
336✔
669
            @simd for i in 2:j-1
336✔
670
                temp += adjoint(A[i,j]) * x[i]
671
            end
672
            r += dot(temp, yj)
420✔
673
            r += dot(x[j], yj)
336✔
674
        end
675
    end
630✔
676
    return r
42✔
677
end
678
function dot(x::AbstractVector, A::LowerTriangular, y::AbstractVector)
42✔
679
    require_one_based_indexing(x, y)
42✔
680
    m = size(A, 1)
42✔
681
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
682
    if iszero(m)
42✔
683
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
684
    end
685
    r = zero(typeof(dot(first(x), first(A), first(y))))
42✔
686
    @inbounds for j in 1:m
42✔
687
        yj = y[j]
378✔
688
        if !iszero(yj)
378✔
689
            temp = adjoint(A[j,j]) * x[j]
378✔
690
            @simd for i in j+1:m
420✔
691
                temp += adjoint(A[i,j]) * x[i]
692
            end
693
            r += dot(temp, yj)
378✔
694
        end
695
    end
714✔
696
    return r
42✔
697
end
698
function dot(x::AbstractVector, A::UnitLowerTriangular, y::AbstractVector)
42✔
699
    require_one_based_indexing(x, y)
42✔
700
    m = size(A, 1)
42✔
701
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
702
    if iszero(m)
42✔
703
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
704
    end
705
    r = zero(typeof(dot(first(x), first(y))))
42✔
706
    @inbounds for j in 1:m
42✔
707
        yj = y[j]
378✔
708
        if !iszero(yj)
378✔
709
            temp = x[j]
378✔
710
            @simd for i in j+1:m
420✔
711
                temp += adjoint(A[i,j]) * x[i]
712
            end
713
            r += dot(temp, yj)
491✔
714
        end
715
    end
714✔
716
    return r
42✔
717
end
718

719
fillstored!(A::LowerTriangular, x)     = (fillband!(A.data, x, 1-size(A,1), 0); A)
1✔
720
fillstored!(A::UnitLowerTriangular, x) = (fillband!(A.data, x, 1-size(A,1), -1); A)
1✔
721
fillstored!(A::UpperTriangular, x)     = (fillband!(A.data, x, 0, size(A,2)-1); A)
×
722
fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); A)
×
723

724
# Binary operations
725
+(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data + B.data)
17✔
726
+(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data + B.data)
7✔
727
+(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data + triu(B.data, 1) + I)
2✔
728
+(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data + tril(B.data, -1) + I)
1✔
729
+(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) + B.data + I)
2✔
730
+(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) + B.data + I)
1✔
731
+(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I)
1✔
732
+(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I)
1✔
733
+(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) + copyto!(similar(parent(B)), B)
406✔
734

735
-(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data - B.data)
19✔
736
-(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data - B.data)
7✔
737
-(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data - triu(B.data, 1) - I)
2✔
738
-(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data - tril(B.data, -1) - I)
1✔
739
-(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) - B.data + I)
2✔
740
-(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) - B.data + I)
1✔
741
-(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) - triu(B.data, 1))
1✔
742
-(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) - tril(B.data, -1))
1✔
743
-(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B)
396✔
744

745
# use broadcasting if the parents are strided, where we loop only over the triangular part
746
for op in (:+, :-)
747
    for TM1 in (:LowerTriangular, :UnitLowerTriangular), TM2 in (:LowerTriangular, :UnitLowerTriangular)
748
        @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B)
798✔
749
    end
750
    for TM1 in (:UpperTriangular, :UnitUpperTriangular), TM2 in (:UpperTriangular, :UnitUpperTriangular)
751
        @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B)
1,174✔
752
    end
753
end
754

755
######################
756
# BlasFloat routines #
757
######################
758

759
# which triangle to use of the underlying data
760
uplo_char(::UpperOrUnitUpperTriangular) = 'U'
×
761
uplo_char(::LowerOrUnitLowerTriangular) = 'L'
×
762
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:AdjOrTrans}) = 'L'
×
763
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:AdjOrTrans}) = 'U'
×
764
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'U'
×
765
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'L'
×
766
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'U'
×
767
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'L'
×
768

769
isunit_char(::UpperTriangular) = 'N'
×
770
isunit_char(::UnitUpperTriangular) = 'U'
×
771
isunit_char(::LowerTriangular) = 'N'
×
772
isunit_char(::UnitLowerTriangular) = 'U'
×
773

774
lmul!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B)
168✔
775

776
# generic fallback for AbstractTriangular matrices outside of the four subtypes provided here
777
_trimul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) =
×
778
    lmul!(A, copyto!(C, B))
779
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) =
×
780
    lmul!(A, copyto!(C, B))
781
_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) =
×
782
    rmul!(copyto!(C, A), B)
783
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractTriangular) =
×
784
    lmul!(A, copyto!(C, B))
785
# redirect for UpperOrLowerTriangular
786
_trimul!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVector) =
3,494✔
787
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
788
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractMatrix) =
5,569✔
789
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
790
_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) =
6,383✔
791
    generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
792
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) =
11,789✔
793
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
794
# disambiguation with AbstractTriangular
795
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) =
×
796
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
797
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) =
×
798
    generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
799

800
lmul!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _trimul!(B, A, B)
792✔
801
rmul!(A::AbstractMatrix, B::AbstractTriangular)   = @inline _trimul!(A, A, B)
552✔
802

803

804
for TC in (:AbstractVector, :AbstractMatrix)
805
    @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number)
806
        if isone(alpha) && iszero(beta)
2,805✔
807
            return _trimul!(C, A, B)
2,608✔
808
        else
809
            return generic_matvecmul!(C, 'N', A, B, MulAddMul(alpha, beta))
273✔
810
        end
811
    end
812
end
813
for (TA, TB) in ((:AbstractTriangular, :AbstractMatrix),
814
                    (:AbstractMatrix, :AbstractTriangular),
815
                    (:AbstractTriangular, :AbstractTriangular)
816
                )
817
    @eval @inline function _mul!(C::AbstractMatrix, A::$TA, B::$TB, alpha::Number, beta::Number)
818
        if isone(alpha) && iszero(beta)
21,429✔
819
            return _trimul!(C, A, B)
21,208✔
820
        else
821
            return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta))
221✔
822
        end
823
    end
824
end
825

826
ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = _ldiv!(C, A, B)
15,749✔
827
# generic fallback for AbstractTriangular, directs to 2-arg [l/r]div!
828
_ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) =
×
829
    ldiv!(A, copyto!(C, B))
830
_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) =
×
831
    rdiv!(copyto!(C, A), B)
832
# redirect for UpperOrLowerTriangular to generic_*div!
833
_ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) =
27,663✔
834
    generic_trimatdiv!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
835
_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) =
7,316✔
836
    generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
837

838
ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _ldiv!(B, A, B)
11,914✔
839
rdiv!(A::AbstractMatrix, B::AbstractTriangular)   = @inline _rdiv!(A, A, B)
1,914✔
840

841
# preserve triangular structure in in-place multiplication/division
842
for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular),
843
                        (:UpperTriangular, :UpperTriangular, :UnitUpperTriangular),
844
                        (:UpperTriangular, :UnitUpperTriangular, :UpperTriangular),
845
                        (:UnitUpperTriangular, :UnitUpperTriangular, :UnitUpperTriangular),
846
                        (:LowerTriangular, :LowerTriangular, :LowerTriangular),
847
                        (:LowerTriangular, :LowerTriangular, :UnitLowerTriangular),
848
                        (:LowerTriangular, :UnitLowerTriangular, :LowerTriangular),
849
                        (:UnitLowerTriangular, :UnitLowerTriangular, :UnitLowerTriangular))
850
    @eval begin
851
        function _trimul!(C::$cty, A::$aty, B::$bty)
852
            _trimul!(parent(C), A, B)
1,377✔
853
            return C
1,377✔
854
        end
855
        function _ldiv!(C::$cty, A::$aty, B::$bty)
856
            _ldiv!(parent(C), A, B)
666✔
857
            return C
666✔
858
        end
859
        function _rdiv!(C::$cty, A::$aty, B::$bty)
860
            _rdiv!(parent(C), A, B)
1,707✔
861
            return C
1,707✔
862
        end
863
    end
864
end
865

866
for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'),
867
                            (:UnitLowerTriangular, 'L', 'U'),
868
                            (:UpperTriangular, 'U', 'N'),
869
                            (:UnitUpperTriangular, 'U', 'U'))
870
    @eval begin
871
        # Matrix inverse
872
        inv!(A::$t{T,S}) where {T<:BlasFloat,S<:StridedMatrix} =
16✔
873
            $t{T,S}(LAPACK.trtri!($uploc, $isunitc, A.data))
874

875
        function inv(A::$t{T}) where {T}
533✔
876
            S = typeof(inv(oneunit(T)))
533✔
877
            if S <: BlasFloat || S === T # i.e. A is unitless
533✔
878
                $t(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A))))
464✔
879
            else
880
                J = (one(T)*I)(size(A, 1))
650✔
881
                $t(ldiv!(similar(A, S, size(A)), A, J))
69✔
882
            end
883
        end
884

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

889
        # Condition numbers
890
        function cond(A::$t{<:BlasFloat,<:StridedMatrix}, p::Real=2)
144✔
891
            checksquare(A)
144✔
892
            if p == 1
144✔
893
                return inv(LAPACK.trcon!('O', $uploc, $isunitc, A.data))
64✔
894
            elseif p == Inf
80✔
895
                return inv(LAPACK.trcon!('I', $uploc, $isunitc, A.data))
64✔
896
            else # use fallback
897
                return cond(copyto!(similar(parent(A)), A), p)
16✔
898
            end
899
        end
900
    end
901
end
902

903
# multiplication
904
generic_trimatmul!(c::StridedVector{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, b::AbstractVector{T}) where {T<:BlasFloat} =
976✔
905
    BLAS.trmv!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, c === b ? c : copyto!(c, b))
906
generic_trimatmul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} =
6,120✔
907
    BLAS.trmm!('L', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), A, C === B ? C : copyto!(C, B))
908
generic_mattrimul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} =
1,674✔
909
    BLAS.trmm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A))
910
# division
911
generic_trimatdiv!(C::StridedVecOrMat{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} =
5,543✔
912
    LAPACK.trtrs!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, C === B ? C : copyto!(C, B))
913
generic_mattridiv!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} =
2,534✔
914
    BLAS.trsm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A))
915

916
errorbounds(A::AbstractTriangular{T}, X::AbstractVecOrMat{T}, B::AbstractVecOrMat{T}) where {T<:Union{BigFloat,Complex{BigFloat}}} =
×
917
    error("not implemented yet! Please submit a pull request.")
918
function errorbounds(A::AbstractTriangular{TA}, X::AbstractVecOrMat{TX}, B::AbstractVecOrMat{TB}) where {TA<:Number,TX<:Number,TB<:Number}
128✔
919
    TAXB = promote_type(TA, TB, TX, Float32)
128✔
920
    errorbounds(convert(AbstractMatrix{TAXB}, A), convert(AbstractArray{TAXB}, X), convert(AbstractArray{TAXB}, B))
128✔
921
end
922

923
# Eigensystems
924
## Notice that trecv works for quasi-triangular matrices and therefore the lower sub diagonal must be zeroed before calling the subroutine
925
function eigvecs(A::UpperTriangular{<:BlasFloat,<:StridedMatrix})
926
    LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
5✔
927
end
928
function eigvecs(A::UnitUpperTriangular{<:BlasFloat,<:StridedMatrix})
929
    for i = 1:size(A, 1)
5✔
930
        A.data[i,i] = 1
45✔
931
    end
85✔
932
    LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
5✔
933
end
934
function eigvecs(A::LowerTriangular{<:BlasFloat,<:StridedMatrix})
5✔
935
    LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)'))
5✔
936
end
937
function eigvecs(A::UnitLowerTriangular{<:BlasFloat,<:StridedMatrix})
5✔
938
    for i = 1:size(A, 1)
5✔
939
        A.data[i,i] = 1
45✔
940
    end
85✔
941
    LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)'))
5✔
942
end
943

944
####################
945
# Generic routines #
946
####################
947

948
for (t, unitt) in ((UpperTriangular, UnitUpperTriangular),
949
                   (LowerTriangular, UnitLowerTriangular))
950
    tstrided = t{<:Any, <:StridedMaybeAdjOrTransMat}
951
    @eval begin
952
        (*)(A::$t, x::Number) = $t(A.data*x)
4✔
953
        (*)(A::$tstrided, x::Number) = A .* x
72✔
954

955
        function (*)(A::$unitt, x::Number)
24✔
956
            B = $t(A.data)*x
24✔
957
            for i = 1:size(A, 1)
24✔
958
                B.data[i,i] = x
188✔
959
            end
352✔
960
            return B
24✔
961
        end
962

963
        (*)(x::Number, A::$t) = $t(x*A.data)
×
964
        (*)(x::Number, A::$tstrided) = x .* A
495✔
965

966
        function (*)(x::Number, A::$unitt)
40✔
967
            B = x*$t(A.data)
40✔
968
            for i = 1:size(A, 1)
40✔
969
                B.data[i,i] = x
332✔
970
            end
624✔
971
            return B
40✔
972
        end
973

974
        (/)(A::$t, x::Number) = $t(A.data/x)
2✔
975
        (/)(A::$tstrided, x::Number) = A ./ x
112✔
976

977
        function (/)(A::$unitt, x::Number)
18✔
978
            B = $t(A.data)/x
18✔
979
            invx = inv(x)
18✔
980
            for i = 1:size(A, 1)
18✔
981
                B.data[i,i] = invx
134✔
982
            end
250✔
983
            return B
18✔
984
        end
985

986
        (\)(x::Number, A::$t) = $t(x\A.data)
×
987
        (\)(x::Number, A::$tstrided) = x .\ A
36✔
988

989
        function (\)(x::Number, A::$unitt)
18✔
990
            B = x\$t(A.data)
18✔
991
            invx = inv(x)
18✔
992
            for i = 1:size(A, 1)
18✔
993
                B.data[i,i] = invx
134✔
994
            end
250✔
995
            return B
18✔
996
        end
997
    end
998
end
999

1000
## Generic triangular multiplication
1001
function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat)
13,534✔
1002
    require_one_based_indexing(C, A, B)
30,221✔
1003
    m, n = size(B, 1), size(B, 2)
87,165✔
1004
    N = size(A, 1)
13,534✔
1005
    if m != N
162,868✔
1006
        throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
149,706✔
1007
    end
1008
    mc, nc = size(C, 1), size(C, 2)
158,296✔
1009
    if mc != N || nc != n
612,993✔
1010
        throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)"))
1,042,791✔
1011
    end
1012
    oA = oneunit(eltype(A))
158,296✔
1013
    unit = isunitc == 'U'
288,843✔
1014
    @inbounds if uploc == 'U'
158,296✔
1015
        if tfun === identity
167,188✔
1016
            for j in 1:n
21,952✔
1017
                for i in 1:m
20,629✔
1018
                    Cij = (unit ? oA : A[i,i]) * B[i,j]
180,738✔
1019
                    for k in i + 1:m
408,168✔
1020
                        Cij += A[i,k] * B[k,j]
838,897✔
1021
                    end
1,949,422✔
1022
                    C[i,j] = Cij
1,638,080✔
1023
                end
1,129,321✔
1024
            end
429,856✔
1025
        else # tfun in (transpose, adjoint)
1026
            for j in 1:n
189,799✔
1027
                for i in m:-1:1
57,679✔
1028
                    Cij = (unit ? oA : tfun(A[i,i])) * B[i,j]
210,136✔
1029
                    for k in 1:i - 1
208,786✔
1030
                        Cij += tfun(A[k,i]) * B[k,j]
835,668✔
1031
                    end
1,480,459✔
1032
                    C[i,j] = Cij
208,786✔
1033
                end
394,263✔
1034
            end
43,640✔
1035
        end
1036
    else # uploc == 'L'
1037
        if tfun === identity
5,435✔
1038
            for j in 1:n
2,360✔
1039
                for i in m:-1:1
33,373✔
1040
                    Cij = (unit ? oA : A[i,i]) * B[i,j]
×
1041
                    for k in 1:i - 1
×
1042
                        Cij += A[i,k] * B[k,j]
×
1043
                    end
×
1044
                    C[i,j] = Cij
×
1045
                end
×
1046
            end
×
1047
        else # tfun in (transpose, adjoint)
1048
            for j in 1:n
×
1049
                for i in 1:m
×
1050
                    Cij = (unit ? oA : tfun(A[i,i])) * B[i,j]
×
1051
                    for k in i + 1:m
×
1052
                        Cij += tfun(A[k,i]) * B[k,j]
×
1053
                    end
×
1054
                    C[i,j] = Cij
×
1055
                end
×
1056
            end
×
1057
        end
1058
    end
1059
    return C
×
1060
end
1061
# conjugate cases
1062
function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat)
22✔
1063
    A = parent(xA)
22✔
1064
    require_one_based_indexing(C, A, B)
22✔
1065
    m, n = size(B, 1), size(B, 2)
22✔
1066
    N = size(A, 1)
22✔
1067
    if m != N
22✔
1068
        throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
×
1069
    end
1070
    mc, nc = size(C, 1), size(C, 2)
22✔
1071
    if mc != N || nc != n
44✔
1072
        throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)"))
×
1073
    end
1074
    oA = oneunit(eltype(A))
22✔
1075
    unit = isunitc == 'U'
22✔
1076
    @inbounds if uploc == 'U'
22✔
1077
        for j in 1:n
11✔
1078
            for i in 1:m
11✔
1079
                Cij = (unit ? oA : conj(A[i,i])) * B[i,j]
4,040✔
1080
                for k in i + 1:m
4,051✔
1081
                    Cij += conj(A[i,k]) * B[k,j]
1,998,147✔
1082
                end
3,992,265✔
1083
                C[i,j] = Cij
4,040✔
1084
            end
8,069✔
1085
        end
11✔
1086
    else # uploc == 'L'
1087
        for j in 1:n
11✔
1088
            for i in m:-1:1
20✔
1089
                Cij = (unit ? oA : conj(A[i,i])) * B[i,j]
4,040✔
1090
                for k in 1:i - 1
4,040✔
1091
                    Cij += conj(A[i,k]) * B[k,j]
1,998,147✔
1092
                end
3,992,265✔
1093
                C[i,j] = Cij
4,040✔
1094
            end
8,069✔
1095
        end
11✔
1096
    end
1097
    return C
22✔
1098
end
1099

1100
function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix)
4,709✔
1101
    require_one_based_indexing(C, A, B)
8,301✔
1102
    m, n = size(A, 1), size(A, 2)
38,577✔
1103
    N = size(B, 1)
4,709✔
1104
    if n != N
36,477✔
1105
        throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N"))
37,832✔
1106
    end
1107
    mc, nc = size(C, 1), size(C, 2)
34,005✔
1108
    if mc != m || nc != N
137,146✔
1109
        throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)"))
228,768✔
1110
    end
1111
    oB = oneunit(eltype(B))
34,005✔
1112
    unit = isunitc == 'U'
34,005✔
1113
    @inbounds if uploc == 'U'
62,181✔
1114
        if tfun === identity
32,835✔
1115
            for i in 1:m
4,766✔
1116
                for j in n:-1:1
14,112✔
1117
                    Cij = A[i,j] * (unit ? oB : B[j,j])
59,618✔
1118
                    for k in 1:j - 1
85,942✔
1119
                        Cij += A[i,k] * B[k,j]
186,860✔
1120
                    end
458,270✔
1121
                    C[i,j] = Cij
244,744✔
1122
                end
438,450✔
1123
            end
107,834✔
1124
        else # tfun in (transpose, adjoint)
1125
            for i in 1:m
54,273✔
1126
                for j in 1:n
7,886✔
1127
                    Cij = A[i,j] * (unit ? oB : tfun(B[j,j]))
71,085✔
1128
                    for k in j + 1:n
56,592✔
1129
                        Cij += A[i,k] * tfun(B[j,k])
207,045✔
1130
                    end
368,796✔
1131
                    C[i,j] = Cij
50,943✔
1132
                end
96,237✔
1133
            end
10,659✔
1134
        end
1135
    else # uploc == 'L'
1136
        if tfun === identity
1,170✔
1137
            for i in 1:m
424✔
1138
                for j in 1:n
3,592✔
1139
                    Cij = A[i,j] * (unit ? oB : B[j,j])
×
1140
                    for k in j + 1:n
×
1141
                        Cij += A[i,k] * B[k,j]
×
1142
                    end
×
1143
                    C[i,j] = Cij
×
1144
                end
×
1145
            end
×
1146
        else # tfun in (transpose, adjoint)
1147
            for i in 1:m
×
1148
                for j in n:-1:1
×
1149
                    Cij = A[i,j] * (unit ? oB : tfun(B[j,j]))
×
1150
                    for k in 1:j - 1
×
1151
                        Cij += A[i,k] * tfun(B[j,k])
×
1152
                    end
×
1153
                    C[i,j] = Cij
×
1154
                end
×
1155
            end
×
1156
        end
1157
    end
1158
    return C
×
1159
end
1160
# conjugate cases
1161
function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans)
×
1162
    B = parent(xB)
×
1163
    require_one_based_indexing(C, A, B)
×
1164
    m, n = size(A, 1), size(A, 2)
×
1165
    N = size(B, 1)
×
1166
    if n != N
×
1167
        throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N"))
×
1168
    end
1169
    mc, nc = size(C, 1), size(C, 2)
×
1170
    if mc != m || nc != N
×
1171
        throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)"))
×
1172
    end
1173
    oB = oneunit(eltype(B))
×
1174
    unit = isunitc == 'U'
×
1175
    @inbounds if uploc == 'U'
×
1176
        for i in 1:m
×
1177
            for j in n:-1:1
×
1178
                Cij = A[i,j] * (unit ? oB : conj(B[j,j]))
×
1179
                for k in 1:j - 1
×
1180
                    Cij += A[i,k] * conj(B[k,j])
×
1181
                end
×
1182
                C[i,j] = Cij
×
1183
            end
×
1184
        end
×
1185
    else # uploc == 'L'
1186
        for i in 1:m
×
1187
            for j in 1:n
×
1188
                Cij = A[i,j] * (unit ? oB : conj(B[j,j]))
×
1189
                for k in j + 1:n
×
1190
                    Cij += A[i,k] * conj(B[k,j])
×
1191
                end
×
1192
                C[i,j] = Cij
×
1193
            end
×
1194
        end
×
1195
    end
1196
    return C
×
1197
end
1198

1199
#Generic solver using naive substitution
1200

1201
@inline _ustrip(a) = oneunit(a) \ a
284,264✔
1202
@inline _ustrip(a::Union{AbstractFloat,Integer,Complex,Rational}) = a
2,287,550✔
1203

1204
# manually hoisting b[j] significantly improves performance as of Dec 2015
1205
# manually eliding bounds checking significantly improves performance as of Dec 2015
1206
# replacing repeated references to A.data[j,j] with [Ajj = A.data[j,j] and references to Ajj]
1207
# does not significantly impact performance as of Dec 2015
1208
# in the transpose and conjugate transpose naive substitution variants,
1209
# accumulating in z rather than b[j,k] significantly improves performance as of Dec 2015
1210
function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat)
16,140✔
1211
    require_one_based_indexing(C, A, B)
155,039✔
1212
    mA, nA = size(A)
573,270✔
1213
    m, n = size(B, 1), size(B,2)
992,009✔
1214
    if nA != m
613,614✔
1215
        throw(DimensionMismatch("second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal"))
324,544✔
1216
    end
1217
    if size(C) != size(B)
240,217✔
1218
        throw(DimensionMismatch("size of output, $(size(C)), does not match size of right hand side, $(size(B))"))
160,886✔
1219
    end
1220
    oA = oneunit(eltype(A))
158,162✔
1221
    @inbounds if uploc == 'U'
154,722✔
1222
        if isunitc == 'N'
146,455✔
1223
            if tfun === identity
198,984✔
1224
                for k in 1:n
317,632✔
1225
                    amm = A[m,m]
374,991✔
1226
                    iszero(amm) && throw(SingularException(m))
380,470✔
1227
                    Cm = C[m,k] = amm \ B[m,k]
766,430✔
1228
                    # fill C-column
1229
                    for i in m-1:-1:1
554,644✔
1230
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm
357,002✔
1231
                    end
831,071✔
1232
                    for j in m-1:-1:1
699,196✔
1233
                        ajj = A[j,j]
224,429✔
1234
                        iszero(ajj) && throw(SingularException(j))
480,935✔
1235
                        Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
602,777✔
1236
                        for i in j-1:-1:1
420,608✔
1237
                            C[i,k] -= _ustrip(A[i,j]) * Cj
841,086✔
1238
                        end
1,429,409✔
1239
                    end
816,446✔
1240
                end
541,448✔
1241
            else # tfun in (adjoint, transpose)
1242
                for k in 1:n
1,027,890✔
1243
                    for j in 1:m
262,452✔
1244
                        ajj = A[j,j]
164,526✔
1245
                        iszero(ajj) && throw(SingularException(j))
156,698✔
1246
                        Bj = B[j,k]
129,898✔
1247
                        for i in 1:j-1
145,772✔
1248
                            Bj -= tfun(A[i,j]) * C[i,k]
×
1249
                        end
×
1250
                        C[j,k] = tfun(ajj) \ Bj
×
1251
                    end
×
1252
                end
×
1253
            end
1254
        else # isunitc == 'U'
1255
            if tfun === identity
×
1256
                for k in 1:n
×
1257
                    Cm = C[m,k] = oA \ B[m,k]
×
1258
                    # fill C-column
1259
                    for i in m-1:-1:1
×
1260
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm
×
1261
                    end
×
1262
                    for j in m-1:-1:1
×
1263
                        Cj = C[j,k]
×
1264
                        for i in 1:j-1
×
1265
                            C[i,k] -= _ustrip(A[i,j]) * Cj
×
1266
                        end
×
1267
                    end
×
1268
                end
×
1269
            else # tfun in (adjoint, transpose)
1270
                for k in 1:n
×
1271
                    for j in 1:m
×
1272
                        Bj = B[j,k]
×
1273
                        for i in 1:j-1
×
1274
                            Bj -= tfun(A[i,j]) * C[i,k]
×
1275
                        end
×
1276
                        C[j,k] = oA \ Bj
×
1277
                    end
×
1278
                end
×
1279
            end
1280
        end
1281
    else # uploc == 'L'
1282
        if isunitc == 'N'
×
1283
            if tfun === identity
×
1284
                for k in 1:n
×
1285
                    a11 = A[1,1]
×
1286
                    iszero(a11) && throw(SingularException(1))
×
1287
                    C1 = C[1,k] = a11 \ B[1,k]
×
1288
                    # fill C-column
1289
                    for i in 2:m
×
1290
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1
×
1291
                    end
×
1292
                    for j in 2:m
×
1293
                        ajj = A[j,j]
×
1294
                        iszero(ajj) && throw(SingularException(j))
×
1295
                        Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
×
1296
                        for i in j+1:m
×
1297
                            C[i,k] -= _ustrip(A[i,j]) * Cj
×
1298
                        end
×
1299
                    end
×
1300
                end
×
1301
            else # tfun in (adjoint, transpose)
1302
                for k in 1:n
×
1303
                    for j in m:-1:1
×
1304
                        ajj = A[j,j]
×
1305
                        iszero(ajj) && throw(SingularException(j))
×
1306
                        Bj = B[j,k]
×
1307
                        for i in j+1:m
×
1308
                            Bj -= tfun(A[i,j]) * C[i,k]
×
1309
                        end
×
1310
                        C[j,k] = tfun(ajj) \ Bj
×
1311
                    end
×
1312
                end
×
1313
            end
1314
        else # isunitc == 'U'
1315
            if tfun === identity
×
1316
                for k in 1:n
×
1317
                    C1 = C[1,k] = oA \ B[1,k]
×
1318
                    # fill C-column
1319
                    for i in 2:m
×
1320
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1
×
1321
                    end
×
1322
                    for j in 2:m
×
1323
                        Cj = C[j,k]
×
1324
                        for i in j+1:m
×
1325
                            C[i,k] -= _ustrip(A[i,j]) * Cj
×
1326
                        end
×
1327
                    end
×
1328
                end
×
1329
            else # tfun in (adjoint, transpose)
1330
                for k in 1:n
×
1331
                    for j in m:-1:1
×
1332
                        Bj = B[j,k]
×
1333
                        for i in j+1:m
×
1334
                            Bj -= tfun(A[i,j]) * C[i,k]
×
1335
                        end
×
1336
                        C[j,k] = oA \ Bj
×
1337
                    end
×
1338
                end
×
1339
            end
1340
        end
1341
    end
1342
    return C
×
1343
end
1344
# conjugate cases
1345
function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat)
164✔
1346
    A = parent(xA)
824✔
1347
    require_one_based_indexing(C, A, B)
164✔
1348
    mA, nA = size(A)
164✔
1349
    m, n = size(B, 1), size(B,2)
164✔
1350
    if nA != m
164✔
1351
        throw(DimensionMismatch("second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal"))
×
1352
    end
1353
    if size(C) != size(B)
328✔
1354
        throw(DimensionMismatch("size of output, $(size(C)), does not match size of right hand side, $(size(B))"))
×
1355
    end
1356
    oA = oneunit(eltype(A))
164✔
1357
    @inbounds if uploc == 'U'
164✔
1358
        if isunitc == 'N'
82✔
1359
            for k in 1:n
82✔
1360
                amm = conj(A[m,m])
742✔
1361
                iszero(amm) && throw(SingularException(m))
742✔
1362
                Cm = C[m,k] = amm \ B[m,k]
742✔
1363
                # fill C-column
1364
                for i in m-1:-1:1
1,648✔
1365
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm
6,058✔
1366
                end
11,952✔
1367
                for j in m-1:-1:1
2,390✔
1368
                    ajj = conj(A[j,j])
11,952✔
1369
                    iszero(ajj) && throw(SingularException(j))
11,952✔
1370
                    Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
17,186✔
1371
                    for i in j-1:-1:1
11,952✔
1372
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
27,072✔
1373
                    end
42,934✔
1374
                end
17,928✔
1375
            end
7,378✔
1376
        else # isunitc == 'U'
1377
            for k in 1:n
21,096✔
1378
                Cm = C[m,k] = oA \ B[m,k]
36,958✔
1379
                # fill C-column
1380
                for i in m-1:-1:1
16,522✔
1381
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm
×
1382
                end
×
1383
                for j in m-1:-1:1
×
1384
                    Cj = C[j,k]
×
1385
                    for i in 1:j-1
×
1386
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
×
1387
                    end
×
1388
                end
×
1389
            end
×
1390
        end
1391
    else # uploc == 'L'
1392
        if isunitc == 'N'
×
1393
            for k in 1:n
×
1394
                a11 = conj(A[1,1])
×
1395
                iszero(a11) && throw(SingularException(1))
×
1396
                C1 = C[1,k] = a11 \ B[1,k]
×
1397
                # fill C-column
1398
                for i in 2:m
×
1399
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1
×
1400
                end
×
1401
                for j in 2:m
×
1402
                    ajj = conj(A[j,j])
×
1403
                    iszero(ajj) && throw(SingularException(j))
×
1404
                    Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
×
1405
                    for i in j+1:m
×
1406
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
×
1407
                    end
×
1408
                end
×
1409
            end
×
1410
        else # isunitc == 'U'
1411
            for k in 1:n
×
1412
                C1 = C[1,k] = oA \ B[1,k]
×
1413
                # fill C-column
1414
                for i in 2:m
×
1415
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1
×
1416
                end
×
1417
                for j in 1:m
×
1418
                    Cj = C[j,k]
×
1419
                    for i in j+1:m
×
1420
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
×
1421
                    end
×
1422
                end
×
1423
            end
×
1424
        end
1425
    end
1426
    return C
×
1427
end
1428

1429
function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix)
4,782✔
1430
    require_one_based_indexing(C, A, B)
5,813✔
1431
    m, n = size(A)
23,824✔
1432
    if size(B, 1) != n
14,303✔
1433
        throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
99,678✔
1434
    end
1435
    if size(C) != size(A)
93,637✔
1436
        throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))"))
364,766✔
1437
    end
1438
    oB = oneunit(eltype(B))
367,520✔
1439
    unit = isunitc == 'U'
2,754✔
1440
    @inbounds if uploc == 'U'
461,516✔
1441
        if tfun === identity
652,305✔
1442
            for i in 1:m
410,901✔
1443
                for j in 1:n
142,826✔
1444
                    Aij = A[i,j]
131,962✔
1445
                    for k in 1:j - 1
255,666✔
1446
                        Aij -= C[i,k]*B[k,j]
550,833✔
1447
                    end
657,746✔
1448
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
134,767✔
1449
                    C[i,j] = Aij / (unit ? oB : B[j,j])
117,049✔
1450
                end
281,837✔
1451
            end
220,303✔
1452
        else # tfun in (adjoint, transpose)
1453
            for i in 1:m
113,922✔
1454
                for j in n:-1:1
49,480✔
1455
                    Aij = A[i,j]
56,240✔
1456
                    for k in j + 1:n
69,200✔
1457
                        Aij -= C[i,k]*tfun(B[j,k])
167,908✔
1458
                    end
204,894✔
1459
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
43,280✔
1460
                    C[i,j] = Aij / (unit ? oB : tfun(B[j,j]))
28,120✔
1461
                end
53,140✔
1462
            end
5,858✔
1463
        end
1464
    else # uploc == 'L'
1465
        if tfun === identity
1,373✔
1466
            for i in 1:m
1,031✔
1467
                for j in n:-1:1
×
1468
                    Aij = A[i,j]
×
1469
                    for k in j + 1:n
×
1470
                        Aij -= C[i,k]*B[k,j]
×
1471
                    end
×
1472
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1473
                    C[i,j] = Aij / (unit ? oB : B[j,j])
×
1474
                end
×
1475
            end
×
1476
        else # tfun in (adjoint, transpose)
1477
            for i in 1:m
×
1478
                for j in 1:n
×
1479
                    Aij = A[i,j]
×
1480
                    for k in 1:j - 1
×
1481
                        Aij -= C[i,k]*tfun(B[j,k])
×
1482
                    end
×
1483
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1484
                    C[i,j] = Aij / (unit ? oB : tfun(B[j,j]))
×
1485
                end
×
1486
            end
×
1487
        end
1488
    end
1489
    return C
×
1490
end
1491
function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans)
×
1492
    B = parent(xB)
×
1493
    require_one_based_indexing(C, A, B)
×
1494
    m, n = size(A)
×
1495
    if size(B, 1) != n
×
1496
        throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
×
1497
    end
1498
    if size(C) != size(A)
×
1499
        throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))"))
×
1500
    end
1501
    oB = oneunit(eltype(B))
×
1502
    unit = isunitc == 'U'
×
1503
    if uploc == 'U'
×
1504
        @inbounds for i in 1:m
×
1505
            for j in 1:n
×
1506
                Aij = A[i,j]
×
1507
                for k in 1:j - 1
×
1508
                    Aij -= C[i,k]*conj(B[k,j])
×
1509
                end
×
1510
                unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1511
                C[i,j] = Aij / (unit ? oB : conj(B[j,j]))
×
1512
            end
×
1513
        end
×
1514
    else # uploc == 'L'
1515
        @inbounds for i in 1:m
×
1516
            for j in n:-1:1
×
1517
                Aij = A[i,j]
×
1518
                for k in j + 1:n
×
1519
                    Aij -= C[i,k]*conj(B[k,j])
×
1520
                end
×
1521
                unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1522
                C[i,j] = Aij / (unit ? oB : conj(B[j,j]))
×
1523
            end
×
1524
        end
×
1525
    end
1526
    return C
×
1527
end
1528

1529
# these are needed because we don't keep track of left- and right-multiplication in tritrimul!
1530
rmul!(A::UpperTriangular, B::UpperTriangular)     = UpperTriangular(rmul!(triu!(A.data), B))
12✔
1531
rmul!(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(rmul!(triu!(A.data), B))
12✔
1532
rmul!(A::LowerTriangular, B::LowerTriangular)     = LowerTriangular(rmul!(tril!(A.data), B))
12✔
1533
rmul!(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(rmul!(tril!(A.data), B))
12✔
1534

1535
# Promotion
1536
## Promotion methods in matmul don't apply to triangular multiplication since
1537
## it is inplace. Hence we have to make very similar definitions, but without
1538
## allocation of a result array. For multiplication and unit diagonal division
1539
## the element type doesn't have to be stable under division whereas that is
1540
## necessary in the general triangular solve problem.
1541

1542
_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA<:Integer,TB<:Integer} =
404✔
1543
    promote_op(matprod, TA, TB)
1544
_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA,TB} =
6,368✔
1545
    promote_op(op, TA, TB)
1546
## The general promotion methods
1547
for mat in (:AbstractVector, :AbstractMatrix)
1548
    ### Left division with triangle to the left hence rhs cannot be transposed. No quotients.
1549
    @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat)
3,677✔
1550
        require_one_based_indexing(B)
4,235✔
1551
        TAB = _inner_type_promotion(\, eltype(A), eltype(B))
4,235✔
1552
        ldiv!(similar(B, TAB, size(B)), A, B)
4,793✔
1553
    end
1554
    ### Left division with triangle to the left hence rhs cannot be transposed. Quotients.
1555
    @eval function \(A::Union{UpperTriangular,LowerTriangular}, B::$mat)
4,363✔
1556
        require_one_based_indexing(B)
10,696✔
1557
        TAB = promote_op(\, eltype(A), eltype(B))
10,696✔
1558
        ldiv!(similar(B, TAB, size(B)), A, B)
17,029✔
1559
    end
1560
    ### Right division with triangle to the right hence lhs cannot be transposed. No quotients.
1561
    @eval function /(A::$mat, B::Union{UnitUpperTriangular, UnitLowerTriangular})
1,979✔
1562
        require_one_based_indexing(A)
2,537✔
1563
        TAB = _inner_type_promotion(/, eltype(A), eltype(B))
2,537✔
1564
        _rdiv!(similar(A, TAB, size(A)), A, B)
3,095✔
1565
    end
1566
    ### Right division with triangle to the right hence lhs cannot be transposed. Quotients.
1567
    @eval function /(A::$mat, B::Union{UpperTriangular,LowerTriangular})
2,003✔
1568
        require_one_based_indexing(A)
2,601✔
1569
        TAB = promote_op(/, eltype(A), eltype(B))
2,601✔
1570
        _rdiv!(similar(A, TAB, size(A)), A, B)
3,199✔
1571
    end
1572
end
1573

1574
## Some Triangular-Triangular cases. We might want to write tailored methods
1575
## for these cases, but I'm not sure it is worth it.
1576
for f in (:*, :\)
1577
    @eval begin
1578
        ($f)(A::LowerTriangular, B::LowerTriangular) =
741✔
1579
            LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix))
1580
        ($f)(A::LowerTriangular, B::UnitLowerTriangular) =
672✔
1581
            LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix))
1582
        ($f)(A::UnitLowerTriangular, B::LowerTriangular) =
627✔
1583
            LowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix))
1584
        ($f)(A::UnitLowerTriangular, B::UnitLowerTriangular) =
616✔
1585
            UnitLowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix))
1586
        ($f)(A::UpperTriangular, B::UpperTriangular) =
2,603✔
1587
            UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix))
1588
        ($f)(A::UpperTriangular, B::UnitUpperTriangular) =
672✔
1589
            UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix))
1590
        ($f)(A::UnitUpperTriangular, B::UpperTriangular) =
627✔
1591
            UpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix))
1592
        ($f)(A::UnitUpperTriangular, B::UnitUpperTriangular) =
621✔
1593
            UnitUpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix))
1594
    end
1595
end
1596
(/)(A::LowerTriangular, B::LowerTriangular) =
123✔
1597
    LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular))
1598
(/)(A::LowerTriangular, B::UnitLowerTriangular) =
117✔
1599
    LowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular))
1600
(/)(A::UnitLowerTriangular, B::LowerTriangular) =
98✔
1601
    LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular))
1602
(/)(A::UnitLowerTriangular, B::UnitLowerTriangular) =
106✔
1603
    UnitLowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular))
1604
(/)(A::UpperTriangular, B::UpperTriangular) =
167✔
1605
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular))
1606
(/)(A::UpperTriangular, B::UnitUpperTriangular) =
117✔
1607
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular))
1608
(/)(A::UnitUpperTriangular, B::UpperTriangular) =
98✔
1609
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular))
1610
(/)(A::UnitUpperTriangular, B::UnitUpperTriangular) =
106✔
1611
    UnitUpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular))
1612

1613
# Complex matrix power for upper triangular factor, see:
1614
#   Higham and Lin, "A Schur-Padé algorithm for fractional powers of a Matrix",
1615
#     SIAM J. Matrix Anal. & Appl., 32 (3), (2011) 1056–1078.
1616
#   Higham and Lin, "An improved Schur-Padé algorithm for fractional powers of
1617
#     a matrix and their Fréchet derivatives", SIAM. J. Matrix Anal. & Appl.,
1618
#     34(3), (2013) 1341–1360.
1619
function powm!(A0::UpperTriangular, p::Real)
64✔
1620
    if abs(p) >= 1
126✔
1621
        throw(ArgumentError("p must be a real number in (-1,1), got $p"))
368✔
1622
    end
1623

1624
    normA0 = opnorm(A0, 1)
214✔
1625
    rmul!(A0, 1/normA0)
124✔
1626

1627
    theta = [1.53e-5, 2.25e-3, 1.92e-2, 6.08e-2, 1.25e-1, 2.03e-1, 2.84e-1]
496✔
1628
    n = checksquare(A0)
124✔
1629

1630
    A, m, s = invsquaring(A0, theta)
124✔
1631
    A = I - A
308✔
1632

1633
    # Compute accurate diagonal of I - T
1634
    sqrt_diag!(A0, A, s)
554✔
1635
    for i = 1:n
308✔
1636
        A[i, i] = -A[i, i]
644✔
1637
    end
428✔
1638
    # Compute the Padé approximant
1639
    c = 0.5 * (p - m) / (2 * m - 1)
62✔
1640
    triu!(A)
63✔
1641
    S = c * A
63✔
1642
    Stmp = similar(S)
62✔
1643
    for j = m-1:-1:1
124✔
1644
        j4 = 4 * j
266✔
1645
        c = (-p - j) / (j4 + 2)
266✔
1646
        for i = 1:n
266✔
1647
            @inbounds S[i, i] = S[i, i] + 1
940✔
1648
        end
1,614✔
1649
        copyto!(Stmp, S)
266✔
1650
        mul!(S, A, c)
532✔
1651
        ldiv!(Stmp, S)
270✔
1652

1653
        c = (p - j) / (j4 - 2)
266✔
1654
        for i = 1:n
266✔
1655
            @inbounds S[i, i] = S[i, i] + 1
940✔
1656
        end
1,614✔
1657
        copyto!(Stmp, S)
266✔
1658
        mul!(S, A, c)
532✔
1659
        ldiv!(Stmp, S)
270✔
1660
    end
470✔
1661
    for i = 1:n
62✔
1662
        S[i, i] = S[i, i] + 1
214✔
1663
    end
366✔
1664
    copyto!(Stmp, S)
62✔
1665
    mul!(S, A, -p)
124✔
1666
    ldiv!(Stmp, S)
63✔
1667
    for i = 1:n
62✔
1668
        @inbounds S[i, i] = S[i, i] + 1
×
1669
    end
×
1670

1671
    blockpower!(A0, S, p/(2^s))
×
1672
    for m = 1:s
×
1673
        mul!(Stmp.data, S, S)
×
1674
        copyto!(S, Stmp)
×
1675
        blockpower!(A0, S, p/(2^(s-m)))
×
1676
    end
×
1677
    rmul!(S, normA0^p)
×
1678
    return S
×
1679
end
1680
powm(A::LowerTriangular, p::Real) = copy(transpose(powm!(copy(transpose(A)), p::Real)))
2✔
1681

1682
# Complex matrix logarithm for the upper triangular factor, see:
1683
#   Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for
1684
#     the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169.
1685
#   Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix
1686
#     logarithm and estimating the condition number", SIAM J. Sci. Comput.,
1687
#     35(4), (2013), C394–C410.
1688
#
1689
# Based on the code available at http://eprints.ma.man.ac.uk/1851/02/logm.zip,
1690
# Copyright (c) 2011, Awad H. Al-Mohy and Nicholas J. Higham
1691
# Julia version relicensed with permission from original authors
1692
log(A::UpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A)
257✔
1693
log(A::UnitUpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A)
26✔
1694
log(A::LowerTriangular) = copy(transpose(log(copy(transpose(A)))))
4✔
1695
log(A::UnitLowerTriangular) = copy(transpose(log(copy(transpose(A)))))
4✔
1696

1697
function log_quasitriu(A0::AbstractMatrix{T}) where T<:BlasFloat
296✔
1698
    # allocate real A if log(A) will be real and complex A otherwise
1699
    n = checksquare(A0)
296✔
1700
    if isreal(A0) && (!istriu(A0) || !any(x -> real(x) < zero(real(T)), diag(A0)))
865✔
1701
        A = T <: Complex ? real(A0) : copy(A0)
91✔
1702
    else
1703
        A = T <: Complex ? copy(A0) : complex(A0)
205✔
1704
    end
1705
    if A0 isa UnitUpperTriangular
296✔
1706
        A = UpperTriangular(parent(A))
26✔
1707
        @inbounds for i in 1:n
26✔
1708
            A[i,i] = 1
234✔
1709
        end
442✔
1710
    end
1711
    Y0 = _log_quasitriu!(A0, A)
387✔
1712
    # return complex result for complex input
1713
    Y = T <: Complex ? complex(Y0) : Y0
322✔
1714

1715
    if A0 isa UpperTriangular || A0 isa UnitUpperTriangular
296✔
1716
        return UpperTriangular(Y)
283✔
1717
    else
1718
        return Y
13✔
1719
    end
1720
end
1721
# type-stable implementation of log_quasitriu
1722
# A is a copy of A0 that is overwritten while computing the result. It has the same eltype
1723
# as the result.
1724
function _log_quasitriu!(A0, A)
296✔
1725
    # Find Padé degree m and s while replacing A with A^(1/2^s)
1726
    m, s = _find_params_log_quasitriu!(A)
296✔
1727

1728
    # Compute accurate superdiagonal of A
1729
    _pow_superdiag_quasitriu!(A, A0, 0.5^s)
585✔
1730

1731
    # Compute accurate block diagonal of A
1732
    _sqrt_pow_diag_quasitriu!(A, A0, s)
296✔
1733

1734
    # Get the Gauss-Legendre quadrature points and weights
1735
    R = zeros(Float64, m, m)
10,090✔
1736
    for i = 1:m - 1
296✔
1737
        R[i,i+1] = i / sqrt((2 * i)^2 - 1)
1,400✔
1738
        R[i+1,i] = R[i,i+1]
1,400✔
1739
    end
2,513✔
1740
    x,V = eigen(R)
592✔
1741
    w = Vector{Float64}(undef, m)
592✔
1742
    for i = 1:m
296✔
1743
        x[i] = (x[i] + 1) / 2
1,696✔
1744
        w[i] = V[1,i]^2
1,696✔
1745
    end
3,096✔
1746

1747
    # Compute the Padé approximation
1748
    t = eltype(A)
296✔
1749
    n = size(A, 1)
296✔
1750
    Y = zeros(t, n, n)
10,036✔
1751
    B = similar(A)
309✔
1752
    for k = 1:m
296✔
1753
        B .= t(x[k]) .* A
1,757✔
1754
        @inbounds for i in 1:n
1,696✔
1755
            B[i,i] += 1
8,279✔
1756
        end
14,862✔
1757
        Y .+= t(w[k]) .* rdiv_quasitriu!(A, B)
3,392✔
1758
    end
3,096✔
1759

1760
    # Scale back
1761
    lmul!(2.0^s, Y)
585✔
1762

1763
    # Compute accurate diagonal and superdiagonal of log(A)
1764
    _log_diag_quasitriu!(Y, A0)
296✔
1765

1766
    return Y
296✔
1767
end
1768

1769
# Auxiliary functions for matrix logarithm and matrix power
1770

1771
# Find Padé degree m and s while replacing A with A^(1/2^s)
1772
#   Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for
1773
#     the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169.
1774
#   from Algorithm 4.1
1775
function _find_params_log_quasitriu!(A)
296✔
1776
    maxsqrt = 100
704✔
1777
    theta = [1.586970738772063e-005,
2,073✔
1778
         2.313807884242979e-003,
1779
         1.938179313533253e-002,
1780
         6.209171588994762e-002,
1781
         1.276404810806775e-001,
1782
         2.060962623452836e-001,
1783
         2.879093714241194e-001]
1784
    tmax = size(theta, 1)
638✔
1785
    n = size(A, 1)
296✔
1786
    p = 0
296✔
1787
    m = 0
536✔
1788

1789
    # Find s0, the smallest s such that the ρ(triu(A)^(1/2^s) - I) ≤ theta[tmax], where ρ(X)
1790
    # is the spectral radius of X
1791
    d = complex.(@view(A[diagind(A)]))
638✔
1792
    dm1 = d .- 1
1,036✔
1793
    s = 0
296✔
1794
    while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
1,732✔
1795
        d .= sqrt.(d)
2,842✔
1796
        dm1 .= d .- 1
3,936✔
1797
        s = s + 1
4,248✔
1798
    end
1,094✔
1799
    s0 = s
2,044✔
1800

1801
    # Compute repeated roots
1802
    for k = 1:min(s, maxsqrt)
2,044✔
1803
        _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A)
1,732✔
1804
    end
1,926✔
1805

1806
    # these three never needed at the same time, so reuse the same temporary
1807
    AmI = AmI4 = AmI5 = A - I
296✔
1808
    AmI2 = AmI * AmI
309✔
1809
    AmI3 = AmI2 * AmI
309✔
1810
    d2 = sqrt(opnorm(AmI2, 1))
296✔
1811
    d3 = cbrt(opnorm(AmI3, 1))
296✔
1812
    alpha2 = max(d2, d3)
296✔
1813
    foundm = false
296✔
1814
    if alpha2 <= theta[2]
296✔
1815
        m = alpha2 <= theta[1] ? 1 : 2
9✔
1816
        foundm = true
9✔
1817
    end
1818

1819
    while !foundm
638✔
1820
        more_sqrt = false
629✔
1821
        mul!(AmI4, AmI2, AmI2)
629✔
1822
        d4 = opnorm(AmI4, 1)^(1/4)
1,059✔
1823
        alpha3 = max(d3, d4)
629✔
1824
        if alpha3 <= theta[tmax]
629✔
1825
            local j
1826
            for outer j = 3:tmax
362✔
1827
                if alpha3 <= theta[j]
1,490✔
1828
                    break
362✔
1829
                end
1830
            end
1,128✔
1831
            if j <= 6
362✔
1832
                m = j
221✔
1833
                break
221✔
1834
            elseif alpha3 / 2 <= theta[5] && p < 2
141✔
1835
                more_sqrt = true
100✔
1836
                p = p + 1
100✔
1837
           end
1838
        end
1839

1840
        if !more_sqrt
408✔
1841
            mul!(AmI5, AmI3, AmI2)
308✔
1842
            d5 = opnorm(AmI5, 1)^(1/5)
535✔
1843
            alpha4 = max(d4, d5)
308✔
1844
            eta = min(alpha3, alpha4)
308✔
1845
            if eta <= theta[tmax]
308✔
1846
                j = 0
65✔
1847
                for outer j = 6:tmax
65✔
1848
                    if eta <= theta[j]
128✔
1849
                        m = j
65✔
1850
                        break
×
1851
                    end
1852
                end
×
1853
                break
×
1854
            end
1855
        end
1856

1857
        if s == maxsqrt
×
1858
            m = tmax
×
1859
            break
×
1860
        end
1861
        _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A)
×
1862
        copyto!(AmI, A)
×
1863
        for i in 1:n
×
1864
            @inbounds AmI[i,i] -= 1
×
1865
        end
×
1866
        mul!(AmI2, AmI, AmI)
×
1867
        mul!(AmI3, AmI2, AmI)
×
1868
        d3 = cbrt(opnorm(AmI3, 1))
×
1869
        s = s + 1
×
1870
    end
×
1871
    return m, s
×
1872
end
1873

1874
# Compute accurate diagonal of A = A0^s - I
1875
function sqrt_diag!(A0::UpperTriangular, A::UpperTriangular, s)
62✔
1876
    n = checksquare(A0)
62✔
1877
    T = eltype(A)
62✔
1878
    @inbounds for i = 1:n
62✔
1879
        a = complex(A0[i,i])
214✔
1880
        A[i,i] = _sqrt_pow(a, s)
214✔
1881
    end
214✔
1882
end
1883
# Compute accurate block diagonal of A = A0^s - I for upper quasi-triangular A0 produced
1884
# by the Schur decomposition. Diagonal is made of 1x1 and 2x2 blocks.
1885
# 2x2 blocks are real with non-negative conjugate pair eigenvalues
1886
function _sqrt_pow_diag_quasitriu!(A, A0, s)
296✔
1887
    n = checksquare(A0)
296✔
1888
    t = typeof(sqrt(zero(eltype(A))))
296✔
1889
    i = 1
296✔
1890
    @inbounds while i < n
296✔
1891
        if iszero(A0[i+1,i])  # 1x1 block
1,112✔
1892
            A[i,i] = _sqrt_pow(t(A0[i,i]), s)
1,095✔
1893
            i += 1
1,095✔
1894
        else  # real 2x2 block
1895
            @views _sqrt_pow_diag_block_2x2!(A[i:i+1,i:i+1], A0[i:i+1,i:i+1], s)
17✔
1896
            i += 2
17✔
1897
        end
1898
    end
1,112✔
1899
    if i == n  # last block is 1x1
296✔
1900
        @inbounds A[n,n] = _sqrt_pow(t(A0[n,n]), s)
289✔
1901
    end
1902
    return A
296✔
1903
end
1904
# compute a^(1/2^s)-1
1905
#   Al-Mohy, "A more accurate Briggs method for the logarithm",
1906
#      Numer. Algorithms, 59, (2012), 393–402.
1907
#   Algorithm 2
1908
function _sqrt_pow(a::Number, s)
1,598✔
1909
    T = typeof(sqrt(zero(a)))
1,598✔
1910
    s == 0 && return T(a) - 1
1,598✔
1911
    s0 = s
1,571✔
1912
    if imag(a) >= 0 && real(a) <= 0 && !iszero(a)  # angle(a) ≥ π / 2
1,571✔
1913
        a = sqrt(a)
139✔
1914
        s0 = s - 1
139✔
1915
    end
1916
    z0 = a - 1
1,571✔
1917
    a = sqrt(a)
1,571✔
1918
    r = 1 + a
1,571✔
1919
    for j = 1:s0-1
1,571✔
1920
        a = sqrt(a)
4,463✔
1921
        r = r * (1 + a)
4,463✔
1922
    end
7,371✔
1923
    return z0 / r
1,571✔
1924
end
1925
# compute A0 = A^(1/2^s)-I for 2x2 real matrices A and A0
1926
# A has non-negative conjugate pair eigenvalues
1927
# "Improved Inverse Scaling and Squaring Algorithms for the Matrix Logarithm"
1928
# SIAM J. Sci. Comput., 34(4), (2012) C153–C169. doi: 10.1137/110852553
1929
# Algorithm 5.1
1930
Base.@propagate_inbounds function _sqrt_pow_diag_block_2x2!(A, A0, s)
1931
    _sqrt_real_2x2!(A, A0)
17✔
1932
    if isone(s)
17✔
1933
        A[1,1] -= 1
×
1934
        A[2,2] -= 1
×
1935
    else
1936
        # Z = A - I
1937
        z11, z21, z12, z22 = A[1,1] - 1, A[2,1], A[1,2], A[2,2] - 1
17✔
1938
        # A = sqrt(A)
1939
        _sqrt_real_2x2!(A, A)
17✔
1940
        # P = A + I
1941
        p11, p21, p12, p22 = A[1,1] + 1, A[2,1], A[1,2], A[2,2] + 1
17✔
1942
        for i in 1:(s - 2)
17✔
1943
            # A = sqrt(A)
1944
            _sqrt_real_2x2!(A, A)
423✔
1945
            a11, a21, a12, a22 = A[1,1], A[2,1], A[1,2], A[2,2]
423✔
1946
            # P += P * A
1947
            r11 = p11*(1 + a11) + p12*a21
423✔
1948
            r22 = p21*a12 + p22*(1 + a22)
423✔
1949
            p21 = p21*(1 + a11) + p22*a21
423✔
1950
            p12 = p11*a12 + p12*(1 + a22)
423✔
1951
            p11 = r11
423✔
1952
            p22 = r22
423✔
1953
        end
829✔
1954
        # A = Z / P
1955
        c = inv(p11*p22 - p21*p12)
17✔
1956
        A[1,1] = (p22*z11 - p21*z12) * c
17✔
1957
        A[2,1] = (p22*z21 - p21*z22) * c
17✔
1958
        A[1,2] = (p11*z12 - p12*z11) * c
17✔
1959
        A[2,2] = (p11*z22 - p12*z21) * c
17✔
1960
    end
1961
    return A
17✔
1962
end
1963
# Compute accurate superdiagonal of A = A0^s - I for upper quasi-triangular A0 produced
1964
# by a Schur decomposition.
1965
# Higham and Lin, "A Schur–Padé Algorithm for Fractional Powers of a Matrix"
1966
# SIAM J. Matrix Anal. Appl., 32(3), (2011), 1056–1078.
1967
# Equation 5.6
1968
# see also blockpower for when A0 is upper triangular
1969
function _pow_superdiag_quasitriu!(A, A0, p)
296✔
1970
    n = checksquare(A0)
296✔
1971
    t = eltype(A)
296✔
1972
    k = 1
296✔
1973
    @inbounds while k < n
296✔
1974
        if !iszero(A[k+1,k])
1,105✔
1975
            k += 2
10✔
1976
            continue
10✔
1977
        end
1978
        if !(k == n - 1 || iszero(A[k+2,k+1]))
1,910✔
1979
            k += 3
7✔
1980
            continue
7✔
1981
        end
1982
        Ak = t(A0[k,k])
1,088✔
1983
        Akp1 = t(A0[k+1,k+1])
1,088✔
1984

1985
        Akp = Ak^p
1,088✔
1986
        Akp1p = Akp1^p
1,088✔
1987

1988
        if Ak == Akp1
1,088✔
1989
            A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
285✔
1990
        elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
1,594✔
1991
            A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
426✔
1992
        else
1993
            logAk = log(Ak)
377✔
1994
            logAkp1 = log(Akp1)
377✔
1995
            z = (Akp1 - Ak)/(Akp1 + Ak)
377✔
1996
            if abs(z) > 1
488✔
1997
                A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
73✔
1998
            else
1999
                w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
399✔
2000
                dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
513✔
2001
                A[k,k+1] = A0[k,k+1] * dd
304✔
2002
            end
2003
        end
2004
        k += 1
1,088✔
2005
    end
1,105✔
2006
end
2007

2008
# Compute accurate block diagonal and superdiagonal of A = log(A0) for upper
2009
# quasi-triangular A0 produced by the Schur decomposition.
2010
function _log_diag_quasitriu!(A, A0)
296✔
2011
    n = checksquare(A0)
296✔
2012
    t = eltype(A)
296✔
2013
    k = 1
296✔
2014
    @inbounds while k < n
296✔
2015
        if iszero(A0[k+1,k])  # 1x1 block
768✔
2016
            Ak = t(A0[k,k])
751✔
2017
            logAk = log(Ak)
751✔
2018
            A[k,k] = logAk
751✔
2019
            if k < n - 2 && iszero(A0[k+2,k+1])
751✔
2020
                Akp1 = t(A0[k+1,k+1])
344✔
2021
                logAkp1 = log(Akp1)
344✔
2022
                A[k+1,k+1] = logAkp1
344✔
2023
                if Ak == Akp1
344✔
2024
                    A[k,k+1] = A0[k,k+1] / Ak
103✔
2025
                elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
486✔
2026
                    A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
121✔
2027
                else
2028
                    z = (Akp1 - Ak)/(Akp1 + Ak)
120✔
2029
                    if abs(z) > 1
152✔
2030
                        A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
32✔
2031
                    else
2032
                        w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
128✔
2033
                        A[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
88✔
2034
                    end
2035
                end
2036
                k += 2
344✔
2037
            else
2038
                k += 1
407✔
2039
            end
2040
        else  # real 2x2 block
2041
            @views _log_diag_block_2x2!(A[k:k+1,k:k+1], A0[k:k+1,k:k+1])
17✔
2042
            k += 2
17✔
2043
        end
2044
    end
768✔
2045
    if k == n  # last 1x1 block
296✔
2046
        @inbounds A[n,n] = log(t(A0[n,n]))
289✔
2047
    end
2048
    return A
296✔
2049
end
2050
# compute A0 = log(A) for 2x2 real matrices A and A0, where A0 is a diagonal 2x2 block
2051
# produced by real Schur decomposition.
2052
# Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix
2053
# logarithm and estimating the condition number", SIAM J. Sci. Comput.,
2054
# 35(4), (2013), C394–C410.
2055
# Eq. 6.1
2056
Base.@propagate_inbounds function _log_diag_block_2x2!(A, A0)
2057
    a, b, c = A0[1,1], A0[1,2], A0[2,1]
17✔
2058
    # avoid underflow/overflow for large/small b and c
2059
    s = sqrt(abs(b)) * sqrt(abs(c))
17✔
2060
    θ = atan(s, a)
17✔
2061
    t = θ / s
17✔
2062
    au = abs(a)
17✔
2063
    if au > s
17✔
2064
        a1 = log1p((s / au)^2) / 2 + log(au)
7✔
2065
    else
2066
        a1 = log1p((au / s)^2) / 2 + log(s)
10✔
2067
    end
2068
    A[1,1] = a1
17✔
2069
    A[2,1] = c*t
17✔
2070
    A[1,2] = b*t
17✔
2071
    A[2,2] = a1
17✔
2072
    return A
17✔
2073
end
2074

2075
# Used only by powm at the moment
2076
# Repeatedly compute the square roots of A so that in the end its
2077
# eigenvalues are close enough to the positive real line
2078
function invsquaring(A0::UpperTriangular, theta)
62✔
2079
    require_one_based_indexing(theta)
122✔
2080
    # assumes theta is in ascending order
2081
    maxsqrt = 100
122✔
2082
    tmax = size(theta, 1)
122✔
2083
    n = checksquare(A0)
122✔
2084
    A = complex(copy(A0))
76✔
2085
    p = 0
108✔
2086
    m = 0
62✔
2087

2088
    # Compute repeated roots
2089
    d = complex(diag(A))
62✔
2090
    dm1 = d .- 1
216✔
2091
    s = 0
154✔
2092
    while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
370✔
2093
        d .= sqrt.(d)
216✔
2094
        dm1 .= d .- 1
370✔
2095
        s = s + 1
216✔
2096
    end
216✔
2097
    s0 = s
62✔
2098
    for k = 1:min(s, maxsqrt)
124✔
2099
        A = sqrt(A)
154✔
2100
    end
262✔
2101

2102
    AmI = A - I
62✔
2103
    d2 = sqrt(opnorm(AmI^2, 1))
62✔
2104
    d3 = cbrt(opnorm(AmI^3, 1))
62✔
2105
    alpha2 = max(d2, d3)
63✔
2106
    foundm = false
62✔
2107
    if alpha2 <= theta[2]
62✔
2108
        m = alpha2 <= theta[1] ? 1 : 2
×
2109
        foundm = true
×
2110
    end
2111

2112
    while !foundm
198✔
2113
        more = false
198✔
2114
        if s > s0
198✔
2115
            d3 = cbrt(opnorm(AmI^3, 1))
122✔
2116
        end
2117
        d4 = opnorm(AmI^4, 1)^(1/4)
392✔
2118
        alpha3 = max(d3, d4)
202✔
2119
        if alpha3 <= theta[tmax]
198✔
2120
            local j
2121
            for outer j = 3:tmax
150✔
2122
                if alpha3 <= theta[j]
644✔
2123
                    break
150✔
2124
                elseif alpha3 / 2 <= theta[5] && p < 2
494✔
2125
                    more = true
124✔
2126
                    p = p + 1
124✔
2127
                end
2128
            end
494✔
2129
            if j <= 6
150✔
2130
                m = j
62✔
2131
                foundm = true
62✔
2132
                break
62✔
2133
            elseif alpha3 / 2 <= theta[5] && p < 2
88✔
2134
                more = true
×
2135
                p = p + 1
×
2136
           end
2137
        end
2138

2139
        if !more
136✔
2140
            d5 = opnorm(AmI^5, 1)^(1/5)
182✔
2141
            alpha4 = max(d4, d5)
94✔
2142
            eta = min(alpha3, alpha4)
94✔
2143
            if eta <= theta[tmax]
92✔
2144
                j = 0
60✔
2145
                for outer j = 6:tmax
60✔
2146
                    if eta <= theta[j]
×
2147
                        m = j
×
2148
                        break
×
2149
                    end
2150
                    break
×
2151
                end
×
2152
            end
2153
            if s == maxsqrt
×
2154
                m = tmax
×
2155
                break
×
2156
            end
2157
            A = sqrt(A)
×
2158
            AmI = A - I
×
2159
            s = s + 1
×
2160
        end
2161
    end
×
2162

2163
    # Compute accurate superdiagonal of T
2164
    p = 1 / 2^s
×
2165
    A = complex(A)
×
2166
    blockpower!(A, A0, p)
×
2167
    return A,m,s
×
2168
end
2169

2170
# Compute accurate diagonal and superdiagonal of A = A0^p
2171
function blockpower!(A::UpperTriangular, A0::UpperTriangular, p)
370✔
2172
    n = checksquare(A0)
370✔
2173
    @inbounds for k = 1:n-1
370✔
2174
        Ak = complex(A0[k,k])
968✔
2175
        Akp1 = complex(A0[k+1,k+1])
968✔
2176

2177
        Akp = Ak^p
968✔
2178
        Akp1p = Akp1^p
968✔
2179

2180
        A[k,k] = Akp
968✔
2181
        A[k+1,k+1] = Akp1p
968✔
2182

2183
        if Ak == Akp1
968✔
2184
            A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
137✔
2185
        elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
1,618✔
2186
            A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
102✔
2187
        else
2188
            logAk = log(Ak)
729✔
2189
            logAkp1 = log(Akp1)
729✔
2190
            z = (Akp1 - Ak)/(Akp1 + Ak)
729✔
2191
            if abs(z) > 1
729✔
2192
                A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
×
2193
            else
2194
                w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
729✔
2195
                dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
1,458✔
2196
                A[k,k+1] = A0[k,k+1] * dd
729✔
2197
            end
2198
        end
2199
    end
968✔
2200
end
2201

2202
# Unwinding number
2203
unw(x::Real) = 0
×
2204
unw(x::Number) = ceil((imag(x) - pi) / (2 * pi))
1,972✔
2205

2206
# compute A / B for upper quasi-triangular B, possibly overwriting B
2207
function rdiv_quasitriu!(A, B)
1,696✔
2208
    n = checksquare(A)
1,696✔
2209
    AG = copy(A)
1,696✔
2210
    # use Givens rotations to annihilate 2x2 blocks
2211
    @inbounds for k in 1:(n-1)
1,696✔
2212
        s = B[k+1,k]
6,583✔
2213
        iszero(s) && continue  # 1x1 block
6,583✔
2214
        G = first(givens(B[k+1,k+1], s, k, k+1))
85✔
2215
        rmul!(B, G)
400✔
2216
        rmul!(AG, G)
85✔
2217
    end
11,485✔
2218
    return rdiv!(AG, UpperTriangular(B))
1,696✔
2219
end
2220

2221
# End of auxiliary functions for matrix logarithm and matrix power
2222

2223
sqrt(A::UpperTriangular) = sqrt_quasitriu(A)
594✔
2224
function sqrt(A::UnitUpperTriangular{T}) where T
20✔
2225
    B = A.data
20✔
2226
    n = checksquare(B)
20✔
2227
    t = typeof(sqrt(zero(T)))
20✔
2228
    R = Matrix{t}(I, n, n)
20✔
2229
    tt = typeof(oneunit(t)*oneunit(t))
20✔
2230
    half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214
22✔
2231
    @inbounds for j = 1:n
20✔
2232
        for i = j-1:-1:1
264✔
2233
            r::tt = B[i,j]
520✔
2234
            @simd for k = i+1:j-1
640✔
2235
                r -= R[i,k]*R[k,j]
2236
            end
2237
            iszero(r) || (R[i,j] = half*r)
1,033✔
2238
        end
914✔
2239
    end
264✔
2240
    return UnitUpperTriangular(R)
20✔
2241
end
2242
sqrt(A::LowerTriangular) = copy(transpose(sqrt(copy(transpose(A)))))
8✔
2243
sqrt(A::UnitLowerTriangular) = copy(transpose(sqrt(copy(transpose(A)))))
8✔
2244

2245
# Auxiliary functions for matrix square root
2246

2247
# square root of upper triangular or real upper quasitriangular matrix
2248
function sqrt_quasitriu(A0; blockwidth = eltype(A0) <: Complex ? 512 : 256)
1,364✔
2249
    n = checksquare(A0)
682✔
2250
    T = eltype(A0)
682✔
2251
    Tr = typeof(sqrt(real(zero(T))))
682✔
2252
    Tc = typeof(sqrt(complex(zero(T))))
682✔
2253
    if isreal(A0)
2,199✔
2254
        is_sqrt_real = true
292✔
2255
        if istriu(A0)
292✔
2256
            for i in 1:n
208✔
2257
                Aii = real(A0[i,i])
618✔
2258
                if Aii < zero(Aii)
618✔
2259
                    is_sqrt_real = false
40✔
2260
                    break
40✔
2261
                end
2262
            end
578✔
2263
        end
2264
        if is_sqrt_real
292✔
2265
            R = zeros(Tr, n, n)
27,078✔
2266
            A = real(A0)
252✔
2267
        else
2268
            R = zeros(Tc, n, n)
347✔
2269
            A = A0
40✔
2270
        end
2271
    else
2272
        A = A0
390✔
2273
        R = zeros(Tc, n, n)
390✔
2274
    end
2275
    _sqrt_quasitriu!(R, A; blockwidth=blockwidth, n=n)
929✔
2276
    Rc = eltype(A0) <: Real ? R : complex(R)
783✔
2277
    if A0 isa UpperTriangular
682✔
2278
        return UpperTriangular(Rc)
594✔
2279
    elseif A0 isa UnitUpperTriangular
88✔
2280
        return UnitUpperTriangular(Rc)
×
2281
    else
2282
        return Rc
88✔
2283
    end
2284
end
2285

2286
# in-place recursive sqrt of upper quasi-triangular matrix A from
2287
# Deadman E., Higham N.J., Ralha R. (2013) Blocked Schur Algorithms for Computing the Matrix
2288
# Square Root. Applied Parallel and Scientific Computing. PARA 2012. Lecture Notes in
2289
# Computer Science, vol 7782. https://doi.org/10.1007/978-3-642-36803-5_12
2290
function _sqrt_quasitriu!(R, A; blockwidth=64, n=checksquare(A))
4,352✔
2291
    if n ≤ blockwidth || !(eltype(R) <: BlasFloat) # base case, perform "point" algorithm
2,212✔
2292
        _sqrt_quasitriu_block!(R, A)
2,149✔
2293
    else  # compute blockwise recursion
2294
        split = div(n, 2)
31✔
2295
        iszero(A[split+1, split]) || (split += 1) # don't split 2x2 diagonal block
39✔
2296
        r1 = 1:split
31✔
2297
        r2 = (split + 1):n
31✔
2298
        n1, n2 = split, n - split
31✔
2299
        A11, A12, A22 = @views A[r1,r1], A[r1,r2], A[r2,r2]
31✔
2300
        R11, R12, R22 = @views R[r1,r1], R[r1,r2], R[r2,r2]
31✔
2301
        # solve diagonal blocks recursively
2302
        _sqrt_quasitriu!(R11, A11; blockwidth=blockwidth, n=n1)
31✔
2303
        _sqrt_quasitriu!(R22, A22; blockwidth=blockwidth, n=n2)
31✔
2304
        # solve off-diagonal block
2305
        R12 .= .- A12
62✔
2306
        _sylvester_quasitriu!(R11, R22, R12; blockwidth=blockwidth, nA=n1, nB=n2, raise=false)
31✔
2307
    end
2308
    return R
2,180✔
2309
end
2310

2311
function _sqrt_quasitriu_block!(R, A)
2312
    _sqrt_quasitriu_diag_block!(R, A)
2,149✔
2313
    _sqrt_quasitriu_offdiag_block!(R, A)
2,149✔
2314
    return R
2,149✔
2315
end
2316

2317
function _sqrt_quasitriu_diag_block!(R, A)
2,149✔
2318
    n = size(R, 1)
2,149✔
2319
    ta = eltype(R) <: Complex ? complex(eltype(A)) : eltype(A)
2,149✔
2320
    i = 1
2,149✔
2321
    @inbounds while i < n
2,149✔
2322
        if iszero(A[i + 1, i])
7,760✔
2323
            R[i, i] = sqrt(ta(A[i, i]))
7,024✔
2324
            i += 1
7,024✔
2325
        else
2326
            # This branch is never reached when A is complex triangular
2327
            @assert eltype(A) <: Real
736✔
2328
            @views _sqrt_real_2x2!(R[i:(i + 1), i:(i + 1)], A[i:(i + 1), i:(i + 1)])
736✔
2329
            i += 2
736✔
2330
        end
2331
    end
7,760✔
2332
    if i == n
2,149✔
2333
        R[n, n] = sqrt(ta(A[n, n]))
1,711✔
2334
    end
2335
    return R
2,149✔
2336
end
2337

2338
function _sqrt_quasitriu_offdiag_block!(R, A)
2,149✔
2339
    n = size(R, 1)
2,149✔
2340
    j = 1
2,149✔
2341
    @inbounds while j ≤ n
2,149✔
2342
        jsize_is_2 = j < n && !iszero(A[j + 1, j])
9,471✔
2343
        i = j - 1
9,471✔
2344
        while i > 0
76,150✔
2345
            isize_is_2 = i > 1 && !iszero(A[i, i - 1])
66,679✔
2346
            if isize_is_2
66,679✔
2347
                if jsize_is_2
831✔
2348
                    _sqrt_quasitriu_offdiag_block_2x2!(R, A, i - 1, j)
417✔
2349
                else
2350
                    _sqrt_quasitriu_offdiag_block_2x1!(R, A, i - 1, j)
417✔
2351
                end
2352
                i -= 2
831✔
2353
            else
2354
                if jsize_is_2
65,848✔
2355
                    _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j)
250✔
2356
                else
2357
                    _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j)
65,598✔
2358
                end
2359
                i -= 1
65,848✔
2360
            end
2361
        end
66,679✔
2362
        j += 2 - !jsize_is_2
9,471✔
2363
    end
9,471✔
2364
    return R
2,149✔
2365
end
2366

2367
# real square root of 2x2 diagonal block of quasi-triangular matrix from real Schur
2368
# decomposition. Eqs 6.8-6.9 and Algorithm 6.5 of
2369
# Higham, 2008, "Functions of Matrices: Theory and Computation", SIAM.
2370
Base.@propagate_inbounds function _sqrt_real_2x2!(R, A)
2371
    # in the real Schur form, A[1, 1] == A[2, 2], and A[2, 1] * A[1, 2] < 0
2372
    θ, a21, a12 = A[1, 1], A[2, 1], A[1, 2]
1,193✔
2373
    # avoid overflow/underflow of μ
2374
    # for real sqrt, |d| ≤ 2 max(|a12|,|a21|)
2375
    μ = sqrt(abs(a12)) * sqrt(abs(a21))
1,193✔
2376
    α = _real_sqrt(θ, μ)
1,307✔
2377
    c = 2α
1,193✔
2378
    R[1, 1] = α
1,193✔
2379
    R[2, 1] = a21 / c
1,193✔
2380
    R[1, 2] = a12 / c
1,193✔
2381
    R[2, 2] = α
1,193✔
2382
    return R
1,193✔
2383
end
2384

2385
# real part of square root of θ+im*μ
2386
@inline function _real_sqrt(θ, μ)
2387
    t = sqrt((abs(θ) + hypot(θ, μ)) / 2)
1,278✔
2388
    return θ ≥ 0 ? t : μ / 2t
1,307✔
2389
end
2390

2391
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j)
2392
    Rii = R[i, i]
65,598✔
2393
    Rjj = R[j, j]
65,598✔
2394
    iszero(Rii) && iszero(Rjj) && return R
65,598✔
2395
    t = eltype(R)
65,597✔
2396
    tt = typeof(zero(t)*zero(t))
65,597✔
2397
    r = tt(-A[i, j])
65,597✔
2398
    @simd for k in (i + 1):(j - 1)
72,510✔
2399
        r += R[i, k] * R[k, j]
2400
    end
2401
    iszero(r) && return R
65,597✔
2402
    R[i, j] = sylvester(Rii, Rjj, r)
57,033✔
2403
    return R
57,033✔
2404
end
2405

2406
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j)
2407
    jrange = j:(j + 1)
250✔
2408
    t = eltype(R)
250✔
2409
    tt = typeof(zero(t)*zero(t))
250✔
2410
    r1 = tt(-A[i, j])
250✔
2411
    r2 = tt(-A[i, j + 1])
250✔
2412
    @simd for k in (i + 1):(j - 1)
360✔
2413
        rik = R[i, k]
2414
        r1 += rik * R[k, j]
2415
        r2 += rik * R[k, j + 1]
2416
    end
2417
    Rjj = @view R[jrange, jrange]
250✔
2418
    Rij = @view R[i, jrange]
250✔
2419
    Rij[1] = r1
250✔
2420
    Rij[2] = r2
250✔
2421
    _sylvester_1x2!(R[i, i], Rjj, Rij)
250✔
2422
    return R
250✔
2423
end
2424

2425
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x1!(R, A, i, j)
2426
    irange = i:(i + 1)
417✔
2427
    t = eltype(R)
417✔
2428
    tt = typeof(zero(t)*zero(t))
417✔
2429
    r1 = tt(-A[i, j])
417✔
2430
    r2 = tt(-A[i + 1, j])
417✔
2431
    @simd for k in (i + 2):(j - 1)
546✔
2432
        rkj = R[k, j]
2433
        r1 += R[i, k] * rkj
2434
        r2 += R[i + 1, k] * rkj
2435
    end
2436
    Rii = @view R[irange, irange]
417✔
2437
    Rij = @view R[irange, j]
417✔
2438
    Rij[1] = r1
417✔
2439
    Rij[2] = r2
417✔
2440
    @views _sylvester_2x1!(Rii, R[j, j], Rij)
417✔
2441
    return R
417✔
2442
end
2443

2444
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x2!(R, A, i, j)
2445
    irange = i:(i + 1)
414✔
2446
    jrange = j:(j + 1)
414✔
2447
    t = eltype(R)
414✔
2448
    tt = typeof(zero(t)*zero(t))
414✔
2449
    for i′ in irange, j′ in jrange
414✔
2450
        Cij = tt(-A[i′, j′])
1,656✔
2451
        @simd for k in (i + 2):(j - 1)
2,332✔
2452
            Cij += R[i′, k] * R[k, j′]
2453
        end
2454
        R[i′, j′] = Cij
1,656✔
2455
    end
2,070✔
2456
    Rii = @view R[irange, irange]
414✔
2457
    Rjj = @view R[jrange, jrange]
414✔
2458
    Rij = @view R[irange, jrange]
414✔
2459
    if !iszero(Rij) && !all(isnan, Rij)
417✔
2460
        _sylvester_2x2!(Rii, Rjj, Rij)
411✔
2461
    end
2462
    return R
414✔
2463
end
2464

2465
# solve Sylvester's equation AX + XB = -C using blockwise recursion until the dimension of
2466
# A and B are no greater than blockwidth, based on Algorithm 1 from
2467
# Jonsson I, Kågström B. Recursive blocked algorithms for solving triangular systems—
2468
# Part I: one-sided and coupled Sylvester-type matrix equations. (2002) ACM Trans Math Softw.
2469
# 28(4), https://doi.org/10.1145/592843.592845.
2470
# specify raise=false to avoid breaking the recursion if a LAPACKException is thrown when
2471
# computing one of the blocks.
2472
function _sylvester_quasitriu!(A, B, C; blockwidth=64, nA=checksquare(A), nB=checksquare(B), raise=true)
863✔
2473
    if 1 ≤ nA ≤ blockwidth && 1 ≤ nB ≤ blockwidth
539✔
2474
        _sylvester_quasitriu_base!(A, B, C; raise=raise)
398✔
2475
    elseif nA ≥ 2nB ≥ 2
141✔
2476
        _sylvester_quasitriu_split1!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
12✔
2477
    elseif nB ≥ 2nA ≥ 2
129✔
2478
        _sylvester_quasitriu_split2!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
12✔
2479
    else
2480
        _sylvester_quasitriu_splitall!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
117✔
2481
    end
2482
    return C
499✔
2483
end
2484
function _sylvester_quasitriu_base!(A, B, C; raise=true)
796✔
2485
    try
398✔
2486
        _, scale = LAPACK.trsyl!('N', 'N', A, B, C)
398✔
2487
        rmul!(C, -inv(scale))
398✔
2488
    catch e
2489
        if !(e isa LAPACKException) || raise
296✔
2490
            throw(e)
148✔
2491
        end
2492
    end
2493
    return C
382✔
2494
end
2495
function _sylvester_quasitriu_split1!(A, B, C; nA=checksquare(A), kwargs...)
24✔
2496
    iA = div(nA, 2)
12✔
2497
    iszero(A[iA + 1, iA]) || (iA += 1)  # don't split 2x2 diagonal block
12✔
2498
    rA1, rA2 = 1:iA, (iA + 1):nA
12✔
2499
    nA1, nA2 = iA, nA-iA
12✔
2500
    A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2]
12✔
2501
    C1, C2 = @views C[rA1,:], C[rA2,:]
12✔
2502
    _sylvester_quasitriu!(A22, B, C2; nA=nA2, kwargs...)
24✔
2503
    mul!(C1, A12, C2, true, true)
8✔
2504
    _sylvester_quasitriu!(A11, B, C1; nA=nA1, kwargs...)
16✔
2505
    return C
8✔
2506
end
2507
function _sylvester_quasitriu_split2!(A, B, C; nB=checksquare(B), kwargs...)
24✔
2508
    iB = div(nB, 2)
12✔
2509
    iszero(B[iB + 1, iB]) || (iB += 1)  # don't split 2x2 diagonal block
12✔
2510
    rB1, rB2 = 1:iB, (iB + 1):nB
12✔
2511
    nB1, nB2 = iB, nB-iB
12✔
2512
    B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2]
12✔
2513
    C1, C2 = @views C[:,rB1], C[:,rB2]
12✔
2514
    _sylvester_quasitriu!(A, B11, C1; nB=nB1, kwargs...)
24✔
2515
    mul!(C2, C1, B12, true, true)
8✔
2516
    _sylvester_quasitriu!(A, B22, C2; nB=nB2, kwargs...)
16✔
2517
    return C
8✔
2518
end
2519
function _sylvester_quasitriu_splitall!(A, B, C; nA=checksquare(A), nB=checksquare(B), kwargs...)
234✔
2520
    iA = div(nA, 2)
117✔
2521
    iszero(A[iA + 1, iA]) || (iA += 1)  # don't split 2x2 diagonal block
128✔
2522
    iB = div(nB, 2)
117✔
2523
    iszero(B[iB + 1, iB]) || (iB += 1)  # don't split 2x2 diagonal block
136✔
2524
    rA1, rA2 = 1:iA, (iA + 1):nA
117✔
2525
    nA1, nA2 = iA, nA-iA
117✔
2526
    rB1, rB2 = 1:iB, (iB + 1):nB
117✔
2527
    nB1, nB2 = iB, nB-iB
117✔
2528
    A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2]
117✔
2529
    B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2]
117✔
2530
    C11, C21, C12, C22 = @views C[rA1,rB1], C[rA2,rB1], C[rA1,rB2], C[rA2,rB2]
117✔
2531
    _sylvester_quasitriu!(A22, B11, C21; nA=nA2, nB=nB1, kwargs...)
129✔
2532
    mul!(C11, A12, C21, true, true)
101✔
2533
    _sylvester_quasitriu!(A11, B11, C11; nA=nA1, nB=nB1, kwargs...)
109✔
2534
    mul!(C22, C21, B12, true, true)
101✔
2535
    _sylvester_quasitriu!(A22, B22, C22; nA=nA2, nB=nB2, kwargs...)
109✔
2536
    mul!(C12, A12, C22, true, true)
101✔
2537
    mul!(C12, C11, B12, true, true)
101✔
2538
    _sylvester_quasitriu!(A11, B22, C12; nA=nA1, nB=nB2, kwargs...)
109✔
2539
    return C
101✔
2540
end
2541

2542
# End of auxiliary functions for matrix square root
2543

2544
# Generic eigensystems
2545
eigvals(A::AbstractTriangular) = diag(A)
100✔
2546
function eigvecs(A::AbstractTriangular{T}) where T
4✔
2547
    TT = promote_type(T, Float32)
4✔
2548
    if TT <: BlasFloat
4✔
2549
        return eigvecs(convert(AbstractMatrix{TT}, A))
4✔
2550
    else
2551
        throw(ArgumentError("eigvecs type $(typeof(A)) not supported. Please submit a pull request."))
×
2552
    end
2553
end
2554
det(A::UnitUpperTriangular{T}) where {T} = one(T)
7✔
2555
det(A::UnitLowerTriangular{T}) where {T} = one(T)
7✔
2556
logdet(A::UnitUpperTriangular{T}) where {T} = zero(T)
7✔
2557
logdet(A::UnitLowerTriangular{T}) where {T} = zero(T)
7✔
2558
logabsdet(A::UnitUpperTriangular{T}) where {T} = zero(T), one(T)
7✔
2559
logabsdet(A::UnitLowerTriangular{T}) where {T} = zero(T), one(T)
7✔
2560
det(A::UpperTriangular) = prod(diag(A.data))
308✔
2561
det(A::LowerTriangular) = prod(diag(A.data))
7✔
2562
function logabsdet(A::Union{UpperTriangular{T},LowerTriangular{T}}) where T
65✔
2563
    sgn = one(T)
104✔
2564
    abs_det = zero(real(T))
104✔
2565
    @inbounds for i in 1:size(A,1)
104✔
2566
        diag_i = A.data[i,i]
664✔
2567
        sgn *= sign(diag_i)
804✔
2568
        abs_det += log(abs(diag_i))
775✔
2569
    end
1,224✔
2570
    return abs_det, sgn
104✔
2571
end
2572

2573
eigen(A::AbstractTriangular) = Eigen(eigvals(A), eigvecs(A))
20✔
2574

2575
# Generic singular systems
2576
for func in (:svd, :svd!, :svdvals)
2577
    @eval begin
2578
        ($func)(A::AbstractTriangular; kwargs...) = ($func)(copyto!(similar(parent(A)), A); kwargs...)
112✔
2579
    end
2580
end
2581

2582
factorize(A::AbstractTriangular) = A
28✔
2583

2584
# disambiguation methods: /(Adjoint of AbsVec, <:AbstractTriangular)
2585
/(u::AdjointAbsVec, A::Union{LowerTriangular,UpperTriangular}) = adjoint(adjoint(A) \ u.parent)
×
2586
/(u::AdjointAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = adjoint(adjoint(A) \ u.parent)
×
2587
# disambiguation methods: /(Transpose of AbsVec, <:AbstractTriangular)
2588
/(u::TransposeAbsVec, A::Union{LowerTriangular,UpperTriangular}) = transpose(transpose(A) \ u.parent)
×
2589
/(u::TransposeAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = transpose(transpose(A) \ u.parent)
×
2590
# disambiguation methods: /(Transpose of AbsVec, Adj/Trans of <:AbstractTriangular)
2591
for (tritype, comptritype) in ((:LowerTriangular, :UpperTriangular),
2592
                               (:UnitLowerTriangular, :UnitUpperTriangular),
2593
                               (:UpperTriangular, :LowerTriangular),
2594
                               (:UnitUpperTriangular, :UnitLowerTriangular))
2595
    @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Adjoint}) = transpose($comptritype(conj(parent(parent(A)))) \ u.parent)
×
2596
    @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Transpose}) = transpose(transpose(A) \ u.parent)
×
2597
end
2598

2599
# Cube root of a 2x2 real-valued matrix with complex conjugate eigenvalues and equal diagonal values.
2600
# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots.
2601
#   SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989).
2602
#   https://doi.org/10.1137/s0895479801392697
2603
function _cbrt_2x2!(A::AbstractMatrix{T}) where {T<:Real}
3✔
2604
    @assert checksquare(A) == 2
3✔
2605
    @inbounds begin
3✔
2606
        (A[1,1] == A[2,2]) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have equal diagonal values."))
3✔
2607
        (A[1,2]*A[2,1] < 0) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have complex conjugate eigenvalues."))
3✔
2608
        μ = sqrt(-A[1,2]*A[2,1])
3✔
2609
        r = cbrt(hypot(A[1,1], μ))
3✔
2610
        θ = atan(μ, A[1,1])
3✔
2611
        s, c = sincos(θ/3)
3✔
2612
        α, β′ = r*c, r*s/µ
3✔
2613
        A[1,1] = α
3✔
2614
        A[2,2] = α
3✔
2615
        A[1,2] = β′*A[1,2]
3✔
2616
        A[2,1] = β′*A[2,1]
3✔
2617
    end
2618
    return A
3✔
2619
end
2620

2621
# Cube root of a quasi upper triangular matrix (output of Schur decomposition)
2622
# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots.
2623
#   SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989).
2624
#   https://doi.org/10.1137/s0895479801392697
2625
@views function _cbrt_quasi_triu!(A::AbstractMatrix{T}) where {T<:Real}
3✔
2626
    m, n = size(A)
225✔
2627
    (m == n) || throw(ArgumentError("_cbrt_quasi_triu!: Matrix A must be square."))
225✔
2628
    # Cube roots of 1x1 and 2x2 diagonal blocks
2629
    i = 1
114✔
2630
    sizes = ones(Int,n)
252✔
2631
    S = zeros(T,2,n)
525✔
2632
    while i < n
273✔
2633
        if !iszero(A[i+1,i])
186✔
2634
            _cbrt_2x2!(A[i:i+1,i:i+1])
117✔
2635
            mul!(S[1:2,i:i+1], A[i:i+1,i:i+1], A[i:i+1,i:i+1])
168✔
2636
            sizes[i] = 2
114✔
2637
            sizes[i+1] = 0
114✔
2638
            i += 2
114✔
2639
        else
2640
            A[i,i] = cbrt(A[i,i])
132✔
2641
            S[1,i] = A[i,i]*A[i,i]
132✔
2642
            i += 1
21✔
2643
        end
2644
    end
24✔
2645
    if i == n
114✔
2646
        A[n,n] = cbrt(A[n,n])
114✔
2647
        S[1,n] = A[n,n]*A[n,n]
225✔
2648
    end
2649
    # Algorithm 4.3 in Reference [1]
2650
    Δ = I(4)
12✔
2651
    M_L₀ = zeros(T,4,4)
48✔
2652
    M_L₁ = zeros(T,4,4)
159✔
2653
    M_Bᵢⱼ⁽⁰⁾ = zeros(T,2,2)
123✔
2654
    M_Bᵢⱼ⁽¹⁾ = zeros(T,2,2)
123✔
2655
    for k = 1:n-1
225✔
2656
        for i = 1:n-k
138✔
2657
            if sizes[i] == 0 || sizes[i+k] == 0 continue end
366✔
2658
            k₁, k₂ = i+1+(sizes[i+1]==0), i+k-1
222✔
2659
            i₁, i₂, j₁, j₂, s₁, s₂ = i, i+sizes[i]-1, i+k, i+k+sizes[i+k]-1, sizes[i], sizes[i+k]
222✔
2660
            L₀ = M_L₀[1:s₁*s₂,1:s₁*s₂]
×
2661
            L₁ = M_L₁[1:s₁*s₂,1:s₁*s₂]
×
2662
            Bᵢⱼ⁽⁰⁾ = M_Bᵢⱼ⁽⁰⁾[1:s₁, 1:s₂]
×
2663
            Bᵢⱼ⁽¹⁾ = M_Bᵢⱼ⁽¹⁾[1:s₁, 1:s₂]
×
2664
            # Compute Bᵢⱼ⁽⁰⁾ and Bᵢⱼ⁽¹⁾
2665
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,k₁:k₂], A[k₁:k₂,j₁:j₂])
×
2666
            # Retreive Rᵢ,ᵢ₊ₖ as A[i+k,i]'
2667
            mul!(Bᵢⱼ⁽¹⁾, A[i₁:i₂,k₁:k₂], A[j₁:j₂,k₁:k₂]')
×
2668
            # Solve Uᵢ,ᵢ₊ₖ using Reference [1, (4.10)]
2669
            kron!(L₀, Δ[1:s₂,1:s₂], S[1:s₁,i₁:i₂])
×
2670
            L₀ .+= kron!(L₁, A[j₁:j₂,j₁:j₂]', A[i₁:i₂,i₁:i₂])
×
2671
            L₀ .+= kron!(L₁, S[1:s₂,j₁:j₂]', Δ[1:s₁,1:s₁])
×
2672
            mul!(A[i₁:i₂,j₁:j₂], A[i₁:i₂,i₁:i₂], Bᵢⱼ⁽⁰⁾, -1.0, 1.0)
×
2673
            A[i₁:i₂,j₁:j₂] .-= Bᵢⱼ⁽¹⁾
×
2674
            ldiv!(lu!(L₀), A[i₁:i₂,j₁:j₂][:])
×
2675
            # Compute and store Rᵢ,ᵢ₊ₖ' in A[i+k,i]
2676
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,i₁:i₂], A[i₁:i₂,j₁:j₂], 1.0, 1.0)
×
2677
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,j₁:j₂], A[j₁:j₂,j₁:j₂], 1.0, 1.0)
×
2678
            A[j₁:j₂,i₁:i₂] .= Bᵢⱼ⁽⁰⁾'
×
2679
        end
×
2680
    end
×
2681
    # Make quasi triangular
2682
    for j=1:n for i=j+1+(sizes[j]==2):n A[i,j] = 0 end end
×
2683
    return A
×
2684
end
2685

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

© 2025 Coveralls, Inc