Skip to content

Commit a480871

Browse files
Merge pull request #439 from SciML/ChrisRackauckas-patch-1
More precise deprecations of linear indexing and add an interface definition page
2 parents 074074d + 50b064f commit a480871

File tree

4 files changed

+140
-14
lines changed

4 files changed

+140
-14
lines changed

docs/pages.jl

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
pages = [
44
"Home" => "index.md",
5+
"AbstractVectorOfArrayInterface.md",
56
"array_types.md",
67
"recursive_array_functions.md"
78
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The AbstractVectorOfArray and AbstractDiffEqArray Interfaces
2+
3+
```@doc
4+
AbstractVectorOfArray
5+
AbstractDiffEqArray
6+
```

src/RecursiveArrayTools.jl

+113
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,120 @@ import Adapt
1313

1414
import Tables, IteratorInterfaceExtensions
1515

16+
"""
17+
AbstractVectorOfArray{T, N, A}
18+
19+
An AbstractVectorOfArray is an object which represents arrays of arrays,
20+
and arbitrary recursive nesting of arrays, as a single array-like object.
21+
Thus a canonical example of an AbstractVectorOfArray is something of the
22+
form `VectorOfArray([[1,2],[3,4]])`, which "acts" like the matrix [1 3; 2 4]
23+
where the data is stored and accessed in a column-ordered fashion (as is typical
24+
in Julia), but the actual matrix is never constructed and instead lazily represented
25+
through the type.
26+
27+
An AbstractVectorOfArray subtype should match the following behaviors.
28+
29+
!!! note
30+
31+
In 2023 the linear indexing `A[i]`` was deprecated. It previously had the behavior that
32+
`A[i] = A.u[i]`. However, this is incompatible with standard `AbstractArray` interfaces,
33+
Since if `A = VectorOfArray([[1,2],[3,4]])` and `A` is supposed to act like `[1 3; 2 4]`,
34+
then there is a difference `A[1] = [1,2]` for the VectorOfArray while `A[1] = 1` for the
35+
matrix. This causes many issues if `AbstractVectorOfArray <: AbstractArray`. Thus we
36+
plan in 2026 to complete the deprecation and thus have a breaking update where `A[i]`
37+
matches the linear indexing of an `AbstractArray`, and then making
38+
`AbstractVectorOfArray <: AbstractArray`. Until then, `AbstractVectorOfArray` due to
39+
this interface break but manaully implements an AbstractArray-like interface for
40+
future compatability.
41+
42+
## Fields
43+
44+
An AbstractVectorOfArray has the following fields:
45+
46+
* `u` which holds the Vector of values at each timestep
47+
48+
## Array Interface
49+
50+
The general operations are as follows. Use
51+
52+
```julia
53+
A.u[j]
54+
```
55+
56+
to access the `j`th array. For multidimensional systems, this
57+
will address first by component and lastly by time, and thus
58+
59+
```julia
60+
A[i, j]
61+
```
62+
63+
will be the `i`th component at array `j`. Hence, `A[j][i] == A[i, j]`. This is done
64+
because Julia is column-major, so the leading dimension should be contiguous in memory.
65+
If the independent variables had shape (for example, was a matrix), then `i` is the
66+
linear index. We can also access solutions with shape:
67+
68+
```julia
69+
A[i, k, j]
70+
```
71+
72+
gives the `[i,k]` component of the system at array `j`. The colon operator is
73+
supported, meaning that
74+
75+
```julia
76+
A[i, :]
77+
```
78+
79+
gives the timeseries for the `i`th component.
80+
81+
## Using the AbstractArray Interface
82+
83+
The `AbstractArray` interface can be directly used. For example, for a vector
84+
system of variables `A[i,j]` is a matrix with rows being the variables and
85+
columns being the timepoints. Operations like `A'` will
86+
transpose the solution type. Functionality written for `AbstractArray`s can
87+
directly use this. For example, the Base `cov` function computes correlations
88+
amongst columns, and thus:
89+
90+
```julia
91+
cov(A)
92+
```
93+
94+
computes the correlation of the system state in time, whereas
95+
96+
```julia
97+
cov(A, 2)
98+
```
99+
100+
computes the correlation between the variables. Similarly, `mean(A,2)` is the
101+
mean of the variable in time, and `var(A,2)` is the variance. Other statistical
102+
functions and packages which work on `AbstractArray` types will work on the
103+
solution type.
104+
105+
## Conversions
106+
107+
At anytime, a true `Array` can be created using `Array(A)`, or more generally `stack(A)`
108+
to make the array type match the internal array type (for example, if `A` is an array
109+
of GPU arrays, `stack(A)` will be a GPU array).
110+
"""
16111
abstract type AbstractVectorOfArray{T, N, A} end
112+
113+
"""
114+
AbstractDiffEqArray{T, N, A} <: AbstractVectorOfArray{T, N, A}
115+
116+
An AbstractVectorOfArray object which has extra information of a time array `A.t`
117+
in order to specify a time series. A canonical AbstractDiffEqArray is for example
118+
the pairing `DiffEqArray([[1,2],[3,4]],[1.0,2.0])` which means that at time 1.0
119+
the values were `[1,2]` and at time 2.0 the values were `[3,4]`.
120+
121+
An AbstractDiffEqArray has all of the same behaviors as an AbstractVectorOfArray with the
122+
additional properties:
123+
124+
## Fields
125+
126+
An AbstractDiffEqArray adds the following fields:
127+
128+
* `t` which holds the times of each timestep.
129+
"""
17130
abstract type AbstractDiffEqArray{T, N, A} <: AbstractVectorOfArray{T, N, A} end
18131

19132
include("utils.jl")

src/vector_of_array.jl

+20-14
Original file line numberDiff line numberDiff line change
@@ -388,29 +388,32 @@ end
388388
@inline Base.IteratorSize(::Type{<:AbstractVectorOfArray}) = Base.HasLength()
389389
@inline Base.first(VA::AbstractVectorOfArray) = first(VA.u)
390390
@inline Base.last(VA::AbstractVectorOfArray) = last(VA.u)
391-
function Base.firstindex(VA::AbstractVectorOfArray)
392-
Base.depwarn(
391+
function Base.firstindex(VA::AbstractVectorOfArray{T,N,A}) where {T,N,A}
392+
N > 1 && Base.depwarn(
393393
"Linear indexing of `AbstractVectorOfArray` is deprecated. Change `A[i]` to `A.u[i]` ",
394394
:firstindex)
395395
return firstindex(VA.u)
396396
end
397397

398-
function Base.lastindex(VA::AbstractVectorOfArray)
399-
Base.depwarn(
398+
function Base.lastindex(VA::AbstractVectorOfArray{T,N,A}) where {T,N,A}
399+
N > 1 && Base.depwarn(
400400
"Linear indexing of `AbstractVectorOfArray` is deprecated. Change `A[i]` to `A.u[i]` ",
401401
:lastindex)
402402
return lastindex(VA.u)
403403
end
404404

405-
@deprecate Base.getindex(A::AbstractVectorOfArray, I::Int) Base.getindex(A, :, I) false
405+
Base.getindex(A::AbstractVectorOfArray, I::Int) = A.u[I]
406+
Base.getindex(A::AbstractVectorOfArray, I::AbstractArray{Int}) = A.u[I]
407+
Base.getindex(A::AbstractDiffEqArray, I::Int) = A.u[I]
408+
Base.getindex(A::AbstractDiffEqArray, I::AbstractArray{Int}) = A.u[I]
406409

407-
@deprecate Base.getindex(A::AbstractVectorOfArray, I::AbstractArray{Int}) Base.getindex(
408-
A, :, I) false
410+
@deprecate Base.getindex(VA::AbstractVectorOfArray{T,N,A}, I::Int) where {T,N,A<:Union{AbstractArray, AbstractVectorOfArray}} VA.u[I] false
409411

410-
@deprecate Base.getindex(A::AbstractDiffEqArray, I::AbstractArray{Int}) Base.getindex(
411-
A, :, I) false
412+
@deprecate Base.getindex(VA::AbstractVectorOfArray{T,N,A}, I::AbstractArray{Int}) where {T,N,A<:Union{AbstractArray, AbstractVectorOfArray}} VA.u[I] false
412413

413-
@deprecate Base.getindex(A::AbstractDiffEqArray, i::Int) Base.getindex(A, :, i) false
414+
@deprecate Base.getindex(VA::AbstractDiffEqArray{T,N,A}, I::AbstractArray{Int}) where {T,N,A<:Union{AbstractArray, AbstractVectorOfArray}} VA.u[I] false
415+
416+
@deprecate Base.getindex(VA::AbstractDiffEqArray{T,N,A}, i::Int) where {T,N,A<:Union{AbstractArray, AbstractVectorOfArray}} VA.u[i] false
414417

415418
__parameterless_type(T) = Base.typename(T).wrapper
416419

@@ -520,22 +523,25 @@ Base.@propagate_inbounds function Base.setindex!(VA::AbstractVectorOfArray{T, N}
520523
VA.u[I] = v
521524
end
522525

523-
@deprecate Base.setindex!(VA::AbstractVectorOfArray, v, I::Int) Base.setindex!(VA, v, :, I) false
526+
Base.@propagate_inbounds Base.setindex!(VA::AbstractVectorOfArray, v, I::Int) = Base.setindex!(VA.u, v, I)
527+
@deprecate Base.setindex!(VA::AbstractVectorOfArray{T,N,A}, v, I::Int) where {T,N,A<:Union{AbstractArray, AbstractVectorOfArray}} Base.setindex!(VA.u, v, I) false
524528

525529
Base.@propagate_inbounds function Base.setindex!(VA::AbstractVectorOfArray{T, N}, v,
526530
::Colon, I::Colon) where {T, N}
527531
VA.u[I] = v
528532
end
529533

530-
@deprecate Base.setindex!(VA::AbstractVectorOfArray, v, I::Colon) Base.setindex!(
531-
VA, v, :, I) false
534+
Base.@propagate_inbounds Base.setindex!(VA::AbstractVectorOfArray, v, I::Colon) = Base.setindex!(VA.u, v, I)
535+
@deprecate Base.setindex!(VA::AbstractVectorOfArray{T,N,A}, v, I::Colon) where {T,N,A<:Union{AbstractArray, AbstractVectorOfArray}} Base.setindex!(
536+
VA.u, v, I) false
532537

533538
Base.@propagate_inbounds function Base.setindex!(VA::AbstractVectorOfArray{T, N}, v,
534539
::Colon, I::AbstractArray{Int}) where {T, N}
535540
VA.u[I] = v
536541
end
537542

538-
@deprecate Base.setindex!(VA::AbstractVectorOfArray, v, I::AbstractArray{Int}) Base.setindex!(
543+
Base.@propagate_inbounds Base.setindex!(VA::AbstractVectorOfArray, v, I::AbstractArray{Int}) = Base.setindex!(VA.u, v, I)
544+
@deprecate Base.setindex!(VA::AbstractVectorOfArray{T,N,A}, v, I::AbstractArray{Int}) where {T,N,A<:Union{AbstractArray, AbstractVectorOfArray}} Base.setindex!(
539545
VA, v, :, I) false
540546

541547
Base.@propagate_inbounds function Base.setindex!(

0 commit comments

Comments
 (0)