From 2ebc7c40ca6c667803ed47f98e428f57a5b5dfdc Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 16 Feb 2017 20:19:28 -0600 Subject: [PATCH 1/3] Extend atsign-deprecate_binding to skip export of old name --- base/deprecated.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 43fdf4db93f7e..40cec1663ba9e 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -100,9 +100,9 @@ end deprecate(s::Symbol) = deprecate(current_module(), s) deprecate(m::Module, s::Symbol) = ccall(:jl_deprecate_binding, Void, (Any, Any), m, s) -macro deprecate_binding(old, new) +macro deprecate_binding(old, new, export_old=true) Expr(:toplevel, - Expr(:export, esc(old)), + export_old ? Expr(:export, esc(old)) : nothing, Expr(:const, Expr(:(=), esc(old), esc(new))), Expr(:call, :deprecate, Expr(:quote, old))) end From 22cd6b0947ad2a0d43f1eb3bec93700553e8639c Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 16 Feb 2017 06:57:56 -0600 Subject: [PATCH 2/3] Rename LinearIndexing->IndexStyle, LinearSlow->IndexLinear, etc. --- base/abstractarray.jl | 141 ++++++++++++++++++--------------- base/abstractarraymath.jl | 2 +- base/bitarray.jl | 2 +- base/deprecated.jl | 5 ++ base/exports.jl | 5 +- base/linalg/conjarray.jl | 4 +- base/linalg/linalg.jl | 4 +- base/linalg/rowvector.jl | 4 +- base/multidimensional.jl | 32 ++++---- base/multinverses.jl | 2 +- base/reduce.jl | 8 +- base/reshapedarray.jl | 10 +-- base/sharedarray.jl | 2 +- base/sort.jl | 4 +- base/sparse/cholmod.jl | 6 +- base/sparse/sparsematrix.jl | 2 +- base/subarray.jl | 20 ++--- doc/src/devdocs/boundscheck.md | 4 +- doc/src/devdocs/subarrays.md | 4 +- doc/src/manual/arrays.md | 4 +- doc/src/manual/interfaces.md | 23 +++--- doc/src/stdlib/arrays.md | 2 +- test/TestHelpers.jl | 8 +- test/abstractarray.jl | 14 ++-- test/arrayops.jl | 22 ++--- test/offsetarray.jl | 4 +- test/perf/array/indexing.jl | 12 +-- test/subarray.jl | 6 +- 28 files changed, 187 insertions(+), 169 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index effa15fc17268..5171452b6e8f4 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -245,36 +245,47 @@ end ## Traits for array types ## -abstract type LinearIndexing end -struct LinearFast <: LinearIndexing end -struct LinearSlow <: LinearIndexing end +abstract type IndexStyle end +struct IndexLinear <: IndexStyle end +struct IndexCartesian <: IndexStyle end """ - Base.linearindexing(A) - -`linearindexing` defines how an AbstractArray most efficiently accesses its elements. If -`Base.linearindexing(A)` returns `Base.LinearFast()`, this means that linear indexing with -only one index is an efficient operation. If it instead returns `Base.LinearSlow()` (by -default), this means that the array intrinsically accesses its elements with indices -specified for every dimension. Since converting a linear index to multiple indexing -subscripts is typically very expensive, this provides a traits-based mechanism to enable -efficient generic code for all array types. - -An abstract array subtype `MyArray` that wishes to opt into fast linear indexing behaviors -should define `linearindexing` in the type-domain: - - Base.linearindexing(::Type{<:MyArray}) = Base.LinearFast() + IndexStyle(A) + IndexStyle(typeof(A)) + +`IndexStyle` specifies the "native indexing style" for array `A`. When +you define a new `AbstractArray` type, you can choose to implement +either linear indexing or cartesian indexing. If you decide to +implement linear indexing, then you must set this trait for your array +type: + + Base.IndexStyle(::Type{<:MyArray}) = IndexLinear() + +The default is `IndexCartesian()`. + +Julia's internal indexing machinery will automatically (and invisibly) +convert all indexing operations into the preferred style using +[`sub2ind`](@ref) or [`ind2sub`](@ref). This allows users to access +elements of your array using any indexing style, even when explicit +methods have not been provided. + +If you define both styles of indexing for your `AbstractArray`, this +trait can be used to select the most performant indexing style. Some +methods check this trait on their inputs, and dispatch to different +algorithms depending on the most efficient access pattern. In +particular, [`eachindex`](@ref) creates an iterator whose type depends +on the setting of this trait. """ -linearindexing(A::AbstractArray) = linearindexing(typeof(A)) -linearindexing(::Type{Union{}}) = LinearFast() -linearindexing(::Type{<:AbstractArray}) = LinearSlow() -linearindexing(::Type{<:Array}) = LinearFast() -linearindexing(::Type{<:Range}) = LinearFast() +IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) +IndexStyle(::Type{Union{}}) = IndexLinear() +IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() +IndexStyle(::Type{<:Array}) = IndexLinear() +IndexStyle(::Type{<:Range}) = IndexLinear() -linearindexing(A::AbstractArray, B::AbstractArray) = linearindexing(linearindexing(A), linearindexing(B)) -linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearindexing(A), linearindexing(B...)) -linearindexing(::LinearFast, ::LinearFast) = LinearFast() -linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow() +IndexStyle(A::AbstractArray, B::AbstractArray) = IndexStyle(IndexStyle(A), IndexStyle(B)) +IndexStyle(A::AbstractArray, B::AbstractArray...) = IndexStyle(IndexStyle(A), IndexStyle(B...)) +IndexStyle(::IndexLinear, ::IndexLinear) = IndexLinear() +IndexStyle(::IndexStyle, ::IndexStyle) = IndexCartesian() ## Bounds checking ## @@ -617,9 +628,9 @@ end ## since a single index variable can be used. copy!(dest::AbstractArray, src::AbstractArray) = - copy!(linearindexing(dest), dest, linearindexing(src), src) + copy!(IndexStyle(dest), dest, IndexStyle(src), src) -function copy!(::LinearIndexing, dest::AbstractArray, ::LinearIndexing, src::AbstractArray) +function copy!(::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractArray) destinds, srcinds = linearindices(dest), linearindices(src) isempty(srcinds) || (first(srcinds) ∈ destinds && last(srcinds) ∈ destinds) || throw(BoundsError(dest, srcinds)) @inbounds for i in srcinds @@ -628,7 +639,7 @@ function copy!(::LinearIndexing, dest::AbstractArray, ::LinearIndexing, src::Abs return dest end -function copy!(::LinearIndexing, dest::AbstractArray, ::LinearSlow, src::AbstractArray) +function copy!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::AbstractArray) destinds, srcinds = linearindices(dest), linearindices(src) isempty(srcinds) || (first(srcinds) ∈ destinds && last(srcinds) ∈ destinds) || throw(BoundsError(dest, srcinds)) i = 0 @@ -720,16 +731,16 @@ copymutable(itr) = collect(itr) zero{T}(x::AbstractArray{T}) = fill!(similar(x), zero(T)) ## iteration support for arrays by iterating over `eachindex` in the array ## -# Allows fast iteration by default for both LinearFast and LinearSlow arrays +# Allows fast iteration by default for both IndexLinear and IndexCartesian arrays -# While the definitions for LinearFast are all simple enough to inline on their -# own, LinearSlow's CartesianRange is more complicated and requires explicit +# While the definitions for IndexLinear are all simple enough to inline on their +# own, IndexCartesian's CartesianRange is more complicated and requires explicit # inlining. 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 iterates over all indices. LinearSlow definitions are later. +# eachindex iterates over all indices. IndexCartesian definitions are later. eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A)) """ @@ -776,18 +787,18 @@ otherwise). If the arrays have different sizes and/or dimensionalities, `eachindex` returns an iterable that spans the largest range along each dimension. """ -eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(linearindexing(A), A)) +eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(IndexStyle(A), A)) function eachindex(A::AbstractArray, B::AbstractArray) @_inline_meta - eachindex(linearindexing(A,B), A, B) + eachindex(IndexStyle(A,B), A, B) end function eachindex(A::AbstractArray, B::AbstractArray...) @_inline_meta - eachindex(linearindexing(A,B...), A, B...) + eachindex(IndexStyle(A,B...), A, B...) end -eachindex(::LinearFast, A::AbstractArray) = linearindices(A) -function eachindex(::LinearFast, A::AbstractArray, B::AbstractArray...) +eachindex(::IndexLinear, A::AbstractArray) = linearindices(A) +function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...) @_inline_meta 1:_maxlength(A, B...) end @@ -838,15 +849,15 @@ pointer{T}(x::AbstractArray{T}, i::Integer) = (@_inline_meta; unsafe_convert(Ptr # That dispatches to an (inlined) internal _getindex function, where the goal is # to transform the indices such that we can call the only getindex method that # we require the type A{T,N} <: AbstractArray{T,N} to define; either: -# getindex(::A, ::Int) # if linearindexing(A) == LinearFast() OR -# getindex{T,N}(::A{T,N}, ::Vararg{Int, N}) # if LinearSlow() +# getindex(::A, ::Int) # if IndexStyle(A) == IndexLinear() OR +# getindex{T,N}(::A{T,N}, ::Vararg{Int, N}) # if IndexCartesian() # If the subtype hasn't defined the required method, it falls back to the # _getindex function again where an error is thrown to prevent stack overflows. function getindex(A::AbstractArray, I...) @_propagate_inbounds_meta - error_if_canonical_indexing(linearindexing(A), A, I...) - _getindex(linearindexing(A), A, to_indices(A, I)...) + error_if_canonical_indexing(IndexStyle(A), A, I...) + _getindex(IndexStyle(A), A, to_indices(A, I)...) end function unsafe_getindex(A::AbstractArray, I...) @_inline_meta @@ -854,17 +865,17 @@ function unsafe_getindex(A::AbstractArray, I...) r end -error_if_canonical_indexing(::LinearFast, A::AbstractArray, ::Int) = error("indexing not defined for ", typeof(A)) -error_if_canonical_indexing{T,N}(::LinearSlow, A::AbstractArray{T,N}, ::Vararg{Int, N}) = error("indexing not defined for ", typeof(A)) -error_if_canonical_indexing(::LinearIndexing, ::AbstractArray, ::Any...) = nothing +error_if_canonical_indexing(::IndexLinear, A::AbstractArray, ::Int) = error("indexing not defined for ", typeof(A)) +error_if_canonical_indexing{T,N}(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int, N}) = error("indexing not defined for ", typeof(A)) +error_if_canonical_indexing(::IndexStyle, ::AbstractArray, ::Any...) = nothing ## Internal definitions -_getindex(::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") +_getindex(::IndexStyle, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") -## LinearFast Scalar indexing: canonical method is one Int -_getindex(::LinearFast, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) -_getindex(::LinearFast, A::AbstractArray) = (@_propagate_inbounds_meta; getindex(A, _to_linear_index(A))) -function _getindex(::LinearFast, A::AbstractArray, I::Int...) +## IndexLinear Scalar indexing: canonical method is one Int +_getindex(::IndexLinear, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) +_getindex(::IndexLinear, A::AbstractArray) = (@_propagate_inbounds_meta; getindex(A, _to_linear_index(A))) +function _getindex(::IndexLinear, A::AbstractArray, I::Int...) @_inline_meta @boundscheck checkbounds(A, I...) # generally _to_linear_index requires bounds checking @inbounds r = getindex(A, _to_linear_index(A, I...)) @@ -876,15 +887,15 @@ _to_linear_index{T,N}(A::AbstractArray{T,N}, I::Vararg{Int,N}) = (@_inline_meta; _to_linear_index(A::AbstractArray) = 1 # TODO: DEPRECATE FOR #14770 _to_linear_index(A::AbstractArray, I::Int...) = (@_inline_meta; sub2ind(A, I...)) # TODO: DEPRECATE FOR #14770 -## LinearSlow Scalar indexing: Canonical method is full dimensionality of Ints -_getindex(::LinearSlow, A::AbstractArray) = (@_propagate_inbounds_meta; getindex(A, _to_subscript_indices(A)...)) -function _getindex(::LinearSlow, A::AbstractArray, I::Int...) +## IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints +_getindex(::IndexCartesian, A::AbstractArray) = (@_propagate_inbounds_meta; getindex(A, _to_subscript_indices(A)...)) +function _getindex(::IndexCartesian, A::AbstractArray, I::Int...) @_inline_meta @boundscheck checkbounds(A, I...) # generally _to_subscript_indices requires bounds checking @inbounds r = getindex(A, _to_subscript_indices(A, I...)...) r end -_getindex{T,N}(::LinearSlow, A::AbstractArray{T,N}, I::Vararg{Int, N}) = (@_propagate_inbounds_meta; getindex(A, I...)) +_getindex{T,N}(::IndexCartesian, A::AbstractArray{T,N}, I::Vararg{Int, N}) = (@_propagate_inbounds_meta; getindex(A, I...)) _to_subscript_indices(A::AbstractArray, i::Int) = (@_inline_meta; _unsafe_ind2sub(A, i)) _to_subscript_indices{T,N}(A::AbstractArray{T,N}) = (@_inline_meta; fill_to_length((), 1, Val{N})) # TODO: DEPRECATE FOR #14770 _to_subscript_indices{T}(A::AbstractArray{T,0}) = () # TODO: REMOVE FOR #14770 @@ -920,8 +931,8 @@ _unsafe_ind2sub(sz, i) = (@_inline_meta; ind2sub(sz, i)) # function that allows dispatch on array storage function setindex!(A::AbstractArray, v, I...) @_propagate_inbounds_meta - error_if_canonical_indexing(linearindexing(A), A, I...) - _setindex!(linearindexing(A), A, v, to_indices(A, I)...) + error_if_canonical_indexing(IndexStyle(A), A, I...) + _setindex!(IndexStyle(A), A, v, to_indices(A, I)...) end function unsafe_setindex!(A::AbstractArray, v, I...) @_inline_meta @@ -929,22 +940,22 @@ function unsafe_setindex!(A::AbstractArray, v, I...) r end ## Internal defitions -_setindex!(::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") +_setindex!(::IndexStyle, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") -## LinearFast Scalar indexing -_setindex!(::LinearFast, A::AbstractArray, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i)) -_setindex!(::LinearFast, A::AbstractArray, v) = (@_propagate_inbounds_meta; setindex!(A, v, _to_linear_index(A))) -function _setindex!(::LinearFast, A::AbstractArray, v, I::Int...) +## IndexLinear Scalar indexing +_setindex!(::IndexLinear, A::AbstractArray, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i)) +_setindex!(::IndexLinear, A::AbstractArray, v) = (@_propagate_inbounds_meta; setindex!(A, v, _to_linear_index(A))) +function _setindex!(::IndexLinear, A::AbstractArray, v, I::Int...) @_inline_meta @boundscheck checkbounds(A, I...) @inbounds r = setindex!(A, v, _to_linear_index(A, I...)) r end -# LinearSlow Scalar indexing -_setindex!{T,N}(::LinearSlow, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) = (@_propagate_inbounds_meta; setindex!(A, v, I...)) -_setindex!(::LinearSlow, A::AbstractArray, v) = (@_propagate_inbounds_meta; setindex!(A, v, _to_subscript_indices(A)...)) -function _setindex!(::LinearSlow, A::AbstractArray, v, I::Int...) +# IndexCartesian Scalar indexing +_setindex!{T,N}(::IndexCartesian, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) = (@_propagate_inbounds_meta; setindex!(A, v, I...)) +_setindex!(::IndexCartesian, A::AbstractArray, v) = (@_propagate_inbounds_meta; setindex!(A, v, _to_subscript_indices(A)...)) +function _setindex!(::IndexCartesian, A::AbstractArray, v, I::Int...) @_inline_meta @boundscheck checkbounds(A, I...) @inbounds r = setindex!(A, v, _to_subscript_indices(A, I...)...) diff --git a/base/abstractarraymath.jl b/base/abstractarraymath.jl index 8c34f7aa36c77..1c419e907bda4 100644 --- a/base/abstractarraymath.jl +++ b/base/abstractarraymath.jl @@ -229,7 +229,7 @@ function cumsum_kbn{T<:AbstractFloat}(v::AbstractVector{T}) end # Uses K-B-N summation -# TODO: Needs a separate LinearSlow method, this is only fast for LinearIndexing +# TODO: Needs a separate IndexCartesian method, this is only fast for IndexLinear """ cumsum_kbn(A, [dim::Integer=1]) diff --git a/base/bitarray.jl b/base/bitarray.jl index 72bcc8a46778c..5f24ae708c43f 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -69,7 +69,7 @@ end isassigned(B::BitArray, i::Int) = 1 <= i <= length(B) -linearindexing(::Type{<:BitArray}) = LinearFast() +IndexStyle(::Type{<:BitArray}) = IndexLinear() ## aux functions ## diff --git a/base/deprecated.jl b/base/deprecated.jl index 40cec1663ba9e..857da376d43eb 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1272,6 +1272,11 @@ for f in (:airyai, :airyaiprime, :airybi, :airybiprime, :airyaix, :airyaiprimex, end end +@deprecate_binding LinearIndexing IndexStyle false +@deprecate_binding LinearFast IndexLinear false +@deprecate_binding LinearSlow IndexCartesian false +@deprecate_binding linearindexing IndexStyle false + # END 0.6 deprecations # BEGIN 1.0 deprecations diff --git a/base/exports.jl b/base/exports.jl index 3479a2324d444..51bfbf6632ef1 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -66,9 +66,11 @@ export ExponentialBackOff, Factorization, FileMonitor, - StepRangeLen, Hermitian, UniformScaling, + IndexCartesian, + IndexLinear, + IndexStyle, InsertionSort, IntSet, IOBuffer, @@ -109,6 +111,7 @@ export SharedMatrix, SharedVector, StepRange, + StepRangeLen, StridedArray, StridedMatrix, StridedVecOrMat, diff --git a/base/linalg/conjarray.jl b/base/linalg/conjarray.jl index 3679d5607c49a..9cad2c8da5be4 100644 --- a/base/linalg/conjarray.jl +++ b/base/linalg/conjarray.jl @@ -42,8 +42,8 @@ ConjMatrix{T, M <: AbstractMatrix} = ConjArray{T, 2, M} @inline parent_type{T,N,A}(::Type{ConjArray{T,N,A}}) = A @inline size(a::ConjArray) = size(a.parent) -linearindexing{CA <: ConjArray}(::CA) = linearindexing(parent_type(CA)) -linearindexing{CA <: ConjArray}(::Type{CA}) = linearindexing(parent_type(CA)) +IndexStyle{CA <: ConjArray}(::CA) = IndexStyle(parent_type(CA)) +IndexStyle{CA <: ConjArray}(::Type{CA}) = IndexStyle(parent_type(CA)) @propagate_inbounds getindex{T,N}(a::ConjArray{T,N}, i::Int) = conj(getindex(a.parent, i)) @propagate_inbounds getindex{T,N}(a::ConjArray{T,N}, i::Vararg{Int,N}) = conj(getindex(a.parent, i...)) diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index 51ec2ebb76a8c..1f63661d53f09 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -7,10 +7,10 @@ import Base: A_mul_Bt, At_ldiv_Bt, A_rdiv_Bc, At_ldiv_B, Ac_mul_Bc, A_mul_Bc, Ac Ac_ldiv_B, Ac_ldiv_Bc, At_mul_Bt, A_rdiv_Bt, At_mul_B import Base: USE_BLAS64, abs, big, broadcast, ceil, conj, convert, copy, copy!, ctranspose, eltype, eye, findmax, findmin, fill!, floor, full, getindex, - hcat, imag, indices, inv, isapprox, kron, length, linearindexing, map, + hcat, imag, indices, inv, isapprox, kron, length, IndexStyle, map, ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, setindex!, show, similar, size, transpose, trunc, typed_hcat -using Base: promote_op, _length, iszero, @pure, @propagate_inbounds, LinearFast, +using Base: promote_op, _length, iszero, @pure, @propagate_inbounds, IndexLinear, reduce, hvcat_fill, typed_vcat, promote_typeof # We use `_length` because of non-1 indices; releases after julia 0.5 # can go back to `length`. `_length(A)` is equivalent to `length(linearindices(A))`. diff --git a/base/linalg/rowvector.jl b/base/linalg/rowvector.jl index 9af083d1f038a..3c4ce60137e22 100644 --- a/base/linalg/rowvector.jl +++ b/base/linalg/rowvector.jl @@ -110,8 +110,8 @@ julia> conj(v) @inline size(rowvec::RowVector, d) = ifelse(d==2, length(rowvec.vec), 1) @inline indices(rowvec::RowVector) = (Base.OneTo(1), indices(rowvec.vec)[1]) @inline indices(rowvec::RowVector, d) = ifelse(d == 2, indices(rowvec.vec)[1], Base.OneTo(1)) -linearindexing(::RowVector) = LinearFast() -linearindexing(::Type{<:RowVector}) = LinearFast() +IndexStyle(::RowVector) = IndexLinear() +IndexStyle(::Type{<:RowVector}) = IndexLinear() @propagate_inbounds getindex(rowvec::RowVector, i) = transpose(rowvec.vec[i]) @propagate_inbounds setindex!(rowvec::RowVector, v, i) = setindex!(rowvec.vec, transpose(v), i) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 07eb83a273b0e..283b4d6801bc5 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -3,12 +3,12 @@ ### Multidimensional iterators module IteratorsMD import Base: eltype, length, size, start, done, next, last, in, getindex, - setindex!, linearindexing, min, max, zero, one, isless, eachindex, + setindex!, IndexStyle, min, max, zero, one, isless, eachindex, ndims, iteratorsize, convert importall ..Base.Operators import Base: simd_outer_range, simd_inner_length, simd_index - using Base: LinearFast, LinearSlow, AbstractCartesianIndex, fill_to_length, tail + using Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail export CartesianIndex, CartesianRange @@ -100,9 +100,9 @@ module IteratorsMD ndims(R::CartesianRange) = length(R.start) ndims{I<:CartesianIndex}(::Type{CartesianRange{I}}) = length(I) - eachindex(::LinearSlow, A::AbstractArray) = CartesianRange(indices(A)) + eachindex(::IndexCartesian, A::AbstractArray) = CartesianRange(indices(A)) - @inline eachindex(::LinearSlow, A::AbstractArray, B::AbstractArray...) = + @inline eachindex(::IndexCartesian, A::AbstractArray, B::AbstractArray...) = CartesianRange(maxsize((), A, B...)) maxsize(sz) = sz @inline maxsize(sz, A, B...) = maxsize(maxt(sz, size(A)), B...) @@ -363,9 +363,9 @@ end end # As an optimization, we allow trailing Array{Bool} and BitArray to be linear over trailing dimensions @inline to_indices{N}(A, inds, I::Tuple{Union{Array{Bool,N}, BitArray{N}}}) = - (_maybe_linear_logical_index(linearindexing(A), A, I[1]),) -_maybe_linear_logical_index(::LinearIndexing, A, i) = to_index(A, i) -_maybe_linear_logical_index(::LinearFast, A, i) = LogicalIndex{Int}(i) + (_maybe_linear_logical_index(IndexStyle(A), A, I[1]),) +_maybe_linear_logical_index(::IndexStyle, A, i) = to_index(A, i) +_maybe_linear_logical_index(::IndexLinear, A, i) = LogicalIndex{Int}(i) # Colons get converted to slices by `uncolon` @inline to_indices(A, inds, I::Tuple{Colon, Vararg{Any}}) = @@ -389,7 +389,7 @@ getindex(t::Tuple, i::CartesianIndex{1}) = getindex(t, i.I[1]) # These are not defined on directly on getindex to avoid # ambiguities for AbstractArray subtypes. See the note in abstractarray.jl -@generated function _getindex(l::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray}...) +@generated function _getindex(l::IndexStyle, A::AbstractArray, I::Union{Real, AbstractArray}...) N = length(I) quote @_inline_meta @@ -397,14 +397,14 @@ getindex(t::Tuple, i::CartesianIndex{1}) = getindex(t, i.I[1]) _unsafe_getindex(l, _maybe_reshape(l, A, I...), I...) end end -# But we can speed up LinearSlow arrays by reshaping them to the appropriate dimensionality: -_maybe_reshape(::LinearFast, A::AbstractArray, I...) = A -_maybe_reshape(::LinearSlow, A::AbstractVector, I...) = A -@inline _maybe_reshape(::LinearSlow, A::AbstractArray, I...) = __maybe_reshape(A, index_ndims(I...)) +# But we can speed up IndexCartesian arrays by reshaping them to the appropriate dimensionality: +_maybe_reshape(::IndexLinear, A::AbstractArray, I...) = A +_maybe_reshape(::IndexCartesian, A::AbstractVector, I...) = A +@inline _maybe_reshape(::IndexCartesian, A::AbstractArray, I...) = __maybe_reshape(A, index_ndims(I...)) @inline __maybe_reshape{T,N}(A::AbstractArray{T,N}, ::NTuple{N,Any}) = A @inline __maybe_reshape{N}(A::AbstractArray, ::NTuple{N,Any}) = reshape(A, Val{N}) -@generated function _unsafe_getindex(::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray}...) +@generated function _unsafe_getindex(::IndexStyle, A::AbstractArray, I::Union{Real, AbstractArray}...) N = length(I) quote # This is specifically not inlined to prevent exessive allocations in type unstable code @@ -435,7 +435,7 @@ end @noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))")) ## setindex! ## -@generated function _setindex!(l::LinearIndexing, A::AbstractArray, x, I::Union{Real, AbstractArray}...) +@generated function _setindex!(l::IndexStyle, A::AbstractArray, x, I::Union{Real, AbstractArray}...) N = length(I) quote @_inline_meta @@ -447,7 +447,7 @@ end _iterable(v::AbstractArray) = v _iterable(v) = Iterators.repeated(v) -@generated function _unsafe_setindex!(::LinearIndexing, A::AbstractArray, x, I::Union{Real,AbstractArray}...) +@generated function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Union{Real,AbstractArray}...) N = length(I) quote X = _iterable(x) @@ -749,7 +749,7 @@ end function copy!{T,N}(dest::AbstractArray{T,N}, src::AbstractArray{T,N}) @boundscheck checkbounds(dest, indices(src)...) - for I in eachindex(linearindexing(src,dest), src) + for I in eachindex(IndexStyle(src,dest), src) @inbounds dest[I] = src[I] end dest diff --git a/base/multinverses.jl b/base/multinverses.jl index 765d5a55f0804..39109d16bcac8 100644 --- a/base/multinverses.jl +++ b/base/multinverses.jl @@ -3,7 +3,7 @@ module MultiplicativeInverses import Base: div, divrem, rem, unsigned -using Base: LinearFast, LinearSlow, tail +using Base: IndexLinear, IndexCartesian, tail export multiplicativeinverse unsigned(::Type{Int8}) = UInt8 diff --git a/base/reduce.jl b/base/reduce.jl index eafd39bb173de..8aa76b7ac5535 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -246,9 +246,9 @@ mr_empty_iter(f, op::typeof(&), itr, ::EltypeUnknown) = true mr_empty_iter(f, op::typeof(|), itr, ::EltypeUnknown) = false mr_empty_iter(f, op, itr, ::EltypeUnknown) = _empty_reduce_error() -_mapreduce(f, op, A::AbstractArray) = _mapreduce(f, op, linearindexing(A), A) +_mapreduce(f, op, A::AbstractArray) = _mapreduce(f, op, IndexStyle(A), A) -function _mapreduce{T}(f, op, ::LinearFast, A::AbstractArray{T}) +function _mapreduce{T}(f, op, ::IndexLinear, A::AbstractArray{T}) inds = linearindices(A) n = length(inds) @inbounds begin @@ -272,9 +272,9 @@ function _mapreduce{T}(f, op, ::LinearFast, A::AbstractArray{T}) end end -_mapreduce(f, op, ::LinearSlow, A::AbstractArray) = mapfoldl(f, op, A) +_mapreduce(f, op, ::IndexCartesian, A::AbstractArray) = mapfoldl(f, op, A) -mapreduce(f, op, A::AbstractArray) = _mapreduce(f, op, linearindexing(A), A) +mapreduce(f, op, A::AbstractArray) = _mapreduce(f, op, IndexStyle(A), A) mapreduce(f, op, a::Number) = f(a) """ diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index 94e3d3f924613..58abec2e26c37 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -9,7 +9,7 @@ struct ReshapedArray{T,N,P<:AbstractArray,MI<:Tuple{Vararg{SignedMultiplicativeI end ReshapedArray{T,N}(parent::AbstractArray{T}, dims::NTuple{N,Int}, mi) = ReshapedArray{T,N,typeof(parent),typeof(mi)}(parent, dims, mi) -# LinearFast ReshapedArray +# IndexLinear ReshapedArray ReshapedArrayLF{T,N,P<:AbstractArray} = ReshapedArray{T,N,P,Tuple{}} # Fast iteration on ReshapedArrays: use the parent iterator @@ -139,14 +139,14 @@ end function _reshape(parent::AbstractArray, dims::Dims) n = _length(parent) prod(dims) == n || throw(DimensionMismatch("parent has $n elements, which is incompatible with size $dims")) - __reshape((parent, linearindexing(parent)), dims) + __reshape((parent, IndexStyle(parent)), dims) end # Reshaping a ReshapedArray _reshape(v::ReshapedArray{<:Any,1}, dims::Dims{1}) = _reshape(v.parent, dims) _reshape(R::ReshapedArray, dims::Dims) = _reshape(R.parent, dims) -function __reshape(p::Tuple{AbstractArray,LinearSlow}, dims::Dims) +function __reshape(p::Tuple{AbstractArray,IndexCartesian}, dims::Dims) parent = p[1] strds = front(size_strides(parent)) strds1 = map(s->max(1,s), strds) # for resizing empty arrays @@ -154,7 +154,7 @@ function __reshape(p::Tuple{AbstractArray,LinearSlow}, dims::Dims) ReshapedArray(parent, dims, reverse(mi)) end -function __reshape(p::Tuple{AbstractArray,LinearFast}, dims::Dims) +function __reshape(p::Tuple{AbstractArray,IndexLinear}, dims::Dims) parent = p[1] ReshapedArray(parent, dims, ()) end @@ -165,7 +165,7 @@ size_strides(out::Tuple) = out size(A::ReshapedArray) = A.dims similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims) -linearindexing(::Type{<:ReshapedArrayLF}) = LinearFast() +IndexStyle(::Type{<:ReshapedArrayLF}) = IndexLinear() parent(A::ReshapedArray) = A.parent parentindexes(A::ReshapedArray) = map(s->1:s, size(parent(A))) reinterpret{T}(::Type{T}, A::ReshapedArray, dims::Dims) = reinterpret(T, parent(A), dims) diff --git a/base/sharedarray.jl b/base/sharedarray.jl index 134445c898f41..c48eb8d790e29 100644 --- a/base/sharedarray.jl +++ b/base/sharedarray.jl @@ -256,7 +256,7 @@ SharedMatrix{T} = SharedArray{T,2} length(S::SharedArray) = prod(S.dims) size(S::SharedArray) = S.dims ndims(S::SharedArray) = length(S.dims) -linearindexing(::Type{<:SharedArray}) = LinearFast() +IndexStyle(::Type{<:SharedArray}) = IndexLinear() function reshape{T,N}(a::SharedArray{T}, dims::NTuple{N,Int}) if length(a) != prod(dims) diff --git a/base/sort.jl b/base/sort.jl index 424fd92012a98..0241693593e04 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -2,7 +2,7 @@ module Sort -using Base: Order, Checked, copymutable, linearindices, linearindexing, viewindexing, LinearFast, _length +using Base: Order, Checked, copymutable, linearindices, IndexStyle, viewindexing, IndexLinear, _length import Base.sort, @@ -774,7 +774,7 @@ end function slicetypeof{T}(A::AbstractArray{T}, i1, i2) I = map(slice_dummy, to_indices(A, (i1, i2))) - fast = isa(linearindexing(viewindexing(I), linearindexing(A)), LinearFast) + fast = isa(IndexStyle(viewindexing(I), IndexStyle(A)), IndexLinear) SubArray{T,1,typeof(A),typeof(I),fast} end slice_dummy(S::Slice) = S diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index 094c50174feee..451d200c6ad87 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -3,7 +3,7 @@ module CHOLMOD import Base: (*), convert, copy, eltype, get, getindex, show, size, - linearindexing, LinearFast, LinearSlow, ctranspose + IndexStyle, IndexLinear, IndexCartesian, ctranspose import Base.LinAlg: (\), A_mul_Bc, A_mul_Bt, Ac_ldiv_B, Ac_mul_B, At_ldiv_B, At_mul_B, cholfact, cholfact!, det, diag, ishermitian, isposdef, @@ -1225,7 +1225,7 @@ function size(F::Factor, i::Integer) end size(F::Factor) = (size(F, 1), size(F, 2)) -linearindexing(::Dense) = LinearFast() +IndexStyle(::Dense) = IndexLinear() size(FC::FactorComponent, i::Integer) = size(FC.F, i) size(FC::FactorComponent) = size(FC.F) @@ -1246,7 +1246,7 @@ function getindex(A::Dense, i::Integer) unsafe_load(s.x, i) end -linearindexing(::Sparse) = LinearSlow() +IndexStyle(::Sparse) = IndexCartesian() function getindex{T}(A::Sparse{T}, i0::Integer, i1::Integer) s = unsafe_load(get(A.p)) !(1 <= i0 <= s.nrow && 1 <= i1 <= s.ncol) && throw(BoundsError()) diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 13b8c4144d301..954d1f1c23a3a 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -1522,7 +1522,7 @@ function _mapreducezeros(f, op, T::Type, nzeros::Int, v0) v end -function Base._mapreduce{T}(f, op, ::Base.LinearSlow, A::SparseMatrixCSC{T}) +function Base._mapreduce{T}(f, op, ::Base.IndexCartesian, A::SparseMatrixCSC{T}) z = nnz(A) n = length(A) if z == 0 diff --git a/base/subarray.jl b/base/subarray.jl index ba083b330b521..536c569f11920 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -19,13 +19,13 @@ end # Compute the linear indexability of the indices, and combine it with the linear indexing of the parent function SubArray(parent::AbstractArray, indexes::Tuple) @_inline_meta - SubArray(linearindexing(viewindexing(indexes), linearindexing(parent)), parent, ensure_indexable(indexes), index_dimsum(indexes...)) + SubArray(IndexStyle(viewindexing(indexes), IndexStyle(parent)), parent, ensure_indexable(indexes), index_dimsum(indexes...)) end -function SubArray(::LinearSlow, parent::P, indexes::I, ::NTuple{N,Any}) where {P,I,N} +function SubArray(::IndexCartesian, parent::P, indexes::I, ::NTuple{N,Any}) where {P,I,N} @_inline_meta SubArray{eltype(P), N, P, I, false}(parent, indexes, 0, 0) end -function SubArray(::LinearFast, parent::P, indexes::I, ::NTuple{N,Any}) where {P,I,N} +function SubArray(::IndexLinear, parent::P, indexes::I, ::NTuple{N,Any}) where {P,I,N} @_inline_meta # Compute the stride and offset stride1 = compute_stride1(parent, indexes) @@ -38,19 +38,19 @@ check_parent_index_match{N}(parent, ::NTuple{N, Bool}) = throw(ArgumentError("number of indices ($N) must match the parent dimensionality ($(ndims(parent)))")) # This computes the linear indexing compatability for a given tuple of indices -viewindexing() = LinearFast() +viewindexing() = IndexLinear() # Leading scalar indexes simply increase the stride viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I))) # Slices may begin a section which may be followed by any number of Slices viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I))) # A UnitRange can follow Slices, but only if all other indices are scalar -viewindexing(I::Tuple{Slice, UnitRange, Vararg{ScalarIndex}}) = LinearFast() +viewindexing(I::Tuple{Slice, UnitRange, Vararg{ScalarIndex}}) = IndexLinear() # In general, ranges are only fast if all other indices are scalar -viewindexing(I::Tuple{Union{Range, Slice}, Vararg{ScalarIndex}}) = LinearFast() +viewindexing(I::Tuple{Union{Range, Slice}, Vararg{ScalarIndex}}) = IndexLinear() # All other index combinations are slow -viewindexing(I::Tuple{Vararg{Any}}) = LinearSlow() +viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() # Of course, all other array types are slow -viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = LinearSlow() +viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian() # Simple utilities size(V::SubArray) = (@_inline_meta; map(n->Int(unsafe_length(n)), indices(V))) @@ -221,8 +221,8 @@ function setindex!(V::FastContiguousSubArray, x, i::Int) V end -linearindexing(::Type{<:FastSubArray}) = LinearFast() -linearindexing(::Type{<:SubArray}) = LinearSlow() +IndexStyle(::Type{<:FastSubArray}) = IndexLinear() +IndexStyle(::Type{<:SubArray}) = IndexCartesian() # Strides are the distance between adjacent elements in a given dimension, # so they are well-defined even for non-linear memory layouts diff --git a/doc/src/devdocs/boundscheck.md b/doc/src/devdocs/boundscheck.md index 178bedc4e5a59..0647e01a722f1 100644 --- a/doc/src/devdocs/boundscheck.md +++ b/doc/src/devdocs/boundscheck.md @@ -42,8 +42,8 @@ code further up the stack. There may be certain scenarios where for code-organization reasons you want more than one layer between the `@inbounds` and `@boundscheck` declarations. For instance, the default `getindex` -methods have the chain `getindex(A::AbstractArray, i::Real)` calls `getindex(linearindexing(A), A, i)` -calls `_getindex(::LinearFast, A, i)`. +methods have the chain `getindex(A::AbstractArray, i::Real)` calls `getindex(IndexStyle(A), A, i)` +calls `_getindex(::IndexLinear, A, i)`. To override the "one layer of inlining" rule, a function may be marked with `@propagate_inbounds` to propagate an inbounds context (or out of bounds context) through one additional layer of inlining. diff --git a/doc/src/devdocs/subarrays.md b/doc/src/devdocs/subarrays.md index fafe9869e03ca..8055e72865b38 100644 --- a/doc/src/devdocs/subarrays.md +++ b/doc/src/devdocs/subarrays.md @@ -139,10 +139,10 @@ a given set of indices supports fast linear indexing with the internal `Base.vie ```julia julia> Base.viewindexing(S1.indexes) -Base.LinearSlow() +IndexCartesian() julia> Base.viewindexing(S2.indexes) -Base.LinearFast() +IndexLinear() ``` This is computed during construction of the `SubArray` and stored in the `L` type parameter as diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 1cf8d4eb4d160..017a2252817c1 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -513,11 +513,11 @@ iterate over any array type. If you write a custom `AbstractArray` type, you can specify that it has fast linear indexing using ```julia -Base.linearindexing{T<:MyArray}(::Type{T}) = LinearFast() +Base.IndexStyle{T<:MyArray}(::Type{T}) = IndexLinear() ``` This setting will cause `eachindex` iteration over a `MyArray` to use integers. If you don't -specify this trait, the default value `LinearSlow()` is used. +specify this trait, the default value `IndexCartesian()` is used. ### Vectorized Operators and Functions diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 04d4f550a5c66..f6ae319302b26 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -194,12 +194,12 @@ ourselves, we can officially define it as a subtype of an `AbstractArray`. | Methods to implement |   | Brief description | |:----------------------------------------------- |:---------------------------------------- |:------------------------------------------------------------------------------------- | | `size(A)` |   | Returns a tuple containing the dimensions of `A` | -| `getindex(A, i::Int)` |   | (if `LinearFast`) Linear scalar indexing | -| `getindex(A, I::Vararg{Int, N})` |   | (if `LinearSlow`, where `N = ndims(A)`) N-dimensional scalar indexing | -| `setindex!(A, v, i::Int)` |   | (if `LinearFast`) Scalar indexed assignment | -| `setindex!(A, v, I::Vararg{Int, N})` |   | (if `LinearSlow`, where `N = ndims(A)`) N-dimensional scalar indexed assignment | +| `getindex(A, i::Int)` |   | (if `IndexLinear`) Linear scalar indexing | +| `getindex(A, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexing | +| `setindex!(A, v, i::Int)` |   | (if `IndexLinear`) Scalar indexed assignment | +| `setindex!(A, v, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexed assignment | | **Optional methods** | **Default definition** | **Brief description** | -| `Base.linearindexing(::Type)` | `Base.LinearSlow()` | Returns either `Base.LinearFast()` or `Base.LinearSlow()`. See the description below. | +| `IndexStyle(::Type)` | `IndexCartesian()` | Returns either `IndexLinear()` or `IndexCartesian()`. See the description below. | | `getindex(A, I...)` | defined in terms of scalar `getindex()` | [Multidimensional and nonscalar indexing](@ref man-array-indexing) | | `setindex!(A, I...)` | defined in terms of scalar `setindex!()` | [Multidimensional and nonscalar indexed assignment](@ref man-array-indexing) | | `start()`/`next()`/`done()` | defined in terms of scalar `getindex()` | Iteration | @@ -217,19 +217,19 @@ If a type is defined as a subtype of `AbstractArray`, it inherits a very large s including iteration and multidimensional indexing built on top of single-element access. See the [arrays manual page](@ref man-multi-dim-arrays) and [standard library section](@ref lib-arrays) for more supported methods. -A key part in defining an `AbstractArray` subtype is [`Base.linearindexing()`](@ref). Since indexing is +A key part in defining an `AbstractArray` subtype is [`IndexStyle`](@ref). Since indexing is such an important part of an array and often occurs in hot loops, it's important to make both indexing and indexed assignment as efficient as possible. Array data structures are typically defined in one of two ways: either it most efficiently accesses its elements using just one index (linear indexing) or it intrinsically accesses the elements with indices specified for every dimension. - These two modalities are identified by Julia as `Base.LinearFast()` and `Base.LinearSlow()`. + These two modalities are identified by Julia as `IndexLinear()` and `IndexCartesian()`. Converting a linear index to multiple indexing subscripts is typically very expensive, so this provides a traits-based mechanism to enable efficient generic code for all array types. -This distinction determines which scalar indexing methods the type must define. `LinearFast()` +This distinction determines which scalar indexing methods the type must define. `IndexLinear()` arrays are simple: just define `getindex(A::ArrayType, i::Int)`. When the array is subsequently indexed with a multidimensional set of indices, the fallback `getindex(A::AbstractArray, I...)()` -efficiently converts the indices into one linear index and then calls the above method. `LinearSlow()` +efficiently converts the indices into one linear index and then calls the above method. `IndexCartesian()` arrays, on the other hand, require methods to be defined for each supported dimensionality with `ndims(A)``Int` indices. For example, the builtin `SparseMatrixCSC` type only supports two dimensions, so it just defines `getindex(A::SparseMatrixCSC, i::Int, j::Int)()`. The same holds for `setindex!()`. @@ -244,7 +244,7 @@ julia> struct SquaresVector <: AbstractArray{Int, 1} julia> Base.size(S::SquaresVector) = (S.count,) -julia> Base.linearindexing{T<:SquaresVector}(::Type{T}) = Base.LinearFast() +julia> Base.IndexStyle(::Type{<:SquaresVector}) = IndexLinear() julia> Base.getindex(S::SquaresVector, i::Int) = i*i ``` @@ -301,7 +301,7 @@ julia> Base.getindex{T,N}(A::SparseArray{T,N}, I::Vararg{Int,N}) = get(A.data, I julia> Base.setindex!{T,N}(A::SparseArray{T,N}, v, I::Vararg{Int,N}) = (A.data[I] = v) ``` -Notice that this is a `LinearSlow` array, so we must manually define [`getindex()`](@ref) and [`setindex!()`](@ref) +Notice that this is an `IndexCartesian` array, so we must manually define [`getindex()`](@ref) and [`setindex!()`](@ref) at the dimensionality of the array. Unlike the `SquaresVector`, we are able to define [`setindex!()`](@ref), and so we can mutate the array: @@ -370,4 +370,3 @@ If you are defining an array type that allows non-traditional indexing (indices something other than 1), you should specialize `indices`. You should also specialize [`similar`](@ref) so that the `dims` argument (ordinarily a `Dims` size-tuple) can accept `AbstractUnitRange` objects, perhaps range-types `Ind` of your own design. For more information, see [Arrays with custom indices](@ref). - diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 1043845c2651e..6d596f0c0d4ad 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -10,7 +10,7 @@ Base.indices(::AbstractArray, ::Any) Base.length(::AbstractArray) Base.eachindex Base.linearindices -Base.linearindexing +Base.IndexStyle Base.countnz Base.conj! Base.stride diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index 5be66c81f78ed..c7393ba0f09a8 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -53,7 +53,7 @@ end module OAs -using Base: Indices, LinearSlow, LinearFast, tail +using Base: Indices, IndexCartesian, IndexLinear, tail export OffsetArray @@ -69,7 +69,7 @@ OffsetArray{T,N}(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) = OffsetArray(A, (::Type{OffsetArray{T,N}}){T,N}(inds::Indices{N}) = OffsetArray{T,N,Array{T,N}}(Array{T,N}(map(length, inds)), map(indsoffset, inds)) (::Type{OffsetArray{T}}){T,N}(inds::Indices{N}) = OffsetArray{T,N}(inds) -Base.linearindexing{T<:OffsetArray}(::Type{T}) = Base.linearindexing(parenttype(T)) +Base.IndexStyle{T<:OffsetArray}(::Type{T}) = Base.IndexStyle(parenttype(T)) parenttype{T,N,AA}(::Type{OffsetArray{T,N,AA}}) = AA parenttype(A::OffsetArray) = parenttype(typeof(A)) @@ -78,8 +78,8 @@ Base.parent(A::OffsetArray) = A.parent errmsg(A) = error("size not supported for arrays with indices $(indices(A)); see http://docs.julialang.org/en/latest/devdocs/offset-arrays/") Base.size(A::OffsetArray) = errmsg(A) Base.size(A::OffsetArray, d) = errmsg(A) -Base.eachindex(::LinearSlow, A::OffsetArray) = CartesianRange(indices(A)) -Base.eachindex(::LinearFast, A::OffsetVector) = indices(A, 1) +Base.eachindex(::IndexCartesian, A::OffsetArray) = CartesianRange(indices(A)) +Base.eachindex(::IndexLinear, A::OffsetVector) = indices(A, 1) # Implementations of indices and indices1. Since bounds-checking is # performance-critical and relies on indices, these are usually worth diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 5bd1f95cef8bf..04a7066f7d6fe 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -191,8 +191,8 @@ Base.convert{T,N,_}(::Type{T24Linear{T }}, X::AbstractArray{_,N}) = convert(T24 Base.convert{T,N }(::Type{T24Linear{T,N}}, X::AbstractArray ) = T24Linear{T,N,size(X)}(X...) Base.size{T,N,dims}(::T24Linear{T,N,dims}) = dims -import Base: LinearFast -Base.linearindexing{A<:T24Linear}(::Type{A}) = LinearFast() +import Base: IndexLinear +Base.IndexStyle{A<:T24Linear}(::Type{A}) = IndexLinear() Base.getindex(A::T24Linear, i::Int) = getfield(A, i) Base.setindex!{T}(A::T24Linear{T}, v, i::Int) = setfield!(A, i, convert(T, v)) @@ -217,8 +217,8 @@ end Base.size(A::TSlow) = A.dims Base.similar{T}(A::TSlow, ::Type{T}, dims::Dims) = TSlow(T, dims) -import Base: LinearSlow -Base.linearindexing{A<:TSlow}(::Type{A}) = LinearSlow() +import Base: IndexCartesian +Base.IndexStyle{A<:TSlow}(::Type{A}) = IndexCartesian() # Until #11242 is merged, we need to define each dimension independently Base.getindex{T}(A::TSlow{T,0}) = get(A.data, (), zero(T)) Base.getindex{T}(A::TSlow{T,1}, i1::Int) = get(A.data, (i1,), zero(T)) @@ -451,10 +451,10 @@ function test_in_bounds(::Type{TestAbstractArray}) end mutable struct UnimplementedFastArray{T, N} <: AbstractArray{T, N} end -Base.linearindexing(::UnimplementedFastArray) = Base.LinearFast() +Base.IndexStyle(::UnimplementedFastArray) = Base.IndexLinear() mutable struct UnimplementedSlowArray{T, N} <: AbstractArray{T, N} end -Base.linearindexing(::UnimplementedSlowArray) = Base.LinearSlow() +Base.IndexStyle(::UnimplementedSlowArray) = Base.IndexCartesian() mutable struct UnimplementedArray{T, N} <: AbstractArray{T, N} end @@ -593,7 +593,7 @@ end mutable struct TSlowNIndexes{T,N} <: AbstractArray{T,N} data::Array{T,N} end -Base.linearindexing{A<:TSlowNIndexes}(::Type{A}) = Base.LinearSlow() +Base.IndexStyle{A<:TSlowNIndexes}(::Type{A}) = Base.IndexCartesian() Base.size(A::TSlowNIndexes) = size(A.data) Base.getindex(A::TSlowNIndexes, index::Int...) = error("Must use $(ndims(A)) indexes") Base.getindex{T}(A::TSlowNIndexes{T,2}, i::Int, j::Int) = A.data[i,j] diff --git a/test/arrayops.jl b/test/arrayops.jl index a35b1527dcc68..6970a12bc2e5f 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -189,7 +189,7 @@ end @test convert(Vector{Float64}, b) == b end -@testset "operations with LinearFast ReshapedArray" begin +@testset "operations with IndexLinear ReshapedArray" begin b = collect(1:12) a = Base.ReshapedArray(b, (4,3), ()) @test a[3,2] == 7 @@ -1333,11 +1333,11 @@ end @testset "linear indexing" begin a = [1:5;] - @test isa(Base.linearindexing(a), Base.LinearFast) + @test isa(Base.IndexStyle(a), Base.IndexLinear) b = view(a, :) - @test isa(Base.linearindexing(b), Base.LinearFast) - @test isa(Base.linearindexing(trues(2)), Base.LinearFast) - @test isa(Base.linearindexing(BitArray{2}), Base.LinearFast) + @test isa(Base.IndexStyle(b), Base.IndexLinear) + @test isa(Base.IndexStyle(trues(2)), Base.IndexLinear) + @test isa(Base.IndexStyle(BitArray{2}), Base.IndexLinear) aa = fill(99, 10) aa[1:2:9] = a shp = [5] @@ -1347,7 +1347,7 @@ end @test mdsum2(A) == 15 AA = reshape(aa, tuple(2, shp...)) B = view(AA, 1:1, ntuple(i->Colon(), i)...) - @test isa(Base.linearindexing(B), Base.IteratorsMD.LinearSlow) + @test isa(Base.IndexStyle(B), Base.IteratorsMD.IndexCartesian) @test mdsum(B) == 15 @test mdsum2(B) == 15 unshift!(shp, 1) @@ -1528,8 +1528,8 @@ R = CartesianRange((0,3)) R = CartesianRange((3,0)) @test done(R, start(R)) == true -@test @inferred(eachindex(Base.LinearSlow(),zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2))) == CartesianRange((3,2,2)) -@test @inferred(eachindex(Base.LinearFast(),zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2))) == 1:8 +@test @inferred(eachindex(Base.IndexCartesian(),zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2))) == CartesianRange((3,2,2)) +@test @inferred(eachindex(Base.IndexLinear(),zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2))) == 1:8 @test @inferred(eachindex(zeros(3),view(zeros(3,3),1:2,1:2),zeros(2,2,2),zeros(2,2))) == CartesianRange((3,2,2)) @test @inferred(eachindex(zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2))) == 1:8 @@ -1659,14 +1659,14 @@ B = 1.5:5.5 end ### -### LinearSlow workout +### IndexCartesian workout ### struct LinSlowMatrix{T} <: DenseArray{T,2} data::Matrix{T} end # This is the default, but just to be sure -Base.linearindexing{A<:LinSlowMatrix}(::Type{A}) = Base.LinearSlow() +Base.IndexStyle{A<:LinSlowMatrix}(::Type{A}) = Base.IndexCartesian() Base.size(A::LinSlowMatrix) = size(A.data) @@ -1755,7 +1755,7 @@ struct SquaresVector <: AbstractArray{Int, 1} count::Int end Base.size(S::SquaresVector) = (S.count,) -Base.linearindexing(::Type{SquaresVector}) = Base.LinearFast() +Base.IndexStyle(::Type{SquaresVector}) = Base.IndexLinear() Base.getindex(S::SquaresVector, i::Int) = i*i foo_squares = SquaresVector(5) @test convert(Array{Int}, foo_squares) == [1,4,9,16,25] diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 909ffe6de63b0..3d9e1355b9b27 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -15,8 +15,8 @@ h = OffsetArray([-1,1,-2,2,0], (-3,)) @test_throws ErrorException size(v, 1) A0 = [1 3; 2 4] -A = OffsetArray(A0, (-1,2)) # LinearFast -S = OffsetArray(view(A0, 1:2, 1:2), (-1,2)) # LinearSlow +A = OffsetArray(A0, (-1,2)) # IndexLinear +S = OffsetArray(view(A0, 1:2, 1:2), (-1,2)) # IndexCartesian @test indices(A) == indices(S) == (0:1, 3:4) @test_throws ErrorException size(A) @test_throws ErrorException size(A, 1) diff --git a/test/perf/array/indexing.jl b/test/perf/array/indexing.jl index 0f6af0e181919..5e5956c3b5ddb 100644 --- a/test/perf/array/indexing.jl +++ b/test/perf/array/indexing.jl @@ -106,10 +106,10 @@ end abstract type MyArray{T,N} <: AbstractArray{T,N} end -struct ArrayLS{T,N} <: MyArray{T,N} # LinearSlow +struct ArrayLS{T,N} <: MyArray{T,N} # IndexCartesian data::Array{T,N} end -struct ArrayLSLS{T,N} <: MyArray{T,N} # LinearSlow with LinearSlow similar +struct ArrayLSLS{T,N} <: MyArray{T,N} # IndexCartesian with IndexCartesian similar data::Array{T,N} end Base.similar{T}(A::ArrayLSLS, ::Type{T}, dims::Tuple{Vararg{Int}}) = ArrayLSLS(similar(A.data, T, dims)) @@ -117,7 +117,7 @@ Base.similar{T}(A::ArrayLSLS, ::Type{T}, dims::Tuple{Vararg{Int}}) = ArrayLSLS(s @inline Base.unsafe_setindex!(A::ArrayLSLS, v, I::Int...) = Base.unsafe_setindex!(A.data, v, I...) Base.first(A::ArrayLSLS) = first(A.data) -struct ArrayLF{T,N} <: MyArray{T,N} # LinearFast +struct ArrayLF{T,N} <: MyArray{T,N} # IndexLinear data::Array{T,N} end struct ArrayStrides{T,N} <: MyArray{T,N} @@ -145,10 +145,10 @@ Base.size(A::MyArray) = size(A.data) @inline Base.unsafe_getindex{T}(A::ArrayStrides{T,2}, i::Real, j::Real) = unsafe_getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) @inline Base.unsafe_getindex(A::ArrayStrides1, i::Real, j::Real) = unsafe_getindex(A.data, i + A.stride1*(j-1)) -# Using the qualified Base.LinearFast() in the linearindexing definition +# Using the qualified Base.IndexLinear() in the IndexStyle definition # requires looking up the symbol in the module on each call. -import Base: LinearFast -Base.linearindexing{T<:ArrayLF}(::Type{T}) = LinearFast() +import Base: IndexLinear +Base.IndexStyle{T<:ArrayLF}(::Type{T}) = IndexLinear() if !applicable(unsafe_getindex, [1 2], 1:1, 2) @inline Base.unsafe_getindex(A::Array, I...) = @inbounds return A[I...] diff --git a/test/subarray.jl b/test/subarray.jl index 0ff38ff999298..4c1ada86728df 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -545,13 +545,13 @@ let size=(x,y)-> error("should not happen"), Base=nothing end # issue #18034 -# ensure that it is possible to create an isbits, LinearFast view of an immutable Array +# ensure that it is possible to create an isbits, IndexLinear view of an immutable Array let struct ImmutableTestArray{T, N} <: Base.DenseArray{T, N} end Base.size(::Union{ImmutableTestArray, Type{ImmutableTestArray}}) = (0, 0) - Base.linearindexing(::Union{ImmutableTestArray, Type{ImmutableTestArray}}) = Base.LinearFast() + Base.IndexStyle(::Union{ImmutableTestArray, Type{ImmutableTestArray}}) = Base.IndexLinear() a = ImmutableTestArray{Float64, 2}() - @test Base.linearindexing(view(a, :, :)) == Base.LinearFast() + @test Base.IndexStyle(view(a, :, :)) == Base.IndexLinear() @test isbits(view(a, :, :)) end From fb17155abc6894ea86772ab4e8724a3d20e8b84a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 16 Feb 2017 11:05:18 -0600 Subject: [PATCH 3/3] Add enumerate(::IndexStyle, A) for index/value iteration `enumerate(A)` doesn't guarantee that the counter corresponds to the index; so when you need an index, call this method. --- base/abstractarray.jl | 2 +- base/iterators.jl | 75 +++++++++++++++++++++++++++++++++++++++++-- test/arrayops.jl | 9 ++++++ 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 5171452b6e8f4..7afa37a614e5f 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1642,7 +1642,7 @@ _sub2ind_vec(inds, out, i) = (@_inline_meta; sub2ind(inds, out...)) function ind2sub{N}(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) M = length(ind) t = ntuple(n->similar(ind),Val{N}) - for (i,idx) in enumerate(ind) # FIXME: change to eachindexvalue + for (i,idx) in enumerate(IndexLinear(), ind) sub = ind2sub(inds, idx) for j = 1:N t[j][i] = sub[j] diff --git a/base/iterators.jl b/base/iterators.jl index 8dbc0ec50a6da..333255e3a7e40 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -4,7 +4,7 @@ module Iterators import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims -using Base: tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo +using Base: tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds export enumerate, zip, rest, countfrom, take, drop, cycle, repeated, product, flatten, partition @@ -40,7 +40,8 @@ and `x` is the `i`th value from the given iterator. It's useful when you need not only the values `x` over which you are iterating, but also the number of iterations so far. Note that `i` may not be valid for indexing `iter`; it's also possible that `x != iter[i]`, if `iter` -has indices that do not start at 1. +has indices that do not start at 1. See the `enumerate(IndexLinear(), +iter)` method if you want to ensure that `i` is an index. ```jldoctest julia> a = ["a", "b", "c"]; @@ -69,6 +70,76 @@ eltype{I}(::Type{Enumerate{I}}) = Tuple{Int, eltype(I)} iteratorsize{I}(::Type{Enumerate{I}}) = iteratorsize(I) iteratoreltype{I}(::Type{Enumerate{I}}) = iteratoreltype(I) +struct IndexValue{I,A<:AbstractArray} + data::A + itr::I +end + +""" + enumerate(IndexLinear(), A) + enumerate(IndexCartesian(), A) + enumerate(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`. + +Specifying `IndexLinear()` ensures that `i` will be an integer; +specifying `IndexCartesian()` ensures that `i` will be a +`CartesianIndex`; specifying `IndexStyle(A)` chooses whichever has +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) + println("\$index \$value") + end +1 a +2 b +3 c +4 d +5 e +6 f + +julia> S = view(A, 1:2, :); + +julia> for (index, value) in enumerate(IndexStyle(S), S) + println("\$index \$value") + end +CartesianIndex{2}((1, 1)) a +CartesianIndex{2}((2, 1)) b +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))) + +length(v::IndexValue) = length(v.itr) +indices(v::IndexValue) = indices(v.itr) +size(v::IndexValue) = size(v.itr) +@inline start(v::IndexValue) = start(v.itr) +@propagate_inbounds function next(v::IndexValue, state) + indx, n = next(v.itr, state) + item = v.data[indx] + (indx, item), n +end +@inline done(v::IndexValue, state) = done(v.itr, state) + +eltype{I,A}(::Type{IndexValue{I,A}}) = Tuple{eltype(I), eltype(A)} + +iteratorsize{I}(::Type{IndexValue{I}}) = iteratorsize(I) +iteratoreltype{I}(::Type{IndexValue{I}}) = iteratoreltype(I) + # zip abstract type AbstractZipIterator end diff --git a/test/arrayops.jl b/test/arrayops.jl index 6970a12bc2e5f..d9fe6ffcc5f79 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1121,6 +1121,15 @@ end end 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] +end + @testset "reverse" begin @test reverse([2,3,1]) == [1,3,2] @test reverse([1:10;],1,4) == [4,3,2,1,5,6,7,8,9,10]