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

JuliaLang / julia / #37898

09 Sep 2024 05:17AM UTC coverage: 87.758% (-0.005%) from 87.763%
#37898

push

local

web-flow
builtins: add `Core.throw_methoderror` (#55705)

This allows us to simulate/mark calls that are known-to-fail.

Required for https://github.com/JuliaLang/julia/pull/54972/

1 of 11 new or added lines in 3 files covered. (9.09%)

10 existing lines in 4 files now uncovered.

78271 of 89190 relevant lines covered (87.76%)

16556892.74 hits per line

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

92.81
/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)
96,218✔
22
                checksquare(data)
96,219✔
23
                new{T,S}(data)
96,219✔
24
            end
25
        end
26
        $t(A::$t) = A
11,958✔
27
        $t{T}(A::$t{T}) where {T} = A
60✔
28
        $t(A::AbstractMatrix) = $t{eltype(A), typeof(A)}(A)
96,184✔
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,624✔
31

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

35
        size(A::$t) = size(A.data)
458,946✔
36
        axes(A::$t) = axes(A.data)
777,036✔
37

38
        # For A<:AbstractTriangular, similar(A[, neweltype]) should yield a matrix with the same
39
        # triangular type and underlying storage type as A. The following method covers these cases.
40
        similar(A::$t, ::Type{T}) where {T} = $t(similar(parent(A), T))
6,317✔
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)
32,981✔
44

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

147
uppertriangular(M) = UpperTriangular(M)
945✔
148
lowertriangular(M) = LowerTriangular(M)
262✔
149

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

153
Base.dataids(A::UpperOrLowerTriangular) = Base.dataids(A.data)
23,457✔
154

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

178
parent(A::UpperOrLowerTriangular) = A.data
177,301✔
179

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

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

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

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

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

230
@propagate_inbounds getindex(A::UnitLowerTriangular{T}, i::Int, j::Int) where {T} =
3,302,232✔
231
    i > j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
232
@propagate_inbounds getindex(A::LowerTriangular, i::Int, j::Int) =
3,201,716✔
233
    i >= j ? A.data[i,j] : _zero(A.data,j,i)
234
@propagate_inbounds getindex(A::UnitUpperTriangular{T}, i::Int, j::Int) where {T} =
3,104,996✔
235
    i < j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
236
@propagate_inbounds getindex(A::UpperTriangular, i::Int, j::Int) =
3,712,976✔
237
    i <= j ? A.data[i,j] : _zero(A.data,j,i)
238

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

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

254
@propagate_inbounds function setindex!(A::UpperTriangular, x, i::Integer, j::Integer)
819✔
255
    if i > j
28,895✔
256
        iszero(x) || throw_nonzeroerror(typeof(A), x, i, j)
2,618✔
257
    else
258
        A.data[i,j] = x
26,534✔
259
    end
260
    return A
28,638✔
261
end
262

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

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

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

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

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

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

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

333
istril(A::UnitLowerTriangular, k::Integer=0) = k >= 0
137✔
334
istriu(A::UnitUpperTriangular, k::Integer=0) = k <= 0
5,815✔
335
Base.@constprop :aggressive function istril(A::LowerTriangular, k::Integer=0)
116✔
336
    k >= 0 && return true
155✔
337
    return _istril(A, k)
45✔
338
end
339
@inline function _istril(A::LowerTriangular, k)
340
    P = parent(A)
9✔
341
    m = size(A, 1)
9✔
342
    for j in max(1, k + 2):m
9✔
343
        all(iszero, view(P, j:min(j - k - 1, m), j)) || return false
81✔
344
    end
153✔
345
    return true
9✔
346
end
347
Base.@constprop :aggressive function istriu(A::UpperTriangular, k::Integer=0)
123✔
348
    k <= 0 && return true
14,160✔
349
    return _istriu(A, k)
45✔
350
end
351
@inline function _istriu(A::UpperTriangular, k)
352
    P = parent(A)
9✔
353
    m = size(A, 1)
9✔
354
    for j in 1:min(m, m + k - 1)
9✔
355
        all(iszero, view(P, max(1, j - k + 1):j, j)) || return false
81✔
356
    end
153✔
357
    return true
9✔
358
end
359
istril(A::Adjoint, k::Integer=0) = istriu(A.parent, -k)
6,836✔
360
istril(A::Transpose, k::Integer=0) = istriu(A.parent, -k)
1,487✔
361
istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k)
6,847✔
362
istriu(A::Transpose, k::Integer=0) = istril(A.parent, -k)
1,487✔
363

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

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

407
function triu!(A::UnitUpperTriangular, k::Integer=0)
10✔
408
    for i in diagind(A)
70✔
409
        A.data[i] = oneunit(eltype(A))
315✔
410
    end
595✔
411
    return triu!(UpperTriangular(A.data), k)
35✔
412
end
413

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

429
function tril!(A::LowerTriangular, k::Integer=0)
70✔
430
    n = size(A,1)
70✔
431
    if k < 0
70✔
432
        for j in 1:n, i in j:min(j-k-1,n)
28✔
433
            A.data[i, j] = zero(eltype(A))
756✔
434
        end
980✔
435
    end
436
    A
70✔
437
end
438

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

458
function tril!(A::UnitLowerTriangular, k::Integer=0)
10✔
459
    for i in diagind(A)
70✔
460
        A.data[i] = oneunit(eltype(A))
315✔
461
    end
595✔
462
    return tril!(LowerTriangular(A.data), k)
35✔
463
end
464

465
adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data))
4,335✔
466
adjoint(A::UpperTriangular) = LowerTriangular(adjoint(A.data))
4,903✔
467
adjoint(A::UnitLowerTriangular) = UnitUpperTriangular(adjoint(A.data))
4,108✔
468
adjoint(A::UnitUpperTriangular) = UnitLowerTriangular(adjoint(A.data))
4,014✔
469
transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data))
3,898✔
470
transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data))
4,182✔
471
transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data))
4,350✔
472
transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data))
3,993✔
473

474
transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L', false, true))
22✔
475
transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L', false, false))
22✔
476
transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U', false, true))
22✔
477
transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U', false, false))
22✔
478
adjoint!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true, true))
22✔
479
adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true, false))
22✔
480
adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true, true))
22✔
481
adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true, false))
22✔
482

483
diag(A::LowerTriangular) = diag(A.data)
69✔
484
diag(A::UnitLowerTriangular) = fill(oneunit(eltype(A)), size(A,1))
161✔
485
diag(A::UpperTriangular) = diag(A.data)
232✔
486
diag(A::UnitUpperTriangular) = fill(oneunit(eltype(A)), size(A,1))
323✔
487

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

510
# use broadcasting if the parents are strided, where we loop only over the triangular part
511
for TM in (:LowerTriangular, :UpperTriangular)
512
    @eval -(A::$TM{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast(-, A)
205✔
513
end
514

515
tr(A::LowerTriangular) = tr(A.data)
7✔
516
tr(A::UnitLowerTriangular) = size(A, 1) * oneunit(eltype(A))
7✔
517
tr(A::UpperTriangular) = tr(A.data)
7✔
518
tr(A::UnitUpperTriangular) = size(A, 1) * oneunit(eltype(A))
7✔
519

520
for T in (:UpperOrUnitUpperTriangular, :LowerOrUnitLowerTriangular)
521
    @eval @propagate_inbounds function copyto!(dest::$T, U::$T)
166✔
522
        if axes(dest) != axes(U)
13,664✔
523
            @invoke copyto!(dest::AbstractArray, U::AbstractArray)
20✔
524
        else
525
            _copyto!(dest, U)
6,827✔
526
        end
527
        return dest
6,825✔
528
    end
529
end
530

531
# copy and scale
532
for (T, UT) in ((:UpperTriangular, :UnitUpperTriangular), (:LowerTriangular, :UnitLowerTriangular))
533
    @eval @inline function _copyto!(A::$T, B::$T)
534
        @boundscheck checkbounds(A, axes(B)...)
6,257✔
535
        copytrito!(parent(A), parent(B), uplo_char(A))
6,257✔
536
        return A
6,257✔
537
    end
538
    @eval @inline function _copyto!(A::$UT, B::$T)
539
        for dind in diagind(A, IndexStyle(A))
8✔
540
            if A[dind] != B[dind]
16✔
541
                throw_nononeerror(typeof(A), B[dind], Tuple(dind)...)
2✔
542
            end
543
        end
26✔
544
        _copyto!($T(parent(A)), B)
2✔
545
        return A
2✔
546
    end
547
end
548
@inline function _copyto!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular)
549
    @boundscheck checkbounds(A, axes(B)...)
296✔
550
    n = size(B,1)
296✔
551
    B2 = Base.unalias(A, B)
296✔
552
    for j = 1:n
296✔
553
        for i = 1:j-1
2,518✔
554
            @inbounds parent(A)[i,j] = parent(B2)[i,j]
9,699✔
555
        end
17,176✔
556
        if A isa UpperTriangular # copy diagonal
2,518✔
557
            @inbounds parent(A)[j,j] = B2[j,j]
12✔
558
        end
559
    end
4,740✔
560
    return A
296✔
561
end
562
@inline function _copyto!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular)
563
    @boundscheck checkbounds(A, axes(B)...)
272✔
564
    n = size(B,1)
272✔
565
    B2 = Base.unalias(A, B)
272✔
566
    for j = 1:n
272✔
567
        if A isa LowerTriangular # copy diagonal
2,302✔
568
            @inbounds parent(A)[j,j] = B2[j,j]
12✔
569
        end
570
        for i = j+1:n
2,574✔
571
            @inbounds parent(A)[i,j] = parent(B2)[i,j]
8,835✔
572
        end
15,640✔
573
    end
4,332✔
574
    return A
272✔
575
end
576

577
_triangularize!(::UpperOrUnitUpperTriangular) = triu!
×
578
_triangularize!(::LowerOrUnitLowerTriangular) = tril!
×
579

580
@propagate_inbounds function copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular)
8✔
581
    if axes(dest) != axes(U)
27,968✔
582
        @invoke copyto!(dest::StridedMatrix, U::AbstractArray)
×
583
    else
584
        _copyto!(dest, U)
13,984✔
585
    end
586
    return dest
13,984✔
587
end
588
@propagate_inbounds function _copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular)
589
    copytrito!(dest, parent(U), U isa UpperOrUnitUpperTriangular ? 'U' : 'L')
665✔
590
    copytrito!(dest, U, U isa UpperOrUnitUpperTriangular ? 'L' : 'U')
665✔
591
    return dest
665✔
592
end
593
@propagate_inbounds function _copyto!(dest::StridedMatrix, U::UpperOrLowerTriangular{<:Any, <:StridedMatrix})
594
    U2 = Base.unalias(dest, U)
13,319✔
595
    copyto_unaliased!(dest, U2)
84,428✔
596
    return dest
13,319✔
597
end
598
# for strided matrices, we explicitly loop over the arrays to improve cache locality
599
# This fuses the copytrito! for the two halves
600
@inline function copyto_unaliased!(dest::StridedMatrix, U::UpperOrUnitUpperTriangular{<:Any, <:StridedMatrix})
601
    @boundscheck checkbounds(dest, axes(U)...)
9,465✔
602
    isunit = U isa UnitUpperTriangular
9,465✔
603
    for col in axes(dest,2)
9,465✔
604
        for row in 1:col-isunit
56,416✔
605
            @inbounds dest[row,col] = U.data[row,col]
248,454✔
606
        end
441,718✔
607
        for row in col+!isunit:size(U,1)
64,649✔
608
            @inbounds dest[row,col] = U[row,col]
209,136✔
609
        end
370,089✔
610
    end
103,373✔
611
    return dest
9,465✔
612
end
613
@inline function copyto_unaliased!(dest::StridedMatrix, L::LowerOrUnitLowerTriangular{<:Any, <:StridedMatrix})
614
    @boundscheck checkbounds(dest, axes(L)...)
3,854✔
615
    isunit = L isa UnitLowerTriangular
3,854✔
616
    for col in axes(dest,2)
3,854✔
617
        for row in 1:col-!isunit
28,000✔
618
            @inbounds dest[row,col] = L[row,col]
124,844✔
619
        end
224,294✔
620
        for row in col+isunit:size(L,1)
29,242✔
621
            @inbounds dest[row,col] = L.data[row,col]
136,622✔
622
        end
246,486✔
623
    end
52,152✔
624
    return dest
3,854✔
625
end
626

627
@inline _rscale_add!(A::AbstractTriangular, B::AbstractTriangular, C::Number, alpha::Number, beta::Number) =
620✔
628
    @stable_muladdmul _triscale!(A, B, C, MulAddMul(alpha, beta))
629
@inline _lscale_add!(A::AbstractTriangular, B::Number, C::AbstractTriangular, alpha::Number, beta::Number) =
26✔
630
    @stable_muladdmul _triscale!(A, B, C, MulAddMul(alpha, beta))
631

632
function checksize1(A, B)
633
    szA, szB = size(A), size(B)
2,084✔
634
    szA == szB || throw(DimensionMismatch(lazy"size of A, $szA, does not match size of B, $szB"))
2,092✔
635
    checksquare(B)
2,076✔
636
end
637

638
function _triscale!(A::UpperTriangular, B::UpperTriangular, c::Number, _add)
595✔
639
    n = checksize1(A, B)
872✔
640
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
871✔
641
    for j = 1:n
871✔
642
        for i = 1:j
3,119✔
643
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
9,165✔
644
        end
15,211✔
645
    end
5,367✔
646
    return A
871✔
647
end
648
function _triscale!(A::UpperTriangular, c::Number, B::UpperTriangular, _add)
534✔
649
    n = checksize1(A, B)
573✔
650
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
572✔
651
    for j = 1:n
572✔
652
        for i = 1:j
2,159✔
653
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
8,767✔
654
        end
15,375✔
655
    end
3,746✔
656
    return A
572✔
657
end
658
function _triscale!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular, c::Number, _add)
12✔
659
    n = checksize1(A, B)
14✔
660
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
13✔
661
    for j = 1:n
9✔
662
        @inbounds _modify!(_add, c, A, (j,j))
69✔
663
        for i = 1:(j - 1)
69✔
664
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
258✔
665
        end
456✔
666
    end
129✔
667
    return A
9✔
668
end
669
function _triscale!(A::UpperOrUnitUpperTriangular, c::Number, B::UnitUpperTriangular, _add)
12✔
670
    n = checksize1(A, B)
12✔
671
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
11✔
672
    for j = 1:n
7✔
673
        @inbounds _modify!(_add, c, A, (j,j))
63✔
674
        for i = 1:(j - 1)
63✔
675
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
252✔
676
        end
448✔
677
    end
119✔
678
    return A
7✔
679
end
680
function _triscale!(A::LowerTriangular, B::LowerTriangular, c::Number, _add)
1✔
681
    n = checksize1(A, B)
142✔
682
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
141✔
683
    for j = 1:n
141✔
684
        for i = j:n
585✔
685
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
2,651✔
686
        end
4,717✔
687
    end
1,029✔
688
    return A
141✔
689
end
690
function _triscale!(A::LowerTriangular, c::Number, B::LowerTriangular, _add)
250✔
691
    n = checksize1(A, B)
289✔
692
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
288✔
693
    for j = 1:n
288✔
694
        for i = j:n
1,224✔
695
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
5,396✔
696
        end
9,568✔
697
    end
2,160✔
698
    return A
288✔
699
end
700
function _triscale!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular, c::Number, _add)
12✔
701
    n = checksize1(A, B)
