From e2ea8951543522f5276bc7e198f51e71661f5a35 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 21 Jul 2017 16:12:35 -0400 Subject: [PATCH 1/2] add `pairs`, and `keys`/`values` for arrays This allows more uniform treatment of indexed collections. --- base/abstractarray.jl | 20 ++++++++++++++----- base/associative.jl | 19 ++++++++++++++++-- base/deprecated.jl | 5 +++++ base/essentials.jl | 10 ++++++++++ base/exports.jl | 1 + base/iterators.jl | 37 ++++++++++++++++++----------------- base/number.jl | 1 + base/process.jl | 2 +- base/strings/basic.jl | 2 +- base/test.jl | 2 +- base/tuple.jl | 4 ++-- doc/src/stdlib/collections.md | 1 + test/abstractarray.jl | 9 +++++++++ test/arrayops.jl | 8 ++++---- 14 files changed, 87 insertions(+), 34 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index e939099865fae..551cebd3b90ae 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -69,9 +69,9 @@ function indices(A) map(OneTo, size(A)) end -# Performance optimization: get rid of a branch on `d` in `indices(A, -# d)` for d=1. 1d arrays are heavily used, and the first dimension -# comes up in other applications. +# Performance optimization: get rid of a branch on `d` in `indices(A, d)` +# for d=1. 1d arrays are heavily used, and the first dimension comes up +# in other applications. indices1(A::AbstractArray{<:Any,0}) = OneTo(1) indices1(A::AbstractArray) = (@_inline_meta; indices(A)[1]) indices1(iter) = OneTo(length(iter)) @@ -103,6 +103,10 @@ julia> extrema(b) """ linearindices(A) = (@_inline_meta; OneTo(_length(A))) linearindices(A::AbstractVector) = (@_inline_meta; indices1(A)) + +keys(a::AbstractArray) = CartesianRange(indices(a)) +keys(a::AbstractVector) = linearindices(a) + eltype(::Type{<:AbstractArray{E}}) where {E} = E elsize(::AbstractArray{T}) where {T} = sizeof(T) @@ -756,8 +760,11 @@ start(A::AbstractArray) = (@_inline_meta; itr = eachindex(A); (itr, start(itr))) next(A::AbstractArray, i) = (@_propagate_inbounds_meta; (idx, s) = next(i[1], i[2]); (A[idx], (i[1], s))) done(A::AbstractArray, i) = (@_propagate_inbounds_meta; done(i[1], i[2])) +# `eachindex` is mostly an optimization of `keys` +eachindex(itrs...) = keys(itrs...) + # eachindex iterates over all indices. IndexCartesian definitions are later. -eachindex(A::Union{Number,AbstractVector}) = (@_inline_meta(); indices1(A)) +eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A)) """ eachindex(A...) @@ -826,6 +833,9 @@ end isempty(a::AbstractArray) = (_length(a) == 0) +# keys with an IndexStyle +keys(s::IndexStyle, A::AbstractArray, B::AbstractArray...) = eachindex(s, A, B...) + ## Conversions ## convert(::Type{AbstractArray{T,N}}, A::AbstractArray{T,N}) where {T,N } = A @@ -1739,7 +1749,7 @@ _sub2ind_vec(i) = () function ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N M = length(ind) t = ntuple(n->similar(ind),Val(N)) - for (i,idx) in enumerate(IndexLinear(), ind) + for (i,idx) in pairs(IndexLinear(), ind) sub = ind2sub(inds, idx) for j = 1:N t[j][i] = sub[j] diff --git a/base/associative.jl b/base/associative.jl index 44ecd6faed3d5..18c4de2ec7915 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -69,11 +69,18 @@ end in(k, v::KeyIterator) = get(v.dict, k, secret_table_token) !== secret_table_token +""" + keys(iterator) + +For an iterator or collection that has keys and values (e.g. arrays and dictionaries), +return an iterator over the keys. +""" +function keys end """ keys(a::Associative) -Return an iterator over all keys in a collection. +Return an iterator over all keys in an associative collection. `collect(keys(a))` returns an array of keys. Since the keys are stored internally in a hash table, the order in which they are returned may vary. @@ -94,7 +101,6 @@ julia> collect(keys(a)) ``` """ keys(a::Associative) = KeyIterator(a) -eachindex(a::Associative) = KeyIterator(a) """ values(a::Associative) @@ -121,6 +127,15 @@ julia> collect(values(a)) """ values(a::Associative) = ValueIterator(a) +""" + pairs(collection) + +Return an iterator over `key => value` pairs for any +collection that maps a set of keys to a set of values. +This includes arrays, where the keys are the array indices. +""" +pairs(collection) = Generator(=>, keys(collection), values(collection)) + function copy(a::Associative) b = similar(a) for (k,v) in a diff --git a/base/deprecated.jl b/base/deprecated.jl index 8aaeeaa5c852f..26ed3243c4ee0 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1733,6 +1733,11 @@ end @deprecate promote_noncircular promote false +import .Iterators.enumerate + +@deprecate enumerate(i::IndexLinear, A::AbstractArray) pairs(i, A) +@deprecate enumerate(i::IndexCartesian, A::AbstractArray) pairs(i, A) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/essentials.jl b/base/essentials.jl index fcb94f19822f8..bef327f36afc6 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -676,3 +676,13 @@ false ``` """ isempty(itr) = done(itr, start(itr)) + +""" + values(iterator) + +For an iterator or collection that has keys and values, return an iterator +over the values. +This function simply returns its argument by default, since the elements +of a general iterator are normally considered its "values". +""" +values(itr) = itr diff --git a/base/exports.jl b/base/exports.jl index addd53d810f7f..608c5f46d0d8f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -688,6 +688,7 @@ export mapreducedim, merge!, merge, + pairs, #pop!, #push!, reduce, diff --git a/base/iterators.jl b/base/iterators.jl index d3671af58bb25..9a401adfb2e00 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -2,7 +2,7 @@ module Iterators -import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims +import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims, pairs using Base: tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds @@ -78,14 +78,16 @@ struct IndexValue{I,A<:AbstractArray} end """ - enumerate(IndexLinear(), A) - enumerate(IndexCartesian(), A) - enumerate(IndexStyle(A), A) + pairs(IndexLinear(), A) + pairs(IndexCartesian(), A) + pairs(IndexStyle(A), A) An iterator that accesses each element of the array `A`, returning -`(i, x)`, where `i` is the index for the element and `x = A[i]`. This -is similar to `enumerate(A)`, except `i` will always be a valid index -for `A`. +`i => x`, where `i` is the index for the element and `x = A[i]`. +Identical to `pairs(A)`, except that the style of index can be selected. +Also similar to `enumerate(A)`, except `i` will be a valid index +for `A`, while `enumerate` always counts from 1 regardless of the indices +of `A`. Specifying `IndexLinear()` ensures that `i` will be an integer; specifying `IndexCartesian()` ensures that `i` will be a @@ -96,7 +98,7 @@ been defined as the native indexing style for array `A`. ```jldoctest julia> A = ["a" "d"; "b" "e"; "c" "f"]; -julia> for (index, value) in enumerate(IndexStyle(A), A) +julia> for (index, value) in pairs(IndexStyle(A), A) println("\$index \$value") end 1 a @@ -108,7 +110,7 @@ julia> for (index, value) in enumerate(IndexStyle(A), A) julia> S = view(A, 1:2, :); -julia> for (index, value) in enumerate(IndexStyle(S), S) +julia> for (index, value) in pairs(IndexStyle(S), S) println("\$index \$value") end CartesianIndex{2}((1, 1)) a @@ -117,15 +119,14 @@ CartesianIndex{2}((1, 2)) d CartesianIndex{2}((2, 2)) e ``` -Note that `enumerate(A)` returns `i` as a *counter* (always starting -at 1), whereas `enumerate(IndexLinear(), A)` returns `i` as an *index* -(starting at the first linear index of `A`, which may or may not be -1). - See also: [`IndexStyle`](@ref), [`indices`](@ref). """ -enumerate(::IndexLinear, A::AbstractArray) = IndexValue(A, linearindices(A)) -enumerate(::IndexCartesian, A::AbstractArray) = IndexValue(A, CartesianRange(indices(A))) +pairs(::IndexLinear, A::AbstractArray) = IndexValue(A, linearindices(A)) +pairs(::IndexCartesian, A::AbstractArray) = IndexValue(A, CartesianRange(indices(A))) + +# faster than zip(keys(a), values(a)) for arrays +pairs(A::AbstractArray) = pairs(IndexCartesian(), A) +pairs(A::AbstractVector) = pairs(IndexLinear(), A) length(v::IndexValue) = length(v.itr) indices(v::IndexValue) = indices(v.itr) @@ -134,11 +135,11 @@ size(v::IndexValue) = size(v.itr) @propagate_inbounds function next(v::IndexValue, state) indx, n = next(v.itr, state) item = v.data[indx] - (indx, item), n + (indx => item), n end @inline done(v::IndexValue, state) = done(v.itr, state) -eltype(::Type{IndexValue{I,A}}) where {I,A} = Tuple{eltype(I), eltype(A)} +eltype(::Type{IndexValue{I,A}}) where {I,A} = Pair{eltype(I), eltype(A)} iteratorsize(::Type{IndexValue{I}}) where {I} = iteratorsize(I) iteratoreltype(::Type{IndexValue{I}}) where {I} = iteratoreltype(I) diff --git a/base/number.jl b/base/number.jl index 4fb47f8c54cf3..c1c22a5866423 100644 --- a/base/number.jl +++ b/base/number.jl @@ -49,6 +49,7 @@ ndims(::Type{<:Number}) = 0 length(x::Number) = 1 endof(x::Number) = 1 iteratorsize(::Type{<:Number}) = HasShape() +keys(::Number) = OneTo(1) getindex(x::Number) = x function getindex(x::Number, i::Integer) diff --git a/base/process.jl b/base/process.jl index 781e1d3ea1f94..cc188a084e675 100644 --- a/base/process.jl +++ b/base/process.jl @@ -822,7 +822,7 @@ wait(x::ProcessChain) = for p in x.processes; wait(p); end show(io::IO, p::Process) = print(io, "Process(", p.cmd, ", ", process_status(p), ")") # allow the elements of the Cmd to be accessed as an array or iterator -for f in (:length, :endof, :start, :eachindex, :eltype, :first, :last) +for f in (:length, :endof, :start, :keys, :eltype, :first, :last) @eval $f(cmd::Cmd) = $f(cmd.exec) end for f in (:next, :done, :getindex) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index e397e782c349a..408d07504dc2c 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -316,7 +316,7 @@ unsafe_chr2ind(s::AbstractString, i::Integer) = map_chr_ind(s, i, first, last) struct EachStringIndex{T<:AbstractString} s::T end -eachindex(s::AbstractString) = EachStringIndex(s) +keys(s::AbstractString) = EachStringIndex(s) length(e::EachStringIndex) = length(e.s) start(e::EachStringIndex) = start(e.s) diff --git a/base/test.jl b/base/test.jl index 19255d6d8e11a..f118d105cebbc 100644 --- a/base/test.jl +++ b/base/test.jl @@ -1415,7 +1415,7 @@ end GenericArray{T}(args...) where {T} = GenericArray(Array{T}(args...)) GenericArray{T,N}(args...) where {T,N} = GenericArray(Array{T,N}(args...)) -Base.eachindex(a::GenericArray) = eachindex(a.a) +Base.keys(a::GenericArray) = keys(a.a) Base.indices(a::GenericArray) = indices(a.a) Base.length(a::GenericArray) = length(a.a) Base.size(a::GenericArray) = size(a.a) diff --git a/base/tuple.jl b/base/tuple.jl index 961107884466f..0f4ed589fe19d 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -38,9 +38,9 @@ start(t::Tuple) = 1 done(t::Tuple, i::Int) = (length(t) < i) next(t::Tuple, i::Int) = (t[i], i+1) -eachindex(t::Tuple) = 1:length(t) +keys(t::Tuple) = 1:length(t) -function eachindex(t::Tuple, t2::Tuple...) +function keys(t::Tuple, t2::Tuple...) @_inline_meta 1:_maxlength(t, t2...) end diff --git a/doc/src/stdlib/collections.md b/doc/src/stdlib/collections.md index fd6bbf38e9171..e83303f7ab89a 100644 --- a/doc/src/stdlib/collections.md +++ b/doc/src/stdlib/collections.md @@ -196,6 +196,7 @@ Base.delete! Base.pop!(::Any, ::Any, ::Any) Base.keys Base.values +Base.pairs Base.merge Base.merge!(::Associative, ::Associative...) Base.merge!(::Function, ::Associative, ::Associative...) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 7baa72f37f378..057b115a164ff 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -835,3 +835,12 @@ end @testset "checkbounds_indices method ambiguities #20989" begin @test Base.checkbounds_indices(Bool, (1:1,), ([CartesianIndex(1)],)) end + +# keys, values, pairs +for A in (rand(2), rand(2,3)) + local A + for (i, v) in pairs(A) + @test A[i] == v + end + @test collect(values(A)) == collect(A) +end diff --git a/test/arrayops.jl b/test/arrayops.jl index 0fe17e2561213..39b914a6a0e55 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1214,10 +1214,10 @@ end @testset "eachindexvalue" begin A14 = [11 13; 12 14] R = CartesianRange(indices(A14)) - @test [a for (a,b) in enumerate(IndexLinear(), A14)] == [1,2,3,4] - @test [a for (a,b) in enumerate(IndexCartesian(), A14)] == vec(collect(R)) - @test [b for (a,b) in enumerate(IndexLinear(), A14)] == [11,12,13,14] - @test [b for (a,b) in enumerate(IndexCartesian(), A14)] == [11,12,13,14] + @test [a for (a,b) in pairs(IndexLinear(), A14)] == [1,2,3,4] + @test [a for (a,b) in pairs(IndexCartesian(), A14)] == vec(collect(R)) + @test [b for (a,b) in pairs(IndexLinear(), A14)] == [11,12,13,14] + @test [b for (a,b) in pairs(IndexCartesian(), A14)] == [11,12,13,14] end @testset "reverse" begin From ef2fd5fdb7ad0da25fc327c62f24d049015f6dea Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 21 Jul 2017 16:46:43 -0400 Subject: [PATCH 2/2] use `pairs` in `findmin` and `findmax`, supporting all indexable collections return `CartesianIndex` for n-d arrays in findmin, findmax, indmin, indmax more compact printing of `CartesianIndex` change sparse `_findr` macro to a function --- NEWS.md | 4 ++ base/array.jl | 24 +++++------ base/multidimensional.jl | 3 +- base/reducedim.jl | 24 +++++------ base/sparse/sparsematrix.jl | 79 ++++++++++++++++++------------------- test/arrayops.jl | 7 +++- test/reducedim.jl | 52 ++++++++++++------------ test/sparse/sparse.jl | 56 +++++++++++++------------- 8 files changed, 130 insertions(+), 119 deletions(-) diff --git a/NEWS.md b/NEWS.md index 97deb4df01f46..e68b1719e960a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -194,6 +194,10 @@ This section lists changes that do not have deprecation warnings. This avoids stack overflows in the common case of definitions like `f(x, y) = f(promote(x, y)...)` ([#22801]). + * `findmin`, `findmax`, `indmin`, and `indmax` used to always return linear indices. + They now return `CartesianIndex`es for all but 1-d arrays, and in general return + the `keys` of indexed collections (e.g. dictionaries) ([#22907]). + Library improvements -------------------- diff --git a/base/array.jl b/base/array.jl index 61ef1fbd63f29..9f43321ac8a38 100644 --- a/base/array.jl +++ b/base/array.jl @@ -2072,13 +2072,13 @@ function findmax(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - s = start(a) - mi = i = 1 - m, s = next(a, s) - while !done(a, s) + p = pairs(a) + s = start(p) + (mi, m), s = next(p, s) + i = mi + while !done(p, s) m != m && break - ai, s = next(a, s) - i += 1 + (i, ai), s = next(p, s) if ai != ai || isless(m, ai) m = ai mi = i @@ -2113,13 +2113,13 @@ function findmin(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - s = start(a) - mi = i = 1 - m, s = next(a, s) - while !done(a, s) + p = pairs(a) + s = start(p) + (mi, m), s = next(p, s) + i = mi + while !done(p, s) m != m && break - ai, s = next(a, s) - i += 1 + (i, ai), s = next(p, s) if ai != ai || isless(ai, m) m = ai mi = i diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 2988daf2beddc..2693333f063b7 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -4,7 +4,7 @@ module IteratorsMD import Base: eltype, length, size, start, done, next, first, last, in, getindex, setindex!, IndexStyle, min, max, zero, one, isless, eachindex, - ndims, iteratorsize, convert + ndims, iteratorsize, convert, show import Base: +, -, * import Base: simd_outer_range, simd_inner_length, simd_index @@ -80,6 +80,7 @@ module IteratorsMD @inline _flatten(i, I...) = (i, _flatten(I...)...) @inline _flatten(i::CartesianIndex, I...) = (i.I..., _flatten(I...)...) CartesianIndex(index::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = CartesianIndex(index...) + show(io::IO, i::CartesianIndex) = (print(io, "CartesianIndex"); show(io, i.I)) # length length(::CartesianIndex{N}) where {N} = N diff --git a/base/reducedim.jl b/base/reducedim.jl index 2bd52371d5529..b2bee574032e7 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -635,7 +635,9 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} # Otherwise, keep the result in Rval/Rind so that we traverse A in storage order. indsAt, indsRt = safe_tail(indices(A)), safe_tail(indices(Rval)) keep, Idefault = Broadcast.shapeindexer(indsAt, indsRt) - k = 0 + ks = keys(A) + k, kss = next(ks, start(ks)) + zi = zero(eltype(ks)) if reducedim1(Rval, A) i1 = first(indices1(Rval)) @inbounds for IA in CartesianRange(indsAt) @@ -643,12 +645,12 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} tmpRv = Rval[i1,IR] tmpRi = Rind[i1,IR] for i in indices(A,1) - k += 1 tmpAv = A[i,IA] - if tmpRi == 0 || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) tmpRv = tmpAv tmpRi = k end + k, kss = next(ks, kss) end Rval[i1,IR] = tmpRv Rind[i1,IR] = tmpRi @@ -657,14 +659,14 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} @inbounds for IA in CartesianRange(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) for i in indices(A, 1) - k += 1 tmpAv = A[i,IA] tmpRv = Rval[i,IR] tmpRi = Rind[i,IR] - if tmpRi == 0 || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) Rval[i,IR] = tmpAv Rind[i,IR] = k end + k, kss = next(ks, kss) end end end @@ -680,7 +682,7 @@ dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,0), A) + findminmax!(isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ @@ -709,10 +711,10 @@ function findmin(A::AbstractArray{T}, region) where T if prod(map(length, reduced_indices(A, region))) != 0 throw(ArgumentError("collection slices must be non-empty")) end - (similar(A, ri), similar(dims->zeros(Int, dims), ri)) + (similar(A, ri), similar(dims->zeros(eltype(keys(A)), dims), ri)) else findminmax!(isless, fill!(similar(A, ri), first(A)), - similar(dims->zeros(Int, dims), ri), A) + similar(dims->zeros(eltype(keys(A)), dims), ri), A) end end @@ -727,7 +729,7 @@ dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,0), A) + findminmax!(isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ @@ -756,10 +758,10 @@ function findmax(A::AbstractArray{T}, region) where T if prod(map(length, reduced_indices(A, region))) != 0 throw(ArgumentError("collection slices must be non-empty")) end - similar(A, ri), similar(dims->zeros(Int, dims), ri) + similar(A, ri), similar(dims->zeros(eltype(keys(A)), dims), ri) else findminmax!(isgreater, fill!(similar(A, ri), first(A)), - similar(dims->zeros(Int, dims), ri), A) + similar(dims->zeros(eltype(keys(A)), dims), ri), A) end end diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index fbf14eec0ba55..5d27cc316c31e 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1820,7 +1820,7 @@ function _findz(A::SparseMatrixCSC{Tv,Ti}, rows=1:A.m, cols=1:A.n) where {Tv,Ti} row = 0 rowmin = rows[1]; rowmax = rows[end] allrows = (rows == 1:A.m) - @inbounds for col in cols + @inbounds for col in cols r1::Int = colptr[col] r2::Int = colptr[col+1] - 1 if !allrows && (r1 <= r2) @@ -1828,93 +1828,92 @@ function _findz(A::SparseMatrixCSC{Tv,Ti}, rows=1:A.m, cols=1:A.n) where {Tv,Ti} (r1 <= r2 ) && (r2 = searchsortedlast(rowval, rowmax, r1, r2, Forward)) end row = rowmin - while (r1 <= r2) && (row == rowval[r1]) && (nzval[r1] != zval) r1 += 1 row += 1 end - (row <= rowmax) && (return sub2ind(size(A), row, col)) + (row <= rowmax) && (return CartesianIndex(row, col)) end - return 0 + return CartesianIndex(0, 0) end -macro _findr(op, A, region, Tv, Ti) - esc(quote - N = nnz($A) - L = length($A) +function _findr(op, A, region, Tv) + Ti = eltype(keys(A)) + i1 = first(keys(A)) + N = nnz(A) + L = length(A) if L == 0 - if prod(map(length, Base.reduced_indices($A, $region))) != 0 + if prod(map(length, Base.reduced_indices(A, region))) != 0 throw(ArgumentError("array slices must be non-empty")) else - ri = Base.reduced_indices0($A, $region) - return (similar($A, ri), similar(dims->zeros(Int, dims), ri)) + ri = Base.reduced_indices0(A, region) + return (similar(A, ri), similar(dims->zeros(Ti, dims), ri)) end end - colptr = $A.colptr; rowval = $A.rowval; nzval = $A.nzval; m = $A.m; n = $A.n - zval = zero($Tv) - szA = size($A) + colptr = A.colptr; rowval = A.rowval; nzval = A.nzval; m = A.m; n = A.n + zval = zero(Tv) + szA = size(A) - if $region == 1 || $region == (1,) - (N == 0) && (return (fill(zval,1,n), fill(convert($Ti,1),1,n))) - S = Vector{$Tv}(n); I = Vector{$Ti}(n) + if region == 1 || region == (1,) + (N == 0) && (return (fill(zval,1,n), fill(i1,1,n))) + S = Vector{Tv}(n); I = Vector{Ti}(n) @inbounds for i = 1 : n - Sc = zval; Ic = _findz($A, 1:m, i:i) - if Ic == 0 + Sc = zval; Ic = _findz(A, 1:m, i:i) + if Ic == CartesianIndex(0, 0) j = colptr[i] - Ic = sub2ind(szA, rowval[j], i) + Ic = CartesianIndex(rowval[j], i) Sc = nzval[j] end for j = colptr[i] : colptr[i+1]-1 - if ($op)(nzval[j], Sc) + if op(nzval[j], Sc) Sc = nzval[j] - Ic = sub2ind(szA, rowval[j], i) + Ic = CartesianIndex(rowval[j], i) end end S[i] = Sc; I[i] = Ic end return(reshape(S,1,n), reshape(I,1,n)) - elseif $region == 2 || $region == (2,) - (N == 0) && (return (fill(zval,m,1), fill(convert($Ti,1),m,1))) - S = Vector{$Tv}(m); I = Vector{$Ti}(m) + elseif region == 2 || region == (2,) + (N == 0) && (return (fill(zval,m,1), fill(i1,m,1))) + S = Vector{Tv}(m); I = Vector{Ti}(m) @inbounds for row in 1:m - S[row] = zval; I[row] = _findz($A, row:row, 1:n) - if I[row] == 0 - I[row] = sub2ind(szA, row, 1) + S[row] = zval; I[row] = _findz(A, row:row, 1:n) + if I[row] == CartesianIndex(0, 0) + I[row] = CartesianIndex(row, 1) S[row] = A[row,1] end end @inbounds for i = 1 : n, j = colptr[i] : colptr[i+1]-1 row = rowval[j] - if ($op)(nzval[j], S[row]) + if op(nzval[j], S[row]) S[row] = nzval[j] - I[row] = sub2ind(szA, row, i) + I[row] = CartesianIndex(row, i) end end return (reshape(S,m,1), reshape(I,m,1)) - elseif $region == (1,2) - (N == 0) && (return (fill(zval,1,1), fill(convert($Ti,1),1,1))) - hasz = nnz($A) != length($A) + elseif region == (1,2) + (N == 0) && (return (fill(zval,1,1), fill(i1,1,1))) + hasz = nnz(A) != length(A) Sv = hasz ? zval : nzval[1] - Iv::($Ti) = hasz ? _findz($A) : 1 - @inbounds for i = 1 : $A.n, j = colptr[i] : (colptr[i+1]-1) - if ($op)(nzval[j], Sv) + Iv::(Ti) = hasz ? _findz(A) : i1 + @inbounds for i = 1 : A.n, j = colptr[i] : (colptr[i+1]-1) + if op(nzval[j], Sv) Sv = nzval[j] - Iv = sub2ind(szA, rowval[j], i) + Iv = CartesianIndex(rowval[j], i) end end return (fill(Sv,1,1), fill(Iv,1,1)) else throw(ArgumentError("invalid value for region; must be 1, 2, or (1,2)")) end - end) #quote end _isless_fm(a, b) = b == b && ( a != a || isless(a, b) ) _isgreater_fm(a, b) = b == b && ( a != a || isless(b, a) ) -findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(_isless_fm, A, region, Tv, Ti) -findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(_isgreater_fm, A, region, Tv, Ti) +findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = _findr(_isless_fm, A, region, Tv) +findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = _findr(_isgreater_fm, A, region, Tv) findmin(A::SparseMatrixCSC) = (r=findmin(A,(1,2)); (r[1][1], r[2][1])) findmax(A::SparseMatrixCSC) = (r=findmax(A,(1,2)); (r[1][1], r[2][1])) diff --git a/test/arrayops.jl b/test/arrayops.jl index 39b914a6a0e55..3848aa14e61cc 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -503,7 +503,7 @@ end @test indmin(5:-2:1) == 3 #23094 - @test findmax(Set(["abc"])) === ("abc", 1) + @test_throws MethodError findmax(Set(["abc"])) @test findmin(["abc", "a"]) === ("a", 2) @test_throws MethodError findmax([Set([1]), Set([2])]) @test findmin([0.0, -0.0]) === (-0.0, 2) @@ -1814,6 +1814,11 @@ s, si = findmax(S) @test a == b == s @test ai == bi == si +for X in (A, B, S) + @test findmin(X) == findmin(Dict(pairs(X))) + @test findmax(X) == findmax(Dict(pairs(X))) +end + fill!(B, 2) @test all(x->x==2, B) diff --git a/test/reducedim.jl b/test/reducedim.jl index d5cf35feeb003..63cc41775f125 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -156,9 +156,9 @@ end A = [1.0 5.0 6.0; 5.0 2.0 4.0] -for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), - ((2,), reshape([1.0,2.0], 2, 1), reshape([1,4], 2, 1)), - ((1,2), fill(1.0,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0,2.0], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,2)], 2, 1)), + ((1,2), fill(1.0,1,1),fill(CartesianIndex(1,1),1,1))] @test findmin(A, tup) == (rval, rind) @test findmin!(similar(rval), similar(rind), A) == (rval, rind) @test isequal(minimum(A, tup), rval) @@ -166,9 +166,9 @@ for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [2 3 5]), - ((2,), reshape([6.0,5.0], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(6.0,1,1),fill(5,1,1))] +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0,5.0], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(6.0,1,1),fill(CartesianIndex(1,3),1,1))] @test findmax(A, tup) == (rval, rind) @test findmax!(similar(rval), similar(rind), A) == (rval, rind) @test isequal(maximum(A, tup), rval) @@ -180,9 +180,9 @@ end A = [1.0 3.0 6.0; NaN 2.0 4.0] -for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), - ((2,), reshape([1.0, NaN], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [CartesianIndex(2,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -191,9 +191,9 @@ for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), @test isequal(Base.reducedim!(min, copy(rval), A), rval) end -for (tup, rval, rind) in [((1,), [NaN 3.0 6.0], [2 3 5]), - ((2,), reshape([6.0, NaN], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 3.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) @@ -204,9 +204,9 @@ end A = [1.0 NaN 6.0; NaN 2.0 4.0] -for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(2,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -214,9 +214,9 @@ for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [2 3 5]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) @@ -226,9 +226,9 @@ end A = [Inf -Inf Inf -Inf; Inf Inf -Inf -Inf] -for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), - ((2,), reshape([-Inf -Inf], 2, 1), reshape([3,6], 2, 1)), - ((1,2), fill(-Inf,1,1),fill(3,1,1))] +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [CartesianIndex(1,1) CartesianIndex(1,2) CartesianIndex(2,3) CartesianIndex(1,4)]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,3)], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(CartesianIndex(1,2),1,1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -236,9 +236,9 @@ for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [1 4 5 7]), - ((2,), reshape([Inf Inf], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(Inf,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(1,3) CartesianIndex(1,4)]), + ((2,), reshape([Inf Inf], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(Inf,1,1),fill(CartesianIndex(1,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) @@ -281,7 +281,7 @@ for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] end A = [BigInt(10) BigInt(-10)] -for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1,1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([CartesianIndex(1,2)], 1, 1))] @test isequal(findmin(A, tup), (rval, rind)) @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(minimum(A, tup), rval) @@ -289,7 +289,7 @@ for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1,1) @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([1], 1, 1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([CartesianIndex(1,1)], 1, 1))] @test isequal(findmax(A, tup), (rval, rind)) @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) @test isequal(maximum(A, tup), rval) diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 06cc5a7b803b3..ebdd7c40c3087 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -998,8 +998,8 @@ end S = spzeros(10,8) A = Array(S) - @test indmax(S) == indmax(A) == 1 - @test indmin(S) == indmin(A) == 1 + @test indmax(S) == indmax(A) == CartesianIndex(1,1) + @test indmin(S) == indmin(A) == CartesianIndex(1,1) A = Array{Int}(0,0) S = sparse(A) @@ -1015,15 +1015,15 @@ end A = sparse([1.0 5.0 6.0; 5.0 2.0 4.0]) -for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), - ((2,), reshape([1.0,2.0], 2, 1), reshape([1,4], 2, 1)), - ((1,2), fill(1.0,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0,2.0], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,2)], 2, 1)), + ((1,2), fill(1.0,1,1),fill(CartesianIndex(1,1),1,1))] @test findmin(A, tup) == (rval, rind) end -for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [2 3 5]), - ((2,), reshape([6.0,5.0], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(6.0,1,1),fill(5,1,1))] +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0,5.0], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(6.0,1,1),fill(CartesianIndex(1,3),1,1))] @test findmax(A, tup) == (rval, rind) end @@ -1031,43 +1031,43 @@ end A = sparse([1.0 5.0 6.0; NaN 2.0 4.0]) -for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [2 4 6]), - ((2,), reshape([1.0, NaN], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [CartesianIndex(2,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((1,), [NaN 5.0 6.0], [2 3 5]), - ((2,), reshape([6.0, NaN], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) end A = sparse([1.0 NaN 6.0; NaN 2.0 4.0]) -for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [2 3 6]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(2,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [2 3 5]), - ((2,), reshape([NaN, NaN], 2, 1), reshape([3,2], 2, 1)), - ((1,2), fill(NaN,1,1),fill(2,1,1))] +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) end A = sparse([Inf -Inf Inf -Inf; Inf Inf -Inf -Inf]) -for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [1 3 6 7]), - ((2,), reshape([-Inf -Inf], 2, 1), reshape([3,6], 2, 1)), - ((1,2), fill(-Inf,1,1),fill(3,1,1))] +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [CartesianIndex(1,1) CartesianIndex(1,2) CartesianIndex(2,3) CartesianIndex(1,4)]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,3)], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(CartesianIndex(1,2),1,1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [1 4 5 7]), - ((2,), reshape([Inf Inf], 2, 1), reshape([1,2], 2, 1)), - ((1,2), fill(Inf,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(1,3) CartesianIndex(1,4)]), + ((2,), reshape([Inf Inf], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(Inf,1,1),fill(CartesianIndex(1,1),1,1))] @test isequal(findmax(A, tup), (rval, rind)) end @@ -1090,11 +1090,11 @@ for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] end A = sparse([BigInt(10) BigInt(-10)]) -for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([2], 1, 1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([CartesianIndex(1,2)], 1, 1))] @test isequal(findmin(A, tup), (rval, rind)) end -for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([1], 1, 1))] +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([CartesianIndex(1,1)], 1, 1))] @test isequal(findmax(A, tup), (rval, rind)) end