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

# Decorators API: coherent, primitive, parametric, adaptive

> Reference for b01t's four program decorators: choose the right one to control safety certification, gate set, and whether your program can measure.

Decorators are the entry point for every b01t program. You apply a decorator to a Python function that accepts `QReg` arguments, and b01t wraps it in a typed object — either an `ExactDSLFunction` or a `DSLFunction` — that you call `.build_exact()` or `.build()` on to produce a compiled program. Your choice of decorator determines which gates are allowed, what certification the program receives, and whether classical branching is permitted.

## @coherent

```python theme={null}
from b01t import coherent

@coherent
def my_program(sys: QReg) -> None:
    ...
```

`@coherent` wraps the decorated function in an `ExactDSLFunction` with `Certification.SAFE`. Use it for programs that must be fully ancilla-certified — every ancilla qubit is guaranteed to return to `|0⟩` by construction. The compiler enforces the complete ancilla discipline (`compute` / `phase` / `uncompute`) and rejects any gate not in the exact gate set (`{X, H, Z, S, S†, T, T†, CX, CZ, SWAP, CCX, CCZ, MCX, MCZ}`).

**When to use:** whenever you need a structurally certified unitary channel. The hero theorem applies: if `.build_exact()` succeeds, the program is provably safe.

**Key rules:**

* Only exact gates are allowed. `rx`, `ry`, `rz`, `cry`, `crz` raise `DSLValidationError`.
* `measure` is forbidden.
* `if_then` is forbidden.
* Ancilla blocks must complete at least one full `compute` / `phase` (or `apply`) / `uncompute` cycle.
* A `@coherent` function may not call a `@primitive` function outside of an ancilla `compute` or `phase` block.

```python theme={null}
from b01t import coherent, QReg, h, cx, z
from b01t import ancilla, compute, phase, uncompute

@coherent
def phase_kickback(ctrl: QReg, tgt: QReg) -> None:
    h(ctrl[0])
    cx(ctrl[0], tgt[0])
    z(tgt[0])
    cx(ctrl[0], tgt[0])
    h(ctrl[0])

prog = phase_kickback.build_exact(("ctrl", 1), ("tgt", 1))
print(prog.certification)  # Certification.SAFE
```

***

## @primitive

```python theme={null}
from b01t import primitive

@primitive
def my_prim(anc: QReg, sys: QReg) -> None:
    ...
```

`@primitive` wraps the decorated function in an `ExactDSLFunction` with `Certification.PRIMITIVE`. The gate set and syntax rules are identical to `@coherent`, but scratch registers are caller-managed rather than certified by the compiler. Use `@primitive` for low-level building blocks where you manage ancilla cleanliness yourself, or where the ancilla is shared with the calling function.

**When to use:** for subroutines that intentionally leave ancilla in a non-zero state for a caller to clean up, or for inner routines that are called from within a `compute` or `phase` block of a `@coherent` function.

**Key rules:**

* Same exact gate set as `@coherent`.
* A `@coherent` function can call a `@primitive` function only from inside a `compute(...)` or `phase(...)` block.
* Calling a `@primitive` function at the top level of a `@coherent` body raises `DSLValidationError`.

```python theme={null}
from b01t import primitive, coherent, QReg, cx
from b01t import ancilla, compute, phase, uncompute, z

@primitive
def carry_bit(a: QReg, b: QReg, anc: QReg) -> None:
    cx(a[0], anc[0])
    cx(b[0], anc[0])

@coherent
def adder(a: QReg, b: QReg) -> None:
    with ancilla(1) as anc:
        compute(lambda: carry_bit(a, b, anc))
        phase(lambda: z(anc[0]))
        uncompute()

prog = adder.build_exact(("a", 1), ("b", 1))
print(prog.certification)  # Certification.PRIMITIVE... wait, caller is @coherent
# => Certification.SAFE
```

***

## @parametric

```python theme={null}
from b01t import parametric

@parametric
def my_program(sys: QReg) -> None:
    ...
```