14✔
702
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
13✔
703
    for j = 1:n
9✔
704
        @inbounds _modify!(_add, c, A, (j,j))
69✔
705
        for i = (j + 1):n
78✔
706
            @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j))
258✔
707
        end
456✔
708
    end
129✔
709
    return A
9✔
710
end
711
function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriangular, _add)
12✔
712
    n = checksize1(A, B)
12✔
713
    iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta)
11✔
714
    for j = 1:n
7✔
715
        @inbounds _modify!(_add, c, A, (j,j))
63✔
716
        for i = (j + 1):n
70✔
717
            @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j))
252✔
718
        end
448✔
719
    end
119✔
720
    return A
7✔
721
end
722

723
function _trirdiv!(A::UpperTriangular, B::UpperOrUnitUpperTriangular, c::Number)
94✔
724
    n = checksize1(A, B)
94✔
725
    for j in 1:n
94✔
726
        for i in 1:j
405✔
727
            @inbounds A[i, j] = B[i, j] / c
1,666✔
728
        end
2,927✔
729
    end
716✔
730
    return A
94✔
731
end
732
function _trirdiv!(A::LowerTriangular, B::LowerOrUnitLowerTriangular, c::Number)
22✔
733
    n = checksize1(A, B)
22✔
734
    for j in 1:n
22✔
735
        for i in j:n
142✔
736
            @inbounds A[i, j] = B[i, j] / c
654✔
737
        end
1,166✔
738
    end
262✔
739
    return A
22✔
740
end
741
function _trildiv!(A::UpperTriangular, c::Number, B::UpperOrUnitUpperTriangular)
20✔
742
    n = checksize1(A, B)
20✔
743
    for j in 1:n
20✔
744
        for i in 1:j
138✔
745
            @inbounds A[i, j] = c \ B[i, j]
648✔
746
        end
1,158✔
747
    end
256✔
748
    return A
20✔
749
end
750
function _trildiv!(A::LowerTriangular, c::Number, B::LowerOrUnitLowerTriangular)
20✔
751
    n = checksize1(A, B)
20✔
752
    for j in 1:n
20✔
753
        for i in j:n
138✔
754
            @inbounds A[i, j] = c \ B[i, j]
648✔
755
        end
1,158✔
756
    end
256✔
757
    return A
20✔
758
end
759

760
rmul!(A::UpperOrLowerTriangular, c::Number) = @inline _triscale!(A, A, c, MulAddMul())
422✔
761
lmul!(c::Number, A::UpperOrLowerTriangular) = @inline _triscale!(A, c, A, MulAddMul())
78✔
762

763
function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector)
42✔
764
    require_one_based_indexing(x, y)
42✔
765
    m = size(A, 1)
42✔
766
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
767
    if iszero(m)
42✔
768
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
769
    end
770
    x₁ = x[1]
42✔
771
    r = dot(x₁, A[1,1], y[1])
42✔
772
    @inbounds for j in 2:m
42✔
773
        yj = y[j]
336✔
774
        if !iszero(yj)
336✔
775
            temp = adjoint(A[1,j]) * x₁
336✔
776
            @simd for i in 2:j
336✔
777
                temp += adjoint(A[i,j]) * x[i]
1,512✔
778
            end
779
            r += dot(temp, yj)
336✔
780
        end
781
    end
630✔
782
    return r
42✔
783
end
784
function dot(x::AbstractVector, A::UnitUpperTriangular, y::AbstractVector)
42✔
785
    require_one_based_indexing(x, y)
42✔
786
    m = size(A, 1)
42✔
787
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
788
    if iszero(m)
42✔
789
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
790
    end
791
    x₁ = first(x)
42✔
792
    r = dot(x₁, y[1])
42✔
793
    @inbounds for j in 2:m
42✔
794
        yj = y[j]
336✔
795
        if !iszero(yj)
336✔
796
            temp = adjoint(A[1,j]) * x₁
336✔
797
            @simd for i in 2:j-1
336✔
798
                temp += adjoint(A[i,j]) * x[i]
1,176✔
799
            end
800
            r += dot(temp, yj)
420✔
801
            r += dot(x[j], yj)
336✔
802
        end
803
    end
630✔
804
    return r
42✔
805
end
806
function dot(x::AbstractVector, A::LowerTriangular, y::AbstractVector)
42✔
807
    require_one_based_indexing(x, y)
42✔
808
    m = size(A, 1)
42✔
809
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
810
    if iszero(m)
42✔
811
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
812
    end
813
    r = zero(typeof(dot(first(x), first(A), first(y))))
42✔
814
    @inbounds for j in 1:m
42✔
815
        yj = y[j]
378✔
816
        if !iszero(yj)
378✔
817
            temp = adjoint(A[j,j]) * x[j]
378✔
818
            @simd for i in j+1:m
420✔
819
                temp += adjoint(A[i,j]) * x[i]
1,512✔
820
            end
821
            r += dot(temp, yj)
378✔
822
        end
823
    end
714✔
824
    return r
42✔
825
end
826
function dot(x::AbstractVector, A::UnitLowerTriangular, y::AbstractVector)
42✔
827
    require_one_based_indexing(x, y)
42✔
828
    m = size(A, 1)
42✔
829
    (length(x) == m == length(y)) || throw(DimensionMismatch())
42✔
830
    if iszero(m)
42✔
831
        return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))
×
832
    end
833
    r = zero(typeof(dot(first(x), first(y))))
42✔
834
    @inbounds for j in 1:m
42✔
835
        yj = y[j]
378✔
836
        if !iszero(yj)
378✔
837
            temp = x[j]
378✔
838
            @simd for i in j+1:m
420✔
839
                temp += adjoint(A[i,j]) * x[i]
1,876✔
840
            end
841
            r += dot(temp, yj)
491✔
842
        end
843
    end
714✔
844
    return r
42✔
845
end
846

847
fillstored!(A::LowerTriangular, x)     = (fillband!(A.data, x, 1-size(A,1), 0); A)
1✔
848
fillstored!(A::UnitLowerTriangular, x) = (fillband!(A.data, x, 1-size(A,1), -1); A)
1✔
849
fillstored!(A::UpperTriangular, x)     = (fillband!(A.data, x, 0, size(A,2)-1); A)
1✔
850
fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); A)
×
851

852
# Binary operations
853
+(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data + B.data)
17✔
854
+(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data + B.data)
11✔
855
+(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data + triu(B.data, 1) + I)
6✔
856
+(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data + tril(B.data, -1) + I)
1✔
857
+(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) + B.data + I)
6✔
858
+(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) + B.data + I)
1✔
859
+(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I)
1✔
860
+(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I)
1✔
861
+(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) + copyto!(similar(parent(B)), B)
406✔
862

863
-(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data - B.data)
19✔
864
-(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data - B.data)
7✔
865
-(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data - triu(B.data, 1) - I)
2✔
866
-(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data - tril(B.data, -1) - I)
1✔
867
-(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) - B.data + I)
2✔
868
-(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) - B.data + I)
1✔
869
-(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) - triu(B.data, 1))
1✔
870
-(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) - tril(B.data, -1))
1✔
871
-(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B)
396✔
872

873
# use broadcasting if the parents are strided, where we loop only over the triangular part
874
for op in (:+, :-)
875
    for TM1 in (:LowerTriangular, :UnitLowerTriangular), TM2 in (:LowerTriangular, :UnitLowerTriangular)
876
        @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B)
630✔
877
    end
878
    for TM1 in (:UpperTriangular, :UnitUpperTriangular), TM2 in (:UpperTriangular, :UnitUpperTriangular)
879
        @eval $op(A::$TM1{<:Any, <:StridedMaybeAdjOrTransMat}, B::$TM2{<:Any, <:StridedMaybeAdjOrTransMat}) = broadcast($op, A, B)
857✔
880
    end
881
end
882

883
function kron(A::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
51✔
884
    C = UpperTriangular(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B)))
51✔
885
    return kron!(C, A, B)
51✔
886
end
887
function kron(A::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
51✔
888
    C = LowerTriangular(Matrix{promote_op(*, eltype(A), eltype(B))}(undef, _kronsize(A, B)))
51✔
889
    return kron!(C, A, B)
51✔
890
end
891

892
function kron!(C::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, A::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::UpperTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
893
    size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!"))
51✔
894
    _triukron!(C.data, A.data, B.data)
51✔
895
    return C
51✔
896
end
897
function kron!(C::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, A::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat}, B::LowerTriangular{<:Number,<:StridedMaybeAdjOrTransMat})
898
    size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!"))
51✔
899
    _trilkron!(C.data, A.data, B.data)
51✔
900
    return C
51✔
901
end
902

903
function _triukron!(C, A, B)
51✔
904
    n_A = size(A, 1)
51✔
905
    n_B = size(B, 1)
51✔
906
    @inbounds for j = 1:n_A
51✔
907
        jnB = (j - 1) * n_B
445✔
908
        for i = 1:(j-1)
445✔
909
            Aij = A[i, j]
1,766✔
910
            inB = (i - 1) * n_B
1,766✔
911
            for l = 1:n_B
1,766✔
912
                for k = 1:l
15,880✔
913
                    C[inB+k, jnB+l] = Aij * B[k, l]
79,386✔
914
                end
142,892✔
915
                for k = 1:(l-1)
15,880✔
916
                    C[inB+l, jnB+k] = zero(eltype(C))
63,506✔
917
                end
112,898✔
918
            end
29,994✔
919
        end
3,138✔
920
        Ajj = A[j, j]
445✔
921
        for l = 1:n_B
445✔
922
            for k = 1:l
3,977✔
923
                C[jnB+k, jnB+l] = Ajj * B[k, l]
19,857✔
924
            end
35,737✔
925
        end
7,509✔
926
    end
445✔
927
end
928

929
function _trilkron!(C, A, B)
51✔
930
    n_A = size(A, 1)
51✔
931
    n_B = size(B, 1)
51✔
932
    @inbounds for j = 1:n_A
51✔
933
        jnB = (j - 1) * n_B
445✔
934
        Ajj = A[j, j]
445✔
935
        for l = 1:n_B
445✔
936
            for k = l:n_B
3,977✔
937
                C[jnB+k, jnB+l] = Ajj * B[k, l]
19,857✔
938
            end
35,737✔
939
        end
7,509✔
940
        for i = (j+1):n_A
496✔
941
            Aij = A[i, j]
1,766✔
942
            inB = (i - 1) * n_B
1,766✔
943
            for l = 1:n_B
1,766✔
944
                for k = l:n_B
15,880✔
945
                    C[inB+k, jnB+l] = Aij * B[k, l]
79,386✔
946
                end
142,892✔
947
                for k = (l+1):n_B
17,646✔
948
                    C[inB+l, jnB+k] = zero(eltype(C))
63,506✔
949
                end
112,898✔
950
            end
29,994✔
951
        end
3,138✔
952
    end
445✔
953
end
954

955
######################
956
# BlasFloat routines #
957
######################
958

959
# which triangle to use of the underlying data
960
uplo_char(::UpperOrUnitUpperTriangular) = 'U'
×
961
uplo_char(::LowerOrUnitLowerTriangular) = 'L'
×
962
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:AdjOrTrans}) = 'L'
×
963
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:AdjOrTrans}) = 'U'
×
964
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'U'
×
965
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'L'
×
966
uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'U'
×
967
uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'L'
×
968

969
isunit_char(::UpperTriangular) = 'N'
×
970
isunit_char(::UnitUpperTriangular) = 'U'
×
971
isunit_char(::LowerTriangular) = 'N'
×
972
isunit_char(::UnitLowerTriangular) = 'U'
×
973

974
# generic fallback for AbstractTriangular matrices outside of the four subtypes provided here
975
_trimul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) =
×
976
    lmul!(A, copyto!(C, B))
977
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) =
10✔
978
    lmul!(A, copyto!(C, B))
979
_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) =
4✔
980
    rmul!(copyto!(C, A), B)
981
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractTriangular) =
×
982
    lmul!(A, copyto!(C, B))
983
# redirect for UpperOrLowerTriangular
984
_trimul!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVector) =
3,430✔
985
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
986
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractMatrix) =
5,600✔
987
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
988
_trimul!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) =
6,433✔
989
    generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
990
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) =
11,361✔
991
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
992
# disambiguation with AbstractTriangular
993
_trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) =
×
994
    generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
995
_trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) =
×
996
    generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
997

998
function lmul!(A::AbstractTriangular, B::AbstractVecOrMat)
556✔
999
    if istriu(A)
804✔
1000
        _trimul!(B, UpperTriangular(A), B)
282✔
1001
    else
1002
        _trimul!(B, LowerTriangular(A), B)
522✔
1003
    end
1004
end
1005
function rmul!(A::AbstractMatrix, B::AbstractTriangular)
516✔
1006
    if istriu(B)
560✔
1007
        _trimul!(A, A, UpperTriangular(B))
280✔
1008
    else
1009
        _trimul!(A, A, LowerTriangular(B))
280✔
1010
    end
1011
end
1012

1013
for TC in (:AbstractVector, :AbstractMatrix)
1014
    @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number)
1015
        if isone(alpha) && iszero(beta)
2,679✔
1016
            return _trimul!(C, A, B)
2,544✔
1017
        else
1018
            return @stable_muladdmul generic_matvecmul!(C, 'N', A, B, MulAddMul(alpha, beta))
211✔
1019
        end
1020
    end
1021
end
1022
for (TA, TB) in ((:AbstractTriangular, :AbstractMatrix),
1023
                    (:AbstractMatrix, :AbstractTriangular),
1024
                    (:AbstractTriangular, :AbstractTriangular)
1025
                )
1026
    @eval @inline function _mul!(C::AbstractMatrix, A::$TA, B::$TB, alpha::Number, beta::Number)
1027
        if isone(alpha) && iszero(beta)
21,455✔
1028
            return _trimul!(C, A, B)
21,266✔
1029
        else
1030
            return generic_matmatmul!(C, 'N', 'N', A, B, alpha, beta)
189✔
1031
        end
1032
    end
1033
end
1034

1035
ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = _ldiv!(C, A, B)
15,768✔
1036
# generic fallback for AbstractTriangular, directs to 2-arg [l/r]div!
1037
_ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) =
×
1038
    ldiv!(A, copyto!(C, B))
1039
_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) =
×
1040
    rdiv!(copyto!(C, A), B)
1041
# redirect for UpperOrLowerTriangular to generic_*div!
1042
_ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) =
28,376✔
1043
    generic_trimatdiv!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B)
1044
_rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) =
7,360✔
1045
    generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B)))
1046

1047
function ldiv!(A::AbstractTriangular, B::AbstractVecOrMat)
6,026✔
1048
    if istriu(A)
12,742✔
1049
        _ldiv!(B, UpperTriangular(A), B)
7,113✔
1050
    else
1051
        _ldiv!(B, LowerTriangular(A), B)
5,495✔
1052
    end
1053
end
1054
function rdiv!(A::AbstractMatrix, B::AbstractTriangular)
104✔
1055
    if istriu(B)
1,956✔
1056
        _rdiv!(A, A, UpperTriangular(B))
1,844✔
1057
    else
1058
        _rdiv!(A, A, LowerTriangular(B))
112✔
1059
    end
1060
end
1061

1062
# preserve triangular structure in in-place multiplication/division
1063
for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular),
1064
                        (:UpperTriangular, :UpperTriangular, :UnitUpperTriangular),
1065
                        (:UpperTriangular, :UnitUpperTriangular, :UpperTriangular),
