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

JuliaLang / julia / #37728

26 Mar 2024 03:46AM UTC coverage: 80.612% (-0.8%) from 81.423%
#37728

push

local

web-flow
Update zlib to 1.3.1 (#53841)

Released January 22, 2024

69920 of 86737 relevant lines covered (80.61%)

14456248.65 hits per line

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

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

3
## Diagonal matrices
4

5
struct Diagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T}
6
    diag::V
7

8
    function Diagonal{T,V}(diag) where {T,V<:AbstractVector{T}}
6✔
9
        require_one_based_indexing(diag)
11,112✔
10
        new{T,V}(diag)
11,112✔
11
    end
12
end
13
Diagonal(v::AbstractVector{T}) where {T} = Diagonal{T,typeof(v)}(v)
11,098✔
14
Diagonal{T}(v::AbstractVector) where {T} = Diagonal(convert(AbstractVector{T}, v)::AbstractVector{T})
406✔
15

16
function Base.promote_rule(A::Type{<:Diagonal{<:Any,V}}, B::Type{<:Diagonal{<:Any,W}}) where {V,W}
17
    X = promote_type(V, W)
6✔
18
    T = eltype(X)
6✔
19
    isconcretetype(T) && return Diagonal{T,X}
6✔
20
    return typejoin(A, B)
2✔
21
end
22

23
"""
24
    Diagonal(V::AbstractVector)
25

26
Construct a lazy matrix with `V` as its diagonal.
27

28
See also [`UniformScaling`](@ref) for the lazy identity matrix `I`,
29
[`diagm`](@ref) to make a dense matrix, and [`diag`](@ref) to extract diagonal elements.
30

31
# Examples
32
```jldoctest
33
julia> d = Diagonal([1, 10, 100])
34
3×3 Diagonal{$Int, Vector{$Int}}:
35
 1   ⋅    ⋅
36
 ⋅  10    ⋅
37
 ⋅   ⋅  100
38

39
julia> diagm([7, 13])
40
2×2 Matrix{$Int}:
41
 7   0
42
 0  13
43

44
julia> ans + I
45
2×2 Matrix{Int64}:
46
 8   0
47
 0  14
48

49
julia> I(2)
50
2×2 Diagonal{Bool, Vector{Bool}}:
51
 1  ⋅
52
 ⋅  1
53
```
54

55
!!! note
56
    A one-column matrix is not treated like a vector, but instead calls the
57
    method `Diagonal(A::AbstractMatrix)` which extracts 1-element `diag(A)`:
58

59
```jldoctest
60
julia> A = transpose([7.0 13.0])
61
2×1 transpose(::Matrix{Float64}) with eltype Float64:
62
  7.0
63
 13.0
64

65
julia> Diagonal(A)
66
1×1 Diagonal{Float64, Vector{Float64}}:
67
 7.0
68
```
69
"""
1,983,287✔
70
Diagonal(V::AbstractVector)
71

72
"""
73
    Diagonal(A::AbstractMatrix)
74

75
Construct a matrix from the principal diagonal of `A`.
3,085✔
76
The input matrix `A` may be rectangular, but the output will
77
be square.
78

79
# Examples
80
```jldoctest
81
julia> A = [1 2; 3 4]
82
2×2 Matrix{Int64}:
83
 1  2
84
 3  4
85

86
julia> D = Diagonal(A)
87
2×2 Diagonal{Int64, Vector{Int64}}:
88
 1  ⋅
89
 ⋅  4
90

91
julia> A = [1 2 3; 4 5 6]
92
2×3 Matrix{Int64}:
133,238✔
93
 1  2  3
94
 4  5  6
95

96
julia> Diagonal(A)
97
2×2 Diagonal{Int64, Vector{Int64}}:
98
 1  ⋅
99
 ⋅  5
507✔
100
```
101
"""
102
Diagonal(A::AbstractMatrix) = Diagonal(diag(A))
1,079✔
103
Diagonal{T}(A::AbstractMatrix) where T = Diagonal{T}(diag(A))
6✔
104
Diagonal{T,V}(A::AbstractMatrix) where {T,V<:AbstractVector{T}} = Diagonal{T,V}(diag(A))
8✔
105
function convert(::Type{T}, A::AbstractMatrix) where T<:Diagonal
18✔
106
    checksquare(A)
24✔
107
    isdiag(A) ? T(A) : throw(InexactError(:convert, T, A))
12✔
108
end
109

110
Diagonal(D::Diagonal) = D
8✔
111
Diagonal{T}(D::Diagonal{T}) where {T} = D
68✔
112
Diagonal{T}(D::Diagonal) where {T} = Diagonal{T}(D.diag)
71✔
113

