Raise a fill to a literal power the way Base does - #446
Open
jishnub wants to merge 1 commit into
Open
Conversation
`x .^ k` with a literal `k` lowers to `literal_pow`, which four styleless rules
intercepted. Each read the answer off the exponent rather than off the value the
operation produces, and three were wrong for a negative one:
Zeros{Float64}(3) .^ -1 -> Zeros [0,0,0] Base: [Inf, Inf, Inf]
Fill(2,3) .^ -1 -> DomainError Base: [0.5, 0.5, 0.5]
Ones{Int}(3) .^ -1 -> Ones{Int} Base: Float64 ones
The rule for a `Zeros` asserted `0^k == 0` for every `k != 0`; the one for a `Ones`
took the element type from its argument rather than from the result, which `inv` on
an integer promotes; and the generic one evaluated `getindex_value(r)^k`, which is
undefined where `literal_pow` is not.
A negative power is a division, and the zero sits in the denominator. The package
already declines to absorb there: no rule intercepts `x ./ Zeros`, and two `Zeros`
under `/` or `\` are sent through `_broadcasted_nan` on purpose. Two spellings of the
same array disagreed, `Fill(0.0,3) .^ -1` giving `Inf` where `Zeros(3) .^ -1` gave
zero, and `inv.(Zeros(3))` already gave `Inf`. Absorption is unchanged where the zero
is a factor.
Classifying by the value is what `_copy_fill` already does, so the four rules collapse
into one that shares it through `_fill_result`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## jishnub/broadcaststyle #446 +/- ##
==========================================================
- Coverage 99.84% 99.84% -0.01%
==========================================================
Files 9 9
Lines 1275 1272 -3
==========================================================
- Hits 1273 1270 -3
Misses 2 2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
dlfivefifty
approved these changes
Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #385 — please review that first; this PR targets
jishnub/broadcaststyleand its diff is a single commit.x .^ kwith a literalklowers to aBase.literal_powbroadcast, which four styleless rules intercept so a fill raised to a power stays a fill. Each read the answer off the exponent rather than off the value the operation produces, and three were wrong for a negative one:Causes: the
AbstractZeros/Val{k}rule asserted0^k == 0for everyk != 0; theAbstractOnesrule took the element type from its argument rather than from the result, whichinvon an integer promotes; and the generic rule evaluatedgetindex_value(r)^k, which is undefined whereliteral_powis defined (2^-1throws,literal_pow(^, 2, Val(-1))is0.5).There was also an internal inconsistency: written as a literal,
Zeros{Float64}(3) .^ -1returned zeros, but with the exponent in a variable — which does not lower toliteral_pow, and so takes the generic path through_copy_fill— the same expression already returnedInf.All of this reproduces on
master; it is pre-existing rather than a regression from #385, but it sits in code that branch rewrites.This restores the strong-zero convention rather than departing from it
Zerosis deliberately an absorbing zero, but only as a factor. The rule loop generates exactly(*, Zeros, T),(/, Zeros, T),(*, T, Zeros)and(\, T, Zeros)— every shape where the zero is a factor or a numerator — andfillalgebra.jldoes the same for matrix products. It deliberately does not absorb in a denominator: there is no(/, T, Zeros)rule, sox ./ Zerosfalls through to IEEE and givesInf, and where both sides areZeros,/and\are routed through_broadcasted_nanto constructFill(NaN)— the more expensive answer, pinned by a testset named"NaN".A negative power is a division with the zero in the denominator, so it belongs in the second group. Three existing behaviours already said so:
inv.(Zeros(3))already returnedFill(Inf),Fill(0.0,3) .^ -1already returnedFill(Inf)whileZeros(3) .^ -1returned zeros — two spellings of the same array disagreeing,Twos ./ Zeros === Fill(Inf, ...)is already asserted in the test suite.Absorption is unchanged wherever the zero is a factor. (The "avoid
∞ * 0in InfiniteArrays.jl" comment is not counter-evidence: it sits on_foldl_length_opguardinglength(A)*v, so that∞is an infinite length, not an infinite value.)Scope of the behaviour change
Measured over
Zeros/OnesinInt,Float64,Float32,Bool,ComplexF64andFillof 0, 1, 2, 2.0, against exponents-3..3:k < 0; nothing atk >= 0changes,Implementation
Classifying by the computed value is what
_copy_fillalready does, so the four rules collapse into one that shares it through a new_fill_result:Note for downstream packages
A package overloading only
broadcasted_zeros/broadcasted_onesnow falls back toFillforZeros .^ negative, since the result is neither zero nor one and onlybroadcasted_fillis left to reach. Recorded in the tests asTaggedZeros{Int}(4) .^ -1 ≡ Fill(Inf, 4).Verification
Test.detect_ambiguities(FillArrays; recursive=false)→ 0DefaultArrayStyle-forwarded path and the infinite-axes cases🤖 Generated with Claude Code