Skip to content

Commit 7870e31

Browse files
committed
Add ArrayOfSimilarArrays
1 parent 9f322e6 commit 7870e31

File tree

4 files changed

+261
-2
lines changed

4 files changed

+261
-2
lines changed

REQUIRE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
julia 0.6
2-
Compat 0.33
2+
Compat 0.54

src/ArraysOfArrays.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ __precompile__(true)
44

55
module ArraysOfArrays
66

7-
# package code goes here
7+
using Compat
8+
using Compat.Markdown
9+
using Compat: axes
10+
11+
include("util.jl")
12+
include("array_of_similar_arrays.jl")
813

914
end # module

src/array_of_similar_arrays.jl

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# This file is a part of ArraysOfArrays.jl, licensed under the MIT License (MIT).
2+
3+
@doc doc"""
4+
AbstractArrayOfSimilarArrays{T,M,N} <: AbstractArray{AbstractArray{T,M},N}
5+
6+
An array that contains arrays that have the same size/axes. The array is
7+
internally stored in flattened form as some kind of array of dimension
8+
`M + N`. The flattened form can be accessed via `parent(A)`.
9+
10+
Subtypes must implement (in addition to typical array operations):
11+
12+
parent(A::SomeArrayOfSimilarArrays)::AbstractArray{T,M+N}
13+
14+
The following type aliases are defined:
15+
16+
* `AbstractVectorOfSimilarArrays{T,M} = AbstractArrayOfSimilarArrays{T,M,1}`
17+
* `AbstractArrayOfSimilarVectors{T,N} = AbstractArrayOfSimilarArrays{T,1,N}`
18+
* `AbstractVectorOfSimilarVectors{T} = AbstractArrayOfSimilarArrays{T,1,1}`
19+
"""
20+
abstract type AbstractArrayOfSimilarArrays{T,M,N} <: AbstractArray{AbstractArray{T,M},N} end
21+
export AbstractArrayOfSimilarArrays
22+
23+
const AbstractVectorOfSimilarArrays{T,M} = AbstractArrayOfSimilarArrays{T,M,1}
24+
export AbstractVectorOfSimilarArrays
25+
26+
const AbstractArrayOfSimilarVectors{T,N} = AbstractArrayOfSimilarArrays{T,1,N}
27+
export AbstractArrayOfSimilarVectors
28+
29+
const AbstractVectorOfSimilarVectors{T} = AbstractArrayOfSimilarArrays{T,1,1}
30+
export AbstractVectorOfSimilarVectors
31+
32+
33+
34+
@doc doc"""
35+
ArrayOfSimilarArrays{T,M,N,L,P,VF} <: AbstractArrayOfSimilarArrays{T,M,N}
36+
37+
Represents a view of an array of dimension `L = M + N` as an array of
38+
dimension M with elements that are arrays with dimension N. All element arrays
39+
implicitly have equal size/axes.
40+
41+
Constructors:
42+
43+
ArrayOfSimilarArrays{N}(parent::AbstractArray)
44+
45+
The following type aliases are defined:
46+
47+
* `VectorOfSimilarArrays{T,M} = AbstractArrayOfSimilarArrays{T,M,1}`
48+
* `ArrayOfSimilarVectors{T,N} = AbstractArrayOfSimilarArrays{T,1,N}`
49+
* `VectorOfSimilarVectors{T} = AbstractArrayOfSimilarArrays{T,1,1}`
50+
51+
`VectorOfSimilarArrays` supports `push!()`, etc., provided the underlying
52+
array supports resizing of it's last dimension (e.g. an `ElasticArray`).
53+
"""
54+
struct ArrayOfSimilarArrays{
55+
T, M, N, L,
56+
P<:AbstractArray{T,L},
57+
VF<:Function
58+
} <: AbstractArrayOfSimilarArrays{T,M,N}
59+
data::P
60+
viewfunc::VF
61+
62+
function ArrayOfSimilarArrays{M}(parent::AbstractArray{T,L}, viewfunc::VF = view) where {T,M,L,VF<:Function}
63+
size_inner, size_outer = split_tuple(size(parent), Val{M}())
64+
N = length(size_outer)
65+
P = typeof(parent)
66+
new{T,M,N,L,P,VF}(parent, viewfunc)
67+
end
68+
end
69+
70+
export ArrayOfSimilarArrays
71+
72+
73+
function _size_inner(A::AbstractArray{<:AbstractArray{T,M},N}) where {T,M,N}
74+
s = if !isempty(A)
75+
map(Int, size(A[1]))
76+
else
77+
ntuple(_ -> zero(Int), Val(M))
78+
end
79+
80+
all(X -> size(X) == s, A) || throw(DimensionMismatch("Shape of element arrays of A is not equal, can't determine common shape"))
81+
s
82+
end
83+
84+
function _size_inner(A::ArrayOfSimilarArrays{T,M,N}) where {T,M,N}
85+
sz_inner, sz_outer = split_tuple(size(A.data), Val{M}())
86+
sz_inner
87+
end
88+
89+
90+
function ArrayOfSimilarArrays(A::AbstractArray{<:AbstractArray{T,M},N}, viewfunc::VF = view) where {T,M,N,VF<:Function}
91+
B = ArrayOfSimilarArrays{M}(Array{T,M+N}(_size_inner(A)..., size(A)...))
92+
copy!(B, A)
93+
end
94+
95+
96+
import Base.==
97+
(==)(A::ArrayOfSimilarArrays{T,M,N}, B::ArrayOfSimilarArrays{T,M,N}) where {T,M,N} =
98+
(A.data == B.data)
99+
100+
101+
Base.parent(A::ArrayOfSimilarArrays) = A.data
102+
103+
104+
Base.size(A::ArrayOfSimilarArrays{T,M,N}) where {T,M,N} = split_tuple(size(A.data), Val{M}())[2]
105+
106+
107+
Base.@propagate_inbounds Base.getindex(A::ArrayOfSimilarArrays{T,M,N}, idxs::Vararg{Any,N}) where {T,M,N} =
108+
A.viewfunc(A.data, _ncolons(Val{M}())..., idxs...)
109+
110+
111+
Base.@propagate_inbounds Base.setindex!(A::ArrayOfSimilarArrays{T,M,N}, x::AbstractArray{U,M}, idxs::Vararg{Any,N}) where {T,M,N,U} =
112+
setindex!(A.data, x, _ncolons(Val{M}())..., idxs...)
113+
114+
115+
@static if VERSION < v"0.7.0-DEV.2791"
116+
Base.repremptyarray(io::IO, X::ArrayOfSimilarArrays{T,M,N,L,P,VF}) where {T,M,N,L,P,VF} = print(io, "ElasticArray{$T,$M,$N,$L,$P,$VF}(", join(size(X),','), ')')
117+
end
118+
119+
120+
@inline function Base.resize!(A::ArrayOfSimilarArrays{T,M,N}, dims::Vararg{Integer,N}) where {T,M,N}
121+
resize!(A.data, _size_inner(A)..., dims...)
122+
A
123+
end
124+
125+
126+
function Base.similar(A::ArrayOfSimilarArrays{T,M,N}, ::Type{<:AbstractArray{U}}, dims::Dims) where {T,M,N,U}
127+
data = A.data
128+
size_inner, size_outer = split_tuple(size(data), Val{M}())
129+
ArrayOfSimilarArrays{M}(similar(data, U, size_inner..., dims...), A.viewfunc)
130+
end
131+
132+
133+
function Base.resize!(dest::ArrayOfSimilarArrays{T,M,N}, src::ArrayOfSimilarArrays{U,M,N}) where {T,M,N,U}
134+
_size_inner(dest) != _size_inner(src) && throw(DimensionMismatch("Can't append, shape of element arrays of source and dest are not equal"))
135+
append!(dest.data, src.data)
136+
dest
137+
end
138+
139+
140+
function Base.append!(dest::ArrayOfSimilarArrays{T,M,N}, src::ArrayOfSimilarArrays{U,M,N}) where {T,M,N,U}
141+
_size_inner(dest) != _size_inner(src) && throw(DimensionMismatch("Can't append, shape of element arrays of source and dest are not equal"))
142+
append!(dest.data, src.data)
143+
dest
144+
end
145+
146+
Base.append!(dest::ArrayOfSimilarArrays{T,M,N}, src::AbstractArray{<:AbstractArray{U,M},N}) where {T,M,N,U} =
147+
append!(dest, ArrayOfSimilarArrays(src))
148+
149+
150+
function Base.prepend!(dest::ArrayOfSimilarArrays{T,M,N}, src::ArrayOfSimilarArrays{U,M,N}) where {T,M,N,U}
151+
_size_inner(dest) != _size_inner(src) && throw(DimensionMismatch("Can't prepend, shape of element arrays of source and dest are not equal"))
152+
prepend!(dest.data, src.data)
153+
dest
154+
end
155+
156+
Base.prepend!(dest::ArrayOfSimilarArrays{T,M,N}, src::AbstractArray{<:AbstractArray{U,M},N}) where {T,M,N,U} =
157+
prepend!(dest, ArrayOfSimilarArrays(src))
158+
159+
160+
Base.view(A::ArrayOfSimilarArrays{T,M,N}, viewfunc::Function) where {T,M,N} =
161+
ArrayOfSimilarArrays{M}(A.data, viewfunc)
162+
163+
164+
165+
const VectorOfSimilarArrays{
166+
T, M, L,
167+
P<:AbstractArray{T,L},
168+
VF<:Function
169+
} = ArrayOfSimilarArrays{T,M,1,L,P,VF}
170+
171+
export VectorOfSimilarArrays
172+
173+
VectorOfSimilarArrays(parent::AbstractArray{T,L}, viewfunc::VF = view) where {T,L,VF<:Function} =
174+
ArrayOfSimilarArrays{L-1}(parent, viewfunc)
175+
176+
177+
@inline Base.IndexStyle(V::VectorOfSimilarArrays) = IndexLinear()
178+
179+
180+
function Base.push!(V::VectorOfSimilarArrays{T,M}, x::AbstractArray{U,M}) where {T,M,U}
181+
size(x) != Base.front(size(V.data)) && throw(DimensionMismatch("Can't push, shape of source and elements of target is incompatible"))
182+
append!(V.data, x)
183+
V
184+
end
185+
186+
function Base.pop!(V::VectorOfSimilarArrays)
187+
isempty(V) && throw(ArgumentError("array must be non-empty"))
188+
x = V[end]
189+
resize!(V, size(V, 1) - 1)
190+
x
191+
end
192+
193+
function Compat.pushfirst!(V::VectorOfSimilarArrays{T,M}, x::AbstractArray{U,M}) where {T,M,U}
194+
size(x) != Base.front(size(V.data)) && throw(DimensionMismatch("Can't push, shape of source and elements of target is incompatible"))
195+
prepend!(V.data, x)
196+
V
197+
end
198+
199+
# Will need equivalent of resize! that resizes in front of data instead of in back:
200+
# Compat.popfirst!(V::ArrayOfSimilarArrays) = ...
201+
202+
203+
204+
const ArrayOfSimilarVectors{
205+
T, N, L,
206+
P<:AbstractArray{T,L},
207+
VF<:Function,
208+
} = ArrayOfSimilarArrays{T,1,N,L,P,VF}
209+
210+
export ArrayOfSimilarVectors
211+
212+
ArrayOfSimilarVectors(parent::AbstractArray{T,L}, viewfunc::VF = view) where {T,L,VF<:Function} =
213+
ArrayOfSimilarArrays{1}(parent, viewfunc)
214+
215+
216+
217+
const VectorOfSimilarVectors{
218+
T,
219+
P<:AbstractArray{T,2},
220+
VF<:Function,
221+
} = ArrayOfSimilarArrays{T,1,1,2,P,VF}
222+
223+
export VectorOfSimilarVectors
224+
225+
VectorOfSimilarVectors(parent::AbstractArray{T,2}, viewfunc::VF = view) where {T,VF<:Function} =
226+
ArrayOfSimilarArrays{1}(parent, viewfunc)

src/util.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This file is a part of ElasticArrays.jl, licensed under the MIT License (MIT).
2+
3+
4+
#=
5+
6+
function _split_dims(dims::NTuple{N,Integer}) where {N}
7+
int_dims = Int.(dims)
8+
Base.front(int_dims), int_dims[end]
9+
end
10+
11+
=#
12+
13+
@inline _tail_impl(x, ys...) = (ys...,)
14+
@inline _tail(x) = _tail_impl(x...)
15+
16+
17+
Base.@pure _ncolons(::Val{N}) where N = ntuple(_ -> Colon(), Val{N}())
18+
19+
20+
@inline function _split_tuple_impl(x::NTuple{N,Any}, y::Tuple, ::Val{N}) where {N}
21+
x, y
22+
end
23+
24+
@inline function _split_tuple_impl(x::NTuple{M,Any}, y::Tuple, ::Val{N}) where {M, N}
25+
_split_tuple_impl((x..., y[1]), _tail(y), Val{N}())
26+
end
27+
28+
@inline split_tuple(x, ::Val{N}) where {N} = _split_tuple_impl((), x, Val{N}())

0 commit comments

Comments
 (0)