@coherent decorator is b01t’s primary tool for writing quantum circuits that are certified safe by construction. When you call .build_exact() on a coherent function, b01t validates the entire program structure — gate types, ancilla discipline, and uncomputation — before producing an ExactProgram with certification == Certification.SAFE. No simulation required.
The full workflow
Import and define a @coherent function
Decorate a Python function with You cannot call a
@coherent. Each parameter must be annotated as QReg — these become the quantum registers your function operates on.@coherent function directly — it must be built via .build_exact().Use ancilla for scratch qubits
Allocate scratch qubits with the
ancilla(n) context manager. All scratch qubits allocated this way are automatically verified to return to |0⟩ at the end of the block.Inside the with ancilla(...) as anc: block, you must structure your operations as one or more complete compute / phase / uncompute cycles:compute(fn)— apply permutation gates (X, CX, CCX, SWAP, MCX) to copy system information into the ancilla. Superposition gates like H are not allowed here.phase(fn)— apply diagonal gates (Z, S, T, CZ, CCZ, MCZ) to accumulate phase. Only gates that are diagonal in the computational basis are allowed.uncompute()— automatically generates and appends the inverse of the compute block.
apply(fn) instead of phase(fn) for the “CMA pattern”: any exact gates are allowed, but they must operate on wires disjoint from the compute block’s wires.Build with build_exact()
Call For functions with multiple registers, pass multiple specs:
.build_exact() with register specifications — tuples of (name, size) matching the function’s parameters in order.build_exact() returns an ExactProgram. If any rule is violated, it raises DSLValidationError before returning.Check certification
A program built with Programs built with
@coherent always has certification == Certification.SAFE. You can assert this to make the guarantee explicit in your code:@primitive have certification == Certification.PRIMITIVE — they use the same closed gate set but do not enforce ancilla cleanliness.Complete example: phase oracle
This is the canonical example from the b01t README. It computes the phase oracleU_f|x⟩ = (-1)^{f(x)}|x⟩ for a single-qubit function.
anc0 starts at |0⟩, the CX gate copies sys[0] into it, the Z gate applies a phase, and uncompute() generates the inverse CX to restore anc0 to |0⟩.
Parallel composition with par()
Usepar() to compose two sub-programs that operate on disjoint registers simultaneously. b01t enforces wire-disjointness at build time.
build_exact() raises DSLValidationError: par() requires disjoint wires.
Inverse operations with adjoint()
Useadjoint() to append the inverse of another @coherent or @primitive function into the current program body. This is useful for uncomputing a subroutine at the top level (outside an ancilla block).
adjoint() cannot be used inside an ancilla block’s compute/phase/uncompute cycle. It is only valid at the top level of a @coherent function body.Common errors
Wrong gate in a compute block
compute(): x, cx, ccx, swap, mcx.
Wrong gate in a phase block
phase(): z, s, sdg, t, tdg, cz, ccz, mcz.
Missing uncompute
compute / phase cycle must be closed with uncompute().
Parameterized rotation in @coherent
@parametric instead of @coherent when you need arbitrary rotation angles like rx, ry, rz, cry, or crz.
Calling @coherent outside build context
.build_exact(("name", size), ...) to produce a program.
Exact gate set
The following gates are available in@coherent programs:
| Gate | Single-qubit | Multi-qubit |
|---|---|---|
| Pauli X | x | — |
| Hadamard | h | — |
| Pauli Z | z | — |
| S / S† | s, sdg | — |
| T / T† | t, tdg | — |
| Controlled-X | — | cx |
| Controlled-Z | — | cz |
| SWAP | — | swap |
| Toffoli | — | ccx |
| CCZ | — | ccz |
| Multi-controlled X | — | mcx |
| Multi-controlled Z | — | mcz |
rx, ry, rz, cry, crz) are not part of the exact gate set. Use @parametric for those.