10
10
export OffsetArray, OffsetMatrix, OffsetVector
11
11
12
12
include (" axes.jl" )
13
+ include (" utils.jl" )
13
14
14
- # # OffsetArray
15
- struct OffsetArray{T,N,AA<: AbstractArray } <: AbstractArray{T,N}
16
- parent:: AA
17
- offsets:: NTuple{N,Int}
18
- function OffsetArray {T, N, AA} (parent:: AA , offsets:: NTuple{N, Int} ) where {T, N, AA<: AbstractArray }
19
- overflow_check .(axes (parent), offsets)
20
- new {T, N, AA} (parent, offsets)
21
- end
22
- end
23
- OffsetVector{T,AA<: AbstractArray } = OffsetArray{T,1 ,AA}
24
- OffsetMatrix{T,AA<: AbstractArray } = OffsetArray{T,2 ,AA}
25
-
26
- function overflow_check (r, offset:: T ) where T
27
- # This gives some performance boost https://github.com/JuliaLang/julia/issues/33273
28
- throw_upper_overflow_error () = throw (ArgumentError (" Boundary overflow detected: offset $offset should be equal or less than $(typemax (T) - last (r)) " ))
29
- throw_lower_overflow_error () = throw (ArgumentError (" Boundary overflow detected: offset $offset should be equal or greater than $(typemin (T) - first (r)) " ))
30
-
31
- if offset > 0 && last (r) > typemax (T) - offset
32
- throw_upper_overflow_error ()
33
- elseif offset < 0 && first (r) < typemin (T) - offset
34
- throw_lower_overflow_error ()
35
- end
36
- end
37
-
38
- # # OffsetArray constructors
39
-
40
- offset (axparent:: AbstractUnitRange , ax:: AbstractUnitRange ) = first (ax) - first (axparent)
41
- offset (axparent:: AbstractUnitRange , ax:: Integer ) = 1 - first (axparent)
42
-
43
- function OffsetArray (A:: AbstractArray{T,N} , offsets:: NTuple{N,Int} ) where {T,N}
44
- OffsetArray {T,N,typeof(A)} (A, offsets)
45
- end
46
- OffsetArray (A:: AbstractArray{T,0} , offsets:: Tuple{} ) where T =
47
- OffsetArray {T,0,typeof(A)} (A, ())
48
-
49
- OffsetArray (A:: AbstractArray{T,N} , offsets:: Vararg{Int,N} ) where {T,N} =
50
- OffsetArray (A, offsets)
51
- OffsetArray (A:: AbstractArray{T,0} ) where {T} = OffsetArray (A, ())
52
-
15
+ # Technically we know the length of CartesianIndices but we need to convert it first, so here we
16
+ # don't put it in OffsetAxisKnownLength.
17
+ const OffsetAxisKnownLength = Union{Integer, AbstractUnitRange, IdOffsetRange}
18
+ const OffsetAxis = Union{OffsetAxisKnownLength, CartesianIndices, Colon}
53
19
const ArrayInitializer = Union{UndefInitializer, Missing, Nothing}
54
- OffsetArray {T,N} (init:: ArrayInitializer , inds:: Indices{N} ) where {T,N} =
55
- OffsetArray (Array {T,N} (init, map (indexlength, inds)), map (indexoffset, inds))
56
- OffsetArray {T} (init:: ArrayInitializer , inds:: Indices{N} ) where {T,N} = OffsetArray {T,N} (init, inds)
57
- OffsetArray {T,N} (init:: ArrayInitializer , inds:: Vararg{AbstractUnitRange,N} ) where {T,N} = OffsetArray {T,N} (init, inds)
58
- OffsetArray {T} (init:: ArrayInitializer , inds:: Vararg{AbstractUnitRange,N} ) where {T,N} = OffsetArray {T,N} (init, inds)
59
-
60
- # OffsetVector constructors
61
- OffsetVector (A:: AbstractVector , offset) = OffsetArray (A, offset)
62
- OffsetVector {T} (init:: ArrayInitializer , inds:: AbstractUnitRange ) where {T} = OffsetArray {T} (init, inds)
63
-
64
- # OffsetMatrix constructors
65
- OffsetMatrix (A:: AbstractMatrix , offset1, offset2) = OffsetArray (A, offset1, offset2)
66
- OffsetMatrix (A:: AbstractMatrix , I:: CartesianIndices{2} ) = OffsetArray (A, I)
67
- OffsetMatrix {T} (init:: ArrayInitializer , inds1:: AbstractUnitRange , inds2:: AbstractUnitRange ) where {T} = OffsetArray {T} (init, inds1, inds2)
68
20
21
+ # # OffsetArray
69
22
"""
70
23
OffsetArray(A, indices...)
71
24
@@ -116,84 +69,74 @@ ERROR: [...]
116
69
```
117
70
118
71
"""
119
- function OffsetArray (A:: AbstractArray{T,N} , inds:: NTuple{N,AbstractUnitRange} ) where {T,N}
120
- axparent = axes (A)
121
- lA = map (length, axparent)
122
- lI = map (length, inds)
123
- lA == lI || throw (DimensionMismatch (" supplied axes do not agree with the size of the array (got size $lA for the array and $lI for the indices" ))
124
- OffsetArray (A, map (offset, axparent, inds))
72
+ struct OffsetArray{T,N,AA<: AbstractArray } <: AbstractArray{T,N}
73
+ parent:: AA
74
+ offsets:: NTuple{N,Int}
75
+ function OffsetArray {T, N, AA} (parent:: AA , offsets:: NTuple{N, Int} ) where {T, N, AA<: AbstractArray }
76
+ @boundscheck overflow_check .(axes (parent), offsets)
77
+ new {T, N, AA} (parent, offsets)
78
+ end
125
79
end
126
- OffsetArray (A :: AbstractArray {T,N} , inds :: Vararg{AbstractUnitRange,N} ) where {T,N} =
127
- OffsetArray (A, inds)
80
+ const OffsetVector {T,AA <: AbstractArray } = OffsetArray {T,1 ,AA}
81
+ const OffsetMatrix{T,AA <: AbstractArray } = OffsetArray{T, 2 ,AA}
128
82
129
- uncolonindices (A :: AbstractArray{<:Any,N} , inds :: NTuple{N,Any} ) where {N} = uncolonindices ( axes (A), inds)
130
- uncolonindices (ax :: Tuple , inds :: Tuple ) = ( first (inds), uncolonindices ( tail (ax), tail (inds)) ... )
131
- uncolonindices (ax :: Tuple , inds :: Tuple{Colon, Vararg{Any}} ) = ( first (ax), uncolonindices ( tail (ax), tail (inds)) ... )
132
- uncolonindices ( :: Tuple{} , :: Tuple{} ) = ( )
83
+ function overflow_check (r, offset :: T ) where T
84
+ # This gives some performance boost https://github.com/JuliaLang/julia/issues/33273
85
+ throw_upper_overflow_error ( ) = throw ( ArgumentError ( " Boundary overflow detected: offset $offset should be equal or less than $( typemax (T) - last (r)) " ) )
86
+ throw_lower_overflow_error () = throw ( ArgumentError ( " Boundary overflow detected: offset $offset should be equal or greater than $( typemin (T) - first (r)) " ) )
133
87
134
- function OffsetArray (A:: AbstractArray{T,N} , inds:: NTuple{N,Union{AbstractUnitRange, CartesianIndices{1}, Colon}} ) where {T,N}
135
- OffsetArray (A, uncolonindices (A, inds))
136
- end
137
- OffsetArray (A:: AbstractArray{T,N} , inds:: Vararg{Union{AbstractUnitRange, CartesianIndices{1}, Colon},N} ) where {T,N} =
138
- OffsetArray (A, uncolonindices (A, inds))
139
-
140
- # Specify offsets using CartesianIndices (issue #71)
141
- # Support a mix of AbstractUnitRanges and CartesianIndices{1}
142
- # Extract the range r from CartesianIndices((r,))
143
- function stripCartesianIndices (inds:: Tuple{CartesianIndices{1},Vararg{Any}} )
144
- I = first (inds)
145
- Ir = convert (Tuple{AbstractUnitRange{Int}}, I) |> first
146
- (Ir, stripCartesianIndices (tail (inds))... )
147
- end
148
- stripCartesianIndices (inds:: Tuple )= (first (inds), stripCartesianIndices (tail (inds))... )
149
- stripCartesianIndices (:: Tuple{} ) = ()
150
-
151
- OffsetArray (A:: AbstractArray{<:Any,N} , inds:: NTuple{N,Union{CartesianIndices{1}, AbstractUnitRange}} ) where {N} =
152
- OffsetArray (A, stripCartesianIndices (inds))
153
- OffsetArray (A:: AbstractArray{<:Any,N} , inds:: Vararg{Union{CartesianIndices{1}, AbstractUnitRange},N} ) where {N} =
154
- OffsetArray (A, inds)
155
-
156
- # Support an arbitrary CartesianIndices alongside colons and ranges
157
- # The total number of indices should equal ndims(arr)
158
- # We split the CartesianIndices{N} into N CartesianIndices{1} indices to facilitate dispatch
159
- splitCartesianIndices (c:: CartesianIndices{0} ) = ()
160
- function splitCartesianIndices (c:: CartesianIndices )
161
- c1, ct = Base. IteratorsMD. split (c, Val (1 ))
162
- (c1, splitCartesianIndices (ct)... )
163
- end
164
- function splitCartesianIndices (t:: Tuple{CartesianIndices, Vararg{Any}} )
165
- (splitCartesianIndices (first (t))... , splitCartesianIndices (tail (t))... )
166
- end
167
- function splitCartesianIndices (t:: Tuple )
168
- (first (t), splitCartesianIndices (tail (t))... )
88
+ if offset > 0 && last (r) > typemax (T) - offset
89
+ throw_upper_overflow_error ()
90
+ elseif offset < 0 && first (r) < typemin (T) - offset
91
+ throw_lower_overflow_error ()
92
+ end
169
93
end
170
- splitCartesianIndices ( :: Tuple{} ) = ()
94
+ # # OffsetArray constructors
171
95
172
- function OffsetArray (A:: AbstractArray , inds:: Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices, Colon}}} )
173
- OffsetArray (A, splitCartesianIndices (inds))
174
- end
175
- function OffsetArray (A:: AbstractArray , inds:: Vararg{Union{AbstractUnitRange, CartesianIndices, Colon}} )
176
- OffsetArray (A, inds)
96
+ for FT in (:OffsetArray , :OffsetVector , :OffsetMatrix )
97
+ # The only route out to inner constructor
98
+ @eval function $FT (A:: AbstractArray{T, N} , offsets:: NTuple{N, Integer} ) where {T, N}
99
+ ndims (A) == N || throw (DimensionMismatch (" The number of offsets $(N) should equal ndims(A) = $(ndims (A)) " ))
100
+ OffsetArray {T, ndims(A), typeof(A)} (A, offsets)
101
+ end
102
+ # nested OffsetArrays
103
+ @eval $ FT (A:: OffsetArray{T, N} , offsets:: NTuple{N, Integer} ) where {T,N} = $ FT (parent (A), A. offsets .+ offsets)
104
+ # convert ranges to offsets
105
+ @eval function $FT (A:: AbstractArray{T} , inds:: NTuple{N,OffsetAxisKnownLength} ) where {T,N}
106
+ axparent = axes (A)
107
+ lA = map (length, axparent)
108
+ lI = map (length, inds)
109
+ lA == lI || throw (DimensionMismatch (" supplied axes do not agree with the size of the array (got size $lA for the array and $lI for the indices" ))
110
+ $ FT (A, map (_offset, axparent, inds))
111
+ end
112
+ # lower CartesianIndices and Colon
113
+ @eval function $FT (A:: AbstractArray{T} , inds:: NTuple{N, OffsetAxis} ) where {T, N}
114
+ indsN = _uncolonindices (A, _expandCartesianIndices (inds))
115
+ $ FT (A, indsN)
116
+ end
117
+ @eval $ FT (A:: AbstractArray{T} , inds:: Vararg{OffsetAxis,N} ) where {T, N} = $ FT (A, inds)
177
118
end
178
119
179
- # Add methods to initialize OffsetArrays using CartesianIndices (issue #71)
180
- function OffsetArray {T,N} (init:: ArrayInitializer , inds:: Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices}} } ) where {T,N}
181
- indsN = stripCartesianIndices ( splitCartesianIndices ( inds))
182
- OffsetArray {T,N} (init, indsN )
120
+ # array initialization
121
+ function OffsetArray {T,N} (init:: ArrayInitializer , inds:: NTuple{N, OffsetAxisKnownLength } ) where {T,N}
122
+ AA = Array {T,N} (init, map (_indexlength, inds))
123
+ OffsetArray {T, N, typeof(AA)} (AA, map (_indexoffset, inds) )
183
124
end
184
- function OffsetArray {T} (init:: ArrayInitializer , inds:: Tuple{Vararg{Union{AbstractUnitRange, CartesianIndices}}} ) where {T}
185
- indsN = stripCartesianIndices (splitCartesianIndices (inds))
186
- OffsetArray {T} (init, indsN)
125
+ function OffsetArray {T, N} (init:: ArrayInitializer , inds:: NTuple{NT, Union{OffsetAxisKnownLength, CartesianIndices}} ) where {T, N, NT}
126
+ # NT is probably not the actual dimension of the array; CartesianIndices might contain multiple dimensions
127
+ indsN = _expandCartesianIndices (inds)
128
+ length (indsN) == N || throw (DimensionMismatch (" The number of offsets $(length (indsN)) should equal ndims(A) = $N " ))
129
+ OffsetArray {T, N} (init, indsN)
187
130
end
188
- OffsetArray {T,N} (init:: ArrayInitializer , inds:: Vararg{Union{AbstractUnitRange, CartesianIndices}} ) where {T,N} = OffsetArray {T,N} (init, inds)
189
- OffsetArray {T} (init:: ArrayInitializer , inds:: Vararg{Union{AbstractUnitRange, CartesianIndices}} ) where {T} = OffsetArray {T} (init, inds)
131
+ OffsetArray {T,N} (init:: ArrayInitializer , inds:: Union{OffsetAxisKnownLength, CartesianIndices} ...) where {T,N} = OffsetArray {T,N} (init, inds)
190
132
191
- # avoid a level of indirection when nesting OffsetArrays
192
- function OffsetArray (A:: OffsetArray , offsets:: NTuple{N,Int} ) where {N}
193
- OffsetArray (parent (A), offsets .+ A. offsets)
133
+ OffsetArray {T} (init:: ArrayInitializer , inds:: NTuple{N, OffsetAxisKnownLength} ) where {T,N} = OffsetArray {T,N} (init, inds)
134
+ function OffsetArray {T} (init:: ArrayInitializer , inds:: NTuple{N, Union{OffsetAxisKnownLength, CartesianIndices}} ) where {T, N}
135
+ # N is probably not the actual dimension of the array; CartesianIndices might contain multiple dimensions
136
+ indsN = _expandCartesianIndices (inds)
137
+ OffsetArray {T, length(indsN)} (init, indsN)
194
138
end
195
- OffsetArray (A:: OffsetArray{T,0} , inds:: Tuple{} ) where {T} = OffsetArray (parent (A), ())
196
- # OffsetArray(A::OffsetArray{T,N}, inds::Tuple{}) where {T,N} = error("this should never be called")
139
+ OffsetArray {T} (init:: ArrayInitializer , inds:: Union{OffsetAxisKnownLength, CartesianIndices} ...) where {T} = OffsetArray {T} (init, inds)
197
140
198
141
Base. IndexStyle (:: Type{OA} ) where {OA<: OffsetArray } = IndexStyle (parenttype (OA))
199
142
parenttype (:: Type{OffsetArray{T,N,AA}} ) where {T,N,AA} = AA
@@ -211,27 +154,24 @@ Base.eachindex(::IndexLinear, A::OffsetVector) = axes(A, 1)
211
154
@inline Base. axes (A:: OffsetArray , d) = d <= ndims (A) ? IdOffsetRange (axes (parent (A), d), A. offsets[d]) : IdOffsetRange (axes (parent (A), d))
212
155
@inline Base. axes1 (A:: OffsetArray{T,0} ) where {T} = IdOffsetRange (axes (parent (A), 1 )) # we only need to specialize this one
213
156
214
- const OffsetAxisKnownLength = Union{Integer, UnitRange, Base. OneTo, IdentityUnitRange, IdOffsetRange}
215
-
216
157
Base. similar (A:: OffsetArray , :: Type{T} , dims:: Dims ) where T =
217
158
similar (parent (A), T, dims)
218
159
function Base. similar (A:: AbstractArray , :: Type{T} , inds:: Tuple{OffsetAxisKnownLength,Vararg{OffsetAxisKnownLength}} ) where T
219
- B = similar (A, T, map (indexlength , inds))
220
- return OffsetArray (B, map (offset , axes (B), inds))
160
+ B = similar (A, T, map (_indexlength , inds))
161
+ return OffsetArray (B, map (_offset , axes (B), inds))
221
162
end
222
163
223
164
# reshape accepts a single colon
224
- const OffsetAxis = Union{OffsetAxisKnownLength, Colon}
225
165
Base. reshape (A:: AbstractArray , inds:: OffsetAxis... ) = reshape (A, inds)
226
166
function Base. reshape (A:: AbstractArray , inds:: Tuple{OffsetAxis,Vararg{OffsetAxis}} )
227
- AR = reshape (A, map (indexlength , inds))
228
- return OffsetArray (AR, map (offset , axes (AR), inds))
167
+ AR = reshape (A, map (_indexlength , inds))
168
+ return OffsetArray (AR, map (_offset , axes (AR), inds))
229
169
end
230
170
231
171
# Reshaping OffsetArrays can "pop" the original OffsetArray wrapper and return
232
172
# an OffsetArray(reshape(...)) instead of an OffsetArray(reshape(OffsetArray(...)))
233
173
Base. reshape (A:: OffsetArray , inds:: Tuple{OffsetAxis,Vararg{OffsetAxis}} ) =
234
- OffsetArray (reshape (parent (A), map (indexlength , inds)), map (indexoffset , inds))
174
+ OffsetArray (reshape (parent (A), map (_indexlength , inds)), map (_indexoffset , inds))
235
175
# And for non-offset axes, we can just return a reshape of the parent directly
236
176
Base. reshape (A:: OffsetArray , inds:: Tuple{Union{Integer,Base.OneTo},Vararg{Union{Integer,Base.OneTo}}} ) = reshape (parent (A), inds)
237
177
Base. reshape (A:: OffsetArray , inds:: Dims ) = reshape (parent (A), inds)
@@ -241,8 +181,8 @@ Base.reshape(A::OffsetArray, inds::Union{Int,Colon}...) = reshape(parent(A), ind
241
181
Base. reshape (A:: OffsetArray , inds:: Tuple{Vararg{Union{Int,Colon}}} ) = reshape (parent (A), inds)
242
182
243
183
function Base. similar (:: Type{T} , shape:: Tuple{OffsetAxis,Vararg{OffsetAxis}} ) where {T<: AbstractArray }
244
- P = T (undef, map (indexlength , shape))
245
- OffsetArray (P, map (offset , axes (P), shape))
184
+ P = T (undef, map (_indexlength , shape))
185
+ OffsetArray (P, map (_offset , axes (P), shape))
246
186
end
247
187
248
188
Base. fill (v, inds:: NTuple{N, Union{Integer, AbstractUnitRange}} ) where {N} =
@@ -346,15 +286,6 @@ Base.pop!(A::OffsetVector) = pop!(A.parent)
346
286
Base. append! (A:: OffsetVector , items) = (append! (A. parent, items); A)
347
287
Base. empty! (A:: OffsetVector ) = (empty! (A. parent); A)
348
288
349
- # ## Low-level utilities ###
350
-
351
- indexoffset (r:: AbstractRange ) = first (r) - 1
352
- indexoffset (i:: Integer ) = 0
353
- indexoffset (i:: Colon ) = 0
354
- indexlength (r:: AbstractRange ) = length (r)
355
- indexlength (i:: Integer ) = i
356
- indexlength (i:: Colon ) = Colon ()
357
-
358
289
# These functions keep the summary compact
359
290
function Base. inds2string (inds:: Tuple {Vararg{Union{IdOffsetRange, IdentityUnitRange{<: IdOffsetRange }}}})
360
291
Base. inds2string (map (UnitRange, inds))
0 commit comments