Skip to content

perf: build the disambiguation eliminant as a double resultant - #9432

Merged
kim-em merged 4 commits into
mainfrom
wave/roots-refinement-fix
Aug 26, 2026
Merged

perf: build the disambiguation eliminant as a double resultant#9432
kim-em merged 4 commits into
mainfrom
wave/roots-refinement-fix

Conversation

@kim-em

@kim-em kim-em commented Aug 22, 2026

Copy link
Copy Markdown
Owner

This PR fix the exponential eliminant growth that #9423's root-cause analysis located in QAdjoin.Roots.evalRoot?: the disambiguation eliminant was assembled by per-candidate lazy Horner arithmetic, multiplying eliminant degree at every step and dragging dead root-isolation work along (one intermediate mul? at the smallest irrational rung exceeded twenty minutes). The replacement computes one shared evaluation eliminant per component as the double resultant Res_y(p(y), Res_z(e(z), S - G(y,z))) with G = clearedOuter f, dilated by the common denominator, exactly as SPEC §Polynomial roots step 4 words it; retainZero?, evalDisambiguationPrec, and evalBall? are unchanged, evalRoot? and its sole consumer are deleted, and a compiled #guard pins the S^2(S^2-8) eliminant for X - sqrt 2 over Q(sqrt 2). The companion rebuilds the affected surface: evalEliminant_isRoot and evalEliminant_ne_zero in the norm-eliminant style, retainZero?_correct restated against the shared eliminant, and the componentRoots? soundness/completeness/totality proofs repaired, with all 23 axiom guards and 24 rfl pinning regressions passing and zero sorries. Rung timings move from 26 ms / killed-past-twenty-minutes at rungs 1/2 to 3.9 ms / 128 ms / 1.17 s / 7.9 s / 105.4 s at rungs 1/2/3/4/6, and the algebraic-poly ladder reads faster than its declared model; the QAdjoin roots ladder's per-call cap widens 60 to 180 s with the derivation in the commit body, its declared model unchanged.

Closes #9423

🤖 Prepared with Claude Code

Kim Morrison and others added 3 commits August 26, 2026 01:16
…luation eliminant

QAdjoin.Roots.evalRoot? built each candidate's disambiguation eliminant by
exact lazy AlgebraicRoot Horner arithmetic: every intermediate mul?/add?
re-isolated all roots of an intermediate eliminant whose degree multiplied
by deg(candidate.p) at each step, so a rung-2 root-ladder call exceeded 20
minutes against a 26 ms rung 1.

Replace that chain with one integer eliminant per component,
QAdjoin.Roots.evalEliminant: the double resultant
Res_y(p(y), Res_z(e(z), S - G(y, z))) with G = clearedOuter f and e the
component's squarefree norm-eliminant core, dilated by the common
denominator so its roots are the candidate evaluation values themselves.
Its degree is deg(p) * deg(e) (8 at ladder rung 2, versus 51 for the old
per-candidate chain), heights are Hadamard-bounded, and no root isolation
happens on the evaluation side. The bounded search retainZero? /
evalDisambiguationPrec / evalBall? is unchanged and still performs the
SPEC's remove-maximal-X-power plus primitive-part normalization
internally; componentRoots? now feeds it the shared evaluation eliminant.
candidateLift and evalShifted are the trivariate resultant inputs, built in
the clearedOuter style. evalRoot? and its only consumer coeffRoot? are
deleted (the companion lemma rebuild follows in the next commit).

Root-ladder timings on chungus2 (runQAdjoinRootsLadder): rung 1 26 ms ->
3.9 ms, rung 2 >20 min (killed) -> 128 ms, rung 3 1.19 s, rung 4 7.9 s,
rung 6 105.7 s; runAlgebraicRootsLadder rungs 2-6: 50 ms / 149 ms /
0.71 s / 6.2 s. A compiled #guard pins the S^2(S^2 - 8) eliminant for
X - sqrt2 over Q(sqrt2).

Progress: progress/2026-08-21T06-33-46Z.md (previous turn)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on eliminant

Replace the deleted evalRoot? lemma cluster (coeffRoot?_isSome/_sound,
evalRootFold_*, evalRoot?_isSome/_sound) with the two facts the bounded
zero test consumes about the new eliminant, both in
HexNumberFieldMathlib/Roots.lean:

- evalEliminant_isRoot: the true evaluation value is a root, via a
  hom-generic private resultant_hom_eq_zero for the inner
  Res_z(e, S - G) layer (over a proof-local CommRing/IsDomain on
  DensePoly ZPoly), the existing resultant_isRoot for the outer layer,
  clearedOuter_eval for the specialization identity, and a dilation
  transport toPolyC_dilate_isRoot.
- evalEliminant_ne_zero: choose a specialization value outside the
  finite set of cleared-lift evaluations at root pairs; both resultant
  layers are then coprime specializations with nonvanishing
  leading-coefficient padding, mirroring normEliminant_ne_zero.

Supporting lemmas: coeff/size characterizations of candidateLift and
evalShifted, the specialization identities candidateLift_map and
evalShifted_map_eval (the transposed double-sum mirror of
clearedOuter_evalAt), one_lt_size_liftOuter, and
size_pos_of_core_degree (a positive-degree squarefree core forces a
nonzero component, so componentRoots?_sound/_complete need no new
hypotheses).

retainZero?_correct now takes the shared evaluation eliminant directly;
componentRoots?_isSome/_sound/_complete drop the evalRoot? bind. All 23
companion axiom guards pass, with the evalRoot?_sound guard replaced by
one on retainZero?_correct.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With the double-resultant evaluation eliminant in place, the shipped
runQAdjoinRootsLadder schedule [1, 2, 3, 4, 6] measures 3.9 ms / 128 ms /
1.17 s / 7.9 s / 105.4 s per call on chungus2 (before the fix, rung 2
alone exceeded 20 minutes and was killed). The declared n^5 log^2 n model
predicts rung 6 at about 17x rung 4 (6^5 * log2(8)^2 / (4^5 * log2(6)^2)
= 69984/16384), i.e. ~134 s against the old 60 s cap, and the measured
105 s confirms the cap, not the model, was the binding constraint. 180 s
leaves ~70% headroom over the measured rung-6 time so the scientific
schedule completes on the timing hardware; the declared model line is
unchanged. Sibling runAlgebraicRootsLadder needs no change (6.2 s at its
top rung against its existing 60 s cap).

Fixes measured against the rung fixtures of issue #9423; the diagnostic
session's reference timings were rung 1 = 26 ms, rung 2 > 20 min.

Progress: progress/2026-08-22T08-39-56Z.md

Closes #9423

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kim-em
kim-em force-pushed the wave/roots-refinement-fix branch from bf68471 to 511750b Compare August 26, 2026 01:31
Add a non-unit-denominator guard for the shared double resultant, make the SPEC's per-component construction explicit, and account for its linear evaluation-variable degree in the root-ladder derivation.
@kim-em
kim-em merged commit 3ef7498 into main Aug 26, 2026
1 check passed
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.

bench-found: QAdjoin.roots? runs for minutes on degree-2 irrational-coefficient components

1 participant