`@parametric` wraps the decorated function in a `DSLFunction` with `Effect.COHERENT`. It unlocks the parametric rotation gates (`rx`, `ry`, `rz`, `cry`, `crz`) while keeping unitary semantics — no measurement, no classical branching. The resulting `IRProgram` has `effect = Effect.COHERENT`.

**When to use:** variational algorithms (VQE, QAOA) and any circuit that needs continuous-angle rotations. Programs that pass the safety checker (`is_safe_program`) are marked `is_safe = True` in the returned `IRProgram`.

**Key rules:**

* All exact gates plus `rx`, `ry`, `rz`, `cry`, `crz` are allowed.
* `measure` and `if_then` are forbidden.
* A `@parametric` function cannot call an `@adaptive` function.

```python theme={null}
from b01t import parametric, QReg, h, rz, cx
import math

@parametric
def ansatz(q: QReg) -> None:
    h(q[0])
    rz(math.pi / 4, q[0])
    cx(q[0], q[1])

prog = ansatz.build(("q", 2))
print(prog.effect)    # Effect.COHERENT
print(prog.is_safe)   # False (rz is parametric, not exact-safe)
```

***

## @adaptive

```python theme={null}
from b01t import adaptive

@adaptive
def my_program(sys: QReg) -> None:
    ...
```

`@adaptive` wraps the decorated function in a `DSLFunction` with `Effect.ADAPTIVE`. Adaptive programs can measure qubits and branch on classical results via `if_then`. The returned `IRProgram` has `effect = Effect.ADAPTIVE` and `is_safe = False` (adaptive programs are never safe by the safety model).

**When to use:** measurement-based protocols, quantum error correction, and teleportation circuits that require mid-circuit measurement and feed-forward.

**Key rules:**

* All gates from both the exact and parametric sets are allowed.
* `measure`, `measure_all`, and `if_then` are allowed.
* An `@adaptive` function can call `@parametric` functions, but not vice versa.

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

@adaptive
def teleport_prep(q: QReg, out: QReg) -> None:
    h(q[0])
    m = measure(q[0])
    if_then(m, lambda: x(out[0]))

prog = teleport_prep.build(("q", 1), ("out", 1))
print(prog.effect)  # Effect.ADAPTIVE
```

***

## ExactDSLFunction.build\_exact()

Compiles a `@coherent` or `@primitive` function into an `ExactProgram`.

```python theme={null}
prog = my_fn.build_exact(*arg_specs)
```

<ParamField path="arg_specs" type="tuple[str, int] | QReg" required>
  One argument per register in the decorated function. Each argument is either a `("name", size)` tuple or an existing `QReg` object. Register names must be unique.
</ParamField>

**Returns:** `ExactProgram` with fields `name`, `regs`, `ops`, and `certification`.

**Raises:** `DSLValidationError` if any gate, ancilla, or structural rule is violated during the build.

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

@coherent
def bell(a: QReg, b: QReg) -> None:
    h(a[0])
    cx(a[0], b[0])

# Using tuples
prog = bell.build_exact(("a", 1), ("b", 1))

# Using QReg objects
a = QReg("a", 1)
b = QReg("b", 1)
prog = bell.build_exact(a, b)
```

***

## DSLFunction.build()

Compiles a `@parametric` or `@adaptive` function into an `IRProgram`.

```python theme={null}
prog = my_fn.build(*arg_specs)
```

<ParamField path="arg_specs" type="tuple[str, int] | QReg" required>
  One argument per register in the decorated function. Each argument is either a `("name", size)` tuple or an existing `QReg` object. Register names must be unique.
</ParamField>

**Returns:** `IRProgram` with fields `name`, `effect`, `regs`, `ops`, and `is_safe`.

**Raises:** `DSLValidationError` if any rule is violated during the build.

```python theme={null}
from b01t import parametric, QReg, h, rz, cx
import math

@parametric
def rotation_layer(q: QReg) -> None:
    for i in range(len(q)):
        rz(math.pi / 8, q[i])

prog = rotation_layer.build(("q", 3))
print(prog.name)    # "rotation_layer"
print(prog.effect)  # Effect.COHERENT
```
