Skip to content

Commit

Permalink
implement an isvalid function for sparse matrices and use it in show
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferC committed Jun 25, 2017
1 parent d0036c9 commit d35d80b
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
50 changes: 50 additions & 0 deletions base/sparse/sparsematrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,54 @@ column. In conjunction with [`nonzeros`](@ref) and
"""
nzrange(S::SparseMatrixCSC, col::Integer) = S.colptr[col]:(S.colptr[col+1]-1)

"""
isvalid(S::SparseMatrixCSC; full = true) -> Bool
Check if a sparse matrix `S` has a valid internal representation.
If `full` is `false` only O(1) checks are done, otherwise full
O(nnz) checks are performed.
# Examples
```jldoctest
julia> Base.SparseArrays.isvalid(sprand(5,5,0.5))
true
julia> Base.SparseArrays.isvalid(SparseMatrixCSC(4, 4, Int[], Int[], Int[]))
false
```
"""
function Base.isvalid(S::SparseMatrixCSC; full = true)
length(S.colptr) != S.n + 1 && return false
S.colptr[1] != 1 && return false
length(S.nzval) != length(S.rowval) && return false

if full
# Check colptr increasing
!issorted(S.colptr) && return false

# Check rowval in range and sorted in each column
for col in 1:S.n
nzrang = nzrange(S, col)
for r in nzrang
ri = S.rowval[r]
(ri <= 0 || ri > S.m) && return false
r == last(nzrang) && break
S.rowval[r+1] <= ri && return false
end
end
end
return true
end

_print_invalid(io, S) = print(io, S.m, "×", S.n, " ", typeof(S), " with invalid internal representation")

function Base.show(io::IO, ::MIME"text/plain", S::SparseMatrixCSC)
xnnz = nnz(S)
if !isvalid(S; full = false)
_print_invalid(io, S)
return
end
print(io, S.m, "×", S.n, " ", typeof(S), " with ", xnnz, " stored ",
xnnz == 1 ? "entry" : "entries")
if xnnz != 0
Expand All @@ -132,6 +178,10 @@ function Base.show(io::IOContext, S::SparseMatrixCSC)
if nnz(S) == 0
return show(io, MIME("text/plain"), S)
end
if !isvalid(S; full = false)
_print_invalid(io, S)
return
end

limit::Bool = get(io, :limit, false)
if limit
Expand Down
36 changes: 36 additions & 0 deletions test/sparse/sparse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1795,3 +1795,39 @@ end
@test nnz(A) == n
@test A == eye(n)
end

@testset "isvalid" begin
for x in [sprand(10,5,0.5), sprand(5,10,0.5), spzeros(0,0), spzeros(2,2)]
@test isvalid(x; full = true)
end

# Colptr doesn't start with 1
x = SparseMatrixCSC(2, 2, [2, 3, 5], [1, 2, 1, 2], rand(4))
@test isvalid(x; full = false) == false

# Length of rowval / nzval mismatch
x = SparseMatrixCSC(2, 2, [1, 3, 5], [1, 2, 1, 2], rand(54))
@test isvalid(x; full = false) == false

# Row val out of range
x = SparseMatrixCSC(2, 2, [1, 3, 5], [1, 2, 1, 3], rand(4))
@test isvalid(x) == false

# Row not sorted in column
x = SparseMatrixCSC(3, 3, [1, 3, 6, 8],
[1, 2, 1, 3, 2, 2, 3],
rand(7))
@test isvalid(x) == false

# Colptr not increasing
x = SparseMatrixCSC(2, 2, [1, 5, 3], [1, 2, 1, 3], rand(4))
@test isvalid(x) == false

# Colptr not correct length
x = SparseMatrixCSC(2, 2, Int64[1, 3, 5, 8], Int64[1, 2, 1, 2], rand(4))
@test isvalid(x; full = false) == false

io = IOBuffer()
show(io, MIME"text/plain"(), sparse(x))
@test String(take!(io)) == "2×2 SparseMatrixCSC{Float64,Int64} with invalid internal representation"
end

0 comments on commit d35d80b

Please sign in to comment.