1066
                        (:UnitUpperTriangular, :UnitUpperTriangular, :UnitUpperTriangular),
1067
                        (:LowerTriangular, :LowerTriangular, :LowerTriangular),
1068
                        (:LowerTriangular, :LowerTriangular, :UnitLowerTriangular),
1069
                        (:LowerTriangular, :UnitLowerTriangular, :LowerTriangular),
1070
                        (:UnitLowerTriangular, :UnitLowerTriangular, :UnitLowerTriangular))
1071
    @eval begin
1072
        function _trimul!(C::$cty, A::$aty, B::$bty)
1073
            _trimul!(parent(C), A, B)
1,412✔
1074
            return C
1,412✔
1075
        end
1076
        function _ldiv!(C::$cty, A::$aty, B::$bty)
1077
            _ldiv!(parent(C), A, B)
666✔
1078
            return C
666✔
1079
        end
1080
        function _rdiv!(C::$cty, A::$aty, B::$bty)
1081
            _rdiv!(parent(C), A, B)
1,705✔
1082
            return C
1,705✔
1083
        end
1084
    end
1085
end
1086

1087
for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'),
1088
                            (:UnitLowerTriangular, 'L', 'U'),
1089
                            (:UpperTriangular, 'U', 'N'),
1090
                            (:UnitUpperTriangular, 'U', 'U'))
1091
    @eval begin
1092
        # Matrix inverse
1093
        inv!(A::$t{T,S}) where {T<:BlasFloat,S<:StridedMatrix} =
16✔
1094
            $t{T,S}(LAPACK.trtri!($uploc, $isunitc, A.data))
1095

1096
        function inv(A::$t{T}) where {T}
357✔
1097
            S = typeof(inv(oneunit(T)))
533✔
1098
            if S <: BlasFloat || S === T # i.e. A is unitless
533✔
1099
                $t(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A))))
464✔
1100
            else
1101
                J = (one(T)*I)(size(A, 1))
650✔
1102
                $t(ldiv!(similar(A, S, size(A)), A, J))
69✔
1103
            end
1104
        end
1105

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

1110
        # Condition numbers
1111
        function cond(A::$t{<:BlasFloat,<:StridedMatrix}, p::Real=2)
144✔
1112
            checksquare(A)
144✔
1113
            if p == 1
144✔
1114
                return inv(LAPACK.trcon!('O', $uploc, $isunitc, A.data))
64✔
1115
            elseif p == Inf
80✔
1116
                return inv(LAPACK.trcon!('I', $uploc, $isunitc, A.data))
64✔
1117
            else # use fallback
1118
                return cond(copyto!(similar(parent(A)), A), p)
16✔
1119
            end
1120
        end
1121
    end
1122
end
1123

1124
# multiplication
1125
generic_trimatmul!(c::StridedVector{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, b::AbstractVector{T}) where {T<:BlasFloat} =
952✔
1126
    BLAS.trmv!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, c === b ? c : copyto!(c, b))
1127
generic_trimatmul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} =
5,671✔
1128
    BLAS.trmm!('L', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), A, C === B ? C : copyto!(C, B))
1129
generic_mattrimul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} =
1,664✔
1130
    BLAS.trmm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A))
1131
# division
1132
generic_trimatdiv!(C::StridedVecOrMat{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} =
4,758✔
1133
    LAPACK.trtrs!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, C === B ? C : copyto!(C, B))
1134
generic_mattridiv!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} =
2,500✔
1135
    BLAS.trsm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A))
1136

1137
errorbounds(A::AbstractTriangular{T}, X::AbstractVecOrMat{T}, B::AbstractVecOrMat{T}) where {T<:Union{BigFloat,Complex{BigFloat}}} =
×
1138
    error("not implemented yet! Please submit a pull request.")
1139
function errorbounds(A::AbstractTriangular{TA}, X::AbstractVecOrMat{TX}, B::AbstractVecOrMat{TB}) where {TA<:Number,TX<:Number,TB<:Number}
128✔
1140
    TAXB = promote_type(TA, TB, TX, Float32)
128✔
1141
    errorbounds(convert(AbstractMatrix{TAXB}, A), convert(AbstractArray{TAXB}, X), convert(AbstractArray{TAXB}, B))
128✔
1142
end
1143

1144
# Eigensystems
1145
## Notice that trecv works for quasi-triangular matrices and therefore the lower sub diagonal must be zeroed before calling the subroutine
1146
function eigvecs(A::UpperTriangular{<:BlasFloat,<:StridedMatrix})
1✔
1147
    LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
10✔
1148
end
1149
function eigvecs(A::UnitUpperTriangular{<:BlasFloat,<:StridedMatrix})
5✔
1150
    for i = 1:size(A, 1)
5✔
1151
        A.data[i,i] = 1
45✔
1152
    end
85✔
1153
    LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
5✔
1154
end
1155
function eigvecs(A::LowerTriangular{<:BlasFloat,<:StridedMatrix})
9✔
1156
    LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)'))
9✔
1157
end
1158
function eigvecs(A::UnitLowerTriangular{<:BlasFloat,<:StridedMatrix})
5✔
1159
    for i = 1:size(A, 1)
5✔
1160
        A.data[i,i] = 1
45✔
1161
    end
85✔
1162
    LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)'))
5✔
1163
end
1164

1165
####################
1166
# Generic routines #
1167
####################
1168

1169
for (t, unitt) in ((UpperTriangular, UnitUpperTriangular),
1170
                   (LowerTriangular, UnitLowerTriangular))
1171
    tstrided = t{<:Any, <:StridedMaybeAdjOrTransMat}
1172
    @eval begin
1173
        (*)(A::$t, x::Number) = $t(A.data*x)
4✔
1174
        function (*)(A::$tstrided, x::Number)
20✔
1175
            eltype_dest = promote_op(*, eltype(A), typeof(x))
76✔
1176
            dest = $t(similar(parent(A), eltype_dest))
76✔
1177
            _triscale!(dest, x, A, MulAddMul())
76✔
1178
        end
1179

1180
        function (*)(A::$unitt, x::Number)
26✔
1181
            B = $t(A.data)*x
26✔
1182
            for i = 1:size(A, 1)
26✔
1183
                B.data[i,i] = x
192✔
1184
            end
358✔
1185
            return B
26✔
1186
        end
1187

1188
        (*)(x::Number, A::$t) = $t(x*A.data)
×
1189
        function (*)(x::Number, A::$tstrided)
458✔
1190
            eltype_dest = promote_op(*, typeof(x), eltype(A))
706✔
1191
            dest = $t(similar(parent(A), eltype_dest))
706✔
1192
            _triscale!(dest, x, A, MulAddMul())
706✔
1193
        end
1194

1195
        function (*)(x::Number, A::$unitt)
42✔
1196
            B = x*$t(A.data)
42✔
1197
            for i = 1:size(A, 1)
42✔
1198
                B.data[i,i] = x
336✔
1199
            end
630✔
1200
            return B
42✔
1201
        end
1202

1203
        (/)(A::$t, x::Number) = $t(A.data/x)
2✔
1204
        function (/)(A::$tstrided, x::Number)
20✔
1205
            eltype_dest = promote_op(/, eltype(A), typeof(x))
116✔
1206
            dest = $t(similar(parent(A), eltype_dest))
116✔
1207
            _trirdiv!(dest, A,  x)
116✔
1208
        end
1209

1210
        function (/)(A::$unitt, x::Number)
20✔
1211
            B = $t(A.data)/x
20✔
1212
            invx = inv(x)
20✔
1213
            for i = 1:size(A, 1)
20✔
1214
                B.data[i,i] = invx
138✔
1215
            end
256✔
1216
            return B
20✔
1217
        end
1218

1219
        (\)(x::Number, A::$t) = $t(x\A.data)
×
1220
        function (\)(x::Number, A::$tstrided)
20✔
1221
            eltype_dest = promote_op(\, typeof(x), eltype(A))
40✔
1222
            dest = $t(similar(parent(A), eltype_dest))
40✔
1223
            _trildiv!(dest, x, A)
40✔
1224
        end
1225

1226
        function (\)(x::Number, A::$unitt)
20✔
1227
            B = x\$t(A.data)
20✔
1228
            invx = inv(x)
20✔
1229
            for i = 1:size(A, 1)
20✔
1230
                B.data[i,i] = invx
138✔
1231
            end
256✔
1232
            return B
20✔
1233
        end
1234
    end
1235
end
1236

1237
## Generic triangular multiplication
1238
function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat)
13,546✔
1239
    require_one_based_indexing(C, A, B)
13,546✔
1240
    m, n = size(B, 1), size(B, 2)
13,546✔
1241
    N = size(A, 1)
13,546✔
1242
    if m != N
13,546✔
1243
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $(size(A,1)), has size $m"))
2,496✔
1244
    end
1245
    mc, nc = size(C, 1), size(C, 2)
11,050✔
1246
    if mc != N || nc != n
22,100✔
1247
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($N,$n)"))
×
1248
    end
1249
    oA = oneunit(eltype(A))
11,050✔
1250
    unit = isunitc == 'U'
11,050✔
1251
    @inbounds if uploc == 'U'
11,050✔
1252
        if tfun === identity
5,574✔
1253
            for j in 1:n
2,581✔
1254
                for i in 1:m
17,590✔
1255
                    Cij = (unit ? oA : A[i,i]) * B[i,j]
158,539✔
1256
                    for k in i + 1:m
175,429✔
1257
                        Cij += A[i,k] * B[k,j]
636,997✔
1258
                    end
1,130,945✔
1259
                    C[i,j] = Cij
157,839✔
1260
                end
298,088✔
1261
            end
32,599✔
1262
        else # tfun in (transpose, adjoint)
1263
            for j in 1:n
2,993✔
1264
                for i in m:-1:1
46,909✔
1265
                    Cij = (unit ? oA : tfun(A[i,i])) * B[i,j]
211,616✔
1266
                    for k in 1:i - 1
210,266✔
1267
                        Cij += tfun(A[k,i]) * B[k,j]
842,474✔
1268
                    end
1,492,737✔
1269
                    C[i,j] = Cij
210,266✔
1270
                end
397,077✔
1271
            end
43,917✔
1272
        end
1273
    else # uploc == 'L'
1274
        if tfun === identity
5,476✔
1275
            for j in 1:n
2,405✔
1276
                for i in m:-1:1
33,385✔
1277
                    Cij = (unit ? oA : A[i,i]) * B[i,j]
150,210✔
1278
                    for k in 1:i - 1
148,110✔
1279
                        Cij += A[i,k] * B[k,j]
595,931✔
1280
                    end
1,052,121✔
1281
                    C[i,j] = Cij
148,110✔
1282
                end
279,451✔
1283
            end
16,769✔
1284
        else # tfun in (transpose, adjoint)
1285
            for j in 1:n
3,071✔
1286
                for i in 1:m
23,719✔
1287
                    Cij = (unit ? oA : tfun(A[i,i])) * B[i,j]
214,302✔
1288
                    for k in i + 1:m
233,971✔
1289
                        Cij += tfun(A[k,i]) * B[k,j]
841,113✔
1290
                    end
1,479,493✔
1291
                    C[i,j] = Cij
210,252✔
1292
                end
396,785✔
1293
            end
23,719✔
1294
        end
1295
    end
1296
    return C
11,050✔
1297
end
1298
# conjugate cases
1299
function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat)
22✔
1300
    A = parent(xA)
22✔
1301
    require_one_based_indexing(C, A, B)
22✔
1302
    m, n = size(B, 1), size(B, 2)
22✔
1303
    N = size(A, 1)
22✔
1304
    if m != N
22✔
1305
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $(size(A,1)), has size $m"))
×
1306
    end
1307
    mc, nc = size(C, 1), size(C, 2)
22✔
1308
    if mc != N || nc != n
44✔
1309
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($N,$n)"))
×
1310
    end
1311
    oA = oneunit(eltype(A))
22✔
1312
    unit = isunitc == 'U'
22✔
1313
    @inbounds if uploc == 'U'
22✔
1314
        for j in 1:n
11✔
1315
            for i in 1:m
11✔
1316
                Cij = (unit ? oA : conj(A[i,i])) * B[i,j]
4,040✔
1317
                for k in i + 1:m
4,051✔
1318
                    Cij += conj(A[i,k]) * B[k,j]
1,998,147✔
1319
                end
3,992,265✔
1320
                C[i,j] = Cij
4,040✔
1321
            end
8,069✔
1322
        end
11✔
1323
    else # uploc == 'L'
1324
        for j in 1:n
11✔
1325
            for i in m:-1:1
20✔
1326
                Cij = (unit ? oA : conj(A[i,i])) * B[i,j]
4,040✔
1327
                for k in 1:i - 1
4,040✔
1328
                    Cij += conj(A[i,k]) * B[k,j]
1,998,147✔
1329
                end
3,992,265✔
1330
                C[i,j] = Cij
4,040✔
1331
            end
8,069✔
1332
        end
11✔
1333
    end
1334
    return C
22✔
1335
end
1336

1337
function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix)
4,769✔
1338
    require_one_based_indexing(C, A, B)
4,769✔
1339
    m, n = size(A, 1), size(A, 2)
4,769✔
1340
    N = size(B, 1)
4,769✔
1341
    if n != N
4,769✔
1342
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $N"))
2,496✔
1343
    end
1344
    mc, nc = size(C, 1), size(C, 2)
2,273✔
1345
    if mc != m || nc != N
4,546✔
1346
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($m,$N)"))
×
1347
    end
1348
    oB = oneunit(eltype(B))
2,273✔
1349
    unit = isunitc == 'U'
2,273✔
1350
    @inbounds if uploc == 'U'
2,273✔
1351
        if tfun === identity
1,079✔
1352
            for i in 1:m
444✔
1353
                for j in n:-1:1
7,428✔
1354
                    Cij = A[i,j] * (unit ? oB : B[j,j])
51,454✔
1355
                    for k in 1:j - 1
33,344✔
1356
                        Cij += A[i,k] * B[k,j]
137,210✔
1357
                    end
244,790✔
1358
                    C[i,j] = Cij
33,344✔
1359
                end
62,974✔
1360
            end
6,984✔
1361
        else # tfun in (transpose, adjoint)
1362
            for i in 1:m
635✔
1363
                for j in 1:n
5,613✔
1364
                    Cij = A[i,j] * (unit ? oB : tfun(B[j,j]))
70,761✔
1365
                    for k in j + 1:n
56,232✔
1366
                        Cij += A[i,k] * tfun(B[j,k])
205,749✔
1367
                    end
366,492✔
1368
                    C[i,j] = Cij
50,619✔
1369
                end
95,625✔
1370
            end
10,591✔
1371
        end
1372
    else # uploc == 'L'
1373
        if tfun === identity
1,194✔
1374
            for i in 1:m
452✔
1375
                for j in 1:n
3,836✔
1376
                    Cij = A[i,j] * (unit ? oB : B[j,j])
51,774✔
1377
                    for k in j + 1:n
37,840✔
1378
                        Cij += A[i,k] * B[k,j]
137,856✔
1379
                    end
245,544✔
1380
                    C[i,j] = Cij
34,004✔
1381
                end
64,172✔
1382
            end
3,836✔
1383
        else # tfun in (transpose, adjoint)
1384
            for i in 1:m
742✔
1385
                for j in n:-1:1
12,300✔
1386
                    Cij = A[i,j] * (unit ? oB : tfun(B[j,j]))
73,452✔
1387
                    for k in 1:j - 1