114
AbstractMatrix{T}(D::Diagonal) where {T} = Diagonal{T}(D)
8✔
115
AbstractMatrix{T}(D::Diagonal{T}) where {T} = copy(D)
486✔
116
Matrix(D::Diagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(D)
3,093✔
117
Array(D::Diagonal{T}) where {T} = Matrix(D)
775✔
118
function Matrix{T}(D::Diagonal) where {T}
3,130✔
119
    n = size(D, 1)
3,247✔
120
    B = Matrix{T}(undef, n, n)
6,191✔
121
    n > 1 && fill!(B, zero(T))
205,020✔
122
    @inbounds for i in 1:n
3,130✔
123
        B[i,i] = D.diag[i]
20,385✔
124
    end
37,709✔
125
    return B
3,130✔
126
end
127

128
"""
129
    Diagonal{T}(undef, n)
130

131
Construct an uninitialized `Diagonal{T}` of length `n`. See `undef`.
132
"""
133
Diagonal{T}(::UndefInitializer, n::Integer) where T = Diagonal(Vector{T}(undef, n))
1✔
134

135
similar(D::Diagonal, ::Type{T}) where {T} = Diagonal(similar(D.diag, T))
380✔
136
similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(D.diag, T, dims)
2,302✔
137

138
copyto!(D1::Diagonal, D2::Diagonal) = (copyto!(D1.diag, D2.diag); D1)
303✔
139

140
size(D::Diagonal) = (n = length(D.diag); (n,n))
50,807✔
141

142
axes(D::Diagonal) = (ax = axes(D.diag, 1); (ax, ax))
66,466,962✔
143

144
@inline function Base.isassigned(D::Diagonal, i::Int, j::Int)
716✔
145
    @boundscheck checkbounds(Bool, D, i, j) || return false
470,075✔
146
    if i == j
470,075✔
147
        @inbounds r = isassigned(D.diag, i)
47,449✔
148
    else
149
        r = true
422,626✔
150
    end
151
    r
470,075✔
152
end
153

154
@inline function Base.isstored(D::Diagonal, i::Int, j::Int)
18✔
155
    @boundscheck checkbounds(D, i, j)
24✔
156
    if i == j
12✔
157
        @inbounds r = Base.isstored(D.diag, i)
6✔
158
    else
159
        r = false
6✔
160
    end
161
    r
12✔
162
end
163

164
function Base.minimum(D::Diagonal{T}) where T <: Number
3✔
165
    mindiag = minimum(D.diag)
3✔
166
    size(D, 1) > 1 && return (min(zero(T), mindiag))
3✔
167
    return mindiag
×
168
end
169

170
function Base.maximum(D::Diagonal{T}) where T <: Number
3✔
171
    maxdiag = Base.maximum(D.diag)
3✔
172
    size(D, 1) > 1 && return (max(zero(T), maxdiag))
3✔
173
    return maxdiag
×
174
end
175

176
@inline function getindex(D::Diagonal, i::Int, j::Int)
737✔
177
    @boundscheck checkbounds(D, i, j)
65,956,572✔
178
    if i == j
65,956,562✔
179
        @inbounds r = D.diag[i]
157,117✔
180
    else
181
        r = diagzero(D, i, j)
65,799,445✔
182
    end
183
    r
65,956,562✔
184
end
185
diagzero(::Diagonal{T}, i, j) where {T} = zero(T)
65,799,400✔
186
diagzero(D::Diagonal{<:AbstractMatrix{T}}, i, j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2))
45✔
187

188
function setindex!(D::Diagonal, v, i::Int, j::Int)
10✔
189
    @boundscheck checkbounds(D, i, j)
346✔
190
    if i == j
336✔
191
        @inbounds D.diag[i] = v
39✔
192
    elseif !iszero(v)
297✔
193
        throw(ArgumentError("cannot set off-diagonal entry ($i, $j) to a nonzero value ($v)"))
135✔
194
    end
195
    return v
201✔
196
end
197

198

199
## structured matrix methods ##
200
function Base.replace_in_print_matrix(A::Diagonal,i::Integer,j::Integer,s::AbstractString)
358✔
201
    i==j ? s : Base.replace_with_centered_mark(s)
642✔
202
end
203

204
parent(D::Diagonal) = D.diag
283✔
205

206
copy(D::Diagonal) = Diagonal(copy(D.diag))
269✔
207

208
ishermitian(D::Diagonal{<:Real}) = true
×
209
ishermitian(D::Diagonal{<:Number}) = isreal(D.diag)
6✔
210
ishermitian(D::Diagonal) = all(ishermitian, D.diag)
3✔
211
issymmetric(D::Diagonal{<:Number}) = true
×
212
issymmetric(D::Diagonal) = all(issymmetric, D.diag)
2✔
213
isposdef(D::Diagonal) = all(isposdef, D.diag)
6✔
214

215
factorize(D::Diagonal) = D
6✔
216

217
real(D::Diagonal) = Diagonal(real(D.diag))
12✔
218
imag(D::Diagonal) = Diagonal(imag(D.diag))
6✔
219

220
iszero(D::Diagonal) = all(iszero, D.diag)
42✔
221
isone(D::Diagonal) = all(isone, D.diag)
28✔
222
isdiag(D::Diagonal) = all(isdiag, D.diag)
13✔
223
isdiag(D::Diagonal{<:Number}) = true
×
224
istriu(D::Diagonal, k::Integer=0) = k <= 0 || iszero(D.diag) ? true : false
74✔
225
istril(D::Diagonal, k::Integer=0) = k >= 0 || iszero(D.diag) ? true : false
74✔
226
function triu!(D::Diagonal{T}, k::Integer=0) where T
30✔
227
    n = size(D,1)
30✔
228
    if !(-n + 1 <= k <= n + 1)
30✔
229
        throw(ArgumentError(string("the requested diagonal, $k, must be at least ",
12✔
230
            "$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix")))
231
    elseif k > 0
18✔
232
        fill!(D.diag, zero(T))
72✔
233
    end
234
    return D
18✔
235
end
236

237
function tril!(D::Diagonal{T}, k::Integer=0) where T
30✔
238
    n = size(D,1)
30✔
239
    if !(-n - 1 <= k <= n - 1)
30✔
240
        throw(ArgumentError(string("the requested diagonal, $k, must be at least ",
12✔
241
            "$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix")))
242
    elseif k < 0
18✔
243
        fill!(D.diag, zero(T))
72✔
244
    end
245
    return D
18✔
246
end
247

248
(==)(Da::Diagonal, Db::Diagonal) = Da.diag == Db.diag
173✔
249
(-)(A::Diagonal) = Diagonal(-A.diag)
20✔
250
(+)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag + Db.diag)
80✔
251
(-)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag - Db.diag)
141✔
252

253
for f in (:+, :-)
254
    @eval function $f(D::Diagonal, S::Symmetric)
24✔
255
        return Symmetric($f(D, S.data), sym_uplo(S.uplo))
24✔
256
    end
257
    @eval function $f(S::Symmetric, D::Diagonal)
24✔
258
        return Symmetric($f(S.data, D), sym_uplo(S.uplo))
24✔
259
    end
260
    @eval function $f(D::Diagonal{<:Real}, H::Hermitian)
12✔
261
        return Hermitian($f(D, H.data), sym_uplo(H.uplo))
12✔
262
    end
263
    @eval function $f(H::Hermitian, D::Diagonal{<:Real})
12✔
264
        return Hermitian($f(H.data, D), sym_uplo(H.uplo))
12✔
265
    end
266
end
267

