Magic states and protocols

Non-Clifford computation is where fault-tolerant resource estimation stops being a memory benchmark: Pauli-product rotations and measurements need magic states, and magic states come from distillation protocols. CUDA-Q Logical treats each protocol as a verified, inspectable definition — never a string-named leaf the compiler trusts.

In the programming model, rotations (cudaq.logical.rz and its siblings, and cudaq.logical.resource_rotate where a magic state is consumed) and Pauli-product measurement (cudaq.logical.mpp) are logical primitives:

import cudaq.logical as cql


@cql.program
def parity_check() -> bool:
    q = cql.allocate(3, state=cql.types.zero)
    q[0], q[1], q[2], parity = cql.mpp(
        cql.types.X(q[0]) @ cql.types.Y(q[1]) @ cql.types.Z(q[2]))
    cql.discard(q)
    return parity

The program states the observable; which code-specific gadgets and protocols realize it is a P2 selection decision, recorded as evidence — not a fact the application author supplies.

Phase convention

CUDA-Q Logical fixes one rotation convention everywhere — the frontend and the synthesis pass both assume it:

\[R_P(\theta) = \exp\!\left(-\tfrac{i}{2}\,\theta P\right), \qquad P \in \{X, Y, Z, \dots\}.\]

This is the standard (Nielsen–Chuang) convention. Two consequences are worth stating outright because they trip up cross-checks against other tools:

  • Litinski’s angle is off by a factor of two. Litinski writes rotations as \(P_\phi = \exp(i\phi P)\), so his \(\phi\) and our \(\theta\) relate by \(\theta = -2\phi\). A Litinski “\(P_{\pi/8}\)” is our \(R_P(\pi/4)\) — the magic (T-class) rotation. Always convert before comparing angles.

  • The Clifford hierarchy lands on quarter-turns of \(\theta\), not eighths: \(R_P(\pi)\) is a Pauli, \(R_P(\pi/2)\) is a Clifford, and \(R_P(\pi/4)\) is the non-Clifford magic rotation that consumes one T state.

Concretely, for \(P = Z\) the diagonal rotation \(R_Z(\theta) = \operatorname{diag}(e^{-i\theta/2},\, e^{+i\theta/2})\) equals the named gates up to an unobservable global phase:

angle \(\theta\)

\(R_Z(\theta)\)

equals (up to global phase)

class

\(\pi/4\)

\(\operatorname{diag}(e^{-i\pi/8}, e^{+i\pi/8})\)

\(e^{-i\pi/8}\,T\), with \(T=\operatorname{diag}(1, e^{i\pi/4})\)

magic (T)

\(\pi/2\)

\(\operatorname{diag}(e^{-i\pi/4}, e^{+i\pi/4})\)

\(e^{-i\pi/4}\,S\), with \(S=\operatorname{diag}(1, i)=T^2\)

Clifford

\(\pi\)

\(\operatorname{diag}(e^{-i\pi/2}, e^{+i\pi/2})\)

\(e^{-i\pi/2}\,Z\), with \(Z=\operatorname{diag}(1, -1)=S^2=T^4\)

Pauli

So \(R_Z(k\,\pi/4) = T^k \pmod 8\) up to global phase — the exact-word fast path the native qlx-synthesize-rotations pass takes before ever calling gridsynth: \(k=1\to T\), \(k=2\to S\), \(k=4\to Z\), and \(T^8 = I\). We drop global phase throughout: it is unobservable and carries no logical content.

Authoring exact angles with cudaq.logical.algebra.pi

Writing angle=0.7853981633974483 leaves the compiler to infer that you meant \(\pi/4\) from a float, within a tolerance. To state the intent exactly, author with the symbol cudaq.logical.algebra.pi — an exact rational multiple of \(\pi\) that arithmetic keeps exact:

@cql.program
def exact_angles() -> bool:
    q = cql.allocate(1, state=cql.types.zero)
    q[0] = cql.rz(q[0], cql.algebra.pi / 4)
    q[0] = cql.rz(q[0], 3 * cql.algebra.pi / 4)
    q[0] = cql.rz(q[0], cql.algebra.pi / 8)
    return cql.measure_z(q[0])

cql.compile(exact_angles)

An Angle authored this way stamps its reduced coefficient onto the rotation op, so the synthesis dispatch classifies it authoritativelycudaq.logical.algebra.pi / 2 is the Clifford point and cudaq.logical.algebra.pi / 4 the magic point by construction, never a near-lattice float that a tolerance might snap or miss. float(cudaq.logical.algebra.pi / 4) still yields the ordinary radian value, so a raw float angle (e.g. 0.3) keeps the numeric path unchanged — both surfaces coexist.

