Skip to main content
The @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

1

Import and define a @coherent function

Decorate a Python function with @coherent. Each parameter must be annotated as QReg — these become the quantum registers your function operates on.
You cannot call a @coherent function directly — it must be built via .build_exact().
2

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.
You can also use 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.
3

Build with build_exact()

Call .build_exact() with register specifications — tuples of (name, size) matching the function’s parameters in order.
For functions with multiple registers, pass multiple specs:
build_exact() returns an ExactProgram. If any rule is violated, it raises DSLValidationError before returning.
4

Check certification

A program built with @coherent always has certification == Certification.SAFE. You can assert this to make the guarantee explicit in your code:
Programs built with @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 oracle U_f|x⟩ = (-1)^{f(x)}|x⟩ for a single-qubit function.
The ancilla qubit 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()

Use par() to compose two sub-programs that operate on disjoint registers simultaneously. b01t enforces wire-disjointness at build time.
If the two lambdas touch the same wire, build_exact() raises DSLValidationError: par() requires disjoint wires.

Inverse operations with adjoint()

Use adjoint() 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

Fix: Use only permutation gates in compute(): x, cx, ccx, swap, mcx.

Wrong gate in a phase block

Fix: Use only diagonal gates in phase(): z, s, sdg, t, tdg, cz, ccz, mcz.

Missing uncompute

Fix: Every compute / phase cycle must be closed with uncompute().

Parameterized rotation in @coherent

Fix: Use @parametric instead of @coherent when you need arbitrary rotation angles like rx, ry, rz, cry, or crz.

Calling @coherent outside build context

Fix: Always call .build_exact(("name", size), ...) to produce a program.

Exact gate set

The following gates are available in @coherent programs: Parameterized rotations (rx, ry, rz, cry, crz) are not part of the exact gate set. Use @parametric for those.