## `eigenkit.core.builder`

A builder that creates a `CircuitSpec` one gate at a time.

The style follows Qiskit, Amazon Braket and CUDA Quantum: `qc.rx(angle, qubit)` adds a gate to `qc` itself and returns `qc`, so that calls can be chained.

`CircuitBuilder` sits between the ready-made blocks and writing the circuit data by hand. You can write a circuit gate by gate when that is the natural way, and then, if you want, call `.to_block()` to combine the result with other blocks.

### `CircuitBuilder`

Build a `CircuitSpec` one gate at a time.

Example

```python
qc = CircuitBuilder(n_qubits=3)
weights = qc.add_weights(6)
inputs = qc.add_inputs(3)
for qubit in range(3):
    qc.ry(inputs[qubit], qubit=qubit)
for depth in range(2):
    for qubit in range(3):
        qc.ry(weights[depth * 3 + qubit], qubit=qubit)
    for qubit in range(2):
        qc.cnot(qubit, qubit + 1)
spec = qc.build()
```

Parameters:

| Name       | Type  | Description                                                                                                             | Default    |
| ---------- | ----- | ----------------------------------------------------------------------------------------------------------------------- | ---------- |
| `n_qubits` | `int` | The number of qubits of the circuit being built. Every qubit index that a gate uses must be between 0 and n_qubits - 1. | *required* |

Raises:

| Type         | Description                  |
| ------------ | ---------------------------- |
| `ValueError` | If n_qubits is not positive. |

#### `n_inputs`

The number of input features that `add_inputs()` has reserved so far.

The built CircuitSpec has the same number.

#### `n_qubits`

The number of qubits of the circuit, as given to the constructor.

Every qubit index that a gate uses must be smaller than this number, and the number never changes.

#### `n_weights`

The number of trainable weights that `add_weights()` has reserved so far.

The built CircuitSpec has the same number.

#### `add_inputs(count)`

Reserve `count` input features and return references to them.

This is the counterpart of `add_weights()` for data instead of trainable parameters.

Parameters:

| Name    | Type  | Description                              | Default    |
| ------- | ----- | ---------------------------------------- | ---------- |
| `count` | `int` | The number of input features to reserve. | *required* |

Returns:

| Type             | Description                                       |
| ---------------- | ------------------------------------------------- |
| `list[InputRef]` | One InputRef for each reserved feature, in order. |

#### `add_weights(count)`

Reserve `count` trainable weights and return references to them.

Pass a returned `WeightRef` wherever a gate takes an angle. The indices are local to this builder and start at `0`. When the block is combined with others, `assemble` shifts them to their positions in the whole circuit.

Parameters:

| Name    | Type  | Description                       | Default    |
| ------- | ----- | --------------------------------- | ---------- |
| `count` | `int` | The number of weights to reserve. | *required* |

Returns:

| Type              | Description                                       |
| ----------------- | ------------------------------------------------- |
| `list[WeightRef]` | One WeightRef for each reserved weight, in order. |

#### `build()`

Return the current state of the builder as an immutable `CircuitSpec`.

#### `cnot(control, target)`

Add a controlled-NOT (CNOT) gate.

Parameters:

| Name      | Type  | Description                       | Default    |
| --------- | ----- | --------------------------------- | ---------- |
| `control` | `int` | The qubit that controls the flip. | *required* |
| `target`  | `int` | The qubit that is flipped.        | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

Raises:

| Type         | Description                               |
| ------------ | ----------------------------------------- |
| `ValueError` | If control and target are the same qubit. |

#### `cphase(angle, control, target)`

Add a controlled phase shift gate.

The gate is symmetric: the control and the target play the same role.

Parameters:

| Name      | Type            | Description                                                                     | Default    |
| --------- | --------------- | ------------------------------------------------------------------------------- | ---------- |
| `angle`   | `ParameterLike` | The phase. It can be a number or a symbolic reference.                          | *required* |
| `control` | `int`           | One of the two qubits.                                                          | *required* |
| `target`  | `int`           | The other qubit. The gate acts in the same way if the two qubits are exchanged. | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `cry(angle, control, target)`

Add a controlled `RY` gate.

Parameters:

| Name      | Type            | Description                                                     | Default    |
| --------- | --------------- | --------------------------------------------------------------- | ---------- |
| `angle`   | `ParameterLike` | The rotation angle. It can be a number or a symbolic reference. | *required* |
| `control` | `int`           | The qubit that controls the rotation.                           | *required* |
| `target`  | `int`           | The qubit that is rotated.                                      | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `h(qubit)`

Add a Hadamard gate.

Parameters:

| Name    | Type  | Description       | Default    |
| ------- | ----- | ----------------- | ---------- |
| `qubit` | `int` | The target qubit. | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `mcx(*qubits)`

Add a multi-controlled `X` gate.

All the qubits except the last one are controls.

Parameters:

| Name      | Type  | Description                                                                           | Default |
| --------- | ----- | ------------------------------------------------------------------------------------- | ------- |
| `*qubits` | `int` | Two or more distinct qubits. The controls come first, and the target is the last one. | `()`    |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

Raises:

| Type         | Description                                                    |
| ------------ | -------------------------------------------------------------- |
| `ValueError` | If fewer than two qubits are given, or if a qubit is repeated. |

#### `mcz(*qubits)`

Add a multi-controlled `Z` gate.

All the qubits except the last one are controls.

Parameters:

