From af713eb1b6e6cc7bf2a224e5236d448313841ba8 Mon Sep 17 00:00:00 2001 From: Sharada Mohanty Date: Sat, 29 Aug 2026 06:57:13 +0200 Subject: [PATCH] docs(changelog): correct two stale claims in the v0.12.0 notes The nan-reduction entry said integer and boolean input is exempt from the new NaN-scan pass, without qualification. That holds for the reductions built on numpy's _replace_nan, but nanmedian, nanpercentile and nanquantile route through _remove_nan_1d, which calls np.isnan whatever the dtype, so they charge the pass at every dtype: on 100 elements nanmedian costs 400 against median's 200 for int32 as well as for float. The code has always been explicit that this is deliberate, and docs/reference/cost-model.md states it correctly; only the release notes were wrong, and they are the notes for the release that opened Phase 2, so they understated which calls got more expensive. The 0-d operand bullet was truncated mid-sentence and said nothing about the change. It also sits under "Costs less" while actually turning a wrongly-refused call into a priced one, which the completed text now says. Also drops a validation term from the symmetry guide's cost estimates: the internal validation pass is not billed, and the guide now notes that Reynolds accumulates in float64 so it is charged at that rate whatever the input dtype. --- CHANGELOG.md | 21 +++++++++++++++------ website/content/docs/guides/symmetry.mdx | 9 ++++++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4b57fe0c..3bdd4a9392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,12 +80,16 @@ Costs more: scans its input for NaN before reducing — a full extra pass its plain sibling does not run — and that pass was not charged, so `nansum` cost exactly what `sum` cost. It is now charged as one additional pass over the input. The pass - is charged only where NumPy actually performs it: **integer and boolean input - is exempt** (NumPy skips the scan entirely), **`nanmax` and `nanmin` are - exempt** (they test the reduced output rather than the input), and for a - symmetric operand the pass is priced over the orbit like every other pass in - the same operation. Twelve operations are affected on float and complex input, - typically by one input-sized pass. + is charged only where NumPy actually performs it: for the reductions built on + NumPy's `_replace_nan`, **integer and boolean input is exempt** (NumPy skips + the scan entirely); **`nanmax` and `nanmin` are exempt** (they test the reduced + output rather than the input); and for a symmetric operand the pass is priced + over the orbit like every other pass in the same operation. **`nanmedian`, + `nanpercentile` and `nanquantile` are the exception to the dtype exemption**: + they route through `_remove_nan_1d`, which calls `np.isnan` whatever the dtype, + so they charge the extra pass at *every* dtype — on 100 elements, `nanmedian` + costs 400 against `median`'s 200 for `int32` as well as for float. Twelve + operations are affected, typically by one input-sized pass. - **`angle` on a boolean input costs twice what it did.** NumPy computes it in float64; it had been billed at the float16 rate, which was half price. Integer @@ -134,6 +138,11 @@ Costs less: and `nanquantile`. - **`dot`, `inner` and two-array `multi_dot` accept a 0-d operand.** NumPy treats + it as a scalar multiply, but flopscope raised from inside its own cost helper + before reaching NumPy at all. All three now price the 0-d case as that scalar + multiply. A 0-d operand inside a three-or-more-array `multi_dot` chain still + raises. This turns a wrongly-refused call into a priced one rather than making + anything cheaper, so it sits in this section only for want of a better one. - **`ldexp` is priced on its mantissa loop.** Its second operand is an exponent, not a value to compute with, so promoting on it was a category error rather diff --git a/website/content/docs/guides/symmetry.mdx b/website/content/docs/guides/symmetry.mdx index 4a20a46fbb..e45de5b981 100644 --- a/website/content/docs/guides/symmetry.mdx +++ b/website/content/docs/guides/symmetry.mdx @@ -187,9 +187,12 @@ This helper is ideal for docs, tests, and experiments: projection and validation behavior is identical. - approximate costs (meaningful estimate), for the default ``mode="reynolds-projection"``: - - `fnp.random.symmetric`: ``C_dist(n) + |G| * n + n`` + validation - - `flops.symmetrize`: ``|G| * n + n`` + validation - with ``n`` total elements and ``|G|`` group order. + - `fnp.random.symmetric`: ``C_dist(n) + |G| * n + n`` + - `flops.symmetrize`: ``|G| * n + n`` + with ``n`` total elements and ``|G|`` group order. The internal validation + pass is not billed. Reynolds averaging always accumulates in float64, so + ``flops.symmetrize`` is charged at the float64 rate whatever the input + dtype; ``mode="canonical-copy"`` keeps the input's own rate. - in exact arithmetic it projects onto the invariant subspace, and in practice `flops.as_symmetric()` validates the result with its usual validation tolerances