FastArrays
(Formerly called FlexibleArrays
.)
Fast multidimensional arrays, with arbitrary lower and upper bounds that can be fixed at compile time to improve efficiency
Background
Sometimes you really want arrays where the lower index bound is different from 1. This is not a question of performance, but of convenience  for example, if you have quantum numbers ranging from 0
to k
, then adding 1
every time you index an array looks tedious. This really should be part of the type declaration.
And sometimes, you know the size of a particular array dimension ahead of time. This is now a question of efficiency  indexing into a multidimensional array is significantly more efficient if the sizes of the dimensions are known ahead of time.
This is just what this package FastArrays
provides: A way to define multidimensional array types where both lower and upper index bounds can be chosen freely, and which generates more efficient code if these bounds are known ahead of time.
Here is an example:
using FastArrays
# A (10x10) fixedsize array
const Arr2d_10x10 = FastArray(1:10, 1:10)
a2 = Arr2d_10x10{Float64}(undef, :, :)
# A 3d array with lower index bounds 0
const Arr3d_lb0 = FastArray(0, 0, 0)
a3 = Arr3d_lb0{Float64}(undef, 9, 9, 9)
# A generic array, all bounds determined at creation time
const Arr4d_generic = FastArray(:, :, :, :)
a4 = Arr4d_generic{Float64}(undef, 1:10, 0:10, 1:10, 15:15)
# These can be mixed: A (2x10) array
FastArray(0:1, 1){Float64}(undef, :, 10)
# Arrays can also be empty:
FastArray(4:13, 10:9)
FastArray(:){Int}(undef, 5:0)
# The trivial 0d array type, always holding one scalar value:
FastArray(){Int}
Fast arrays are accessed like regular arrays, using the []
notation or the getindex
and setindex!
functions.
You will have noticed the slightly unusual notation for fast arrays. Implementationwise, the set of all bounds that are kept fixed determine the (parameterized) type of the array; different choices for fixed array bounds correspond to different types. FastArray
is a function that returns the respective type, creating this type if necessary.
Currently, fast arrays do not yet support resizing, reshaping, or subarrays; adding this would be straightforward.
I designed FastArrays for the two reasons above  I needed to model quantum numbers that have a range of 0:k
, and I noticed that the C++ version of the respecived code became significantly faster when setting array sizes at compile time. In Julia, I confirmed that the generated machine code is also much simpler in this case. Of course, this provides a benefit only if array accessing is actually a bottleneck in the code.
Manual
Each array dimension has a lower and an upper bound. Both can either be fixed or flexible. Fixed bounds are the same for all objects of this type, flexible bounds have to be chosen when the array is allocated. Julia's Array
types correspond to FastArray
types where all lower bounds are fixed to 1
, and all upper bounds are flexible.
Internally, the fixed bounds are represented as a tuple of either nothing or integers: DimSpec = NTuple{2, Union{Void, Int}}
.
For each dimension, the fixed bounds are set via:
 A range
lb:ub
to define both bounds  An integer
lb
to define the lower bound, leaving the upper bound flexible  A colon
:
to indicate that both lower and upper bounds are flexible Instead of the above, you can also define the fixed bounds via a tuple of typeDimSpec
.
When an array is allocated, the flexible bounds are set conversely:
 A colon
:
indicates no flexible bounds (if both bounds are fixed)  An integer
ub
defines the upper bound (if the lower bound is fixed)  A range
lb:ub
defines both lower and upper bounds  A oneelement integer tuple
(lb,)
defines the lower bound (if the upper bound is fixed)
Each fast array type is subtype of AbstractFastArray{T,N}
, where T
is the element type and N
is the rank. This abstract is a subtype of DenseArray{T,N}
.
Available array functions:

Define a fast array type:
FastArray(<dimspec>*)
Example:
const MyArrayType = FastArray(1:2, 1, :)

Allocate an array:
FastArray(<dimspec>*){<type>}(undef, <flexible bounds>*)
Example: Create an array with bounds
(1:2, 1:10, 1:10)
:myarray = MyArrayType{Float64}(undef, :, 10, 1:10)

Element type:
eltype{T}(arr::FastArray(...){T}) eltype{T}(::Type{FastArray(...){T}})
Example:
eltype(myarray) eltype(MyArrayType)

Rank (number of dimension):
ndims{T}(arr::FastArray(...){T}) ndims{T}(::Type{FastArray(...){T}})
Example:
ndims(myarray) ndims(MyArrayType)

Array length:
length{T}(arr::FastArray(...){T})
If all bounds are fixed, then the array length can also be obtained from the type:
length{T}(::Type{FastArray(...){T}})
Example:
length(myarray)

Array bounds and sizes:
lbnd{T}(arr::FastArray(...){T}, n::Int) ubnd{T}(arr::FastArray(...){T}, n::Int) size{T}(arr::FastArray(...){T}, n::Int) lbnd{T}(arr::FastArray(...){T}) ubnd{T}(arr::FastArray(...){T}) size{T}(arr::FastArray(...){T})
Fixed bounds and sizes can also be obtained from the type:
lbnd{T,n}(::Type{FastArray(...){T}}, ::Val{n}) ubnd{T,n}(::Type{FastArray(...){T}}, ::Val{n}) size{T,n}(::Type{FastArray(...){T}}, ::Val{n}) lbnd{T}(::Type{FastArray(...){T}}) ubnd{T}(::Type{FastArray(...){T}}) size{T}(::Type{FastArray(...){T}})
Example:
lbnd(myarray, 3) ubnd(myarray, Val{1}) size(myarray, 2) lbnd(myarray) ubnd(myarray) size(myarray)

Access array elements:
getindex{T}(arr::FastArray(...){T}, i::Int, j::Int, ...) setindex!{T}(arr::FastArray(...){T}, val, i::Int, j::Int, ...)
Example:
myarray[1,2,3] myarray[2,3,4] = 42