|
1 |
| -### Indexing returns either a scalar or a smartly-subindexed AxisArray ### |
| 1 | +typealias Idx Union{Real,Colon,AbstractArray{Int}} |
2 | 2 |
|
3 |
| -# Limit indexing to types supported by SubArrays, at least initially |
4 |
| -typealias Idx Union{Colon,Int,AbstractVector{Int}} |
| 3 | +using Base: ViewIndex, linearindexing, unsafe_getindex, unsafe_setindex! |
5 | 4 |
|
6 | 5 | # Defer linearindexing to the wrapped array
|
7 |
| -import Base: linearindexing, unsafe_getindex, unsafe_setindex! |
8 | 6 | Base.linearindexing{T,N,D}(::AxisArray{T,N,D}) = linearindexing(D)
|
9 | 7 |
|
10 | 8 | # Simple scalar indexing where we just set or return scalars
|
11 |
| -Base.getindex(A::AxisArray, idxs::Int...) = A.data[idxs...] |
12 |
| -Base.setindex!(A::AxisArray, v, idxs::Int...) = (A.data[idxs...] = v) |
13 |
| - |
14 |
| -# Default to views already |
15 |
| -Base.getindex{T}(A::AxisArray{T,1}, idx::Colon) = A |
| 9 | +@inline Base.getindex(A::AxisArray, idxs::Int...) = A.data[idxs...] |
| 10 | +@inline Base.setindex!(A::AxisArray, v, idxs::Int...) = (A.data[idxs...] = v) |
16 | 11 |
|
17 | 12 | # Cartesian iteration
|
18 | 13 | Base.eachindex(A::AxisArray) = eachindex(A.data)
|
19 | 14 | Base.getindex(A::AxisArray, idx::Base.IteratorsMD.CartesianIndex) = A.data[idx]
|
20 | 15 | Base.setindex!(A::AxisArray, v, idx::Base.IteratorsMD.CartesianIndex) = (A.data[idx] = v)
|
21 | 16 |
|
22 |
| -# More complicated cases where we must create a subindexed AxisArray |
23 |
| -# TODO: do we want to be dogmatic about using views? For the data? For the axes? |
24 |
| -# TODO: perhaps it would be better to return an entirely lazy SubAxisArray view |
25 |
| -@generated function Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, idxs::Idx...) |
26 |
| - newdims = length(idxs) |
27 |
| - # If the last index is a linear indexing range that may span multiple |
28 |
| - # dimensions in the original AxisArray, we can no longer track those axes. |
29 |
| - droplastaxis = N > newdims && !(idxs[end] <: Real) ? 1 : 0 |
30 |
| - # Drop trailing scalar dimensions |
31 |
| - while newdims > 0 && idxs[newdims] <: Real |
32 |
| - newdims -= 1 |
33 |
| - end |
34 |
| - names = axisnames(A) |
35 |
| - axes = Expr(:tuple) |
36 |
| - Isplat = Expr[] |
37 |
| - reshape = false |
38 |
| - newshape = Expr[] |
39 |
| - for i = 1:newdims-droplastaxis |
40 |
| - prepaxis!(axes.args, Isplat, idxs[i], names, i) |
41 |
| - end |
42 |
| - for i = newdims-droplastaxis+1:length(idxs) |
43 |
| - push!(Isplat, :(idxs[$i])) |
44 |
| - end |
45 |
| - quote |
46 |
| - data = view(A.data, $(Isplat...)) |
47 |
| - AxisArray(data, $axes) # TODO: avoid checking the axes here |
48 |
| - end |
49 |
| -end |
50 |
| - |
51 |
| -# When we index with non-vector arrays, we *add* dimensions. This isn't |
52 |
| -# supported by SubArray currently, so we instead return a copy. |
53 |
| -# TODO: we probably shouldn't hack Base like this, but it's so convenient... |
54 |
| -if VERSION < v"0.5.0-dev" |
55 |
| - @inline Base.index_shape_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), Base.index_shape_dim(A, dim+1, I...)...) |
56 |
| - @inline Base.index_shape_dim(A, dim, i::AbstractArray, I...) = (size(i)..., Base.index_shape_dim(A, dim+1, I...)...) |
57 |
| -end |
58 |
| -@generated function Base.getindex(A::AxisArray, I::Union{Idx, AbstractArray{Int}}...) |
| 17 | +@generated function reaxis(A::AxisArray, I::Idx...) |
59 | 18 | N = length(I)
|
60 |
| - Isplat = [:(I[$d]) for d=1:N] |
61 | 19 | # Determine the new axes:
|
62 |
| - # Like above, drop linear indexing over multiple axes |
| 20 | + # Drop linear indexing over multiple axes |
63 | 21 | droplastaxis = ndims(A) > N && !(I[end] <: Real) ? 1 : 0
|
64 | 22 | # Drop trailing scalar dimensions
|
65 | 23 | lastnonscalar = N
|
|
70 | 28 | newaxes = Expr[]
|
71 | 29 | for d=1:lastnonscalar-droplastaxis
|
72 | 30 | if I[d] <: AxisArray
|
| 31 | + # Indexing with an AxisArray joins the axis names |
73 | 32 | idxnames = axisnames(I[d])
|
74 | 33 | for i=1:ndims(I[d])
|
75 | 34 | push!(newaxes, :($(Axis{Symbol(names[d], "_", idxnames[i])})(I[$d].axes[$i].val)))
|
76 | 35 | end
|
77 |
| - elseif I[d] <: Idx |
78 |
| - push!(newaxes, :($(Axis{names[d]})(A.axes[$d].val[J[$d]]))) |
| 36 | + elseif I[d] <: Real |
| 37 | + elseif I[d] <: Union{AbstractVector,Colon} |
| 38 | + push!(newaxes, :($(Axis{names[d]})(A.axes[$d].val[Base.to_index(I[$d])]))) |
79 | 39 | elseif I[d] <: AbstractArray
|
80 | 40 | for i=1:ndims(I[d])
|
| 41 | + # When we index with non-vector arrays, we *add* dimensions. |
81 | 42 | push!(newaxes, :($(Axis{Symbol(names[d], "_", i)})(indices(I[$d], $i))))
|
82 | 43 | end
|
83 | 44 | end
|
84 | 45 | end
|
85 | 46 | quote
|
86 |
| - # First copy the data using scalar indexing - an adaptation of Base |
87 |
| - checkbounds(A, I...) |
88 |
| - J = Base.to_indexes($(Isplat...)) |
89 |
| - sz = Base.index_shape(A, J...) |
90 |
| - idx_lens = Base.index_lengths(A, J...) |
91 |
| - src = A.data |
92 |
| - dest = similar(A.data, sz) |
93 |
| - D = eachindex(dest) |
94 |
| - Ds = start(D) |
95 |
| - Base.Cartesian.@nloops $N i d->(1:idx_lens[d]) d->(@inbounds j_d = J[d][i_d]) begin |
96 |
| - d, Ds = next(D, Ds) |
97 |
| - v = Base.Cartesian.@ncall $N unsafe_getindex src j |
98 |
| - unsafe_setindex!(dest, v, d) |
99 |
| - end |
100 |
| - # And now create the AxisArray: |
101 |
| - AxisArray(dest, $(newaxes...)) |
| 47 | + ($(newaxes...),) |
| 48 | + end |
| 49 | +end |
| 50 | + |
| 51 | +@inline function Base.getindex(A::AxisArray, idxs::Idx...) |
| 52 | + AxisArray(A.data[idxs...], reaxis(A, idxs...)) |
| 53 | +end |
| 54 | + |
| 55 | +# To resolve ambiguities, we need several definitions |
| 56 | +if VERSION >= v"0.6.0-dev.672" |
| 57 | + using Base.AbstractCartesianIndex |
| 58 | + Base.view(A::AxisArray, idxs::Idx...) = AxisArray(view(A.data, idxs...), reaxis(A, idxs...)) |
| 59 | +else |
| 60 | + @inline function Base.view{T,N}(A::AxisArray{T,N}, idxs::Vararg{Idx,N}) |
| 61 | + AxisArray(view(A.data, idxs...), reaxis(A, idxs...)) |
| 62 | + end |
| 63 | + function Base.view(A::AxisArray, idx::Idx) |
| 64 | + AxisArray(view(A.data, idx), reaxis(A, idx)) |
| 65 | + end |
| 66 | + @inline function Base.view{N}(A::AxisArray, idxs::Vararg{Idx,N}) |
| 67 | + # this should eventually be deleted, see julia #14770 |
| 68 | + AxisArray(view(A.data, idxs...), reaxis(A, idxs...)) |
102 | 69 | end
|
103 | 70 | end
|
104 | 71 |
|
105 | 72 | # Setindex is so much simpler. Just assign it to the data:
|
106 |
| -Base.setindex!(A::AxisArray, v, idxs::Idx...) = (A.data[idxs...] = v) |
| 73 | +@inline Base.setindex!(A::AxisArray, v, idxs::Idx...) = (A.data[idxs...] = v) |
107 | 74 |
|
108 | 75 | ### Fancier indexing capabilities provided only by AxisArrays ###
|
109 |
| -Base.getindex(A::AxisArray, idxs...) = A[to_index(A,idxs...)...] |
110 |
| -Base.setindex!(A::AxisArray, v, idxs...) = (A[to_index(A,idxs...)...] = v) |
| 76 | +@inline Base.getindex(A::AxisArray, idxs...) = A[to_index(A,idxs...)...] |
| 77 | +@inline Base.setindex!(A::AxisArray, v, idxs...) = (A[to_index(A,idxs...)...] = v) |
| 78 | +# Deal with lots of ambiguities here |
| 79 | +if VERSION >= v"0.6.0-dev.672" |
| 80 | + Base.view(A::AxisArray, idxs::ViewIndex...) = view(A, to_index(A,idxs...)...) |
| 81 | + Base.view(A::AxisArray, idxs::Union{ViewIndex,AbstractCartesianIndex}...) = view(A, to_index(A,Base.IteratorsMD.flatten(idxs)...)...) |
| 82 | + Base.view(A::AxisArray, idxs...) = view(A, to_index(A,idxs...)...) |
| 83 | +else |
| 84 | + for T in (:ViewIndex, :Any) |
| 85 | + @eval begin |
| 86 | + @inline function Base.view{T,N}(A::AxisArray{T,N}, idxs::Vararg{$T,N}) |
| 87 | + view(A, to_index(A,idxs...)...) |
| 88 | + end |
| 89 | + function Base.view(A::AxisArray, idx::$T) |
| 90 | + view(A, to_index(A,idx)...) |
| 91 | + end |
| 92 | + @inline function Base.view{N}(A::AxisArray, idsx::Vararg{$T,N}) |
| 93 | + # this should eventually be deleted, see julia #14770 |
| 94 | + view(A, to_index(A,idxs...)...) |
| 95 | + end |
| 96 | + end |
| 97 | + end |
| 98 | +end |
111 | 99 |
|
112 | 100 | # First is indexing by named axis. We simply sort the axes and re-dispatch.
|
113 | 101 | # When indexing by named axis the shapes of omitted dimensions are preserved
|
|
214 | 202 | meta = Expr(:meta, :inline)
|
215 | 203 | return :($meta; $ex)
|
216 | 204 | end
|
217 |
| - |
218 |
| -function prepaxis!{I<:Union{AbstractVector,Colon}}(axesargs, Isplat, ::Type{I}, names, i) |
219 |
| - idx = :(idxs[$i]) |
220 |
| - push!(axesargs, :($(Axis{names[i]})(A.axes[$i].val[$idx]))) |
221 |
| - push!(Isplat, :(idxs[$i])) |
222 |
| - axesargs, Isplat |
223 |
| -end |
224 |
| -function prepaxis!{I<:AxisArray}(axesargs, Isplat, ::Type{I}, names, i) |
225 |
| - idxnames = axisnames(I) |
226 |
| - push!(axesargs, :($(Axis{Symbol(names[i], "_", idxnames[1])})(idxs[$i].axes[1].val))) |
227 |
| - push!(Isplat, :(idxs[$i])) |
228 |
| - axesargs, Isplat |
229 |
| -end |
230 |
| -# For anything scalar-like |
231 |
| -if VERSION < v"0.5.0-dev" |
232 |
| - function prepaxis!{I}(axesargs, Isplat, ::Type{I}, names, i) |
233 |
| - idx = :(idxs[$i]:idxs[$i]) |
234 |
| - push!(axesargs, :($(Axis{names[i]})(A.axes[$i].val[$idx]))) |
235 |
| - push!(Isplat, idx) |
236 |
| - axesargs, Isplat |
237 |
| - end |
238 |
| -else |
239 |
| - function prepaxis!{I}(axesargs, Isplat, ::Type{I}, names, i) |
240 |
| - push!(Isplat, :(idxs[$i])) |
241 |
| - axesargs, Isplat |
242 |
| - end |
243 |
| -end |
0 commit comments