AND-based reversible adder with explicit fresh per-carry AND temporaries (Tier-X / L5-c prerequisite) #
Category: 1-Mathlib (CSD-free; staged as a Mathlib-upstream candidate). Pure Boolean-DSL
value-correctness; no amplitude bridge and no measurement (those are #31 / L5-d).
Why this file exists (the L5-c wall verdict) #
The measurement-based AND-uncomputation gadget (Empirical/QM/MeasurementUncompute.lean, Gidney's
measure-and-correct, the ~2× Toffoli saving) needs an attachment point: a fresh ancilla holding a
logical AND of two data wires (andInput-shaped), uncomputed at an explicit sub-circuit. The
corpus's two ripple adders do not provide one:
ModAdd.rippleCirccomputes carries into a carry chainCand leaves them dirty (never uncomputed) — no uncompute sub-block to replace.CuccaroAdd.cuccaroAddrestores the carries unitarily insideuma(in-place, carry-restoring), with no fresh AND temporary — again nothing for a measurement gadget to attach to.
This file supplies the missing primitive: an adder whose carries live in fresh |0⟩ ancillas,
computed by an explicit AND (Toffoli) and uncomputed by an explicit reverse Toffoli. The
uncompute-AND Toffolis are exactly the ones the measurement route would later eliminate.
What is built #
Reusable circuit infrastructure (target-frame + agreement propagation):
gateTarget/denote_apply_of_forall_not_mem_target— a wire that is never a target (it may be a control) is preserved. The control-permitting refinement ofgateWires/denote_apply_of_forall_not_mem.denote_agree_on— two states agreeing on every wire a circuit's gates touch produce equal outputs on those wires. The locality lemma that makes the uncompute-pass cleanup provable.
The AND-uncompute cell (item 1, the must-have attachment point):
andCarry a b g := [CCX a b g]— the fresh-AND compute:g ← g ⊕ (a ∧ b)(= a ∧ bforg = 0),a/bpreserved (andCarry_correct). TheandInput-shaped stateg = a ∧ b.andUncompute a b g := [CCX a b g]— the explicit AND-uncompute (the Toffoli a measurement gadget replaces).andUncompute_restores: it undoesandCarry, returninggto its input value.andCell a b g out := andCarry ++ [CX g out] ++ andUncompute— compute-use-uncompute:out ← out ⊕ (a ∧ b), ancillagrestored (andCell_correct/andCell_ancilla_clean).
The full AND-based ripple adder (items 2/3): andAdd, on an AndAddLayout with addends A, B
(both preserved), a separate sum register S, and a fresh per-carry ancilla chain G. Structure:
andForward ++ andSumPass ++ inverse andForward — compute all carries into G (preserving A, B),
write the sums into S, then uncompute every carry via the reverse pass.
andAdd_correct:regValRange S (denote (andAdd L) s) n = (A + B) % 2 ^ n.andAdd_ancilla_clean: every fresh carry-AND ancillaG ireturns tofalse.andAdd_toffoli:6 * nToffolis, of which the3 * nuncompute half is the measurement-route saving target (andAdd_uncompute_toffoli).
Honest scope #
Value-correct + ancilla-clean over regVal, Boolean DSL only. The compute / uncompute Toffolis here
are unitary; replacing the uncompute half with the measurement gadget is L5-d, and the
amplitude lift of the AND-uncompute block is #31. No measurement, no amplitudes, no ECDSA resource
claim here. The carry cell uses a 3-Toffoli preserving majority
MAJ(a,b,c) = (a∧b) ⊕ (a∧c) ⊕ (b∧c) (GF(2) identity, majority_eq_xor3) so the addend register B
survives untouched — which is what makes the reverse pass an exact inverse and the cleanup provable.
Target-frame lemma (controls allowed, only the written wire matters) #
The set of wires a gate writes (its target), excluding control wires. A wire outside this set is
preserved even if it is a control. The control-permitting refinement of gateWires.
Equations
- Reversible.gateTarget (Reversible.Gate.X i) = {i}
- Reversible.gateTarget (Reversible.Gate.CX c t) = {t}
- Reversible.gateTarget (Reversible.Gate.CCX c₁ c₂ t) = {t}
- Reversible.gateTarget (Reversible.Gate.swap i j) = {i, j}
Instances For
Target-frame (single gate). A wire that is not the target of g is preserved by denoteGate g
— even if it is a control.
Target-frame (circuit). A wire that is never a target of any gate of c is preserved by
denote c (it may appear as a control).
Agreement propagation (the cleanup locality lemma) #
Agreement (single gate). If s and s' agree on every wire satisfying P, and every wire of
g satisfies P, then denoteGate g s and denoteGate g s' agree on every P-wire.
Agreement propagation (circuit). If every gate of c touches only P-wires, and s, s'
agree on all P-wires, then denote c s and denote c s' agree on all P-wires. The locality lemma
behind the carry-ancilla cleanup: the sum pass leaves the addend/carry wires untouched, so the reverse
pass undoes the forward pass on them exactly.
The fresh-AND carry cell and its explicit uncompute (item 1, the attachment point) #
The fresh-AND compute. A single Toffoli folding the logical AND of data wires a, b into a
fresh ancilla g: g ← g ⊕ (a ∧ b). For a |0⟩-initialised g this writes g = a ∧ b — the
andInput-shaped state (a fresh ancilla holding a ∧ b, correlated with the data).
Equations
- Reversible.andCarry a b g = [Reversible.Gate.CCX a b g]
Instances For
The explicit AND-uncompute (the Toffoli a measurement gadget replaces). Identical gate list to
andCarry (a Toffoli is its own inverse): applied after andCarry it restores the ancilla. Named
separately to mark the attachment point.
Equations
- Reversible.andUncompute a b g = [Reversible.Gate.CCX a b g]
Instances For
The AND-uncompute restores the ancilla. Running andUncompute after andCarry returns the
whole state to its input — in particular the fresh ancilla g is restored. This is the explicit
uncompute point: the second Toffoli (andUncompute) is the one a measurement-based gadget eliminates.
The compute-use-uncompute cell. Fold the AND of a, b into out through a fresh ancilla:
compute g = a ∧ b (andCarry), use it (CX g out, i.e. out ← out ⊕ g), then uncompute g
(andUncompute). The cell is [CCX a b g, CX g out, CCX a b g]: an explicit fresh-AND temporary with
an explicit AND-uncompute sub-block, value-correct and ancilla-clean.
Equations
- Reversible.andCell a b g out = Reversible.andCarry a b g ++ [Reversible.Gate.CX g out] ++ Reversible.andUncompute a b g
Instances For
Cell correctness (general). out ← out ⊕ g ⊕ (a ∧ b) (reading the fresh ancilla's input bit
g), the ancilla g is restored to its input, and a, b are preserved. For a |0⟩ ancilla
(hg0) the g term drops and out ← out ⊕ (a ∧ b) (andCell_correct_clean). The genuine fresh-AND
temporary (g = g ⊕ a ∧ b between the compute and uncompute) is used and then cleaned up.
Cell cost: one compute-AND + one uncompute-AND Toffoli #
Cell Toffoli cost = 2. One compute-AND (andCarry) + one uncompute-AND (andUncompute); the
CX g out is a CNOT.
The uncompute-AND half is one Toffoli — the measurement-route saving target. The cell's two
Toffolis split as one compute + one uncompute; the measurement gadget (#31/L5-d) eliminates this
uncompute Toffoli (replacing it with H + measurement + a conditional Clifford).
The preserving-majority carry cell (3-Toffoli, addend-preserving) #
The ripple's per-carry cell must compute the carry-out MAJ(a, b, c) into a fresh ancilla while
leaving its inputs (in particular the addend register B) untouched — that is what makes the reverse
pass an exact inverse and the ancilla cleanup provable. The GF(2) identity
MAJ(a,b,c) = (a∧b) ⊕ (a∧c) ⊕ (b∧c) lets three Toffolis into a single fresh target do this.
The preserving-majority carry cell. Three Toffolis folding the AND of each input pair into a
fresh target g: [CCX a b g, CCX a c g, CCX b c g]. Each gate writes only g (the inputs are pure
controls), so a, b, c are preserved and g ← g ⊕ MAJ(a,b,c). These are the explicit fresh-AND
computes; the reverse pass (inverse) is the explicit AND-uncompute.
Equations
- Reversible.andCarryCell a b c g = [Reversible.Gate.CCX a b g, Reversible.Gate.CCX a c g, Reversible.Gate.CCX b c g]
Instances For
Preserving-majority correctness. For a fresh ancilla g = false distinct from a, b, c, the
cell writes g = MAJ(a, b, c) and preserves a, b, c.
Cell Toffoli cost = 3 (the three fresh-AND computes).
The AND-based ripple-adder layout #
The AND-based ripple-adder wire geometry for n-bit registers on m wires: addends A, B
(both preserved), a separate sum register S (output), 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). The four images are
pairwise disjoint and each injective on its used index range — the bounded-injectivity pattern of
RippleLayout / CuccaroLayout, refined to a separate sum register so the reverse pass is an exact
inverse.
First addend (preserved).
Second addend (preserved).
Sum output register (
(A + B) mod 2 ^ n).Fresh per-carry ancilla chain (
G i= carry into biti; init/returnedfalse).
Instances For
Carry / sum / forward / uncompute circuits #
The forward carry pass over the first k bits.
Equations
Instances For
The sum pass over the first k bits.
Equations
Instances For
The full sum pass (all n bits).
Equations
Instances For
The sum pass is the full-length sum prefix (interface lemma, §9.1).
The AND-based ripple adder. Compute all carries into the fresh ancilla chain G
(andForward), write the sums into S (andSumPass), then uncompute every carry via the reverse
pass inverse (andForward L). The uncompute pass is the explicit AND-uncompute sub-block — exactly
the Toffolis a measurement-based gadget would eliminate.
Equations
Instances For
Membership / wire / target characterisation of the forward & sum gates #
Preservation lemmas (target-frame) #
The forward slice for bit k preserves any wire other than its single target G (k+1).
Arithmetic: the true carry and the adder sum identity #
The true ripple carry sequence: carryOf 0 = false, carryOf (i+1) = MAJ(aᵢ, bᵢ, carryOf i).
Equations
- Reversible.carryOf a b 0 = false
- Reversible.carryOf a b i.succ = Reversible.majority (a i) (b i) (Reversible.carryOf a b i)
Instances For
The adder sum identity (ℕ). The low-k sum bits plus the carry into bit k, place-weighted,
equal the low-k parts of the two addends. Induction on k via the bitwise fulladder_nat.
Forward carry invariant #
The forward carry invariant. After the first k forward slices, every computed carry ancilla
G i (i ≤ k) holds the true carry carryOf i, and the not-yet-computed ancillas (k < i ≤ n) are
still false. By induction on k, each step a preserving-majority andCarryCell on fresh G (k+1).
Sum-pass value invariant #
The sum slice for bit k preserves any wire other than its single target S k.
Sum-pass value invariant. After the first k sum slices, the processed sum wires (i < k)
carry S i ⊕ A i ⊕ B i ⊕ G i, and the unprocessed ones (k ≤ i < n) are unchanged. By induction on
k, each step a single andSumSlice writing only S k.
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 uncompute pass
(inverse andForward) only touches G wires, so S k is the value written by andSumPass over the
forward-completed carries.
Headline theorems #
AND-based ripple-adder correctness. For a disjoint-wire layout with all carry ancillas and the
sum register initialised false, the sum register ends holding (A + B) mod 2 ^ n. The carries are
computed into fresh ancillas (andForward), the sums written (andSumPass), and the carries
uncomputed (inverse andForward); the value is read off the exhibited circuit, not postulated.
Ancilla-clean: every fresh carry-AND ancilla returns to false. The reverse pass
inverse (andForward L) uncomputes every carry: the sum pass leaves the addend / carry wires
untouched (denote_agree_on), so the reverse pass undoes the forward pass on the G chain exactly
(reversible_inverse_correct). This is the explicit AND-uncompute working — the property a
measurement-based gadget must reproduce.
Derived cost: 6n Toffolis (3n compute + 3n uncompute) #
The uncompute half is 3 * n Toffolis — the AND-uncompute cost a measurement-based gadget
would eliminate (the ~2× saving target for L5-d). It equals the compute half (andForward,
3 * n) by cost_inverse_toffoli.
Derived Toffoli cost: 6 * n. 3 * n compute-AND Toffolis (andForward) + 3 * n
uncompute-AND Toffolis (inverse andForward); the sum pass is CNOT-only. The uncompute 3 * n
(andAdd_uncompute_toffoli) is exactly what the measurement route saves.
Non-vacuity witness + #eval cross-check #
A concrete 2-bit AND-based adder layout on Fin 9: A → {0,1}, B → {2,3}, S → {4,5}, carry chain
G → {6,7,8}. The headlines apply, and the strict Array evaluator runArr (bridge
regValRangeArr_eq) witnesses 2 + 3 = 5, the sum register reading 5 and every carry ancilla
returning false.
A concrete 2-bit AND-based adder layout on Fin 9.
Equations
- One or more equations did not get rendered due to their size.