Skip to content

Commit

Permalink
make sparse arrays print more consistently, also add some doctests (#…
Browse files Browse the repository at this point in the history
…20488)

* make sparse and dense arrays print more similarly

* update doctest after dropstored fix

* add extra method for isassigned for mixed indices

* change confusing doctest

* fixup

* change adding isassigned to Int on call ot it

* various fixes

* address review

* more .+1
  • Loading branch information
KristofferC committed Feb 13, 2017
1 parent 2c5db52 commit cee29b0
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 34 deletions.
2 changes: 1 addition & 1 deletion base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ Example for a sparse 2-d array:
```jldoctest
julia> A = sparse([1, 1, 2], [1, 3, 1], [1, 2, -5])
2×3 sparse matrix with 3 Int64 stored entries:
2×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries:
[1, 1] = 1
[2, 1] = -5
[1, 3] = 2
Expand Down
83 changes: 66 additions & 17 deletions base/sparse/sparsematrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Returns the number of stored (filled) elements in a sparse array.
```jldoctest
julia> A = speye(3)
3×3 sparse matrix with 3 Float64 stored entries:
3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
Expand All @@ -58,7 +58,7 @@ modifications to the returned vector will mutate `A` as well. See
```jldoctest
julia> A = speye(3)
3×3 sparse matrix with 3 Float64 stored entries:
3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
Expand All @@ -82,7 +82,7 @@ nonzero values. See also [`nonzeros`](@ref) and [`nzrange`](@ref).
```jldoctest
julia> A = speye(3)
3×3 sparse matrix with 3 Float64 stored entries:
3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
Expand Down Expand Up @@ -118,8 +118,10 @@ column. In conjunction with [`nonzeros`](@ref) and
nzrange(S::SparseMatrixCSC, col::Integer) = S.colptr[col]:(S.colptr[col+1]-1)

function Base.show(io::IO, ::MIME"text/plain", S::SparseMatrixCSC)
print(io, S.m, "×", S.n, " sparse matrix with ", nnz(S), " ", eltype(S), " stored entries")
if nnz(S) != 0
xnnz = nnz(S)
print(io, S.m, "×", S.n, " ", typeof(S), " with ", xnnz, " stored ",
xnnz == 1 ? "entry" : "entries")
if xnnz != 0
print(io, ":")
show(io, S)
end
Expand Down Expand Up @@ -147,8 +149,8 @@ function Base.show(io::IOContext, S::SparseMatrixCSC)
for col = 1:S.n, k = S.colptr[col] : (S.colptr[col+1]-1)
if k < half_screen_rows || k > nnz(S)-half_screen_rows
print(io, sep, '[', rpad(S.rowval[k], pad), ", ", lpad(col, pad), "] = ")
if isassigned(S.nzval, k)
Base.show(io, S.nzval[k])
if isassigned(S.nzval, Int(k))
show(io, S.nzval[k])
else
print(io, Base.undef_ref_str)
end
Expand Down Expand Up @@ -291,7 +293,12 @@ function copy!(A::SparseMatrixCSC, B::SparseMatrixCSC)
end

similar(S::SparseMatrixCSC, Tv::Type=eltype(S)) = SparseMatrixCSC(S.m, S.n, copy(S.colptr), copy(S.rowval), Array{Tv}(length(S.nzval)))
similar{Tv,Ti}(S::SparseMatrixCSC, ::Type{Tv}, ::Type{Ti}) = SparseMatrixCSC(S.m, S.n, convert(Array{Ti}, S.colptr), convert(Array{Ti}, S.rowval), Array{Tv}(length(S.nzval)))
function similar{Tv,Ti}(S::SparseMatrixCSC, ::Type{Tv}, ::Type{Ti})
new_colptr = copy!(similar(S.colptr, Ti), S.colptr)
new_rowval = copy!(similar(S.rowval, Ti), S.rowval)
new_nzval = copy!(similar(S.nzval, Tv), S.nzval)
SparseMatrixCSC(S.m, S.n, new_colptr, new_rowval, new_nzval)
end
@inline similar{Tv}(S::SparseMatrixCSC, ::Type{Tv}, d::Dims) = spzeros(Tv, d...)

