A carry-clean adder-parametric modular multiplier (ECDLP Phase 2, Stage S6.3-36b) #
Category: 1-Mathlib (CSD-free; staged as a Mathlib-upstream candidate).
This module is the carry-clean (Θ(n)-qubit) counterpart of the fresh-ancilla keystone 36a
(VerifiedAdder). It bundles the carry-clean multiplier fold's consumption surface into a structure
VerifiedAdderCarryClean, proves the multiply correct + cost it once, parametric over any
conforming restored-clean step, and exhibits the existing carry-clean Cuccaro multiply
(cuccaroModMul, Stage 2b) as the faithfulness instance — recovering its proven (X · Y) mod N
correctness and 20·n² + 14·n Toffoli figure exactly.
The restored-clean discipline (the crux vs 36a) #
36a's VerifiedAdder models the FRESH-ANCILLA memory model: each bank has a PRIVATE clean predicate
clean k s with a precondition-free inter-bank frame cleanStable (bank j never touches bank k's
ancilla). That is exactly wrong for the carry-clean multiply, which reuses ONE scratch bank across all
n Horner steps. Modelling that single bank as n independently-fresh banks would make the
Θ(n)-qubit win vacuous.
So this variant carries a GLOBAL cleanliness predicate clean : State m → Prop (one bank, no per-bank
index) under a restored-clean discipline: each step has clean as a PRECONDITION of both its value
correctness and its cleanliness, and RE-ESTABLISHES clean as a POSTCONDITION (cleanRestored). The
fold maintains "the bank is clean" as a loop invariant (clean in → step → clean out), threaded step to
step — not "all banks independently clean throughout". This is the genuine single-bank-reuse invariant;
cf. cuccaroModMul_clean (the all-scratch-restored property that enables the reuse).
What is proved #
VerifiedAdderCarryClean— the structure with the restored-clean fields (correct/cleanRestored/presYall gated onclean s,digitStable,digit,toffoli,hNpos,hToffoli).genMulCC/genMulCC_correct— the multiplier fold + its correctnessacc = (accVal digit · y) mod N, the same induction ascuccaroModMul_invariant, now citing the abstract fields with the restored-clean loop invariant. REUSESaccVal/accVal_mod_step/accVal_succfrom 36a UNCHANGED.genMulCC_clean— the shared bank restored after the full multiply (theΘ(n)reuse is real).genMulCC_toffoli— the cost recurrencen · step.toffoli.cuccaroAdder— the faithfulness instance from aCuccaroMulLayoutand the existing carry-clean Cuccaro step (the REALcuccaroModMulStep, independently derived; NOT 36a's fresh-ancillahornerStep);genMulCC cuccaroAdder = cuccaroModMul(definitionally),genMulCC_cuccaroAdder_correctreproducescuccaroModMul_correct's(X · Y) mod N, andgenMulCC_cuccaroAdder_toffoli = 20·n² + 14·nrecoverscuccaroModMul_toffoli. The abstraction is non-lossy.
Honest scope #
36b validates the keystone generalises across memory models (fresh-ancilla 36a + carry-clean here), and
gives the first generic-multiplier instance that moves the cost (20n² + 14n < the fresh-ancilla
30n² for n ≥ 2; equal-order, the genuine prize is the Θ(n)-vs-Θ(n²) qubit collapse below),
FULLY Boolean-verified (the whole stack is CCX-circuit denote, no amplitude wall, unlike a
measurement adder). This is the MULTIPLY cost; the score-dominant term is the INVERTER (36c, safegcd
parametric, the ~67%). No ECDSA score change is claimed yet (still needs 36c + harness #7). The
Θ(n)-qubit win is real: the restored-clean invariant (this module's cleanRestored, realised by
cuccaroModMul_clean) is exactly what lets the single scratch bank be reused across all n steps.
Load-bearing hypothesis: N odd (inherited from the doubler's parity flag-uncompute), which holds
for the ECDLP prime field.
The carry-clean variant interface #
A verified carry-clean per-bit modular-multiply step, the substitutable atom of the
single-scratch-bank multiplier fold. Bundles exactly the carry-clean fold's consumption surface: a
per-step circuit step k over a shared accumulator B and multiplicand Y, the digit digit k s
step k folds, a GLOBAL cleanliness predicate clean s (one reused scratch bank, no per-bank index),
and the restored-clean facts the fold consumes. Unlike 36a's fresh-ancilla signature, clean is a
PRECONDITION of correct / cleanRestored / presY and is RE-ESTABLISHED by cleanRestored; the fold
threads "bank clean" as a loop invariant. N is the modulus; hNpos keeps the running residue
genuine.
Step
k's circuit on the shared bank.The shared accumulator register.
The shared multiplicand register.
The digit (
0/1) stepkfolds, read off the state.The GLOBAL single-bank cleanliness precondition (no per-bank index).
- toffoli : ℕ
The per-step Toffoli cost.
The modulus is positive.
The step's gate-list Toffoli count is
toffoli.- cleanRestored (k : ℕ) (s : State m) (c Yval : ℕ) : k < n → self.clean s → Yval < N → regValRange self.B s n = c → c < N → regValRange self.Y s n = Yval → self.clean (denote (self.step k) s)
Cleanliness restoration (the load-bearing POSTCONDITION). From a clean bank with the same value preconditions, step
kre-establishescleanon the output state. This is what makes the single-bank reuse sound: each step consumes a clean bank and gives one back. - presY (k : ℕ) (s : State m) (c Yval : ℕ) : k < n → self.clean s → Yval < N → regValRange self.B s n = c → c < N → regValRange self.Y s n = Yval → regValRange self.Y (denote (self.step k) s) n = Yval
Multiplicand persistence. Step
kleavesYat its value. Digit frame. Step
jleaves stepk's digit intact (the digit lives on the read-only multiplier register, preserved by every step).
Instances For
The generic carry-clean multiplier #
The adder-parametric carry-clean modular multiplier. Fold the restored-clean step over the n
banks on the single shared scratch bank.
Equations
- Reversible.genMulCC A = List.flatMap id (List.map (fun (j : ℕ) => A.step j) (List.range n))
Instances For
The first k steps of the loop (the induction handle).
Equations
- Reversible.genMulCCUpto A k = List.flatMap id (List.map (fun (j : ℕ) => A.step j) (List.range k))
Instances For
Step k's digit survives the prefix of steps [0, p) (every step preserves all digits, by
digitStable).
The generic carry-clean multiply-loop invariant. After the first k steps (k ≤ n) from a
clean bank with B = 0, the accumulator holds (accVal digit k · y) mod N, the bank is STILL clean
(the restored-clean loop invariant: clean in → correct + cleanRestored → clean out), and Y still
holds y. The same induction as cuccaroModMul_invariant, now citing the abstract fields.
The adder-parametric carry-clean modular multiply (the keystone). For any conforming
restored-clean step, with B = 0, Y = y < N, and the bank clean, genMulCC A leaves the
accumulator holding (accVal digit n · y) mod N. Proved ONCE, parametric over A.
The shared bank is restored clean after the full multiply (so the Θ(n) reuse is real).
Derived cost recurrence: n · A.toffoli. The fold is n copies of the step composed through
the concatenation; multiplier_toffoli turns the gate list into the sum of the per-step counts.
The faithfulness instance: the carry-clean Cuccaro multiply (the non-lossy guard) #
cuccaroAdder instantiates VerifiedAdderCarryClean with the existing Stage-2b carry-clean Cuccaro
step cuccaroModMulStep (= cuccaroModDouble ++ cuccaroCModAdd), reading the proven cuccaroModDouble
/ cuccaroCModAdd lemmas into the abstract fields via the per-step workhorse cuccaroModMulStep_spec.
The generic theorems then recover cuccaroModMul's (X · Y) mod N correctness and 20·n² + 14·n cost
exactly.
The GLOBAL single-bank clean precondition for the Cuccaro multiply: every scratch wire of the
shared bank mod restored to false, plus the modulus preset Nreg = N. This is the carry-clean
"bank ready for a step" predicate; cuccaroModMul_clean is exactly its restoration.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The read-only multiplier register survives one Cuccaro step (both sub-gadgets leave it external).
The per-step Cuccaro workhorse (clean in → step → clean out). From a clean bank with
Acc = c < N and Y = y < N, one Horner step (cuccaroModDouble ++ cuccaroCModAdd) advances the
residue to (2·c + [X bit]·y) mod N, restores the whole shared bank clean, and preserves Y. The
refactor of the succ case of cuccaroModMul_invariant to consume / produce cuccaroClean.
The faithfulness instance: the carry-clean Cuccaro multiply as a VerifiedAdderCarryClean.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Definitional faithfulness: the generic carry-clean multiplier on the Cuccaro step IS the
existing cuccaroModMul.
Correctness faithfulness (the non-lossy guard): genMulCC_correct on the Cuccaro instance
recovers cuccaroModMul_correct's (X · Y) mod N statement exactly. The generic accVal digit n
lands on regValRange X s n via accVal_eq_hornerVal + regValRange_eq_hornerVal_bits.
Cost faithfulness (the non-lossy guard): the cost recurrence on the Cuccaro instance recovers
cuccaroModMul_toffoli's figure 20·n² + 14·n exactly — the first generic-multiplier instance below
the fresh-ancilla 30·n² baseline, fully Boolean-verified.