The minimal 1-Toffoli-per-carry (Gidney) reversible adder (Tier-X / Build #35) #
Category: 1-Mathlib (CSD-free; staged as a Mathlib-upstream candidate). Pure Boolean-DSL
value-correctness; the amplitude / measurement re-cost is Empirical/QM/MeasurementGidneyAdder.lean.
Why this file exists #
#30 (AndAdd.lean) computes each carry into a fresh ancilla with a 3-Toffoli preserving-majority
cell (andCarryCell), so its adder costs 6n Toffoli. The Cuccaro adder (CuccaroAdd.lean) restores
the carries in place with 2n Toffoli but exposes no fresh-AND attachment point (its uma is a
unitary in-place restore), so the measurement-uncomputation gadget (#31) cannot drop its Toffolis.
This file supplies the competitive adder: the Gidney carry cell folds the carry into a fresh
ancilla with a single Toffoli (the addend bits are XOR-shifted by the carry-in and restored on the
reverse pass), and the reverse pass's per-carry Toffoli is a genuine andUncompute-shaped block — the
attachment point the measurement gadget eliminates. The unitary cost is 2n; the measurement re-cost
(in MeasurementGidneyAdder.lean) drops the reverse pass to 0, giving n.
The carry cell (Gidney, one Toffoli) #
c_{i+1} = MAJ(a_i, b_i, c_i) = c_i ⊕ ((a_i ⊕ c_i) ∧ (b_i ⊕ c_i)). The cell on (a, b, cin, cout)
(cout a fresh runway ancilla, init false) is
majCell a b cin cout := [CX cin a, CX cin b, CCX a b cout, CX cin cout]:
CX cin a/CX cin bshifta ← a ⊕ cin,b ← b ⊕ cin;- the single
CCX a b coutwritescout ← (a ⊕ cin) ∧ (b ⊕ cin)(the fresh-AND temporary); CX cin coutadds thecinterm, givingcout = MAJ(a, b, cin).
majCell_toffoli : … = 1 is the cost win. majCell_correct proves the Boolean semantics.
The adder + correctness (the anti-hollow requirement) #
gidneyAdd L := gidneyForward L ++ andSumPass L.toAnd ++ inverse (gidneyForward L) on a
GidneyLayout (addends A, B, sum register S, fresh carry chain G). The forward pass computes
every carry into G (XOR-shifting A, B); the sum pass writes S i ← A i ⊕ B i ⊕ G i
(= a_i ⊕ b_i ⊕ c_i, since the two c_i shifts cancel one); the reverse pass uncomputes the carries
and restores A, B, G.
gidneyAdd_correct : regValRange L.S (denote (gidneyAdd L) s) n = (A + B) % 2 ^ n— generaln.gidneyAdd_ancilla_clean/gidneyAdd_preserves_A/_preserves_B— the reverse pass restores the carry chain tofalseand the addends to their inputs.gidneyAdd_toffoli : … = 2 * n—nforward +nreverse, sum pass CNOT-only.
The sum-pass machinery, the carry arithmetic (carryOf, adder_sum_identity), and the circuit
locality lemmas (denote_apply_of_forall_not_mem_target, denote_agree_on) are reused from #30
via the projection GidneyLayout.toAnd.
Honest scope #
Value-correct + ancilla-clean over regVal, Boolean DSL only, general n. The Toffoli win over #30
(6n) is at the unitary level; the win over Cuccaro (2n) requires the measurement re-cost
(MeasurementGidneyAdder.lean), which inherits #31's cell-granularity amplitude bridge. No
amplitudes, no measurement, no ECDSA resource claim here.
Space tradeoff (honest). The Toffoli parity (and the measurement-level advantage over Cuccaro) is
bought with O(n) extra space: a separate sum register S (n wires) and an (n+1)-wire fresh carry
runway G, against Cuccaro's single in-place ancilla. The win is on the Toffoli axis only. The count is
also Toffoli-only (the dominant fault-tolerant cost); Cliffords / measurements are not counted.
The Gidney carry cell (one Toffoli) #
The Gidney carry cell on (a, b, cin, cout) with cout a fresh runway ancilla:
[CX cin a, CX cin b, CCX a b cout, CX cin cout]. The single CCX folds the fresh-AND
(a ⊕ cin) ∧ (b ⊕ cin) into cout; the surrounding CNOTs shift a, b by cin and add the cin
term, giving cout = MAJ(a, b, cin). Deviates from Cuccaro's 3-wire in-place maj by using a fresh
cout (the attachment point the measurement gadget needs). One Toffoli per carry.
Equations
- Reversible.majCell a b cin cout = [Reversible.Gate.CX cin a, Reversible.Gate.CX cin b, Reversible.Gate.CCX a b cout, Reversible.Gate.CX cin cout]
Instances For
Cell Toffoli cost = 1 (the single CCX; the other three gates are CNOTs). The key cost win
over #30's 3-Toffoli andCarryCell.
Cell correctness. For pairwise-distinct wires and a fresh cout = false, the cell writes
cout = MAJ(a, b, cin), shifts a ← a ⊕ cin and b ← b ⊕ cin, and preserves cin. The fresh-AND
temporary (a ⊕ cin) ∧ (b ⊕ cin) is computed by the single Toffoli and completed to the majority by
the final CNOT.
The Gidney-adder wire geometry on m wires for n-bit registers: addends A, B (XOR-shifted
during the forward pass, restored by the reverse pass), a separate sum register S, and a fresh
per-carry ancilla chain G (G 0 the input carry, G (i+1) the carry out of bit i, all init
false). Unlike AndAddLayout, A and B carry their own injectivity (hAinj / hBinj) because
the forward pass writes them. The four images are pairwise disjoint.
First addend (shifted then restored).
Second addend (shifted then restored).
Sum output register (
(A + B) mod 2 ^ n).Fresh per-carry ancilla chain (
G i= carry into biti; init/returnedfalse).
Instances For
The #30 AndAddLayout underlying a GidneyLayout (drops the addend injectivity). Lets the
Gidney adder reuse #30's sum-pass machinery and carry arithmetic verbatim.
Equations
Instances For
Forward carry pass #
The forward carry pass over the first k bits.
Equations
Instances For
The full forward carry pass (all n bits).
Equations
Instances For
The Gidney adder. Compute all carries into G (gidneyForward, n Toffoli, XOR-shifting
A, B), write the sums into S (andSumPass, CNOT-only, reused from #30), then uncompute
every carry and restore A, B, G via the reverse pass inverse (gidneyForward L). The reverse
pass's per-carry Toffoli is an andUncompute-shaped block — the measurement-gadget attachment point.
Equations
Instances For
Membership / wire / target characterisation #
Preservation lemmas (target-frame) #
The forward prefix preserves the sum register (S is never a target).
The forward carry invariant #
The forward carry invariant. After the first k forward slices: every computed carry G i
(i ≤ k) holds the true carry carryOf i; the not-yet-computed ancillas (k < i ≤ n) are still
false; the processed addend bits (i < k) are XOR-shifted A i = a_i ⊕ carryOf i,
B i = b_i ⊕ carryOf i; and the unprocessed addend bits (k ≤ i < n) are still their inputs. By
induction on k, each step one Gidney cell on the fresh G (k+1).
Per-bit value of the full adder #
Per-bit sum value. Bit k of the sum register holds A k ⊕ B k ⊕ carry k. The forward pass
XOR-shifts the addends (A k ⊕ c, B k ⊕ c), the sum pass XORs them with G k = c (the two c
shifts cancel one), and the reverse pass only touches A, B, G, so S k survives.
Headline theorems #
Gidney adder correctness. For a disjoint-wire layout with the carry chain and the sum register
initialised false, the sum register ends holding (A + B) mod 2 ^ n. General n. The proof is
the #30 arithmetic (adder_sum_identity) over the Gidney per-bit value gidneyAdd_sum_bit.
Non-S wires are restored. Any wire that is not a (used) sum-register wire is returned to its
input: the sum pass leaves it untouched (andSumPrefix_preserves_of_ne_S), so the reverse pass
inverse (gidneyForward L) undoes the forward pass on it (reversible_inverse_correct). The frame
behind addend / carry restoration.
Derived cost: 2n Toffolis (n forward + n reverse) #
The reverse pass is n Toffolis — the andUncompute-shaped blocks a measurement-based gadget
eliminates (the win over Cuccaro). It equals the forward pass (gidneyForward, n) by
cost_inverse_toffoli.
Derived Toffoli cost: 2 * n. n forward Gidney cells (one Toffoli each) + n reverse
uncompute Toffolis; the sum pass is CNOT-only. One third of #30's 6n; equal to Cuccaro's 2n at
the unitary level (the win over Cuccaro is the measurement re-cost of the reverse n).
Non-vacuity witness + #eval cross-check #
A concrete 2-bit Gidney layout on Fin 9: A → {0,1}, B → {2,3}, S → {4,5}, carry chain
G → {6,7,8}. The headline applies, and the strict Array evaluator runArr (bridge
regValRangeArr_eq) witnesses 2 + 3 = 5 (= 1 mod 4), every carry ancilla / addend restored.
A concrete 2-bit Gidney layout on Fin 9.
Equations
- One or more equations did not get rendered due to their size.