# convert'ing between SparseMatrixCSC types
Expand Down Expand Up @@ -340,7 +347,7 @@ Convert a sparse matrix or vector `S` into a dense matrix or vector.
```jldoctest
julia> A = speye(3)
3×3 sparse matrix with 3 Float64 stored entries:
3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
Expand Down Expand Up @@ -376,7 +383,7 @@ julia> A = eye(3)
0.0 0.0 1.0
julia> sparse(A)
3×3 sparse matrix with 3 Float64 stored entries:
3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
Expand Down Expand Up @@ -457,7 +464,7 @@ julia> Js = [1; 2; 3];
julia> Vs = [1; 2; 3];
julia> sparse(Is, Js, Vs)
3×3 sparse matrix with 3 Int64 stored entries:
3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries:
[1, 1] = 1
[2, 2] = 2
[3, 3] = 3
Expand Down Expand Up @@ -1253,6 +1260,19 @@ which the probability of any element being nonzero is independently given by
values are sampled from the distribution specified by `rfn` and have the type `type`. The uniform
distribution is used in case `rfn` is not specified. The optional `rng`
argument specifies a random number generator, see [Random Numbers](@ref).
```jldoctest
julia> rng = MersenneTwister(1234);
julia> sprand(rng, Bool, 2, 2, 0.5)
2×2 SparseMatrixCSC{Bool,Int64} with 2 stored entries:
[1, 1] = true
[2, 1] = true
julia> sprand(rng, Float64, 3, 0.75)
3-element SparseVector{Float64,Int64} with 1 stored entry:
[3] = 0.298614
````
"""
function sprand{T}(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat,
rfn::Function, ::Type{T}=eltype(rfn(r,1)))
Expand Down Expand Up @@ -1291,6 +1311,16 @@ Create a random sparse vector of length `m` or sparse matrix of size `m` by `n`
with the specified (independent) probability `p` of any entry being nonzero,
where nonzero values are sampled from the normal distribution. The optional `rng`
argument specifies a random number generator, see [Random Numbers](@ref).
```jldoctest
julia> rng = MersenneTwister(1234);
julia> sprandn(rng, 2, 2, 0.75)
2×2 SparseMatrixCSC{Float64,Int64} with 3 stored entries:
[1, 1] = 0.532813
[2, 1] = -0.271735
[2, 2] = 0.502334
```
"""
sprandn(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) = sprand(r,m,n,density,randn,Float64)
sprandn(m::Integer, n::Integer, density::AbstractFloat) = sprandn(GLOBAL_RNG,m,n,density)
Expand All @@ -1304,14 +1334,14 @@ element having the value `1.0`.
```jldoctest
julia> A = sparse([1,2,3,4],[2,4,3,1],[5.,4.,3.,2.])
4×4 sparse matrix with 4 Float64 stored entries:
4×4 SparseMatrixCSC{Float64,Int64} with 4 stored entries:
[4, 1] = 2.0
[1, 2] = 5.0
[3, 3] = 3.0
[2, 4] = 4.0
julia> spones(A)
4×4 sparse matrix with 4 Float64 stored entries:
4×4 SparseMatrixCSC{Float64,Int64} with 4 stored entries:
[4, 1] = 1.0
[1, 2] = 1.0
[3, 3] = 1.0
Expand All @@ -1330,6 +1360,14 @@ Create a sparse vector of length `m` or sparse matrix of size `m x n`. This
sparse array will not contain any nonzero values. No storage will be allocated
for nonzero values during construction. The type defaults to `Float64` if not
specified.
```jldoctest
julia> spzeros(3, 3)
3×3 SparseMatrixCSC{Float64,Int64} with 0 stored entries
julia> spzeros(Float32, 4)
4-element SparseVector{Float32,Int64} with 0 stored entries
```
"""
spzeros(m::Integer, n::Integer) = spzeros(Float64, m, n)
spzeros(Tv::Type, m::Integer, n::Integer) = spzeros(Tv, Int, m, n)
Expand All @@ -1351,14 +1389,14 @@ Create a sparse identity matrix with the same size as `S`.
```jldoctest
julia> A = sparse([1,2,3,4],[2,4,3,1],[5.,4.,3.,2.])
4×4 sparse matrix with 4 Float64 stored entries:
4×4 SparseMatrixCSC{Float64,Int64} with 4 stored entries:
[4, 1] = 2.0
[1, 2] = 5.0
[3, 3] = 3.0
[2, 4] = 4.0
julia> speye(A)
4×4 sparse matrix with 4 Float64 stored entries:
4×4 SparseMatrixCSC{Float64,Int64} with 4 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
Expand Down Expand Up @@ -2733,6 +2771,17 @@ end
dropstored!(A::SparseMatrixCSC, i::Integer, j::Integer)
Drop entry `A[i,j]` from `A` if `A[i,j]` is stored, and otherwise do nothing.
```jldoctest
julia> A = sparse([1 2; 0 0])
2×2 SparseMatrixCSC{Int64,Int64} with 2 stored entries:
[1, 1] = 1
[1, 2] = 2
julia> Base.SparseArrays.dropstored!(A, 1, 2); A
2×2 SparseMatrixCSC{Int64,Int64} with 1 stored entry:
[1, 1] = 1
```
"""
function dropstored!(A::SparseMatrixCSC, i::Integer, j::Integer)
if !((1 <= i <= A.m) & (1 <= j <= A.n))
Expand Down Expand Up @@ -2935,7 +2984,7 @@ Concatenate matrices block-diagonally. Currently only implemented for sparse mat
# Example
```jldoctest
julia> blkdiag(speye(3), 2*speye(2))
5×5 sparse matrix with 5 Float64 stored entries:
5×5 SparseMatrixCSC{Float64,Int64} with 5 stored entries:
[1, 1] = 1.0
[2, 2] = 1.0
[3, 3] = 1.0
Expand Down Expand Up @@ -3143,7 +3192,7 @@ of the resulting sparse matrix.
```jldoctest
julia> spdiagm(([1,2,3,4],[4,3,2,1]),(-1,1))
5×5 sparse matrix with 8 Int64 stored entries:
5×5 SparseMatrixCSC{Int64,Int64} with 8 stored entries:
[2, 1] = 1
[1, 2] = 4
[3, 2] = 2
Expand Down
86 changes: 74 additions & 12 deletions base/sparse/sparsevector.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ count(x::SparseVector) = count(x.nzval)
nonzeros(x::SparseVector) = x.nzval
nonzeroinds(x::SparseVector) = x.nzind

similar(x::SparseVector, Tv::Type=eltype(x)) = SparseVector(x.n, copy(x.nzind), Array{Tv}(length(x.nzval)))
function similar{Tv,Ti}(x::SparseVector, ::Type{Tv}, ::Type{Ti})
return SparseVector(x.n, copy!(similar(x.nzind, Ti), x.nzind), copy!(similar(x.nzval, Tv), x.nzval))
end
similar{T}(x::SparseVector, ::Type{T}, D::Dims) = spzeros(T, D...)

### Construct empty sparse vector

spzeros(len::Integer) = spzeros(Float64, len)
spzeros{T}(::Type{T}, len::Integer) = SparseVector(len, Int[], T[])
spzeros{Tv, Ti <: Integer}(::Type{Tv}, ::Type{Ti}, len::Integer) = SparseVector(len, Ti[], Tv[])

# Construction of same structure, but with all ones
spones{T}(x::SparseVector{T}) = SparseVector(x.n, copy(x.nzind), ones(T, length(x.nzval)))
Expand Down Expand Up @@ -97,6 +102,28 @@ Create a sparse vector `S` of length `m` such that `S[I[k]] = V[k]`.
Duplicates are combined using the `combine` function, which defaults to
`+` if no `combine` argument is provided, unless the elements of `V` are Booleans
in which case `combine` defaults to `|`.
```jldoctest
julia> II = [1, 3, 3, 5]; V = [0.1, 0.2, 0.3, 0.2];
julia> sparsevec(II, V)
5-element SparseVector{Float64,Int64} with 3 stored entries:
[1] = 0.1
[3] = 0.5
[5] = 0.2
julia> sparsevec(II, V, 8, -)
8-element SparseVector{Float64,Int64} with 3 stored entries:
[1] = 0.1
[3] = -0.1
[5] = 0.2
julia> sparsevec([1, 3, 1, 2, 2], [true, true, false, false, false])
3-element SparseVector{Bool,Int64} with 3 stored entries:
[1] = true
[2] = false
[3] = true
```
"""
function sparsevec(I::AbstractVector{<:Integer}, V::AbstractVector, combine::Function)
length(I) == length(V) ||
Expand Down Expand Up @@ -144,10 +171,17 @@ sparsevec(I::AbstractVector, v::Number, len::Integer, combine::Function) =

