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

JuliaLang / julia / #37632

26 Sep 2023 06:44AM UTC coverage: 86.999% (-0.9%) from 87.914%
#37632

push

local

web-flow
inference: make `throw` block deoptimization concrete-eval friendly (#49235)

The deoptimization can sometimes destroy the effects analysis and
disable [semi-]concrete evaluation that is otherwise possible. This is
because the deoptimization was designed with the type domain
profitability in mind (#35982), and hasn't been adequately considering
the effects domain.

This commit makes the deoptimization aware of the effects domain more
and enables the `throw` block deoptimization only when the effects
already known to be ineligible for concrete-evaluation.

In our current effect system, `ALWAYS_FALSE`/`false` means that the
effect can not be refined to `ALWAYS_TRUE`/`true` anymore (unless given
user annotation later). Therefore we can enable the `throw` block
deoptimization without hindering the chance of concrete-evaluation when
any of the following conditions are met:
- `effects.consistent === ALWAYS_FALSE`
- `effects.effect_free === ALWAYS_FALSE`
- `effects.terminates === false`
- `effects.nonoverlayed === false`

Here are some numbers:

| Metric | master | this commit | #35982 reverted (set
`unoptimize_throw_blocks=false`) |

|-------------------------|-----------|-------------|--------------------------------------------|
| Base (seconds) | 15.579300 | 15.206645 | 15.296319 |
| Stdlibs (seconds) | 17.919013 | 17.667094 | 17.738128 |
| Total (seconds) | 33.499279 | 32.874737 | 33.035448 |
| Precompilation (seconds) | 49.967516 | 49.421121 | 49.999998 |
| First time `plot(rand(10,3))` [^1] | `2.476678 seconds (11.74 M
allocations)` | `2.430355 seconds (11.77 M allocations)` | `2.514874
seconds (11.64 M allocations)` |
| First time `solve(prob, QNDF())(5.0)` [^2] | `4.469492 seconds (15.32
M allocations)` | `4.499217 seconds (15.41 M allocations)` | `4.470772
seconds (15.38 M allocations)` |

[^1]: With disabling precompilation of Plots.jl.
[^2]: With disabling precompilation of OrdinaryDiffEq.

These numbers ma... (continued)

7 of 7 new or added lines in 1 file covered. (100.0%)

73407 of 84377 relevant lines covered (87.0%)

11275130.05 hits per line

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

91.67
/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}}
5,528✔
9
        require_one_based_indexing(diag)
5,528✔
10
        new{T,V}(diag)
5,528✔
11
    end
12
end
13
Diagonal{T,V}(d::Diagonal) where {T,V<:AbstractVector{T}} = Diagonal{T,V}(d.diag)
2✔
14
Diagonal(v::AbstractVector{T}) where {T} = Diagonal{T,typeof(v)}(v)
5,520✔
15
Diagonal{T}(v::AbstractVector) where {T} = Diagonal(convert(AbstractVector{T}, v)::AbstractVector{T})
244✔
16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

92
julia> A = [1 2 3; 4 5 6]
93
2×3 Matrix{Int64}:
94
 1  2  3
95
 4  5  6
96

97
julia> Diagonal(A)
98
2×2 Diagonal{Int64, Vector{Int64}}:
99
 1  ⋅
100
 ⋅  5