53,310✔
1388
                        Cij += A[i,k] * tfun(B[j,k])
211,140✔
1389
                    end
375,120✔
1390
                    C[i,j] = Cij
53,310✔
1391
                end
100,470✔
1392
            end
6,150✔
1393
        end
1394
    end
1395
    return C
2,273✔
1396
end
1397
# conjugate cases
1398
function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans)
×
1399
    B = parent(xB)
×
1400
    require_one_based_indexing(C, A, B)
×
1401
    m, n = size(A, 1), size(A, 2)
×
1402
    N = size(B, 1)
×
1403
    if n != N
×
1404
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $N"))
×
1405
    end
1406
    mc, nc = size(C, 1), size(C, 2)
×
1407
    if mc != m || nc != N
×
1408
        throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($m,$N)"))
×
1409
    end
1410
    oB = oneunit(eltype(B))
×
1411
    unit = isunitc == 'U'
×
1412
    @inbounds if uploc == 'U'
×
1413
        for i in 1:m
×
1414
            for j in n:-1:1
×
1415
                Cij = A[i,j] * (unit ? oB : conj(B[j,j]))
×
1416
                for k in 1:j - 1
×
1417
                    Cij += A[i,k] * conj(B[k,j])
×
1418
                end
×
1419
                C[i,j] = Cij
×
1420
            end
×
1421
        end
×
1422
    else # uploc == 'L'
1423
        for i in 1:m
×
1424
            for j in 1:n
×
1425
                Cij = A[i,j] * (unit ? oB : conj(B[j,j]))
×
1426
                for k in j + 1:n
×
1427
                    Cij += A[i,k] * conj(B[k,j])
×
1428
                end
×
1429
                C[i,j] = Cij
×
1430
            end
×
1431
        end
×
1432
    end
1433
    return C
×
1434
end
1435

1436
#Generic solver using naive substitution
1437

1438
@inline _ustrip(a) = oneunit(a) \ a
322,882✔
1439
@inline _ustrip(a::Union{AbstractFloat,Integer,Complex,Rational}) = a
2,608,215✔
1440

1441
# manually hoisting b[j] significantly improves performance as of Dec 2015
1442
# manually eliding bounds checking significantly improves performance as of Dec 2015
1443
# replacing repeated references to A.data[j,j] with [Ajj = A.data[j,j] and references to Ajj]
1444
# does not significantly impact performance as of Dec 2015
1445
# in the transpose and conjugate transpose naive substitution variants,
1446
# accumulating in z rather than b[j,k] significantly improves performance as of Dec 2015
1447
function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat)
19,317✔
1448
    require_one_based_indexing(C, A, B)
19,317✔
1449
    mA, nA = size(A)
19,317✔
1450
    m, n = size(B, 1), size(B,2)
19,317✔
1451
    if nA != m
19,317✔
1452
        throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal"))
236✔
1453
    end
1454
    if size(C) != size(B)
38,162✔
1455
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of right hand side, $(size(B))"))
×
1456
    end
1457
    iszero(mA) && return C
19,081✔
1458
    oA = oneunit(eltype(A))
19,075✔
1459
    @inbounds if uploc == 'U'
19,075✔
1460
        if isunitc == 'N'
10,177✔
1461
            if tfun === identity
8,842✔
1462
                for k in 1:n
6,051✔
1463
                    amm = A[m,m]
24,493✔
1464
                    iszero(amm) && throw(SingularException(m))
24,423✔
1465
                    Cm = C[m,k] = amm \ B[m,k]
24,343✔
1466
                    # fill C-column
1467
                    for i in m-1:-1:1
48,621✔
1468
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm
203,759✔
1469
                    end
383,368✔
1470
                    for j in m-1:-1:1
48,621✔
1471
                        ajj = A[j,j]
204,383✔
1472
                        iszero(ajj) && throw(SingularException(j))
203,759✔
1473
                        Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
203,759✔
1474
                        for i in j-1:-1:1
383,433✔
1475
                            C[i,k] -= _ustrip(A[i,j]) * Cj
851,569✔
1476
                        end
1,523,529✔
1477
                    end
383,368✔
1478
                end
42,715✔
1479
            else # tfun in (adjoint, transpose)
1480
                for k in 1:n
2,791✔
1481
                    for j in 1:m
14,635✔
1482
                        ajj = A[j,j]
136,814✔
1483
                        iszero(ajj) && throw(SingularException(j))
135,434✔
1484
                        Bj = B[j,k]
135,434✔
1485
                        for i in 1:j-1
135,434✔
1486
                            Bj -= tfun(A[i,j]) * C[i,k]
686,463✔
1487
                        end
1,003,643✔
1488
                        C[j,k] = tfun(ajj) \ Bj
135,434✔
1489
                    end
256,233✔
1490
                end
26,479✔
1491
            end
1492
        else # isunitc == 'U'
1493
            if tfun === identity
1,335✔
1494
                for k in 1:n
743✔
1495
                    Cm = C[m,k] = oA \ B[m,k]
5,044✔
1496
                    # fill C-column
1497
                    for i in m-1:-1:1
10,085✔
1498
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm
41,671✔
1499
                    end
78,298✔
1500
                    for j in m-1:-1:1
10,085✔
1501
                        Cj = C[j,k]
41,671✔
1502
                        for i in 1:j-1
41,671✔
1503
                            C[i,k] -= _ustrip(A[i,j]) * Cj
151,868✔
1504
                        end
267,109✔
1505
                    end
78,298✔
1506
                end
5,044✔
1507
            else # tfun in (adjoint, transpose)
1508
                for k in 1:n
592✔
1509
                    for j in 1:m
1,840✔
1510
                        Bj = B[j,k]
16,920✔
1511
                        for i in 1:j-1
16,920✔
1512
                            Bj -= tfun(A[i,j]) * C[i,k]
87,956✔
1513
                        end
123,880✔
1514
                        C[j,k] = oA \ Bj
16,920✔
1515
                    end
32,000✔
1516
                end
1,840✔
1517
            end
1518
        end
1519
    else # uploc == 'L'
1520
        if isunitc == 'N'
8,898✔
1521
            if tfun === identity
7,567✔
1522
                for k in 1:n
5,127✔
1523
                    a11 = A[1,1]
20,835✔
1524
                    iszero(a11) && throw(SingularException(1))
20,766✔
1525
                    C1 = C[1,k] = a11 \ B[1,k]
20,766✔
1526
                    # fill C-column
1527
                    for i in 2:m
20,766✔
1528
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1
175,740✔
1529
                    end
330,714✔
1530
                    for j in 2:m
20,766✔
1531
                        ajj = A[j,j]
176,361✔
1532
                        iszero(ajj) && throw(SingularException(j))
175,740✔
1533
                        Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
175,740✔
1534
                        for i in j+1:m
196,506✔
1535
                            C[i,k] -= _ustrip(A[i,j]) * Cj
745,988✔
1536
                        end
1,337,002✔
1537
                    end
330,714✔
1538
                end
20,766✔
1539
            else # tfun in (adjoint, transpose)
1540
                for k in 1:n
2,440✔
1541
                    for j in m:-1:1
26,062✔
1542
                        ajj = A[j,j]
121,584✔
1543
                        iszero(ajj) && throw(SingularException(j))
120,204✔
1544
                        Bj = B[j,k]
120,229✔
1545
                        for i in j+1:m
133,235✔
1546
                            Bj -= tfun(A[i,j]) * C[i,k]
618,689✔
1547
                        end
884,449✔
1548
                        C[j,k] = tfun(ajj) \ Bj
120,204✔
1549
                    end
227,377✔
1550
                end
13,031✔
1551
            end
1552
        else # isunitc == 'U'
1553
            if tfun === identity
1,331✔
1554
                for k in 1:n
739✔
1555
                    C1 = C[1,k] = oA \ B[1,k]
5,004✔
1556
                    # fill C-column
1557
                    for i in 2:m
5,004✔
1558
                        C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1
41,311✔
1559
                    end
77,618✔
1560
                    for j in 2:m
5,004✔
1561
                        Cj = C[j,k]
41,311✔
1562
                        for i in j+1:m
46,315✔
1563
                            C[i,k] -= _ustrip(A[i,j]) * Cj
150,428✔
1564
                        end
264,549✔
1565
                    end
77,618✔
1566
                end
5,004✔
1567
            else # tfun in (adjoint, transpose)
1568
                for k in 1:n
592✔
1569
                    for j in m:-1:1
3,680✔
1570
                        Bj = B[j,k]
16,920✔
1571
                        for i in j+1:m
18,760✔
1572
                            Bj -= tfun(A[i,j]) * C[i,k]
87,956✔
1573
                        end
123,880✔
1574
                        C[j,k] = oA \ Bj
16,920✔
1575
                    end
32,000✔
1576
                end
1,840✔
1577
            end
1578
        end
1579
    end
1580
    return C
18,995✔
1581
end
1582
# conjugate cases
1583
function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat)
166✔
1584
    A = parent(xA)
166✔
1585
    require_one_based_indexing(C, A, B)
166✔
1586
    mA, nA = size(A)
166✔
1587
    m, n = size(B, 1), size(B,2)
166✔
1588
    if nA != m
166✔
1589
        throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal"))
×
1590
    end
1591
    if size(C) != size(B)
332✔
1592
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of right hand side, $(size(B))"))
×
1593
    end
1594
    iszero(mA) && return C
166✔
1595
    oA = oneunit(eltype(A))
164✔
1596
    @inbounds if uploc == 'U'
164✔
1597
        if isunitc == 'N'
82✔
1598
            for k in 1:n
82✔
1599
                amm = conj(A[m,m])
742✔
1600
                iszero(amm) && throw(SingularException(m))
742✔
1601
                Cm = C[m,k] = amm \ B[m,k]
742✔
1602
                # fill C-column
1603
                for i in m-1:-1:1
1,484✔
1604
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm
5,976✔
1605
                end
11,210✔
1606
                for j in m-1:-1:1
1,484✔
1607
                    ajj = conj(A[j,j])
5,976✔
1608
                    iszero(ajj) && throw(SingularException(j))
5,976✔
1609
                    Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
5,976✔
1610
                    for i in j-1:-1:1
11,210✔
1611
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
21,096✔
1612
                    end
36,958✔
1613
                end
11,210✔
1614
            end
1,402✔
1615
        else # isunitc == 'U'
1616
            for k in 1:n
×
1617
                Cm = C[m,k] = oA \ B[m,k]
×
1618
                # fill C-column
1619
                for i in m-1:-1:1
×
1620
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm
×
1621
                end
×
1622
                for j in m-1:-1:1
×
1623
                    Cj = C[j,k]
×
1624
                    for i in 1:j-1
×
1625
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
×
1626
                    end
×
1627
                end
×
1628
            end
×
1629
        end
1630
    else # uploc == 'L'
1631
        if isunitc == 'N'
82✔
1632
            for k in 1:n
82✔
1633
                a11 = conj(A[1,1])
742✔
1634
                iszero(a11) && throw(SingularException(1))
742✔
1635
                C1 = C[1,k] = a11 \ B[1,k]
742✔
1636
                # fill C-column
1637
                for i in 2:m
742✔
1638
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1
5,976✔
1639
                end
11,210✔
1640
                for j in 2:m
742✔
1641
                    ajj = conj(A[j,j])
5,976✔
1642
                    iszero(ajj) && throw(SingularException(j))
5,976✔
1643
                    Cj = C[j,k] = _ustrip(ajj) \ C[j,k]
5,976✔
1644
                    for i in j+1:m
6,718✔
1645
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
21,096✔
1646
                    end
36,958✔
1647
                end
11,210✔
1648
            end
742✔
1649
        else # isunitc == 'U'
1650
            for k in 1:n
×
1651
                C1 = C[1,k] = oA \ B[1,k]
×
1652
                # fill C-column
1653
                for i in 2:m
×
1654
                    C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1
×
1655
                end
×
1656
                for j in 1:m
×
1657
                    Cj = C[j,k]
×
1658
                    for i in j+1:m
×
1659
                        C[i,k] -= _ustrip(conj(A[i,j])) * Cj
×
1660
                    end
×
1661
                end
×
1662
            end
×
1663
        end
1664
    end
1665
    return C
164✔
1666
end
1667

1668
function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix)
4,858✔
1669
    require_one_based_indexing(C, A, B)
4,858✔
1670
    m, n = size(A)
4,858✔
1671
    if size(B, 1) != n
4,858✔
1672
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))"))
2,028✔
1673
    end
1674
    if size(C) != size(A)
5,660✔
1675
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of left hand side, $(size(A))"))
×
1676
    end
1677
    oB = oneunit(eltype(B))
2,830✔
1678
    unit = isunitc == 'U'
2,830✔
1679
    @inbounds if uploc == 'U'
2,830✔
1680
        if tfun === identity
1,420✔
1681
            for i in 1:m
1,072✔
1682
                for j in 1:n
9,898✔
1683
                    Aij = A[i,j]
91,682✔
1684
                    for k in 1:j - 1
91,682✔
1685
                        Aij -= C[i,k]*B[k,j]
473,944✔
1686
                    end
678,112✔
1687
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
140,093✔
1688
                    C[i,j] = Aij / (unit ? oB : B[j,j])
91,682✔
1689
                end
173,466✔
1690
            end
18,724✔
1691
        else # tfun in (adjoint, transpose)
1692
            for i in 1:m
348✔
1693
                for j in n:-1:1
6,328✔
1694
                    Aij = A[i,j]
28,796✔
1695
                    for k in j + 1:n
31,960✔
1696
                        Aij -= C[i,k]*tfun(B[j,k])
142,992✔
1697
                    end
207,936✔
1698
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
44,956✔
1699
                    C[i,j] = Aij / (unit ? oB : tfun(B[j,j]))
28,796✔
1700
                end
54,428✔
1701
            end
5,980✔
1702
        end
1703
    else # uploc == 'L'
1704
        if tfun === identity
1,410✔
1705
            for i in 1:m
1,062✔
1706
                for j in n:-1:1
19,616✔
1707
                    Aij = A[i,j]
90,832✔
1708
                    for k in j + 1:n
100,640✔
1709
                        Aij -= C[i,k]*B[k,j]
470,244✔
1710
                    end
671,472✔
1711
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
138,793✔
1712
                    C[i,j] = Aij / (unit ? oB : B[j,j])
90,832✔
1713
                end
171,856✔
1714
            end
9,808✔
1715
        else # tfun in (adjoint, transpose)
1716
            for i in 1:m
348✔
1717
                for j in 1:n
3,164✔
1718
                    Aij = A[i,j]
28,796✔
1719
                    for k in 1:j - 1
28,796✔
1720
                        Aij -= C[i,k]*tfun(B[j,k])
142,992✔
1721
                    end
207,936✔
1722
                    unit || (iszero(B[j,j]) && throw(SingularException(j)))
44,956✔
1723
                    C[i,j] = Aij / (unit ? oB : tfun(B[j,j]))
28,796✔
1724
                end
54,428✔
1725
            end
3,164✔
1726
        end
1727
    end
1728
    return C
2,830✔
1729
end
1730
function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans)
2✔
1731
    B = parent(xB)
2✔
1732
    require_one_based_indexing(C, A, B)
2✔
1733
    m, n = size(A)
2✔
1734
    if size(B, 1) != n
2✔
1735
        throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))"))
×
1736
    end
1737
    if size(C) != size(A)
4✔
1738
        throw(DimensionMismatch(lazy"size of output, $(size(C)), does not match size of left hand side, $(size(A))"))