### Construction from dictionary
"""
sparsevec(D::Dict, [m])
sparsevec(d::Dict, [m])
Create a sparse vector of length `m` where the nonzero indices are keys from
the dictionary, and the nonzero values are the values from the dictionary.
```jldoctest
julia> sparsevec(Dict(1 => 3, 2 => 2))
2-element SparseVector{Int64,Int64} with 2 stored entries:
[1] = 3
[2] = 2
```
"""
function sparsevec{Tv,Ti<:Integer}(dict::Associative{Ti,Tv})
m = length(dict)
Expand Down Expand Up @@ -218,6 +252,21 @@ setindex!{Tv, Ti<:Integer}(x::SparseVector{Tv,Ti}, v, i::Integer) =
dropstored!(x::SparseVector, i::Integer)
Drop entry `x[i]` from `x` if `x[i]` is stored and otherwise do nothing.
```jldoctest
julia> x = sparsevec([1, 3], [1.0, 2.0])
3-element SparseVector{Float64,Int64} with 2 stored entries:
[1] = 1.0
[3] = 2.0
julia> Base.SparseArrays.dropstored!(x, 3)
3-element SparseVector{Float64,Int64} with 1 stored entry:
[1] = 1.0
julia> Base.SparseArrays.dropstored!(x, 2)
3-element SparseVector{Float64,Int64} with 1 stored entry:
[1] = 1.0
```
"""
function dropstored!(x::SparseVector, i::Integer)
if !(1 <= i <= x.n)
Expand Down Expand Up @@ -255,6 +304,14 @@ convert{Tv,Ti}(::Type{SparseVector}, s::SparseMatrixCSC{Tv,Ti}) =
sparsevec(A)
Convert a vector `A` into a sparse vector of length `m`.
```jldoctest
julia> sparsevec([1.0, 2.0, 0.0, 0.0, 3.0, 0.0])
6-element SparseVector{Float64,Int64} with 3 stored entries:
[1] = 1.0
[2] = 2.0
[5] = 3.0
```
"""
sparsevec{T}(a::AbstractVector{T}) = convert(SparseVector{T, Int}, a)
sparsevec(a::AbstractArray) = sparsevec(vec(a))
Expand Down Expand Up @@ -677,8 +734,13 @@ getindex(x::AbstractSparseVector, ::Colon) = copy(x)
### show and friends

