## `eigenkit.core.backends.qiskit_aer_backend`

The Qiskit Aer backend, with the statevector and matrix product state (MPS) methods.

Aer comes with the `qiskit` extra (`pip install eigenkit[qiskit]`), so this backend needs no new dependency. It reuses, unchanged, the translation of the plain Qiskit backend: the gate translation, the binding of the symbolic `Parameter` values and the `SparsePauliOp` observables. Only the execution is different. Instead of `Statevector.from_instruction`, it runs the bound circuit on an `AerSimulator` and reads the expectation values with the Aer `save_expectation_value` instruction. As a result, the **same** code measures with both the exact statevector simulator and the MPS simulator, which `method` selects.

`method="statevector"` is the dense Aer simulator, which is exact. `method="matrix_product_state"` is the Aer MPS simulator. It is exact when the bond dimension is not bounded, which is the Aer default. Otherwise, it is a fast approximation for circuits with little entanglement. The provider names are `qiskit-aer` for the statevector method and `qiskit-aer-mps` for the MPS method.

Both methods get the circuit translated first to gates that they can run (see `_to_native_basis`). The MPS method rejects `cry` with "contains invalid instructions" instead of decomposing it, and the statevector method cannot run the controlled unitary that `ControlledQubitUnitary` becomes.

The backend has `gradient_method = "parameter_shift"`. Aer has no cheap analytic reverse-mode Jacobian for these expectation values, so the dispatch layer computes the gradients with the parameter-shift rule, as for plain Qiskit. Expectation values do not depend on the qubit ordering convention, as long as the circuit and the operator use the same one, and here both use the Qiskit convention. So `run` needs no reordering. Only the statevectors that are read out are reordered to the convention of the library, where qubit 0 is the most significant bit.

Example

```python
from eigenkit import Circuit, Encoding, Variational, all_z

blocks = [Encoding(in_features=4), Variational(depth=2)]

# by its registered name
circuit = Circuit(blocks=blocks, observables=all_z(4), provider="qiskit-aer")

# or with the configuration, when a device setting matters
from eigenkit.configs import QiskitAerConfig

circuit = Circuit(
    blocks=blocks,
    observables=all_z(4),
    provider=QiskitAerConfig(method="matrix_product_state"),
)
```

The name `"qiskit-aer-mps"` selects the same backend with the MPS method already set. The backend comes with the `qiskit` extra.

### `QiskitAerBackend`

The provider for Qiskit Aer.

It is registered as `"qiskit-aer"` for the statevector method and as `"qiskit-aer-mps"` for the matrix product state method.

This class is internal. Select the backend with `provider="qiskit-aer"` or by passing a QiskitAerConfig, instead of constructing this class.

#### `capabilities`

Return what this backend supports.

It supports every gate, but only exact expectation values. It does not batch, and it has no limit on the number of qubits. The adjoint of `AmplitudeEncoding` is excluded, because the Qiskit gate translation does not implement it. See Capabilities.

#### `__init__(config=None, name=None)`

Build the backend from a device configuration.

Parameters:

