Skip to content

Fix empty StridedView broadcasting#50

Merged
lkdvos merged 2 commits into
QuantumKitHub:mainfrom
mtfishman:mf/empty-broadcasting
Mar 12, 2026
Merged

Fix empty StridedView broadcasting#50
lkdvos merged 2 commits into
QuantumKitHub:mainfrom
mtfishman:mf/empty-broadcasting

Conversation

@mtfishman
Copy link
Copy Markdown
Contributor

This fixes a bug I came across when broadcasting empty StridedViews. Without this change, I see the following:

(dev) pkg> st
Status `~/.julia/dev/Strided/dev/Project.toml`
  [5e0ebb24] Strided v2.3.4

julia> using Strided

julia> A1 = StridedView(randn(2, 0))
2×0 StridedView{Float64, 2, Memory{Float64}, typeof(identity)}

julia> A2 = StridedView(randn(2, 0))
2×0 StridedView{Float64, 2, Memory{Float64}, typeof(identity)}

julia> A1 .+ A2
ERROR: ArgumentError: step cannot be zero
Stacktrace:
  [1] steprange_last
    @ ./range.jl:349 [inlined]
  [2] StepRange
    @ ./range.jl:336 [inlined]
  [3] StepRange
    @ ./range.jl:391 [inlined]
  [4] _colon
    @ ./range.jl:24 [inlined]
  [5] Colon
    @ ./range.jl:22 [inlined]
  [6] macro expansion
    @ ~/.julia/packages/Strided/yfmeB/src/mapreduce.jl:421 [inlined]
  [7] _mapreduce_kernel!(f::Strided.CaptureArgs{…}, op::Nothing, initop::Nothing, dims::Tuple{…}, blocks::Tuple{…}, arrays::Tuple{…}, strides::Tuple{…}, offsets::Tuple{…})
    @ Strided ~/.julia/packages/Strided/yfmeB/src/mapreduce.jl:257
  [8] _mapreduce_block!(f::Strided.CaptureArgs{…}, op::Nothing, initop::Nothing, dims::Tuple{…}, strides::Tuple{…}, offsets::Tuple{…}, costs::Tuple{…}, arrays::Tuple{…})
    @ Strided ~/.julia/packages/Strided/yfmeB/src/mapreduce.jl:170
  [9] _mapreduce_order!(f::Strided.CaptureArgs{…}, op::Nothing, initop::Nothing, dims::Tuple{…}, strides::Tuple{…}, arrays::Tuple{…})
    @ Strided ~/.julia/packages/Strided/yfmeB/src/mapreduce.jl:154
 [10] _mapreduce_fuse!
    @ ~/.julia/packages/Strided/yfmeB/src/mapreduce.jl:130 [inlined]
 [11] copyto!
    @ ~/.julia/packages/Strided/yfmeB/src/broadcast.jl:39 [inlined]
 [12] copy
    @ ./broadcast.jl:919 [inlined]
 [13] materialize(bc::Base.Broadcast.Broadcasted{Strided.StridedArrayStyle{…}, Nothing, typeof(+), Tuple{…}})
    @ Base.Broadcast ./broadcast.jl:894
 [14] top-level scope
    @ REPL[9]:1
 [15] top-level scope
    @ REPL:1
Some type information was truncated. Use `show(err)` to see complete types.

Other codepaths like map[!] seem to already have this kind of check for the empty case (before calling _mapreduce_fuse!).

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/broadcast.jl 89.28% <100.00%> (+89.28%) ⬆️

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Member

@lkdvos lkdvos left a comment

Choose a reason for hiding this comment

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

Thanks for the report and fix! (And added tests, greatly appreciated)

@lkdvos lkdvos merged commit a0a8f15 into QuantumKitHub:main Mar 12, 2026
10 of 13 checks passed
@mtfishman mtfishman deleted the mf/empty-broadcasting branch March 12, 2026 21:34
mtfishman added a commit to ITensor/TensorAlgebra.jl that referenced this pull request May 7, 2026
#173)

## Summary
- The 0-dim branch of `bipermutedimsopadd!` read `dest[]`
unconditionally (`β * dest[] + α * op(src[])`), even when `β = 0`. By
BLAS convention, `β = 0` means `dest` is treated as write-only — its
contents need not be defined. With element types whose `undef` storage
is unreadable, the unguarded read crashed:
`bipermutedimsopadd!(Array{BigFloat, 0}(undef), identity, src, (), (),
true, false)` threw `UndefRefError`, since the unassigned slot of a
0-dim array of mutable `BigFloat` cannot be read. This surfaced via
callers that allocate a 0-dim destination via `similar` and call into
`bipermutedimsopadd!` with `β = 0` (e.g. scalar contractions producing a
0-dim result).
- Adds an `iszero(β)` guard to the 0-dim branch to skip reading `dest`
in the write-only case. The 0-dim short-circuit itself is kept: it lets
downstream array types (e.g. `BlockSparseArray{T, 0}`) use direct
`dest[]`/`src[]` accesses instead of having to support `getindex` on a
0-dim `PermutedDimsArray` wrapper around their type.
- The companion `isempty(dest) && return dest` early return worked
around a `Strided.jl` broadcasting bug fixed by
[QuantumKitHub/Strided.jl#50](QuantumKitHub/Strided.jl#50),
released in `Strided` 2.3.5. Adds `Strided` to `[deps]` (with `using
Strided: Strided` so it isn't reported as stale) and a `Strided =
"2.3.5"` compat floor, then drops the workaround.
- Adds a regression test covering `Array{T, 0}` for `T ∈ {Float64,
BigFloat}` with both `β = 0` (write-only) and `β ≠ 0` (accumulating)
cases.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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