Skip to content

Commit afd9e00

Browse files
committed
simplify spy plot + fix aliasing
1 parent 7969cbe commit afd9e00

2 files changed

Lines changed: 35 additions & 109 deletions

File tree

src/sparsematrix.jl

Lines changed: 26 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -406,106 +406,48 @@ end
406406

407407
const brailleBlocks = UInt16['', '', '', '', '', '', '', '']
408408
function _show_with_braille_patterns(io::IO, S::AbstractSparseMatrixCSCInclAdjointAndTranspose)
409+
# The maximum number of characters we allow to display the matrix
410+
h, w = get(io, :limit, false) ? displaysize(io) .- (4, 2) : (typemax(Int)÷4, typemax(Int)÷2)
409411
m, n = size(S)
410-
(m == 0 || n == 0) && return show(io, MIME("text/plain"), S)
411-
412-
# The maximal number of characters we allow to display the matrix
413-
local maxHeight::Int, maxWidth::Int
414-
maxHeight = displaysize(io)[1] - 4 # -4 from [Prompt, header, newline after elements, new prompt]
415-
maxWidth = displaysize(io)[2] - 2 # -2 from brackets
416412

417413
warn = false
418414
try
419415
zero(eltype(S))
420416
catch
421417
warn = true
422-
maxHeight -= 1
423-
end
424-
425-
# In the process of generating the braille pattern to display the nonzero
426-
# structure of `S`, we need to be able to scale the matrix `S` to a
427-
# smaller matrix with the same aspect ratio as `S`, but fits on the
428-
# available screen space. The size of that smaller matrix is stored
429-
# in the variables `scaleHeight` and `scaleWidth`. If no scaling is needed,
430-
# we can use the size `m × n` of `S` directly.
431-
# We determine if scaling is needed and set the scaling factors
432-
# `scaleHeight` and `scaleWidth` accordingly. Note that each available
433-
# character can contain up to 4 braille dots in its height (⡇) and up to
434-
# 2 braille dots in its width (⠉).
435-
if get(io, :limit, false) && (m > 4maxHeight || n > 2maxWidth)
436-
s = min(2maxWidth / n, 4maxHeight / m)
437-
scaleHeight = floor(Int, s * m)
438-
scaleWidth = floor(Int, s * n)
439-
else
440-
scaleHeight = m
441-
scaleWidth = n
418+
h -= 1
442419
end
443420

444-
# Make sure that the matrix size is big enough to be able to display all
445-
# the corner border characters
446-
if scaleHeight < 8
447-
scaleHeight = 8
448-
end
449-
if scaleWidth < 4
450-
scaleWidth = 4
451-
end
421+
# In order to prevent aliasing, we only scale down by full integers.
422+
# While 1 to 1/2 is a big jump, it's less noticeable as you get bigger.
423+
# Note each character has 4 dots of height and 2 dots of width.
424+
scale = max(n÷2w, m÷4h) + 1
425+
char_h, char_w = fld1(m, 4scale), fld1(n, 2scale)
426+
427+
scale != 1 && print(io, " (displaying at 1/$scale scale)")
428+
println(io, ":")
429+
warn && printstyled(stderr, "WARNING: could not find generic zero for given elements. expect errors and wrong results\n", color=:red)
452430

453-
# `brailleGrid` is used to store the needed braille characters for
454-
# the matrix `S`. Each row of the braille pattern to print is stored
455-
# in a column of `brailleGrid`.
456-
brailleGrid = fill(UInt16(10240), (scaleWidth - 1) ÷ 2 + 4, (scaleHeight - 1) ÷ 4 + 1)
431+
# Rows of output are cols of `brailleGrid` since julia is column-major
432+
brailleGrid = fill(UInt16(10240), char_w + 3, char_h)
457433
brailleGrid[1,:] .= ''
458434
brailleGrid[end-1,:] .= ''
459-
brailleGrid[1,1] = ''
460-
brailleGrid[1,end] = ''
461-
brailleGrid[end-1,1] = ''
462-
brailleGrid[end-1,end] = ''
463435
brailleGrid[end, :] .= '\n'
436+
brailleGrid[[1,end-1],[1,end]] .= ['' '';'' '']
437+
char_h == 1 && (brailleGrid[[1,end-1],1] .= ['[', ']'])
464438

