Skip to content

Commit 9a4a715

Browse files
committed
let spdiagm preserve sparsity
1 parent 91d7e18 commit 9a4a715

File tree

3 files changed

+80
-15
lines changed

3 files changed

+80
-15
lines changed

NEWS.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,13 @@ Standard library changes
163163

164164
#### SparseArrays
165165

166-
* Display large sparse matrices with a Unicode "spy" plot of their nonzero patterns, and display small sparse matrices by an `Matrix`-like 2d layout of their contents.
166+
* Display large sparse matrices with a Unicode "spy" plot of their nonzero patterns,
167+
and display small sparse matrices by an `Matrix`-like 2d layout of their contents.
168+
* New convenient `spdiagm([m, n,] v::AbstractVector)` methods which call
169+
`spdiagm([m, n,] 0 => v)`, consistently with their dense `diagm` counterparts. ([#37684])
167170

168171
#### Dates
172+
169173
* `Quarter` period is defined ([#35519]).
170174
* Zero-valued `FixedPeriod`s and `OtherPeriod`s now compare equal, e.g.,
171175
`Year(0) == Day(0)`. The behavior of non-zero `Period`s is not changed. ([#37486])
@@ -180,6 +184,7 @@ Standard library changes
180184

181185

182186
#### UUIDs
187+
183188
* Change `uuid1` and `uuid4` to use `Random.RandomDevice()` as default random number generator ([#35872]).
184189
* Added `parse(::Type{UUID}, ::AbstractString)` method
185190

stdlib/SparseArrays/src/sparsematrix.jl

+62-14
Original file line numberDiff line numberDiff line change
@@ -3491,37 +3491,55 @@ function istril(A::AbstractSparseMatrixCSC)
34913491
return true
34923492
end
34933493

3494+
_nnz(v::AbstractSparseVector) = nnz(v)
3495+
_nnz(v::AbstractVector) = length(v)
3496+
3497+
function _indices(v::AbstractSparseVector, row, col)
3498+
ix = nonzeroinds(v)
3499+
return (row .+ ix, col .+ ix)
3500+
end
3501+
function _indices(v::AbstractVector, row, col)
3502+
veclen = length(v)
3503+
return (row+1:row+veclen, col+1:col+veclen)
3504+
end
3505+
3506+
_nzvals(v::AbstractSparseVector) = nonzeros(v)
3507+
_nzvals(v::AbstractVector) = v
34943508

34953509
function spdiagm_internal(kv::Pair{<:Integer,<:AbstractVector}...)
34963510
ncoeffs = 0
34973511
for p in kv
3498-
ncoeffs += length(p.second)
3512+
ncoeffs += _nnz(p.second)
34993513
end
35003514
I = Vector{Int}(undef, ncoeffs)
35013515
J = Vector{Int}(undef, ncoeffs)
35023516
V = Vector{promote_type(map(x -> eltype(x.second), kv)...)}(undef, ncoeffs)
35033517
i = 0
3518+
m = 0
3519+
n = 0
35043520
for p in kv
3505-
dia = p.first
3506-
vect = p.second
3507-
numel = length(vect)
3508-
if dia < 0
3509-
row = -dia
3521+
k = p.first
3522+
v = p.second
3523+
if k < 0
3524+
row = -k
35103525
col = 0
3511-
elseif dia > 0
3526+
elseif k > 0
35123527
row = 0
3513-
col = dia
3528+
col = k
35143529
else
35153530
row = 0
35163531
col = 0
35173532
end
3533+
numel = _nnz(v)
35183534
r = 1+i:numel+i
3519-
I[r] = row+1:row+numel
3520-
J[r] = col+1:col+numel
3521-
copyto!(view(V, r), vect)
3535+
I[r], J[r] = _indices(v, row, col)
3536+
copyto!(view(V, r), _nzvals(v))
3537+
veclen = length(v)
3538+
m = max(m, row + veclen)
3539+
n = max(n, col + veclen)
35223540
i += numel
35233541
end
3524-
return I, J, V
3542+
return I, J, V, m, n
35253543
end
35263544

35273545
"""
@@ -3547,9 +3565,39 @@ julia> spdiagm(-1 => [1,2,3,4], 1 => [4,3,2,1])
35473565
"""
35483566
spdiagm(kv::Pair{<:Integer,<:AbstractVector}...) = _spdiagm(nothing, kv...)
35493567
spdiagm(m::Integer, n::Integer, kv::Pair{<:Integer,<:AbstractVector}...) = _spdiagm((Int(m),Int(n)), kv...)
3568+
3569+
"""
3570+
spdiagm(v::AbstractVector)
3571+
spdiagm(m::Integer, n::Integer, v::AbstractVector)
3572+
3573+
Construct a sparse matrix with elements of the vector as diagonal elements.
3574+
By default (no given `m` and `n`), the matrix is square and its size is given
3575+
by `length(v)`, but a non-square size `m`×`n` can be specified by passing `m`
3576+
and `n` as the first arguments.
3577+
3578+
!!! compat "Julia 1.6"
3579+
These functions require at least Julia 1.6.
3580+
3581+
# Examples
3582+
```jldoctest
3583+
julia> spdiagm([1,2,3])
3584+
3×3 SparseMatrixCSC{Int64, Int64} with 3 stored entries:
3585+
1 ⋅ ⋅
3586+
⋅ 2 ⋅
3587+
⋅ ⋅ 3
3588+
3589+
julia> spdiagm(sparse([1,0,3]))
3590+
3×3 SparseMatrixCSC{Int64, Int64} with 2 stored entries:
3591+
1 ⋅ ⋅
3592+
⋅ ⋅ ⋅
3593+
⋅ ⋅ 3
3594+
```
3595+
"""
3596+
spdiagm(v::AbstractVector) = _spdiagm(nothing, 0 => v)
3597+
spdiagm(m::Integer, n::Integer, v::AbstractVector) = _spdiagm((Int(m), Int(n)), 0 => v)
3598+
35503599
function _spdiagm(size, kv::Pair{<:Integer,<:AbstractVector}...)
3551-
I, J, V = spdiagm_internal(kv...)
3552-
mmax, nmax = dimlub(I), dimlub(J)
3600+
I, J, V, mmax, nmax = spdiagm_internal(kv...)
35533601
mnmax = max(mmax, nmax)
35543602
m, n = something(size, (mnmax,mnmax))
35553603
(m mmax && n nmax) || throw(DimensionMismatch("invalid size=$size"))

stdlib/SparseArrays/test/sparse.jl

+12
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,13 @@ end
17501750
# promotion
17511751
@test spdiagm(0 => [1,2], 1 => [3.5], -1 => [4+5im]) == [1 3.5; 4+5im 2]
17521752

1753+
# convenience constructor
1754+
@test spdiagm(x)::SparseMatrixCSC == diagm(x)
1755+
@test nnz(spdiagm(x)) == count(!iszero, x)
1756+
@test nnz(spdiagm(sparse([x; 0]))) == 2
1757+
@test spdiagm(3, 4, x)::SparseMatrixCSC == diagm(3, 4, x)
1758+
@test nnz(spdiagm(3, 4, sparse([x; 0]))) == 2
1759+
17531760
# non-square:
17541761
for m=1:4, n=2:4
17551762
if m < 2 || n < 3
@@ -1760,6 +1767,11 @@ end
17601767
@test spdiagm(m,n, 0 => x, 1 => x) == M
17611768
end
17621769
end
1770+
1771+
# sparsity-preservation
1772+
x = sprand(10, 0.2); y = ones(9)
1773+
@test spdiagm(0 => x, 1 => y) == diagm(0 => x, 1 => y)
1774+
@test nnz(spdiagm(0 => x, 1 => y)) == length(y) + nnz(x)
17631775
end
17641776

17651777
@testset "diag" begin

0 commit comments

Comments
 (0)