feat(spectra): port the muon positron spectrum to rust - #63
Merged
LoganAMorrison merged 4 commits intoAug 12, 2026
Merged
Conversation
The first kernel swap of cython-to-rust Phase 04. `dnde_positron_muon` now comes from `hazma._core.positron`. `_muon.pyx` keeps its `cdef`s, because the mediator positron modules and `_positron/_pion.pyx` still cimport them through `__pyx_capi__`, but its Python `def` is gone — so no caller can reach the implementation the swap replaced, which is as close to rules.md rule 1 as the phase's declared capi exception allows. No public value moves. The Rust is bit-for-bit identical to the pre-port Cython over 126,182 points spanning 14 muon energies and every kinematic edge, and the parity corpus gated the swap at `rtol = 0` — the declared budget for this entry point is the EXACT class. Matching it took reading the nine `fmadd`/`fmsub` sites out of the shipped object code first; three expressions that look fusable are not, and pattern-matching would have missed all three. Two mechanisms every later swap inherits: `cases.PORTED_ENTRY_POINTS` repoints a swapped corpus case at its wrapper while recording the `.pyx` origin, without which the gate keeps calling the twin and measures nothing; and the eight-step swap recipe now in the phase file's Goal. Also records a live 2.1.0 defect the port reproduces rather than repairs: the kernel divides by its Michel normalization where it should multiply, leaving every positron spectrum 0.0374% low. The corpus pins the low values by construction, so the repair is blocked until after Phase 06 — docs/followups/todo/positron-muon-spectrum-normalization-inverted.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI caught this on Linux/py3.11 after a green macOS run (PR #63, run 31562223329): `TestAgainstTheCythonTwin` failed on a one-byte diff on a platform it is written to skip. `unfused_point` — the Python transcription `cython_contracts()` compares the compiled kernel against, to decide whether this build fuses its multiply-adds — ended `pre * numerator / denominator`, where the Cython divides inside `dndx_positron_muon` and multiplies by `pre` in its caller. Same operations, different association, last bit different. So on Linux, where nothing contracts and the two should have agreed exactly, the probe concluded "this build contracts", un-skipped the platform-scoped class, and its bit-equality assertions failed. The failure mode is the inverse of Task 3.4's `[platform-scoped-oracle-asserted-globally]`: there a platform-scoped claim was asserted everywhere; here the scoping mechanism itself reported the wrong platform, and on the capturing platform that is invisible because the probe answers True either way. Fixed by matching the association, and pinned in both directions. `test_the_reference_is_the_cython_where_nothing_contracts` asserts bit-equality wherever the probe says False, so a future non-contraction divergence in the reference turns red instead of silently disabling the guard; `test_the_unfused_form_actually_differs_somewhere` already covered the other direction. Verified structurally, since the bug is unobservable on macOS: a *fused* Python reference built with a correctly-rounded `fma` at exactly the seven sites the Rust fuses reproduces the shipped Cython bit-for-bit — 0 mismatches in 21,000 points, against 11,713 for the unfused form on the same draw. That confirms both the disassembly-derived FMA map and that `unfused_point` now differs from the Cython by contraction alone. No production code changes; the kernel and every number it returns are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The against-the-Cython class decided whether to run by probing the compiled kernel against an unfused Python transcription: agree ⇒ this build does not contract ⇒ skip. On Linux/x86-64 the probe answered "contracts" on a build with no -march flag and so no hardware FMA to explain it, the class ran, and its bit-equality assertions failed 16 ways across py3.10-3.14. The mechanism was never localized, and it does not need to be: a compiler contracting a different set of expressions, or a libm rounding one call differently, breaks bit-equality just as thoroughly, and no probe over one mechanism can see the others. Declare the mode from the platform instead, and measure what the other mode has to absorb. Decoding both byte arrays out of the failure output recovers 21,953 differing values: median relative difference 7.2e-15, worst 1.5e-7, no sign flip, no NaN, no disagreement about support or zeros. It is rounding amplified by the kernel's own conditioning — both bad regimes (β → 0 just off rest, γ ≫ 1) form xm/xp as γ²(x ∓ β·root) and then difference nearly-equal terms. So the budget is scaled to the peak of the spectrum rather than applied pointwise. Pointwise the worst case sits at a value 4.3e-4 of the peak, and an rtol loose enough to admit it (≥1e-6) would be loose enough to hide a real defect; against the peak the worst disagreement anywhere is 1.3e-10. OFF_PLATFORM_BUDGET = 1e-8 clears every array recovered from run 31564747071 by at least 84x, replayed through the shipped assertion, while a wrong branch or dropped term lands at O(1). Nothing skips on any platform now: 47 tests, two modes. Two are new — the support comparison, which is structural and so runs exactly everywhere, and a guard that the budget rejects a perturbation of 1e-6 of the peak, since on the capturing platform nothing else exercises the tolerance branch. The corpus budget for spectra.positron.muon stays at rtol=0. Task 4.1 moved provenance off the capturing tree, so effective_budget now hands out the declared budget on macOS too — the port meets it and CI is green, and loosening it would weaken a passing gate for a platform the corpus does not run on. Only its justification changed: it claimed exactness followed from the kernel being closed-form with no quad, and the measurement says the opposite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The test module went from 45 tests to 47 in the previous commit, and the verification records written in the earlier round kept reporting the superseded figure alongside every number derived from it: a phase README (45 tests, 17 scoped), two task notes (1422 full-suite passes, +45), and the PR body (46, 1423). Re-ran the commands and regenerated all of it as one fixed point rather than patching the cited lines: bare `pytest -q` -> 1424 passed, 14 skipped in 555.96s, which closes the arithmetic exactly against Task 3.5's 1378/13 (+47 for the new module, -1 pass / +1 skip for test_running_on_the_capturing_tree). Module 47, parity 629/1, aggregation 69, cargo 80. Scoped preflight re-run -> RESULT: PASS with markdownlint over all seven changed docs. Also drops a duplicated sentence the previous commit left in the task note's full-suite bullet, and corrects the claim that 17 tests skip off the capturing platform — none do now, the module drops to its budget mode instead. The lessons ledger gets the temporal shape of [sibling-copies-of-a-fixed-claim]: a count changed mid-PR invalidates every number derived from it, not just its own copies, and the PR body is a durable record with the rest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LoganAMorrison
deleted the
claude/cython-to-rust/task-4.1-positron-muon-template-swap
branch
August 12, 2026 16:49
9 tasks
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.
Summary
dnde_positron_muonnow runs on Rust. The kernel isrust/src/kernels/positron_muon.rs(PyO3-free), registered onhazma._core.positronthroughdispatch::map_unarywith the quantitywording its Cython twin's
assertcarried.hazma/spectra/_positron/__init__.pycalls it.
_muon.pyxkeeps itscdefs —_positron/_pion.pyxand bothmediator positron modules still cimport them via
__pyx_capi__, which isthe phase's declared exception to rules.md rule 1 — but its Python
defis deleted, so no caller can reach the replaced implementation.
pre-port Cython (max relative deviation
0.000e+00) over 126,182 pointsspanning 14 muon energies and every kinematic edge, and the parity corpus
gated the swap at
rtol = 0, sincespectra.positron.muonis in theEXACTbudget class. Reaching that took mapping the ninefmadd/fmsubsites out of the shipped
.sobefore writing any Rust: three expressionsthat look fusable are not, and pattern-matching would have missed all
three.
test/parity/cases.py's newPORTED_ENTRY_POINTSrepoints a swappedcorpus case at its wrapper while recording the
.pyxorigin — without itthe gate keeps calling the twin and measures nothing — and
assert_full_coveragenow also fails if a ported entry point's.pyxstill exports its
def. The eight-step swap recipe is written into thephase file's Goal.
test/test_core_positron_muon.pyis the per-kerneltest template, deliberately not a copy of
test/test_core_dispatch.py(Task 3.5 made the dispatch layer three shared helpers, so those 118
tests now cover code every kernel routes through unchanged).
by its Michel normalization where it should multiply, so every positron
spectrum hazma returns is 0.0374% low (
∫ dN/dE dE = 1/R_FACTOR² = 0.9996259instead of 1, at every parent energy). The un-normalizedpolynomial integrates to exactly
1/R_FACTOR— scipy0.999812949171142against the closed form
0.9998129491711419— and the siblinghazma/spectra/_neutrino/_muon.pyxdeclares the identical constant andmultiplies by it, which is what makes this an inversion rather than a
convention. It propagates to
dnde_positron_charged_pion(∫ = 0.999623at
E_π = 500 MeV), both mediator positron spectra, and everypositron-based limit. This PR moves no number — rules.md rule 1 says
reproduce it, and the corpus pins the low values by construction, so a
fix here would fail the gate governing every remaining swap. Filed as
docs/followups/todo/positron-muon-spectrum-normalization-inverted.md,blocked until after Phase 06 Task 6.4.
19 of the 41 cases lose nothing (the
EXACTclass's declared budget isitself
0.0); the other 22 fall back to their declared budgets. Tworeasons and only one is the swap — the kernel digest also moved, because
removing a
defchanges the.pyxbytes it covers.off-platform one is a measured budget. An earlier revision decided
whether to run by probing the compiled kernel against an unfused Python
transcription; on Linux/x86-64 that probe answered "contracts" on a build
with no
-marchflag and so no hardware FMA to explain it, and itsbit-equality assertions failed 16 ways per interpreter. The mode is now
declared from the platform (read out of the corpus manifest, so the two
scopes cannot drift), and what the other mode absorbs was measured rather
than assumed: decoding both byte arrays out of that failure output
recovers 21,953 differing values, median relative difference 7.2e-15,
worst 1.5e-7, with no sign flip, no
NaN, and no disagreement aboutsupport or zeros. It is rounding amplified by the kernel's own
conditioning (
β → 0just off rest,γ ≫ 1), so the budget is scaled tothe peak of the spectrum rather than applied pointwise — against the
peak the worst disagreement anywhere is 1.3e-10, and
OFF_PLATFORM_BUDGET = 1e-8clears every recovered Linux array by atleast 84×, replayed through the shipped assertion. Nothing skips on any
platform now.
spectra.positron.muonstays atrtol=0; onlyits justification changed. Task 4.1 moved provenance off the capturing
tree, so
effective_budgetnow hands out the declared budget on macOStoo — the port meets it and CI is green, and loosening it would weaken a
passing gate for a platform the corpus does not run on. The old
whyclaimed exactness followed from the kernel being closed-form with no
quad; the measurement above says the opposite, so it now records thatthe exactness is a property of the platform.
Project
projects/cython-to-rust/— Task 4.1:_positron/_muon(thewalking-skeleton kernel swap). Phase 04 is now In Progress.
See
projects/cython-to-rust/task-notes/phase-04/task-4.1-positron-muon.mdfor implementation detail, decisions, the mutation campaign, and the full
verification record.
Test plan
scripts/agents/preflight.sh— RESULT: PASS--pathsdeliberately omitshazma/spectra/_positron/__init__.py:configured
ruffreports 24 findings there, and adiffofruff check --output-format conciseagainstgit show origin/master:hazma/spectra/_positron/__init__.pyis empty— the same 24 both sides. They are typing/docstring modernization across
a 500-line public wrapper (
UP007would rewriteUnion[...]in a modulemarked
runtime-typing = true), i.e.docs/followups/todo/preflight-isort-ruff-red-on-trunk.md, not anythingthis diff introduced.
blackandisortpass on that file.Bare suite:
1424 passed, 14 skipped(from1378 passed, 13 skippedat Task 3.5). The arithmetic closes exactly:
+47for the new testmodule, and
test_running_on_the_capturing_treemoving from pass to skip— which is the corpus's designed signal that it is now in budget mode.
Parity corpus:
pytest test/parity -q -rs→629 passed, 1 skipped,the skip reason naming both expected differences:
declared budgets in force: kernel digest f5e6e269be47 -> fdbae2c19d87; hazma._core serves 1 kernel(s).Model-layer gate, run either side of the swap:
pytest test/test_theory_aggregation.py -q→69 passed.pytest test/test_core_positron_muon.py -q→47 passed in 0.36sonthe capturing platform, and
47 passedagain withplatform.machineforced to
x86_64so the off-platform budget branch is the one undertest. Nothing skips in either mode.
cargo test --manifest-path rust/Cargo.toml --no-default-features→80 passed(11 new).Numerical impact:
dnde_positron_muonovernp.logspace(-2, 3, 200)MeV at muon energies 150 / 500 / 1500 MeV, against the pre-port Cython
cdefthrough__pyx_capi__—3 arrays / 600 values, max relative deviation 0.000e+00; and the wider sweep,compared 126182 points; 0 not bit-equal; worst rel 0.000e+00.Mutation campaign, 18 mutations against
rust/src/kernels/positron_muon.rs, sequential from a green baselinewith the baseline re-asserted after:
caught=13 survived=5. Three of thefive were caught after adding tests; the remaining two are provably
equivalent mutants (
x.mul_add(2.0, C)is bit-identical tox * 2.0 + Cbecause doubling is exact — so one of the ninefmaddsites is unobservable — and
beta + betavs2.0 * beta, included as acontrol).
🤖 Generated with Claude Code