465-
rvals = rowvals(parent(S))
466-
rowscale = max(1, scaleHeight - 1) / max(1, m - 1)
467-
colscale = max(1, scaleWidth - 1) / max(1, n - 1)
439+
rinds = rowvals(parent(S))
440+
cinds = ColumnIndices(parent(S))
468441

469-
if rowscale != 1 || colscale != 1
470-
print(io, " (downscaled to fit on screen)")
471-
end
472-
println(io, ":")
473-
warn && printstyled(stderr, "WARNING: could not find generic zero for given elements. expect errors and wrong results\n", color=:red)
474-
475-
if isa(S, AbstractSparseMatrixCSC)
476-
@inbounds for j in axes(S,2)
477-
# Scale the column index `j` to the best matching column index
478-
# of a matrix of size `scaleHeight × scaleWidth`
479-
sj = round(Int, (j - 1) * colscale + 1)
480-
for x in nzrange(S, j)
481-
# Scale the row index `i` to the best matching row index
482-
# of a matrix of size `scaleHeight × scaleWidth`
483-
si = round(Int, (rvals[x] - 1) * rowscale + 1)
484-
485-
# Given the index pair `(si, sj)` of the scaled matrix,
486-
# calculate the corresponding triple `(k, l, p)` such that the
487-
# element at `(si, sj)` can be found at position `(k, l)` in the
488-
# braille grid `brailleGrid` and corresponds to the 1-dot braille
489-
# character `brailleBlocks[p]`
490-
k = (sj - 1) ÷ 2 + 2
491-
l = (si - 1) ÷ 4 + 1
492-
p = ((sj - 1) % 2) * 4 + ((si - 1) % 4 + 1)
493-
494-
brailleGrid[k, l] |= brailleBlocks[p]
495-
end
442+
if S isa AbstractSparseMatrixCSC
443+
for cords in zip(rinds, cinds)
444+
row, col = fld1.(cords, scale) |>x-> fldmod1.(x, (4, 2))
445+
brailleGrid[col[1]+1, row[1]] |= brailleBlocks[row[2] + 4(col[2]-1)]
496446
end
497-
else
498-
# If `S` is a adjoint or transpose of a sparse matrix we invert the
499-
# roles of the indices `i` and `j`
500-
@inbounds for i = 1:m
501-
si = round(Int, (i - 1) * rowscale + 1)
502-
for x in nzrange(parent(S), i)
503-
sj = round(Int, (rvals[x] - 1) * colscale + 1)
504-
k = (sj - 1) ÷ 2 + 2
505-
l = (si - 1) ÷ 4 + 1
506-
p = ((sj - 1) % 2) * 4 + ((si - 1) % 4 + 1)
507-
brailleGrid[k, l] |= brailleBlocks[p]
508-
end
447+
else # swap rows / cols for adj and transpose
448+
for cords in zip(rinds, cinds)
449+
col, row = fld1.(cords, scale) |>x-> fldmod1.(x, (2, 4))
450+
brailleGrid[col[1]+1, row[1]] |= brailleBlocks[row[2] + 4(col[2]-1)]
509451
end
510452
end
511453
foreach(c -> print(io, Char(c)), @view brailleGrid[1:end-1])

