|
3 | 3 | [](https://travis-ci.com/oschulz/ArraysOfArrays.jl)
|
4 | 4 | [](https://ci.appveyor.com/project/oschulz/ArraysOfArrays-jl)
|
5 | 5 | [](https://codecov.io/gh/oschulz/ArraysOfArrays.jl)
|
| 6 | + |
| 7 | + |
| 8 | +A Julia package for efficient storage of nested arrays. |
| 9 | + |
| 10 | +ArraysOfArrays provides two different types of nested arrays: `ArrayOfSimilarArrays` and `VectorOfArrays`. |
| 11 | + |
| 12 | + |
| 13 | +## ArrayOfSimilarArrays |
| 14 | + |
| 15 | +An `ArrayOfSimilarArrays` offers duality of viewing the same data as both a flat multi-dimensional array and as an array of equally-sized arrays. |
| 16 | + |
| 17 | +```julia |
| 18 | +A_flat = rand(2,3,4,5,6) |
| 19 | +A_nested = nestedview(A_flat, 2) |
| 20 | +``` |
| 21 | + |
| 22 | +creates a view of `A_flat` as an array of arrays: |
| 23 | + |
| 24 | +```julia |
| 25 | +A_nested isa AbstractArray{<:AbstractArray{T,2},3} where T |
| 26 | +``` |
| 27 | + |
| 28 | +`A_flat` is always available via `flatview`. `A_flat` and `A_nested` are backed by the same data, no data is copied: |
| 29 | + |
| 30 | +```julia |
| 31 | +flatview(A_nested) === A_flat |
| 32 | +``` |
| 33 | + |
| 34 | +Calling `getindex` on `A_nested` returns a view into `A_flat`: |
| 35 | + |
| 36 | +```julia |
| 37 | +fill!(A_nested[2, 4, 3], 4.2) |
| 38 | +all(x -> x == 4.2, A_flat[:, :, 2, 4, 3]) |
| 39 | +``` |
| 40 | + |
| 41 | +### Type aliases |
| 42 | + |
| 43 | +The following type aliases are defined: |
| 44 | + |
| 45 | +* `VectorOfSimilarArrays{T,M} = AbstractArrayOfSimilarArrays{T,M,1}` |
| 46 | +* `ArrayOfSimilarVectors{T,N} = AbstractArrayOfSimilarArrays{T,1,N}` |
| 47 | +* `VectorOfSimilarVectors{T} = AbstractArrayOfSimilarArrays{T,1,1}` |
| 48 | + |
| 49 | +For each of the types there is also an abstract type (`AbstractArrayOfSimilarArrays`, etc.). |
| 50 | + |
| 51 | +If a `VectorOfSimilarArrays` is backed by an `ElasticArrays.ElasticArray`, additional element arrays can be pushed into it and `resize!` is available too: |
| 52 | + |
| 53 | +### Appending data and resizing |
| 54 | + |
| 55 | +```julia |
| 56 | +using ElasticArrays |
| 57 | + |
| 58 | +A_nested = nestedview(ElasticArray{Float64}(undef, 2, 3, 0), 2) |
| 59 | + |
| 60 | +for i in 1:4 |
| 61 | + push!(A_nested, rand(2, 3)) |
| 62 | +end |
| 63 | +size(flatview(A_nested)) == (2, 3, 4) |
| 64 | + |
| 65 | +resize!(A_nested, 6) |
| 66 | +size(flatview(A_nested)) == (2, 3, 6) |
| 67 | +``` |
| 68 | + |
| 69 | +There is a full duality between the nested and the flat view of the data. `A_flat` may be resized freely without breaking the inner consistency of `A_nested`: Changes in the shape of one will result in changes in the shape of the other. |
| 70 | + |
| 71 | + |
| 72 | +## VectorOfArrays |
| 73 | + |
| 74 | +A `VectorOfArrays` represents a vector of arrays of equal dimensionality but different size. It is a nested interpretation of the concept of a "ragged array". |
| 75 | + |
| 76 | +```julia |
| 77 | +VA = VectorOfArrays{Float64, 2}() |
| 78 | + |
| 79 | +push!(VA, rand(2, 3)) |
| 80 | +push!(VA, rand(4, 2)) |
| 81 | + |
| 82 | +size(VA[1]) == (2,3) |
| 83 | +size(VA[2]) == (4,2) |
| 84 | +``` |
| 85 | + |
| 86 | +Internally, all data is stored efficiently in a single, flat and memory-contiguous vector, accessible via `flatview`: |
| 87 | + |
| 88 | +```julia |
| 89 | +VA_flat = flatview(VA) |
| 90 | +VA_flat isa Vector{Float64} |
| 91 | +``` |
| 92 | + |
| 93 | +Calling `getindex` on `A_nested` returns a view into `A_flat`: |
| 94 | + |
| 95 | +```julia |
| 96 | +VA_flat = flatview(VA) |
| 97 | +view(VA_flat, 7:14) == vec(VA[2]) |
| 98 | + |
| 99 | +fill!(view(VA_flat, 7:14), 2.4) |
| 100 | +all(x -> x == 2.4, VA[2]) |
| 101 | + |
| 102 | +fill!(view(VA_flat, 7:14), 4.2) |
| 103 | +all(x -> x == 4.2, VA[2]) |
| 104 | +``` |
| 105 | + |
| 106 | +### Type aliases |
| 107 | +The following type aliases are defined: |
| 108 | + |
| 109 | +* `VectorOfVectors{T,VT,VI,VD} = VectorOfArrays{T,1,VT,VI,VD}` |
| 110 | + |
| 111 | +### Appending data and resizing |
| 112 | + |
| 113 | +A `VectorOfArrays` is grown by appending data to it. `resize!` can be used to shrink it, but not to grow it (the size of the additional element arrays would be unknown): |
| 114 | + |
| 115 | +``` |
| 116 | +length(resize!(VA, 1)) == 1 |
| 117 | +``` |
| 118 | + |
| 119 | +but |
| 120 | + |
| 121 | +``` |
| 122 | +resize!(VA, 4) |
| 123 | +``` |
| 124 | + |
| 125 | +will fail. |
| 126 | + |
| 127 | +Note: The vector returned by `flatview(VA)` *must not* be resized directly, doing so would break the internal consistency of `VA`. |
| 128 | + |
| 129 | + |
| 130 | +## Allocation free element access |
| 131 | + |
| 132 | +Element access via `getindex` returns (possibly reshaped) instances of `SubArray` for both `ArrayOfSimilarArrays` and `VectorOfArrays`. Usually this is not a problem, but frequent allocation of a large number of views can become a limiting factor in multi-threaded applications. |
| 133 | + |
| 134 | +Both types support `UnsafeArrays.@uviews` for allocation-free getindex: |
| 135 | + |
| 136 | +```julia |
| 137 | +using UnsafeArrays |
| 138 | + |
| 139 | +A = nestedview(rand(2,3,4,5), 2) |
| 140 | + |
| 141 | +isbits(A[2,2]) == false |
| 142 | + |
| 143 | +@uviews A begin |
| 144 | + isbits(A[2,2]) == true |
| 145 | +end |
| 146 | +``` |
| 147 | + |
| 148 | +As always, `UnsafeArray`s should be used with great care: The pointer-based bitstype |
| 149 | +views *must not* be allowed to escape the `@uviews` scope, and internal data of `A` *must not* be reallocated (e.g. due to a `push!` or `append!` on `A`) while the `@uviews` scope is active. |
0 commit comments