Skip to content

Commit 714bfd8

Browse files
committed
Add deepgetindex, deepsetindex! and deepview
1 parent a04fa4f commit 714bfd8

File tree

6 files changed

+294
-3
lines changed

6 files changed

+294
-3
lines changed

src/array_of_similar_arrays.jl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,58 @@ end
204204

205205

206206

207+
Base.@pure _result_is_nested(idxs_outer::Tuple, idxs_inner::Tuple) =
208+
Val{!(Base.index_dimsum(idxs_outer...) isa Tuple{}) && !(Base.index_dimsum(idxs_inner...) isa Tuple{})}()
209+
210+
Base.@pure ndims_after_getindex(idxs::Tuple) = Val{length(Base.index_dimsum(idxs...))}()
211+
212+
213+
Base.@propagate_inbounds function deepgetindex(A::ArrayOfSimilarArrays{T,M,N,L}, idxs::Vararg{Any,L}) where {T,M,N,L}
214+
idxs_outer, idxs_inner = split_tuple(idxs, Val{N}())
215+
nested = _result_is_nested(idxs_outer, idxs_inner)
216+
_deepgetindex_impl_aosa(A, idxs_outer, idxs_inner, nested)
217+
end
218+
219+
Base.@propagate_inbounds _deepgetindex_impl_aosa(A::ArrayOfSimilarArrays, idxs_outer::Tuple, idxs_inner::Tuple, nested::Val{false}) =
220+
getindex(A.data, idxs_inner..., idxs_outer...)
221+
222+
Base.@propagate_inbounds function _deepgetindex_impl_aosa(A::ArrayOfSimilarArrays, idxs_outer::Tuple, idxs_inner::Tuple, nested::Val{true})
223+
new_data = getindex(A.data, idxs_inner..., idxs_outer...)
224+
nestedview(new_data, ndims_after_getindex(idxs_inner))
225+
end
226+
227+
228+
Base.@propagate_inbounds function deepsetindex!(A::ArrayOfSimilarArrays{T,M,N,L}, x, idxs::Vararg{Any,L}) where {T,M,N,L}
229+
idxs_outer, idxs_inner = split_tuple(idxs, Val{N}())
230+
_deepsetindex_impl_aosa!(A, x, idxs_outer, idxs_inner, _result_is_nested(idxs_outer, idxs_inner))
231+
A
232+
end
233+
234+
Base.@propagate_inbounds _deepsetindex_impl_aosa!(A::ArrayOfSimilarArrays, x, idxs_outer::Tuple, idxs_inner::Tuple, nested::Val{false}) =
235+
setindex!(A.data, x, idxs_inner..., idxs_outer...)
236+
237+
Base.@propagate_inbounds _deepsetindex_impl_aosa!(A::ArrayOfSimilarArrays, x, idxs_outer::Tuple, idxs_inner::Tuple, nested::Val{true}) =
238+
_deepsetindex_impl!(A, x, idxs_outer, idxs_inner)
239+
240+
# TODO: Specialized method _deepsetindex_impl_aosa!(A::ArrayOfSimilarArrays, x::ArrayOfSimilarArrays, idxs_outer::Tuple, idxs_inner::Tuple, nested::Val{true}) =
241+
242+
243+
Base.@propagate_inbounds function deepview(A::ArrayOfSimilarArrays{T,M,N,L}, idxs::Vararg{Any,L}) where {T,M,N,L}
244+
idxs_outer, idxs_inner = split_tuple(idxs, Val{N}())
245+
nested = _result_is_nested(idxs_outer, idxs_inner)
246+
_deepview_impl_aosa(A, idxs_outer, idxs_inner, nested)
247+
end
248+
249+
Base.@propagate_inbounds _deepview_impl_aosa(A::ArrayOfSimilarArrays, idxs_outer::Tuple, idxs_inner::Tuple, nested::Val{false}) =
250+
view(A.data, idxs_inner..., idxs_outer...)
251+
252+
Base.@propagate_inbounds function _deepview_impl_aosa(A::ArrayOfSimilarArrays, idxs_outer::Tuple, idxs_inner::Tuple, nested::Val{true})
253+
new_data = view(A.data, idxs_inner..., idxs_outer...)
254+
nestedview(new_data, ndims_after_getindex(idxs_inner))
255+
end
256+
257+
258+
207259
const VectorOfSimilarArrays{
208260
T, M, L,
209261
P<:AbstractArray{T,L}

src/functions.jl

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export flatview
4444

4545
@inline flatview(A::AbstractArray) = A
4646

47+
# TODO: Implement flatview on generic nested arrays via new `FlatView`, using
48+
# deepgetindex to implement getindex, etc.
4749
@inline flatview(A::AbstractArray{<:AbstractArray}) = Base.Iterators.flatten(A)
4850

4951

@@ -92,3 +94,107 @@ end
9294

9395
@inline innersize(A::AbstractArray{<:AbstractArray}, dim::Integer) =
9496
innersize(A)[dim]
97+
98+
99+
"""
100+
deepgetindex(A::AbstractArray, idxs...)
101+
deepgetindex(A::AbstractArray{<:AbstractArray, N}, idxs...) where {N}
102+
103+
Recursive `getindex` on flat or nested arrays. If A is an array of arrays,
104+
uses the first `N` entries in `idxs` on `A`, then the rest on the inner
105+
array(s). If A is not a nested array, `deepgetindex` is equivalent to
106+
`getindex`.
107+
108+
See also [`deepsetindex!`](@ref) and [`deepview`](@ref).
109+
"""
110+
function deepgetindex end
111+
export deepgetindex
112+
113+
Base.@propagate_inbounds deepgetindex(A::AbstractArray{T,N}, idxs::Vararg{Any,N}) where {T,N} = getindex(A, idxs...)
114+
Base.@propagate_inbounds deepgetindex(A::AbstractArray{<:AbstractArray,N}, idxs::Vararg{Any,N}) where {N} = getindex(A, idxs...)
115+
116+
Base.@propagate_inbounds function deepgetindex(A::AbstractArray{<:AbstractArray,N}, idxs...) where {N}
117+
idxs_outer, idxs_inner = split_tuple(idxs, Val{N}())
118+
_deepgetindex_impl(A, idxs_outer, idxs_inner)
119+
end
120+
121+
Base.@propagate_inbounds _deepgetindex_impl(A::AbstractArray{<:AbstractArray}, idxs_outer::NTuple{N,Real}, idxs_inner::Tuple) where {N} =
122+
deepgetindex(getindex(A, idxs_outer...), idxs_inner...)
123+
124+
Base.@propagate_inbounds _deepgetindex_impl(A::AbstractArray{<:AbstractArray}, idxs_outer::NTuple{N,Any}, idxs_inner::Tuple) where {N} =
125+
_deepgetindex_tupled.(view(A, idxs_outer...), (idxs_inner,))
126+
127+
Base.@propagate_inbounds _deepgetindex_tupled(A::AbstractArray, idxs::Tuple) = deepgetindex(A, idxs...)
128+
129+
130+
"""
131+
deepsetindex!(A::AbstractArray, x, idxs...)
132+
deepsetindex!(A::AbstractArray{<:AbstractArray,N}, x, idxs...) where {N}
133+
134+
Recursive `setindex!` on flat or nested arrays. If A is an array of arrays,
135+
uses the first `N` entries in `idxs` on `A`, then the rest on the inner
136+
array(s). If A is not a nested array, `deepsetindex!` is equivalent to
137+
`setindex!`.
138+
139+
See also [`deepgetindex`](@ref) and [`deepview`](@ref).
140+
"""
141+
function deepsetindex! end
142+
export deepsetindex!
143+
144+
Base.@propagate_inbounds deepsetindex!(A::AbstractArray{T,N}, x, idxs::Vararg{Any,N}) where {T,N} = setindex!(A, x, idxs...)
145+
Base.@propagate_inbounds deepsetindex!(A::AbstractArray{<:AbstractArray,N}, x, idxs::Vararg{Any,N}) where {N} = setindex!(A, x, idxs...)
146+
147+
Base.@propagate_inbounds function deepsetindex!(A::AbstractArray{<:AbstractArray,N}, x, idxs...) where {N}
148+
idxs_outer, idxs_inner = split_tuple(idxs, Val{N}())
149+
_deepsetindex_impl!(A, x, idxs_outer, idxs_inner)
150+
A
151+
end
152+
153+
Base.@propagate_inbounds function _deepsetindex_impl!(A::AbstractArray{<:AbstractArray}, x, idxs_outer::NTuple{N,Real}, idxs_inner::Tuple) where {N}
154+
B = getindex(A, idxs_outer...)
155+
deepsetindex!(B, x, idxs_inner...)
156+
end
157+
158+
Base.@propagate_inbounds function _deepsetindex_impl!(A::AbstractArray{<:AbstractArray}, x, idxs_outer::NTuple{N,Any}, idxs_inner::Tuple) where {N}
159+
B = view(A, idxs_outer...)
160+
for i in eachindex(B, x)
161+
deepsetindex!(B[i], x[i], idxs_inner...)
162+
end
163+
end
164+
165+
166+
167+
"""
168+
deepview(A::AbstractArray, idxs...)
169+
deepview(A::AbstractArray{<:AbstractArray, N}, idxs...) where {N}
170+
171+
Recursive `view` on flat or nested arrays. If A is an array of arrays,
172+
uses the first `N` entries in `idxs` on `A`, then the rest on the inner
173+
array(s). If A is not a nested array, `deepview` is equivalent to `view`.
174+
175+
See also [`deepgetindex`](@ref) and [`deepsetindex!`](@ref).
176+
"""
177+
function deepview end
178+
export deepview
179+
180+
Base.@propagate_inbounds deepview(A::AbstractArray{T,N}, idxs::Vararg{Any,N}) where {T,N} = view(A, idxs...)
181+
Base.@propagate_inbounds deepview(A::AbstractArray{<:AbstractArray,N}, idxs::Vararg{Any,N}) where {N} = view(A, idxs...)
182+
183+
Base.@propagate_inbounds function deepview(A::AbstractArray{<:AbstractArray,N}, idxs...) where {N}
184+
idxs_outer, idxs_inner = split_tuple(idxs, Val{N}())
185+
_deepview_impl(A, idxs_outer, idxs_inner)
186+
end
187+
188+
Base.@propagate_inbounds _deepview_impl(A::AbstractArray{<:AbstractArray}, idxs_outer::NTuple{N,Real}, idxs_inner::NTuple{M,Real}) where {N,M} =
189+
deepview(getindex(A, idxs_outer...), idxs_inner...)
190+
191+
Base.@propagate_inbounds _deepview_impl(A::AbstractArray{<:AbstractArray}, idxs_outer::NTuple{N,Real}, idxs_inner::NTuple{M,Any}) where {N,M} =
192+
deepview(getindex(A, idxs_outer...), idxs_inner...)
193+
194+
Base.@propagate_inbounds _deepview_impl(A::AbstractArray{<:AbstractArray}, idxs_outer::NTuple{N,Any}, idxs_inner::NTuple{M,Real}) where {N,M} =
195+
throw(ArgumentError("deepview not supported yes with outer indices $idxs_outer and inner indices $idxs_inner"))
196+
197+
Base.@propagate_inbounds _deepview_impl(A::AbstractArray{<:AbstractArray}, idxs_outer::NTuple{N,Any}, idxs_inner::NTuple{M,Any}) where {N,M} =
198+
_deepview_tupled.(view(A, idxs_outer...), (idxs_inner,))
199+
200+
Base.@propagate_inbounds _deepview_tupled(A::AbstractArray, idxs::Tuple) = deepview(A, idxs...)

src/util.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ end
1717
Base.@pure _ncolons(::Val{N}) where N = ntuple(_ -> Colon(), Val{N}())
1818

1919

20-
Base.@propagate_inbounds front_tuple(x::NTuple{N}, ::Val{M}) where {N,M} =
20+
Base.@propagate_inbounds front_tuple(x::NTuple{N,Any}, ::Val{M}) where {N,M} =
2121
Base.ntuple(i -> x[i], Val{M}())
2222

23-
Base.@propagate_inbounds back_tuple(x::NTuple{N}, ::Val{M}) where {N,M} =
23+
Base.@propagate_inbounds back_tuple(x::NTuple{N,Any}, ::Val{M}) where {N,M} =
2424
Base.ntuple(i -> x[i + N - M], Val{M}())
2525

26-
Base.@propagate_inbounds split_tuple(x::NTuple{N}, ::Val{M}) where {N,M} =
26+
Base.@propagate_inbounds split_tuple(x::NTuple{N,Any}, ::Val{M}) where {N,M} =
2727
(front_tuple(x, Val{M}()), back_tuple(x, Val{N - M}()))
2828

29+
Base.@propagate_inbounds swap_front_back_tuple(x::NTuple{N,Any}, ::Val{M}) where {N,M} =
30+
(back_tuple(x, Val{N - M}())..., front_tuple(x, Val{M}())...)
31+
2932

3033
_convert_elype(::Type{T}, A::AbstractArray{T}) where {T} = A
3134

test/array_of_similar_arrays.jl

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ using UnsafeArrays
8585
test_from_flat(VectorOfSimilarVectors{Float32}, VectorOfSimilarVectors{Float32,Array{Float32,2}}, Val(2))
8686
end
8787

88+
8889
@testset "construct/convert from nested arrays" begin
8990
test_from_nested(ArrayOfSimilarArrays, ArrayOfSimilarArrays{Float64,2,3,5,Array{Float64,5}}, Val(2), Val(3))
9091
test_from_nested(ArrayOfSimilarArrays{Float64,2,3}, ArrayOfSimilarArrays{Float64,2,3,5,Array{Float64,5}}, Val(2), Val(3))
@@ -110,6 +111,69 @@ using UnsafeArrays
110111
test_from_nested(VectorOfSimilarVectors, VectorOfSimilarVectors{Float64,Array{Float64,2}}, Val(1), Val(1))
111112
end
112113

114+
@testset "flatview" begin
115+
A = rand_nested_similar_arrays(Val(3), Val(2))
116+
B = ArrayOfSimilarArrays(A)
117+
@inferred(flatview(B))[:] == collect(flatview(A))
118+
end
119+
120+
121+
@testset "deepgetindex" begin
122+
A = rand_nested_similar_arrays(Val(3), Val(2))
123+
B = ArrayOfSimilarArrays(A)
124+
125+
@test deepgetindex(A, 3, 4, 2, 1, 2) == @inferred deepgetindex(B, 3, 4, 2, 1, 2)
126+
@test deepgetindex(A, 2:3, 4, 2, 1, 2) == @inferred deepgetindex(B, 2:3, 4, 2, 1, 2)
127+
@test deepgetindex(A, 2:3, 2:4, 2, 1, 2) == @inferred deepgetindex(B, 2:3, 2:4, 2, 1, 2)
128+
@test deepgetindex(A, 2, 4, :, 1, 2) == @inferred deepgetindex(B, 2, 4, :, 1, 2)
129+
@test deepgetindex(A, 2, 4, :, 1, 1:2) == @inferred deepgetindex(B, 2, 4, :, 1, 1:2)
130+
@test deepgetindex(A, 2:3, 4, :, 1, 2) == @inferred deepgetindex(B, 2:3, 4, :, 1, 2)
131+
@test deepgetindex(A, 2:3, 4, :, 1, 1:2) == @inferred deepgetindex(B, 2:3, 4, :, 1, 1:2)
132+
end
133+
134+
135+
@testset "deepsetindex!" begin
136+
function testdata()
137+
A = rand_nested_similar_arrays(Val(3), Val(2))
138+
B = ArrayOfSimilarArrays(A)
139+
A, B
140+
end
141+
142+
A, B = testdata()
143+
@test deepsetindex!(A, 42, 3, 4, 2, 1, 2) == @inferred deepsetindex!(B, 42, 3, 4, 2, 1, 2)
144+
@test deepgetindex(B, 3, 4, 2, 1, 2) == 42
145+
146+
A, B = testdata()
147+
X1 = rand(2)
148+
@test deepsetindex!(A, X1, 2:3, 4, 2, 1, 2) == @inferred deepsetindex!(B, X1, 2:3, 4, 2, 1, 2)
149+
@test deepgetindex(B, 2:3, 4, 2, 1, 2) == X1
150+
151+
A, B = testdata()
152+
X2 = rand(2,2)
153+
@test deepsetindex!(A, X2, 2, 4, :, 1, 1:2) == @inferred deepsetindex!(B, X2, 2, 4, :, 1, 1:2)
154+
@test deepgetindex(B, 2, 4, :, 1, 1:2) == X2
155+
156+
A, B = testdata()
157+
X3 = [rand(2,2), rand(2,2)]
158+
@test deepsetindex!(A, X3, 2:3, 4, :, 1, 1:2) == @inferred deepsetindex!(B, X3, 2:3, 4, :, 1, 1:2)
159+
@test deepgetindex(B, 2:3, 4, :, 1, 1:2) == X3
160+
end
161+
162+
163+
@testset "deepview" begin
164+
A = rand_nested_similar_arrays(Val(3), Val(2))
165+
B = ArrayOfSimilarArrays(A)
166+
167+
@test deepview(A, 3, 4, 2, 1, 2) == @inferred deepview(B, 3, 4, 2, 1, 2)
168+
@test deepgetindex(A, 2:3, 4, 2, 1, 2) == @inferred deepview(B, 2:3, 4, 2, 1, 2)
169+
@test deepgetindex(A, 2:3, 2:4, 2, 1, 2) == @inferred deepview(B, 2:3, 2:4, 2, 1, 2)
170+
@test deepview(A, 2, 4, :, 1, 2) == @inferred deepview(B, 2, 4, :, 1, 2)
171+
@test deepview(A, 2, 4, :, 1, 1:2) == @inferred deepview(B, 2, 4, :, 1, 1:2)
172+
@test deepview(A, 2:3, 4, :, 1, 2) == @inferred deepview(B, 2:3, 4, :, 1, 2)
173+
@test deepview(A, 2:3, 4, :, 1, 1:2) == @inferred deepview(B, 2:3, 4, :, 1, 1:2)
174+
end
175+
176+
113177
@testset "examples" begin
114178
A_flat = rand(2,3,4,5,6)
115179
A_nested = nestedview(A_flat, 2)

test/functions.jl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# This file is a part of ArraysOfArrays.jl, licensed under the MIT License (MIT).
2+
3+
using ArraysOfArrays
4+
using Test
5+
6+
7+
@testset "functions" begin
8+
function gen_nested()
9+
A11 = [1 2; 3 4]
10+
A21 = [4 5 6; 7 8 9]
11+
A12 = [10 11; 12 13; 14 15]
12+
A22 = [16 17; 18 19]
13+
hcat(Array[A11, A21], Array[A12, A22])
14+
end
15+
16+
17+
@testset "deepgetindex" begin
18+
A = gen_nested()
19+
@test @inferred(deepgetindex(A, 1, 2)) === A[1, 2]
20+
@test @inferred(deepgetindex(A, 1, 2, 2, 1)) == A[1, 2][2, 1]
21+
@test @inferred(deepgetindex(A, 1, 2, 1:2, 1)) == A[1, 2][1:2, 1]
22+
@test @inferred(deepgetindex(A, 1, 1:2, 2, 1)) == [A[1, 1][2, 1], A[1, 2][2, 1]]
23+
@test @inferred(deepgetindex(A, 1, 1:2, 1:2, 2)) == [A[1, 1][1:2, 2], A[1, 2][1:2, 2]]
24+
@test_throws MethodError deepgetindex(A, 1, 2, 2, 1, 2)
25+
26+
B = rand(3, 4, 5)
27+
@test @inferred(deepgetindex(B, 2, 3, 4)) === getindex(B, 2, 3, 4)
28+
@test_throws MethodError deepgetindex(B, 6)
29+
@test_throws MethodError deepgetindex(B, 2, 3)
30+
end
31+
32+
33+
@testset "deepsetindex!" begin
34+
B12 = [20 21 22; 23 24 25]
35+
X = [[41, 42], [43, 44]]
36+
37+
A = gen_nested()
38+
@test @inferred(deepsetindex!(A, B12, 1, 2)) === A
39+
@test A[1, 2] == B12
40+
41+
A = gen_nested()
42+
@test @inferred(deepsetindex!(A, 42, 1, 2, 2, 1)) === A
43+
@test A[1, 2][2, 1] == 42
44+
45+
A = gen_nested()
46+
@test @inferred(deepsetindex!(A, X, 1, 1:2, 1:2, 2)) === A
47+
@test deepgetindex(A, 1, 1:2, 1:2, 2) == X
48+
end
49+
50+
51+
@testset "deepview" begin
52+
A = gen_nested()
53+
@test @inferred(deepview(A, 1, 2)) == view(A, 1, 2)
54+
@test @inferred(deepview(A, 1, 2, 2, 1)) == view(A[1, 2], 2, 1)
55+
@test @inferred(deepview(A, 1, 2, 1:2, 1)) == view(A[1, 2], 1:2, 1)
56+
@test_throws ArgumentError deepview(A, 1, 1:2, 2, 1)
57+
@test @inferred(deepview(A, 1, 1:2, 1:2, 2)) == [A[1, 1][1:2, 2], A[1, 2][1:2, 2]]
58+
@test_throws MethodError deepview(A, 1, 2, 2, 1, 2)
59+
60+
B = rand(3, 4, 5)
61+
@test @inferred(deepview(B, 2, 3, 4)) === view(B, 2, 3, 4)
62+
@test_throws MethodError deepview(B, 6)
63+
@test_throws MethodError deepview(B, 2, 3)
64+
end
65+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Test
44

55
Test.@testset "Package ArraysOfArrays" begin
6+
include("functions.jl")
67
include("array_of_similar_arrays.jl")
78
include("vector_of_arrays.jl")
89
end

0 commit comments

Comments
 (0)