Skip to content

feat(spectra): port the muon positron spectrum to rust - #63

Merged
LoganAMorrison merged 4 commits into
masterfrom
claude/cython-to-rust/task-4.1-positron-muon-template-swap
Aug 12, 2026
Merged

feat(spectra): port the muon positron spectrum to rust#63
LoganAMorrison merged 4 commits into
masterfrom
claude/cython-to-rust/task-4.1-positron-muon-template-swap

Conversation

@LoganAMorrison

@LoganAMorrison LoganAMorrison commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • dnde_positron_muon now runs on Rust. The kernel is
    rust/src/kernels/positron_muon.rs (PyO3-free), registered on
    hazma._core.positron through dispatch::map_unary with the quantity
    wording its Cython twin's assert carried. hazma/spectra/_positron/__init__.py
    calls it. _muon.pyx keeps its cdefs — _positron/_pion.pyx and both
    mediator positron modules still cimport them via __pyx_capi__, which is
    the phase's declared exception to rules.md rule 1 — but its Python def
    is deleted, so no caller can reach the replaced implementation.
  • No public value moves. The Rust is bit-for-bit identical to the
    pre-port Cython (max relative deviation 0.000e+00) over 126,182 points
    spanning 14 muon energies and every kinematic edge, and the parity corpus
    gated the swap at rtol = 0, since spectra.positron.muon is in the
    EXACT budget class. Reaching that took mapping the nine fmadd/fmsub
    sites out of the shipped .so before writing any Rust: three expressions
    that look fusable are not, and pattern-matching would have missed all
    three.
  • Two mechanisms every later swap inherits.
    test/parity/cases.py's new PORTED_ENTRY_POINTS repoints a swapped
    corpus case at its wrapper while recording the .pyx origin — without it
    the gate keeps calling the twin and measures nothing — and
    assert_full_coverage now also fails if a ported entry point's .pyx
    still exports its def. The eight-step swap recipe is written into the
    phase file's Goal. test/test_core_positron_muon.py is the per-kernel
    test 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).
  • A live 2.1.0 defect is recorded, not repaired. The kernel divides
    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.9996259 instead of 1, at every parent energy). The un-normalized
    polynomial integrates to exactly 1/R_FACTOR — scipy 0.999812949171142
    against the closed form 0.9998129491711419 — and the sibling
    hazma/spectra/_neutrino/_muon.pyx declares the identical constant and
    multiplies by it, which is what makes this an inversion rather than a
    convention. It propagates to dnde_positron_charged_pion (∫ = 0.999623
    at E_π = 500 MeV), both mediator positron spectra, and every
    positron-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.
  • The corpus leaves bit-equality mode from this commit, permanently.
    19 of the 41 cases lose nothing (the EXACT class's declared budget is
    itself 0.0); the other 22 fall back to their declared budgets. Two
    reasons and only one is the swap — the kernel digest also moved, because
    removing a def changes the .pyx bytes it covers.
  • The against-the-Cython oracle has two declared modes, and the
    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 -march flag and so no hardware FMA to explain it, and its
    bit-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 about
    support or zeros. It is rounding amplified by the kernel's own
    conditioning (β → 0 just off rest, γ ≫ 1), so the budget is scaled to
    the peak of the spectrum rather than applied pointwise — against the
    peak the worst disagreement anywhere is 1.3e-10, and
    OFF_PLATFORM_BUDGET = 1e-8 clears every recovered Linux array by at
    least 84×, replayed through the shipped assertion. Nothing skips on any
    platform now.
  • The corpus budget for spectra.positron.muon stays at rtol=0; only
    its justification changed.
    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. The old why
    claimed exactness followed from the kernel being closed-form with no
    quad; the measurement above says the opposite, so it now records that
    the exactness is a property of the platform.

Project

projects/cython-to-rust/ — Task 4.1: _positron/_muon (the
walking-skeleton kernel swap). Phase 04 is now In Progress.

See projects/cython-to-rust/task-notes/phase-04/task-4.1-positron-muon.md
for implementation detail, decisions, the mutation campaign, and the full
verification record.

Test plan

  • scripts/agents/preflight.shRESULT: PASS

    PASS   black --check           hazma/_core.pyi test/parity/cases.py test/parity/test_parity.py test/parity/tolerances.py test/test_core_positron_muon.py
    PASS   isort --check-only      (same paths)
    PASS   ruff check              (same paths)
    PASS   cargo fmt --check       rust/
    PASS   cargo clippy            rust/
    PASS   cargo test              rust/
    PASS   pytest                  1424 passed, 14 skipped, 5 warnings in 572.75s (0:09:32)
    PASS   import hazma            version 2.1.0
    PASS   markdownlint            (the seven changed docs)
    PASS   forbidden tokens        none added
    

    --paths deliberately omits hazma/spectra/_positron/__init__.py:
    configured ruff reports 24 findings there, and a diff of
    ruff check --output-format concise against
    git show origin/master:hazma/spectra/_positron/__init__.py is empty
    — the same 24 both sides. They are typing/docstring modernization across
    a 500-line public wrapper (UP007 would rewrite Union[...] in a module
    marked runtime-typing = true), i.e.
    docs/followups/todo/preflight-isort-ruff-red-on-trunk.md, not anything
    this diff introduced. black and isort pass on that file.

  • Bare suite: 1424 passed, 14 skipped (from 1378 passed, 13 skipped
    at Task 3.5). The arithmetic closes exactly: +47 for the new test
    module, and test_running_on_the_capturing_tree moving from pass to skip
    — which is the corpus's designed signal that it is now in budget mode.

  • Parity corpus: pytest test/parity -q -rs629 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 -q69 passed.

  • pytest test/test_core_positron_muon.py -q47 passed in 0.36s on
    the capturing platform, and 47 passed again with platform.machine
    forced to x86_64 so the off-platform budget branch is the one under
    test. Nothing skips in either mode.

  • cargo test --manifest-path rust/Cargo.toml --no-default-features
    80 passed (11 new).

  • Numerical impact: dnde_positron_muon over np.logspace(-2, 3, 200)
    MeV at muon energies 150 / 500 / 1500 MeV, against the pre-port Cython
    cdef through __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 baseline
    with the baseline re-asserted after: caught=13 survived=5. Three of the
    five were caught after adding tests; the remaining two are provably
    equivalent mutants (x.mul_add(2.0, C) is bit-identical to
    x * 2.0 + C because doubling is exact — so one of the nine fmadd
    sites is unobservable — and beta + beta vs 2.0 * beta, included as a
    control).

🤖 Generated with Claude Code

LoganAMorrison and others added 4 commits August 11, 2026 21:06
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
LoganAMorrison merged commit 00600e7 into master Aug 12, 2026
8 checks passed
@LoganAMorrison
LoganAMorrison deleted the claude/cython-to-rust/task-4.1-positron-muon-template-swap branch August 12, 2026 16:49
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.

1 participant