Skip to content

Discharge E7: the record was a wrong-basis comparison, and a published performance claim is off by half - #43

Merged
salindne merged 4 commits into
masterfrom
pr34-discharge-e7
Aug 28, 2026
Merged

Discharge E7: the record was a wrong-basis comparison, and a published performance claim is off by half#43
salindne merged 4 commits into
masterfrom
pr34-discharge-e7

Conversation

@salindne

Copy link
Copy Markdown
Owner

E7 was the one erratum deliberately absent from ERRATA.md, reserved for weeks pending "the operation counter run over both trees". The counter was run. The record did not survive it, and the more valuable finding turned out to have nothing to do with E7.

E7's record was a comparison against the wrong sibling

The record was not merely stale in a plan file. It was a comment in verification/driver.py, duplicated in verification/README.md, justifying a live exclusion filter.

claim, as written verdict
"a different ccs layout (ccs[2][3] against ccs[1][3][1])" false. ccs[2][3] is posReduced's, byte-identical to the timings tree. ccs[1][3][1] is negReduced's
"opposite signs on some terms" false. dw := v0+vp0-u0*f4-upp0*upp1 is byte-identical between the timings ADD :197 and posReduced :394. The + form is negReduced :387 — it is the basis, and a basis is supposed to differ in sign
"reference a nch23_splitG2_UTL.mag that the canonical tree does not have" false twice. A stale doc comment shared with posReduced, not a load. The real load is nch2_splitG2_UTL.mag, which canonical ships in both genus-2 bases and at genus 3
"same function names as the canonical files" partly false. True at genus-2 split (11/11). Genus-2 ramified has zero overlap, every name _RAM-suffixed. Genus 3 is 24/27
"an earlier generation of the split formulas" wrong scope. The filter it justifies excludes ramified files too

All three specific claims fail the same way: they compare the genus-2 timings split files against negReduced. That tree is posReduced — this repository's genus-2 basis of record, and what the thesis presents. Two other artifacts already said so, so the comment contradicted its own siblings.

The polarity was backwards too. The record called the timings trees "a divergent generation". They are the frozen 2020 original; the canonical tree is what moved, by dated post-publication improvements.

How it stayed wrong, which is the part worth carrying

The record lived in a comment that justified excluding the very files that would have exposed it. That made it unfalsifiable, and it simultaneously froze the tree it described — which is why the clearest evidence of what that tree actually is turns out to be PR10's Deg22ADDDeg2ADD rename, absent there because the tree is excluded from every gate.

The tell that would have caught it at any point in two years: the "missing" filename appears in posReduced's own addition file.

A record that justifies not looking at its subject cannot decay gracefully. That is NEW_WORK.md N36.

The more valuable finding, and it is not E7

Thesis/chapter5.tex:2669-2671 claimed split model arithmetic is "about 20% slower than ramified arithmetic for genus 2 curves". Recomputed from the raw file the published genus-2 plots were produced from, g2/timings/processing/poutg2_complete.raw:

bits 2 4 8 16 32 64 128 256 512 1024
addition 0.7% 28.0% 33.2% 35.1% 37.5% 33.4% 31.8% 28.0% 24.4% 22.2%
doubling 40.5% 52.8% 31.8% 31.8% 44.4% 38.7% 40.4% 33.9% 29.8% 28.0%

"About 20%" holds at one cell of eighteen from 4 bits up. Corrected to give both ranges: addition 22% to 38%, doubling 28% to 53%.

Ranges rather than a single revised figure, for reasons recorded in E-T15:

  • doubling is worse than addition at nine of ten field sizes, and one number hides that;
  • the trend is not flat — addition peaks at 32 bits and falls monotonically to 1024, the signature of a fixed overhead being amortised as field arithmetic gets dearer, where a constant percentage asserts the opposite;
  • the corrected statement is stronger for the thesis's own argument, not weaker.

The 2-bit column is excluded by explicit scope rather than discarded: there addition differs by 0.7% while doubling differs by 40.5%, which is a chain dominated by interpreter overhead rather than field arithmetic.

This is not an E7 consequence. Both columns come from one tree, so the comparison is internally valid whatever that tree's relation to the canonical one. The error was in reading the numbers, not in producing them.

Also landed

E-T16 — both chapters described the doubling experiment as "series of thousands of additions" and gave no trial count. The committed raw records an identical schedule at both genera, from 500,000 chain steps at 2 bits down to 45,000 at 1024, every figure the mean of 5 trials. "Thousands" is true of none of them, and a reader told "thousands" cannot tell that the 1024-bit points rest on a tenth the samples of the 2-bit ones — which is what they need before reading a trend off the tails of those plots.

