BREAKING: Make AbstractVectorOfArray <: AbstractArray#547
Conversation
Invalidation Analysis & Cleanup (second commit)Invalidation Results8 invalidation trees, all minimal:
Removed Redundant OverridesAll of these are now inherited from
Zygote Fixes
|
Subpackage for VA/AP constructors (cc @rokke-git)The The using RecursiveArrayToolsShorthandConstructorsThis also moves the Result: |
| Base.any(f, A::ArrayPartition) = any((any(f, x) for x in A.x)) | ||
| Base.any(f::Function, A::ArrayPartition) = any((any(f, x) for x in A.x)) | ||
| Base.any(A::ArrayPartition) = any(identity, A) | ||
| Base.all(f, A::ArrayPartition) = all((all(f, x) for x in A.x)) | ||
| Base.all(f::Function, A::ArrayPartition) = all((all(f, x) for x in A.x)) | ||
| Base.all(A::ArrayPartition) = all(identity, A) |
There was a problem hiding this comment.
Why is this getting deleted again? I thought that idea was rejected in #540 .
There was a problem hiding this comment.
It's a 1.5x optimization that leads to major compile time issues. I think the good solution here is to make it a sublibrary that people can opt into, but not force it to always exist. If people need this optimization, then they can take the compile time hit, but this allows most libraries to avoid the major invalidations.
There was a problem hiding this comment.
Are you okay with that solution? It seems best for the mix of use cases.
There was a problem hiding this comment.
OK, if it's still in a subpackage then it's good enough for me.
|
You probably already planned it like this, but I want to mention it anyways: Currently, there are some unreleased commits on master. It would be good to create a (probably final) release of v3, such that the change to v4 only includes the breaking change(s), but not the other non-breaking changes. |
|
Oh good point. I'll make sure to do that. |
|
I am testing this branch right now in JoshuaLampert/SimpleDiscontinuousGalerkin.jl#74 to see what breaks because in that package I used some complicated julia> f = VectorOfArray([[1.0], [2.0, 3.0]])
VectorOfArray{Float64,2}:
2-element Vector{Vector{Float64}}:
[1.0]
[2.0, 3.0]
julia> f .= zero(f)
ERROR: DimensionMismatch: destination axes (Base.OneTo(2),) are not compatible with source axes (Base.OneTo(1),)
Stacktrace:
[1] throwdm(axdest::Tuple{Base.OneTo{Int64}}, axsrc::Tuple{Base.OneTo{Int64}})
@ Base.Broadcast ./broadcast.jl:1086
[2] copyto!
@ ./broadcast.jl:983 [inlined]
[3] copyto!
@ ./broadcast.jl:947 [inlined]
[4] copyto!
@ ~/.julia/dev/RecursiveArrayTools/src/vector_of_array.jl:1174 [inlined]
[5] materialize!
@ ./broadcast.jl:905 [inlined]
[6] materialize!(dest::VectorOfArray{Float64, 2, Vector{Vector{Float64}}}, bc::Base.Broadcast.Broadcasted{RecursiveArrayTools.VectorOfArrayStyle{2}, Nothing, typeof(identity), Tuple{VectorOfArray{Float64, 2, Vector{Vector{…}}}}})
@ Base.Broadcast ./broadcast.jl:902
[7] top-level scope
@ REPL[24]:1
Some type information was truncated. Use `show(err)` to see complete types. |
|
I am also not sure about the zero-padding. If I have a ragged array, I usually would not want to have padded zeros. Note that ragged array are the natural data structures for finite element methods with p-adaptivity, where you have different polynomial degrees (and therefore different number of DOFs) in each element. When I want to index into an element, e.g., via |
|
I think we need zero-padding as the default way to do things for the ODESolution because (a) 99.99% or something wild of use cases are non-ragged, and (b) the ragged indexing adds major invalidations which slows down everybody by like 10 seconds while making automatic differentiation have a bunch of small bugs that are difficult to work out (since AD is generally written to assume indexing matches the AbstractArray interface and its unwritten assumptions, one of which is the sizing assumption. That last part then compounds the issue, because what happens is that size either needs to be defined small (i.e. the minimum size, or the size of the first vector), in which case AD overloads build a smaller matrix which succeeds but can give silent incorrect results, or it's defined using the maximum without zero padding, which then makes AD just error. We can then fix this by overloading every operation, which over time we did get a decent amount of coverage for people to not really realize how crazy this is, but it never got full coverage (for example, Cholesky still does weird things) and the more overloads we add, the more invalidations we get from it. We had to make it all not an AbstractArray to ensure we got more correctness but, ultimately it's not the right path for most people. That said, there are some nice use cases for it. And we did battle test it enough that it ended up being quite a useful interface. So what I can do is keep it alive as a sublibrary as well, again with the other pieces a sublibrary to prevent the invalidations from being in the main path, but make it easy to convert the type into this form and it would just give it different actions. Indeed adaptive meshing is probably the one major use case of that form which always wants to keep the ragged form. |
|
I understand and agree that ragged arrays are not that common and am fine with zero-padding by default to avoid the issues you mention as long as there exists a way to opt-in the current interface. Keeping the possibility of non-padded ragged arrays alive by adding a sublibrary and making it easy to convert the type sounds good to me. |
New commit: Ragged arrays, interpolation interface, and plotting delegationRagged Arrays (
|
Project.toml
Outdated
| LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" | ||
| PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" | ||
| RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" | ||
| SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" |
There was a problem hiding this comment.
| SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" |
Should RecursiveArrayTools.jl really depend on SciMLBase.jl? As far as I can see it is not needed and it leads to circular dependencies since SciMLBase.jl depends on RecursiveArrayTools.jl.
|
I just tried some of the tests specifically for ragged arrays we had before using the new julia> u = RaggedVectorOfArray([[1.0], [2.0, 3.0]])
RaggedVectorOfArray{Float64,2}:
2-element Vector{Vector{Float64}}:
[1.0]
[2.0, 3.0]
julia> @test length(view(u, :, 1)) == 1
Error During Test at REPL[19]:1
Test threw exception
Expression: length(view(u, :, 1)) == 1
MethodError: no method matching view(::RaggedVectorOfArray{Float64, 2, Vector{Vector{Float64}}}, ::Colon, ::Int64)
The function `view` exists, but no method is defined for this combination of argument types.
Closest candidates are:
view(::AbstractVectorOfArray{T, N, <:AbstractVector{T}}, ::Any...) where {T, N, M}
@ RecursiveArrayTools ~/.julia/dev/RecursiveArrayTools/src/vector_of_array.jl:986
view(::AbstractVectorOfArray, ::Any...) where M
@ RecursiveArrayTools ~/.julia/dev/RecursiveArrayTools/src/vector_of_array.jl:1001
view(::AbstractArray, ::Any...) where M
@ Base subarray.jl:211
...
Stacktrace:
[1] top-level scope
@ REPL[19]:1
[2] macro expansion
@ ~/.julia/juliaup/julia-1.12.5+0.x64.linux.gnu/share/julia/stdlib/v1.12/Test/src/Test.jl:677 [inlined]
ERROR: There was an error during testing
julia> @test length(view(u, :, 2)) == 2
Error During Test at REPL[20]:1
Test threw exception
Expression: length(view(u, :, 2)) == 2
MethodError: no method matching view(::RaggedVectorOfArray{Float64, 2, Vector{Vector{Float64}}}, ::Colon, ::Int64)
The function `view` exists, but no method is defined for this combination of argument types.
Closest candidates are:
view(::AbstractVectorOfArray{T, N, <:AbstractVector{T}}, ::Any...) where {T, N, M}
@ RecursiveArrayTools ~/.julia/dev/RecursiveArrayTools/src/vector_of_array.jl:986
view(::AbstractVectorOfArray, ::Any...) where M
@ RecursiveArrayTools ~/.julia/dev/RecursiveArrayTools/src/vector_of_array.jl:1001
view(::AbstractArray, ::Any...) where M
@ Base subarray.jl:211
...
Stacktrace:
[1] top-level scope
@ REPL[20]:1
[2] macro expansion
@ ~/.julia/juliaup/julia-1.12.5+0.x64.linux.gnu/share/julia/stdlib/v1.12/Test/src/Test.jl:677 [inlined]
ERROR: There was an error during testing
julia> u = RaggedVectorOfArray([zeros(1, n) for n in (2, 3)])
RaggedVectorOfArray{Float64,3}:
2-element Vector{Matrix{Float64}}:
[0.0 0.0]
[0.0 0.0 0.0]
julia> u[1, :, 2] .= [1.0, 2.0, 3.0]
ERROR: MethodError: no method matching getindex(::RaggedVectorOfArray{Float64, 3, Vector{Matrix{Float64}}}, ::Int64, ::Colon, ::Int64)
The function `getindex` exists, but no method is defined for this combination of argument types.
Closest candidates are:
getindex(::RaggedVectorOfArray{T, N}, ::Int64...) where {T, N}
@ RecursiveArrayToolsRaggedArrays ~/.julia/dev/RecursiveArrayTools/lib/RecursiveArrayToolsRaggedArrays/src/RecursiveArrayToolsRaggedArrays.jl:360
getindex(::RaggedVectorOfArray, ::Int64, ::Colon)
@ RecursiveArrayToolsRaggedArrays ~/.julia/dev/RecursiveArrayTools/lib/RecursiveArrayToolsRaggedArrays/src/RecursiveArrayToolsRaggedArrays.jl:418
getindex(::RaggedVectorOfArray{T, N}, ::Int64) where {T, N}
@ RecursiveArrayToolsRaggedArrays ~/.julia/dev/RecursiveArrayTools/lib/RecursiveArrayToolsRaggedArrays/src/RecursiveArrayToolsRaggedArrays.jl:321
...
Stacktrace:
[1] maybeview(::RaggedVectorOfArray{Float64, 3, Vector{Matrix{Float64}}}, ::Int64, ::Function, ::Vararg{Any})
@ Base ./views.jl:148
[2] dotview(::RaggedVectorOfArray{Float64, 3, Vector{Matrix{Float64}}}, ::Int64, ::Function, ::Vararg{Any})
@ Base.Broadcast ./broadcast.jl:1250
[3] top-level scope
@ REPL[22]:1
I stopped trying out more tests. So there are likely more. I would just suggest to keep the tests, which used ragged arrays before and now test them for |
|
@JoshuaLampert Thanks for testing — fixed in the latest push:
One difference from v3: 243 tests pass now including ported v3 ragged behavior tests. |
…liance
This is a breaking change that completes the long-planned migration (documented
since 2023) to make AbstractVectorOfArray a proper AbstractArray subtype.
Key changes:
- AbstractVectorOfArray{T,N,A} <: AbstractArray{T,N}
- Linear indexing A[i] now returns the i-th element in column-major order
(previously returned A.u[i], the i-th inner array)
- size() uses maximum sizes across inner arrays for ragged data
- Ragged out-of-bounds elements are treated as zero (sparse interpretation)
- Iteration goes over scalar elements (AbstractArray default)
- Removed deprecated linear indexing methods
- Updated Zygote extension (some adjoints marked broken pending update)
- Added parameter_values(::AbstractDiffEqArray, i) to fix dispatch ambiguity
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Invalidation analysis and cleanup: - Remove IndexStyle instance method (type method sufficient) - Remove size(VA, i), lastindex(VA, d) (inherited from AbstractArray) - Remove checkbounds override (inherited from AbstractArray via size) - Remove isassigned, isapprox, CartesianIndices, adjoint overrides - Remove reshape, vec, convert(Array, ...), maybeview overrides - Remove +, -, *, / operator overrides (use broadcasting) - Remove 2-arg show for AbstractVectorOfArray (use AbstractArray display) Fix Zygote extension: - Remove all getindex/view adjoint overrides (Zygote's AbstractArray rules apply) - Fix VectorOfArray(u) adjoint to return .u (plain Vector) not VectorOfArray - Fix DiffEqArray(u, t) adjoint similarly - All 12 adjoint tests now pass (was 4 pass + 8 broken) Invalidation trees: 8 total, all minimal (max 20 mt_backedges from Colon(::Integer, ::RaggedEnd) which is inherent to the type) Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Massive cleanup removing code that is no longer reachable or duplicates
AbstractArray defaults:
Removed (dead code since lastindex returns plain Ints):
- RaggedEnd/RaggedRange structs and all Colon methods (caused 4 invalidations)
- _column_indices, _resolve_ragged_index, _resolve_ragged_indices
- _has_ragged_end, _resolve_ragged_end_args
- _ragged_getindex, _ragged_getindex_nm1dims, _ragged_getindex_full
- _checkbounds_ragged, _preserve_array_type, _padded_resolved_indices
- _is_ragged_dim, __parameterless_type
Removed (inherited from AbstractArray):
- size(::Adjoint{T, <:AbstractVectorOfArray}), getindex(::Adjoint{...})
- check_parent_index_match, SubArray constructor override
- CartesianIndex setindex! (handled by Int... method)
- broadcastable (AbstractArray is already broadcastable)
- mapreduce for N==1 case
- show(io, ::AbstractVectorOfArray) 2-arg (use AbstractArray display)
Added back:
- getindex/setindex! for CartesianIndex with more dims than ndims(A)
(needed for heterogeneous inner arrays like [zeros(20), zeros(3,3,3)])
Invalidation trees: 8 → 4 (removed all Colon/RaggedEnd invalidations)
Net: -379 lines removed, +43 added
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…alidation This method conflicted with AbstractArray's similar(a, dims...) contract and caused 469 invalidation children. Users should use standard similar(VA, T, dims) or construct VectorOfArrays directly. Invalidation trees: 4 → 3 (and 0 from AbstractVectorOfArray itself — all 3 remaining are pre-existing from ArrayPartition/NamedArrayPartition) Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create lib/RecursiveArrayToolsShorthandConstructors as a separate subpackage for methods that cause invalidations: - VA[...] and AP[...] shorthand constructors (getindex on Type) - Optimized any/all for ArrayPartition (partition-level short-circuit) Users who want these opt in with: using RecursiveArrayToolsShorthandConstructors Invalidation trees after `using RecursiveArrayTools`: 3 → 1 (only NamedArrayPartition.setindex! remains, pre-existing) Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- NamedArrayPartition: narrow setindex!(x, args...) to setindex!(x, v, i::Int) instead of catching all signatures. AbstractVector only needs the Int method. - Remove setindex! CartesianIndex from Union in multi-index method (Base handles it) - Remove dedicated (Int, CartesianIndex) getindex/setindex! methods; flatten inside the multi-arg dispatcher instead - Fix NamedArrayPartition test: x[1:end] now preserves type (correct behavior) Result: `using RecursiveArrayTools` causes 0 invalidation trees. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RecursiveArrayToolsShorthandConstructors now only has the VA[...] and AP[...] constructor syntax. The any/all partition-level optimizations are in their own RecursiveArrayToolsArrayPartitionOptimizations subpackage since they are unrelated to shorthand constructors. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The partition-level any/all short-circuiting is not actually an optimization — the AbstractArray fallback already short-circuits element-by-element. Both stop at the first match. Iterating partition-by-partition doesn't skip any work. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Benchmarking shows the partition-by-partition any/all IS ~1.5-1.8x faster than the AbstractArray default for full scans, because ArrayPartition's generic element iteration has per-element partition lookup overhead. Kept as a separate lib/ subpackage (not a submodule) because submodules are loaded at package load time and would still cause invalidations. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Since AbstractVectorOfArray <: AbstractArray, convert(AbstractArray, va) returns va itself. Use stack(va.u) to get a dense CuArray instead. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…legation
## Ragged Arrays (RecursiveArrayToolsRaggedArrays sublibrary)
- Add AbstractRaggedVectorOfArray and AbstractRaggedDiffEqArray abstract types
- Add RaggedVectorOfArray and RaggedDiffEqArray concrete types that preserve
true ragged structure without zero-padding
- Column-major linear indexing, no-padding A[:, i], symbolic indexing,
broadcasting, and callable interpolation interface
- Isolated in lib/ sublibrary to avoid invalidations
## Interpolation Interface on DiffEqArray
- Add `interp` and `dense` fields to DiffEqArray (new type parameter I)
- Callable syntax: da(t), da(t; idxs=1), da(t, Val{1})
- All constructors accept `interp=nothing, dense=false` kwargs
## Plotting Delegation
- Full-featured AbstractDiffEqArray plot recipe with:
- idxs variable selection (integer, array, tuple for phase plots, symbolic)
- Dense interpolation via callable interface
- denseplot, plotdensity, tspan, plotat keyword arguments
- Export plotting helpers for SciMLBase delegation:
DEFAULT_PLOT_FUNC, plottable_indices, plot_indices, getindepsym_defaultt,
interpret_vars, add_labels!, diffeq_to_arrays, solplot_vecs_and_labels
## Bug Fix
- Fix ragged VectorOfArray .= zero broadcast (DimensionMismatch)
by using dest.u[i] instead of dest[:, i] in copyto!
## Version
- Bump to v4.0.0
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Style Change RaggedVectorOfArrayStyle to subtype BroadcastStyle instead of AbstractArrayStyle. This eliminates all invalidations from the ragged sublibrary except the unavoidable 1 from materialize (needed because non-AbstractArray types can't provide axes/size for instantiate). Invalidation breakdown for `using RecursiveArrayToolsRaggedArrays`: - 1 from our materialize override (unavoidable for non-AbstractArray) - 17 from Accessors.jl (SII transitive dep, not our code) - 20 from Base cat chain (SII transitive dep, not our code) Also update sublibrary compat bounds to RecursiveArrayTools v4. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the fast-path branch in the AbstractDiffEqArray recipe that bypassed diffeq_to_arrays when no interp was available. Now all cases go through the same code path, ensuring denseplot=true/false is always respected: - denseplot=true: generates plotdensity interpolated points via callable - denseplot=false: uses raw saved time points from A.t Also clean up SciMLBase that leaked into [deps] from Pkg.develop. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Disambiguate CuArray(::AbstractArray{T,N}) from CUDA.jl by defining
(::Type{GA})(::AbstractVectorOfArray{T,N}) where {T,N,GA<:AbstractGPUArray}
AbstractVectorOfArray{T,N} is strictly more specific than AbstractArray{T,N}
on arg2, so this method wins dispatch for VectorOfArray arguments.
Uses stack(VA.u) to stay on GPU (avoids GPU→CPU→GPU round-trip).
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The functor-style `(::Type{<:AbstractGPUArray})(::AbstractVectorOfArray)`
cannot resolve the ambiguity with CUDA's `CuArray(::AbstractArray{T,N})`
because neither method is uniformly more specific (arg1 vs arg2).
Fix: add a CUDA.jl extension that defines the exact disambiguation
Julia requests: `CuArray(::AbstractVectorOfArray{T,N}) where {T,N}`.
This is loaded only when CUDA is available and resolves the ambiguity
by being a direct method on the concrete `CuArray` type.
Uses stack(VA.u) to stay on GPU (avoids GPU→CPU→GPU round-trip).
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add @test_broken checks verifying that the optimized partition-level any/all methods (from RecursiveArrayToolsArrayPartitionAnyAll) are NOT active in the base package. Without the extension, any/all use the AbstractArray element-by-element fallback which is correct but slower. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ding Without RecursiveArrayToolsArrayPartitionAnyAll, any/all on ArrayPartition with GPU sub-arrays would trigger scalar indexing errors. The base package now defines any/all methods for ArrayPartition that: - Check for GPU sub-arrays and throw a helpful error directing users to load RecursiveArrayToolsArrayPartitionAnyAll - Fall through to AbstractArray element-by-element iteration for CPU arrays Also adds a Subpackages test group that verifies loading RecursiveArrayToolsArrayPartitionAnyAll correctly overrides the methods with optimized partition-level iteration. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Julia 1.13 (JuliaLang/julia#61184) removes the f::Function restriction from any/all, so defining any(f, ::ArrayPartition) no longer causes ~780 invalidations. On 1.13+, the optimized partition-level methods are defined directly in the main package. On older Julia, the GPU-check fallback remains and the RecursiveArrayToolsArrayPartitionAnyAll subpackage is needed for optimized methods. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cb29da4 to
a9edc84
Compare
The 1.13 inlining of optimized any/all should be a separate PR. Keep the clean split: RecursiveArrayToolsArrayPartitionAnyAll provides the optimized methods, Base's AbstractArray fallback is used without it. Tests verify dispatch goes to Base without extension, and the Subpackages test group verifies loading the extension overrides correctly. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The GPU test uses `all(pA .== true)` on ArrayPartition with CuArrays, which triggers scalar indexing via the AbstractArray element-by-element fallback. Fix by loading RecursiveArrayToolsArrayPartitionAnyAll in the GPU test environment, which provides partition-level any/all that dispatches to GPU kernels per sub-array. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
23df3f6 to
4e28332
Compare
|
Alright, I think this did it all. @JoshuaLampert and @mateuszbaran, do follow up if the ragged versions aren't matching the previous behavior. But I think we hit every requirement here using the sublibraries. |
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
a5c2379 to
484d3af
Compare
|
@JuliaRegistrator register Release notes: RecursiveArrayTools v4.0.0Breaking Changes
New Features
|
|
Error while trying to register: Register Failed |
|
@JuliaRegistrator register subdir=lib/RecursiveArrayToolsArrayPartitionAnyAll |
|
Error while trying to register: Register Failed |
|
@JuliaRegistrator register subdir=lib/RecursiveArrayToolsRaggedArrays |
|
Error while trying to register: Register Failed |
|
@JuliaRegistrator register subdir=lib/RecursiveArrayToolsShorthandConstructors |
|
Error while trying to register: Register Failed |
The ragged version does not match the previous behavior regarding the treatment of
What is the reason to let it throw a |
|
Okay easiest way to square it was to just make sure all old code and tests was revived #559 |
Summary
This PR completes the long-planned migration (documented in the codebase since 2023) to make
AbstractVectorOfArray{T, N, A} <: AbstractArray{T, N}. This is a breaking major update that changes fundamental indexing and iteration behavior.Breaking Changes
Linear Indexing
A[i]now returns theith element in column-major order (standardAbstractArraybehavior)A[i]returnedA.u[i](theith inner array)A.u[i]orA[:, i]to access inner arraysRagged Arrays (Sparse Rectangular Interpretation)
size(A)reports maximum size in each dimension across all inner arrayszero(T)(sparse representation)Array(A)on ragged data produces a zero-padded dense arrayIteration & Length
iterate(A)goes over scalar elements (column-major), not inner arrayslength(A)returnsprod(size(A)), notlength(A.u)first(A)/last(A)return scalar elements, not inner arraysA.ufor column-wise iteration,length(A.u)for countmapmap(f, A)maps over elements, not inner arraysmap(f, A.u)to map over inner arraysOther
eachindex(A)returnsCartesianIndices(size(A))convert(AbstractArray, A)is identity (A is already an AbstractArray)parameter_values(::AbstractDiffEqArray, i)now dispatches correctly (fixes ambiguity withparameter_values(::AbstractArray, i)from SymbolicIndexingInterface)Implementation Details
Core AbstractArray Interface
size(VA)usesmaximum(size(u, d) for u in VA.u)for each leading dimensiongetindex(A, I::Vararg{Int, N})checks ragged bounds and returnszero(T)for out-of-storage indicessetindex!throwsArgumentErrorwhen trying to set non-zero values outside ragged storageIndexStyleremainsIndexCartesian()CartesianIndexgetindex/setindex! handle ragged zero-paddingRemoved/Changed Methods
length,eachindex,iterate,first,last,firstindex,lastindexoverrides (inherited from AbstractArray)eltype,ndims,axesoverrides (inherited from AbstractArray)any,alloverrides (inherited from AbstractArray)sum,prod,mapreduceoverrides (inherited from AbstractArray)mapoverride (inherited from AbstractArray)@deprecatelinear indexing methods==(::AbstractVectorOfArray, ::AbstractArray)(AbstractArray default)convert(::Type{AbstractArray}, ...)(identity)append!usesnew_item.uinstead of iteratingrecursivefill!usesbs.uinstead of iteratingzerouses[zero(u) for u in VA.u]Zygote Extension
@test_brokenpending Zygote-specific updatesTest Plan
sum,prod,mapreducewithdimswork correctlyNote on Version Bump
This PR does not bump the version number so that CI tests can run on the current compat bounds. The version bump to v4.0.0 should be done when merging.
🤖 Generated with Claude Code