feat(univariate): Cantor-Zassenhaus linear-factor splitter and root finder #252
feat(univariate): Cantor-Zassenhaus linear-factor splitter and root finder #252DimitriosMitsios wants to merge 19 commits into
Conversation
… + soundness) Add an executable Cantor–Zassenhaus equal-degree (degree-one) splitter for field-root products over odd-cardinality finite fields, plugging into the `Univariate/Roots` `LinearFactorProductSplitter` interface alongside the smooth-subgroup splitter (PR Verified-zkEVM#244). - czRefine / czSplitWithShifts / czSplitLinearFactors: executable algorithm. - czSound: every emitted factor is linear (proved). - czShift_eq_linearFactor: the shift bucket `X + s` is `linearFactor (-s)`, the first link of completeness (prime-field isolation, no QR needed). - czLinearFactorProductSplitterOf: adapter taking `complete` as a parameter. Completeness (prime fields) is in progress on this branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Analytic core of the quadratic-residue routing: at a root a of p, shiftedPowModWith M D p s k evaluates to (a+s)^k, wrapping the framework's raw_eval_powModWith_eq_pow. Plus eval_X_add_C. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
czRefine_root_in_residue_bucket: for odd q and a+s != 0, a root a of p lands in one of the residue buckets gRes/gNon. Uses Fermat (a^q=a) to show the discriminating power (a+s)^((q-1)/2) squares to 1, hence is +/-1 at a. Adds eval_add helper and czRefine projection lemmas. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gcdMonic_linearFactor_of_root: at a root a of p, gcdMonic p (linearFactor a) = linearFactor a, via toPoly + normalize_eq_normalize + Monic.normalize_eq_self. This makes the s=-a quotient bucket of czRefine isolate the root. Adds linearFactor_toPoly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
monicNormalize fixes the monic linear factor X - a, completing the s=-a isolation chain (gZero = linearFactor a). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
czSplit_emits: a normalized represented linear factor with root a is emitted as a candidate. Plus represented_coeff_one_ne_zero, represented_zero_one_eq_false. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
czComplete_core: the recursion finds a root factor candidate for every root a (q odd, schedule reaches -a). Root preserved into a residue bucket at each non-isolating shift (QR routing), isolated at s=-a via the X+s bucket. czComplete: top-level completeness given prime-field schedule coverage. Adds eval_linearFactor_self and czSplitWithShifts_cons_else. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Concrete LinearFactorProductSplitter from Cantor-Zassenhaus, with validInput recording odd q, Frobenius, and prime-field schedule coverage. Completeness discharged by czComplete. The CZ splitter is now sound and complete over odd prime fields, with no sorry/native_decide. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…zmod) Discharges czComplete's validInput for ZMod q (odd prime) via ZMod.pow_card and schedule coverage (zmod_mem_czDefaultShifts). Covers KoalaBear/BabyBear. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…od 7 #guard checks that czSplitLinearFactors splits (X-2)(X-3) and X(X-1) into linear factors (including root 0), all factors linear, plus a czComplete_zmod instantiation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update module doc to state soundness/completeness are proved over odd prime fields and note the schedule-length efficiency caveat; trim over-explanation and an em-dash connective. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…actor Introduce HasRootFactor abbreviation for the repeated 'exists a linear root factor candidate' conclusion, removing the deeply nested existential from czComplete/_core/_zmod, czSplit_emits, and the splitter builder parameter. Add HasRootFactor.append_left/right to collapse the three membership lifts in czComplete_core to one line each. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the Cantor-Zassenhaus splitter into the framework root pipeline (rootsInFiniteField): czFiniteFieldContext for ZMod q plus czRoots, with czRoots_sound/czRoots_complete derived from the orchestrator's correctness and the splitter's validInput facts. Finds the distinct field roots of an arbitrary univariate f (stage 1 gcd(f, X^q - X) + CZ split). Tests over ZMod 7 incl. multiplicity collapse and a rootless irreducible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trim proof sketches and usage motivation from 11 declaration docstrings (shiftedPowModWith, czShift_eq_linearFactor, eval_shiftedPowModWith, eval_add, czRefine_root_in_residue_bucket, gcdMonic_linearFactor_of_root, czSplitWithShifts, czComplete_core, zmod_mem_czDefaultShifts, czComplete_zmod); drop inaccurate 'odd order' from czFiniteFieldContext. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trim the 'why abstract hypotheses' and 'stage 1 then splitter' how-detail from two section headers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reword the module-doc efficiency note so the fast large-field (short/random schedule) variant and its QR-separation completeness are clearly marked as unimplemented future work, not an existing guarantee. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🤖 PR SummaryMathematical Formalization
Formal Verification
Integration and Testing
Statistics
Lean Declarations ✏️ **Added:** 41 declaration(s)
📋 **Additional Analysis**Style and Naming Guidelines
Citation and Documentation Standards
📄 **Per-File Summaries**
Last updated: 2026-06-12 11:54 UTC. |
dhsorens
left a comment
There was a problem hiding this comment.
hey @DimitriosMitsios ! I'm happy with this PR. It's concise, and gets the foundational theory there. I think this is a good start to something like #254 which really digs into all the randomization theory.
Just a note on the style guide, if you could make those updates we can go ahead and get this merged in.
| /-- One Cantor–Zassenhaus refinement against shift `s`: returns the three | ||
| sub-factors `(gcd(p, X+s), gcd(p, w-1), gcd(p, w+1))` where `w` is the shifted | ||
| discriminating power. Their product is `p` (for squarefree `p` of odd `q`). -/ | ||
| def czRefine (M : CPolynomial.Raw.MulContext F) (D : CPolynomial.Raw.ModContext F) |
There was a problem hiding this comment.
note on style (from the review bot):
- Theorem Naming: Most theorems in CompPoly/Univariate/Roots/CantorZassenhaus.lean use mixed camelCase_snake_case (e.g., czShift_eq_linearFactor, czRefine_fst, czComplete_zmod). According to the style guide, theorems and proofs should use snake_case (e.g., cz_shift_eq_linear_factor, cz_refine_fst, cz_complete_zmod).
- Function Syntax: Several functions and proofs use fun x => ... (lines 222, 319, 368, 390). The guide prefers the maps-to arrow: fun x ↦ ....
There was a problem hiding this comment.
@dhsorens I went through the changes:
- Theorem naming: I think that the bot is confusing snake_case as "lowercase every component". I suggest that we keep names as they are because the same convention is found in many other places e.g.:
- Univariate/Basic.lean: natDegree_C, coeff_divX, divX_size_lt
- Univariate/EuclideanAlgorithm.lean: xgcdAux_bezout, monicNormalize_toPoly_eq_normalize
- Univariate/DivisionCorrectness.lean: divByMonic_toPoly_eq_divByMonic, leadingCoeff_inv_smul_monic
- Univariate/ToPoly/: toPoly_add, toPoly_mul_coeffC
- Function syntax: Fixed
Per the style guide and matching the neighboring SmoothSubgroup code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DimitriosMitsios is this implementation with |
|
@olympichek Yes they are effectively the same because the current PR implements a deterministic version of CZ. They are not the same algorithms though. If the randomized CZ is built on top of the current PR then they would diverge. |
This PR implements the Cantor–Zassenhaus root-finding algorithm over odd prime fields. It is implemented in two stages:
Univariate/Rootspipeline.The splitter is proved sound and complete over odd prime fields (no sorry, no native_decide).
The original paper includes a probabilistic choice of shift parameters. On the other hand, the current implementation goes through all field elements deterministically (the schedule 0..q−1), which guarantees completeness but is practical only on small fields. The shift schedule is included as an explicit argument in the splitter and its proofs, so correctness does not depend on how the shifts are chosen — swapping in random shifts is just a different schedule.