Skip to content

reservoir

eigenkit.photonics.reservoir

Photonic reservoir computing, as a quantum extreme learning machine.

The reservoir is a fixed linear-optical interferometer, which is random and never trained. At each timestep, the input is encoded into its phases, the photons pass through it, and photon-number statistics are read out. This gives a nonlinear map to many features. Only a classical linear readout is trained, so the executor only has to evaluate the circuit, and no gradients go through the optics.

The parts that do not depend on the kind of hardware come from the qubit reservoir package, eigenkit.reservoir. This module uses its Stateless policy, which gives temporal context with a classical window of past inputs, and the readout can be that package's LinearReadout, a ridge regression solved in closed form. The part specific to photonics turns the input of each timestep into a vector of features from a Fock distribution, with a photonic Executor.

Scope: no memory, or a classical window of inputs (Stateless). Carrying the photonic state from one timestep to the next (a quantum memory, like the qubit Carry policy) needs the executor to accept a Fock state as input, and is not supported yet. Policies that carry state raise an error here.

fock_basis(n_modes, n_photons)

Return the sorted list of output Fock states.

The list holds every way to place n_photons photons into n_modes modes. Passive linear optics conserves the number of photons, so these are exactly the outputs that the circuit can reach. They are the fixed columns of the feature vector.

Parameters:

Name Type Description Default
n_modes int

The number of optical modes.

required
n_photons int

The total number of photons.

required

fock_probability_features(dist, basis)

Return the probability of every state in basis, as a dense vector.

These are the detailed features of the reservoir.

Parameters:

Name Type Description Default
dist dict

A Fock distribution, as {photon_counts_tuple: probability}.

required
basis list[tuple[int, ...]]

The output states, in the order of the features, for example from fock_basis.

required

photon_number_features(dist, n_modes)

Return the expected number of photons <n̂_m> in each mode m.

This readout is compact, and easy to measure with detectors.

Parameters:

Name Type Description Default
dist dict

A Fock distribution, as {photon_counts_tuple: probability}.

required
n_modes int

The number of optical modes.

required

random_reservoir_spec(n_modes, n_inputs, *, depth=2, input_state=None, input_scale=1.0, rng=None)

Build a fixed, random photonic reservoir and its frozen weights.

A mixing layer comes first, so that the photons are spread over the modes. Then the n_inputs features are encoded as phases on the modes 0 to n_inputs - 1 (each multiplied by input_scale), and depth more mixing layers follow. A mixing layer is a beam splitter on every pair of neighbouring modes, followed by a phase shifter on every mode. All their angles are random weights, drawn uniformly from [0, 2π).

Parameters:

Name Type Description Default
n_modes int

The number of optical modes. It must be at least 2.

required
n_inputs int

The number of features encoded at each step, with one phase each. It must be at least 1 and at most n_modes - 1.

required
depth int

The number of mixing layers after the encoding. It must be at least 1. Default: 2.

2
input_state tuple[int, ...] | None

The Fock state sent into the circuit. The default is a single photon in mode 0. More photons give a larger space of Fock features.

None
input_scale float

The factor applied to each input before it is used as a phase. Default: 1.0.

1.0
rng Generator | None

The source of the random weights of the reservoir. The default is a new, unseeded generator.

None

Returns:

Name Type Description
spec PhotonicCircuitSpec

The circuit of the reservoir.

weights ndarray

The frozen weights of the reservoir, which are never trained.

Raises:

Type Description
ValueError

If n_modes < 2, depth < 1, n_inputs is outside [1, n_modes - 1], or input_state holds no photon. In each of these cases, the features of the reservoir would ignore the input, or, when every mode encodes an input, would see only the differences between inputs.

run_photonic_reservoir(spec, weights, sequences, *, executor=None, policy=None, features='fock')

Run the fixed reservoir over input sequences, and return the features.

The reservoir spec, with its frozen weights, is evaluated at every timestep of sequences, of shape (B, T, F). The result is the sequence of features, of shape (B, T, n_features).

Parameters:

Name Type Description Default
spec PhotonicCircuitSpec

The reservoir, for example from random_reservoir_spec.

required
weights ndarray

The frozen weights of the reservoir.

required
sequences ndarray

The input sequences, of shape (B, T, F).

required
executor Executor | None

The executor that runs each step. The default is PercevalBackend().

None
policy Stateless | None

The policy whose expand_inputs turns each timestep into the circuit's n_inputs values (window * F). The default is Stateless(window=1). Policies that carry the state from one step to the next are not supported for photonics yet, because they need a Fock state as the input of the executor.

None
features str

"fock" gives the probabilities of all the states of the Fock basis with the input number of photons. This is the default, and gives the most features. "photon_number" gives the expected number of photons <n̂_m> in each mode.

'fock'

Returns:

Type Description
ndarray

The features, of shape (B, T, n_features). n_features is the number of Fock states with n_modes modes and spec.n_photons photons for "fock", and n_modes for "photon_number".

Raises:

Type Description
ValueError

If features is unknown, sequences is not 3-D, or the number of inputs per step that the policy gives does not match spec.n_inputs.

NotImplementedError

If policy carries state from one step to the next.