Skip to content

Commit 693d7d3

Browse files
kim-emKim Morrison
andauthored
perf(hex-int-factor): complete decomposed Phase-4 evidence (#9864)
* perf(hex-int-factor): complete phase 4 benchmark surface * perf(hex-int-factor): record phase 4 measurements * perf(hex-int-factor): document phase 4 acceptance Cost-model derivation: balanced rho uses the O(n^(1/4)) expected iteration bound; fixed-bound word routes are constant and direct-Nat p-1/ECM use a quadratic operand-cost bound. Replay, entry products, square divisors, and totient use quadratic widening-product bounds; order and primitive-root search use linear candidate bounds. Power forms use the conservative 2^n input-value bound, cyclotomic recursion is quadratic, and sigma exponentiation uses n log n. * docs(hex-int-factor): correct comparator contract PARI factor does not expose a mode restricted to Hex's trial-division-plus-rho portfolio, so an external ratio cannot diagnose dispatch or rho by itself. Require same-input internal controls for those claims and keep PARI/GMP-ECM as explicitly portfolio-mismatched informational comparisons. * docs(hex-int-factor): set balanced policy budget Use the existing Phase-4 one-second soft ceiling at the 80-bit top rung as the independent absolute criterion for deciding whether the current balanced route requires SQUFOF work. * docs(hex-int-factor): make internal controls falsifiable The table control has an explicit 1.25 ratio, while the balanced control uses the dispatcher's exact rho allocation, rejects failures, normalizes outputs, and has an explicit factor-of-two ratio. The comparator contract now reports every GMP-ECM rung with eligibility labels, scopes each endpoint, and leaves SQUFOF to a genuine within-Lean prototype comparison. * docs(hex-int-factor): compare complete rho pipeline Replace the mismatched complete-factorization/direct-split ratio with a preregistered comparison between default and rho-only route policies through the same completion and certificate path. Keep direct rho as the independent 2^(bits/4) scaling and profiling target. * perf(hex-int-factor): measure complete route policy Thread a private split policy through the production search so the diagnostic rho-only arm retains the exact public preprocessing, random-state progression, recursive certificate construction, and checked-factorization acceptance while disabling only smooth fallback after rho exhaustion. Use canonical full-factorization encodings for the seven balanced fixed pairs and retain raw rho solely as a 2^(bits/4) scaling target. Preregister per-target fixed ceilings and expected hashes, prevent fixed-term lifting, cover the full default-fuel schedule, and add the 1-through-10 kernel replay ladder ending at a 61-bit Pocklington certificate. The replay n^2 model follows one bounded guarded multiplication per exponent step on a widening accumulator. The square-divisor n^2 model follows sequential multiplication of n fixed-exponent entries into a linearly widening accumulator; its constant 8192-run loop clears the subprocess floor without changing the model. Sigma entry and totient ladders likewise sum linearly widening fixed-entry products, while sigma exponent uses the n log n surrogate for its growing exact quotient. Raw balanced rho uses the expected 2^(bits/4) cycle cost for factors of half the input width, and order uses one modular multiplication per scanned exponent. Run table trial division before perfect-power root search and classify fully table-factored powers by the gcd of their multiplicities, removing redundant root searches from the dominant small-dispatch batch without changing its route classification. * bench(hex-int-factor): collect honest phase 4 evidence Add per-rung closed-term-safe ECM anchors with literal expected hashes and absolute ceilings. Replace the obsolete collector registrations with the complete normal/forced policy pairs and validate every harness verdict, expected hash, internal ratio, control audit, and default-fuel result before writing an artifact. Use the same 256-input GMP-ECM subprocess shape for the factor-of-15 protocol control and every shared rung. Pass sigma 7, B1 1000, and B2 1 explicitly, so stage 2 is disabled by B2 < B1. Preserve no-factor outcomes as results rather than misreporting them as protocol failures, and mark ratio eligibility solely from the preregistered overhead-fraction rule. * bench(hex-int-factor): clear pinned spawn floor Double the constant square-divisor hot loop so its upper three rungs clear the unchanged ten-times subprocess floor even when pinned spawn overhead approaches 200 ms. This does not change the n^2 model, schedule, tolerance, or fixtures. Surface captured benchmark stdout and stderr when the collector rejects a child run. * bench(hex-int-factor): admit final square rung Raise only the per-call safety cap from 60 to 120 seconds so the already-declared 1024-entry hot-loop rung completes under single-CPU affinity. Keep the n^2 model, 0.20 tolerance, schedule, subprocess filter, and fixtures unchanged. * bench(hex-int-factor): enforce evidence budgets * fix(bench): accept matching hash status * fix(bench): keep control audit untimed * fix(bench): capture comparator version stream * docs(hex-int-factor): record corrected phase 4 evidence * bench(hex-int-factor): decompose phase 4 evidence * bench(hex-int-factor): record decomposed native evidence * bench(hex-int-factor): record kernel replay evidence * bench(hex-int-factor): expose mode 3 profile runners Cost-model derivation: runSmooth performs n copies of one fixed 80-bit unbalanced rho split, so its repetition parameter is linear. runPower performs n copies of one fixed exponent-80 power factorization, so its repetition parameter is also linear. These registrations are attribution-only and do not model operand-size scaling. * docs(hex-int-factor): report decomposed phase 4 evidence * docs(hex-int-factor): record five clean profiles * bench(hex-int-factor): address phase 4 review Cost-model derivation: the attribution runner repeats one fixed 80-bit ECM stage-1 call, so its declared work is linear in the repetition count. * docs(hex-int-factor): refresh phase 4 evidence * docs(hex-int-factor): commit five profile summaries * test(bench): exempt int-factor proof target from sweep --------- Co-authored-by: Kim Morrison <kim@lean-fro.org>
1 parent 2596c76 commit 693d7d3

32 files changed

Lines changed: 46591 additions & 136 deletions

‎HexIntFactor/SPEC/hex-int-factor.md‎

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,13 @@ prerequisite, specified there and sited in hex-basic.
295295

296296
### 0. Structural reductions, always applied
297297

298+
The dispatcher removes powers of two first, then runs table trial division.
299+
When the table completely factors the odd cofactor, the gcd of the recovered
300+
multiplicities identifies a perfect power without a second root scan. Only a
301+
residual above the table reaches the general perfect-power detector. This
302+
ordering keeps the dominant small-input path linear in the committed table
303+
while preserving the same factorization and structural-route result.
304+
298305
- **Powers of two**, by a dedicated trailing-zero count followed by one
299306
right shift. For positive `n`, the isolated lowest set bit is
300307
`n XOR (n AND (n - 1))`, so its `log₂` is exactly the multiplicity of two.
@@ -309,6 +316,8 @@ prerequisite, specified there and sited in hex-basic.
309316
full structural pipeline is reapplied to every popped search-stack entry,
310317
including recursive cofactors produced by a split; its exponent is multiplied
311318
by the entry's accumulated multiplicity before the result is merged.
319+
On a table-complete input the recovered prime-power multiplicities supply
320+
`k` directly; otherwise the general detector runs after table division.
312321
Strongly recommended rather than mathematically required: an earlier draft
313322
claimed Pollard `p − 1` and ECM "fail on prime powers, because the
314323
group they work in has no distinct primes to separate", and that is
@@ -319,7 +328,8 @@ prerequisite, specified there and sited in hex-basic.
319328
reasons to do it first.
320329
- **Small primes**, by trial division against hex-primality's
321330
`primeTable`. This is where most inputs finish and it is the only
322-
route whose cost is predictable.
331+
route whose cost is predictable. It precedes the general perfect-power root
332+
search on the odd cofactor.
323333

324334
### 1. Pollard rho with Brent's cycle detection
325335

@@ -1220,30 +1230,37 @@ Families:
12201230
times the direct-trial median. This internal control, rather than an
12211231
external system with a different portfolio, determines whether dispatch
12221232
overhead is invisible on the case that dominates call volume.
1223-
- **Balanced semiprimes** at 32, 48, 64, and 80 bits. Report `factor?` and
1224-
`Internal.rhoSplitCountedWith?` on the same ladder and seed. The direct arm
1225-
receives `defaultPrimeCertBudget.rhoRestarts` and `.rhoSteps`, matching the
1226-
public dispatcher's allocation. Output-agreeing wrappers return the least
1227-
factor, reject an error rather than hashing its attempt count, and admit a
1228-
ratio only when both arms succeeded. The public-dispatch median must be at
1229-
most twice the matched direct-rho median at every admitted rung. This is the
1230-
internal Route-1 control for dispatch and certificate construction; rho's
1231-
own two-sided registration and profile diagnose its scaling and hot path.
1232-
These controls and their 80-bit top rung are Phase-4 obligations; the
1233-
smaller pre-Phase-4 registrations do not satisfy them.
1233+
- **Balanced semiprimes** at 32, 40, 48, 56, 64, 72, and 80 bits. Report three
1234+
distinct surfaces on the same five-seed ladder: the full public `factor?`
1235+
pipeline; direct `Internal.rhoSplitCountedWith?`, which returns only one
1236+
normalized divisor; and completion from that precomputed divisor and exact
1237+
advanced random state through factorization of both sides, canonical merge,
1238+
and final `checkFactorization` acceptance. The direct-rho target alone has
1239+
the two-sided `2^(bits/4)` model and profile. The other two are fixed targets
1240+
because certificate construction dominates different lower rungs. Their
1241+
output hashes must agree, but `full / rho` and
1242+
`full / (rho + completion)` are explanatory decompositions rather than
1243+
acceptance thresholds. Rho-first dispatch and rho-first dispatch with its
1244+
smooth fallback disabled are not distinct algorithms and are not compared.
12341245
- **Smooth `p − 1` semiprimes** at the same sizes. Route 2; the base is
12351246
fixed so the benchmark measures the specified stage-1 success case.
1236-
- **`b^n ± 1`**, with and without the cyclotomic split, which is the
1237-
measurement that justifies the split existing.
1247+
- **`b^n ± 1`**, with and without the cyclotomic split, on identical
1248+
target-derived seeds and canonical outputs for exponents through 80. Seven
1249+
fixed repeats must show `split / generic ≤ 0.98`, or the split must prevent a
1250+
generic failure on the admitted family, to justify the factorization route.
1251+
Otherwise that route is removed; the independently checked split constructor
1252+
is a separate API.
12381253
- **Unbalanced semiprimes**, a small factor times a large one, where
12391254
ECM is expected to win and rho is expected to as well -- reported
12401255
together, because a route that never wins on any family should be
12411256
removed rather than kept.
12421257
- **Certificate replay**, `checkFactorization` in the kernel on
12431258
factorizations with `k` from `1` to `10` and factors up to `64` bits.
1244-
- **Order and primitive root**, primes up to `64` bits, reported
1245-
separately from the factorization of `p − 1` they depend on, so the
1246-
check's cost is visible next to the search's.
1259+
- **Order and primitive root**, a scan ladder through `1048589` plus fixed
1260+
50- and 61-bit primes whose order for base 3 is short, reported separately
1261+
from the factorization of `p - 1` they depend on. The two tracks expose both
1262+
the reference scan cost and downstream operand-size behavior without
1263+
pretending a linear scan is feasible for an arbitrary 64-bit order.
12471264
- **Generalized divisor sums**, with one ladder growing a prime-power
12481265
exponent through multi-million-bit output and one growing the number
12491266
of certified prime-power entries. Per-rung preparation constructs or
@@ -1255,7 +1272,7 @@ Families:
12551272

12561273
**Comparators.** PARI `factor` via cypari2 is **informational**:
12571274
PARI dispatches among trial division, SQUFOF, Pollard-Brent rho,
1258-
`p − 1`, and MPQS with tuned crossovers, and this library specifies
1275+
`p - 1`, and MPQS with tuned crossovers, and this library specifies
12591276
neither SQUFOF nor MPQS, so a required ratio would check an algorithm
12601277
that does not exist here. PARI does not expose a benchmark mode that restricts
12611278
`factor` to Hex's trial-division-plus-rho portfolio, so its selected route must
@@ -1292,6 +1309,13 @@ missing portfolio.
12921309
No advance claim is made on anything the quadratic sieve would reach,
12931310
because nothing here reaches it.
12941311

1312+
The proof-track replay is measured outside lean-bench with rotated paired
1313+
fresh-module builds. A baseline imports the shared replay input; ten candidate
1314+
modules add one `decide +kernel` theorem each for `k = 1..10`, with the final
1315+
case containing a 61-bit Pocklington-certified factor. Compiled profiles cover
1316+
one representative of every `libraries.yml` Phase-4 input family; proof-track
1317+
fresh builds do not create an additional compiled-profile family.
1318+
12951319
## The Mathlib layer
12961320

12971321
```lean
@@ -1453,7 +1477,7 @@ Neither `HexPrimality` nor `HexIntFactor` is in `libraries.yml` yet, so
14531477
the dependency claims above are draft prose rather than repository
14541478
state until those entries land.
14551479

1456-
## Open questions
1480+
## Deferred question
14571481

14581482
- **The default fuel schedule.** Stated above as a function of bit
14591483
length and not fixed. It should be set so that the 80-bit balanced

‎HexIntFactor/Small.lean‎

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,25 +137,48 @@ def SmallCandidate.scale (candidate : SmallCandidate) (multiplier : Nat) :
137137
{ factors := candidate.factors.map fun entry =>
138138
{ entry with exponent := entry.exponent * multiplier }
139139
residualBase := candidate.residualBase
140-
residualExponent := candidate.residualExponent * multiplier
140+
residualExponent := if candidate.residualBase = 1 then 1
141+
else candidate.residualExponent * multiplier
141142
route := candidate.route }
142143

143-
/-- Apply perfect-power reduction before table trial division to an odd
144-
cofactor. -/
144+
private def exponentGcd : List PrimePower → Nat
145+
| [] => 0
146+
| entry :: entries =>
147+
entries.foldl (fun common next => Nat.gcd common next.exponent)
148+
entry.exponent
149+
150+
/-- Apply table trial division and perfect-power reduction to an odd cofactor.
151+
A complete table factorization exposes perfect powers directly through the gcd
152+
of its multiplicities, avoiding a redundant root search on the dominant small
153+
dispatch path while preserving the structural route classification. -/
145154
private def oddCandidate (n : Nat) : SmallCandidate :=
146-
match perfectPower? n with
147-
| some (base, exponent) =>
148-
let out := trialFactors base
149-
{ factors := out.1.map fun e => { e with exponent := e.exponent * exponent }
150-
residualBase := out.2
151-
residualExponent := exponent
155+
let out := trialFactors n
156+
if out.2 = 1 then
157+
let exponent := exponentGcd out.1
158+
if 1 < exponent then
159+
{ factors := out.1
160+
residualBase := 1
161+
residualExponent := 1
152162
route := .perfectPower }
153-
| none =>
154-
let out := trialFactors n
163+
else
155164
{ factors := out.1
156-
residualBase := out.2
165+
residualBase := 1
157166
residualExponent := 1
158167
route := .trial }
168+
else
169+
match perfectPower? n with
170+
| some (base, exponent) =>
171+
let baseOut := trialFactors base
172+
{ factors := baseOut.1.map fun e =>
173+
{ e with exponent := e.exponent * exponent }
174+
residualBase := baseOut.2
175+
residualExponent := exponent
176+
route := .perfectPower }
177+
| none =>
178+
{ factors := out.1
179+
residualBase := out.2
180+
residualExponent := 1
181+
route := .trial }
159182

160183
/-- Remove the full power of two, then apply perfect-power reduction and table
161184
trial division to the odd cofactor. -/

‎bench/HexBench/IntFactorKernel.lean‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ private def orderTwoModSeven : OrderCert :=
5353
private def corruptOrder : OrderCert :=
5454
⟨2, 7, 2, ⟨2, [⟨1, .small 2⟩]⟩⟩
5555

56+
/- The final entry is a 61-bit prime, so this 1-through-10 ladder exercises
57+
both table leaves and an above-table Pocklington certificate while keeping the
58+
factor subjects in canonical ascending order. -/
59+
private def cert61 : PrimeCert :=
60+
.pock 1945555039024054273
61+
[(891154892214722695, 55, .small 2),
62+
(110189291828549774, 2, .small 3)]
63+
64+
private def replayCerts : List PrimeCert :=
65+
[.small 2, .small 3, .small 5, .small 7, .small 11,
66+
.small 13, .small 17, .small 19, .small 23, cert61]
67+
68+
private def replayFactors (count : Nat) : List PrimePower :=
69+
(replayCerts.take count).map fun cert => ⟨1, cert⟩
70+
71+
private def replayCase (count : Nat) : Factorization :=
72+
let factors := replayFactors count
73+
⟨(factors.map (·.prime)).prod, factors⟩
74+
5675
theorem factorizationValid : checkFactorization raw360 = true := by
5776
decide +kernel
5877

@@ -62,6 +81,36 @@ theorem factorizationCorrupt : checkFactorization corruptProduct = false := by
6281
theorem factorizationPocklington : checkFactorization rawPock7 = true := by
6382
decide +kernel
6483

84+
theorem replay1 : checkFactorization (replayCase 1) = true := by
85+
decide +kernel
86+
87+
theorem replay2 : checkFactorization (replayCase 2) = true := by
88+
decide +kernel
89+
90+
theorem replay3 : checkFactorization (replayCase 3) = true := by
91+
decide +kernel
92+
93+
theorem replay4 : checkFactorization (replayCase 4) = true := by
94+
decide +kernel
95+
96+
theorem replay5 : checkFactorization (replayCase 5) = true := by
97+
decide +kernel
98+
99+
theorem replay6 : checkFactorization (replayCase 6) = true := by
100+
decide +kernel
101+
102+
theorem replay7 : checkFactorization (replayCase 7) = true := by
103+
decide +kernel
104+
105+
theorem replay8 : checkFactorization (replayCase 8) = true := by
106+
decide +kernel
107+
108+
theorem replay9 : checkFactorization (replayCase 9) = true := by
109+
decide +kernel
110+
111+
theorem replay10 : checkFactorization (replayCase 10) = true := by
112+
decide +kernel
113+
65114
theorem orderValid : checkOrder orderTwoModSeven = true := by
66115
decide +kernel
67116

0 commit comments

Comments
 (0)