Skip to content

Implement the SciMLStructures interface for ArrayPartition - #643

Merged
ChrisRackauckas merged 4 commits into
SciML:masterfrom
AJ0070:feat/scimlstructures-arraypartition
Aug 12, 2026
Merged

Implement the SciMLStructures interface for ArrayPartition#643
ChrisRackauckas merged 4 commits into
SciML:masterfrom
AJ0070:feat/scimlstructures-arraypartition

Conversation

@AJ0070

@AJ0070 AJ0070 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Checklist

  • Appropriate tests were added
  • Any code changes were done in a way that does not break public API
  • All documentation related to code changes were updated
  • The new code follows the
    contributor guidelines, in particular the SciML Style Guide and
    COLPRAC.
  • Any new documentation only uses public API

Additional context

  • Implements the SciMLStructures interface for ArrayPartition, in src/scimlstructures.jl with SciMLStructures as a direct dependency.
  • SciMLStructures opts every numeric array into the interface and canonicalizes it with vec. That is right for a contiguous array, but an ArrayPartition is already an AbstractVector, so vec returns it unchanged and the buffer comes back unflattened. The generic repack then errors, and its message asks for exactly this: "Please define the SciMLStructures interface for this type."
  • Implements canonicalize, replace and replace! for the Tunable portion, laying the partitions out one after another so the flat buffer matches collect(p).
  • The buffer is reduce(vcat, p.x). The partitions need not share an array type or an element type, so taking the buffer's type from one of them is wrong: ArrayPartition([1, 2], [3.0, 4.5]) would give a Vector{Int} and truncate. Concatenating promotes across both, and keeps a uniformly static partitioning as an MVector.
  • reduce(vcat, (x,)) returns x itself, so a single partition is copied. Otherwise the buffer would alias p while canonicalize reports aliases = false. The test asserts this by mutating the buffer and checking the partitions are untouched.
  • replace takes new_values::AbstractArray rather than leaving it untyped, which would be ambiguous with the generic replace(::Tunable, ::AbstractArray, ::AbstractArray).
  • Tests cover the flattening order, repack and replace agreeing, replace! writing into the same partition arrays rather than replacing them, uneven, single and empty partitions, mixed element and array types, integer element types, and the five non-tunable portions reporting absent.
  • Motivated by Krylov: support an ArrayPartition right-hand side LinearSolve.jl#1206, where a Krylov solve on an ArrayPartition right-hand side needs a flat buffer and a way back. Related: isscimlstructure opts in numeric arrays whose repack then errors SciMLStructures.jl#79.
  • Core test group passes locally.

SciMLStructures opts every numeric array into the interface and
canonicalizes it with `vec`. That is right for a contiguous array, but an
`ArrayPartition` is already an `AbstractVector`, so `vec` returns it
unchanged and the buffer comes back unflattened. The generic `repack`
then errors, since an `ArrayPartition` cannot be rebuilt by a trivial
array constructor, and its message asks for exactly this.

Implements `canonicalize`, `replace` and `replace!` for the `Tunable`
portion, laying the partitions out one after another so the flat buffer
matches `collect(p)`.

The buffer is a copy, so `aliases` is `false`: the partitions are
separate arrays and no flat view over them exists. It is built from the
first partition rather than as a `Vector` so that an exotic backing type
stays itself.

`replace` takes `new_values::AbstractArray` rather than leaving it
untyped, which would be ambiguous with the generic
`replace(::Tunable, ::AbstractArray, ::AbstractArray)`.
@AJ0070

AJ0070 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas

isempty(p.x) && throw(
ArgumentError("cannot canonicalize an `ArrayPartition` with no partitions")
)
return copyto!(similar(first(p.x), length(p)), p)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It cannot assume that it all matches p.x[1] form.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reduce(vcat, p.x)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed this

Review feedback: sizing the buffer from `p.x[1]` assumed every partition
shares that one's array and element type. `ArrayPartition([1, 2], [3.0,
4.5])` gave a `Vector{Int}` buffer and truncated the second partition.

`reduce(vcat, p.x)` promotes across both, and keeps a uniformly static
partitioning as an `MVector` where sizing from the first partition
dropped it to a `Vector`.

`reduce(vcat, (x,))` returns `x` itself, so a single partition is copied
rather than handing back a buffer that aliases `p` while `canonicalize`
reports `aliases = false`.
@AJ0070

AJ0070 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas

Comment thread Project.toml Outdated
MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca"
Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588"
ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267"
SciMLStructures = "53ae85a6-f571-4167-b2af-e1d143709226"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just make a dep it's small

Review feedback: the package is small, so the interface moves from
`ext/RecursiveArrayToolsSciMLStructuresExt.jl` into
`src/scimlstructures.jl` and SciMLStructures becomes a direct dependency.
@AJ0070

AJ0070 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas both addressed.

reduce(vcat, p.x) for the flattening. It turned out to be a real bug rather than only a style point: sizing from p.x[1] gave a Vector{Int} for ArrayPartition([1, 2], [3.0, 4.5]) and truncated the second partition. Concatenating promotes across differing element and array types, and keeps a uniformly static partitioning as an MVector rather than dropping it to a Vector. One wrinkle on top of it: reduce(vcat, (x,)) returns x itself, so a single partition is copied, otherwise the buffer would alias p while canonicalize reports aliases = false.

SciMLStructures is now a direct dependency and the code moved from ext/ to src/scimlstructures.jl.

Tests added for mixed element types, mixed array types and the single-partition aliasing, 53 assertions in total, Core group green locally. Description updated too, it still described the old buffer construction.

Runic's format check flagged it; the file picked it up when the code
moved out of the extension module.
@AJ0070

AJ0070 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@ChrisRackauckas sorry for the multiple pings. Fixed the Runic failure here, could you trigger CI again please?

@ChrisRackauckas
ChrisRackauckas merged commit 43b3d35 into SciML:master Aug 12, 2026
37 of 43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants