@@ -245,36 +245,47 @@ end
245245
246246# # Traits for array types ##
247247
248- abstract type LinearIndexing end
249- struct LinearFast <: LinearIndexing end
250- struct LinearSlow <: LinearIndexing end
248+ abstract type IndexStyle end
249+ struct IndexLinear <: IndexStyle end
250+ struct IndexCartesian <: IndexStyle end
251251
252252"""
253- Base.linearindexing(A)
254-
255- `linearindexing` defines how an AbstractArray most efficiently accesses its elements. If
256- `Base.linearindexing(A)` returns `Base.LinearFast()`, this means that linear indexing with
257- only one index is an efficient operation. If it instead returns `Base.LinearSlow()` (by
258- default), this means that the array intrinsically accesses its elements with indices
259- specified for every dimension. Since converting a linear index to multiple indexing
260- subscripts is typically very expensive, this provides a traits-based mechanism to enable
261- efficient generic code for all array types.
262-
263- An abstract array subtype `MyArray` that wishes to opt into fast linear indexing behaviors
264- should define `linearindexing` in the type-domain:
265-
266- Base.linearindexing(::Type{<:MyArray}) = Base.LinearFast()
253+ IndexStyle(A)
254+ IndexStyle(typeof(A))
255+
256+ `IndexStyle` specifies the "native indexing style" for array `A`. When
257+ you define a new `AbstractArray` type, you can choose to implement
258+ either linear indexing or cartesian indexing. If you decide to
259+ implement linear indexing, then you must set this trait for your array
260+ type:
261+
262+ Base.IndexStyle(::Type{<:MyArray}) = IndexLinear()
263+
264+ The default is `IndexCartesian()`.
265+
266+ Julia's internal indexing machinery will automatically (and invisibly)
267+ convert all indexing operations into the preferred style using
268+ [`sub2ind`](@ref) or [`ind2sub`](@ref). This allows users to access
269+ elements of your array using any indexing style, even when explicit
270+ methods have not been provided.
271+
272+ If you define both styles of indexing for your `AbstractArray`, this
273+ trait can be used to select the most performant indexing style. Some
274+ methods check this trait on their inputs, and dispatch to different
275+ algorithms depending on the most efficient access pattern. In
276+ particular, [`eachindex`](@ref) creates an iterator whose type depends
277+ on the setting of this trait.
267278"""
268- linearindexing (A:: AbstractArray ) = linearindexing (typeof (A))
269- linearindexing (:: Type{Union{}} ) = LinearFast ()
270- linearindexing (:: Type{<:AbstractArray} ) = LinearSlow ()
271- linearindexing (:: Type{<:Array} ) = LinearFast ()
272- linearindexing (:: Type{<:Range} ) = LinearFast ()
279+ IndexStyle (A:: AbstractArray ) = IndexStyle (typeof (A))
280+ IndexStyle (:: Type{Union{}} ) = IndexLinear ()
281+ IndexStyle (:: Type{<:AbstractArray} ) = IndexCartesian ()
282+ IndexStyle (:: Type{<:Array} ) = IndexLinear ()
283+ IndexStyle (:: Type{<:Range} ) = IndexLinear ()
273284
274- linearindexing (A:: AbstractArray , B:: AbstractArray ) = linearindexing ( linearindexing (A), linearindexing (B))
275- linearindexing (A:: AbstractArray , B:: AbstractArray... ) = linearindexing ( linearindexing (A), linearindexing (B... ))
276- linearindexing (:: LinearFast , :: LinearFast ) = LinearFast ()
277- linearindexing (:: LinearIndexing , :: LinearIndexing ) = LinearSlow ()
285+ IndexStyle (A:: AbstractArray , B:: AbstractArray ) = IndexStyle ( IndexStyle (A), IndexStyle (B))
286+ IndexStyle (A:: AbstractArray , B:: AbstractArray... ) = IndexStyle ( IndexStyle (A), IndexStyle (B... ))
287+ IndexStyle (:: IndexLinear , :: IndexLinear ) = IndexLinear ()
288+ IndexStyle (:: IndexStyle , :: IndexStyle ) = IndexCartesian ()
278289
279290# # Bounds checking ##
280291
617628# # since a single index variable can be used.
618629
619630copy! (dest:: AbstractArray , src:: AbstractArray ) =
620- copy! (linearindexing (dest), dest, linearindexing (src), src)
631+ copy! (IndexStyle (dest), dest, IndexStyle (src), src)
621632
622- function copy! (:: LinearIndexing , dest:: AbstractArray , :: LinearIndexing , src:: AbstractArray )
633+ function copy! (:: IndexStyle , dest:: AbstractArray , :: IndexStyle , src:: AbstractArray )
623634 destinds, srcinds = linearindices (dest), linearindices (src)
624635 isempty (srcinds) || (first (srcinds) ∈ destinds && last (srcinds) ∈ destinds) || throw (BoundsError (dest, srcinds))
625636 @inbounds for i in srcinds
@@ -628,7 +639,7 @@ function copy!(::LinearIndexing, dest::AbstractArray, ::LinearIndexing, src::Abs
628639 return dest
629640end
630641
631- function copy! (:: LinearIndexing , dest:: AbstractArray , :: LinearSlow , src:: AbstractArray )
642+ function copy! (:: IndexStyle , dest:: AbstractArray , :: IndexCartesian , src:: AbstractArray )
632643 destinds, srcinds = linearindices (dest), linearindices (src)
633644 isempty (srcinds) || (first (srcinds) ∈ destinds && last (srcinds) ∈ destinds) || throw (BoundsError (dest, srcinds))
634645 i = 0
@@ -720,16 +731,16 @@ copymutable(itr) = collect(itr)
720731zero {T} (x:: AbstractArray{T} ) = fill! (similar (x), zero (T))
721732
722733# # iteration support for arrays by iterating over `eachindex` in the array ##
723- # Allows fast iteration by default for both LinearFast and LinearSlow arrays
734+ # Allows fast iteration by default for both IndexLinear and IndexCartesian arrays
724735
725- # While the definitions for LinearFast are all simple enough to inline on their
726- # own, LinearSlow 's CartesianRange is more complicated and requires explicit
736+ # While the definitions for IndexLinear are all simple enough to inline on their
737+ # own, IndexCartesian 's CartesianRange is more complicated and requires explicit
727738# inlining.
728739start (A:: AbstractArray ) = (@_inline_meta ; itr = eachindex (A); (itr, start (itr)))
729740next (A:: AbstractArray ,i) = (@_propagate_inbounds_meta ; (idx, s) = next (i[1 ], i[2 ]); (A[idx], (i[1 ], s)))
730741done (A:: AbstractArray ,i) = (@_propagate_inbounds_meta ; done (i[1 ], i[2 ]))
731742
732- # eachindex iterates over all indices. LinearSlow definitions are later.
743+ # eachindex iterates over all indices. IndexCartesian definitions are later.
733744eachindex (A:: AbstractVector ) = (@_inline_meta (); indices1 (A))
734745
735746"""
@@ -776,18 +787,18 @@ otherwise).
776787If the arrays have different sizes and/or dimensionalities, `eachindex` returns an
777788iterable that spans the largest range along each dimension.
778789"""
779- eachindex (A:: AbstractArray ) = (@_inline_meta (); eachindex (linearindexing (A), A))
790+ eachindex (A:: AbstractArray ) = (@_inline_meta (); eachindex (IndexStyle (A), A))
780791
781792function eachindex (A:: AbstractArray , B:: AbstractArray )
782793 @_inline_meta
783- eachindex (linearindexing (A,B), A, B)
794+ eachindex (IndexStyle (A,B), A, B)
784795end
785796function eachindex (A:: AbstractArray , B:: AbstractArray... )
786797 @_inline_meta
787- eachindex (linearindexing (A,B... ), A, B... )
798+ eachindex (IndexStyle (A,B... ), A, B... )
788799end
789- eachindex (:: LinearFast , A:: AbstractArray ) = linearindices (A)
790- function eachindex (:: LinearFast , A:: AbstractArray , B:: AbstractArray... )
800+ eachindex (:: IndexLinear , A:: AbstractArray ) = linearindices (A)
801+ function eachindex (:: IndexLinear , A:: AbstractArray , B:: AbstractArray... )
791802 @_inline_meta
792803 1 : _maxlength (A, B... )
793804end
@@ -838,33 +849,33 @@ pointer{T}(x::AbstractArray{T}, i::Integer) = (@_inline_meta; unsafe_convert(Ptr
838849# That dispatches to an (inlined) internal _getindex function, where the goal is
839850# to transform the indices such that we can call the only getindex method that
840851# we require the type A{T,N} <: AbstractArray{T,N} to define; either:
841- # getindex(::A, ::Int) # if linearindexing (A) == LinearFast () OR
842- # getindex{T,N}(::A{T,N}, ::Vararg{Int, N}) # if LinearSlow ()
852+ # getindex(::A, ::Int) # if IndexStyle (A) == IndexLinear () OR
853+ # getindex{T,N}(::A{T,N}, ::Vararg{Int, N}) # if IndexCartesian ()
843854# If the subtype hasn't defined the required method, it falls back to the
844855# _getindex function again where an error is thrown to prevent stack overflows.
845856
846857function getindex (A:: AbstractArray , I... )
847858 @_propagate_inbounds_meta
848- error_if_canonical_indexing (linearindexing (A), A, I... )
849- _getindex (linearindexing (A), A, to_indices (A, I)... )
859+ error_if_canonical_indexing (IndexStyle (A), A, I... )
860+ _getindex (IndexStyle (A), A, to_indices (A, I)... )
850861end
851862function unsafe_getindex (A:: AbstractArray , I... )
852863 @_inline_meta
853864 @inbounds r = getindex (A, I... )
854865 r
855866end
856867
857- error_if_canonical_indexing (:: LinearFast , A:: AbstractArray , :: Int ) = error (" indexing not defined for " , typeof (A))
858- error_if_canonical_indexing {T,N} (:: LinearSlow , A:: AbstractArray{T,N} , :: Vararg{Int, N} ) = error (" indexing not defined for " , typeof (A))
859- error_if_canonical_indexing (:: LinearIndexing , :: AbstractArray , :: Any... ) = nothing
868+ error_if_canonical_indexing (:: IndexLinear , A:: AbstractArray , :: Int ) = error (" indexing not defined for " , typeof (A))
869+ error_if_canonical_indexing {T,N} (:: IndexCartesian , A:: AbstractArray{T,N} , :: Vararg{Int, N} ) = error (" indexing not defined for " , typeof (A))
870+ error_if_canonical_indexing (:: IndexStyle , :: AbstractArray , :: Any... ) = nothing
860871
861872# # Internal definitions
862- _getindex (:: LinearIndexing , A:: AbstractArray , I... ) = error (" indexing $(typeof (A)) with types $(typeof (I)) is not supported" )
873+ _getindex (:: IndexStyle , A:: AbstractArray , I... ) = error (" indexing $(typeof (A)) with types $(typeof (I)) is not supported" )
863874
864- # # LinearFast Scalar indexing: canonical method is one Int
865- _getindex (:: LinearFast , A:: AbstractArray , i:: Int ) = (@_propagate_inbounds_meta ; getindex (A, i))
866- _getindex (:: LinearFast , A:: AbstractArray ) = (@_propagate_inbounds_meta ; getindex (A, _to_linear_index (A)))
867- function _getindex (:: LinearFast , A:: AbstractArray , I:: Int... )
875+ # # IndexLinear Scalar indexing: canonical method is one Int
876+ _getindex (:: IndexLinear , A:: AbstractArray , i:: Int ) = (@_propagate_inbounds_meta ; getindex (A, i))
877+ _getindex (:: IndexLinear , A:: AbstractArray ) = (@_propagate_inbounds_meta ; getindex (A, _to_linear_index (A)))
878+ function _getindex (:: IndexLinear , A:: AbstractArray , I:: Int... )
868879 @_inline_meta
869880 @boundscheck checkbounds (A, I... ) # generally _to_linear_index requires bounds checking
870881 @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;
876887_to_linear_index (A:: AbstractArray ) = 1 # TODO : DEPRECATE FOR #14770
877888_to_linear_index (A:: AbstractArray , I:: Int... ) = (@_inline_meta ; sub2ind (A, I... )) # TODO : DEPRECATE FOR #14770
878889
879- # # LinearSlow Scalar indexing: Canonical method is full dimensionality of Ints
880- _getindex (:: LinearSlow , A:: AbstractArray ) = (@_propagate_inbounds_meta ; getindex (A, _to_subscript_indices (A)... ))
881- function _getindex (:: LinearSlow , A:: AbstractArray , I:: Int... )
890+ # # IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints
891+ _getindex (:: IndexCartesian , A:: AbstractArray ) = (@_propagate_inbounds_meta ; getindex (A, _to_subscript_indices (A)... ))
892+ function _getindex (:: IndexCartesian , A:: AbstractArray , I:: Int... )
882893 @_inline_meta
883894 @boundscheck checkbounds (A, I... ) # generally _to_subscript_indices requires bounds checking
884895 @inbounds r = getindex (A, _to_subscript_indices (A, I... )... )
885896 r
886897end
887- _getindex {T,N} (:: LinearSlow , A:: AbstractArray{T,N} , I:: Vararg{Int, N} ) = (@_propagate_inbounds_meta ; getindex (A, I... ))
898+ _getindex {T,N} (:: IndexCartesian , A:: AbstractArray{T,N} , I:: Vararg{Int, N} ) = (@_propagate_inbounds_meta ; getindex (A, I... ))
888899_to_subscript_indices (A:: AbstractArray , i:: Int ) = (@_inline_meta ; _unsafe_ind2sub (A, i))
889900_to_subscript_indices {T,N} (A:: AbstractArray{T,N} ) = (@_inline_meta ; fill_to_length ((), 1 , Val{N})) # TODO : DEPRECATE FOR #14770
890901_to_subscript_indices {T} (A:: AbstractArray{T,0} ) = () # TODO : REMOVE FOR #14770
@@ -920,31 +931,31 @@ _unsafe_ind2sub(sz, i) = (@_inline_meta; ind2sub(sz, i))
920931# function that allows dispatch on array storage
921932function setindex! (A:: AbstractArray , v, I... )
922933 @_propagate_inbounds_meta
923- error_if_canonical_indexing (linearindexing (A), A, I... )
924- _setindex! (linearindexing (A), A, v, to_indices (A, I)... )
934+ error_if_canonical_indexing (IndexStyle (A), A, I... )
935+ _setindex! (IndexStyle (A), A, v, to_indices (A, I)... )
925936end
926937function unsafe_setindex! (A:: AbstractArray , v, I... )
927938 @_inline_meta
928939 @inbounds r = setindex! (A, v, I... )
929940 r
930941end
931942# # Internal defitions
932- _setindex! (:: LinearIndexing , A:: AbstractArray , v, I... ) = error (" indexing $(typeof (A)) with types $(typeof (I)) is not supported" )
943+ _setindex! (:: IndexStyle , A:: AbstractArray , v, I... ) = error (" indexing $(typeof (A)) with types $(typeof (I)) is not supported" )
933944
934- # # LinearFast Scalar indexing
935- _setindex! (:: LinearFast , A:: AbstractArray , v, i:: Int ) = (@_propagate_inbounds_meta ; setindex! (A, v, i))
936- _setindex! (:: LinearFast , A:: AbstractArray , v) = (@_propagate_inbounds_meta ; setindex! (A, v, _to_linear_index (A)))
937- function _setindex! (:: LinearFast , A:: AbstractArray , v, I:: Int... )
945+ # # IndexLinear Scalar indexing
946+ _setindex! (:: IndexLinear , A:: AbstractArray , v, i:: Int ) = (@_propagate_inbounds_meta ; setindex! (A, v, i))
947+ _setindex! (:: IndexLinear , A:: AbstractArray , v) = (@_propagate_inbounds_meta ; setindex! (A, v, _to_linear_index (A)))
948+ function _setindex! (:: IndexLinear , A:: AbstractArray , v, I:: Int... )
938949 @_inline_meta
939950 @boundscheck checkbounds (A, I... )
940951 @inbounds r = setindex! (A, v, _to_linear_index (A, I... ))
941952 r
942953end
943954
944- # LinearSlow Scalar indexing
945- _setindex! {T,N} (:: LinearSlow , A:: AbstractArray{T,N} , v, I:: Vararg{Int, N} ) = (@_propagate_inbounds_meta ; setindex! (A, v, I... ))
946- _setindex! (:: LinearSlow , A:: AbstractArray , v) = (@_propagate_inbounds_meta ; setindex! (A, v, _to_subscript_indices (A)... ))
947- function _setindex! (:: LinearSlow , A:: AbstractArray , v, I:: Int... )
955+ # IndexCartesian Scalar indexing
956+ _setindex! {T,N} (:: IndexCartesian , A:: AbstractArray{T,N} , v, I:: Vararg{Int, N} ) = (@_propagate_inbounds_meta ; setindex! (A, v, I... ))
957+ _setindex! (:: IndexCartesian , A:: AbstractArray , v) = (@_propagate_inbounds_meta ; setindex! (A, v, _to_subscript_indices (A)... ))
958+ function _setindex! (:: IndexCartesian , A:: AbstractArray , v, I:: Int... )
948959 @_inline_meta
949960 @boundscheck checkbounds (A, I... )
950961 @inbounds r = setindex! (A, v, _to_subscript_indices (A, I... )... )
@@ -1631,7 +1642,7 @@ _sub2ind_vec(inds, out, i) = (@_inline_meta; sub2ind(inds, out...))
16311642function ind2sub {N} (inds:: Union{DimsInteger{N},Indices{N}} , ind:: AbstractVector{<:Integer} )
16321643 M = length (ind)
16331644 t = ntuple (n-> similar (ind),Val{N})
1634- for (i,idx) in enumerate (ind) # FIXME : change to eachindexvalue
1645+ for (i,idx) in enumerate (IndexLinear (), ind)
16351646 sub = ind2sub (inds, idx)
16361647 for j = 1 : N
16371648 t[j][i] = sub[j]
0 commit comments