The AbstractVectorOfArray and AbstractDiffEqArray Interfaces

AbstractVectorOfArray is an extension point for array containers that store a collection of inner arrays. A concrete subtype must also satisfy the relevant AbstractArray interface: provide size, getindex, and setindex! when the storage is mutable, and use an indexable u field for the inner arrays. The last index selects an entry of u; A[:, j] must agree with A.u[j] for vector-valued inner arrays. Rectangular implementations may expose the ordinary array shape, while ragged input uses the maximum shape with zero values outside each inner array's stored bounds.

Generic operations such as Array(A), recursivecopy!, recursivecopyto!, recursivefill!, and vecarr_to_vectors rely only on that contract. Code that needs the stored inner arrays directly should use A.u, not iteration over A: iteration follows the scalar AbstractArray interface.

AbstractDiffEqArray adds aligned t, p, and sys metadata to the same contract. A concrete subtype must keep length(A.t) == length(A.u) under mutation. When A.interp !== nothing, calling A(t; idxs = ..., continuity = ...) forwards to A.interp(t, idxs, deriv, A.p, continuity); otherwise the call reports that interpolation data is unavailable.

RecursiveArrayTools.AbstractVectorOfArray — Type
AbstractVectorOfArray{T, N, A}

An AbstractVectorOfArray is an object which represents arrays of arrays, and arbitrary recursive nesting of arrays, as a single array-like object. Thus a canonical example of an AbstractVectorOfArray is something of the form VectorOfArray([[1,2],[3,4]]), which "acts" like the matrix [1 3; 2 4] where the data is stored and accessed in a column-ordered fashion (as is typical in Julia), but the actual matrix is never constructed and instead lazily represented through the type.

An AbstractVectorOfArray subtype should match the following behaviors.

Note

As of v4.0, AbstractVectorOfArray <: AbstractArray. Linear indexing A[i] now returns the ith element in column-major order, matching standard Julia AbstractArray behavior. To access the ith inner array, use A.u[i] or A[:, i]. For ragged arrays (inner arrays of different sizes), size(A) reports the maximum size in each dimension and out-of-bounds elements are interpreted as zero (sparse representation). This means all standard linear algebra operations work out of the box.

Fields

Concrete subtypes must expose the following field:

  • u, an indexable collection holding the inner arrays. The last index of the array interface selects an entry of u.

Concrete subtypes must also implement size, getindex, and setindex! as appropriate for their storage. They must preserve the column-major convention: A[I..., j] indexes A.u[j], and A[:, j] returns that inner array (or its rectangular, zero-padded view when the inner arrays are ragged). The generic copying, filling, conversion, and component-series functions in RecursiveArrayTools are defined in terms of this contract.

Array Interface

The general operations are as follows. Use

A.u[j]

to access the jth array. For multidimensional systems, this will address first by component and lastly by time, and thus

A[i, j]

will be the ith component at array j. Hence, A[j][i] == A[i, j]. This is done because Julia is column-major, so the leading dimension should be contiguous in memory. If the independent variables had shape (for example, was a matrix), then i is the linear index. We can also access solutions with shape:

A[i, k, j]

gives the [i,k] component of the system at array j. The colon operator is supported, meaning that

A[i, :]

gives the timeseries for the ith component.

Using the AbstractArray Interface

The AbstractArray interface can be directly used. For example, for a vector system of variables A[i,j] is a matrix with rows being the variables and columns being the timepoints. Operations like A' will transpose the solution type. Functionality written for AbstractArrays can directly use this. For example, the Base cov function computes correlations amongst columns, and thus:

cov(A)

computes the correlation of the system state in time, whereas

cov(A, 2)

computes the correlation between the variables. Similarly, mean(A,2) is the mean of the variable in time, and var(A,2) is the variance. Other statistical functions and packages which work on AbstractArray types will work on the solution type.

Conversions

At anytime, a true Array can be created using Array(A), or more generally stack(A) to make the array type match the internal array type (for example, if A is an array of GPU arrays, stack(A) will be a GPU array).

source
RecursiveArrayTools.AbstractDiffEqArray — Type
AbstractDiffEqArray{T, N, A} <: AbstractVectorOfArray{T, N, A}

An AbstractVectorOfArray object which has extra information of a time array A.t in order to specify a time series. A canonical AbstractDiffEqArray is for example the pairing DiffEqArray([[1,2],[3,4]],[1.0,2.0]) which means that at time 1.0 the values were [1,2] and at time 2.0 the values were [3,4].

An AbstractDiffEqArray has all of the same behaviors as an AbstractVectorOfArray with the additional properties:

Fields

Concrete subtypes of AbstractDiffEqArray add the following fields:

  • t, the time corresponding to each entry of u.
  • p, the parameter values associated with the saved states.
  • sys, symbolic-indexing metadata such as a SymbolCache, or nothing.
  • discretes, discrete parameter timeseries, or nothing.
  • interp, an interpolation object for dense output, or nothing.
  • dense, whether dense interpolation is available.

The lengths of u and t must stay aligned when the container is resized or otherwise mutated. A subtype with a non-nothinginterp field must accept the callable interface below; otherwise calling it should report that no interpolation data is available.

Callable Interface

When interp is not nothing, the array supports callable syntax for interpolation:

da(t)                           # interpolate at time t
da(t, Val{1})                   # first derivative at time t
da(t; idxs=1)                   # interpolate component 1
da(t; idxs=[1,2])              # interpolate components 1 and 2
da(t; continuity=:right)        # right-continuity at discontinuities

The interpolation object is called as interp(t, idxs, deriv, p, continuity).

source