function show(io::IO, ::MIME"text/plain", x::AbstractSparseVector)
println(io, summary(x))
show(io, x)
xnnz = length(nonzeros(x))
print(io, length(x), "-element ", typeof(x), " with ", xnnz,
" stored ", xnnz == 1 ? "entry" : "entries")
if xnnz != 0
println(io, ":")
show(io, x)
end
end

show(io::IO, x::AbstractSparseVector) = show(convert(IOContext, io), x)
Expand All @@ -688,30 +750,30 @@ function show(io::IOContext, x::AbstractSparseVector)
nzind = nonzeroinds(x)
nzval = nonzeros(x)
xnnz = length(nzind)

if length(nzind) == 0
return show(io, MIME("text/plain"), x)
end
limit::Bool = get(io, :limit, false)
half_screen_rows = limit ? div(displaysize(io)[1] - 8, 2) : typemax(Int)
pad = ndigits(n)
sep = "\n\t"
if !haskey(io, :compact)
io = IOContext(io, :compact => true)
end
for k = 1:length(nzind)
if k < half_screen_rows || k > xnnz - half_screen_rows
print(io, " ", '[', rpad(nzind[k], pad), "] = ")
Base.show(io, nzval[k])
println(io)
if isassigned(nzval, Int(k))
show(io, nzval[k])
else
print(io, Base.undef_ref_str)
end
k != length(nzind) && println(io)
elseif k == half_screen_rows
println(io, " ", " "^pad, " \u22ee")
end
end
end

function summary(x::AbstractSparseVector)
string("Sparse vector of length ", length(x), " with ", length(nonzeros(x)),
" ", eltype(x), " nonzero entries:")
end

### Conversion to matrix

function convert{Tv,Ti}(::Type{SparseMatrixCSC{Tv,Ti}}, x::AbstractSparseVector)
Expand Down
Loading

0 comments on commit cee29b0

Please sign in to comment.