From 38b421cb2169cea16db85bf5fc755580bd84dbe3 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 7 Feb 2017 10:02:37 +0100 Subject: [PATCH] make sparse and dense arrays print more similarly --- base/abstractarray.jl | 2 +- base/sparse/sparsematrix.jl | 70 +++++++++++++++++++++++++++------- base/sparse/sparsevector.jl | 76 +++++++++++++++++++++++++++++++------ doc/src/manual/arrays.md | 8 ++-- test/sparse/sparse.jl | 8 ++++ test/sparse/sparsevector.jl | 12 ++++++ 6 files changed, 147 insertions(+), 29 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 5ca6ef34820f1..4f8923c3d4272 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -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 diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 7ebaae7c0b5fd..69247382b91b2 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -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 @@ -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 @@ -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 @@ -118,7 +118,9 @@ 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") + xnnz = nnz(S) + print(io, S.m, "×", S.n, " ", typeof(S), " with ", nnz(S), " stored ", + xnnz == 1 ? "entry" : "entries") if nnz(S) != 0 print(io, ":") show(io, S) @@ -340,7 +342,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 @@ -376,7 +378,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 @@ -457,7 +459,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 @@ -1253,6 +1255,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))) @@ -1291,6 +1306,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) @@ -1304,14 +1329,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 @@ -1330,6 +1355,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) @@ -1351,14 +1384,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 @@ -2733,6 +2766,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, 2] = 1 +``` """ function dropstored!(A::SparseMatrixCSC, i::Integer, j::Integer) if !((1 <= i <= A.m) & (1 <= j <= A.n)) @@ -2935,7 +2979,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 @@ -3143,7 +3187,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 diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index f9478910d66f7..75070d5c56d67 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -41,6 +41,7 @@ similar{T}(x::SparseVector, ::Type{T}, D::Dims) = spzeros(T, D...) 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))) @@ -97,6 +98,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{Ti<:Integer}(I::AbstractVector{Ti}, V::AbstractVector, combine::Function) length(I) == length(V) || @@ -144,10 +167,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 => 4)) +2-element SparseVector{Int64,Int64} with 2 stored entries: + [1] = 3 + [2] = 4 +``` """ function sparsevec{Tv,Ti<:Integer}(dict::Associative{Ti,Tv}) m = length(dict) @@ -218,6 +248,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) @@ -255,6 +300,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)) @@ -678,8 +731,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 function show(io::IO, x::AbstractSparseVector) @@ -688,11 +746,12 @@ function show(io::IO, 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" io = IOContext(io) if !haskey(io, :compact) io = IOContext(io, :compact => true) @@ -701,18 +760,13 @@ function show(io::IO, x::AbstractSparseVector) if k < half_screen_rows || k > xnnz - half_screen_rows print(io, " ", '[', rpad(nzind[k], pad), "] = ") Base.show(io, nzval[k]) - println(io) + 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{TvD,TiD,Tv,Ti}(::Type{SparseMatrixCSC{TvD,TiD}}, x::AbstractSparseVector{Tv,Ti}) diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index f413d62ae9dc1..1cf8d4eb4d160 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -754,10 +754,10 @@ sparse matrices instead, you can use the same names with an `sp` prefix: ```jldoctest julia> spzeros(3,5) -3×5 sparse matrix with 0 Float64 stored entries +3×5 SparseMatrixCSC{Float64,Int64} with 0 stored entries julia> speye(3,5) -3×5 sparse matrix with 3 Float64 stored entries: +3×5 SparseMatrixCSC{Float64,Int64} with 3 stored entries: [1, 1] = 1.0 [2, 2] = 1.0 [3, 3] = 1.0 @@ -771,7 +771,7 @@ values. `sparse(I,J,V)` constructs a sparse matrix such that `S[I[k], J[k]] = V[ julia> I = [1, 4, 3, 5]; J = [4, 7, 18, 9]; V = [1, 2, -5, 3]; julia> S = sparse(I,J,V) -5×18 sparse matrix with 4 Int64 stored entries: +5×18 SparseMatrixCSC{Int64,Int64} with 4 stored entries: [1 , 4] = 1 [4 , 7] = 2 [5 , 9] = 3 @@ -794,7 +794,7 @@ the [`sparse()`](@ref) function: ```jldoctest julia> sparse(eye(5)) -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 diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index f6bed5fe0dfe3..ea342f31f5c13 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1721,3 +1721,11 @@ end @test x.colptr == [1, 2, 4, 6] @test x[2, 3] == 0.0 end + +@testset "show" begin + io = IOBuffer() + show(io, MIME"text/plain"(), sparse(Int64[1], Int64[1], [1.0])) + @test String(take!(io)) == "1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:\n [1, 1] = 1.0" + show(io, MIME"text/plain"(), spzeros(Float32, Int64, 2, 2)) + @test String(take!(io)) == "2×2 SparseMatrixCSC{Float32,Int64} with 0 stored entries" +end diff --git a/test/sparse/sparsevector.jl b/test/sparse/sparsevector.jl index a38cc436c373c..5f860e3b23235 100644 --- a/test/sparse/sparsevector.jl +++ b/test/sparse/sparsevector.jl @@ -1122,3 +1122,15 @@ end @test issparse([sprand(10,.1) rand(10)]) @test issparse([sprand(10,.1); rand(10)]) + +@testset "show" begin + io = IOBuffer() + show(io, MIME"text/plain"(), sparsevec(Int64[1], [1.0])) + @test String(take!(io)) == "1-element SparseVector{Float64,Int64} with 1 stored entry:\n [1] = 1.0" + show(io, MIME"text/plain"(), spzeros(Float64, Int64, 2)) + @test String(take!(io)) == "2-element SparseVector{Float64,Int64} with 0 stored entries" +end + +@testset "spzeros with index type" begin + @test typeof(spzeros(Float32, Int16, 3)) == SparseVector{Float32,Int16} +end