268
(*)(x::Number, D::Diagonal) = Diagonal(x * D.diag)
80✔
269
(*)(D::Diagonal, x::Number) = Diagonal(D.diag * x)
6✔
270
(/)(D::Diagonal, x::Number) = Diagonal(D.diag / x)
9✔
271
(\)(x::Number, D::Diagonal) = Diagonal(x \ D.diag)
3✔
272
(^)(D::Diagonal, a::Number) = Diagonal(D.diag .^ a)
6✔
273
(^)(D::Diagonal, a::Real) = Diagonal(D.diag .^ a) # for disambiguation
12✔
274
(^)(D::Diagonal, a::Integer) = Diagonal(D.diag .^ a) # for disambiguation
6✔
275
Base.literal_pow(::typeof(^), D::Diagonal, valp::Val) =
6✔
276
    Diagonal(Base.literal_pow.(^, D.diag, valp)) # for speed
277
Base.literal_pow(::typeof(^), D::Diagonal, ::Val{-1}) = inv(D) # for disambiguation
6✔
278

279
function _muldiag_size_check(A, B)
280
    nA = size(A, 2)
3,380✔
281
    mB = size(B, 1)
3,380✔
282
    @noinline throw_dimerr(::AbstractMatrix, nA, mB) = throw(DimensionMismatch("second dimension of A, $nA, does not match first dimension of B, $mB"))
3,397✔
283
    @noinline throw_dimerr(::AbstractVector, nA, mB) = throw(DimensionMismatch("second dimension of D, $nA, does not match length of V, $mB"))
2✔
284
    nA == mB || throw_dimerr(B, nA, mB)
3,397✔
285
    return nothing
3,363✔
286
end
287
# the output matrix should have the same size as the non-diagonal input matrix or vector
288
@noinline throw_dimerr(szC, szA) = throw(DimensionMismatch("output matrix has size: $szC, but should have size $szA"))
8✔
289
_size_check_out(C, ::Diagonal, A) = _size_check_out(C, A)
1,032✔
290
_size_check_out(C, A, ::Diagonal) = _size_check_out(C, A)
1,531✔
291
_size_check_out(C, A::Diagonal, ::Diagonal) = _size_check_out(C, A)
252✔
292
function _size_check_out(C, A)
293
    szA = size(A)
2,807✔
294
    szC = size(C)
2,807✔
295
    szA == szC || throw_dimerr(szC, szA)
2,815✔
296
    return nothing
2,799✔
297
end
298
function _muldiag_size_check(C, A, B)
299
    _muldiag_size_check(A, B)
2,827✔
300
    _size_check_out(C, A, B)
2,815✔
301
end
302

303
function (*)(Da::Diagonal, Db::Diagonal)
162✔
304
    _muldiag_size_check(Da, Db)
163✔
305
    return Diagonal(Da.diag .* Db.diag)
161✔
306
end
307

308
function (*)(D::Diagonal, V::AbstractVector)
127✔
309
    _muldiag_size_check(D, V)
131✔
310
    return D.diag .* V
129✔
311
end
312

313
rmul!(A::AbstractMatrix, D::Diagonal) = @inline mul!(A, A, D)
59✔
314
lmul!(D::Diagonal, B::AbstractVecOrMat) = @inline mul!(B, D, B)
70✔
315

316
function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
1,017✔
317
    require_one_based_indexing(out, B)
1,019✔
318
    alpha, beta = _add.alpha, _add.beta
1,015✔
319
    if iszero(alpha)
1,015✔
320
        _rmul_or_fill!(out, beta)
×
321
    else
322
        if bis0
1,015✔
323
            @inbounds for j in axes(B, 2)
924✔
324
                @simd for i in axes(B, 1)
29,425✔
325
                    out[i,j] = D.diag[i] * B[i,j] * alpha
326
                end
327
            end
29,425✔
328
        else
329
            @inbounds for j in axes(B, 2)
91✔
330
                @simd for i in axes(B, 1)
321✔
331
                    out[i,j] = D.diag[i] * B[i,j] * alpha + out[i,j] * beta
332
                end
333
            end
321✔
334
        end
335
    end
336
    return out
1,015✔
337
end
338
function __muldiag!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
1,533✔
339
    require_one_based_indexing(out, A)
1,535✔
340
    alpha, beta = _add.alpha, _add.beta
1,531✔
341
    if iszero(alpha)
1,531✔
342
        _rmul_or_fill!(out, beta)
54✔
343
    else
344
        if bis0
1,504✔
345
            @inbounds for j in axes(A, 2)
1,451✔
346
                dja = D.diag[j] * alpha
9,269✔
347
                @simd for i in axes(A, 1)
9,269✔
348
                    out[i,j] = A[i,j] * dja
349
                end
350
            end
9,269✔
351
        else
352
            @inbounds for j in axes(A, 2)
53✔
353
                dja = D.diag[j] * alpha
163✔
354
                @simd for i in axes(A, 1)
163✔
355
                    out[i,j] = A[i,j] * dja + out[i,j] * beta
356
                end
357
            end
163✔
358
        end
359
    end
360
    return out
1,531✔
361
end
362
function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
252✔
363
    d1 = D1.diag
252✔
364
    d2 = D2.diag
252✔
365
    alpha, beta = _add.alpha, _add.beta
252✔
366
    if iszero(alpha)
252✔
367
        _rmul_or_fill!(out.diag, beta)
108✔
368
    else
369
        if bis0
198✔
370
            @inbounds @simd for i in eachindex(out.diag)
159✔
371
                out.diag[i] = d1[i] * d2[i] * alpha
372
            end
373
        else
374
            @inbounds @simd for i in eachindex(out.diag)
39✔
375
                out.diag[i] = d1[i] * d2[i] * alpha + out.diag[i] * beta
376
            end
377
        end
378
    end
379
    return out
252✔
380
end
381
function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
×
382
    require_one_based_indexing(out)
×
383
    alpha, beta = _add.alpha, _add.beta
×
384
    mA = size(D1, 1)
×
385
    d1 = D1.diag
×
386
    d2 = D2.diag
×
387
    _rmul_or_fill!(out, beta)
×
388
    if !iszero(alpha)
×
389
        @inbounds @simd for i in 1:mA
×
390
            out[i,i] += d1[i] * d2[i] * alpha
×
391
        end
×
392
    end
393
    return out
×
394
end
395