×
1739
    end
1740
    oB = oneunit(eltype(B))
2✔
1741
    unit = isunitc == 'U'
2✔
1742
    if uploc == 'U'
2✔
1743
        @inbounds for i in 1:m
2✔
1744
            for j in 1:n
20✔
1745
                Aij = A[i,j]
×
1746
                for k in 1:j - 1
×
1747
                    Aij -= C[i,k]*conj(B[k,j])
×
1748
                end
×
1749
                unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1750
                C[i,j] = Aij / (unit ? oB : conj(B[j,j]))
×
1751
            end
×
1752
        end
38✔
1753
    else # uploc == 'L'
1754
        @inbounds for i in 1:m
×
1755
            for j in n:-1:1
×
1756
                Aij = A[i,j]
×
1757
                for k in j + 1:n
×
1758
                    Aij -= C[i,k]*conj(B[k,j])
×
1759
                end
×
1760
                unit || (iszero(B[j,j]) && throw(SingularException(j)))
×
1761
                C[i,j] = Aij / (unit ? oB : conj(B[j,j]))
×
1762
            end
×
1763
        end
×
1764
    end
1765
    return C
2✔
1766
end
1767

1768
# these are needed because we don't keep track of left- and right-multiplication in tritrimul!
1769
rmul!(A::UpperTriangular, B::UpperTriangular)     = UpperTriangular(rmul!(triu!(A.data), B))
12✔
1770
rmul!(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(rmul!(triu!(A.data), B))
12✔
1771
rmul!(A::LowerTriangular, B::LowerTriangular)     = LowerTriangular(rmul!(tril!(A.data), B))
12✔
1772
rmul!(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(rmul!(tril!(A.data), B))
12✔
1773

1774
# Promotion
1775
## Promotion methods in matmul don't apply to triangular multiplication since
1776
## it is inplace. Hence we have to make very similar definitions, but without
1777
## allocation of a result array. For multiplication and unit diagonal division
1778
## the element type doesn't have to be stable under division whereas that is
1779
## necessary in the general triangular solve problem.
1780

1781
_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA<:Integer,TB<:Integer} =
404✔
1782
    promote_op(matprod, TA, TB)
1783
_inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA,TB} =
6,368✔
1784
    promote_op(op, TA, TB)
1785
## The general promotion methods
1786
for mat in (:AbstractVector, :AbstractMatrix)
1787
    ### Left division with triangle to the left hence rhs cannot be transposed. No quotients.
1788
    @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat)
3,677✔
1789
        require_one_based_indexing(B)
4,235✔
1790
        TAB = _inner_type_promotion(\, eltype(A), eltype(B))
4,235✔
1791
        ldiv!(similar(B, TAB, size(B)), A, B)
4,793✔
1792
    end
1793
    ### Left division with triangle to the left hence rhs cannot be transposed. Quotients.
1794
    @eval function \(A::Union{UpperTriangular,LowerTriangular}, B::$mat)
4,350✔
1795
        require_one_based_indexing(B)
10,707✔
1796
        TAB = promote_op(\, eltype(A), eltype(B))
10,707✔
1797
        ldiv!(similar(B, TAB, size(B)), A, B)
17,064✔
1798
    end
1799
    ### Right division with triangle to the right hence lhs cannot be transposed. No quotients.
1800
    @eval function /(A::$mat, B::Union{UnitUpperTriangular, UnitLowerTriangular})
1,979✔
1801
        require_one_based_indexing(A)
2,537✔
1802
        TAB = _inner_type_promotion(/, eltype(A), eltype(B))
2,537✔
1803
        _rdiv!(similar(A, TAB, size(A)), A, B)
3,095✔
1804
    end
1805
    ### Right division with triangle to the right hence lhs cannot be transposed. Quotients.
1806
    @eval function /(A::$mat, B::Union{UpperTriangular,LowerTriangular})
2,005✔
1807
        require_one_based_indexing(A)
2,603✔
1808
        TAB = promote_op(/, eltype(A), eltype(B))
2,603✔
1809
        _rdiv!(similar(A, TAB, size(A)), A, B)
3,201✔
1810
    end
1811
end
1812

1813
## Some Triangular-Triangular cases. We might want to write tailored methods
1814
## for these cases, but I'm not sure it is worth it.
1815
for f in (:*, :\)
1816
    @eval begin
1817
        ($f)(A::LowerTriangular, B::LowerTriangular) =
741✔
1818
            LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix))
1819
        ($f)(A::LowerTriangular, B::UnitLowerTriangular) =
672✔
1820
            LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix))
1821
        ($f)(A::UnitLowerTriangular, B::LowerTriangular) =
627✔
1822
            LowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix))
1823
        ($f)(A::UnitLowerTriangular, B::UnitLowerTriangular) =
616✔
1824
            UnitLowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix))
1825
        ($f)(A::UpperTriangular, B::UpperTriangular) =
2,599✔
1826
            UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix))
1827
        ($f)(A::UpperTriangular, B::UnitUpperTriangular) =
672✔
1828
            UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix))
1829
        ($f)(A::UnitUpperTriangular, B::UpperTriangular) =
627✔
1830
            UpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix))
1831
        ($f)(A::UnitUpperTriangular, B::UnitUpperTriangular) =
621✔
1832
            UnitUpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix))
1833
    end
1834
end
1835
(/)(A::LowerTriangular, B::LowerTriangular) =
123✔
1836
    LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular))
1837
(/)(A::LowerTriangular, B::UnitLowerTriangular) =
117✔
1838
    LowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular))
1839
(/)(A::UnitLowerTriangular, B::LowerTriangular) =
98✔
1840
    LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular))
1841
(/)(A::UnitLowerTriangular, B::UnitLowerTriangular) =
106✔
1842
    UnitLowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular))
1843
(/)(A::UpperTriangular, B::UpperTriangular) =
167✔
1844
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular))
1845
(/)(A::UpperTriangular, B::UnitUpperTriangular) =
117✔
1846
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular))
1847
(/)(A::UnitUpperTriangular, B::UpperTriangular) =
98✔
1848
    UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular))
1849
(/)(A::UnitUpperTriangular, B::UnitUpperTriangular) =
106✔
1850
    UnitUpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular))
1851

1852
# Complex matrix power for upper triangular factor, see:
1853
#   Higham and Lin, "A Schur-Padé algorithm for fractional powers of a Matrix",
1854
#     SIAM J. Matrix Anal. & Appl., 32 (3), (2011) 1056–1078.
1855
#   Higham and Lin, "An improved Schur-Padé algorithm for fractional powers of
1856
#     a matrix and their Fréchet derivatives", SIAM. J. Matrix Anal. & Appl.,
1857
#     34(3), (2013) 1341–1360.
1858
function powm!(A0::UpperTriangular, p::Real)
64✔
1859
    if abs(p) >= 1
64✔
1860
        throw(ArgumentError(lazy"p must be a real number in (-1,1), got $p"))
2✔
1861
    end
1862

1863
    normA0 = opnorm(A0, 1)
62✔
1864
    rmul!(A0, 1/normA0)
62✔
1865

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

1869
    A, m, s = invsquaring(A0, theta)
62✔
1870
    A = I - A
62✔
1871

1872
    # Compute accurate diagonal of I - T
1873
    sqrt_diag!(A0, A, s)
213✔
1874
    for i = 1:n
62✔
1875
        A[i, i] = -A[i, i]
214✔
1876
    end
366✔
1877
    # Compute the Padé approximant
1878
    c = 0.5 * (p - m) / (2 * m - 1)
62✔
1879
    triu!(A)
63✔
1880
    S = c * A
63✔
1881
    Stmp = similar(S)
62✔
1882
    for j = m-1:-1:1
124✔
1883
        j4 = 4 * j
266✔
1884
        c = (-p - j) / (j4 + 2)
266✔
1885
        for i = 1:n
266✔
1886
            @inbounds S[i, i] = S[i, i] + 1
940✔
1887
        end
1,614✔
1888
        copyto!(Stmp, S)
266✔
1889
        mul!(S, A, c)
270✔
1890
        ldiv!(Stmp, S)
270✔
1891

1892
        c = (p - j) / (j4 - 2)
266✔
1893
        for i = 1:n
266✔
1894
            @inbounds S[i, i] = S[i, i] + 1
940✔
1895
        end
1,614✔
1896
        copyto!(Stmp, S)
266✔
1897
        mul!(S, A, c)
270✔
1898
        ldiv!(Stmp, S)
270✔
1899
    end
470✔
1900
    for i = 1:n
62✔
1901
        S[i, i] = S[i, i] + 1
214✔
1902
    end
366✔
1903
    copyto!(Stmp, S)
62✔
1904
    mul!(S, A, -p)
63✔
1905
    ldiv!(Stmp, S)
63✔
1906
    for i = 1:n
62✔
1907
        @inbounds S[i, i] = S[i, i] + 1
214✔
1908
    end
366✔
1909

1910
    blockpower!(A0, S, p/(2^s))
62✔
1911
    for m = 1:s
62✔
1912
        mul!(Stmp.data, S, S)
248✔
1913
        copyto!(S, Stmp)
246✔
1914
        blockpower!(A0, S, p/(2^(s-m)))
246✔
1915
    end
430✔
1916
    rmul!(S, normA0^p)
63✔
1917
    return S
62✔
1918
end
1919
powm(A::LowerTriangular, p::Real) = copy(transpose(powm!(copy(transpose(A)), p::Real)))
2✔
1920

1921
# Complex matrix logarithm for the upper triangular factor, see:
1922
#   Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for
1923
#     the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169.
1924
#   Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix
1925
#     logarithm and estimating the condition number", SIAM J. Sci. Comput.,
1926
#     35(4), (2013), C394–C410.
1927
#
1928
# Based on the code available at http://eprints.ma.man.ac.uk/1851/02/logm.zip,
1929
# Copyright (c) 2011, Awad H. Al-Mohy and Nicholas J. Higham
1930
# Julia version relicensed with permission from original authors
1931
log(A::UpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A)
256✔
1932
log(A::UnitUpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A)
26✔
1933
log(A::LowerTriangular) = copy(transpose(log(copy(transpose(A)))))
4✔
1934
log(A::UnitLowerTriangular) = copy(transpose(log(copy(transpose(A)))))
4✔
1935

1936
function log_quasitriu(A0::AbstractMatrix{T}) where T<:BlasFloat
295✔
1937
    # allocate real A if log(A) will be real and complex A otherwise
1938
    n = checksquare(A0)
295✔
1939
    if isreal(A0) && (!istriu(A0) || !any(x -> real(x) < zero(real(T)), diag(A0)))
1,235✔
1940
        A = T <: Complex ? real(A0) : copy(A0)
90✔
1941
    else
1942
        A = T <: Complex ? copy(A0) : complex(A0)
205✔
1943
    end
1944
    if A0 isa UnitUpperTriangular
295✔
1945
        A = UpperTriangular(parent(A))
26✔
1946
        @inbounds for i in 1:n
26✔
1947
            A[i,i] = 1
234✔
1948
        end
442✔
1949
    end
1950
    Y0 = _log_quasitriu!(A0, A)
385✔
1951
    # return complex result for complex input
1952
    Y = T <: Complex ? complex(Y0) : Y0
320✔
1953

1954
    if A0 isa UpperTriangular || A0 isa UnitUpperTriangular
295✔
1955
        return UpperTriangular(Y)
282✔
1956
    else
1957
        return Y
13✔
1958
    end
1959
end
1960
# type-stable implementation of log_quasitriu
1961
# A is a copy of A0 that is overwritten while computing the result. It has the same eltype
1962
# as the result.
1963
function _log_quasitriu!(A0, A)
295✔
1964
    # Find Padé degree m and s while replacing A with A^(1/2^s)
1965
    m, s = _find_params_log_quasitriu!(A)
295✔
1966

1967
    # Compute accurate superdiagonal of A
1968
    _pow_superdiag_quasitriu!(A, A0, 0.5^s)
583✔
1969

1970
    # Compute accurate block diagonal of A
1971
    _sqrt_pow_diag_quasitriu!(A, A0, s)
295✔
1972

1973
    # Get the Gauss-Legendre quadrature points and weights
1974
    R = zeros(Float64, m, m)
10,102✔
1975
    for i = 1:m - 1
295✔
1976
        R[i,i+1] = i / sqrt((2 * i)^2 - 1)
1,399✔
1977
        R[i+1,i] = R[i,i+1]
1,399✔
1978
    end
2,512✔
1979
    x,V = eigen(R)
590✔
1980
    w = Vector{Float64}(undef, m)
590✔
1981
    for i = 1:m
295✔
1982
        x[i] = (x[i] + 1) / 2
1,694✔
1983
        w[i] = V[1,i]^2
1,694✔
1984
    end
3,093✔
1985

1986
    # Compute the Padé approximation
1987
    t = eltype(A)
295✔
1988
    n = size(A, 1)
295✔
1989
    Y = zeros(t, n, n)
10,278✔
1990
    B = similar(A)
308✔
1991
    for k = 1:m
295✔
1992
        B .= t(x[k]) .* A
1,755✔
1993
        @inbounds for i in 1:n
1,694✔
1994
            B[i,i] += 1
8,412✔
1995
        end
15,130✔
1996
        Y .+= t(w[k]) .* rdiv_quasitriu!(A, B)
3,388✔
1997
    end
3,093✔
1998

1999
    # Scale back
2000
    lmul!(2.0^s, Y)
583✔
2001

2002
    # Compute accurate diagonal and superdiagonal of log(A)
2003
    _log_diag_quasitriu!(Y, A0)
295✔
2004

2005
    return Y
295✔
2006
end
2007

2008
# Auxiliary functions for matrix logarithm and matrix power
2009

2010
# Find Padé degree m and s while replacing A with A^(1/2^s)
2011
#   Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for
2012
#     the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153–C169.
2013
#   from Algorithm 4.1
2014
function _find_params_log_quasitriu!(A)
295✔
2015
    maxsqrt = 100
295✔
2016
    theta = [1.586970738772063e-005,
2,065✔
2017
         2.313807884242979e-003,
2018
         1.938179313533253e-002,
2019
         6.209171588994762e-002,
2020
         1.276404810806775e-001,
2021
         2.060962623452836e-001,
2022
         2.879093714241194e-001]
2023
    tmax = size(theta, 1)
295✔
2024
    n = size(A, 1)
295✔
2025
    p = 0
295✔
2026
    m = 0
295✔
2027

2028
    # Find s0, the smallest s such that the ρ(triu(A)^(1/2^s) - I) ≤ theta[tmax], where ρ(X)
2029
    # is the spectral radius of X
2030
    d = complex.(@view(A[diagind(A)]))
295✔
2031
    dm1 = d .- 1
590✔
2032
    s = 0
295✔
2033
    while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
1,384✔
2034
        d .= sqrt.(d)
1,089✔
2035
        dm1 .= d .- 1
2,178✔
2036
        s = s + 1
1,089✔
2037
    end
1,089✔
2038
    s0 = s
295✔
2039

2040
    # Compute repeated roots
2041
    for k = 1:min(s, maxsqrt)
295✔
2042
        _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A)
1,089✔
2043
    end
1,917✔
2044

2045
    # these three never needed at the same time, so reuse the same temporary
2046
    AmI = AmI4 = AmI5 = A - I
1,401✔
2047
    AmI2 = AmI * AmI
308✔
2048
    AmI3 = AmI2 * AmI
308✔
2049
    d2 = sqrt(opnorm(AmI2, 1))