101
```
102
"""
103
Diagonal(A::AbstractMatrix) = Diagonal(diag(A))
599✔
104
Diagonal{T}(A::AbstractMatrix) where T = Diagonal{T}(diag(A))
6✔
105
function convert(::Type{T}, A::AbstractMatrix) where T<:Diagonal
12✔
106
    checksquare(A)
18✔
107
    isdiag(A) ? T(A) : throw(InexactError(:convert, T, A))
6✔
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)
45✔
113

114
AbstractMatrix{T}(D::Diagonal) where {T} = Diagonal{T}(D)
8✔
115
Matrix(D::Diagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(D)
2,586✔
116
Array(D::Diagonal{T}) where {T} = Matrix(D)
796✔
117
function Matrix{T}(D::Diagonal) where {T}
2,604✔
118
    n = size(D, 1)
2,604✔
119
    B = Matrix{T}(undef, n, n)
2,604✔
120
    n > 1 && fill!(B, zero(T))
203,531✔
121
    @inbounds for i in 1:n
5,139✔
122
        B[i,i] = D.diag[i]
19,024✔
123
    end
35,513✔
124
    return B
2,604✔
125
end
126

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

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

134
similar(D::Diagonal, ::Type{T}) where {T} = Diagonal(similar(D.diag, T))
294✔
135
similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(D.diag, T, dims)
904✔
136

137
copyto!(D1::Diagonal, D2::Diagonal) = (copyto!(D1.diag, D2.diag); D1)
518✔
138

139
size(D::Diagonal) = (n = length(D.diag); (n,n))
40,112✔
140

141
axes(D::Diagonal) = (ax = axes(D.diag, 1); (ax, ax))
131,027,036✔
142

143
@inline function Base.isassigned(D::Diagonal, i::Int, j::Int)
1,044✔
144
    @boundscheck checkbounds(Bool, D, i, j) || return false
1,044✔
145
    if i == j
1,044✔
146
        @inbounds r = isassigned(D.diag, i)
268✔
147
    else
148
        r = true
776✔
149
    end
150
    r
1,044✔
151
end
152

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

163
@inline function getindex(D::Diagonal, i::Int, j::Int)
65,492,464✔
164
    @boundscheck checkbounds(D, i, j)
65,492,469✔
165
    if i == j
65,492,459✔
166
        @inbounds r = D.diag[i]
109,638✔
167
    else
168
        r = diagzero(D, i, j)
65,382,821✔
169
    end
170
    r
65,492,459✔
171
end
172
diagzero(::Diagonal{T}, i, j) where {T} = zero(T)
65,382,774✔
173
diagzero(D::Diagonal{<:AbstractMatrix{T}}, i, j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2))
45✔
174

175
function setindex!(D::Diagonal, v, i::Int, j::Int)
333✔
176
    @boundscheck checkbounds(D, i, j)
338✔
177
    if i == j
328✔
178
        @inbounds D.diag[i] = v
31✔
179
    elseif !iszero(v)
297✔
180
        throw(ArgumentError("cannot set off-diagonal entry ($i, $j) to a nonzero value ($v)"))
135✔
181
    end
182
    return v
193✔
183
end
184

185

186
## structured matrix methods ##
187
function Base.replace_in_print_matrix(A::Diagonal,i::Integer,j::Integer,s::AbstractString)
522✔
188
    i==j ? s : Base.replace_with_centered_mark(s)
642✔
189
end
190

191
parent(D::Diagonal) = D.diag
289✔
192

193
ishermitian(D::Diagonal{<:Real}) = true
6✔
194
ishermitian(D::Diagonal{<:Number}) = isreal(D.diag)
6✔
195
ishermitian(D::Diagonal) = all(ishermitian, D.diag)
4✔
196
issymmetric(D::Diagonal{<:Number}) = true
17✔
197
issymmetric(D::Diagonal) = all(issymmetric, D.diag)
4✔
198
isposdef(D::Diagonal) = all(isposdef, D.diag)
6✔
199

200
factorize(D::Diagonal) = D
6✔
201

202
real(D::Diagonal) = Diagonal(real(D.diag))
12✔
203
imag(D::Diagonal) = Diagonal(imag(D.diag))
6✔
204

205
iszero(D::Diagonal) = all(iszero, D.diag)
45✔
206
isone(D::Diagonal) = all(isone, D.diag)
30✔
207
isdiag(D::Diagonal) = all(isdiag, D.diag)
13✔
208
isdiag(D::Diagonal{<:Number}) = true
24✔
209
istriu(D::Diagonal, k::Integer=0) = k <= 0 || iszero(D.diag) ? true : false
74✔
210
istril(D::Diagonal, k::Integer=0) = k >= 0 || iszero(D.diag) ? true : false
74✔
211
function triu!(D::Diagonal{T}, k::Integer=0) where T
30✔
212
    n = size(D,1)
30✔
213
    if !(-n + 1 <= k <= n + 1)
30✔
214
        throw(ArgumentError(string("the requested diagonal, $k, must be at least ",
12✔
215
            "$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix")))
216
    elseif k > 0
18✔
217
        fill!(D.diag, zero(T))
72✔
218
    end
219
    return D
18✔
220
end
221

222
function tril!(D::Diagonal{T}, k::Integer=0) where T
30✔
223
    n = size(D,1)
30✔
224
    if !(-n - 1 <= k <= n - 1)
30✔
225
        throw(ArgumentError(string("the requested diagonal, $k, must be at least ",
12✔
226
            "$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix")))
227
    elseif k < 0
18✔
228
        fill!(D.diag, zero(T))
72✔
229
    end
230
    return D
18✔
231
end
232

233
(==)(Da::Diagonal, Db::Diagonal) = Da.diag == Db.diag
165✔
234
(-)(A::Diagonal) = Diagonal(-A.diag)
20✔
235
(+)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag + Db.diag)
80✔
236
(-)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag - Db.diag)
140✔
237

238
for f in (:+, :-)
239
    @eval function $f(D::Diagonal, S::Symmetric)
24✔
240
        return Symmetric($f(D, S.data), sym_uplo(S.uplo))
24✔
241
    end
242
    @eval function $f(S::Symmetric, D::Diagonal)
24✔
243
        return Symmetric($f(S.data, D), sym_uplo(S.uplo))
24✔
244
    end
245
    @eval function $f(D::Diagonal{<:Real}, H::Hermitian)
12✔
246
        return Hermitian($f(D, H.data), sym_uplo(H.uplo))
12✔
247
    end
248
    @eval function $f(H::Hermitian, D::Diagonal{<:Real})
12✔
249
        return Hermitian($f(H.data, D), sym_uplo(H.uplo))
12✔
250
    end
251
end
252

253
(*)(x::Number, D::Diagonal) = Diagonal(x * D.diag)
30✔
254
(*)(D::Diagonal, x::Number) = Diagonal(D.diag * x)
6✔
255
(/)(D::Diagonal, x::Number) = Diagonal(D.diag / x)
9✔
256
(\)(x::Number, D::Diagonal) = Diagonal(x \ D.diag)
3✔
257
(^)(D::Diagonal, a::Number) = Diagonal(D.diag .^ a)
6✔
258
(^)(D::Diagonal, a::Real) = Diagonal(D.diag .^ a) # for disambiguation
12✔
259
(^)(D::Diagonal, a::Integer) = Diagonal(D.diag .^ a) # for disambiguation
6✔
260
Base.literal_pow(::typeof(^), D::Diagonal, valp::Val) =
6✔
261
    Diagonal(Base.literal_pow.(^, D.diag, valp)) # for speed
262
Base.literal_pow(::typeof(^), D::Diagonal, ::Val{-1}) = inv(D) # for disambiguation
6✔
263

264
function _muldiag_size_check(A, B)
2,706✔
265
    nA = size(A, 2)
2,706✔
266
    mB = size(B, 1)
2,706✔
267
    @noinline throw_dimerr(::AbstractMatrix, nA, mB) = throw(DimensionMismatch("second dimension of A, $nA, does not match first dimension of B, $mB"))
2,723✔
268
    @noinline throw_dimerr(::AbstractVector, nA, mB) = throw(DimensionMismatch("second dimension of D, $nA, does not match length of V, $mB"))
2✔
269
    nA == mB || throw_dimerr(B, nA, mB)
2,725✔
270
    return nothing
2,687✔
271
end
272
# the output matrix should have the same size as the non-diagonal input matrix or vector
273
@noinline throw_dimerr(szC, szA) = throw(DimensionMismatch("output matrix has size: $szC, but should have size $szA"))
8✔
274
_size_check_out(C, ::Diagonal, A) = _size_check_out(C, A)
865✔
275
_size_check_out(C, A, ::Diagonal) = _size_check_out(C, A)
1,269✔
276
_size_check_out(C, A::Diagonal, ::Diagonal) = _size_check_out(C, A)
9✔
277
function _size_check_out(C, A)
2,135✔
278
    szA = size(A)
2,135✔
279
    szC = size(C)
2,135✔
280
    szA == szC || throw_dimerr(szC, szA)
2,143✔
281
    return nothing
2,127✔
282
end
283
function _muldiag_size_check(C, A, B)
2,147✔
284
    _muldiag_size_check(A, B)
2,159✔
285
    _size_check_out(C, A, B)
2,143✔
286
end
287

288
function (*)(Da::Diagonal, Db::Diagonal)
162✔
289
    _muldiag_size_check(Da, Db)
163✔
290
    return Diagonal(Da.diag .* Db.diag)
161✔
291
end
292

293
function (*)(D::Diagonal, V::AbstractVector)
128✔
294
    _muldiag_size_check(D, V)
129✔
295
    return D.diag .* V
127✔
296
end
297

298
(*)(A::AbstractMatrix, D::Diagonal) =
1,384✔
299
    mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag))), A, D)
300
(*)(A::HermOrSym, D::Diagonal) =
24✔
301
    mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), A, D)
302
(*)(D::Diagonal, A::AbstractMatrix) =
623✔
303
    mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag))), D, A)
304
(*)(D::Diagonal, A::HermOrSym) =
54✔
305
    mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), D, A)
306

307
rmul!(A::AbstractMatrix, D::Diagonal) = @inline mul!(A, A, D)
185✔
308
lmul!(D::Diagonal, B::AbstractVecOrMat) = @inline mul!(B, D, B)
407✔
309

310
function (*)(A::AdjOrTransAbsMat, D::Diagonal)
126✔
311
    Ac = copy_similar(A, promote_op(*, eltype(A), eltype(D.diag)))
126✔
312
    rmul!(Ac, D)
126✔
313
end
314
function (*)(D::Diagonal, A::AdjOrTransAbsMat)
337✔
315
    Ac = copy_similar(A, promote_op(*, eltype(A), eltype(D.diag)))
337✔
316
    lmul!(D, Ac)
337✔
317
end
318

319
function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
849✔
320
    require_one_based_indexing(out, B)
851✔
321
    alpha, beta = _add.alpha, _add.beta
847✔
322
    if iszero(alpha)
847✔
323
        _rmul_or_fill!(out, beta)
×
324
    else
325
        if bis0
847✔
326
            @inbounds for j in axes(B, 2)
1,556✔
327
                @simd for i in axes(B, 1)
28,905✔
328
                    out[i,j] = D.diag[i] * B[i,j] * alpha
1,978,150✔
329
                end
×
330
            end
28,905✔
331
        else
332
            @inbounds for j in axes(B, 2)
78✔
333
                @simd for i in axes(B, 1)
276✔
334
                    out[i,j] = D.diag[i] * B[i,j] * alpha + out[i,j] * beta
2,956✔
335
                end
×
336
            end
276✔
337
        end
338
    end
339
    return out
847✔
340
end
341
function __muldiag!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
1,269✔
342
    require_one_based_indexing(out, A)
1,271✔
343
    alpha, beta = _add.alpha, _add.beta
1,267✔
344
    if iszero(alpha)
1,267✔
345
        _rmul_or_fill!(out, beta)
×
346
    else
347
        if bis0
1,267✔
348
            @inbounds for j in axes(A, 2)
2,510✔
349
                dja = D.diag[j] * alpha
8,281✔
350
                @simd for i in axes(A, 1)
8,281✔
351
                    out[i,j] = A[i,j] * dja
125,734✔
352
                end
×
353
            end
8,281✔
354
        else
355
            @inbounds for j in axes(A, 2)
24✔
356
                dja = D.diag[j] * alpha
42✔
357
                @simd for i in axes(A, 1)
42✔
358
                    out[i,j] = A[i,j] * dja + out[i,j] * beta
148✔
359
                end
×
360
            end
42✔
361
        end
362
    end
363
    return out
1,267✔
364
end
365
function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
9✔
366
    d1 = D1.diag
9✔
367
    d2 = D2.diag
9✔
368
    alpha, beta = _add.alpha, _add.beta
9✔
369
    if iszero(alpha)
9✔
370
        _rmul_or_fill!(out.diag, beta)
×
371
    else
372
        if bis0
9✔
373
            @inbounds @simd for i in eachindex(out.diag)
9✔
374
                out.diag[i] = d1[i] * d2[i] * alpha
36✔
375
            end
×
376
        else
377
            @inbounds @simd for i in eachindex(out.diag)
×
378
                out.diag[i] = d1[i] * d2[i] * alpha + out.diag[i] * beta
×
379
            end
×
380
        end
381
    end
382
    return out
9✔
383
end
384
function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0}
×
385
    require_one_based_indexing(out)
×
386
    alpha, beta = _add.alpha, _add.beta
×
387
    mA = size(D1, 1)
×
388
    d1 = D1.diag
×
389
    d2 = D2.diag
×
390
    _rmul_or_fill!(out, beta)
×
391
    if !iszero(alpha)
×
392
        @inbounds @simd for i in 1:mA
×
393
            out[i,i] += d1[i] * d2[i] * alpha
×
394
        end
×
395
    end
396
    return out
×
397
end
398

399
function _mul_diag!(out, A, B, _add)
2,147✔
400
    _muldiag_size_check(out, A, B)
2,155✔
401
    __muldiag!(out, A, B, _add)
2,127✔
402
    return out
2,123✔
403
end
404

405
_mul!(out::AbstractVecOrMat, D::Diagonal, V::AbstractVector, _add) =
61✔
406
    _mul_diag!(out, D, V, _add)
407
_mul!(out::AbstractMatrix, D::Diagonal, B::AbstractMatrix, _add) =
809✔
408
    _mul_diag!(out, D, B, _add)
409
_mul!(out::AbstractMatrix, A::AbstractMatrix, D::Diagonal, _add) =
1,276✔
410
    _mul_diag!(out, A, D, _add)
411
_mul!(C::Diagonal, Da::Diagonal, Db::Diagonal, _add) =
9✔
412
    _mul_diag!(C, Da, Db, _add)
413
_mul!(C::AbstractMatrix, Da::Diagonal, Db::Diagonal, _add) =
×
414
    _mul_diag!(C, Da, Db, _add)
415

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

422
function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal)
52✔
423
    _muldiag_size_check(Da, Db)
54✔
424
    _muldiag_size_check(Db, Dc)
51✔
425
    return Diagonal(Da.diag .* Db.diag .* Dc.diag)
49✔
426
end
427

428
/(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D))), A, D)
189✔
429
/(A::HermOrSym, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D)), size(A)), A, D)
×
430

431
rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D)
161✔
432
# avoid copy when possible via internal 3-arg backend
433
function _rdiv!(B::AbstractVecOrMat, A::AbstractVecOrMat, D::Diagonal)
349✔
434
    require_one_based_indexing(A)
349✔
435
    dd = D.diag
349✔
436
    m, n = size(A, 1), size(A, 2)
349✔
437
    if (k = length(dd)) != n
349✔
438
        throw(DimensionMismatch("left hand side has $n columns but D is $k by $k"))
8✔
439
    end
440
    @inbounds for j in 1:n
682✔
441
        ddj = dd[j]
2,483✔
442
        iszero(ddj) && throw(SingularException(j))
2,483✔
443
        for i in 1:m
4,950✔
444
            B[i, j] = A[i, j] / ddj
25,877✔
445
        end
36,719✔
446
    end
4,617✔
447
    B
333✔
448
end
449

450
function \(D::Diagonal, B::AbstractVector)
148✔
451
    j = findfirst(iszero, D.diag)
1,569✔
452
    isnothing(j) || throw(SingularException(j))
149✔
453
    return D.diag .\ B
147✔
454
end
455
\(D::Diagonal, B::AbstractMatrix) = ldiv!(similar(B, _init_eltype(\, eltype(D), eltype(B))), D, B)
556✔
456
\(D::Diagonal, B::HermOrSym) = ldiv!(similar(B, _init_eltype(\, eltype(D), eltype(B)), size(B)), D, B)
24✔
457

458
ldiv!(D::Diagonal, B::AbstractVecOrMat) = @inline ldiv!(B, D, B)
251✔
459
function ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat)
912✔
460
    require_one_based_indexing(A, B)
912✔
461
    dd = D.diag
912✔
462
    d = length(dd)
912✔
463
    m, n = size(A, 1), size(A, 2)
912✔
464
    m′, n′ = size(B, 1), size(B, 2)
912✔
465
    m == d || throw(DimensionMismatch("right hand side has $m rows but D is $d by $d"))
954✔
466
    (m, n) == (m′, n′) || throw(DimensionMismatch("expect output to be $m by $n, but got $m′ by $n′"))
870✔
467
    j = findfirst(iszero, D.diag)
7,271✔
468
    isnothing(j) || throw(SingularException(j))
894✔
469
    @inbounds for j = 1:n, i = 1:m
5,856✔
470
        B[i, j] = dd[i] \ A[i, j]
50,376✔
471
    end
48,282✔
472
    B
846✔
473
end
474

475
# Optimizations for \, / between Diagonals
476
\(D::Diagonal, B::Diagonal) = ldiv!(similar(B, promote_op(\, eltype(D), eltype(B))), D, B)
27✔
477
/(A::Diagonal, D::Diagonal) = _rdiv!(similar(A, promote_op(/, eltype(A), eltype(D))), A, D)
6✔
478
function _rdiv!(Dc::Diagonal, Db::Diagonal, Da::Diagonal)
6✔
479
    n, k = length(Db.diag), length(Da.diag)
6✔
480
    n == k || throw(DimensionMismatch("left hand side has $n columns but D is $k by $k"))
6✔
481
    j = findfirst(iszero, Da.diag)
78✔
482
    isnothing(j) || throw(SingularException(j))
6✔
483
    Dc.diag .= Db.diag ./ Da.diag
12✔
484
    Dc
6✔
485
end
486
ldiv!(Dc::Diagonal, Da::Diagonal, Db::Diagonal) = Diagonal(ldiv!(Dc.diag, Da, Db.diag))
27✔
487

488
# optimizations for (Sym)Tridiagonal and Diagonal
489
@propagate_inbounds _getudiag(T::Tridiagonal, i) = T.du[i]
72✔
490
@propagate_inbounds _getudiag(S::SymTridiagonal, i) = S.ev[i]
72✔
491
@propagate_inbounds _getdiag(T::Tridiagonal, i) = T.d[i]
98✔
492
@propagate_inbounds _getdiag(S::SymTridiagonal, i) = symmetric(S.dv[i], :U)::symmetric_type(eltype(S.dv))
98✔
493
@propagate_inbounds _getldiag(T::Tridiagonal, i) = T.dl[i]
72✔
494
@propagate_inbounds _getldiag(S::SymTridiagonal, i) = transpose(S.ev[i])
72✔
495

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

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

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

613
@inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal)
×
614
    valA = A.diag; nA = length(valA)
×
615
    valB = B.diag; nB = length(valB)
×
616
    nC = checksquare(C)
×
617
    @boundscheck nC == nA*nB ||
×
618
        throw(DimensionMismatch("expect C to be a $(nA*nB)x$(nA*nB) matrix, got size $(nC)x$(nC)"))
619
    isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1]))
×
620
    @inbounds for i = 1:nA, j = 1:nB
×
621
        idx = (i-1)*nB+j
×
622
        C[idx, idx] = valA[i] * valB[j]
×
623
    end
×
624
    return C
×
625
end
626

627
kron(A::Diagonal, B::Diagonal) = Diagonal(kron(A.diag, B.diag))
8✔
628

629
function kron(A::Diagonal, B::SymTridiagonal)
4✔
630
    kdv = kron(diag(A), B.dv)
4✔
631
    # We don't need to drop the last element
632
    kev = kron(diag(A), _pushzero(_evview(B)))
4✔
633
    SymTridiagonal(kdv, kev)
4✔
634
end
635
function kron(A::Diagonal, B::Tridiagonal)
2✔
636
    # `_droplast!` is only guaranteed to work with `Vector`
637
    kd = _makevector(kron(diag(A), B.d))
2✔
638
    kdl = _droplast!(_makevector(kron(diag(A), _pushzero(B.dl))))
2✔
639
    kdu = _droplast!(_makevector(kron(diag(A), _pushzero(B.du))))
2✔
640
    Tridiagonal(kdl, kd, kdu)
2✔
641
end
642

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

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

689
conj(D::Diagonal) = Diagonal(conj(D.diag))
50✔
690
transpose(D::Diagonal{<:Number}) = D
287✔
691
transpose(D::Diagonal) = Diagonal(transpose.(D.diag))
23✔
692
adjoint(D::Diagonal{<:Number}) = Diagonal(vec(adjoint(D.diag)))
537✔
693
adjoint(D::Diagonal{<:Number,<:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = Diagonal(adjoint(parent(D.diag)))
2✔
694
adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag))
23✔
695
permutedims(D::Diagonal) = D
3✔
696
permutedims(D::Diagonal, perm) = (Base.checkdims_perm(D, D, perm); D)
21✔
697

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

717
# Matrix functions
718
for f in (:exp, :cis, :log, :sqrt,
719
          :cos, :sin, :tan, :csc, :sec, :cot,
720
          :cosh, :sinh, :tanh, :csch, :sech, :coth,
721
          :acos, :asin, :atan, :acsc, :asec, :acot,
722
          :acosh, :asinh, :atanh, :acsch, :asech, :acoth)
723
    @eval $f(D::Diagonal) = Diagonal($f.(D.diag))
84✔
724
end
725

726
function inv(D::Diagonal{T}) where T
45✔
727
    Di = similar(D.diag, typeof(inv(oneunit(T))))
45✔
728
    for i = 1:length(D.diag)
89✔
729
        if iszero(D.diag[i])
927✔
730
            throw(SingularException(i))
3✔
731
        end
732
        Di[i] = inv(D.diag[i])
1,004✔
733
    end
1,807✔
734
    Diagonal(Di)
42✔
735
end
736

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

771
#Eigensystem
772
eigvals(D::Diagonal{<:Number}; permute::Bool=true, scale::Bool=true) = copy(D.diag)
304✔
773
eigvals(D::Diagonal; permute::Bool=true, scale::Bool=true) =
2✔
774
    [eigvals(x) for x in D.diag] #For block matrices, etc.
775
eigvecs(D::Diagonal) = Matrix{eltype(D)}(I, size(D))
×
776
function eigen(D::Diagonal; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=nothing)
106✔
777
    if any(!isfinite, D.diag)
387✔
778
        throw(ArgumentError("matrix contains Infs or NaNs"))
3✔
779
    end
780
    Td = Base.promote_op(/, eltype(D), eltype(D))
50✔
781
    λ = eigvals(D)
50✔
782
    if !isnothing(sortby)
50✔
783
        p = sortperm(λ; alg=QuickSort, by=sortby)
17✔
784
        λ = λ[p]
17✔
785
        evecs = zeros(Td, size(D))
748✔
786
        @inbounds for i in eachindex(p)
34✔
787
            evecs[p[i],i] = one(Td)
87✔
788
        end
157✔
789
    else
790
        evecs = Matrix{Td}(I, size(D))
33✔
791
    end
792
    Eigen(λ, evecs)
50✔
793
end
794
function eigen(Da::Diagonal, Db::Diagonal; sortby::Union{Function,Nothing}=nothing)
40✔
795
    if any(!isfinite, Da.diag) || any(!isfinite, Db.diag)
120✔
796
        throw(ArgumentError("matrices contain Infs or NaNs"))
×
797
    end
798
    if any(iszero, Db.diag)
120✔
799
        throw(ArgumentError("right-hand side diagonal matrix is singular"))
×
800
    end
801
    return GeneralizedEigen(eigen(Db \ Da; sortby)...)
20✔
802
end
803
function eigen(A::AbstractMatrix, D::Diagonal; sortby::Union{Function,Nothing}=nothing)
184✔
804
    if any(iszero, D.diag)
552✔
805
        throw(ArgumentError("right-hand side diagonal matrix is singular"))
×
806
    end
807
    if size(A, 1) == size(A, 2) && isdiag(A)
122✔
808
        return eigen(Diagonal(A), D; sortby)
10✔
809
    elseif all(isposdef, D.diag)
194✔
810
        S = promote_type(eigtype(eltype(A)), eltype(D))
82✔
811
        return eigen(A, cholesky(Diagonal{S}(D)); sortby)
82✔
812
    else
813
        return eigen!(D \ A; sortby)
×
814
    end
815
end
816

817
#Singular system
818
svdvals(D::Diagonal{<:Number}) = sort!(abs.(D.diag), rev = true)
12✔
819
svdvals(D::Diagonal) = [svdvals(v) for v in D.diag]
1✔
820
function svd(D::Diagonal{T}) where {T<:Number}
18✔
821
    d = D.diag
18✔
822
    s = abs.(d)
36✔
823
    piv = sortperm(s, rev = true)
18✔
824
    S = s[piv]
18✔
825
    Td  = typeof(oneunit(T)/oneunit(T))
18✔
826
    U = zeros(Td, size(D))
2,163✔
827
    Vt = copy(U)
18✔
828
    for i in 1:length(d)
36✔
829
        j = piv[i]
216✔
830
        U[j,i] = d[j] / S[i]
216✔
831
        Vt[i,j] = one(Td)
216✔
832
    end
414✔
833
    return SVD(U, S, Vt)
18✔
834
end
835

836
# disambiguation methods: * and / of Diagonal and Adj/Trans AbsVec
837
*(u::AdjointAbsVec, D::Diagonal) = (D'u')'
7✔
838
*(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) * transpose(u))
7✔
839
*(x::AdjointAbsVec,   D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y)
4✔
840
*(x::TransposeAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y)
2✔
841
/(u::AdjointAbsVec, D::Diagonal) = (D' \ u')'
×
842
/(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) \ transpose(u))
×
843
# disambiguation methods: Call unoptimized version for user defined AbstractTriangular.
844
*(A::AbstractTriangular, D::Diagonal) = @invoke *(A::AbstractMatrix, D::Diagonal)
×
845
*(D::Diagonal, A::AbstractTriangular) = @invoke *(D::Diagonal, A::AbstractMatrix)
×
846

847
dot(x::AbstractVector, D::Diagonal, y::AbstractVector) = _mapreduce_prod(dot, x, D, y)
11✔
848

849
dot(A::Diagonal, B::Diagonal) = dot(A.diag, B.diag)
5✔
850
function dot(D::Diagonal, B::AbstractMatrix)
2✔
851
    size(D) == size(B) || throw(DimensionMismatch("Matrix sizes $(size(D)) and $(size(B)) differ"))
2✔
852
    return dot(D.diag, view(B, diagind(B)))
2✔
853
end
854

855
dot(A::AbstractMatrix, B::Diagonal) = conj(dot(B, A))
1✔
856

857
function _mapreduce_prod(f, x, D::Diagonal, y)
17✔
858
    if !(length(x) == length(D.diag) == length(y))
17✔
859
        throw(DimensionMismatch("x has length $(length(x)), D has size $(size(D)), and y has $(length(y))"))
4✔
860
    end
861
    if isempty(x) && isempty(D) && isempty(y)
13✔
862
        return zero(promote_op(f, eltype(x), eltype(D), eltype(y)))
3✔
863
    else
864
        return mapreduce(t -> f(t[1], t[2], t[3]), +, zip(x, D.diag, y))
94✔
865
    end
866
end
867

868
function cholesky!(A::Diagonal, ::NoPivot = NoPivot(); check::Bool = true)
279✔
869
    info = 0
93✔
870
    for (i, di) in enumerate(A.diag)
186✔
871
        if isreal(di) && real(di) > 0
443✔
872
            A.diag[i] = √di
440✔
873
        elseif check
4✔
874
            throw(PosDefException(i))
2✔
875
        else
876
            info = i
2✔
877
            break
2✔
878
        end
879
    end
788✔
880
    Cholesky(A, 'U', convert(BlasInt, info))
90✔
881
end
882
@deprecate cholesky!(A::Diagonal, ::Val{false}; check::Bool = true) cholesky!(A::Diagonal, NoPivot(); check) false
883
@deprecate cholesky(A::Diagonal, ::Val{false}; check::Bool = true) cholesky(A::Diagonal, NoPivot(); check) false
884

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

887
cholcopy(A::Diagonal) = copymutable_oftype(A, choltype(A))
90✔
888
cholcopy(A::RealHermSymComplexHerm{<:Any,<:Diagonal}) = Diagonal(copy_similar(diag(A), choltype(A)))
2✔
889

890
function getproperty(C::Cholesky{<:Any,<:Diagonal}, d::Symbol)
185✔
891
    Cfactors = getfield(C, :factors)
185✔
892
    if d in (:U, :L, :UL)
185✔
893
        return Cfactors
172✔
894
    else
895
        return getfield(C, d)
13✔
896
    end
897
end
898

899
Base._sum(A::Diagonal, ::Colon) = sum(A.diag)
5✔
900
function Base._sum(A::Diagonal, dims::Integer)
15✔
901
    res = Base.reducedim_initarray(A, dims, zero(eltype(A)))
29✔
902
    if dims <= 2
12✔
903
        for i = 1:length(A.diag)
15✔
904
            @inbounds res[i] = A.diag[i]
12✔
905
        end
18✔
906
    else
907
        for i = 1:length(A.diag)
5✔
908
            @inbounds res[i,i] = A.diag[i]
4✔
909
        end
4✔
910
    end
911
    res
12✔
912
end
913

914
function logabsdet(A::Diagonal)
25✔
915
     mapreduce(x -> (log(abs(x)), sign(x)), ((d1, s1), (d2, s2)) -> (d1 + d2, s1 * s2),
354✔
916
               A.diag)
917
end
918

919
function Base.muladd(A::Diagonal, B::Diagonal, z::Diagonal)
1✔
920
    Diagonal(A.diag .* B.diag .+ z.diag)
1✔
921
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