396
function _mul_diag!(out, A, B, _add)
397
    _muldiag_size_check(out, A, B)
2,825✔
398
    __muldiag!(out, A, B, _add)
2,799✔
399
    return out
2,795✔
400
end
401

402
_mul!(out::AbstractVecOrMat, D::Diagonal, V::AbstractVector, _add) =
182✔
403
    _mul_diag!(out, D, V, _add)
404
_mul!(out::AbstractMatrix, D::Diagonal, B::AbstractMatrix, _add) =
847✔
405
    _mul_diag!(out, D, B, _add)
406
_mul!(out::AbstractMatrix, A::AbstractMatrix, D::Diagonal, _add) =
1,536✔
407
    _mul_diag!(out, A, D, _add)
408
_mul!(C::Diagonal, Da::Diagonal, Db::Diagonal, _add) =
252✔
409
    _mul_diag!(C, Da, Db, _add)
410
_mul!(C::AbstractMatrix, Da::Diagonal, Db::Diagonal, _add) =
×
411
    _mul_diag!(C, Da, Db, _add)
412

413
function (*)(Da::Diagonal, A::AbstractMatrix, Db::Diagonal)
84✔
414
    _muldiag_size_check(Da, A)
85✔
415
    _muldiag_size_check(A, Db)
84✔
416
    return broadcast(*, Da.diag, A, permutedims(Db.diag))
82✔
417
end
418

419
function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal)
53✔
420
    _muldiag_size_check(Da, Db)
55✔
421
    _muldiag_size_check(Db, Dc)
52✔
422
    return Diagonal(Da.diag .* Db.diag .* Dc.diag)
50✔
423
end
424

425
/(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(matprod_dest(A, D, promote_op(/, eltype(A), eltype(D))), A, D)
307✔
426

427
rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D)
161✔
428
# avoid copy when possible via internal 3-arg backend
429
function _rdiv!(B::AbstractVecOrMat, A::AbstractVecOrMat, D::Diagonal)
216✔
430
    require_one_based_indexing(A)
377✔
431
    dd = D.diag
377✔
432
    m, n = size(A, 1), size(A, 2)
377✔
433
    if (k = length(dd)) != n
377✔
434
        throw(DimensionMismatch("left hand side has $n columns but D is $k by $k"))
8✔
435
    end
436
    @inbounds for j in 1:n
369✔
437
        ddj = dd[j]
2,819✔
438
        iszero(ddj) && throw(SingularException(j))
2,819✔
439
        for i in 1:m
2,811✔
440
            B[i, j] = A[i, j] / ddj
23,629✔
441
        end
44,447✔
442
    end
5,261✔
443
    B
361✔
444
end
445

446
function \(D::Diagonal, B::AbstractVector)
166✔
447
    j = findfirst(iszero, D.diag)
1,642✔
448
    isnothing(j) || throw(SingularException(j))
167✔
449
    return D.diag .\ B
165✔
450
end
451
\(D::Diagonal, B::AbstractMatrix) = ldiv!(matprod_dest(D, B, promote_op(\, eltype(D), eltype(B))), D, B)
1,094✔
452

453
ldiv!(D::Diagonal, B::AbstractVecOrMat) = @inline ldiv!(B, D, B)
251✔
454
function ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat)
691✔
455
    require_one_based_indexing(A, B)
942✔
456
    dd = D.diag
942✔
457
    d = length(dd)
942✔
458
    m, n = size(A, 1), size(A, 2)
942✔
459
    m′, n′ = size(B, 1), size(B, 2)
942✔
460
    m == d || throw(DimensionMismatch("right hand side has $m rows but D is $d by $d"))
984✔
461
    (m, n) == (m′, n′) || throw(DimensionMismatch("expect output to be $m by $n, but got $m′ by $n′"))
900✔
462
    j = findfirst(iszero, D.diag)
7,786✔
463
    isnothing(j) || throw(SingularException(j))
924✔
464
    @inbounds for j = 1:n, i = 1:m
876✔
465
        B[i, j] = dd[i] \ A[i, j]
48,088✔
466
    end
53,334✔
467
    B
876✔
468
end
469

470
function _rdiv!(Dc::Diagonal, Db::Diagonal, Da::Diagonal)
6✔
471
    n, k = length(Db.diag), length(Da.diag)
6✔
472
    n == k || throw(DimensionMismatch("left hand side has $n columns but D is $k by $k"))
6✔
473
    j = findfirst(iszero, Da.diag)
78✔
474
    isnothing(j) || throw(SingularException(j))
6✔
475
    Dc.diag .= Db.diag ./ Da.diag
12✔
476
    Dc
6✔
477
end
478
ldiv!(Dc::Diagonal, Da::Diagonal, Db::Diagonal) = Diagonal(ldiv!(Dc.diag, Da, Db.diag))
27✔
479

480
# optimizations for (Sym)Tridiagonal and Diagonal
481
@propagate_inbounds _getudiag(T::Tridiagonal, i) = T.du[i]
72✔
482
@propagate_inbounds _getudiag(S::SymTridiagonal, i) = S.ev[i]
72✔
483
@propagate_inbounds _getdiag(T::Tridiagonal, i) = T.d[i]
98✔
484
@propagate_inbounds _getdiag(S::SymTridiagonal, i) = symmetric(S.dv[i], :U)::symmetric_type(eltype(S.dv))
98✔
485
@propagate_inbounds _getldiag(T::Tridiagonal, i) = T.dl[i]
72✔
486
@propagate_inbounds _getldiag(S::SymTridiagonal, i) = transpose(S.ev[i])
72✔
487

488
function (\)(D::Diagonal, S::SymTridiagonal)
22✔
489
    T = promote_op(\, eltype(D), eltype(S))
22✔
490
    du = similar(S.ev, T, max(length(S.dv)-1, 0))
35✔
491
    d  = similar(S.dv, T, length(S.dv))
43✔
492
    dl = similar(S.ev, T, max(length(S.dv)-1, 0))
35✔
493
    ldiv!(Tridiagonal(dl, d, du), D, S)
22✔
494
end
495
(\)(D::Diagonal, T::Tridiagonal) = ldiv!(similar(T, promote_op(\, eltype(D), eltype(T))), D, T)
22✔
496
function ldiv!(T::Tridiagonal, D::Diagonal, S::Union{SymTridiagonal,Tridiagonal})
44✔
497
    m = size(S, 1)
