Skip to content

Respect destination dimensions in VectorOfArray broadcasting - #685

Draft
ChrisRackauckas wants to merge 1 commit into
masterfrom
fix/voa-broadcast-dimensions-680
Draft

ChrisRackauckas wants to merge 1 commit into
masterfrom
fix/voa-broadcast-dimensions-680

Conversation

@ChrisRackauckas

Copy link
Copy Markdown
Member

🚧 UNREVIEWED — awaiting maintainer review. Filed by an AI agent; no human has reviewed this.
Harness: Codex CLI 0.162.1 · Model: gpt-6.1-sol
Session: 01a14bd6-bd53-7dd2-94c0-6f6613286f91

In-place VectorOfArray broadcasting slices array arguments along the destination's outer dimension. Lower-dimensional arguments broadcast across inner arrays, and singleton outer dimensions reuse their sole slice. Inner broadcasts use the destination axes for expansion, including immutable StaticArrays, StructArrays storage, and serial/Polyester FastBroadcast.

Fixes #680

Keep this draft out of merge consideration until a maintainer has reviewed it. No public signatures or dependencies change.

Reproduction and regression evidence

Reproduced on default-branch commit 214fb8d556b24581e65015bdeb340102aa33a76d (package version 4.5.5) and the latest registered release, 4.5.5, with Julia 1.12.7. Both produce [11.0 24.0; 12.0 25.0; 13.0 26.0] for the issue's vector-addition example instead of dense-array broadcasting's [11.0 14.0; 22.0 25.0; 33.0 36.0].

The new regression file compares against Base dense-array broadcasting for mutable vectors, SVectors, MVectors, StructArray storage, matrix inner arrays, zero-dimensional operands, singleton dimensions, and full-dimensional operands. It exercises serial and threaded FastBroadcast on SVector storage.

With the implementation fix stashed, running the regression file in a local environment that develops the checkout:

JULIA_NUM_THREADS=4 JULIA_NUM_PRECOMPILE_TASKS=4 timeout 3600 julia --project=tmp/main -e 'using RecursiveArrayTools, StaticArrays; v=VectorOfArray([SVector(1.,2.), SVector(3.,4.)]); f(v)=(v .= v .+ v); f(v); @show @allocated f(v); include("RecursiveArrayTools.jl/test/Core/voa_broadcast_dimensions.jl")'

Relevant output (stack traces omitted):

#= none:1 =# @allocated(f(v)) = 0
VectorOfArray broadcast dimensions: Test Failed
  Expression: Array(v) == expected
   Evaluated: [11.0 24.0; 12.0 25.0; 13.0 26.0] == [11.0 14.0; 22.0 25.0; 33.0 36.0]
DimensionMismatch: destination axes (Base.OneTo(3),) are not compatible with source axes ()
Test Summary:                      | Fail  Error  Total  Time
VectorOfArray broadcast dimensions |    1      1      2  2.0s

After restoring the fix, the same regression file:

JULIA_NUM_THREADS=4 JULIA_NUM_PRECOMPILE_TASKS=4 timeout 3600 julia --project=tmp/main -e 'include("RecursiveArrayTools.jl/test/Core/voa_broadcast_dimensions.jl")'
Test Summary:                      | Pass  Total  Time
VectorOfArray broadcast dimensions |  130    130  8.8s

Validation

All Julia commands use JULIA_NUM_THREADS=4, JULIA_NUM_PRECOMPILE_TASKS=4, and timeout 3600.

  • GROUP=Core julia --project=RecursiveArrayTools.jl -e 'using Pkg; Pkg.test()': Testing RecursiveArrayTools tests passed; includes all 130 new checks and the existing zero-allocation assertions.
  • GROUP=QA julia --project=RecursiveArrayTools.jl -e 'using Pkg; Pkg.test()': 20/20 passed.
  • julia --project=@runic -m Runic --inplace on all six changed Julia files: passed.
  • typos tmp/diff.patch on the complete diff, including the new test file, and git diff --check: passed.
  • Docs build omitted: no docs, docstrings, or public API changes.
  • Not run: Downstream, GPU, AD, NoPre, other Julia versions, and performance benchmarks. Independent review is pending.

Range-only FastBroadcast expressions can simplify to a range assignment before reaching these broadcast methods. A separate @.. v = x + 1 check with an SVector-backed destination and x = 10.:10.:30. fails on the unmodified default branch with setindex!(::SVector{3, Float64}, value, ::Int) is not defined. That assignment path is outside this fix; the tests cover lower-dimensional ranges through Base broadcasting.

Risk assessment

  • Risk: medium-high (MH1: changes incorrect results and errors on the core VectorOfArray broadcasting lane).
  • Blast radius: VectorOfArray and DiffEqArray broadcasting, including static inner arrays and the StructArrays/FastBroadcast extensions sharing the unpacking helper; no public signature changes.
  • Evidence: reproduced on default branch and registered release; stashed-fix regression fails and restored-fix regression passes 130 checks; full Core and QA pass, including existing allocation checks. No downstream or GPU/AD validation.
  • Independent review: pending (a different harness reviews it).
  • Merge: needs human review — MH1 affects a widely used core entry point.

🤖 Posted by an AI agent — harness: Codex CLI 0.162.1 · model: gpt-6.1-sol
Conversation: Codex exec session 01a14bd6-bd53-7dd2-94c0-6f6613286f91 (no session URL)

Slice operands using the destination dimension and preserve lower-dimensional
arguments across inner arrays. Expand each inner broadcast to its destination
axes, including static arrays and FastBroadcast extension paths.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Codex <noreply@openai.com>
Agent-Harness: Codex CLI 0.162.1
Agent-Model: gpt-6.1-sol
Agent-Session: 01a14bd6-bd53-7dd2-94c0-6f6613286f91

This branch has not been deployed

No deployments
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.

In-place broadcasting into a VectorOfArray mishandles arrays with fewer dimensions

2 participants