Skip to content

Add a BroadcastStyle for AbstractFill - #385

Open
jishnub wants to merge 19 commits into
masterfrom
jishnub/broadcaststyle
Open

Add a BroadcastStyle for AbstractFill#385
jishnub wants to merge 19 commits into
masterfrom
jishnub/broadcaststyle

Conversation

@jishnub

@jishnub jishnub commented Aug 26, 2024

Copy link
Copy Markdown
Member

Fixes #40
Fixes #58
Fixes #143
Fixes #145
Fixes #188

After this,

julia> Broadcast.BroadcastStyle(typeof(Fill(2,3)))
FillArrays.FillStyle{1}()

julia> Broadcast.BroadcastStyle(typeof(Zeros(2,3)))
FillArrays.ZerosStyle{2}()

A special ZerosStyle is necessary to properly handle broadcasting with structured matrix types from LinearAlgebra.

Often, we will have AbstractFill return types returned now in broadcasting operations, and --- where possible --- ones whose values are statically known:

julia> exp.(Zeros(4))
4-element Ones{Float64}

julia> (x -> x^2).(Zeros(4))
4-element Zeros{Float64}

julia> .!(Trues(4))
4-element Zeros{Bool}

This does not change the fact that broadcasting over a Fill is evaluated before others in a fused broadcast:

julia> ones(1,5) .+ (ones(1) .+ (_ -> rand()).(Fill("vec", 2)))
2×5 Matrix{Float64}:
 2.76296  2.76296  2.76296  2.76296  2.76296
 2.76296  2.76296  2.76296  2.76296  2.76296

This is achieved by recursively walking through a Broadcasted object, and evaluating the components that produce AbstractFills eagerly.

@jishnub
jishnub marked this pull request as draft August 26, 2024 10:19
@codecov

codecov Bot commented Aug 26, 2024

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.92%. Comparing base (7b96cc2) to head (0984e80).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #385   +/-   ##
=======================================
  Coverage   99.91%   99.92%           
=======================================
  Files           9        9           
  Lines        1238     1291   +53     
=======================================
+ Hits         1237     1290   +53     
  Misses          1        1           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

@putianyi889

Copy link
Copy Markdown
Contributor

This could also fix #394

jishnub and others added 4 commits July 27, 2026 17:49
An `AbstractFillStyle` now resolves conflicts the same way that a
`DefaultArrayStyle` of the same dimension does: styles that win against
`DefaultArrayStyle` (lazy, banded, block) win against a fill style too,
and those that defer to it (e.g. `StaticArrayStyle`) keep deferring.
Neither side has to define a rule for `FillStyle`/`ZerosStyle`.

Packages that opt out of their own style for fills by forwarding to
`DefaultArrayStyle` keep obtaining a fill: such `broadcasted` calls are
re-dispatched on the arguments alone. Where the arguments carry a foreign
style, only the style-independent rules are applied, recorded by the new
`has_fill_rule` table, so the forwarding package stays in control.

Also add the styleless `Ones op Ones -> Ones` rules for `*`, `/` and `\`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`x .^ k` lowers to a three-argument `literal_pow` broadcast, which none of
the `DefaultArrayStyle` shapes that packages forward to us matched, so such
calls materialized densely instead of obtaining a fill. Unwrap the `Ref`s so
that the styleless rules apply, and record the rule in `has_fill_rule` so
that arguments carrying a foreign style (e.g. an infinite fill, which
`InfiniteArrays` marks as lazy) are routed to it as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The paths that packages defining their own broadcast style rely on weren't
exercised by the test suite, as nothing in it carries a foreign style. Give
the `InfiniteArrays` test helper the lazy style and the forwards to
`DefaultArrayStyle` that `InfiniteArrays` obtains through `LazyArrays`, and
test that fills, ranges and `literal_pow` keep being simplified through it,
that the unforwarded cases stay lazy, and that an array with a
dimension-agnostic style wins against a fill.

`Ones{Int}(∞) .* (1:∞)` threw along the way, as `_range_convert` rebuilds a
unit range out of its endpoints, which an infinite range can't convert. Return
a range whose eltype already matches as-is instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jishnub
jishnub marked this pull request as ready for review July 29, 2026 15:19
jishnub and others added 4 commits July 29, 2026 21:34
Commenting out the `has_fill_rule` methods that record which rules hold
whatever the style of the other argument is left the test suite passing, so
nothing exercised the one shape they uniquely protect: `Zeros` absorbing an
argument that is neither a fill nor something we can size or materialize.
Without the fast path such a call fails to find a `similar` method rather
than collapsing to `Zeros`, so test it through the `DefaultArrayStyle` entry
point that packages forward to.

Drop the `_range_convert` method for a `OneTo` whose eltype already matches.
The diagonal `where T` makes it lose against the `OneTo` method that converts
the endpoint, so it was never dispatched to, and the comment above it named
the wrong methods.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commenting each entry out in turn showed which ones a test would notice.
The `+`/`-` entries for a `Zeros` against an arbitrary vector, and the
`literal_pow` one, are needed whenever the other argument can't be
materialized, so give the test helper an infinite vector that is neither a
fill nor a range and exercise all three through the `DefaultArrayStyle` entry
point. `.^` on a fill is caught by the styleless rule before a style is ever
consulted, so the forwarded three-argument form needs testing on its own.

The `AbstractOnes`/`AbstractOnes` entry is neither observable nor needed to
resolve an ambiguity, as two fills let the caller evaluate the operation on
the fill values instead, so drop it and record the criterion. The remaining
two-fill entries stay: without them the one-sided entries they sit next to
are ambiguous, which is what they are now documented as being for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neither entry can be dropped — `Zeros`/`Zeros` and `ZerosVector`/`ZerosVector`
disambiguate the one-sided entries beside them, and Aqua reports the
ambiguities as soon as either goes — but nothing reached them either, as the
infix tests forward through a layer that lets them be resolved before the call
is made. Call the forwarded form directly, as a package would.

Co-Authored-By: Claude Opus 5 <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

2 participants