44✔
498
    dd = D.diag
44✔
499
    if (k = length(dd)) != m
44✔
500
        throw(DimensionMismatch("diagonal matrix is $k by $k but right hand side has $m rows"))
×
501
    end
502
    if length(T.d) != m
44✔
503
        throw(DimensionMismatch("target matrix size $(size(T)) does not match input matrix size $(size(S))"))
×
504
    end
505
    m == 0 && return T
44✔
506
    j = findfirst(iszero, dd)
156✔
507
    isnothing(j) || throw(SingularException(j))
58✔
508
    ddj = dd[1]
26✔
509
    T.d[1] = ddj \ _getdiag(S, 1)
26✔
510
    @inbounds if m > 1
26✔
511
        T.du[1] = ddj \ _getudiag(S, 1)
18✔
512
        for j in 2:m-1
18✔
513
            ddj = dd[j]
54✔
514
            T.dl[j-1] = ddj \ _getldiag(S, j-1)
54✔
515
            T.d[j]  = ddj \ _getdiag(S, j)
54✔
516
            T.du[j] = ddj \ _getudiag(S, j)
54✔
517
        end
90✔
518
        ddj = dd[m]
18✔
519
        T.dl[m-1] = ddj \ _getldiag(S, m-1)
18✔
520
        T.d[m] = ddj \ _getdiag(S, m)
18✔
521
    end
522
    return T
26✔
523
end
524

525
function (/)(S::SymTridiagonal, D::Diagonal)
22✔
526
    T = promote_op(\, eltype(D), eltype(S))
22✔
527
    du = similar(S.ev, T, max(length(S.dv)-1, 0))
35✔
528
    d  = similar(S.dv, T, length(S.dv))
43✔
529
    dl = similar(S.ev, T, max(length(S.dv)-1, 0))
35✔
530
    _rdiv!(Tridiagonal(dl, d, du), S, D)
22✔
531
end
532
(/)(T::Tridiagonal, D::Diagonal) = _rdiv!(matprod_dest(T, D, promote_op(/, eltype(T), eltype(D))), T, D)
22✔
533
function _rdiv!(T::Tridiagonal, S::Union{SymTridiagonal,Tridiagonal}, D::Diagonal)
44✔
534
    n = size(S, 2)
44✔
535
    dd = D.diag
44✔
536
    if (k = length(dd)) != n
44✔
537
        throw(DimensionMismatch("left hand side has $n columns but D is $k by $k"))
×
538
    end
539
    if length(T.d) != n
44✔
540
        throw(DimensionMismatch("target matrix size $(size(T)) does not match input matrix size $(size(S))"))
×
541
    end
542
    n == 0 && return T
44✔
543
    j = findfirst(iszero, dd)
156✔
544
    isnothing(j) || throw(SingularException(j))
58✔
545
    ddj = dd[1]
26✔
546
    T.d[1] = _getdiag(S, 1) / ddj
26✔
547
    @inbounds if n > 1
26✔
548
        T.dl[1] = _getldiag(S, 1) / ddj
18✔
549
        for j in 2:n-1
18✔
550
            ddj = dd[j]
54✔
551
            T.dl[j] = _getldiag(S, j) / ddj
54✔
552
            T.d[j] = _getdiag(S, j) / ddj
54✔
553
            T.du[j-1] = _getudiag(S, j-1) / ddj
54✔
554
        end
90✔
555
        ddj = dd[n]
18✔
556
        T.d[n] = _getdiag(S, n) / ddj
18✔
557
        T.du[n-1] = _getudiag(S, n-1) / ddj
18✔
558
    end
559
    return T
26✔
560
end
561

562
# Optimizations for [l/r]mul!, l/rdiv!, *, / and \ between Triangular and Diagonal.
563
# These functions are generally more efficient if we calculate the whole data field.
564
# The following code implements them in a unified pattern to avoid missing.
565
@inline function _setdiag!(data, f, diag, diag′ = nothing)
566
    @inbounds for i in 1:length(diag)
836✔
567
        data[i,i] = isnothing(diag′) ? f(diag[i]) : f(diag[i],diag′[i])
914✔
568
    end
1,604✔
569
    data
224✔
570
end
571
for Tri in (:UpperTriangular, :LowerTriangular)
572
    UTri = Symbol(:Unit, Tri)
573
    # 2 args
574
    for (fun, f) in zip((:*, :rmul!, :rdiv!, :/), (:identity, :identity, :inv, :inv))
575
        @eval $fun(A::$Tri, D::Diagonal) = $Tri($fun(A.data, D))
74✔
576
        @eval $fun(A::$UTri, D::Diagonal) = $Tri(_setdiag!($fun(A.data, D), $f, D.diag))
62✔
577
    end
578
    for (fun, f) in zip((:*, :lmul!, :ldiv!, :\), (:identity, :identity, :inv, :inv))
579
        @eval $fun(D::Diagonal, A::$Tri) = $Tri($fun(D, A.data))
74✔
580
        @eval $fun(D::Diagonal, A::$UTri) = $Tri(_setdiag!($fun(D, A.data), $f, D.diag))
62✔
581
    end
582
    # 3-arg ldiv!
583
    @eval ldiv!(C::$Tri, D::Diagonal, A::$Tri) = $Tri(ldiv!(C.data, D, A.data))
2✔
584
    @eval ldiv!(C::$Tri, D::Diagonal, A::$UTri) = $Tri(_setdiag!(ldiv!(C.data, D, A.data), inv, D.diag))
2✔
585
    # 3-arg mul! is disambiguated in special.jl
586
    # 5-arg mul!
587
    @eval _mul!(C::$Tri, D::Diagonal, A::$Tri, _add) = $Tri(mul!(C.data, D, A.data, _add.alpha, _add.beta))
4✔
588
    @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
4✔
589
        α, β = _add.alpha, _add.beta
4✔
590
        iszero(α) && return _rmul_or_fill!(C, β)
4✔
591
        diag′ = bis0 ? nothing : diag(C)
4✔
592
        data = mul!(C.data, D, A.data, α, β)
