## `eigenkit.photonics.backends.perceval_backend`

The Perceval executor for photonic circuits (experimental).

It translates a PhotonicCircuitSpec into a Perceval `Circuit`, and computes the output distribution of photon numbers with an exact Perceval simulator, by default simulation of linear optical systems (SLOS). It is the linear-optics counterpart of a qubit `Provider`, but it does **not** implement the qubit `Provider` protocol: there is no `CircuitSpec`, no Pauli `Observable` and no array of expectation values. This is why photonics is a separate package.

`perceval` is imported only *inside* the methods, when they run, so importing this module (and therefore `import eigenkit`) never imports the Perceval software development kit (SDK). The data module `photonics.ir` never uses Perceval at all.

The work is split between compilation and evaluation, as in the qubit providers: `compile()` is called once, and `run(weights, inputs)` at every call. For now, the Perceval circuit is built again at every call, after the `Parameter` expressions are evaluated to numbers. Building it once with symbolic `pcvl.P` parameters, and setting their values at each call, is not done yet. (This is the saving that the qubit providers get from the split between compilation and evaluation.)

### `PercevalBackend`

The photonic executor that runs on a local Perceval simulator.

`compile(spec)` returns a `PercevalCompiled`.

Parameters:

| Name           | Type  | Description                                                                                                                                                                                                                                                                                                               | Default  |
| -------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `backend_name` | `str` | A Perceval backend that computes the full output distribution. "SLOS" (the default) and "Naive" are exact, while "MPS" and "NaiveApprox" approximate the distribution, and so the gradients too. Backends that only sample, such as "CliffordClifford2017", are rejected, because run returns probabilities, not samples. | `'SLOS'` |

Raises:

| Type         | Description                                 |
| ------------ | ------------------------------------------- |
| `ValueError` | If backend_name is unknown or only samples. |

#### `compile(spec)`

Wrap `spec` in a reusable handle. Perceval runs at each call of the handle.

Parameters:

| Name   | Type                  | Description             | Default    |
| ------ | --------------------- | ----------------------- | ---------- |
| `spec` | `PhotonicCircuitSpec` | The circuit to compile. | *required* |

Returns:

| Type               | Description                           |
| ------------------ | ------------------------------------- |
| `PercevalCompiled` | The PercevalCompiled handle for spec. |

### `PercevalCompiled`

A reusable handle to a compiled photonic circuit, cheap to call many times.

It is the linear-optics counterpart of `CompiledCircuit`. `PercevalBackend.compile` builds it.

Parameters:

| Name           | Type                  | Description                                                    | Default    |
| -------------- | --------------------- | -------------------------------------------------------------- | ---------- |
| `spec`         | `PhotonicCircuitSpec` | The circuit that this handle evaluates.                        | *required* |
| `backend_name` | `str`                 | The Perceval backend, as for PercevalBackend. Default: "SLOS". | `'SLOS'`   |

Raises:

| Type         | Description                                 |
| ------------ | ------------------------------------------- |
| `ValueError` | If backend_name is unknown or only samples. |

#### `expectation(weights, inputs, observable)`

Return the expectation value of a `PhotonicObservable`, as a single number.

The observable is diagonal in the Fock basis.

Parameters:

| Name         | Type                 | Description                          | Default    |
| ------------ | -------------------- | ------------------------------------ | ---------- |
| `weights`    | `ndarray`            | The 1-D weight vector.               | *required* |
| `inputs`     | `ndarray`            | The 1-D input vector.                | *required* |
| `observable` | `PhotonicObservable` | A PhotonNumber or a FockProbability. | *required* |

Raises:

| Type         | Description                                              |
| ------------ | -------------------------------------------------------- |
| `ValueError` | If observable can never match the output of the circuit. |

#### `jacobian(weights, inputs, observable, *, method='parameter_shift', need_weight_grad=True, need_input_grad=True)`

Return `(d<O>/d weights, d<O>/d inputs)` for an observable.

The observable is diagonal in the Fock basis. Only one sample is accepted (1-D weights and inputs).

Parameters:

| Name               | Type                 | Description                                                                                                                                                                               | Default             |
| ------------------ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| `weights`          | `ndarray`            | The 1-D weight vector.                                                                                                                                                                    | *required*          |
| `inputs`           | `ndarray`            | The 1-D input vector.                                                                                                                                                                     | *required*          |
| `observable`       | `PhotonicObservable` | A PhotonNumber or a FockProbability.                                                                                                                                                      | *required*          |
| `method`           | `str`                | "parameter_shift" for the exact photonic parameter-shift rule (in gradients.py), or "finite_difference" for central finite differences, the reference method. Default: "parameter_shift". | `'parameter_shift'` |
| `need_weight_grad` | `bool`               | If True, compute the gradient with respect to the weights. If False, it is skipped, and None is returned in its place.                                                                    | `True`              |
| `need_input_grad`  | `bool`               | If True, compute the gradient with respect to the inputs. If False, it is skipped, and None is returned in its place.                                                                     | `True`              |

Raises:

| Type         | Description                               |
| ------------ | ----------------------------------------- |
| `ValueError` | If method is neither of these two values. |

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

Return the output distribution of photon numbers.

The distribution is a dictionary `{photon_counts_tuple: probability}`. Fock states with probability zero may be left out, because Perceval's `prob_distribution` is sparse.

Parameters:

| Name      | Type      | Description                                      | Default    |
| --------- | --------- | ------------------------------------------------ | ---------- |
| `weights` | `ndarray` | The 1-D weight vector, of length spec.n_weights. | *required* |
| `inputs`  | `ndarray` | The 1-D input vector, of length spec.n_inputs.   | *required* |

Raises:

| Type         | Description                               |
| ------------ | ----------------------------------------- |
| `ValueError` | If weights or inputs has the wrong shape. |

### `build_perceval_circuit(spec, weights, inputs)`

Translate a `PhotonicCircuitSpec` into a Perceval `Circuit`.

The `Parameter` angles are evaluated to numbers first. The local and the remote executors both use this function, so they build exactly the same circuit. (The live test of the remote executor compares sampled and exact results on the *same* circuit.)

Parameters:

| Name      | Type                  | Description                                        | Default    |
| --------- | --------------------- | -------------------------------------------------- | ---------- |
| `spec`    | `PhotonicCircuitSpec` | The circuit to translate.                          | *required* |
| `weights` | `ndarray`             | The 1-D weight vector used to evaluate the angles. | *required* |
| `inputs`  | `ndarray`             | The 1-D input vector used to evaluate the angles.  | *required* |

Returns:

| Name      | Type      | Description                         |
| --------- | --------- | ----------------------------------- |
| `circuit` | `Circuit` | The circuit, on spec.n_modes modes. |

Raises:

| Type         | Description                                                             |
| ------------ | ----------------------------------------------------------------------- |
| `ValueError` | If weights or inputs is not 1-D with the length that the spec declares. |

### `fock_tuple(state, n_modes)`

Convert a Perceval `BasicState`, or any indexable object, to a tuple of photon counts.

Parameters:

| Name      | Type         | Description                  | Default                                              |
| --------- | ------------ | ---------------------------- | ---------------------------------------------------- |
| `state`   | \`BasicState | Sequence[int]\`              | The number of photons in each mode, indexed by mode. |
| `n_modes` | `int`        | The number of modes to read. | *required*                                           |

Returns:

| Type              | Description                                                 |
| ----------------- | ----------------------------------------------------------- |
| `tuple[int, ...]` | The number of photons in each mode, as a tuple of integers. |