295✔
2050
    d3 = cbrt(opnorm(AmI3, 1))
295✔
2051
    alpha2 = max(d2, d3)
295✔
2052
    foundm = false
295✔
2053
    if alpha2 <= theta[2]
295✔
2054
        m = alpha2 <= theta[1] ? 1 : 2
9✔
2055
        foundm = true
9✔
2056
    end
2057

2058
    while !foundm
633✔
2059
        more_sqrt = false
624✔
2060
        mul!(AmI4, AmI2, AmI2)
624✔
2061
        d4 = opnorm(AmI4, 1)^(1/4)
1,248✔
2062
        alpha3 = max(d3, d4)
624✔
2063
        if alpha3 <= theta[tmax]
624✔
2064
            local j
2065
            for outer j = 3:tmax
359✔
2066
                if alpha3 <= theta[j]
1,478✔
2067
                    break
359✔
2068
                end
2069
            end
1,119✔
2070
            if j <= 6
359✔
2071
                m = j
220✔
2072
                break
220✔
2073
            elseif alpha3 / 2 <= theta[5] && p < 2
139✔
2074
                more_sqrt = true
98✔
2075
                p = p + 1
98✔
2076
           end
2077
        end
2078

2079
        if !more_sqrt
404✔
2080
            mul!(AmI5, AmI3, AmI2)
306✔
2081
            d5 = opnorm(AmI5, 1)^(1/5)
612✔
2082
            alpha4 = max(d4, d5)
306✔
2083
            eta = min(alpha3, alpha4)
306✔
2084
            if eta <= theta[tmax]
306✔
2085
                j = 0
65✔
2086
                for outer j = 6:tmax
65✔
2087
                    if eta <= theta[j]
130✔
2088
                        m = j
65✔
2089
                        break
65✔
2090
                    end
2091
                end
65✔
2092
                break
65✔
2093
            end
2094
        end
2095

2096
        if s == maxsqrt
339✔
2097
            m = tmax
1✔
2098
            break
1✔
2099
        end
2100
        _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A)
338✔
2101
        copyto!(AmI, A)
440✔
2102
        for i in 1:n
338✔
2103
            @inbounds AmI[i,i] -= 1
1,726✔
2104
        end
3,114✔
2105
        mul!(AmI2, AmI, AmI)
338✔
2106
        mul!(AmI3, AmI2, AmI)
338✔
2107
        d3 = cbrt(opnorm(AmI3, 1))
338✔
2108
        s = s + 1
338✔
2109
    end
338✔
2110
    return m, s
295✔
2111
end
2112

2113
# Compute accurate diagonal of A = A0^s - I
2114
function sqrt_diag!(A0::UpperTriangular, A::UpperTriangular, s)
1✔
2115
    n = checksquare(A0)
62✔
2116
    T = eltype(A)
62✔
2117
    @inbounds for i = 1:n
62✔
2118
        a = complex(A0[i,i])
214✔
2119
        A[i,i] = _sqrt_pow(a, s)
214✔
2120
    end
365✔
2121
end
2122
# Compute accurate block diagonal of A = A0^s - I for upper quasi-triangular A0 produced
2123
# by the Schur decomposition. Diagonal is made of 1x1 and 2x2 blocks.
2124
# 2x2 blocks are real with non-negative conjugate pair eigenvalues
2125
function _sqrt_pow_diag_quasitriu!(A, A0, s)
295✔
2126
    n = checksquare(A0)
295✔
2127
    t = typeof(sqrt(zero(eltype(A))))
295✔
2128
    i = 1
295✔
2129
    @inbounds while i < n
295✔
2130
        if iszero(A0[i+1,i])  # 1x1 block
1,131✔
2131
            A[i,i] = _sqrt_pow(t(A0[i,i]), s)
1,114✔
2132
            i += 1
1,114✔
2133
        else  # real 2x2 block
2134
            @views _sqrt_pow_diag_block_2x2!(A[i:i+1,i:i+1], A0[i:i+1,i:i+1], s)
17✔
2135
            i += 2
17✔
2136
        end
2137
    end
1,131✔
2138
    if i == n  # last block is 1x1
295✔
2139
        @inbounds A[n,n] = _sqrt_pow(t(A0[n,n]), s)
288✔
2140
    end
2141
    return A
295✔
2142
end
2143
# compute a^(1/2^s)-1
2144
#   Al-Mohy, "A more accurate Briggs method for the logarithm",
2145
#      Numer. Algorithms, 59, (2012), 393–402.
2146
#   Algorithm 2
2147
function _sqrt_pow(a::Number, s)
1,616✔
2148
    T = typeof(sqrt(zero(a)))
1,616✔
2149
    s == 0 && return T(a) - 1
1,616✔
2150
    s0 = s
1,589✔
2151
    if imag(a) >= 0 && real(a) <= 0 && !iszero(a)  # angle(a) ≥ π / 2
1,589✔
2152
        a = sqrt(a)
139✔
2153
        s0 = s - 1
139✔
2154
    end
2155
    z0 = a - 1
1,589✔
2156
    a = sqrt(a)
1,589✔
2157
    r = 1 + a
1,589✔
2158
    for j = 1:s0-1
1,589✔
2159
        a = sqrt(a)
4,444✔
2160
        r = r * (1 + a)
4,444✔
2161
    end
7,315✔
2162
    return z0 / r
1,589✔
2163
end
2164
# compute A0 = A^(1/2^s)-I for 2x2 real matrices A and A0
2165
# A has non-negative conjugate pair eigenvalues
2166
# "Improved Inverse Scaling and Squaring Algorithms for the Matrix Logarithm"
2167
# SIAM J. Sci. Comput., 34(4), (2012) C153–C169. doi: 10.1137/110852553
2168
# Algorithm 5.1
2169
Base.@propagate_inbounds function _sqrt_pow_diag_block_2x2!(A, A0, s)
2170
    _sqrt_real_2x2!(A, A0)
17✔
2171
    if isone(s)
17✔
2172
        A[1,1] -= 1
×
2173
        A[2,2] -= 1
×
2174
    else
2175
        # Z = A - I
2176
        z11, z21, z12, z22 = A[1,1] - 1, A[2,1], A[1,2], A[2,2] - 1
17✔
2177
        # A = sqrt(A)
2178
        _sqrt_real_2x2!(A, A)
17✔
2179
        # P = A + I
2180
        p11, p21, p12, p22 = A[1,1] + 1, A[2,1], A[1,2], A[2,2] + 1
17✔
2181
        for i in 1:(s - 2)
17✔
2182
            # A = sqrt(A)
2183
            _sqrt_real_2x2!(A, A)
423✔
2184
            a11, a21, a12, a22 = A[1,1], A[2,1], A[1,2], A[2,2]
423✔
2185
            # P += P * A
2186
            r11 = p11*(1 + a11) + p12*a21
423✔
2187
            r22 = p21*a12 + p22*(1 + a22)
423✔
2188
            p21 = p21*(1 + a11) + p22*a21
423✔
2189
            p12 = p11*a12 + p12*(1 + a22)
423✔
2190
            p11 = r11
423✔
2191
            p22 = r22
423✔
2192
        end
829✔
2193
        # A = Z / P
2194
        c = inv(p11*p22 - p21*p12)
17✔
2195
        A[1,1] = (p22*z11 - p21*z12) * c
17✔
2196
        A[2,1] = (p22*z21 - p21*z22) * c
17✔
2197
        A[1,2] = (p11*z12 - p12*z11) * c
17✔
2198
        A[2,2] = (p11*z22 - p12*z21) * c
17✔
2199
    end
2200
    return A
17✔
2201
end
2202
# Compute accurate superdiagonal of A = A0^s - I for upper quasi-triangular A0 produced
2203
# by a Schur decomposition.
2204
# Higham and Lin, "A Schur–Padé Algorithm for Fractional Powers of a Matrix"
2205
# SIAM J. Matrix Anal. Appl., 32(3), (2011), 1056–1078.
2206
# Equation 5.6
2207
# see also blockpower for when A0 is upper triangular
2208
function _pow_superdiag_quasitriu!(A, A0, p)
295✔
2209
    n = checksquare(A0)
295✔
2210
    t = eltype(A)
295✔
2211
    k = 1
295✔
2212
    @inbounds while k < n
295✔
2213
        if !iszero(A[k+1,k])
1,124✔
2214
            k += 2
10✔
2215
            continue
10✔
2216
        end
2217
        if !(k == n - 1 || iszero(A[k+2,k+1]))
1,949✔
2218
            k += 3
7✔
2219
            continue
7✔
2220
        end
2221
        Ak = t(A0[k,k])
1,107✔
2222
        Akp1 = t(A0[k+1,k+1])
1,107✔
2223

2224
        Akp = Ak^p
1,107✔
2225
        Akp1p = Akp1^p
1,107✔
2226

2227
        if Ak == Akp1
1,107✔
2228
            A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
306✔
2229
        elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
1,590✔
2230
            A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
424✔
2231
        else
2232
            logAk = log(Ak)
377✔
2233
            logAkp1 = log(Akp1)
377✔
2234
            z = (Akp1 - Ak)/(Akp1 + Ak)
377✔
2235
            if abs(z) > 1
488✔
2236
                A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
73✔
2237
            else
2238
                w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
399✔
2239
                dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
513✔
2240
                A[k,k+1] = A0[k,k+1] * dd
304✔
2241
            end
2242
        end
2243
        k += 1
1,107✔
2244
    end
1,124✔
2245
end
2246

2247
# Compute accurate block diagonal and superdiagonal of A = log(A0) for upper
2248
# quasi-triangular A0 produced by the Schur decomposition.
2249
function _log_diag_quasitriu!(A, A0)
295✔
2250
    n = checksquare(A0)
295✔
2251
    t = eltype(A)
295✔
2252
    k = 1
295✔
2253
    @inbounds while k < n
295✔
2254
        if iszero(A0[k+1,k])  # 1x1 block
776✔
2255
            Ak = t(A0[k,k])
759✔
2256
            logAk = log(Ak)
759✔
2257
            A[k,k] = logAk
759✔
2258
            if k < n - 2 && iszero(A0[k+2,k+1])
759✔
2259
                Akp1 = t(A0[k+1,k+1])
355✔
2260
                logAkp1 = log(Akp1)
355✔
2261
                A[k+1,k+1] = logAkp1
355✔
2262
                if Ak == Akp1
355✔
2263
                    A[k,k+1] = A0[k,k+1] / Ak
114✔
2264
                elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
486✔
2265
                    A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
121✔
2266
                else
2267
                    z = (Akp1 - Ak)/(Akp1 + Ak)
120✔
2268
                    if abs(z) > 1
152✔
2269
                        A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
32✔
2270
                    else
2271
                        w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
