Skip to content

Commit

Permalink
Merge pull request #25532 from JuliaLang/nl/findn
Browse files Browse the repository at this point in the history
Deprecate findn(x) in favor of find(!iszero, x), which now returns cartesian indices
  • Loading branch information
JeffBezanson authored Jan 15, 2018
2 parents 60cd7cf + 06eeaa3 commit 1c68f8a
Show file tree
Hide file tree
Showing 15 changed files with 57 additions and 140 deletions.
5 changes: 3 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,8 @@ This section lists changes that do not have deprecation warnings.
trait; see its documentation for details. Types which support subtraction (operator
`-`) must now implement `widen` for hashing to work inside heterogeneous arrays.

* `findn(x::AbstractVector)` now returns a 1-tuple with the vector of indices, to be
consistent with higher order arrays ([#25365]).
* `findn(x::AbstractArray)` has been deprecated in favor of `find(!iszero, x)`, which
now returns cartesian indices for multidimensional arrays (see below, [#25532]).

* `find` now returns the same type of indices as `keys`/`pairs` for `AbstractArray`,
`AbstractDict`, `AbstractString`, `Tuple` and `NamedTuple` objects ([#24774]).
Expand Down Expand Up @@ -1191,3 +1191,4 @@ Command-line option changes
[#25231]: https://github.com/JuliaLang/julia/issues/25231
[#25365]: https://github.com/JuliaLang/julia/issues/25365
[#25424]: https://github.com/JuliaLang/julia/issues/25424
[#25532]: https://github.com/JuliaLang/julia/issues/25532
20 changes: 19 additions & 1 deletion base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1778,11 +1778,29 @@ _pairs(iter) = zip(OneTo(typemax(Int)), iter) # safe for objects that don't imp
"""
find(A)
Return a vector of the linear indices of the `true` values in `A`.
Return a vector `I` of the `true` indices or keys of `A`.
If there are no such elements of `A`, return an empty array.
To search for other kinds of values, pass a predicate as the first argument.
Indices or keys are of the same type as those returned by [`keys(A)`](@ref)
and [`pairs(A)`](@ref) for `AbstractArray`, `AbstractDict`, `AbstractString`
`Tuple` and `NamedTuple` objects, and are linear indices starting at `1`
for other iterables.
# Examples
```jldoctest
julia> A = [true, false, false, true]
4-element Array{Bool,1}:
true
false
false
true
julia> find(A)
2-element Array{Int64,1}:
1
4
julia> A = [true false; false true]
2×2 Array{Bool,2}:
true false
Expand Down
22 changes: 9 additions & 13 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1601,39 +1601,40 @@ end
function find(B::BitArray)
l = length(B)
nnzB = count(B)
I = Vector{Int}(uninitialized, nnzB)
ind = first(keys(B))
I = Vector{typeof(ind)}(uninitialized, nnzB)
nnzB == 0 && return I
Bc = B.chunks
Bcount = 1
Icount = 1
for i = 1:length(Bc)-1
u = UInt64(1)
c = Bc[i]
for j = 1:64
if c & u != 0
I[Icount] = Bcount
I[Icount] = ind
Icount += 1
end
Bcount += 1
ind = nextind(B, ind)
u <<= 1
end
end
u = UInt64(1)
c = Bc[end]
for j = 0:_mod64(l-1)
if c & u != 0
I[Icount] = Bcount
I[Icount] = ind
Icount += 1
end
Bcount += 1
ind = nextind(B, ind)
u <<= 1
end
return I
end

findn(B::BitVector) = find(B)
# For performance
find(::typeof(!iszero), B::BitArray) = find(B)

function findn(B::BitMatrix)
function findnz(B::BitMatrix)
nnzB = count(B)
I = Vector{Int}(uninitialized, nnzB)
J = Vector{Int}(uninitialized, nnzB)
Expand All @@ -1645,11 +1646,6 @@ function findn(B::BitMatrix)
cnt += 1
end
end
return I, J
end

function findnz(B::BitMatrix)
I, J = findn(B)
return I, J, trues(length(I))
end

Expand Down
3 changes: 3 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2778,6 +2778,9 @@ end

@deprecate findin(a, b) find(occursin(b), a)

@deprecate findn(a::AbstractVector) (find(!iszero, a),)
@deprecate findn(x::AbstractMatrix) (I = find(!iszero, x); (getindex.(I, 1), getindex.(I, 2)))
@deprecate findn(a::AbstractArray{T, N}) where {T, N} (I = find(!iszero, x); ntuple(i -> getindex.(I, i), N))

# END 0.7 deprecations

Expand Down
1 change: 0 additions & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,6 @@ export
findmin,
findmin!,
findmax!,
findn,
findnext,
findprev,
findnz,
Expand Down
64 changes: 0 additions & 64 deletions base/multidimensional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -687,51 +687,6 @@ end

##

# small helper function since we cannot use a closure in a generated function
_countnz(x) = x != 0

"""
findn(A)
Return one vector for each dimension containing indices giving the
locations of the non-zeros in `A` (determined by `A[i] != 0`).
# Examples
```jldoctest
julia> A = [1 2 0; 0 0 3; 0 4 0]
3×3 Array{Int64,2}:
1 2 0
0 0 3
0 4 0
julia> findn(A)
([1, 1, 3, 2], [1, 2, 2, 3])
julia> A = [0 0; 0 0]
2×2 Array{Int64,2}:
0 0
0 0
julia> findn(A)
(Int64[], Int64[])
```
"""
@generated function findn(A::AbstractArray{T,N}) where {T,N}
quote
nnzA = count(_countnz, A)
@nexprs $N d->(I_d = Vector{Int}(uninitialized, nnzA))
k = 1
@nloops $N i A begin
@inbounds if (@nref $N A i) != 0
@nexprs $N d->(I_d[k] = i_d)
k += 1
end
end
@ntuple $N I
end
end


# see discussion in #18364 ... we try not to widen type of the resulting array
# from cumsum or cumprod, but in some cases (+, Bool) we may not have a choice.
rcum_promote_type(op, ::Type{T}, ::Type{S}) where {T,S<:Number} = promote_op(op, T, S)
Expand Down Expand Up @@ -1597,25 +1552,6 @@ end
end
end

## findn

@generated function findn(B::BitArray{N}) where N
quote
nnzB = count(B)
I = ntuple(x->Vector{Int}(uninitialized, nnzB), Val($N))
if nnzB > 0
count = 1
@nloops $N i B begin
if (@nref $N B i) # TODO: should avoid bounds checking
@nexprs $N d->(I[d][count] = i_d)
count += 1
end
end
end
return I
end
end

## isassigned

@generated function isassigned(B::BitArray, I_0::Int, I::Int...)
Expand Down
1 change: 0 additions & 1 deletion doc/src/base/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ Base.circshift!
Base.circcopy!
Base.find(::Any)
Base.find(::Function, ::Any)
Base.findn
Base.findnz
Base.findfirst(::Any)
Base.findfirst(::Function, ::Any)
Expand Down
11 changes: 8 additions & 3 deletions stdlib/SparseArrays/docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,19 @@ julia> R = sparsevec(I,V)

The inverse of the [`sparse`](@ref) and [`sparsevec`](@ref) functions is
[`findnz`](@ref), which retrieves the inputs used to create the sparse array.
There is also a [`findn`](@ref) function which only returns the index vectors.
[`find(!iszero, x)`](@ref) returns the cartesian indices of non-zero entries in `x`
(including stored entries equal to zero).

```jldoctest sparse_function
julia> findnz(S)
([1, 4, 5, 3], [4, 7, 9, 18], [1, 2, 3, -5])
julia> findn(S)
([1, 4, 5, 3], [4, 7, 9, 18])
julia> find(!iszero, S)
4-element Array{CartesianIndex{2},1}:
CartesianIndex(1, 4)
CartesianIndex(4, 7)
CartesianIndex(5, 9)
CartesianIndex(3, 18)
julia> findnz(R)
([1, 3, 4, 5], [1, -5, 2, 3])
Expand Down
2 changes: 1 addition & 1 deletion stdlib/SparseArrays/src/SparseArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import Base.LinAlg: mul!, ldiv!, rdiv!
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, count, csc, cscd, csch, adjoint!, diag, diff, done, dot, eig,
exp10, exp2, findn, findprev, findnext, floor, hash, indmin, inv,
exp10, exp2, 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,
Expand Down
19 changes: 5 additions & 14 deletions stdlib/SparseArrays/src/sparsematrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1274,31 +1274,22 @@ function find(p::Function, S::SparseMatrixCSC)
if p(zero(eltype(S)))
return invoke(find, Tuple{Function, Any}, p, S)
end
sz = size(S)
I, J = _findn(p, S)
return CartesianIndex.(I, J)
end
find(p::Base.OccursIn, x::SparseMatrixCSC) =
invoke(find, Tuple{Base.OccursIn, AbstractArray}, p, x)

findn(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = _findn(x->true, S)

function _findn(p::Function, S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti}
numnz = nnz(S)
I = Vector{Ti}(uninitialized, numnz)
J = Vector{Ti}(uninitialized, numnz)
inds = Vector{CartesianIndex{2}}(uninitialized, numnz)

count = 1
@inbounds for col = 1 : S.n, k = S.colptr[col] : (S.colptr[col+1]-1)
if p(S.nzval[k])
I[count] = S.rowval[k]
J[count] = col
inds[count] = CartesianIndex(S.rowval[k], col)
count += 1
end
end

return (I, J)
return inds
end
find(p::Base.OccursIn, x::SparseMatrixCSC) =
invoke(find, Tuple{Base.OccursIn, AbstractArray}, p, x)

function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti}
numnz = nnz(S)
Expand Down
6 changes: 0 additions & 6 deletions stdlib/SparseArrays/test/sparse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1157,12 +1157,6 @@ Base.isless(x::CustomType, y::CustomType) = isless(x.x, y.x)
end
end

@testset "findn" begin
b = findn( sparse(1.0I, 4, 4) )
@test (length(b[1]) == 4)
@test (length(b[2]) == 4)
end

@testset "rotations" begin
a = sparse( [1,1,2,3], [1,3,4,1], [1,2,3,4] )

Expand Down
9 changes: 5 additions & 4 deletions test/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -436,12 +436,13 @@ function test_vector_indexing(::Type{T}, shape, ::Type{TestAbstractArray}) where

mask = bitrand(shape)
@testset "test logical indexing" begin
@test B[mask] == A[mask] == B[find(mask)] == A[find(mask)] == find(mask)
@test B[vec(mask)] == A[vec(mask)] == find(mask)
@test B[mask] == A[mask] == B[find(mask)] == A[find(mask)] == LinearIndices(mask)[find(mask)]
@test B[vec(mask)] == A[vec(mask)] == LinearIndices(mask)[find(mask)]
mask1 = bitrand(size(A, 1))
mask2 = bitrand(size(A, 2))
@test B[mask1, mask2, trailing2] == A[mask1, mask2, trailing2] == B[find(mask1), find(mask2), trailing2]
@test B[mask1, 1, trailing2] == A[mask1, 1, trailing2] == find(mask1)
@test B[mask1, mask2, trailing2] == A[mask1, mask2, trailing2] ==
B[LinearIndices(mask1)[find(mask1)], LinearIndices(mask2)[find(mask2)], trailing2]
@test B[mask1, 1, trailing2] == A[mask1, 1, trailing2] == LinearIndices(mask)[find(mask1)]
end
end
end
Expand Down
27 changes: 0 additions & 27 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -473,23 +473,6 @@ end
@test find(isascii, g) == Int[]
@test find(!iszero, (i % 2 for i in 1:10)) == 1:2:9
end
@testset "findn" begin
b = findn(fill(1,2,2,2,2))
@test (length(b[1]) == 16)
@test (length(b[2]) == 16)
@test (length(b[3]) == 16)
@test (length(b[4]) == 16)

#hand made case
a = ([2,1,2],[1,2,2],[2,2,2])
z = zeros(2,2,2)
for i = 1:3
z[a[1][i],a[2][i],a[3][i]] = 10
end
@test isequal(a,findn(z))

@test findn([1, 0, 2]) == ([1, 3], )
end

@testset "findmin findmax indmin indmax" begin
@test indmax([10,12,9,11]) == 2
Expand Down Expand Up @@ -1854,16 +1837,6 @@ end
fill!(B, 2)
@test all(x->x==2, B)

iall = repmat(1:size(A,1), 1, size(A,2))
jall = repmat((1:size(A,2))', size(A,1), 1)
i,j = findn(B)
@test vec(i) == vec(iall)
@test vec(j) == vec(jall)
fill!(S, 2)
i,j = findn(S)
@test vec(i) == vec(iall)
@test vec(j) == vec(jall)

copyto!(B, A)
copyto!(S, A)

Expand Down
2 changes: 2 additions & 0 deletions test/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,8 @@ timesofar("datamove")
end

b1 = bitrand(n1, n2)
@check_bit_operation find(b1) Vector{CartesianIndex{2}}
@check_bit_operation find(!iszero, b1) Vector{CartesianIndex{2}}
@check_bit_operation findnz(b1) Tuple{Vector{Int}, Vector{Int}, BitArray}
end

Expand Down
5 changes: 2 additions & 3 deletions test/offsetarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,8 @@ pmax, ipmax = findmax(parent(A))
@test A[iamax] == amax
@test amax == parent(A)[ipmax]
z = OffsetArray([0 0; 2 0; 0 0; 0 0], (-3,-1))
I,J = findn(z)
@test I == [-1]
@test J == [0]
I = find(!iszero, z)
@test I == [CartesianIndex(-1, 0)]
I,J,N = findnz(z)
@test I == [-1]
@test J == [0]
Expand Down

0 comments on commit 1c68f8a

Please sign in to comment.