> ## Documentation Index
> Fetch the complete documentation index at: https://b01t.com/llms.txt
> Use this file to discover all available pages before exploring further.

# b01t's gate set: exact gates and parametric rotations

> b01t's gate set: exact gates for @coherent, parametric rotations for @parametric and @adaptive, and which gates are allowed in compute vs phase blocks.

b01t's gates are ordinary Python functions. You call them inside a decorated function body, and the build system intercepts the calls to construct a typed AST rather than executing them directly. The gate set is split in two: the **exact gate set** for `@coherent` and `@primitive` programs, which contains only gates synthesisable from {H, S, T, CNOT}, and **parametric rotations** (`rx`, `ry`, `rz`, `cry`, `crz`), which are only available in `@parametric` and `@adaptive` programs.

## The exact gate set

The exact gate set is the universe of gates available in a `@coherent` or `@primitive` program. It is a closed, finite set: no free-angle parameters, no symbolic expressions. Every gate in this set can be synthesised exactly from the universal basis {H, S, T, CNOT}, which is the basis of the Hero Theorem proof.

<Note>
  Attempting to use `rx`, `ry`, `rz`, `cry`, or `crz` inside a `@coherent` or `@primitive` function raises `DSLValidationError` immediately — parameterized rotations are not in the exact gate set.
</Note>

## Single-qubit gates

All single-qubit gates take a single `Wire` argument. You get a `Wire` by indexing a `QReg`: `sys[0]`, `anc[1]`, etc.

```python theme={null}
from b01t import coherent, QReg, x, h, z, s, sdg, t, tdg

@coherent
def single_qubit_demo(q: QReg):
    x(q[0])    # Pauli X (NOT gate)
    h(q[0])    # Hadamard
    z(q[0])    # Pauli Z (phase flip)
    s(q[0])    # S gate (√Z)
    sdg(q[0])  # S-dagger (S†, inverse of S)
    t(q[0])    # T gate (⁴√Z)
    tdg(q[0])  # T-dagger (T†, inverse of T)
```

## Two-qubit gates

Two-qubit gates take two `Wire` arguments. All wires must be distinct — passing the same wire twice raises `DSLValidationError`.

```python theme={null}
from b01t import coherent, QReg, cx, cz, swap

@coherent
def two_qubit_demo(a: QReg, b: QReg):
    cx(a[0], b[0])   # CNOT: control=a[0], target=b[0]
    cz(a[0], b[0])   # Controlled-Z
    swap(a[0], b[0]) # SWAP
```

## Three-qubit gates

The Toffoli gate (`ccx`) and doubly-controlled-Z (`ccz`) take three wire arguments — two controls and one target.

```python theme={null}
from b01t import coherent, QReg, ccx, ccz

@coherent
def three_qubit_demo(a: QReg, b: QReg, c: QReg):
    ccx(a[0], b[0], c[0])   # Toffoli: controls=a[0],b[0], target=c[0]
    ccz(a[0], b[0], c[0])   # CCZ: controls=a[0],b[0], target=c[0]
```

## Multi-controlled gates

`mcx` and `mcz` accept an arbitrary number of controls as a list or sequence of wires, plus a single target wire. They require at least one control.

```python theme={null}
from b01t import coherent, QReg, mcx, mcz

@coherent
def multi_controlled_demo(ctrl: QReg, target: QReg):
    mcx([ctrl[0], ctrl[1], ctrl[2]], target[0])  # 3-controlled X
    mcz([ctrl[0], ctrl[1]], target[0])           # 2-controlled Z
```

## Parametric rotation gates

Parametric gates take an angle (in radians) as their first argument, followed by the wire. They are only available inside `@parametric` and `@adaptive` functions.

```python theme={null}
from b01t import parametric, QReg, rx, ry, rz, cry, crz
import math

@parametric
def rotation_demo(q: QReg, r: QReg):
    rx(math.pi / 4, q[0])          # X-axis rotation by π/4
    ry(math.pi / 3, q[0])          # Y-axis rotation by π/3
    rz(math.pi / 2, q[0])          # Z-axis rotation by π/2
    cry(math.pi / 4, r[0], q[0])   # Controlled-RY
    crz(math.pi / 2, r[0], q[0])   # Controlled-RZ
```

## Gate classification for ancilla blocks

Inside `@coherent` ancilla blocks, not every gate is available everywhere. The `compute` section allows only **permutation gates** and the `phase` section allows only **diagonal gates**. See [Ancilla Discipline](/concepts/ancilla-discipline) for the full explanation of why these restrictions exist.

| Gate   | Exact set | Compute | Phase | Parametric |
| ------ | --------- | ------- | ----- | ---------- |
| `x`    | Yes       | Yes     | No    | No         |
| `h`    | Yes       | No      | No    | No         |
| `z`    | Yes       | No      | Yes   | No         |
| `s`    | Yes       | No      | Yes   | No         |
| `sdg`  | Yes       | No      | Yes   | No         |
| `t`    | Yes       | No      | Yes   | No         |
| `tdg`  | Yes       | No      | Yes   | No         |
| `cx`   | Yes       | Yes     | No    | No         |
| `cz`   | Yes       | No      | Yes   | No         |
| `swap` | Yes       | Yes     | No    | No         |
| `ccx`  | Yes       | Yes     | No    | No         |
| `ccz`  | Yes       | No      | Yes   | No         |
| `mcx`  | Yes       | Yes     | No    | No         |
| `mcz`  | Yes       | No      | Yes   | No         |
| `rx`   | No        | No      | No    | Yes        |
| `ry`   | No        | No      | No    | Yes        |
| `rz`   | No        | No      | No    | Yes        |
| `cry`  | No        | No      | No    | Yes        |
| `crz`  | No        | No      | No    | Yes        |

"Parametric" here means the gate is available at the top level of `@parametric` and `@adaptive` functions.

<Note>
  `h` is in the exact gate set and is available at the top level of `@coherent` functions, but it is not a permutation gate and not a diagonal gate — so it is not allowed inside `compute` or `phase` blocks.
</Note>

## Measurement

`measure` and `measure_all` are only available in `@adaptive` functions. Attempting to use them in `@coherent`, `@primitive`, or `@parametric` raises `DSLValidationError`.

```python theme={null}
from b01t import adaptive, QReg, h, cx, measure

@adaptive
def measure_demo(q: QReg):
    h(q[0])
    result = measure(q[0])  # returns a classical bit name as a string
```

```python theme={null}
from b01t import adaptive, QReg, h, measure_all

@adaptive
def measure_all_demo(q: QReg):
    h(q[0])
    h(q[1])
    bits = measure_all(q)   # returns a list of classical bit names
```

## Importing gates

All gates are available from the top-level `b01t` package:

```python theme={null}
from b01t import (
    # Single-qubit
    x, h, z, s, sdg, t, tdg,
    # Parametric single-qubit
    rx, ry, rz,
    # Two-qubit
    cx, cz, swap, cry, crz,
    # Three-qubit
    ccx, ccz,
    # Multi-controlled
    mcx, mcz,
    # Measurement
    measure, measure_all,
)
```
