## `eigenkit.photonics.ir`

The intermediate representation (IR) of photonic circuits, which use linear optics.

This module holds only data, and uses neither Perceval nor NumPy. It is the photonic counterpart of `core.ir`.

A photonic circuit is a sequence of linear-optical **components** that act on `n_modes` optical modes. They transform an input **Fock state**, which gives the number of photons in each mode. This model of computation is very different from the qubit-gate `CircuitSpec`. The states live in Fock space, the "gates" are beam splitters and phase shifters that act on modes, and the readout is a distribution of photon numbers, not the expectation value of a Pauli operator. This is why the photonic circuits have their own IR, instead of an adapted `CircuitSpec`.

It deliberately **reuses** the `Parameter` expression tree of the qubit IR (`Constant`, `WeightRef`, `InputRef`, `Scale`, `Sum` and `Multiply`). The angles and phases of the components are symbolic numbers, so a component can be trained (with a `WeightRef`) or can encode data (with an `InputRef`) with no extra code. The same `resolve` function evaluates the angles of both kinds of circuit. The simulation in Fock space and the translation to Perceval are in `backends/perceval_backend`, never in this module.

### `BeamSplitter`

A beam splitter on two modes.

`modes` are two adjacent modes `(m, m + 1)`, because Perceval can place a beam splitter only on adjacent modes. `theta` sets the reflectivity, with the Perceval convention `R = cos²(theta/2)`, so `theta = π/2` gives a balanced 50:50 beam splitter. `phi` is a relative phase. Each angle is a `Parameter` expression or a plain number, and a plain number is stored as a `Constant`. The exact unitary is set by the executor, which maps the component to Perceval's `pcvl.BS`, so this class only describes the component.

### `FockProbability`

The probability of detecting exactly the output Fock state `target`.

In formula form, `<O> = P(target)`.

### `Permutation`

A reordering of the modes, which is a network of mode swaps given as one component.

`perm[i]` is the output mode to which input mode `i` is sent. The component acts on the modes `0` to `len(perm) - 1`, so `perm` must reorder exactly these modes.

### `PhaseShifter`

A phase shift `exp(i·phi·n̂)` on a single mode.

`n̂` is the photon-number operator of the mode. `phi` is a `Parameter` expression or a plain number, and a plain number is stored as a `Constant`.

### `PhotonNumber`

The expected number of photons in a mode: `<O> = <n̂_mode>`.

### `PhotonicCircuitSpec`

A description of a linear-optical circuit that does not depend on the backend.

It is the photonic counterpart of `core.ir.CircuitSpec`. It cannot be changed after it is built, and it can be hashed and pickled.

`input_state` is the Fock state sent into the circuit, as the number of photons in each mode. For example, `(1, 0, 1, 0)` means one photon in mode `0` and one in mode `2`. What is measured (the full distribution of photon numbers, the expected photon number of each mode, or a function of the detection pattern) is left to the executor for now. A `PhotonicMeasurement` type is planned.

Every component must act on the modes `0` to `n_modes - 1`, and `input_state` must hold non-negative integers. Both are checked when the object is built, so a wrong circuit fails at that point, and not inside Perceval.

#### `n_photons`

The total number of photons sent into the circuit.

This number sets the cost of the simulation in Fock space.
