Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 54 additions & 25 deletions stdlib/SparseArrays/src/sparsematrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ size(S::SparseMatrixCSC) = (getfield(S, :m), getfield(S, :n))

_goodbuffers(S::SparseMatrixCSC) = _goodbuffers(size(S)..., getcolptr(S), getrowval(S), nonzeros(S))
_checkbuffers(S::SparseMatrixCSC) = (@assert _goodbuffers(S); S)
_checkbuffers(S::Union{Adjoint, Transpose}) = (_checkbuffers(parent(S)); S)

function _goodbuffers(m, n, colptr, rowval, nzval)
(length(colptr) == n + 1 && colptr[end] - 1 == length(rowval) == length(nzval))
Expand Down Expand Up @@ -125,6 +126,8 @@ julia> nnz(A)
"""
nnz(S::AbstractSparseMatrixCSC) = Int(getcolptr(S)[size(S, 2) + 1]) - 1
nnz(S::ReshapedArray{<:Any,1,<:AbstractSparseMatrixCSC}) = nnz(parent(S))
nnz(S::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = nnz(parent(S))
nnz(S::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = nnz(parent(S))
nnz(S::UpperTriangular{<:Any,<:AbstractSparseMatrixCSC}) = nnz1(S)
nnz(S::LowerTriangular{<:Any,<:AbstractSparseMatrixCSC}) = nnz1(S)
nnz(S::SparseMatrixCSCView) = nnz1(S)
Expand Down Expand Up @@ -215,6 +218,7 @@ nzrange(S::SparseMatrixCSCView, col::Integer) = nzrange(S.parent, S.indices[2][c
nzrange(S::UpperTriangular{<:Any,<:SparseMatrixCSCUnion}, i::Integer) = nzrangeup(S.data, i)
nzrange(S::LowerTriangular{<:Any,<:SparseMatrixCSCUnion}, i::Integer) = nzrangelo(S.data, i)

const AbstractSparseMatrixCSCInclAdjointAndTranspose = Union{AbstractSparseMatrixCSC,Adjoint{<:Any,<:AbstractSparseMatrixCSC},Transpose{<:Any,<:AbstractSparseMatrixCSC}}
function Base.isstored(A::AbstractSparseMatrixCSC, i::Integer, j::Integer)
@boundscheck checkbounds(A, i, j)
rows = rowvals(A)
Expand All @@ -224,20 +228,30 @@ function Base.isstored(A::AbstractSparseMatrixCSC, i::Integer, j::Integer)
return false
end

Base.replace_in_print_matrix(A::AbstractSparseMatrix, i::Integer, j::Integer, s::AbstractString) =
function Base.isstored(A::Union{Adjoint{<:Any,<:AbstractSparseMatrixCSC},Transpose{<:Any,<:AbstractSparseMatrixCSC}}, i::Integer, j::Integer)
@boundscheck checkbounds(A, i, j)
cols = rowvals(parent(A))
for istored in nzrange(parent(A), i)
j == cols[istored] && return true
end
return false
end

Base.replace_in_print_matrix(A::AbstractSparseMatrixCSCInclAdjointAndTranspose, i::Integer, j::Integer, s::AbstractString) =
Base.isstored(A, i, j) ? s : Base.replace_with_centered_mark(s)

function Base.array_summary(io::IO, S::AbstractSparseMatrixCSC, dims::Tuple{Vararg{Base.OneTo}})
function Base.array_summary(io::IO, S::AbstractSparseMatrixCSCInclAdjointAndTranspose, dims::Tuple{Vararg{Base.OneTo}})
_checkbuffers(S)

xnnz = nnz(S)
m, n = size(S)
print(io, m, "×", n, " ", typeof(S), " with ", xnnz, " stored ",
xnnz == 1 ? "entry" : "entries")
nothing
end

# called by `show(io, MIME("text/plain"), ::AbstractSparseMatrixCSC)`
function Base.print_array(io::IO, S::AbstractSparseMatrixCSC)
# called by `show(io, MIME("text/plain"), ::AbstractSparseMatrixCSCInclAdjointAndTranspose)`
function Base.print_array(io::IO, S::AbstractSparseMatrixCSCInclAdjointAndTranspose)
if max(size(S)...) < 16
Base.print_matrix(io, S)
else
Expand All @@ -246,7 +260,7 @@ function Base.print_array(io::IO, S::AbstractSparseMatrixCSC)
end

# always show matrices as `sparse(I, J, K)`
function Base.show(io::IO, S::AbstractSparseMatrixCSC)
function Base.show(io::IO, S::AbstractSparseMatrixCSCInclAdjointAndTranspose)
_checkbuffers(S)
# can't use `findnz`, because that expects all values not to be #undef
I = rowvals(S)
Expand All @@ -257,7 +271,7 @@ function Base.show(io::IO, S::AbstractSparseMatrixCSC)
end

const brailleBlocks = UInt16['⠁', '⠂', '⠄', '⡀', '⠈', '⠐', '⠠', '⢀']
function _show_with_braille_patterns(io::IO, S::AbstractSparseMatrixCSC)
function _show_with_braille_patterns(io::IO, S::AbstractSparseMatrixCSCInclAdjointAndTranspose)
m, n = size(S)
(m == 0 || n == 0) && return show(io, MIME("text/plain"), S)

Expand Down Expand Up @@ -291,28 +305,43 @@ function _show_with_braille_patterns(io::IO, S::AbstractSparseMatrixCSC)
brailleGrid = fill(UInt16(10240), (scaleWidth - 1) ÷ 2 + 2, (scaleHeight - 1) ÷ 4 + 1)
brailleGrid[end, :] .= '\n'

rvals = rowvals(S)
rvals = rowvals(parent(S))
rowscale = max(1, scaleHeight - 1) / max(1, m - 1)
colscale = max(1, scaleWidth - 1) / max(1, n - 1)
@inbounds for j = 1:n
# Scale the column index `j` to the best matching column index
# of a matrix of size `scaleHeight × scaleWidth`
sj = round(Int, (j - 1) * colscale + 1)
for x in nzrange(S, j)
# Scale the row index `i` to the best matching row index
if isa(S, AbstractSparseMatrixCSC)
@inbounds for j = 1:n
# Scale the column index `j` to the best matching column index
# of a matrix of size `scaleHeight × scaleWidth`
si = round(Int, (rvals[x] - 1) * rowscale + 1)

# Given the index pair `(si, sj)` of the scaled matrix,
# calculate the corresponding triple `(k, l, p)` such that the
# element at `(si, sj)` can be found at position `(k, l)` in the
# braille grid `brailleGrid` and corresponds to the 1-dot braille
# character `brailleBlocks[p]`
k = (sj - 1) ÷ 2 + 1
l = (si - 1) ÷ 4 + 1
p = ((sj - 1) % 2) * 4 + ((si - 1) % 4 + 1)

brailleGrid[k, l] |= brailleBlocks[p]
sj = round(Int, (j - 1) * colscale + 1)
for x in nzrange(S, j)
# Scale the row index `i` to the best matching row index
# of a matrix of size `scaleHeight × scaleWidth`
si = round(Int, (rvals[x] - 1) * rowscale + 1)

# Given the index pair `(si, sj)` of the scaled matrix,
# calculate the corresponding triple `(k, l, p)` such that the
# element at `(si, sj)` can be found at position `(k, l)` in the
# braille grid `brailleGrid` and corresponds to the 1-dot braille
# character `brailleBlocks[p]`
k = (sj - 1) ÷ 2 + 1
l = (si - 1) ÷ 4 + 1
p = ((sj - 1) % 2) * 4 + ((si - 1) % 4 + 1)

brailleGrid[k, l] |= brailleBlocks[p]
end
end
else
# If `S` is a adjoint or transpose of a sparse matrix we invert the
# roles of the indices `i` and `j`
@inbounds for i = 1:m
si = round(Int, (i - 1) * rowscale + 1)
for x in nzrange(parent(S), i)
sj = round(Int, (rvals[x] - 1) * colscale + 1)
k = (sj - 1) ÷ 2 + 1
l = (si - 1) ÷ 4 + 1
p = ((sj - 1) % 2) * 4 + ((si - 1) % 4 + 1)
brailleGrid[k, l] |= brailleBlocks[p]
end
end
end
foreach(c -> print(io, Char(c)), @view brailleGrid[1:end-1])
Expand Down
116 changes: 93 additions & 23 deletions stdlib/SparseArrays/test/sparse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2365,22 +2365,70 @@ end
for c in unstored_indices
@test Base.isstored(A, c[1], c[2]) == false
end

# `isstored` for adjoint and tranposed matrices:
for trans in (adjoint, transpose)
B = trans(A)
stored_indices = [CartesianIndex(j, i) for (j, i) in zip(J, I)]
unstored_indices = [c for c in CartesianIndices((n, m)) if !(c in stored_indices)]
for c in stored_indices
@test Base.isstored(B, c[1], c[2]) == true
end
for c in unstored_indices
@test Base.isstored(B, c[1], c[2]) == false
end
end
end

@testset "show" begin
io = IOBuffer()
show(io, MIME"text/plain"(), spzeros(Float64, Int64, 0, 0))
@test String(take!(io)) == "0×0 SparseArrays.SparseMatrixCSC{Float64, Int64} with 0 stored entries"
show(io, MIME"text/plain"(), sparse(Int64[1], Int64[1], [1.0]))
@test String(take!(io)) == "1×1 SparseArrays.SparseMatrixCSC{Float64, Int64} with 1 stored entry:\n 1.0"
show(io, MIME"text/plain"(), spzeros(Float32, Int64, 2, 2))
@test String(take!(io)) == "2×2 SparseArrays.SparseMatrixCSC{Float32, Int64} with 0 stored entries:\n ⋅ ⋅ \n ⋅ ⋅ "

A = spzeros(Float64, Int64, 0, 0)
for (transform, showstring) in zip(
(identity, adjoint, transpose), (
"0×0 $SparseMatrixCSC{Float64, Int64} with 0 stored entries",
"0×0 $Adjoint{Float64, $SparseMatrixCSC{Float64, Int64}} with 0 stored entries",
"0×0 $Transpose{Float64, $SparseMatrixCSC{Float64, Int64}} with 0 stored entries"
))
show(io, MIME"text/plain"(), transform(A))
@test String(take!(io)) == showstring
end

A = sparse(Int64[1], Int64[1], [1.0])
for (transform, showstring) in zip(
(identity, adjoint, transpose), (
"1×1 $SparseMatrixCSC{Float64, Int64} with 1 stored entry:\n 1.0",
"1×1 $Adjoint{Float64, $SparseMatrixCSC{Float64, Int64}} with 1 stored entry:\n 1.0",
"1×1 $Transpose{Float64, $SparseMatrixCSC{Float64, Int64}} with 1 stored entry:\n 1.0",
))
show(io, MIME"text/plain"(), transform(A))
@test String(take!(io)) == showstring
end

A = spzeros(Float32, Int64, 2, 2)
for (transform, showstring) in zip(
(identity, adjoint, transpose), (
"2×2 $SparseMatrixCSC{Float32, Int64} with 0 stored entries:\n ⋅ ⋅ \n ⋅ ⋅ ",
"2×2 $Adjoint{Float32, $SparseMatrixCSC{Float32, Int64}} with 0 stored entries:\n ⋅ ⋅ \n ⋅ ⋅ ",
"2×2 $Transpose{Float32, $SparseMatrixCSC{Float32, Int64}} with 0 stored entries:\n ⋅ ⋅ \n ⋅ ⋅ ",
))
show(io, MIME"text/plain"(), transform(A))
@test String(take!(io)) == showstring
end

A = sparse(Int64[1, 1], Int64[1, 2], [1.0, 2.0])
show(io, MIME"text/plain"(), A)
@test String(take!(io)) == "1×2 SparseArrays.SparseMatrixCSC{Float64, Int64} with 2 stored entries:\n 1.0 2.0"
_show_with_braille_patterns(convert(IOContext, io), A)
@test String(take!(io)) == "⠉"
for (transform, showstring, braille) in zip(
(identity, adjoint, transpose), (
"1×2 $SparseMatrixCSC{Float64, Int64} with 2 stored entries:\n 1.0 2.0",
"2×1 $Adjoint{Float64, $SparseMatrixCSC{Float64, Int64}} with 2 stored entries:\n 1.0\n 2.0",
"2×1 $Transpose{Float64, $SparseMatrixCSC{Float64, Int64}} with 2 stored entries:\n 1.0\n 2.0",
),
("⠉", "⠃", "⠃"))
show(io, MIME"text/plain"(), transform(A))
@test String(take!(io)) == showstring
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@test String(take!(io)) == braille
end

# every 1-dot braille pattern
for (i, b) in enumerate(split("⠁⠂⠄⡀⠈⠐⠠⢀", ""))
Expand All @@ -2392,28 +2440,50 @@ end

# empty braille pattern Char(10240)
A = spzeros(Int64, Int64, 4, 2)
_show_with_braille_patterns(convert(IOContext, io), A)
@test String(take!(io)) == "" * Char(10240)
for (transform, braille) in zip(
(identity, adjoint, transpose),
("" * Char(10240), "" * Char(10240)^2, "" * Char(10240)^2))
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@test String(take!(io)) == braille
end

A = sparse(Int64[1, 2, 4, 2, 3], Int64[1, 1, 1, 2, 2], Int64[1, 1, 1, 1, 1], 4, 2)
show(io, MIME"text/plain"(), A)
@test String(take!(io)) == "4×2 SparseArrays.SparseMatrixCSC{Int64, Int64} with 5 stored entries:\n 1 ⋅\n 1 1\n ⋅ 1\n 1 ⋅"
_show_with_braille_patterns(convert(IOContext, io), A)
@test String(take!(io)) == "⡳"
for (transform, showstring, braille) in zip(
(identity, adjoint, transpose), (
"4×2 $SparseMatrixCSC{Int64, Int64} with 5 stored entries:\n 1 ⋅\n 1 1\n ⋅ 1\n 1 ⋅",
"2×4 $Adjoint{Int64, $SparseMatrixCSC{Int64, Int64}} with 5 stored entries:\n 1 1 ⋅ 1\n ⋅ 1 1 ⋅",
"2×4 $Transpose{Int64, $SparseMatrixCSC{Int64, Int64}} with 5 stored entries:\n 1 1 ⋅ 1\n ⋅ 1 1 ⋅",
),
("⡳", "⠙⠊", "⠙⠊"))
show(io, MIME"text/plain"(), transform(A))
@test String(take!(io)) == showstring
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@test String(take!(io)) == braille
end

A = sparse(Int64[1, 3, 2, 4], Int64[1, 1, 2, 2], Int64[1, 1, 1, 1], 7, 3)
show(io, MIME"text/plain"(), A)
@test String(take!(io)) == "7×3 SparseArrays.SparseMatrixCSC{Int64, Int64} with 4 stored entries:\n 1 ⋅ ⋅\n ⋅ 1 ⋅\n 1 ⋅ ⋅\n ⋅ 1 ⋅\n ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅"
_show_with_braille_patterns(convert(IOContext, io), A)
@test String(take!(io)) == "⢕" * Char(10240) * "\n" * Char(10240)^2
for (transform, showstring, braille) in zip(
(identity, adjoint, transpose), (
"7×3 $SparseMatrixCSC{Int64, Int64} with 4 stored entries:\n 1 ⋅ ⋅\n ⋅ 1 ⋅\n 1 ⋅ ⋅\n ⋅ 1 ⋅\n ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅",
"3×7 $Adjoint{Int64, $SparseMatrixCSC{Int64, Int64}} with 4 stored entries:\n 1 ⋅ 1 ⋅ ⋅ ⋅ ⋅\n ⋅ 1 ⋅ 1 ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅",
"3×7 $Transpose{Int64, $SparseMatrixCSC{Int64, Int64}} with 4 stored entries:\n 1 ⋅ 1 ⋅ ⋅ ⋅ ⋅\n ⋅ 1 ⋅ 1 ⋅ ⋅ ⋅\n ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅",
),
("⢕" * Char(10240) * "\n" * Char(10240)^2, "⠑⠑" * Char(10240)^2, "⠑⠑" * Char(10240)^2))
show(io, MIME"text/plain"(), transform(A))
@test String(take!(io)) == showstring
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@test String(take!(io)) == braille
end

A = sparse(Int64[1:10;], Int64[1:10;], fill(Float64(1), 10))
_show_with_braille_patterns(convert(IOContext, io), A)
brailleString = "⠑⢄" * Char(10240)^3 * "\n" * Char(10240)^2 * "⠑⢄" * Char(10240) * "\n" * Char(10240)^4 * "⠑"
@test String(take!(io)) == brailleString
for transform in (identity, adjoint, transpose)
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@test String(take!(io)) == brailleString
end

# Issue #30589
@test repr("text/plain", sparse([true true])) == "1×2 SparseArrays.SparseMatrixCSC{Bool, $Int} with 2 stored entries:\n 1 1"
@test repr("text/plain", sparse([true true])) == "1×2 $SparseMatrixCSC{Bool, $Int} with 2 stored entries:\n 1 1"

function _filled_sparse(m::Integer, n::Integer)
C = CartesianIndices((m, n))[:]
Expand Down