E25g2/timings/nch2_g2_complete.mag:29-30 ships length := 5000; trials := 1; where its genus-3 sibling carries 500000; 5 and where every genus-2 raw header, its own included, records # Average over: 5. The committed genus-2 script cannot reproduce its own published figures. Not fixed: editing it would make it claim to reproduce a 2020 run on a 64-core Xeon 7550 that no current machine matches, and the thesis disclaims absolute timings. Recording the real parameters where a reader finds them is the useful half, and the seeds are 643202358 and 1362920179.

One file deletedg3/timings/formulas/splitFormulas/nch2_splitG3_rad.mag, byte-identical to nch2_splitG3_onlyFreq.mag (both md5 6cb5151a4272fa1cfbbb5ab549054231), declaring the same *_onlyFreq names, referenced by nothing anywhere in the repository, while named after another author's work and containing ours.

Recorded, not fixed — three items needing an editorial call

Three latent driver.py defects. They fire only if the timings tree becomes discoverable, which nothing does. The first is a real bug today: :125-126 keys families on (model + basis, genus, kind) with basis empty off the posReduced/negReduced paths, so a timings ramified file keys identically to its canonical counterpart and seen.setdefault(key, {})[op] = path silently overwrites, decided by os.walk order. Whoever attempts the reach must fix it first.

The guard-width difference, and it is worth repeating. The timings copy of nch2_ramifiedG2_ADD tests only dw21 where canonical tests IsZero(dw20) and IsZero(dw21), so its return condition is wider: on dw21 = 0, dw20 ≠ 0 it returns the identity where canonical raises. A silent wrong value against a loud crash, neither correct. This is E1's region and is cross-referenced there. A widened and adds no operator, moves no count and changes no fingerprint, so every instrument in this PR is structurally blind to it — it was found by reading the two guards side by side.

previousBest/lange_2005.mag:1-11 carries an author line over an implementation of Lange's published formulas, where the other five previousBest/ files carry none. Left alone: the header is accurate about who wrote the transcription, and rewording published-adjacent provenance is the author's call.

What is not claimed

No differential ran under the gates. The two trees are indicated to compute the same group law, not shown to. What supports it: an arithmetic operator census per shared function, identical in 22 of 24 genus-3 split additions and 8 of 11 genus-2 split additions against posReduced, survivors differing by single operations; and against negReduced none match with the up/down adjustment pairs swapped, which is what opposite adjustment senses look like. Establishing agreement properly needs an opt-in reach and an adapter for the packed-tuple interface, deliberately scoped out.

14 of 82 functions are uncovered by that census — 10 dispatchers, 3 Precomputes, 1 curve generator — and the dispatchers are exactly where operand order and the equal-divisor route live. E7 says so.

A negative finding on this evidence is tolerable where a positive claim would not be, and the entry states that rather than leaving it to be inferred.

Nothing was re-measured by a fresh timing run. The 20% table is the published raw data recomputed.

No formula gate applies. No canonical .mag file is touched, so Magma, blockcheck and the rest have nothing to say here. The only .mag in the diff is the deleted duplicate in the excluded tree.

Two deviations from the plan

The rad.mag deletion moved from its own commit into the E7 commit, because E7's text records the deletion and an entry asserting a deletion that had not happened would be false at that commit. And the genus-2 irreproducibility went to the root ERRATA.md as E25 rather than Thesis/ERRATA.md, that script not being part of Thesis/ and the file's own header scoping it to thesis divergences.

The plan's recorded acceptance criterion — "a normalising pre-pass validated by producing ZERO count change on the canonical tree" — is deleted, both its premises having been measured dead: the two latexConverter.py faults it works around do not exist in the counter of record, and maginterp.discover parses every timings formula file cleanly with matching function counts.

Gates

opcount.py --json byte-identical, md5 2a93ffaa1e39fcbbe2bb3a1abfc878ce before and after · driver --strict 13,746/13,746, 0 mismatched, matching the recorded baseline · whitebox PASS · dominance clean on 39 files · selftest 19 passed, 0 failed, 0 skipped · thesis build 266 pages, 0 errors, 0 undefined references, 0 undefined citations, 0 rerun requests · readme-paths 100/100 · check_paths --strict 259/259 · CI ruff selection and compileall clean · ThesisPublished/ byte-identical · no canonical .mag touched.

`driver.py`'s exclusion comment and its duplicate in `verification/README.md` described
`g2/timings/` and `g3/timings/` as an earlier generation in which "every body differs",
citing a different `ccs` layout (`ccs[2][3]` against `ccs[1][3][1]`), opposite signs on
some terms, and a `nch23_splitG2_UTL.mag` the canonical tree does not have.

All three are false, and false the same way: they compare the genus-2 timings **split**
files against `negReduced`.  That tree is `posReduced`, where `ccs[2][3]` and
`dw := v0+vp0-u0*f4-upp0*upp1` are byte-identical to it; `ccs[1][3][1]` and the `+` signs
are `negReduced`'s, which is the basis and is supposed to differ in sign.  And
`nch23_splitG2_UTL.mag` is a stale doc comment that the timings tree **shares** with
`posReduced`, not a load: the real load is `nch2_splitG2_UTL.mag`, which canonical ships in
both genus-2 bases and at genus 3.

Two further corrections.  The scope sentence said "the split formulas", but the filter it
justifies excludes ramified files too.  And "same function names as the canonical files"
holds only at genus-2 split: genus-2 ramified has zero overlap, every name `_RAM`-suffixed,
and at genus 3 the timings tree carries `Deg22ADD`/`Deg22ADDUP`/`Deg22ADDUP2` where
canonical has `Deg2ADD`/`Deg2ADDUP`/`Deg2ADDUP2`, which is PR10's rename that this tree
never received.

What the trees actually are is now recorded: the frozen 2020 generation, repackaged for a
chain-driving harness rather than derived differently -- packed-tuple dispatchers so a
Fibonacci loop chains without repacking polynomials, tuple returns where the canonical
files carry `*_DEBUG` guards, inline `ccs` reads where canonical hoists them, and `_RAM`
suffixes so several implementations load in one Magma session.

The exclusion itself stands, for the two reasons that survive: they are not the formulas of
record, and half of them carry zero `*_DEBUG` labels, so the coverage half of a driver pass
would be vacuous for them.

Gates: `opcount.py --json` **byte-identical**, md5 2a93ffaa1e39fcbbe2bb3a1abfc878ce before
and after; `driver.py --list` identical, still 15 families and the same 10 excluded paths;
selftest 19 passed 0 failed 0 skipped; readme-paths 100/100; check_paths --strict 259/259;
CI ruff selection and compileall clean.  No .mag file touched.
E7 has been a reserved number deliberately absent from `ERRATA.md`, pending "the operation
counter run over both trees".  The measurement was made and it contradicted the record that
reserved it, so E7 lands **downgraded: not a defect in the published figures.**

What the timings trees are: the frozen 2020 generation, kept because the published timing
figures were produced from it, repackaged for a chain-driving harness rather than derived
differently.  The old record's polarity was backwards -- it called them "a divergent
generation", where in fact they are the frozen original and the canonical tree is what
moved, by dated post-2020 improvements.

Three limits are stated in the entry rather than left to be assumed.  No differential was
run under the gates, so the trees are *indicated* to compute the same group law, not shown
to.  14 of 82 functions are uncovered by the census -- 10 dispatchers, 3 Precomputes, 1
curve generator -- and the dispatchers are exactly where operand order and the
equal-divisor route live.  And an operator census is structurally blind to a guard
predicate, which matters because of the next paragraph.

**E1 gains a cross-reference, and the timings copy fails the other way.**
`nch2_ramifiedG2_ADD.mag:98` in the timings tree tests only `dw21` where the canonical file
at `:284` tests `IsZero(dw20) and IsZero(dw21)`.  Its return condition is *wider*: on
`dw21 = 0, dw20 != 0` it returns the identity where canonical raises.  A silent wrong value
against a loud crash, neither correct, and no count, census or fingerprint could have found
it -- a widened `and` adds no operator.  Not fixed: that tree is the provenance of published
figures and is not edited.

Also recorded, not fixed: three latent `driver.py` defects that fire only if the timings
tree becomes discoverable.  The first is a real bug today -- `:125-126` keys families on
`(model + basis, genus, kind)` with `basis` empty off the posReduced/negReduced paths, so a
timings ramified file collides exactly with its canonical counterpart and one silently
overwrites the other on `os.walk` order.

**One file deleted:** `g3/timings/formulas/splitFormulas/nch2_splitG3_rad.mag`, byte-identical
to `nch2_splitG3_onlyFreq.mag` (both md5 6cb5151a4272fa1cfbbb5ab549054231), declaring the same
`*_onlyFreq` names, referenced by nothing anywhere in the repository, while named after
another author's work and containing ours.  Folded into this commit rather than a later one
because E7 records the deletion and an entry asserting a deletion that had not happened
would be false at that commit.

`previousBest/lange_2005.mag`'s author line over Lange's formulas is recorded and left
alone: the header is accurate about who wrote the transcription, and rewording
published-adjacent provenance is the author's call.