4✔
593
        $Tri(_setdiag!(data, _add, D.diag, diag′))
4✔
594
    end
595
    @eval _mul!(C::$Tri, A::$Tri, D::Diagonal, _add) = $Tri(mul!(C.data, A.data, D, _add.alpha, _add.beta))
112✔
596
    @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
139✔
597
        α, β = _add.alpha, _add.beta
139✔
598
        iszero(α) && return _rmul_or_fill!(C, β)
139✔
599
        diag′ = bis0 ? nothing : diag(C)
94✔
600
        data = mul!(C.data, A.data, D, α, β)
108✔
601
        $Tri(_setdiag!(data, _add, D.diag, diag′))
94✔
602
    end
603
end
604

605
@inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal)
2✔
606
    valA = A.diag; nA = length(valA)
2✔
607
    valB = B.diag; nB = length(valB)
2✔
608
    nC = checksquare(C)
2✔
609
    @boundscheck nC == nA*nB ||
2✔
610
        throw(DimensionMismatch("expect C to be a $(nA*nB)x$(nA*nB) matrix, got size $(nC)x$(nC)"))
611
    isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1]))
4✔
612
    @inbounds for i = 1:nA, j = 1:nB
2✔
613
        idx = (i-1)*nB+j
8✔
614
        C[idx, idx] = valA[i] * valB[j]
8✔
615
    end
10✔
616
    return C
2✔
617
end
618

619
kron(A::Diagonal, B::Diagonal) = Diagonal(kron(A.diag, B.diag))
8✔
620

621
function kron(A::Diagonal, B::SymTridiagonal)
4✔
622
    kdv = kron(diag(A), B.dv)
8✔
623
    # We don't need to drop the last element
624
    kev = kron(diag(A), _pushzero(_evview(B)))
8✔
625
    SymTridiagonal(kdv, kev)
4✔
626
end
627
function kron(A::Diagonal, B::Tridiagonal)
2✔
628
    # `_droplast!` is only guaranteed to work with `Vector`
629
    kd = _makevector(kron(diag(A), B.d))
4✔
630
    kdl = _droplast!(_makevector(kron(diag(A), _pushzero(B.dl))))
4✔
631
    kdu = _droplast!(_makevector(kron(diag(A), _pushzero(B.du))))
4✔
632
    Tridiagonal(kdl, kd, kdu)
2✔
633
end
634

635
@inline function kron!(C::AbstractMatrix, A::Diagonal, B::AbstractMatrix)
636
    require_one_based_indexing(B)
18✔
637
    (mA, nA) = size(A)
18✔
638
    (mB, nB) = size(B)
18✔
639
    (mC, nC) = size(C)
18✔
640
    @boundscheck (mC, nC) == (mA * mB, nA * nB) ||
18✔
641
        throw(DimensionMismatch("expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)"))
642
    isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1]))
36✔
643
    m = 1
18✔
644
    @inbounds for j = 1:nA
18✔
645
        A_jj = A[j,j]
60✔
646
        for k = 1:nB
60✔
647
            for l = 1:mB
264✔
648
                C[m] = A_jj * B[l,k]
1,392✔
649
                m += 1
1,392✔
650
            end
2,520✔
651
            m += (nA - 1) * mB
264✔
652
        end
468✔
653
        m += mB
60✔
654
    end
102✔
655
    return C
18✔
656
end
657

658
@inline function kron!(C::AbstractMatrix, A::AbstractMatrix, B::Diagonal)
659
    require_one_based_indexing(A)
6✔
660
    (mA, nA) = size(A)
6✔
661
    (mB, nB) = size(B)
6✔
662
    (mC, nC) = size(C)
6✔
663
    @boundscheck (mC, nC) == (mA * mB, nA * nB) ||
6✔
664
        throw(DimensionMismatch("expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)"))
665
    isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1]))
12✔
666
    m = 1
6✔
667
    @inbounds for j = 1:nA
6✔
668
        for l = 1:mB
36✔
669
            Bll = B[l,l]
216✔
670
            for k = 1:mA
216✔
671
                C[m] = A[k,j] * Bll
1,296✔
672
                m += nB
1,296✔
673
            end
2,376✔
674
            m += 1
216✔
675
        end
396✔
676
        m -= nB
36✔
677
    end
66✔
678
    return C
6✔
679
end
680

681
conj(D::Diagonal) = Diagonal(conj(D.diag))
50✔
682
transpose(D::Diagonal{<:Number}) = D
287✔
683
transpose(D::Diagonal) = Diagonal(transpose.(D.diag))
23✔
684
adjoint(D::Diagonal{<:Number}) = Diagonal(vec(adjoint(D.diag)))
537✔
685
adjoint(D::Diagonal{<:Number,<:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = Diagonal(adjoint(parent(D.diag)))
2✔
686
adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag))
23✔
687
permutedims(D::Diagonal) = D
3✔
688
permutedims(D::Diagonal, perm) = (Base.checkdims_perm(D, D, perm); D)
15✔
689

690
function diag(D::Diagonal{T}, k::Integer=0) where T
147✔
691
    # every branch call similar(..., ::Int) to make sure the
692
    # same vector type is returned independent of k
693
    if k == 0
211✔
694
        return copyto!(similar(D.diag, length(D.diag)), D.diag)
97✔
695
    elseif -size(D,1) <= k <= size(D,1)
26✔
696
        return fill!(similar(D.diag, size(D,1)-abs(k)), zero(T))
14✔
697
    else
698
        throw(ArgumentError(string("requested diagonal, $k, must be at least $(-size(D, 1)) ",
12✔
699
            "and at most $(size(D, 2)) for an $(size(D, 1))-by-$(size(D, 2)) matrix")))
700
    end
701
end
702
tr(D::Diagonal) = sum(tr, D.diag)
7✔
703
det(D::Diagonal) = prod(det, D.diag)
7✔
704
function logdet(D::Diagonal{<:Complex}) # make sure branch cut is correct
2✔
705
    z = sum(log, D.diag)
2✔
706
    complex(real(z), rem2pi(imag(z), RoundNearest))
2✔
707
end
708