test/sparsematrix_constructors_indexing.jl

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,12 +1529,7 @@ end
15291529
"2×1 $Adjoint{Float64, $SparseMatrixCSC{Float64, Int64}} with 2 stored entries:\n 1.0\n 2.0",
15301530
"2×1 $Transpose{Float64, $SparseMatrixCSC{Float64, Int64}} with 2 stored entries:\n 1.0\n 2.0",
15311531
),
1532-
("⎡⠁⠈⎤\n" *
1533-
"⎣⠀⠀⎦",
1534-
"⎡⠁⠀⎤\n" *
1535-
"⎣⡀⠀⎦",
1536-
"⎡⠁⠀⎤\n" *
1537-
"⎣⡀⠀⎦"))
1532+
("\n[⠉]", "\n[⠃]", "\n[⠃]"))
15381533
show(io, MIME"text/plain"(), transform(A))
15391534
@test String(take!(io)) == showstring
15401535
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@@ -1557,7 +1552,7 @@ end
15571552
# empty braille pattern Char(10240)
15581553
A = spzeros(Int64, Int64, 4, 2)
15591554
for transform in (identity, adjoint, transpose)
1560-
expected = "" * Char(10240)^2 * "\n" * Char(10240)^2 * ""
1555+
expected = ":\n[" * Char(10240)^2 * "]"
15611556
_show_with_braille_patterns(convert(IOContext, io), transform(A))
15621557
@test contains(String(take!(io)), expected)
15631558
end
@@ -1569,12 +1564,7 @@ end
15691564
"2×4 $Adjoint{Int64, $SparseMatrixCSC{Int64, Int64}} with 5 stored entries:\n 1 1 ⋅ 1\n ⋅ 1 1 ⋅",
15701565
"2×4 $Transpose{Int64, $SparseMatrixCSC{Int64, Int64}} with 5 stored entries:\n 1 1 ⋅ 1\n ⋅ 1 1 ⋅",
15711566
),
1572-
("⎡⠅⠠⎤\n" *
1573-
"⎣⡀⠐⎦",
1574-
"⎡⠉⠈⎤\n" *
1575-
"⎣⢀⡀⎦",
1576-
"⎡⠉⠈⎤\n" *
1577-
"⎣⢀⡀⎦"))
1567+
("\n[⡳]", "\n[⠙⠊]", "\n[⠙⠊]"))
15781568
show(io, MIME"text/plain"(), transform(A))
15791569
@test String(take!(io)) == showstring
15801570
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@@ -1590,10 +1580,8 @@ end
15901580
),
15911581
("⎡⢕⠀⎤\n" *
15921582
"⎣⠀⠀⎦",
1593-
"⎡⢁⢁⠀⠀⎤\n" *
1594-
"⎣⠀⠀⠀⠀⎦",
1595-
"⎡⢁⢁⠀⠀⎤\n" *
1596-
"⎣⠀⠀⠀⠀⎦"))
1583+
"[⠑⠑⠀⠀]",
1584+
"[⠑⠑⠀⠀]"))
15971585
show(io, MIME"text/plain"(), transform(A))
15981586
@test String(take!(io)) == showstring
15991587
_show_with_braille_patterns(convert(IOContext, io), transform(A))
@@ -1622,22 +1610,18 @@ end
16221610
# vertical scaling
16231611
ioc = IOContext(io, :displaysize => (5, 80), :limit => true)
16241612
_show_with_braille_patterns(ioc, _filled_sparse(10, 10))
1625-
@test contains(String(take!(io)), "⎡⣿⣿⎤\n" *
1626-
"⎣⣿⣿⎦")
1613+
@test contains(String(take!(io)), "\n[⣿⣿]")
16271614

16281615
_show_with_braille_patterns(ioc, _filled_sparse(20, 10))
1629-
@test contains(String(take!(io)), "⎡⣿⣿⎤\n" *
1630-
"⎣⣿⣿⎦")
1616+
@test contains(String(take!(io)), "\n[⣿]")
16311617

16321618
# horizontal scaling
16331619
ioc = IOContext(io, :displaysize => (80, 4), :limit => true)
16341620
_show_with_braille_patterns(ioc, _filled_sparse(8, 8))
1635-
@test contains(String(take!(io)), "⎡⣿⣿⎤\n" *
1636-
"⎣⣿⣿⎦")
1621+
@test contains(String(take!(io)), "\n[⠿⠇]")
16371622

16381623
_show_with_braille_patterns(ioc, _filled_sparse(8, 16))
1639-
@test contains(String(take!(io)), "⎡⣿⣿⎤\n" *
1640-
"⎣⣿⣿⎦")
1624+
@test contains(String(take!(io)), "\n[⠛⠛]")
16411625

16421626
# respect IOContext while displaying J
16431627
I, J, V = shuffle(1:50), shuffle(1:50), [1:50;]

0 commit comments

Comments
 (0)