From d45df8dd3354a53ebaf5601c9af19dfffd3a69ff Mon Sep 17 00:00:00 2001 From: Timo Kluck Date: Wed, 24 May 2017 00:09:49 +0200 Subject: [PATCH 1/8] Sparse vector/matrix: add fast implementation of find_next and find_prev Before this commit, find_next() will just use the default implementation of looping over each element. When find_next is called without a function filter as first argument, we *know* that semantics are to find elements x satisfying x != 0, so for sparse matrices/vectors, we may only loop over the stored elements. Some care must be taken for stored zero values; that's the reason for the indirection of _sparse_find_next (which only finds the next stored element) and the actual find_next (which does actual non-zero checks). --- base/sparse/abstractsparse.jl | 33 ++++++++++++++++++++++++++++++++ base/sparse/sparse.jl | 4 ++-- base/sparse/sparsematrix.jl | 36 +++++++++++++++++++++++++++++++++++ base/sparse/sparsevector.jl | 18 ++++++++++++++++++ test/sparse/sparse.jl | 23 ++++++++++++++++++++++ 5 files changed, 112 insertions(+), 2 deletions(-) diff --git a/base/sparse/abstractsparse.jl b/base/sparse/abstractsparse.jl index e17d3be97dbcb..593fd651ef679 100644 --- a/base/sparse/abstractsparse.jl +++ b/base/sparse/abstractsparse.jl @@ -21,3 +21,36 @@ issparse(S::UpperTriangular{<:Any,<:AbstractSparseMatrix}) = true issparse(S::LinAlg.UnitUpperTriangular{<:Any,<:AbstractSparseMatrix}) = true indtype(S::AbstractSparseArray{<:Any,Ti}) where {Ti} = Ti + +# The following two methods should be overloaded by concrete types to avoid +# allocating the I = find(...) +_sparse_findnext(v::AbstractSparseArray, i) = (I = find(v); n = searchsortedfirst(I, i); n<=length(I) ? I[n] : 0) +_sparse_findprev(v::AbstractSparseArray, i) = (I = find(v); n = searchsortedlast(I, i); n>0 ? I[n] : 0) + +function findnext(v::AbstractSparseArray, i::Int) + j = _sparse_findnext(v, i) + if j == 0 + return 0 + end + while v[j] == 0 + j = _sparse_findnext(v, j+1) + if j == 0 + return 0 + end + end + return j +end + +function findprev(v::AbstractSparseArray, i::Int) + j = _sparse_findprev(v, i) + if j == 0 + return 0 + end + while v[j] == 0 + j = _sparse_findprev(v, j-1) + if j == 0 + return 0 + end + end + return j +end diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index 28126d551c10b..23d54505e144f 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -15,8 +15,8 @@ import Base.LinAlg: At_ldiv_B!, Ac_ldiv_B!, A_rdiv_B!, A_rdiv_Bc! import Base: @get!, acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh, atan, atand, atanh, broadcast!, chol, conj!, cos, cosc, cosd, cosh, cospi, cot, cotd, coth, countnz, csc, cscd, csch, ctranspose!, diag, diff, done, dot, eig, - exp10, exp2, eye, findn, floor, hash, indmin, inv, issymmetric, istril, istriu, - log10, log2, lu, next, sec, secd, sech, show, sin, + exp10, exp2, eye, findn, findprev, findnext, floor, hash, indmin, inv, issymmetric, + istril, istriu, log10, log2, lu, next, sec, secd, sech, show, sin, sinc, sind, sinh, sinpi, squeeze, start, sum, summary, tan, tand, tanh, trace, transpose!, tril!, triu!, trunc, vecnorm, abs, abs2, broadcast, ceil, complex, cond, conj, convert, copy, copy!, ctranspose, diagm, diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 212565d4cddcc..c8769e62b94c7 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1332,6 +1332,42 @@ function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} return (I, J, V) end +function _sparse_findnext(m::SparseMatrixCSC, i::Int) + if i > length(m) + return 0 + end + row, col = ind2sub(m, i) + lo, hi = m.colptr[col], m.colptr[col+1] + n = searchsortedfirst(@view(m.rowval[lo:hi-1]), row) + if 1 <= n <= hi-lo + return sub2ind(m, m.rowval[lo+n-1], col) + end + nextcol = findnext(c->(c>hi), m.colptr, col+1) + if nextcol == 0 + return 0 + end + nextlo = m.colptr[nextcol-1] + return sub2ind(m, m.rowval[nextlo], nextcol-1) +end + +function _sparse_findprev(m::SparseMatrixCSC, i::Int) + if i < 1 + return 0 + end + row, col = ind2sub(m, i) + lo, hi = m.colptr[col], m.colptr[col+1] + n = searchsortedlast(@view(m.rowval[lo:hi-1]), row) + if 1 <= n <= hi-lo + return sub2ind(m, m.rowval[lo+n-1], col) + end + prevcol = findprev(c->(c length(v.nzind) + return 0 + else + return v.nzind[n] + end +end + +function _sparse_findprev(v::SparseVector, i::Int) + n = searchsortedlast(v.nzind, i) + if n < 1 + return 0 + else + return v.nzind[n] + end +end + ### Generic functions operating on AbstractSparseVector ### getindex diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 714c8fea0f582..19025e68707f1 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1899,3 +1899,26 @@ end @test isfinite.(cov_sparse) == isfinite.(cov_dense) end end + +@testset "sparse findprev/findnext operations" begin + + x = [0,0,0,0,1,0,1,0,1,1,0] + x_sp = sparse(x) + + for i=1:length(x) + @test findnext(x,i) == findnext(x_sp,i) + @test findprev(x,i) == findprev(x_sp,i) + end + + y = [0 0 0 0 0; + 1 0 1 0 0; + 1 0 0 0 1; + 0 0 1 0 0; + 1 0 1 1 0] + y_sp = sparse(y) + + for i=1:length(y) + @test findnext(y,i) == findnext(y_sp,i) + @test findprev(y,i) == findprev(y_sp,i) + end +end From 14f443a86ce902bd52a3ce27430781d9b109cc00 Mon Sep 17 00:00:00 2001 From: Timo Kluck Date: Fri, 18 Aug 2017 11:49:15 +0200 Subject: [PATCH 2/8] Sparse findprev/findnext: replace searchsorted(@view...) by searchsorted(...,lo=,hi=) --- base/sparse/sparsematrix.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index c8769e62b94c7..1e07981d8628c 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1338,9 +1338,9 @@ function _sparse_findnext(m::SparseMatrixCSC, i::Int) end row, col = ind2sub(m, i) lo, hi = m.colptr[col], m.colptr[col+1] - n = searchsortedfirst(@view(m.rowval[lo:hi-1]), row) - if 1 <= n <= hi-lo - return sub2ind(m, m.rowval[lo+n-1], col) + n = searchsortedfirst(m.rowval, row, lo, hi-1, Base.Order.Forward) + if lo <= n <= hi-1 + return sub2ind(m, m.rowval[n], col) end nextcol = findnext(c->(c>hi), m.colptr, col+1) if nextcol == 0 @@ -1356,9 +1356,9 @@ function _sparse_findprev(m::SparseMatrixCSC, i::Int) end row, col = ind2sub(m, i) lo, hi = m.colptr[col], m.colptr[col+1] - n = searchsortedlast(@view(m.rowval[lo:hi-1]), row) - if 1 <= n <= hi-lo - return sub2ind(m, m.rowval[lo+n-1], col) + n = searchsortedlast(m.rowval, row, lo, hi-1, Base.Order.Forward) + if lo <= n <= hi-1 + return sub2ind(m, m.rowval[n], col) end prevcol = findprev(c->(c Date: Sat, 19 Aug 2017 09:13:41 +0200 Subject: [PATCH 3/8] _sparse_findnext/prev: fix indentation (Use git blame -w for finding the non-whitespace edits to this code.) --- base/sparse/sparsematrix.jl | 60 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 1e07981d8628c..013943882da27 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1333,39 +1333,39 @@ function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} end function _sparse_findnext(m::SparseMatrixCSC, i::Int) - if i > length(m) - return 0 - end - row, col = ind2sub(m, i) - lo, hi = m.colptr[col], m.colptr[col+1] - n = searchsortedfirst(m.rowval, row, lo, hi-1, Base.Order.Forward) - if lo <= n <= hi-1 - return sub2ind(m, m.rowval[n], col) - end - nextcol = findnext(c->(c>hi), m.colptr, col+1) - if nextcol == 0 - return 0 - end - nextlo = m.colptr[nextcol-1] - return sub2ind(m, m.rowval[nextlo], nextcol-1) + if i > length(m) + return 0 + end + row, col = ind2sub(m, i) + lo, hi = m.colptr[col], m.colptr[col+1] + n = searchsortedfirst(m.rowval, row, lo, hi-1, Base.Order.Forward) + if lo <= n <= hi-1 + return sub2ind(m, m.rowval[n], col) + end + nextcol = findnext(c->(c>hi), m.colptr, col+1) + if nextcol == 0 + return 0 + end + nextlo = m.colptr[nextcol-1] + return sub2ind(m, m.rowval[nextlo], nextcol-1) end function _sparse_findprev(m::SparseMatrixCSC, i::Int) - if i < 1 - return 0 - end - row, col = ind2sub(m, i) - lo, hi = m.colptr[col], m.colptr[col+1] - n = searchsortedlast(m.rowval, row, lo, hi-1, Base.Order.Forward) - if lo <= n <= hi-1 - return sub2ind(m, m.rowval[n], col) - end - prevcol = findprev(c->(c(c Date: Sat, 19 Aug 2017 09:21:47 +0200 Subject: [PATCH 4/8] sparse findprev/findnext: add doctest with stored zeros --- test/sparse/sparse.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 19025e68707f1..e0031e33d7034 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -1921,4 +1921,12 @@ end @test findnext(y,i) == findnext(y_sp,i) @test findprev(y,i) == findprev(y_sp,i) end + + z_sp = sparsevec(Dict(1=>1, 5=>1, 8=>0, 10=>1)) + z = collect(z_sp) + + for i=1:length(z) + @test findnext(z,i) == findnext(z_sp,i) + @test findprev(z,i) == findprev(z_sp,i) + end end From fe4b76ef3edc258fb73fe29824f345ae1923f0bc Mon Sep 17 00:00:00 2001 From: Timo Kluck Date: Fri, 3 Nov 2017 09:41:45 -0700 Subject: [PATCH 5/8] Optimized findnext() for sparse: update now that predicate needs to be explicit Since we now need explicit predicates [1], this optimization only works if we know that the predicate is a function that is false for zero values. As suggested in that pull request, we could find out by calling `f(zero(eltype(array)))` and hoping that `f` is pure, but I like being a bit more conservative and only applying this optimization only to the case where we *know* `f` is equal to `!iszero`. For clarity, this commit also renames the helper method _sparse_findnext() to _sparse_findnextnz(), because now that the predicate-less version doesn't exist anymore, the `nz` part isn't implicit anymore either. [1]: https://github.com/JuliaLang/julia/pull/23812 --- base/sparse/abstractsparse.jl | 20 ++++++++++---------- base/sparse/sparsematrix.jl | 4 ++-- base/sparse/sparsevector.jl | 4 ++-- test/sparse/sparse.jl | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/base/sparse/abstractsparse.jl b/base/sparse/abstractsparse.jl index 1402241c422c5..05d65d97d9645 100644 --- a/base/sparse/abstractsparse.jl +++ b/base/sparse/abstractsparse.jl @@ -31,16 +31,16 @@ end # The following two methods should be overloaded by concrete types to avoid # allocating the I = find(...) -_sparse_findnext(v::AbstractSparseArray, i) = (I = find(v); n = searchsortedfirst(I, i); n<=length(I) ? I[n] : 0) -_sparse_findprev(v::AbstractSparseArray, i) = (I = find(v); n = searchsortedlast(I, i); n>0 ? I[n] : 0) +_sparse_findnextnz(v::AbstractSparseArray, i) = (I = find(!iszero, v); n = searchsortedfirst(I, i); n<=length(I) ? I[n] : 0) +_sparse_findprevnz(v::AbstractSparseArray, i) = (I = find(!iszero, v); n = searchsortedlast(I, i); n>0 ? I[n] : 0) -function findnext(v::AbstractSparseArray, i::Int) - j = _sparse_findnext(v, i) +function findnext(f::typeof(!iszero), v::AbstractSparseArray, i::Int) + j = _sparse_findnextnz(v, i) if j == 0 return 0 end - while v[j] == 0 - j = _sparse_findnext(v, j+1) + while !f(v[j]) + j = _sparse_findnextnz(v, j+1) if j == 0 return 0 end @@ -48,13 +48,13 @@ function findnext(v::AbstractSparseArray, i::Int) return j end -function findprev(v::AbstractSparseArray, i::Int) - j = _sparse_findprev(v, i) +function findprev(f::typeof(!iszero), v::AbstractSparseArray, i::Int) + j = _sparse_findprevnz(v, i) if j == 0 return 0 end - while v[j] == 0 - j = _sparse_findprev(v, j-1) + while !f(v[j]) + j = _sparse_findprevnz(v, j-1) if j == 0 return 0 end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 1405dcb841ea8..ace945eb5ff0d 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1318,7 +1318,7 @@ function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} return (I, J, V) end -function _sparse_findnext(m::SparseMatrixCSC, i::Int) +function _sparse_findnextnz(m::SparseMatrixCSC, i::Int) if i > length(m) return 0 end @@ -1336,7 +1336,7 @@ function _sparse_findnext(m::SparseMatrixCSC, i::Int) return sub2ind(m, m.rowval[nextlo], nextcol-1) end -function _sparse_findprev(m::SparseMatrixCSC, i::Int) +function _sparse_findprevnz(m::SparseMatrixCSC, i::Int) if i < 1 return 0 end diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 922b1b10e55c1..b900b696cacb5 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -737,7 +737,7 @@ function findnz(x::SparseVector{Tv,Ti}) where {Tv,Ti} return (I, V) end -function _sparse_findnext(v::SparseVector, i::Int) +function _sparse_findnextnz(v::SparseVector, i::Int) n = searchsortedfirst(v.nzind, i) if n > length(v.nzind) return 0 @@ -746,7 +746,7 @@ function _sparse_findnext(v::SparseVector, i::Int) end end -function _sparse_findprev(v::SparseVector, i::Int) +function _sparse_findprevnz(v::SparseVector, i::Int) n = searchsortedlast(v.nzind, i) if n < 1 return 0 diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 19732dc88dc83..645182a30ff5e 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -2155,8 +2155,8 @@ end x_sp = sparse(x) for i=1:length(x) - @test findnext(x,i) == findnext(x_sp,i) - @test findprev(x,i) == findprev(x_sp,i) + @test findnext(!iszero, x,i) == findnext(!iszero, x_sp,i) + @test findprev(!iszero, x,i) == findprev(!iszero, x_sp,i) end y = [0 0 0 0 0; @@ -2167,15 +2167,15 @@ end y_sp = sparse(y) for i=1:length(y) - @test findnext(y,i) == findnext(y_sp,i) - @test findprev(y,i) == findprev(y_sp,i) + @test findnext(!iszero, y,i) == findnext(!iszero, y_sp,i) + @test findprev(!iszero, y,i) == findprev(!iszero, y_sp,i) end z_sp = sparsevec(Dict(1=>1, 5=>1, 8=>0, 10=>1)) z = collect(z_sp) for i=1:length(z) - @test findnext(z,i) == findnext(z_sp,i) - @test findprev(z,i) == findprev(z_sp,i) + @test findnext(!iszero, z,i) == findnext(!iszero, z_sp,i) + @test findprev(!iszero, z,i) == findprev(!iszero, z_sp,i) end end From 132ff2758199e540735250759b3717fc09fde0fe Mon Sep 17 00:00:00 2001 From: Timo Kluck Date: Fri, 10 Nov 2017 17:42:12 +0100 Subject: [PATCH 6/8] sparse findnext()/findprev(): remove code duplication Thanks to @Sacha0 for the suggestion! --- base/sparse/abstractsparse.jl | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/base/sparse/abstractsparse.jl b/base/sparse/abstractsparse.jl index 05d65d97d9645..785d82ee4dcf5 100644 --- a/base/sparse/abstractsparse.jl +++ b/base/sparse/abstractsparse.jl @@ -36,28 +36,16 @@ _sparse_findprevnz(v::AbstractSparseArray, i) = (I = find(!iszero, v); n = searc function findnext(f::typeof(!iszero), v::AbstractSparseArray, i::Int) j = _sparse_findnextnz(v, i) - if j == 0 - return 0 - end - while !f(v[j]) + while j != 0 && !f(v[j]) j = _sparse_findnextnz(v, j+1) - if j == 0 - return 0 - end end return j end function findprev(f::typeof(!iszero), v::AbstractSparseArray, i::Int) j = _sparse_findprevnz(v, i) - if j == 0 - return 0 - end - while !f(v[j]) + while j != 0 && !f(v[j]) j = _sparse_findprevnz(v, j-1) - if j == 0 - return 0 - end end return j end From 85bc7733ff84df4f612232ccbe248d5d8bf23dff Mon Sep 17 00:00:00 2001 From: Timo Kluck Date: Tue, 14 Nov 2017 20:36:33 +0100 Subject: [PATCH 7/8] sparse findnext()/findprev(): better type stability for exotic index types This mostly means returning `zero(indtype(...))` instead of `0` in the not-found case. In addition, this commit replaces a few `== 0` checks by `iszero()` to avoid unnecessary promotions. We could similarly replace `+ 1` by `+ one(...)` but that becomes cumbersome very quickly. --- base/sparse/abstractsparse.jl | 12 ++++++------ base/sparse/sparsematrix.jl | 18 +++++++++--------- base/sparse/sparsevector.jl | 10 +++++----- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/base/sparse/abstractsparse.jl b/base/sparse/abstractsparse.jl index 785d82ee4dcf5..52a8fb614136f 100644 --- a/base/sparse/abstractsparse.jl +++ b/base/sparse/abstractsparse.jl @@ -31,20 +31,20 @@ end # The following two methods should be overloaded by concrete types to avoid # allocating the I = find(...) -_sparse_findnextnz(v::AbstractSparseArray, i) = (I = find(!iszero, v); n = searchsortedfirst(I, i); n<=length(I) ? I[n] : 0) -_sparse_findprevnz(v::AbstractSparseArray, i) = (I = find(!iszero, v); n = searchsortedlast(I, i); n>0 ? I[n] : 0) +_sparse_findnextnz(v::AbstractSparseArray, i::Integer) = (I = find(!iszero, v); n = searchsortedfirst(I, i); n<=length(I) ? I[n] : zero(indtype(v))) +_sparse_findprevnz(v::AbstractSparseArray, i::Integer) = (I = find(!iszero, v); n = searchsortedlast(I, i); !iszero(n) ? I[n] : zero(indtype(v))) -function findnext(f::typeof(!iszero), v::AbstractSparseArray, i::Int) +function findnext(f::typeof(!iszero), v::AbstractSparseArray, i::Integer) j = _sparse_findnextnz(v, i) - while j != 0 && !f(v[j]) + while !iszero(j) && !f(v[j]) j = _sparse_findnextnz(v, j+1) end return j end -function findprev(f::typeof(!iszero), v::AbstractSparseArray, i::Int) +function findprev(f::typeof(!iszero), v::AbstractSparseArray, i::Integer) j = _sparse_findprevnz(v, i) - while j != 0 && !f(v[j]) + while !iszero(j) && !f(v[j]) j = _sparse_findprevnz(v, j-1) end return j diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index ace945eb5ff0d..aa2847a8bbd27 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1318,9 +1318,9 @@ function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} return (I, J, V) end -function _sparse_findnextnz(m::SparseMatrixCSC, i::Int) +function _sparse_findnextnz(m::SparseMatrixCSC, i::Integer) if i > length(m) - return 0 + return zero(indtype(m)) end row, col = ind2sub(m, i) lo, hi = m.colptr[col], m.colptr[col+1] @@ -1329,16 +1329,16 @@ function _sparse_findnextnz(m::SparseMatrixCSC, i::Int) return sub2ind(m, m.rowval[n], col) end nextcol = findnext(c->(c>hi), m.colptr, col+1) - if nextcol == 0 - return 0 + if iszero(nextcol) + return zero(indtype(m)) end nextlo = m.colptr[nextcol-1] return sub2ind(m, m.rowval[nextlo], nextcol-1) end -function _sparse_findprevnz(m::SparseMatrixCSC, i::Int) - if i < 1 - return 0 +function _sparse_findprevnz(m::SparseMatrixCSC, i::Integer) + if iszero(i) + return zero(indtype(m)) end row, col = ind2sub(m, i) lo, hi = m.colptr[col], m.colptr[col+1] @@ -1347,8 +1347,8 @@ function _sparse_findprevnz(m::SparseMatrixCSC, i::Int) return sub2ind(m, m.rowval[n], col) end prevcol = findprev(c->(c length(v.nzind) - return 0 + return zero(indtype(v)) else return v.nzind[n] end end -function _sparse_findprevnz(v::SparseVector, i::Int) +function _sparse_findprevnz(v::SparseVector, i::Integer) n = searchsortedlast(v.nzind, i) - if n < 1 - return 0 + if iszero(n) + return zero(indtype(v)) else return v.nzind[n] end From db62ae41020085ab897998977fa75b172660bd9a Mon Sep 17 00:00:00 2001 From: Timo Kluck Date: Sat, 6 Jan 2018 13:03:07 +0100 Subject: [PATCH 8/8] Fix sparse findprev()/findnext() for sub2ind deprecation This is needed in response to #24715. --- base/sparse/sparsematrix.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 375da89b954a7..205752af48225 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1319,36 +1319,36 @@ function _sparse_findnextnz(m::SparseMatrixCSC, i::Integer) if i > length(m) return zero(indtype(m)) end - row, col = ind2sub(m, i) + row, col = Tuple(CartesianIndices(m)[i]) lo, hi = m.colptr[col], m.colptr[col+1] n = searchsortedfirst(m.rowval, row, lo, hi-1, Base.Order.Forward) if lo <= n <= hi-1 - return sub2ind(m, m.rowval[n], col) + return LinearIndices(m)[m.rowval[n], col] end nextcol = findnext(c->(c>hi), m.colptr, col+1) if iszero(nextcol) return zero(indtype(m)) end nextlo = m.colptr[nextcol-1] - return sub2ind(m, m.rowval[nextlo], nextcol-1) + return LinearIndices(m)[m.rowval[nextlo], nextcol-1] end function _sparse_findprevnz(m::SparseMatrixCSC, i::Integer) if iszero(i) return zero(indtype(m)) end - row, col = ind2sub(m, i) + row, col = Tuple(CartesianIndices(m)[i]) lo, hi = m.colptr[col], m.colptr[col+1] n = searchsortedlast(m.rowval, row, lo, hi-1, Base.Order.Forward) if lo <= n <= hi-1 - return sub2ind(m, m.rowval[n], col) + return LinearIndices(m)[m.rowval[n], col] end prevcol = findprev(c->(c