What makes an ancilla dangerous
The danger is not that ancillae are hard to use — it is that the failure mode is invisible. Consider a function that uses an ancilla to compute a temporary flag:- You copy some information from your system into the ancilla using a CX gate.
- You apply a phase based on the ancilla.
- You “undo” the copy.
The ancilla block
Inside a@coherent function, you open an ancilla block with the ancilla context manager:
compute, then either phase or apply, then uncompute. Each section is a lambda that contains gate calls. You cannot skip a section or reorder them — b01t raises DSLValidationError if the structure is violated.
The CPU pattern: compute / phase / uncompute
The CPU pattern (Compute–Phase–Uncompute) is the standard way to use a phase oracle via an ancilla:1
compute
Copy information from your system register into the ancilla using only permutation gates — gates that map computational basis states to computational basis states without creating superposition. This is the reversibility contract: permutation gates can always be exactly undone.Allowed gates:
x, cx, ccx, swap, mcx2
phase
Apply a diagonal gate to the ancilla. Diagonal gates act on the phase of each basis state without mixing them, which is why they compose cleanly with the permutation-based compute step.Allowed gates:
z, cz, ccz, s, sdg, t, tdg, mcz3
uncompute
Call
uncompute() with no arguments. b01t automatically generates the gate-by-gate inverse of your compute block and appends it. You do not write the uncomputation by hand.The CMA pattern: compute / apply / uncompute
The CMA pattern (Compute–Apply–Uncompute) is a second structural form for ancilla blocks. Instead of aphase section, you use an apply section, which may contain any exact gate — but those gates must operate on wires that are disjoint from the wires used in the compute block. This is the PreservesFirst condition: the apply section must not touch the wires that were modified during compute.
The disjointness requirement for
apply blocks is checked at build time. If any gate in the apply section touches a wire that was also touched in the compute section, b01t raises DSLValidationError.Gate restrictions
The gate restrictions are what give the discipline its mathematical guarantee. They are not style rules — they are the boundary conditions of the Lean proof.
Gates that are neither permutations nor diagonals — most importantly,
h — are not allowed in compute or phase blocks. They are available at the top level of a @coherent function, but not inside an ancilla block.
Automatic uncomputation
Theuncompute() function takes no arguments. When you call it, b01t looks at the compute block you already wrote and generates its exact inverse:
- For self-inverse gates (
x,cx,ccx,cz,ccz,swap,mcx,mcz), the inverse is the same gate. - For
s, the inverse issdg; fort, the inverse istdg; and vice versa. - The sequence is reversed — the last gate in
computebecomes the first gate inuncompute.
What happens when you break the rules
Every violation raisesDSLValidationError at build time with a specific message:
Multiple cycles in one ancilla block
You can reuse the same ancilla register for multiple compute/phase/uncompute cycles within a singlewith ancilla(...) block: