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

# Choosing the Right b01t Decorator

> Compare b01t's four decorators: which gates each allows, what safety level it provides, and whether to call .build_exact() or .build() to compile.

Every b01t program starts with a decorator. The decorator you choose determines which gates are available, what safety guarantees the resulting program carries, and which build method you call to materialise the circuit. There are four decorators: `@coherent` and `@primitive` for the exact gate set, and `@parametric` and `@adaptive` for programs that require arbitrary-angle rotations or classical feedback.

## Overview

<Tabs>
  <Tab title="@coherent">
    `@coherent` is the primary decorator for safe quantum programming. It enforces the exact gate set, requires all ancillae to be managed through the compute/phase/uncompute discipline, and produces an `ExactProgram` with `Certification.SAFE`. Every `@coherent` program that builds successfully is a proven unitary channel.

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

    @coherent
    def phase_oracle(sys: QReg):
        with ancilla(1) as anc:
            compute(lambda: cx(sys[0], anc[0]))
            phase(lambda: z(anc[0]))
            uncompute()

    prog = phase_oracle.build_exact(("sys", 1))
    assert prog.certification == Certification.SAFE
    ```

    **Use `@coherent` when:**

    * You are writing a quantum subroutine intended for production use
    * You need the Hero Theorem guarantee (proven unitary channel)
    * Your circuit uses only gates from the exact gate set
    * You want b01t to certify ancilla cleanliness for you
  </Tab>

  <Tab title="@primitive">
    `@primitive` targets the same exact gate set as `@coherent`, and the resulting program is coherent (unitary), but scratch registers are caller-managed rather than certified by b01t. The program builds to `Certification.PRIMITIVE`.

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

    @primitive
    def bell_pair(q: QReg):
        h(q[0])
        cx(q[0], q[1])

    prog = bell_pair.build_exact(("q", 2))
    assert prog.certification == Certification.PRIMITIVE
    ```

    **Use `@primitive` when:**

    * You are writing a low-level building block with no ancillae, or where you are managing scratch manually
    * You understand the ancilla contract and are taking responsibility for it
    * You want the exact gate set restriction without the ancilla-discipline overhead
  </Tab>

  <Tab title="@parametric">
    `@parametric` allows arbitrary-angle rotation gates (`rx`, `ry`, `rz`, `cry`, `crz`) in addition to the exact gate set. It produces a `DSLFunction` that builds to an `IRProgram` via `.build()`. The resulting program uses `Effect.COHERENT` and does not carry a `Certification` value.

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

    @parametric
    def phase_shift(q: QReg, theta: float):
        h(q[0])
        rz(theta, q[0])
        h(q[0])

    prog = phase_shift.build(("q", 1))
    ```

    **Use `@parametric` when:**

    * Your circuit requires rotation gates with angles that are not multiples of π/4
    * You are implementing variational algorithms (VQE, QAOA) or quantum machine learning
    * You do not need the Hero Theorem guarantee
  </Tab>

  <Tab title="@adaptive">
    `@adaptive` extends `@parametric` with mid-circuit measurement and classical feedback via `measure`, `measure_all`, and `if_then`. It produces a `DSLFunction` that builds to an `IRProgram` via `.build()` with `Effect.ADAPTIVE`.

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

    @adaptive
    def teleport(src: QReg, bell: QReg, dst: QReg):
        h(bell[0])
        cx(bell[0], bell[1])
        cx(src[0], bell[0])
        h(src[0])
        m0 = measure(src[0])
        m1 = measure(bell[0])
        if_then(m1, lambda: cx(bell[1], dst[0]))
        if_then(m0, lambda: h(dst[0]))

    prog = teleport.build(("src", 1), ("bell", 2), ("dst", 1))
    ```

    **Use `@adaptive` when:**

    * Your circuit performs mid-circuit measurements
    * You need classical feedback to condition later gates on measurement outcomes
    * You are implementing quantum error correction or measurement-based protocols
  </Tab>
</Tabs>

## Comparison table

| Decorator     | Gate set          | Ancilla management | Measurements | Build method     | Result type    | Certification |
| ------------- | ----------------- | ------------------ | ------------ | ---------------- | -------------- | ------------- |
| `@coherent`   | Exact only        | Certified by b01t  | Forbidden    | `.build_exact()` | `ExactProgram` | `SAFE`        |
| `@primitive`  | Exact only        | Caller-managed     | Forbidden    | `.build_exact()` | `ExactProgram` | `PRIMITIVE`   |
| `@parametric` | Exact + rotations | Caller-managed     | Forbidden    | `.build()`       | `IRProgram`    | —             |
| `@adaptive`   | Exact + rotations | Caller-managed     | Allowed      | `.build()`       | `IRProgram`    | —             |

## `.build_exact()` vs `.build()`

The build method you use depends on which decorator you used.

### `.build_exact()` — for `@coherent` and `@primitive`

`build_exact()` is a method on `ExactDSLFunction` (the type returned by `@coherent` and `@primitive`). It takes one or more register specifications as `(name, size)` tuples, runs the validation pipeline, and returns an immutable `ExactProgram`.

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

@coherent
def my_circuit(a: QReg, b: QReg):
    h(a[0])
    cx(a[0], b[0])

# Pass one (name, size) tuple per register argument
prog = my_circuit.build_exact(("a", 1), ("b", 1))
print(prog.name)           # "my_circuit"
print(prog.regs)           # (QReg("a", 1), QReg("b", 1))
print(prog.certification)  # Certification.SAFE
```

<Note>
  The order of `(name, size)` tuples must match the order of the function's register parameters. b01t binds each `QReg` argument by position.
</Note>

### `.build()` — for `@parametric` and `@adaptive`

`build()` is a method on `DSLFunction` (returned by `@parametric` and `@adaptive`). It has the same call signature as `build_exact()` but returns an `IRProgram` instead.

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

@parametric
def variational_layer(q: QReg, theta: float, phi: float):
    rx(theta, q[0])
    rz(phi, q[0])

prog = variational_layer.build(("q", 1))
print(prog.effect)    # Effect.COHERENT
print(prog.is_safe)   # True (if ancilla discipline was followed)
```

<Warning>
  Do not call a `@coherent` or `@primitive` function with `.build()` — those are `ExactDSLFunction` instances and only expose `.build_exact()`. Likewise, `.build_exact()` is not available on `@parametric` or `@adaptive` functions.
</Warning>

## Calling decorated functions inside other decorated functions

You can call one decorated function from inside another, with some restrictions:

* A `@coherent` function **can** call another `@coherent` function freely.
* A `@coherent` function **cannot** call a `@primitive` function outside a `compute()` or `phase()` block. If you need to use a `@primitive` subroutine at the top level, declare the caller `@primitive` as well.
* A `@parametric` or `@adaptive` function **can** call `@coherent` and `@primitive` functions (they are subsumed).
* A `@parametric` function **cannot** call an `@adaptive` function — coherent functions cannot contain measurements.

```python theme={null}
from b01t import coherent, primitive, QReg, x, cx
from b01t.kit import ancilla, compute, phase, uncompute, adjoint

@primitive
def flip(q: QReg):
    x(q[0])

@coherent
def use_flip(sys: QReg):
    with ancilla(1) as anc:
        # @primitive can be called inside compute/phase blocks
        compute(lambda: flip(anc))
        phase(lambda: None)
        uncompute()
```