128✔
2272
                        A[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
88✔
2273
                    end
2274
                end
2275
                k += 2
355✔
2276
            else
2277
                k += 1
404✔
2278
            end
2279
        else  # real 2x2 block
2280
            @views _log_diag_block_2x2!(A[k:k+1,k:k+1], A0[k:k+1,k:k+1])
17✔
2281
            k += 2
17✔
2282
        end
2283
    end
776✔
2284
    if k == n  # last 1x1 block
295✔
2285
        @inbounds A[n,n] = log(t(A0[n,n]))
288✔
2286
    end
2287
    return A
295✔
2288
end
2289
# compute A0 = log(A) for 2x2 real matrices A and A0, where A0 is a diagonal 2x2 block
2290
# produced by real Schur decomposition.
2291
# Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix
2292
# logarithm and estimating the condition number", SIAM J. Sci. Comput.,
2293
# 35(4), (2013), C394–C410.
2294
# Eq. 6.1
2295
Base.@propagate_inbounds function _log_diag_block_2x2!(A, A0)
2296
    a, b, c = A0[1,1], A0[1,2], A0[2,1]
17✔
2297
    # avoid underflow/overflow for large/small b and c
2298
    s = sqrt(abs(b)) * sqrt(abs(c))
17✔
2299
    θ = atan(s, a)
17✔
2300
    t = θ / s
17✔
2301
    au = abs(a)
17✔
2302
    if au > s
17✔
2303
        a1 = log1p((s / au)^2) / 2 + log(au)
7✔
2304
    else
2305
        a1 = log1p((au / s)^2) / 2 + log(s)
10✔
2306
    end
2307
    A[1,1] = a1
17✔
2308
    A[2,1] = c*t
17✔
2309
    A[1,2] = b*t
17✔
2310
    A[2,2] = a1
17✔
2311
    return A
17✔
2312
end
2313

2314
# Used only by powm at the moment
2315
# Repeatedly compute the square roots of A so that in the end its
2316
# eigenvalues are close enough to the positive real line
2317
function invsquaring(A0::UpperTriangular, theta)
62✔
2318
    require_one_based_indexing(theta)
62✔
2319
    # assumes theta is in ascending order
2320
    maxsqrt = 100
62✔
2321
    tmax = size(theta, 1)
62✔
2322
    n = checksquare(A0)
62✔
2323
    A = complex(copy(A0))
62✔
2324
    p = 0
62✔
2325
    m = 0
62✔
2326

2327
    # Compute repeated roots
2328
    d = complex(diag(A))
62✔
2329
    dm1 = d .- 1
124✔
2330
    s = 0
62✔
2331
    while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
216✔
2332
        d .= sqrt.(d)
154✔
2333
        dm1 .= d .- 1
308✔
2334
        s = s + 1
154✔
2335
    end
154✔
2336
    s0 = s
62✔
2337
    for k = 1:min(s, maxsqrt)
62✔
2338
        A = sqrt(A)
154✔
2339
    end
262✔
2340

2341
    AmI = A - I
213✔
2342
    d2 = sqrt(opnorm(AmI^2, 1))
62✔
2343
    d3 = cbrt(opnorm(AmI^3, 1))
62✔
2344
    alpha2 = max(d2, d3)
63✔
2345
    foundm = false
62✔
2346
    if alpha2 <= theta[2]
62✔
2347
        m = alpha2 <= theta[1] ? 1 : 2
×
2348
        foundm = true
×
2349
    end
2350

2351
    while !foundm
198✔
2352
        more = false
198✔
2353
        if s > s0
198✔
2354
            d3 = cbrt(opnorm(AmI^3, 1))
122✔
2355
        end
2356
        d4 = opnorm(AmI^4, 1)^(1/4)
392✔
2357
        alpha3 = max(d3, d4)
202✔
2358
        if alpha3 <= theta[tmax]
198✔
2359
            local j
2360
            for outer j = 3:tmax
150✔
2361
                if alpha3 <= theta[j]
644✔
2362
                    break
150✔
2363
                elseif alpha3 / 2 <= theta[5] && p < 2
494✔
2364
                    more = true
124✔
2365
                    p = p + 1
124✔
2366
                end
2367
            end
494✔
2368
            if j <= 6
150✔
2369
                m = j
62✔
2370
                foundm = true
62✔
2371
                break
62✔
2372
            elseif alpha3 / 2 <= theta[5] && p < 2
88✔
2373
                more = true
×
2374
                p = p + 1
×
2375
           end
2376
        end
2377

2378
        if !more
136✔
2379
            d5 = opnorm(AmI^5, 1)^(1/5)
182✔
2380
            alpha4 = max(d4, d5)
94✔
2381
            eta = min(alpha3, alpha4)
94✔
2382
            if eta <= theta[tmax]
92✔
2383
                j = 0
60✔
2384
                for outer j = 6:tmax
60✔
2385
                    if eta <= theta[j]
60✔
2386
                        m = j
14✔
2387
                        break
14✔
2388
                    end
2389
                    break
46✔
2390
                end
×
2391
            end
2392
            if s == maxsqrt
92✔
2393
                m = tmax
×
2394
                break
×
2395
            end
2396
            A = sqrt(A)
92✔
2397
            AmI = A - I
300✔
2398
            s = s + 1
92✔
2399
        end
2400
    end
136✔
2401

2402
    # Compute accurate superdiagonal of T
2403
    p = 1 / 2^s
62✔
2404
    A = complex(A)
62✔
2405
    blockpower!(A, A0, p)
62✔
2406
    return A,m,s
62✔
2407
end
2408

2409
# Compute accurate diagonal and superdiagonal of A = A0^p
2410
function blockpower!(A::UpperTriangular, A0::UpperTriangular, p)
370✔
2411
    n = checksquare(A0)
370✔
2412
    @inbounds for k = 1:n-1
370✔
2413
        Ak = complex(A0[k,k])
968✔
2414
        Akp1 = complex(A0[k+1,k+1])
968✔
2415

2416
        Akp = Ak^p
968✔
2417
        Akp1p = Akp1^p
968✔
2418

2419
        A[k,k] = Akp
968✔
2420
        A[k+1,k+1] = Akp1p
968✔
2421

2422
        if Ak == Akp1
968✔
2423
            A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
137✔
2424
        elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
1,618✔
2425
            A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
102✔
2426
        else
2427
            logAk = log(Ak)
729✔
2428
            logAkp1 = log(Akp1)
729✔
2429
            z = (Akp1 - Ak)/(Akp1 + Ak)
729✔
2430
            if abs(z) > 1
729✔
2431
                A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
×
2432
            else
2433
                w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
729✔
2434
                dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
1,458✔
2435
                A[k,k+1] = A0[k,k+1] * dd
729✔
2436
            end
2437
        end
2438
    end
968✔
2439
end
2440

2441
# Unwinding number
2442
unw(x::Real) = 0
×
2443
unw(x::Number) = ceil((imag(x) - pi) / (2 * pi))
1,972✔
2444

2445
# compute A / B for upper quasi-triangular B, possibly overwriting B
2446
function rdiv_quasitriu!(A, B)
1,694✔
2447
    n = checksquare(A)
1,694✔
2448
    AG = copy(A)
1,694✔
2449
    # use Givens rotations to annihilate 2x2 blocks
2450
    @inbounds for k in 1:(n-1)
1,694✔
2451
        s = B[k+1,k]
6,718✔
2452
        iszero(s) && continue  # 1x1 block
6,718✔
2453
        G = first(givens(B[k+1,k+1], s, k, k+1))
85✔
2454
        rmul!(B, G)
400✔
2455
        rmul!(AG, G)
85✔
2456
    end
11,758✔
2457
    return rdiv!(AG, UpperTriangular(B))
1,694✔
2458
end
2459

2460
# End of auxiliary functions for matrix logarithm and matrix power
2461

2462
sqrt(A::UpperTriangular) = sqrt_quasitriu(A)
590✔
2463
function sqrt(A::UnitUpperTriangular{T}) where T
20✔
2464
    B = A.data
20✔
2465
    n = checksquare(B)
20✔
2466
    t = typeof(sqrt(zero(T)))
20✔
2467
    R = Matrix{t}(I, n, n)
20✔
2468
    tt = typeof(oneunit(t)*oneunit(t))
20✔
2469
    half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214
22✔
2470
    @inbounds for j = 1:n
20✔
2471
        for i = j-1:-1:1
264✔
2472
            r::tt = B[i,j]
520✔
2473
            @simd for k = i+1:j-1
640✔
2474
                r -= R[i,k]*R[k,j]
1,180✔
2475
            end
2476
            iszero(r) || (R[i,j] = half*r)
1,035✔
2477
        end
914✔
2478
    end
264✔
2479
    return UnitUpperTriangular(R)
20✔
2480
end
2481
sqrt(A::LowerTriangular) = copy(transpose(sqrt(copy(transpose(A)))))
8✔
2482
sqrt(A::UnitLowerTriangular) = copy(transpose(sqrt(copy(transpose(A)))))
8✔
2483

2484
# Auxiliary functions for matrix square root
2485

2486
# square root of upper triangular or real upper quasitriangular matrix
2487
function sqrt_quasitriu(A0; blockwidth = eltype(A0) <: Complex ? 512 : 256)
1,356✔
2488
    n = checksquare(A0)
678✔
2489
    T = eltype(A0)
678✔
2490
    Tr = typeof(sqrt(real(zero(T))))
678✔
2491
    Tc = typeof(sqrt(complex(zero(T))))
678✔
2492
    if isreal(A0)
1,981✔
2493
        is_sqrt_real = true
288✔
2494
        if istriu(A0)
288✔
2495
            for i in 1:n
204✔
2496
                Aii = real(A0[i,i])
592✔
2497
                if Aii < zero(Aii)
592✔
2498
                    is_sqrt_real = false
40✔
2499
                    break
40✔
2500
                end
2501
            end
552✔
2502
        end
2503
        if is_sqrt_real
288✔
2504
            R = zeros(Tr, n, n)
26,860✔
2505
            A = real(A0)
248✔
2506
        else
2507
            R = zeros(Tc, n, n)
347✔
2508
            A = A0
40✔
2509
        end
2510
    else
2511
        A = A0
390✔
2512
        R = zeros(Tc, n, n)
390✔
2513
    end
2514
    _sqrt_quasitriu!(R, A; blockwidth=blockwidth, n=n)
921✔
2515
    Rc = eltype(A0) <: Real ? R : complex(R)
775✔
2516
    if A0 isa UpperTriangular
678✔
2517
        return UpperTriangular(Rc)
590✔
2518
    elseif A0 isa UnitUpperTriangular
88✔
2519
        return UnitUpperTriangular(Rc)
×
2520
    else
2521
        return Rc
88✔
2522
    end
2523
end
2524

2525
# in-place recursive sqrt of upper quasi-triangular matrix A from
2526
# Deadman E., Higham N.J., Ralha R. (2013) Blocked Schur Algorithms for Computing the Matrix
2527
# Square Root. Applied Parallel and Scientific Computing. PARA 2012. Lecture Notes in
2528
# Computer Science, vol 7782. https://doi.org/10.1007/978-3-642-36803-5_12
2529
function _sqrt_quasitriu!(R, A; blockwidth=64, n=checksquare(A))
4,326✔
2530
    if n ≤ blockwidth || !(eltype(R) <: BlasFloat) # base case, perform "point" algorithm
2,199✔
2531
        _sqrt_quasitriu_block!(R, A)
2,136✔
2532
    else  # compute blockwise recursion
2533
        split = div(n, 2)
31✔
2534
        iszero(A[split+1, split]) || (split += 1) # don't split 2x2 diagonal block
39✔
2535
        r1 = 1:split
31✔
2536
        r2 = (split + 1):n
31✔
2537
        n1, n2 = split, n - split
31✔
2538
        A11, A12, A22 = @views A[r1,r1], A[r1,r2], A[r2,r2]
31✔
2539
        R11, R12, R22 = @views R[r1,r1], R[r1,r2], R[r2,r2]
31✔
2540
        # solve diagonal blocks recursively
2541
        _sqrt_quasitriu!(R11, A11; blockwidth=blockwidth, n=n1)
31✔
2542
        _sqrt_quasitriu!(R22, A22; blockwidth=blockwidth, n=n2)
31✔
2543
        # solve off-diagonal block
2544
        R12 .= .- A12
62✔
2545
        _sylvester_quasitriu!(R11, R22, R12; blockwidth=blockwidth, nA=n1, nB=n2, raise=false)
31✔
2546
    end
2547
    return R
2,167✔
2548
end
2549

2550
function _sqrt_quasitriu_block!(R, A)
2551
    _sqrt_quasitriu_diag_block!(R, A)
2,136✔
2552
    _sqrt_quasitriu_offdiag_block!(R, A)
2,136✔
2553
    return R
2,136✔
2554
end
2555

2556
function _sqrt_quasitriu_diag_block!(R, A)
2,136✔
2557
    n = size(R, 1)
2,136✔
2558
    ta = eltype(R) <: Complex ? complex(eltype(A)) : eltype(A)
2,136✔
2559
    i = 1
2,136✔
2560
    @inbounds while i < n
2,136✔
2561
        if iszero(A[i + 1, i])
7,770✔
2562
            R[i, i] = sqrt(ta(A[i, i]))
7,034✔
2563
            i += 1
7,034✔
2564
        else
2565
            # This branch is never reached when A is complex triangular
2566
            @assert eltype(A) <: Real
736✔
2567
            @views _sqrt_real_2x2!(R[i:(i + 1), i:(i + 1)], A[i:(i + 1), i:(i + 1)])
736✔
2568
            i += 2
736✔
2569
        end
2570
    end
7,770✔
2571
    if i == n
2,136✔
2572
        R[n, n] = sqrt(ta(A[n, n]))
1,698✔
2573
    end
2574
    return R
2,136✔
2575
end
2576

2577
function _sqrt_quasitriu_offdiag_block!(R, A)
2,136✔
2578
    n = size(R, 1)
2,136✔
2579
    j = 1
2,136✔
2580
    @inbounds while j ≤ n
2,136✔
2581
        jsize_is_2 = j < n && !iszero(A[j + 1, j])
9,468✔
2582
        i = j - 1
9,468✔
2583
        while i > 0
76,283✔
2584
            isize_is_2 = i > 1 && !iszero(A[i, i - 1])
66,815✔
2585
            if isize_is_2
66,815✔
2586
                if jsize_is_2
831✔
2587
                    _sqrt_quasitriu_offdiag_block_2x2!(R, A, i - 1, j)
417✔
2588
                else
2589
                    _sqrt_quasitriu_offdiag_block_2x1!(R, A, i - 1, j)
417✔
2590
                end
2591
                i -= 2
831✔
2592
            else
2593
                if jsize_is_2
65,984✔
2594
                    _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j)
250✔
2595
                else
2596
                    _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j)
65,734✔
2597
                end
2598
                i -= 1
65,984✔
2599
            end
2600
        end
66,815✔
2601
        j += 2 - !jsize_is_2
9,468✔
2602
    end
9,468✔
2603
    return R
2,136✔
2604
end
2605

2606
# real square root of 2x2 diagonal block of quasi-triangular matrix from real Schur
2607
# decomposition. Eqs 6.8-6.9 and Algorithm 6.5 of
2608
# Higham, 2008, "Functions of Matrices: Theory and Computation", SIAM.
2609
Base.@propagate_inbounds function _sqrt_real_2x2!(R, A)
2610
    # in the real Schur form, A[1, 1] == A[2, 2], and A[2, 1] * A[1, 2] < 0
2611
    θ, a21, a12 = A[1, 1], A[2, 1], A[1, 2]
1,193✔
2612
    # avoid overflow/underflow of μ
2613
    # for real sqrt, |d| ≤ 2 max(|a12|,|a21|)
2614
    μ = sqrt(abs(a12)) * sqrt(abs(a21))
1,193✔
2615
    α = _real_sqrt(θ, μ)
1,307✔
2616
    c = 2α
1,193✔
2617
    R[1, 1] = α
1,193✔
2618
    R[2, 1] = a21 / c
1,193✔
2619
    R[1, 2] = a12 / c
1,193✔
2620
    R[2, 2] = α
1,193✔
2621
    return R
1,193✔
2622
end
2623

2624
# real part of square root of θ+im*μ
2625
@inline function _real_sqrt(θ, μ)
2626
    t = sqrt((abs(θ) + hypot(θ, μ)) / 2)
1,278✔
2627
    return θ ≥ 0 ? t : μ / 2t
1,307✔
2628
end
2629

2630
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j)
2631
    Rii = R[i, i]
65,734✔
2632
    Rjj = R[j, j]
65,734✔
2633
    iszero(Rii) && iszero(Rjj) && return R
65,734✔
2634
    t = eltype(R)
65,733✔
2635
    tt = typeof(zero(t)*zero(t))
65,733✔
2636
    r = tt(-A[i, j])
65,733✔
2637
    @simd for k in (i + 1):(j - 1)
72,656✔
2638
        r += R[i, k] * R[k, j]
2,991,886✔
2639
    end
2640
    iszero(r) && return R
65,733✔
2641
    R[i, j] = sylvester(Rii, Rjj, r)
56,712✔
2642
    return R
56,712✔
2643
end
2644

2645
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j)
2646
    jrange = j:(j + 1)
250✔
2647
    t = eltype(R)
250✔
2648
    tt = typeof(zero(t)*zero(t))
250✔
2649
    r1 = tt(-A[i, j])
250✔
2650
    r2 = tt(-A[i, j + 1])
250✔
2651
    @simd for k in (i + 1):(j - 1)
360✔
2652
        rik = R[i, k]
476✔
2653
        r1 += rik * R[k, j]
476✔
2654
        r2 += rik * R[k, j + 1]
476✔
2655
    end
2656
    Rjj = @view R[jrange, jrange]
250✔
2657
    Rij = @view R[i, jrange]
250✔
2658
    Rij[1] = r1
250✔
2659
    Rij[2] = r2
250✔
2660
    _sylvester_1x2!(R[i, i], Rjj, Rij)
250✔
2661
    return R
250✔
2662
end
2663

2664
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x1!(R, A, i, j)
2665
    irange = i:(i + 1)
417✔
2666
    t = eltype(R)
417✔
2667
    tt = typeof(zero(t)*zero(t))
417✔
2668
    r1 = tt(-A[i, j])
417✔
2669
    r2 = tt(-A[i + 1, j])
417✔
2670
    @simd for k in (i + 2):(j - 1)
546✔
2671
        rkj = R[k, j]
1,003✔
2672
        r1 += R[i, k] * rkj
1,003✔
2673
        r2 += R[i + 1, k] * rkj
1,003✔
2674
    end
2675
    Rii = @view R[irange, irange]
417✔
2676
    Rij = @view R[irange, j]
417✔
2677
    Rij[1] = r1
417✔
2678
    Rij[2] = r2
417✔
2679
    @views _sylvester_2x1!(Rii, R[j, j], Rij)
417✔
2680
    return R
417✔
2681
end
2682

2683
Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x2!(R, A, i, j)
2684
    irange = i:(i + 1)
414✔
2685
    jrange = j:(j + 1)
414✔
2686
    t = eltype(R)
414✔
2687
    tt = typeof(zero(t)*zero(t))
414✔
2688
    for i′ in irange, j′ in jrange
414✔
2689
        Cij = tt(-A[i′, j′])
1,656✔
2690
        @simd for k in (i + 2):(j - 1)
2,332✔
2691
            Cij += R[i′, k] * R[k, j′]
3,464✔
2692
        end
2693
        R[i′, j′] = Cij
1,656✔
2694
    end
2,070✔
2695
    Rii = @view R[irange, irange]
414✔
2696
    Rjj = @view R[jrange, jrange]
414✔
2697
    Rij = @view R[irange, jrange]
414✔
2698
    if !iszero(Rij) && !all(isnan, Rij)
417✔
2699
        _sylvester_2x2!(Rii, Rjj, Rij)
411✔
2700
    end
2701
    return R
414✔
2702
end
2703