| Name     | Type              | Description | Default                                                                                                                                |
| -------- | ----------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `config` | \`QiskitAerConfig | None\`      | The device settings. None uses QiskitAerConfig().                                                                                      |
| `name`   | \`str             | None\`      | The registry name, used in error messages. None gives "qiskit-aer-mps" for the matrix product state method and "qiskit-aer" otherwise. |

#### `compile(circuit, observables)`

Prepare `circuit` for repeated evaluation of `observables`.

It is called once per `Circuit`, and the handle is reused for every call, so the expensive work is done here.

Parameters:

| Name          | Type                   | Description                               | Default    |
| ------------- | ---------------------- | ----------------------------------------- | ---------- |
| `circuit`     | `CircuitSpec`          | The circuit to prepare.                   | *required* |
| `observables` | `Sequence[Observable]` | The observables that the handle measures. | *required* |

Returns:

| Type                | Description                 |
| ------------------- | --------------------------- |
| `QiskitAerCompiled` | A QiskitAerCompiled handle. |

### `QiskitAerCompiled`

Bases: `CompiledCircuit`

A circuit prepared for Qiskit Aer.

This class is internal. It is built by QiskitAerBackend.compile() and obtained from it, and it is never constructed directly.

#### `__init__(circuit, observables, config)`

Prepare the circuit for repeated evaluation.

`QiskitAerBackend.compile()` builds it, and there is no reason to construct one directly.

Parameters:

| Name          | Type                   | Description                                | Default    |
| ------------- | ---------------------- | ------------------------------------------ | ---------- |
| `circuit`     | `CircuitSpec`          | The circuit to prepare.                    | *required* |
| `observables` | `Sequence[Observable]` | The observables that this handle measures. | *required* |
| `config`      | `QiskitAerConfig`      | The device settings.                       | *required* |

#### `run(weights, inputs)`

Evaluate the circuit and return one expectation value per observable.

It follows the `CompiledCircuit` contract: the result has shape `(n_obs,)` when `weights` and `inputs` are both 1-D, and `(B, n_obs)` otherwise.

Parameters:

| Name      | Type      | Description                                                                                 | Default    |
| --------- | --------- | ------------------------------------------------------------------------------------------- | ---------- |
| `weights` | `ndarray` | The parameter vector: a 1-D array for one sample, or a 2-D array with a leading batch axis. | *required* |
| `inputs`  | `ndarray` | The input features, with the same 1-D or 2-D shapes as weights.                             | *required* |

Returns:

| Type      | Description                           |
| --------- | ------------------------------------- |
| `ndarray` | One expectation value per observable. |

#### `statevector(weights, inputs, initial_state=None)`

Return the full complex statevector after the circuit.

In the result, qubit 0 is the most significant bit. When `inputs` is 2-D, the method loops over its rows and returns an array of shape `(B, 2**n_qubits)`. `initial_state`, in the same order as the result, is loaded with the Aer `set_statevector` instruction.

Parameters:

| Name            | Type      | Description                                                                               | Default                             |
| --------------- | --------- | ----------------------------------------------------------------------------------------- | ----------------------------------- |
| `weights`       | `ndarray` | The parameter vector, which must be 1-D.                                                  | *required*                          |
| `inputs`        | `ndarray` | The input features: a 1-D array for one sample, or a 2-D array with a leading batch axis. | *required*                          |
| `initial_state` | \`ndarray | None\`                                                                                    | The state to evolve from instead of |

Returns:

| Type      | Description                                                                    |
| --------- | ------------------------------------------------------------------------------ |
| `ndarray` | The statevector, of shape (2\*\*n_qubits,), or (B, 2\*\*n_qubits) for a batch. |

#### `zero_state_probability(weights, inputs)`

Return the probability of the all-zeros state for one sample.

It is the squared magnitude of the all-zeros amplitude of the simulated statevector, and it is the basic operation of fidelity kernels.

Parameters:

| Name      | Type      | Description                                          | Default    |
| --------- | --------- | ---------------------------------------------------- | ---------- |
| `weights` | `ndarray` | The parameter vector, which must be 1-D.             | *required* |
| `inputs`  | `ndarray` | The input features of one sample, which must be 1-D. | *required* |

Returns:

| Type    | Description                         |
| ------- | ----------------------------------- |
| `float` | The probability, a float in [0, 1]. |

### `QiskitAerConfig`

Device configuration for the Qiskit Aer backend.

Parameters:

| Name     | Type  | Description                                                                                                                                                                                                                                                                                                                                                                                                                            | Default                                                                                                                                                               |
| -------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `method` | `str` | The Aer simulation method. "statevector" selects the exact dense simulator. "matrix_product_state" selects the matrix product state (MPS) simulator, which is exact when the bond dimension is not bounded, and an approximation for circuits with little entanglement otherwise. The provider name "qiskit-aer-mps" is this configuration with the MPS method already set. Any other value raises ValueError. Default: "statevector". | `'statevector'`                                                                                                                                                       |
| `shots`  | \`int | None\`                                                                                                                                                                                                                                                                                                                                                                                                                                 | None returns exact expectation values. Finite-shot sampling is not supported yet, and any other value makes the compilation raise NotImplementedError. Default: None. |

#### `make_backend()`

Build the backend that this configuration describes.

The provider layer calls it when a configuration is passed as `provider=`. There is no reason to call it directly.

Returns:

| Type                 | Description                                  |
| -------------------- | -------------------------------------------- |
| `'QiskitAerBackend'` | The QiskitAerBackend for this configuration. |