Gates: opcount.py --json byte-identical, md5 2a93ffaa1e39fcbbe2bb3a1abfc878ce; selftest 19
passed 0 failed 0 skipped; readme-paths 100/100; check_paths --strict 259/259; no reference
to the deleted file remains.  No canonical .mag file touched.
`chapter5.tex:2669-2671` claimed split model arithmetic is "about 20% slower than ramified
arithmetic for genus 2 curves".  Computed from the raw file the published genus-2 plots were
produced from, `g2/timings/processing/poutg2_complete.raw`, columns OXAR/OXAS for addition
and OXDR/OXDS for doubling:

  bits        2     4     8    16    32    64   128   256   512  1024
  addition  0.7% 28.0% 33.2% 35.1% 37.5% 33.4% 31.8% 28.0% 24.4% 22.2%
  doubling 40.5% 52.8% 31.8% 31.8% 44.4% 38.7% 40.4% 33.9% 29.8% 28.0%

"About 20%" holds at one cell of eighteen from 4 bits up, and the nearest is 22.2%.
Corrected to give both ranges: addition 22% to 38%, doubling 28% to 53%, over 4 to 1024
bits.

Ranges rather than a single revised figure, for three reasons recorded in `E-T15`.  Doubling
is worse than addition at nine of ten field sizes, and one number hides that.  The trend is
not flat -- addition peaks at 32 bits and falls monotonically to 1024, the signature of a
fixed overhead being amortised as field arithmetic gets dearer, where a constant percentage
asserts the opposite.  And the corrected statement is *stronger* for the thesis's own
argument, not weaker.

The 2-bit column is excluded by an explicit scope, not discarded: there addition differs by
0.7% while doubling differs by 40.5%, which is a chain dominated by interpreter overhead
rather than field arithmetic.

**Both methodology sentences corrected too**, `chapter5.tex:2566` and `chapter6.tex:2604`.
They described the doubling experiment as "series of thousands of additions" and gave no
trial count.  The committed raw records both: an identical schedule at both genera running
from 500,000 chain steps at 2 bits down to 45,000 at 1024, every figure the mean of 5
trials.  "Thousands" is true of none of them.  A reader told "thousands" cannot tell that
the 1024-bit points rest on a tenth the samples of the 2-bit ones, which is what they need
before reading a trend off the tails of those plots.  Recorded as `E-T16`.

**And the genus-2 script cannot reproduce its own figures**, recorded as `ERRATA.md` **E25**.
`g2/timings/nch2_g2_complete.mag:29-30` ships `length := 5000; trials := 1;` where its
genus-3 sibling carries `500000; 5` and where every genus-2 raw header, its own included,
records `# Average over:  5`.  Not fixed: editing it would make it claim to reproduce a 2020
run on a 64-core Xeon 7550 that no current machine matches, and the thesis disclaims absolute
timings.  Recording the real parameters where a reader finds them is the useful half, and the
seeds are `643202358` and `1362920179`.

None of this is an E7 consequence.  Both columns of the 20% comparison come from one tree, so
it is internally valid whatever that tree's relation to the canonical one; the error was in
reading the numbers, not in producing them.

Not re-measured, deliberately: these are the published raw data recomputed, not a fresh run.

Build: 266 pages, 0 errors, 0 undefined references, 0 undefined citations, 0 rerun requests.
readme-paths 100/100, check_paths --strict 259/259.  `ThesisPublished/` untouched; no .mag
file touched.
…oking

Ground rule 7: this PR corrects the thesis and establishes a result, so it owes an entry.

N36 records the mechanism rather than just the correction.  E7 was a comparison against the
wrong sibling, written into a code comment, which then justified excluding the very files
that would have exposed it.  The exclusion made the record unfalsifiable and simultaneously
froze the tree it described, which is why a two-year-old rename absent from that tree is the
clearest evidence of what the tree actually is.  A record that justifies not looking at its
subject cannot decay gracefully.

The companion finding is the opposite shape and equally cheap: a published performance claim
contradicted by raw data committed alongside it, which nobody had recomputed.

Limits stated inline per the file's own rule: no differential ran under the gates so the two
trees are indicated rather than shown to agree; 14 of 82 functions are uncovered by the
census, the dispatchers among them; and the widened `and` in the timings `Deg2ADD` guard is
invisible to every instrument used, having been found by reading.

Gates: readme-paths 100/100, check_paths --strict 259/259.  No .mag file touched.
@salindne
salindne merged commit 83b6d93 into master Aug 28, 2026
6 checks passed
@salindne
salindne deleted the pr34-discharge-e7 branch August 28, 2026 01:00
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