2704
# solve Sylvester's equation AX + XB = -C using blockwise recursion until the dimension of
2705
# A and B are no greater than blockwidth, based on Algorithm 1 from
2706
# Jonsson I, Kågström B. Recursive blocked algorithms for solving triangular systems—
2707
# Part I: one-sided and coupled Sylvester-type matrix equations. (2002) ACM Trans Math Softw.
2708
# 28(4), https://doi.org/10.1145/592843.592845.
2709
# specify raise=false to avoid breaking the recursion if a LAPACKException is thrown when
2710
# computing one of the blocks.
2711
function _sylvester_quasitriu!(A, B, C; blockwidth=64, nA=checksquare(A), nB=checksquare(B), raise=true)
863✔
2712
    if 1 ≤ nA ≤ blockwidth && 1 ≤ nB ≤ blockwidth
539✔
2713
        _sylvester_quasitriu_base!(A, B, C; raise=raise)
398✔
2714
    elseif nA ≥ 2nB ≥ 2
141✔
2715
        _sylvester_quasitriu_split1!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
12✔
2716
    elseif nB ≥ 2nA ≥ 2
129✔
2717
        _sylvester_quasitriu_split2!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
12✔
2718
    else
2719
        _sylvester_quasitriu_splitall!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise)
117✔
2720
    end
2721
    return C
499✔
2722
end
2723
function _sylvester_quasitriu_base!(A, B, C; raise=true)
796✔
2724
    try
398✔
2725
        _, scale = LAPACK.trsyl!('N', 'N', A, B, C)
398✔
2726
        rmul!(C, -inv(scale))
398✔
2727
    catch e
2728
        if !(e isa LAPACKException) || raise
296✔
2729
            throw(e)
148✔
2730
        end
2731
    end
2732
    return C
382✔
2733
end
2734
function _sylvester_quasitriu_split1!(A, B, C; nA=checksquare(A), kwargs...)
24✔
2735
    iA = div(nA, 2)
12✔
2736
    iszero(A[iA + 1, iA]) || (iA += 1)  # don't split 2x2 diagonal block
12✔
2737
    rA1, rA2 = 1:iA, (iA + 1):nA
12✔
2738
    nA1, nA2 = iA, nA-iA
12✔
2739
    A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2]
12✔
2740
    C1, C2 = @views C[rA1,:], C[rA2,:]
12✔
2741
    _sylvester_quasitriu!(A22, B, C2; nA=nA2, kwargs...)
24✔
2742
    mul!(C1, A12, C2, true, true)
8✔
2743
    _sylvester_quasitriu!(A11, B, C1; nA=nA1, kwargs...)
16✔
2744
    return C
8✔
2745
end
2746
function _sylvester_quasitriu_split2!(A, B, C; nB=checksquare(B), kwargs...)
24✔
2747
    iB = div(nB, 2)
12✔
2748
    iszero(B[iB + 1, iB]) || (iB += 1)  # don't split 2x2 diagonal block
12✔
2749
    rB1, rB2 = 1:iB, (iB + 1):nB
12✔
2750
    nB1, nB2 = iB, nB-iB
12✔
2751
    B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2]
12✔
2752
    C1, C2 = @views C[:,rB1], C[:,rB2]
12✔
2753
    _sylvester_quasitriu!(A, B11, C1; nB=nB1, kwargs...)
24✔
2754
    mul!(C2, C1, B12, true, true)
8✔
2755
    _sylvester_quasitriu!(A, B22, C2; nB=nB2, kwargs...)
16✔
2756
    return C
8✔
2757
end
2758
function _sylvester_quasitriu_splitall!(A, B, C; nA=checksquare(A), nB=checksquare(B), kwargs...)
234✔
2759
    iA = div(nA, 2)
117✔
2760
    iszero(A[iA + 1, iA]) || (iA += 1)  # don't split 2x2 diagonal block
128✔
2761
    iB = div(nB, 2)
117✔
2762
    iszero(B[iB + 1, iB]) || (iB += 1)  # don't split 2x2 diagonal block
136✔
2763
    rA1, rA2 = 1:iA, (iA + 1):nA
117✔
2764
    nA1, nA2 = iA, nA-iA
117✔
2765
    rB1, rB2 = 1:iB, (iB + 1):nB
117✔
2766
    nB1, nB2 = iB, nB-iB
117✔
2767
    A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2]
117✔
2768
    B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2]
117✔
2769
    C11, C21, C12, C22 = @views C[rA1,rB1], C[rA2,rB1], C[rA1,rB2], C[rA2,rB2]
117✔
2770
    _sylvester_quasitriu!(A22, B11, C21; nA=nA2, nB=nB1, kwargs...)
129✔
2771
    mul!(C11, A12, C21, true, true)
101✔
2772
    _sylvester_quasitriu!(A11, B11, C11; nA=nA1, nB=nB1, kwargs...)
109✔
2773
    mul!(C22, C21, B12, true, true)
101✔
2774
    _sylvester_quasitriu!(A22, B22, C22; nA=nA2, nB=nB2, kwargs...)
109✔
2775
    mul!(C12, A12, C22, true, true)
101✔
2776
    mul!(C12, C11, B12, true, true)
101✔
2777
    _sylvester_quasitriu!(A11, B22, C12; nA=nA1, nB=nB2, kwargs...)
109✔
2778
    return C
101✔
2779
end
2780

2781
# End of auxiliary functions for matrix square root
2782

2783
# Generic eigensystems
2784
eigvals(A::AbstractTriangular) = diag(A)
108✔
2785
# fallback for unknown types
2786
function eigvecs(A::AbstractTriangular{<:BlasFloat})
6✔
2787
    if istriu(A)
8✔
2788
        eigvecs(UpperTriangular(Matrix(A)))
4✔
2789
    else # istril(A)
2790
        eigvecs(LowerTriangular(Matrix(A)))
4✔
2791
    end
2792
end
2793
function eigvecs(A::AbstractTriangular{T}) where T
8✔
2794
    TT = promote_type(T, Float32)
12✔
2795
    if TT <: BlasFloat
12✔
2796
        return eigvecs(convert(AbstractMatrix{TT}, A))
12✔
2797
    else
UNCOV
2798
        throw(ArgumentError(lazy"eigvecs type $(typeof(A)) not supported. Please submit a pull request."))
×
2799
    end
2800
end
2801
det(A::UnitUpperTriangular{T}) where {T} = one(T)
7✔
2802
det(A::UnitLowerTriangular{T}) where {T} = one(T)
7✔
2803
logdet(A::UnitUpperTriangular{T}) where {T} = zero(T)
7✔
2804
logdet(A::UnitLowerTriangular{T}) where {T} = zero(T)
7✔
2805
logabsdet(A::UnitUpperTriangular{T}) where {T} = zero(T), one(T)
7✔
2806
logabsdet(A::UnitLowerTriangular{T}) where {T} = zero(T), one(T)
7✔
2807
det(A::UpperTriangular) = prod(diag(A.data))
308✔
2808
det(A::LowerTriangular) = prod(diag(A.data))
7✔
2809
function logabsdet(A::Union{UpperTriangular{T},LowerTriangular{T}}) where T
65✔
2810
    sgn = one(T)
104✔
2811
    abs_det = zero(real(T))
104✔
2812
    @inbounds for i in 1:size(A,1)
104✔
2813
        diag_i = A.data[i,i]
664✔
2814
        sgn *= sign(diag_i)
826✔
2815
        abs_det += log(abs(diag_i))
775✔
2816
    end
1,224✔
2817
    return abs_det, sgn
104✔
2818
end
2819

2820
eigen(A::AbstractTriangular) = Eigen(eigvals(A), eigvecs(A))
20✔
2821

2822
# Generic singular systems
2823
for func in (:svd, :svd!, :svdvals)
2824
    @eval begin
2825
        ($func)(A::AbstractTriangular; kwargs...) = ($func)(copyto!(similar(parent(A)), A); kwargs...)
112✔
2826
    end
2827
end
2828

2829
factorize(A::AbstractTriangular) = A
28✔
2830

2831
# disambiguation methods: /(Adjoint of AbsVec, <:AbstractTriangular)
UNCOV
2832
/(u::AdjointAbsVec, A::Union{LowerTriangular,UpperTriangular}) = adjoint(adjoint(A) \ u.parent)
×
UNCOV
2833
/(u::AdjointAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = adjoint(adjoint(A) \ u.parent)
×
2834
# disambiguation methods: /(Transpose of AbsVec, <:AbstractTriangular)
2835
/(u::TransposeAbsVec, A::Union{LowerTriangular,UpperTriangular}) = transpose(transpose(A) \ u.parent)
×
UNCOV
2836
/(u::TransposeAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = transpose(transpose(A) \ u.parent)
×
2837
# disambiguation methods: /(Transpose of AbsVec, Adj/Trans of <:AbstractTriangular)
2838
for (tritype, comptritype) in ((:LowerTriangular, :UpperTriangular),
2839
                               (:UnitLowerTriangular, :UnitUpperTriangular),
2840
                               (:UpperTriangular, :LowerTriangular),
2841
                               (:UnitUpperTriangular, :UnitLowerTriangular))
UNCOV
2842
    @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Adjoint}) = transpose($comptritype(conj(parent(parent(A)))) \ u.parent)
×
UNCOV
2843
    @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Transpose}) = transpose(transpose(A) \ u.parent)
×
2844
end
2845

2846
# Cube root of a 2x2 real-valued matrix with complex conjugate eigenvalues and equal diagonal values.
2847
# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots.
2848
#   SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989).
2849
#   https://doi.org/10.1137/s0895479801392697
2850
function _cbrt_2x2!(A::AbstractMatrix{T}) where {T<:Real}
3✔
2851
    @assert checksquare(A) == 2
3✔
2852
    @inbounds begin
3✔
2853
        (A[1,1] == A[2,2]) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have equal diagonal values."))
3✔
2854
        (A[1,2]*A[2,1] < 0) || throw(ArgumentError("_cbrt_2x2!: Matrix A must have complex conjugate eigenvalues."))
3✔
2855
        μ = sqrt(-A[1,2]*A[2,1])
3✔
2856
        r = cbrt(hypot(A[1,1], μ))
3✔
2857
        θ = atan(μ, A[1,1])
3✔
2858
        s, c = sincos(θ/3)
3✔
2859
        α, β′ = r*c, r*s/µ
3✔
2860
        A[1,1] = α
3✔
2861
        A[2,2] = α
3✔
2862
        A[1,2] = β′*A[1,2]
3✔
2863
        A[2,1] = β′*A[2,1]
3✔
2864
    end
2865
    return A
3✔
2866
end
2867

2868
# Cube root of a quasi upper triangular matrix (output of Schur decomposition)
2869
# Reference [1]: Smith, M. I. (2003). A Schur Algorithm for Computing Matrix pth Roots.
2870
#   SIAM Journal on Matrix Analysis and Applications (Vol. 24, Issue 4, pp. 971–989).
2871
#   https://doi.org/10.1137/s0895479801392697
2872
@views function _cbrt_quasi_triu!(A::AbstractMatrix{T}) where {T<:Real}
3✔
2873
    m, n = size(A)
3✔
2874
    (m == n) || throw(ArgumentError("_cbrt_quasi_triu!: Matrix A must be square."))
3✔
2875
    # Cube roots of 1x1 and 2x2 diagonal blocks
2876
    i = 1
3✔
2877
    sizes = ones(Int,n)
30✔
2878
    S = zeros(T,2,n)
60✔
2879
    while i < n
27✔
2880
        if !iszero(A[i+1,i])
24✔
2881
            _cbrt_2x2!(A[i:i+1,i:i+1])
3✔
2882
            mul!(S[1:2,i:i+1], A[i:i+1,i:i+1], A[i:i+1,i:i+1])
3✔
2883
            sizes[i] = 2
3✔
2884
            sizes[i+1] = 0
3✔
2885
            i += 2
3✔
2886
        else
2887
            A[i,i] = cbrt(A[i,i])
21✔
2888
            S[1,i] = A[i,i]*A[i,i]
21✔
2889
            i += 1
21✔
2890
        end
2891
    end
24✔
2892
    if i == n
3✔
2893
        A[n,n] = cbrt(A[n,n])
3✔
2894
        S[1,n] = A[n,n]*A[n,n]
3✔
2895
    end
2896
    # Algorithm 4.3 in Reference [1]
2897
    Δ = I(4)
12✔
2898
    M_L₀ = zeros(T,4,4)
48✔
2899
    M_L₁ = zeros(T,4,4)
48✔
2900
    M_Bᵢⱼ⁽⁰⁾ = zeros(T,2,2)
12✔
2901
    M_Bᵢⱼ⁽¹⁾ = zeros(T,2,2)
12✔
2902
    for k = 1:n-1
3✔
2903
        for i = 1:n-k
27✔
2904
            if sizes[i] == 0 || sizes[i+k] == 0 continue end
255✔
2905
            k₁, k₂ = i+1+(sizes[i+1]==0), i+k-1
111✔
2906
            i₁, i₂, j₁, j₂, s₁, s₂ = i, i+sizes[i]-1, i+k, i+k+sizes[i+k]-1, sizes[i], sizes[i+k]
111✔
2907
            L₀ = M_L₀[1:s₁*s₂,1:s₁*s₂]
111✔
2908
            L₁ = M_L₁[1:s₁*s₂,1:s₁*s₂]
111✔
2909
            Bᵢⱼ⁽⁰⁾ = M_Bᵢⱼ⁽⁰⁾[1:s₁, 1:s₂]
111✔
2910
            Bᵢⱼ⁽¹⁾ = M_Bᵢⱼ⁽¹⁾[1:s₁, 1:s₂]
111✔
2911
            # Compute Bᵢⱼ⁽⁰⁾ and Bᵢⱼ⁽¹⁾
2912
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,k₁:k₂], A[k₁:k₂,j₁:j₂])
111✔
2913
            # Retrieve Rᵢ,ᵢ₊ₖ as A[i+k,i]'
2914
            mul!(Bᵢⱼ⁽¹⁾, A[i₁:i₂,k₁:k₂], A[j₁:j₂,k₁:k₂]')
111✔
2915
            # Solve Uᵢ,ᵢ₊ₖ using Reference [1, (4.10)]
2916
            kron!(L₀, Δ[1:s₂,1:s₂], S[1:s₁,i₁:i₂])
111✔
2917
            L₀ .+= kron!(L₁, A[j₁:j₂,j₁:j₂]', A[i₁:i₂,i₁:i₂])
222✔
2918
            L₀ .+= kron!(L₁, S[1:s₂,j₁:j₂]', Δ[1:s₁,1:s₁])
222✔
2919
            mul!(A[i₁:i₂,j₁:j₂], A[i₁:i₂,i₁:i₂], Bᵢⱼ⁽⁰⁾, -1.0, 1.0)
111✔
2920
            A[i₁:i₂,j₁:j₂] .-= Bᵢⱼ⁽¹⁾
222✔
2921
            ldiv!(lu!(L₀), A[i₁:i₂,j₁:j₂][:])
111✔
2922
            # Compute and store Rᵢ,ᵢ₊ₖ' in A[i+k,i]
2923
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,i₁:i₂], A[i₁:i₂,j₁:j₂], 1.0, 1.0)
111✔
2924
            mul!(Bᵢⱼ⁽⁰⁾, A[i₁:i₂,j₁:j₂], A[j₁:j₂,j₁:j₂], 1.0, 1.0)
111✔
2925
            A[j₁:j₂,i₁:i₂] .= Bᵢⱼ⁽⁰⁾'
111✔
2926
        end
243✔
2927
    end
51✔
2928
    # Make quasi triangular
2929
    for j=1:n for i=j+1+(sizes[j]==2):n A[i,j] = 0 end end
30✔
2930
    return A
3✔
2931
end
2932

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