709
# Matrix functions
710
for f in (:exp, :cis, :log, :sqrt,
711
          :cos, :sin, :tan, :csc, :sec, :cot,
712
          :cosh, :sinh, :tanh, :csch, :sech, :coth,
713
          :acos, :asin, :atan, :acsc, :asec, :acot,
714
          :acosh, :asinh, :atanh, :acsch, :asech, :acoth)
715
    @eval $f(D::Diagonal) = Diagonal($f.(D.diag))
84✔
716
end
717

718
# Cube root of a real-valued diagonal matrix
719
cbrt(A::Diagonal{<:Real}) = Diagonal(cbrt.(A.diag))
1✔
720

721
function inv(D::Diagonal{T}) where T
32✔
722
    Di = similar(D.diag, typeof(inv(oneunit(T))))
109✔
723
    for i = 1:length(D.diag)
55✔
724
        if iszero(D.diag[i])
1,027✔
725
            throw(SingularException(i))
3✔
726
        end
727
        Di[i] = inv(D.diag[i])
1,024✔
728
    end
1,997✔
729
    Diagonal(Di)
52✔
730
end
731

732
function pinv(D::Diagonal{T}) where T
21✔
733
    Di = similar(D.diag, typeof(inv(oneunit(T))))
53✔
734
    for i = 1:length(D.diag)
27✔
735
        if !iszero(D.diag[i])
4,852✔
736
            invD = inv(D.diag[i])
6,000✔
737
            if isfinite(invD)
4,804✔
738
                Di[i] = invD
4,796✔
739
                continue
4,796✔
740
            end
741
        end
742
        # fallback
743
        Di[i] = zero(T)
56✔
744
    end
9,678✔
745
    Diagonal(Di)
27✔
746
end
747
function pinv(D::Diagonal{T}, tol::Real) where T
19✔
748
    Di = similar(D.diag, typeof(inv(oneunit(T))))
37✔
749
    if !isempty(D.diag)
19✔
750
        maxabsD = maximum(abs, D.diag)
18✔
751
        for i = 1:length(D.diag)
18✔
752
            if abs(D.diag[i]) > tol*maxabsD
6,039✔
753
                invD = inv(D.diag[i])
522✔
754
                if isfinite(invD)
498✔
755
                    Di[i] = invD
498✔
756
                    continue
498✔
757
                end
758
            end
759
            # fallback
760
            Di[i] = zero(T)
4,338✔
761
        end
9,654✔
762
    end
763
    Diagonal(Di)
19✔
764
end
765

766
# TODO Docstrings for eigvals, eigvecs, eigen all mention permute, scale, sortby as keyword args
767
# but not all of them below provide them. Do we need to fix that?
768
#Eigensystem
769
eigvals(D::Diagonal{<:Number}; permute::Bool=true, scale::Bool=true) = copy(D.diag)
566✔
770
eigvals(D::Diagonal; permute::Bool=true, scale::Bool=true) =
6✔
771
    reduce(vcat, eigvals(x) for x in D.diag) #For block matrices, etc.
772
function eigvecs(D::Diagonal{T}) where T<:AbstractMatrix
2✔
773
    diag_vecs = [ eigvecs(x) for x in D.diag ]
2✔
774
    matT = reduce((a,b) -> promote_type(typeof(a),typeof(b)), diag_vecs)
4✔
775
    ncols_diag = [ size(x, 2) for x in D.diag ]
2✔
776
    nrows = size(D, 1)
2✔
777
    vecs = Matrix{Vector{eltype(matT)}}(undef, nrows, sum(ncols_diag))
4✔
778
    for j in axes(D, 2), i in axes(D, 1)
2✔
779
        jj = sum(view(ncols_diag,1:j-1))
12✔
780
        if i == j
8✔
781
            for k in 1:ncols_diag[j]
4✔
782
                vecs[i,jj+k] = diag_vecs[i][:,k]
9✔
783
            end
14✔
784
        else
785
            for k in 1:ncols_diag[j]
4✔
786
                vecs[i,jj+k] = zeros(eltype(T), ncols_diag[i])
20✔
787
            end
9✔
788
        end
789
    end
10✔
790
    return vecs
2✔
791
end
792
function eigen(D::Diagonal; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=nothing)
368✔
793
    if any(!isfinite, D.diag)
1,776✔
794
        throw(ArgumentError("matrix contains Infs or NaNs"))
3✔
795
    end
796
    Td = Base.promote_op(/, eltype(D), eltype(D))
181✔
797
    λ = eigvals(D)
181✔
798
    if !isnothing(sortby)
181✔
799
        p = sortperm(λ; alg=QuickSort, by=sortby)
148✔
800
        λ = λ[p]
148✔
801
        evecs = zeros(Td, size(D))
13,053✔
802
        @inbounds for i in eachindex(p)
148✔
803
            evecs[p[i],i] = one(Td)
1,345✔
804
        end
2,542✔
805
    else
806
        evecs = Matrix{Td}(I, size(D))
33✔
807
    end
808
    Eigen(λ, evecs)
181✔
809
end
810
function eigen(D::Diagonal{<:AbstractMatrix}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=nothing)
4✔
811
    if any(any(!isfinite, x) for x in D.diag)
2✔
812
        throw(ArgumentError("matrix contains Infs or NaNs"))
×
813
    end
814
    λ = eigvals(D)
2✔
815
    evecs = eigvecs(D)
2✔
816
    if !isnothing(sortby)
2✔
817
        p = sortperm(λ; alg=QuickSort, by=sortby)
×
818
        λ = λ[p]
×
819
        evecs = evecs[:,p]
×
820
    end
821
    Eigen(λ, evecs)
2✔
822
end
823
function eigen(Da::Diagonal, Db::Diagonal; sortby::Union{Function,Nothing}=nothing)
40✔
824
    if any(!isfinite, Da.diag) || any(!isfinite, Db.diag)
116✔
825
        throw(ArgumentError("matrices contain Infs or NaNs"))
×
826
    end
827
    if any(iszero, Db.diag)
120✔
828
        throw(ArgumentError("right-hand side diagonal matrix is singular"))
×
829
    end
830
    return GeneralizedEigen(eigen(Db \ Da; sortby)...)