| Name      | Type  | Description                                                                           | Default |
| --------- | ----- | ------------------------------------------------------------------------------------- | ------- |
| `*qubits` | `int` | Two or more distinct qubits. The controls come first, and the target is the last one. | `()`    |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

Raises:

| Type         | Description                                                    |
| ------------ | -------------------------------------------------------------- |
| `ValueError` | If fewer than two qubits are given, or if a qubit is repeated. |

#### `pauli_exponential(paulis, angle, coefficient=1.0)`

Add the exponential of a product of Pauli operators.

The gate is `exp(-i * coefficient * angle * P)`, where `P` is the product of `paulis`. Each element of `paulis` is a pair `(op, qubit)`, where `op` is `"X"`, `"Y"` or `"Z"`, and the qubits are distinct.

This is the Trotter building block that the quantum approximate optimisation algorithm (QAOA) needs for its cost and mixer layers, and that the unitary coupled-cluster singles and doubles (UCCSD) ansatz needs for its excitation operators. It is built from the core gates only. First, `H` gates (for `X`) and `RX(π/2)` gates (for `Y`) change each factor to the `Z` basis. Then a ladder of controlled-NOT (CNOT) gates accumulates the parity on the last qubit, and a central `RZ(2 * coefficient * angle)` is applied (because `RZ(φ) = exp(-i φ Z / 2)`). Finally, the inverse of the first steps is applied.

Parameters:

| Name          | Type                        | Description                                                                                                                                                                | Default    |
| ------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `paulis`      | `Sequence[tuple[str, int]]` | The Pauli product, as (operator, qubit) pairs, where the operator is "X", "Y" or "Z" and the qubits are distinct. An empty sequence is the identity, and nothing is added. | *required* |
| `angle`       | `ParameterLike`             | The rotation angle. It can be a number or a symbolic reference.                                                                                                            | *required* |
| `coefficient` | `float`                     | A number that multiplies the angle. This is where the weight of a Hamiltonian term goes. Default: 1.0.                                                                     | `1.0`      |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `phase_shift(angle, qubit)`

Add a phase shift gate `R(φ)`.

The gate multiplies `|1⟩` by `e^(iφ)` and leaves `|0⟩` unchanged.

Parameters:

| Name    | Type            | Description                                              | Default    |
| ------- | --------------- | -------------------------------------------------------- | ---------- |
| `angle` | `ParameterLike` | The phase φ. It can be a number or a symbolic reference. | *required* |
| `qubit` | `int`           | The target qubit.                                        | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `rx(angle, qubit)`

Add a rotation about the `X` axis.

Parameters:

| Name    | Type            | Description                                                                                                                          | Default    |
| ------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
| `angle` | `ParameterLike` | The rotation angle. It can be a number, or a WeightRef, an InputRef or an expression, which is evaluated when the circuit is called. | *required* |
| `qubit` | `int`           | The target qubit.                                                                                                                    | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `ry(angle, qubit)`

Add a rotation about the `Y` axis.

Parameters:

| Name    | Type            | Description                                                                           | Default    |
| ------- | --------------- | ------------------------------------------------------------------------------------- | ---------- |
| `angle` | `ParameterLike` | The rotation angle. It can be a number, or a WeightRef, an InputRef or an expression. | *required* |
| `qubit` | `int`           | The target qubit.                                                                     | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `rz(angle, qubit)`

Add a rotation about the `Z` axis.

Parameters:

| Name    | Type            | Description                                                                           | Default    |
| ------- | --------------- | ------------------------------------------------------------------------------------- | ---------- |
| `angle` | `ParameterLike` | The rotation angle. It can be a number, or a WeightRef, an InputRef or an expression. | *required* |
| `qubit` | `int`           | The target qubit.                                                                     | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `s(qubit)`

Add an `S` gate, which is a quarter turn about the `Z` axis.

Its inverse is `sdg()`.

Parameters:

| Name    | Type  | Description       | Default    |
| ------- | ----- | ----------------- | ---------- |
| `qubit` | `int` | The target qubit. | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `sdg(qubit)`

Add an S-dagger gate (`Sdg`), which is the inverse of `s()`.

Parameters:

| Name    | Type  | Description       | Default    |
| ------- | ----- | ----------------- | ---------- |
| `qubit` | `int` | The target qubit. | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `swap(qubit0, qubit1)`

Add a `SWAP` gate, which exchanges the states of two qubits.

Parameters:

| Name     | Type  | Description       | Default    |
| -------- | ----- | ----------------- | ---------- |
| `qubit0` | `int` | The first qubit.  | *required* |
| `qubit1` | `int` | The second qubit. | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `to_block()`

Wrap the current state of the builder in a `CustomBlock`.

This is useful to build a part of a circuit one gate at a time, and then to combine it with other blocks. When the blocks are assembled, the indices of the `WeightRef` and `InputRef` objects of this block are shifted to their positions in the whole circuit.

#### `x(qubit)`

Add a Pauli `X` gate, which is a bit flip.

Parameters:

| Name    | Type  | Description       | Default    |
| ------- | ----- | ----------------- | ---------- |
| `qubit` | `int` | The target qubit. | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |

#### `z(qubit)`

Add a Pauli `Z` gate, which is a phase flip.

Parameters:

| Name    | Type  | Description       | Default    |
| ------- | ----- | ----------------- | ---------- |
| `qubit` | `int` | The target qubit. | *required* |

Returns:

| Type               | Description                                 |
| ------------------ | ------------------------------------------- |
| `'CircuitBuilder'` | This builder, so that calls can be chained. |