Synthesis legalizes off-lattice rotations to Clifford+T; see Logical Clifford+T synthesis.

Typed resource kinds

Magic states are typed resources, not ad-hoc qubits. The standard library namespace cudaq.logical.logical declares the kinds — T_STATE, RAW_T_STATE, Y_STATE, CCZ_STATE, CS_STATE, ENCODED_BELL_PAIR — each with a typed consume action, and its produce(...) builds the objective a production protocol claims to implement. A protocol body sees resources through cudaq.logical.types.resource[...] handles: cudaq.logical.request_many draws raw inputs, cudaq.logical.unpack_resource opens a resource into a patch, and cudaq.logical.pack_resource certifies the output kind.

15-to-1: a concrete factory

examples/standalone/03_magic_state_distillation.py authors the real five-row triorthogonal circuit with the root authoring facade — not an analytical placeholder:

The 15-to-1 T-state protocol (examples/standalone/03_magic_state_distillation.py).
import cudaq.logical as cql


The protocol unpacks fifteen linear raw-state inputs onto bare patches; eleven resource-assisted product rotations from cudaq.logical.protocols.FIFTEEN_TO_ONE_ROTATION_STEPS apply the triorthogonal circuit; cudaq.logical.protocols.bare_s converts the resulting T† on the odd row to the canonical T|+⟩; and exactly the four even rows must measure \(+X\) — recorded with cudaq.logical.postselect, so the acceptance condition is part of the definition rather than a comment about it.

Because the protocol is an ordinary compiled definition, the static estimation tier counts it directly:

# Estimate the protocol directly at its encoded static-resource layer.
resources = cql.estimate(distill_15to1, tier=cql.estimate.Tier.STATIC)

assert resources.operation_counts["resource_request"] == 15
assert resources.operation_counts["resource_rotate_product"] == 11
assert resources.operation_counts["selection"] == 4
assert resources.operation_counts["pack_resource"] == 1

print("15-to-1 static resource estimate:")
print(f"  raw T-state requests: "
      f"{resources.operation_counts['resource_request']}")
print(f"  peak live patches: {resources.patches_peak}")
15-to-1 static resource estimate:
  raw T-state requests: 15
  peak live patches: 5

The selection operation count records the four postselection checks, so a downstream study does not have to rediscover that this factory rejects.

The leading-order analytical curves attach to the same protocol library entry for supply/demand studies — output error \(35p^3\) and acceptance \(1 - 15p\):

model = cql.protocols.DISTILL_15TO1_T
assert model.output_error(1e-3) == 3.510537795740123e-08
assert model.acceptance_probability(1e-3) == 0.9851045810483217

The cudaq.logical.protocols library also exposes the reusable pieces — FIFTEEN_TO_ONE_ROTATION_STEPS and their supports, bare_s, bare_measure_x, and the ready-made distill_15to1 definition — so you can compose your own production protocol from verified parts.

Evidence boundary :class: note

A static estimate counts what the declared protocol costs; it does not sample the factory, decode its checks, or model the noise that makes distillation necessary. The estimate reports postselection as counts and success rows, not as simulated accept/reject statistics. :::

Compact factory models at P3

A protocol says what a factory does; at P3 a factory binding can also say how fast it does it. cudaq.logical.devices.FactoryModel carries a startup latency, a steady output interval, and the evidence behind both:

A declared factory model with explicit provenance (examples/gidney_ekera_factory.py).
builder.physical.bind(factory_qec, to=factory_layout)
builder.physical.bind(
    factory_qec.auxiliary_regions[0],
    to=factory_layout,
)
builder.physical.bind(
    raw_qec,
    to=raw_injection_qubits,
    factory_model=cql.devices.FactoryModel(
        startup_cycles=1,
        output_interval_cycles=1,
        evidence=cql.analysis.user_assertion(
            "one physical injection qubit and one surface cycle per raw "
            "T state"),
    ),
)

Declaring the numbers is one option, and evidence= is where you say they are an assumption rather than a measurement. The other option is to derive them: cudaq.logical.compiler.factory_model reads a verified P3 schedule and characterizes the factory it implements.

factory_model = cql.compiler.factory_model(
    detailed_schedule,
    produces=cql.logical.AUTO_CCZ_STATE,
)

The derived model reports the same two figures the declared one asserts — startup_cycles and output_interval_cycles — plus a characterization recording the physical units it took. A supply/demand study can then use a compact factory model in place of the full protocol schedule, without losing track of where its timing came from.

Continue from here