20✔
831
end
832
function eigen(A::AbstractMatrix, D::Diagonal; sortby::Union{Function,Nothing}=nothing)
184✔
833
    if any(iszero, D.diag)
552✔
834
        throw(ArgumentError("right-hand side diagonal matrix is singular"))
×
835
    end
836
    if size(A, 1) == size(A, 2) && isdiag(A)
92✔
837
        return eigen(Diagonal(A), D; sortby)
10✔
838
    elseif all(isposdef, D.diag)
122✔
839
        S = promote_type(eigtype(eltype(A)), eltype(D))
82✔
840
        return eigen(A, cholesky(Diagonal{S}(D)); sortby)
82✔
841
    else
842
        return eigen!(D \ A; sortby)
×
843
    end
844
end
845

846
#Singular system
847
svdvals(D::Diagonal{<:Number}) = sort!(abs.(D.diag), rev = true)
12✔
848
svdvals(D::Diagonal) = [svdvals(v) for v in D.diag]
1✔
849
function svd(D::Diagonal{T}) where {T<:Number}
18✔
850
    d = D.diag
18✔
851
    s = abs.(d)
27✔
852
    piv = sortperm(s, rev = true)
18✔
853
    S = s[piv]
18✔
854
    Td  = typeof(oneunit(T)/oneunit(T))
18✔
855
    U = zeros(Td, size(D))
1,734✔
856
    Vt = copy(U)
18✔
857
    for i in 1:length(d)
18✔
858
        j = piv[i]
216✔
859
        U[j,i] = d[j] / S[i]
216✔
860
        Vt[i,j] = one(Td)
216✔
861
    end
414✔
862
    return SVD(U, S, Vt)
18✔
863
end
864

865
*(x::AdjointAbsVec,   D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y)
4✔
866
*(x::TransposeAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y)
2✔
867
/(u::AdjointAbsVec, D::Diagonal) = (D' \ u')'
×
868
/(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) \ transpose(u))
×
869
# disambiguation methods: Call unoptimized version for user defined AbstractTriangular.
870
*(A::AbstractTriangular, D::Diagonal) = @invoke *(A::AbstractMatrix, D::Diagonal)
×
871
*(D::Diagonal, A::AbstractTriangular) = @invoke *(D::Diagonal, A::AbstractMatrix)
×
872

873
dot(x::AbstractVector, D::Diagonal, y::AbstractVector) = _mapreduce_prod(dot, x, D, y)
11✔
874

875
dot(A::Diagonal, B::Diagonal) = dot(A.diag, B.diag)
5✔
876
function dot(D::Diagonal, B::AbstractMatrix)
2✔
877
    size(D) == size(B) || throw(DimensionMismatch("Matrix sizes $(size(D)) and $(size(B)) differ"))
2✔
878
    return dot(D.diag, view(B, diagind(B, IndexStyle(B))))
2✔
879
end
880

881
dot(A::AbstractMatrix, B::Diagonal) = conj(dot(B, A))
1✔
882

883
function _mapreduce_prod(f, x, D::Diagonal, y)
17✔
884
    if !(length(x) == length(D.diag) == length(y))
17✔
885
        throw(DimensionMismatch("x has length $(length(x)), D has size $(size(D)), and y has $(length(y))"))
4✔
886
    end
887
    if isempty(x) && isempty(D) && isempty(y)
13✔
888
        return zero(promote_op(f, eltype(x), eltype(D), eltype(y)))
3✔
889
    else
890
        return mapreduce(t -> f(t[1], t[2], t[3]), +, zip(x, D.diag, y))
98✔
891
    end
892
end
893

894
function cholesky!(A::Diagonal, ::NoPivot = NoPivot(); check::Bool = true)
887✔
895
    info = 0
93✔
896
    for (i, di) in enumerate(A.diag)
185✔
897
        if isreal(di) && real(di) > 0
443✔
898
            A.diag[i] = √di
440✔
899
        elseif check
4✔
900
            throw(PosDefException(i))
2✔
901
        else
902
            info = i
2✔
903
            break
2✔
904
        end
905
    end
788✔
906
    Cholesky(A, 'U', convert(BlasInt, info))
90✔
907
end
908
@deprecate cholesky!(A::Diagonal, ::Val{false}; check::Bool = true) cholesky!(A::Diagonal, NoPivot(); check) false
909
@deprecate cholesky(A::Diagonal, ::Val{false}; check::Bool = true) cholesky(A::Diagonal, NoPivot(); check) false
910

911
inv(C::Cholesky{<:Any,<:Diagonal}) = Diagonal(map(inv∘abs2, C.factors.diag))
1✔
912

913
cholcopy(A::Diagonal) = copymutable_oftype(A, choltype(A))
180✔
914
cholcopy(A::RealHermSymComplexHerm{<:Any,<:Diagonal}) = Diagonal(copy_similar(diag(A), choltype(A)))
4✔
915

916
function getproperty(C::Cholesky{<:Any,<:Diagonal}, d::Symbol)
10✔
917
    Cfactors = getfield(C, :factors)
185✔
918
    if d in (:U, :L, :UL)
185✔
919
        return Cfactors
172✔
920
    else
921
        return getfield(C, d)
13✔
922
    end
923
end
924

925
Base._sum(A::Diagonal, ::Colon) = sum(A.diag)
5✔
926
function Base._sum(A::Diagonal, dims::Integer)
15✔
927
    res = Base.reducedim_initarray(A, dims, zero(eltype(A)))
15✔
928
    if dims <= 2
12✔
929
        for i = 1:length(A.diag)
9✔
930
            @inbounds res[i] = A.diag[i]
12✔
931
        end
18✔
932
    else
933
        for i = 1:length(A.diag)
3✔
934
            @inbounds res[i,i] = A.diag[i]
4✔
935
        end
4✔
936
    end
937
    res
12✔
938
end
939

940
function logabsdet(A::Diagonal)
7✔
941
     mapreduce(x -> (log(abs(x)), sign(x)), ((d1, s1), (d2, s2)) -> (d1 + d2, s1 * s2),
330✔
942
               A.diag)
943
end
944

945
function Base.muladd(A::Diagonal, B::Diagonal, z::Diagonal)
1✔
946
    Diagonal(A.diag .* B.diag .+ z.diag)
1✔
947
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc