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

# Parametric and Adaptive Quantum Programs

> Use @parametric for coherent programs with arbitrary rotation angles, and @adaptive for measurement-based programs with classical feedback.

Not all quantum programs fit the exact gate set that `@coherent` requires. When you need arbitrary rotation angles — like `rx(0.785, q)` — use `@parametric`. When your program needs to measure qubits and branch on the results, use `@adaptive`. Both decorators produce an `IRProgram` via `.build()`, the broad-IR compilation path.

## @parametric: coherent programs with rotations

`@parametric` marks a function as a coherent (unitary) program that may use rotation gates with arbitrary float angles. The gate set includes everything in `@coherent` plus `rx`, `ry`, `rz`, `cry`, and `crz`.

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

@parametric
def variational_layer(sys: QReg, theta: float, phi: float):
    h(sys[0])
    rx(theta, sys[0])
    rz(phi, sys[1])
    cx(sys[0], sys[1])
```

Compile it to an `IRProgram` with `.build()`, passing register specs for the `QReg` parameters. Classical parameters like `theta` and `phi` are regular Python values — you pass them at build time as additional arguments after the register specs.

```python theme={null}
import math

ir = variational_layer.build(("sys", 2), math.pi / 4, math.pi / 2)
print(ir.name, ir.effect)  # variational_layer coherent
```

<Note>
  `@parametric` and `@coherent` both produce unitary programs, but `@coherent` uses the exact gate set and provides a machine-checked safety certification. Use `@parametric` when you need rotation angles; use `@coherent` when you want the strongest correctness guarantees.
</Note>

## @adaptive: measurement and classical feedback

`@adaptive` marks a function as a program that may measure qubits and branch on classical results. Use `measure(q)` to measure a single qubit or `measure_all(reg)` to measure all qubits in a register. Use `if_then()` from `b01t.kit` to branch on a measurement result.

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

@adaptive
def teleport_correct(sys: QReg, ancilla: QReg):
    # ... Bell pair preparation and Bell measurement omitted ...
    result = measure_all(sys)
    if_then(
        result,
        lambda: x(ancilla[0]),  # then: apply correction
    )
```

Build an `@adaptive` function the same way — with `.build()` and register specs:

```python theme={null}
ir = my_adaptive_fn.build(("sys", 2), ("ancilla", 1))
```

<Warning>
  `@adaptive` functions cannot be called from inside a `@coherent` or `@parametric` function. Adaptive programs break unitarity by branching on measurement outcomes. If you try it, `DSLValidationError` is raised: `coherent functions cannot call adaptive functions`.
</Warning>

## Control flow

The `b01t.kit` module provides three control-flow helpers for use inside `@parametric` and `@adaptive` functions.

### repeat(count, body)

Repeat a body lambda a fixed number of times at build time. Useful for Grover iterations, variational circuit layers, or any fixed-repetition pattern.

```python theme={null}
from b01t import parametric, QReg, h, cx
from b01t.kit import repeat

@parametric
def layer_stack(sys: QReg):
    repeat(3, lambda: (h(sys[0]), cx(sys[0], sys[1])))
```

### for\_each(data, body)

Iterate over classical data at build time, emitting parameterized gates for each element. The body receives an index and the current data element.

```python theme={null}
from b01t import parametric, QReg, rz
from b01t.kit import for_each

@parametric
def angle_sweep(sys: QReg, angles: list):
    for_each(angles, lambda i, theta: rz(theta, sys[i % sys.size]))
```

### if\_then(cond, then\_body, else\_body)

Branch on a classical condition — typically a measurement result from an earlier `measure()` or `measure_all()` call. Only valid in `@adaptive` functions.

```python theme={null}
from b01t import adaptive, QReg, h, x, z, measure_all
from b01t.kit import if_then

@adaptive
def measure_and_correct(sys: QReg):
    h(sys[0])
    result = measure_all(sys)
    if_then(
        result,
        lambda: x(sys[0]),   # then: flip
        lambda: z(sys[0]),   # else: phase flip
    )
```

<Note>
  `if_then()` raises `DSLValidationError` if called inside a `@coherent` function. Classical branching is not allowed in unitary programs.
</Note>

## Complete example: Grover search

The Grover search demo combines `@parametric` and `@adaptive`. The oracle and diffusion are `@parametric` (they are unitary subroutines), and the outer search loop is `@adaptive` (it measures at the end).

```python theme={null}
from b01t import parametric, adaptive, QReg
from b01t import h, x, z, cx, ccx, cz, mcx, measure_all
from b01t import ancilla, compute, phase, uncompute
from b01t.kit import repeat


@parametric
def grover_step(sys: QReg):
    """One Grover iteration: oracle followed by diffusion."""
    # Phase oracle for f(x) = x0 AND x1
    with ancilla(1) as anc:
        compute(lambda: ccx(sys[0], sys[1], anc[0]))
        phase(lambda: z(anc[0]))
        uncompute()

    # Diffusion operator
    for q in sys:
        h(q)
        x(q)
    with ancilla(1) as anc:
        compute(lambda: mcx(sys.wires()[:-1], anc[0]))
        phase(lambda: cz(anc[0], sys[-1]))
        uncompute()
    for q in sys:
        x(q)
        h(q)


@adaptive
def grover_search(sys: QReg):
    """Full Grover search: initialize, iterate, measure."""
    for q in sys:
        h(q)
    repeat(2, lambda: grover_step(sys))
    return measure_all(sys)


# Build and inspect the IR
ir = grover_search.build(("sys", 2))
print(ir.effect)   # adaptive
print(ir.is_safe)  # False — adaptive programs are not certified safe
```

## Calling @parametric from @adaptive

`@parametric` functions are unitary subroutines, so they can be called freely from inside `@adaptive` functions. The Grover example above shows this pattern: `grover_step` is `@parametric` and is called inside `grover_search` which is `@adaptive`.

The `repeat()` helper works in both `@parametric` and `@adaptive` contexts.

## When to use which decorator

|                                 | `@coherent`      | `@parametric` | `@adaptive` |
| ------------------------------- | ---------------- | ------------- | ----------- |
| Arbitrary rotation angles       | No               | Yes           | Yes         |
| Measurement                     | No               | No            | Yes         |
| Classical branching (`if_then`) | No               | No            | Yes         |
| Safety certification            | `SAFE`           | No            | No          |
| Can be called from `@coherent`  | Yes              | No            | No          |
| Build method                    | `.build_exact()` | `.build()`    | `.build()`  |
| Returns                         | `ExactProgram`   | `IRProgram`   | `IRProgram` |
