Skip to content

Commit cb50dee

Browse files
authored
Merge pull request #16378 from JuliaLang/teh/visit
Rename LinearFast etc. and define an indexing `enumerate(::IndexMethod, iter)` method
2 parents 3958491 + fb17155 commit cb50dee

29 files changed

+272
-174
lines changed

base/abstractarray.jl

Lines changed: 77 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -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

@@ -617,9 +628,9 @@ end
617628
## since a single index variable can be used.
618629

619630
copy!(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
629640
end
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)
720731
zero{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.
728739
start(A::AbstractArray) = (@_inline_meta; itr = eachindex(A); (itr, start(itr)))
729740
next(A::AbstractArray,i) = (@_propagate_inbounds_meta; (idx, s) = next(i[1], i[2]); (A[idx], (i[1], s)))
730741
done(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.
733744
eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A))
734745

735746
"""
@@ -776,18 +787,18 @@ otherwise).
776787
If the arrays have different sizes and/or dimensionalities, `eachindex` returns an
777788
iterable 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

781792
function eachindex(A::AbstractArray, B::AbstractArray)
782793
@_inline_meta
783-
eachindex(linearindexing(A,B), A, B)
794+
eachindex(IndexStyle(A,B), A, B)
784795
end
785796
function eachindex(A::AbstractArray, B::AbstractArray...)
786797
@_inline_meta
787-
eachindex(linearindexing(A,B...), A, B...)
798+
eachindex(IndexStyle(A,B...), A, B...)
788799
end
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...)
793804
end
@@ -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

846857
function 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)...)
850861
end
851862
function unsafe_getindex(A::AbstractArray, I...)
852863
@_inline_meta
853864
@inbounds r = getindex(A, I...)
854865
r
855866
end
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
886897
end
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
921932
function 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)...)
925936
end
926937
function unsafe_setindex!(A::AbstractArray, v, I...)
927938
@_inline_meta
928939
@inbounds r = setindex!(A, v, I...)
929940
r
930941
end
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
942953
end
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...))
16311642
function 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]

base/abstractarraymath.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ function cumsum_kbn{T<:AbstractFloat}(v::AbstractVector{T})
229229
end
230230

231231
# Uses K-B-N summation
232-
# TODO: Needs a separate LinearSlow method, this is only fast for LinearIndexing
232+
# TODO: Needs a separate IndexCartesian method, this is only fast for IndexLinear
233233

234234
"""
235235
cumsum_kbn(A, [dim::Integer=1])

base/bitarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ end
6969

7070
isassigned(B::BitArray, i::Int) = 1 <= i <= length(B)
7171

72-
linearindexing(::Type{<:BitArray}) = LinearFast()
72+
IndexStyle(::Type{<:BitArray}) = IndexLinear()
7373

7474
## aux functions ##
7575

base/deprecated.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ end
100100
deprecate(s::Symbol) = deprecate(current_module(), s)
101101
deprecate(m::Module, s::Symbol) = ccall(:jl_deprecate_binding, Void, (Any, Any), m, s)
102102

103-
macro deprecate_binding(old, new)
103+
macro deprecate_binding(old, new, export_old=true)
104104
Expr(:toplevel,
105-
Expr(:export, esc(old)),
105+
export_old ? Expr(:export, esc(old)) : nothing,
106106
Expr(:const, Expr(:(=), esc(old), esc(new))),
107107
Expr(:call, :deprecate, Expr(:quote, old)))
108108
end
@@ -1272,6 +1272,11 @@ for f in (:airyai, :airyaiprime, :airybi, :airybiprime, :airyaix, :airyaiprimex,
12721272
end
12731273
end
12741274

1275+
@deprecate_binding LinearIndexing IndexStyle false
1276+
@deprecate_binding LinearFast IndexLinear false
1277+
@deprecate_binding LinearSlow IndexCartesian false
1278+
@deprecate_binding linearindexing IndexStyle false
1279+
12751280
# END 0.6 deprecations
12761281

12771282
# BEGIN 1.0 deprecations

base/exports.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ export
6666
ExponentialBackOff,
6767
Factorization,
6868
FileMonitor,
69-
StepRangeLen,
7069
Hermitian,
7170
UniformScaling,
71+
IndexCartesian,
72+
IndexLinear,
73+
IndexStyle,
7274
InsertionSort,
7375
IntSet,
7476
IOBuffer,
@@ -109,6 +111,7 @@ export
109111
SharedMatrix,
110112
SharedVector,
111113
StepRange,
114+
StepRangeLen,
112115
StridedArray,
113116
StridedMatrix,
114117
StridedVecOrMat,

0 commit comments

Comments
 (0)