An adder-parametric modular multiplier — the substitution keystone (ECDLP Phase 2, Stage S6.3-36a) #
Category: 1-Mathlib (CSD-free; staged as a Mathlib-upstream candidate).
This module turns re-costing the modular multiplier with a different (cheaper) per-bit step from a
per-circuit re-proof into an instantiation. It bundles the multiplier fold's consumption surface
into a structure VerifiedAdder, proves the multiply correct + cost it once, parametric over any
conforming step, and exhibits the existing corpus multiplier (mulLoop, S6.3d-2b) as the faithfulness
instance — recovering its proven (X · Y) mod N correctness and 30 · n² Toffoli figure exactly.
Interface level (the scout-chosen granularity) #
The Horner loop body is hornerStep = modDouble ++ cModAdd (S6.3d-2a). The multiplier fold
(mulLoop_invariant) consumes each bank as a black box through exactly four facts:
- value correctness
acc ← (2·acc + [bit]·Y) mod N(hornerStep_correct), - multiplicand persistence
Y ← Y(hornerStep_preserves_Y), - an inter-bank frame: bank
jleaves bankk's clean / preset wires untouched (k ≠ j) (notTouches_preserved+hInter), - a derived per-step Toffoli cost (
hornerStep_toffoli).
So the interface is placed at the per-bit Horner-step level (the substitutable atom whose cost is
adder-dominated), NOT one level finer at the bare controlled adder. Placing it at the step level is the
least-refactor non-lossy choice: the existing modular stack (modReduce / modDouble / cModAdd) is
reused wholesale inside the faithfulness instance, with zero re-derivation. The doubling is absorbed
into the step's cost, so the cost recurrence is n · step.toffoli with no separate overhead term.
What is proved #
VerifiedAdder— the structure with exactly the four consumed fields (+digit,clean,hNpos).genMul/genMul_correct— the multiplier fold + its correctnessacc = (accVal digit · Y) mod N, the SAME induction asmulLoop_invariant, now citing the abstract fields (genuine, parametric).genMul_toffoli— the cost recurrencen · step.toffoli(derived from the gate-list sum).corpusAdder— the faithfulness instance built from aMulLoopLayoutand the existinghornerSteplemmas;genMul corpusAdder = mulLoop(definitionally),genMul_corpusAdder_correctreproducesmulLoop_correct's(X · Y) mod Nstatement, andgenMul_corpusAdder_toffoli = 30 · n²recoversmulLoop_toffoli. The abstraction is non-lossy.
Carve line (what this is, and is NOT) #
This is the keystone abstraction: interface + parametric multiplier + faithfulness. It is reusable;
every future fresh-ancilla step is one VerifiedAdder instance. No ECDSA score change is claimed.
Deferred and the precise propagation cost:
- 36b (carry-clean Cuccaro multiply). A cheaper BASE (ripple) adder does NOT propagate to the
score by instantiation alone. The modular wrappers
modAdd/cModAdd/modDoublesit between the ripple adder and the step; a new base adder forces re-deriving them to build a new step. 36b inherits the arithmetic core (accVal/accVal_mod_step) and the cost-recurrence shape, but must build the new step (the modular wrappers on the Cuccaro base) before instantiating. Moreover the Cuccaro multiply reuses a SINGLE scratch bank (carry-clean,Θ(n)qubits), so its frame discipline is a global restored-clean invariant carried per step, NOT this module's per-bank disjointclean. Serving it therefore needs a carry-clean variant ofVerifiedAdder(acleanrestored by every step, consuming the value precondition), not this fresh-ancilla signature. That variant is 36b's to build; what it reuses unchanged isaccVal/accVal_mod_stepand thegenMul_toffolishape. - 36c (generic inverter / safegcd). Same pattern one layer up.
- 36d (Gidney measurement adder). Amplitude-gated per #21 / #31.
Honest cost #
genMul_toffoli A derives n · A.toffoli; the corpus instance has A.toffoli = 30 · n
(hornerStep_toffoli), so genMul_corpusAdder_toffoli = 30 · n² — equal to mulLoop_toffoli. This is
the Θ(n²)-qubit fresh-ancilla figure inherited from the corpus step.
The abstract accumulator value and its modular step #
Process-step accumulator: accVal d 0 = 0, accVal d (k+1) = 2 · accVal d k + d k. The MSB-first
Horner reconstruction folded by a multiplier whose bank k contributes digit d k.
Equations
- Reversible.accVal d 0 = 0
- Reversible.accVal d k.succ = 2 * Reversible.accVal d k + d k
Instances For
Bridge to the Horner reconstruction. accVal (fun j => bits (n-1-j)) k = hornerVal bits n k:
both satisfy 0 / 2·prev + bits (n-1-k). Lets the faithfulness instance land accVal digit n on
regValRange X s n via regValRange_eq_hornerVal_bits.
The interface #
A verified per-bit modular-multiply step, the substitutable atom of the multiplier fold. Bundles
exactly the fold's consumption surface for the fresh-ancilla discipline: a per-bank circuit step k
over a shared accumulator B and multiplicand Y, the digit digit k s bank k folds, the per-bank
cleanliness predicate clean k s, and the four facts the fold induction consumes (correct, presY,
the inter-bank frame cleanStable / digitStable, the cost hToffoli). N is the modulus; hNpos
keeps the running residue genuine.
Bank
k's step circuit.The shared accumulator register.
The shared multiplicand register.
The digit (
0/1) bankkfolds, read off the state.Bank
k's private clean / preset precondition.- toffoli : ℕ
The per-step Toffoli cost.
The modulus is positive.
The step's gate-list Toffoli count is
toffoli.- presY (k : ℕ) (s : State m) (Yval : ℕ) : k < n → self.clean k s → regValRange self.Y s n = Yval → regValRange self.Y (denote (self.step k) s) n = Yval
Multiplicand persistence. Bank
kleavesYat its value. - cleanStable (j k : ℕ) (s : State m) : j < n → k < n → j ≠ k → self.clean k s → self.clean k (denote (self.step j) s)
Inter-bank clean frame. Bank
jleaves bankk's clean precondition intact (j ≠ k). - digitStable (j k : ℕ) (s : State m) : j < n → k < n → j ≠ k → self.digit k (denote (self.step j) s) = self.digit k s
Inter-bank digit frame. Bank
jleaves bankk's digit intact (j ≠ k).
Instances For
The generic multiplier #
The adder-parametric modular multiplier. Fold the verified step over the n banks.
Equations
- Reversible.genMul A = List.flatMap id (List.map (fun (j : ℕ) => A.step j) (List.range n))
Instances For
The first k banks of the loop (the induction handle).
Equations
- Reversible.genMulUpto A k = List.flatMap id (List.map (fun (j : ℕ) => A.step j) (List.range k))
Instances For
Bank k's clean precondition survives the prefix of banks [0, p) (p ≤ k): each earlier bank
j < p ≤ k has j ≠ k, so cleanStable applies.
Bank k's digit survives the prefix of banks [0, p) (p ≤ k), by digitStable.
The generic multiply-loop invariant. After the first k banks (k ≤ n) from B = 0, the
accumulator holds (accVal digit k · y) mod N and Y still holds y. The SAME induction as
mulLoop_invariant, now citing the abstract VerifiedAdder fields: the prefix preserves bank k's
clean precondition (genMul_clean_pres) and its digit (genMul_digit_pres); correct advances the
residue (accVal_mod_step); presY keeps y.
The adder-parametric modular multiply (the keystone). For any conforming step, with B = 0,
Y = y < N, and every bank clean, genMul A leaves the accumulator holding (accVal digit n · y) mod N.
Specialised from genMul_invariant at k = n. Proved ONCE, parametric over A.
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 corpus mulLoop (the non-lossy guard) #
corpusAdder instantiates VerifiedAdder with the existing S6.3d-2b multiplier's per-bit Horner step,
reading the proven hornerStep_* lemmas into the abstract fields. The generic theorems then recover the
corpus's (X · Y) mod N correctness and 30 · n² cost exactly.
Bank k's clean / preset precondition for the corpus Horner step (the 14 facts hornerStep_correct
consumes about bank k's private wires: 10 clean carries / ancilla false, 4 reduce presets).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Corpus presY field: hornerStep_preserves_Y.
Corpus cleanStable field: each clean wire / preset survives bank j via corpus_step_pres.
Corpus digitStable field: the control bit X (n-1-k) survives bank j (hCtrlTouch).
The faithfulness instance: the corpus S6.3d-2b multiplier as a VerifiedAdder.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Definitional faithfulness: the generic multiplier on the corpus step IS the corpus mulLoop.
Correctness faithfulness (the non-lossy guard): genMul_correct on the corpus instance recovers
mulLoop_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 corpus instance recovers
mulLoop_toffoli's figure 30 · n².