Skip to content

gradients

eigenkit.photonics.gradients

Gradients of photonic circuits: the photonic parameter-shift rule, and finite differences as a simple reference method.

The qubit rule shifts the angle by ±π/2. It holds for a Pauli rotation because a Pauli generator has the eigenvalues ±½, which give a single frequency. The generator of a linear-optical component instead has equally spaced eigenvalues. For a phase shifter (and for the phi of a beam splitter), the generator is the number operator n̂, whose eigenvalues are integers. The expectation value of any observable that is diagonal in the Fock basis is then a trigonometric polynomial in the angle, of degree at most N, where N is the total number of photons. Its derivative is given exactly by the general shift rule for equally spaced frequencies (Wierichs et al., 2022), which uses 2N terms. At N = 1, this rule is the usual ±π/2 rule.

The theta of a beam splitter needs more care. Its generator is a generator of the special unitary group SU(2), on the two modes that the beam splitter acts on. The eigenvalues of this generator are integers or half-integers, depending on whether the number of photons n_ij in those two modes is even or odd: U(theta + 2π) = (-1)^n_ij. This is a sign that depends on the photon-number sector, not a global phase. In a circuit with two modes, n_ij = N always, so the frequencies are integers at most N, and the rule above applies. With more modes, the components around the beam splitter create coherent combinations of sectors with even and odd photon numbers, and half-integer frequencies appear. The rule is then applied in the angle theta / 2, with order 2N (4N evaluations).

This module is the linear-optics counterpart of core.provider.parameter_shift_jacobian. It reuses the chain_factor and chain_factor_input functions of the qubit intermediate representation to turn the derivative with respect to a component angle into derivatives with respect to the weights and inputs that the angle depends on. The executor reuses the same Parameter expression tree.

Reference: the photonic parameter-shift rule, arXiv:2410.02726 (Phys. Rev. A 111, 032429).

Scope: one sample at a time (1-D weights and inputs), and observables that are diagonal in the Fock basis.

finite_difference_jacobian(executor, spec, weights, inputs, observable, *, eps=1e-06, need_weight_grad=True, need_input_grad=True)

Compute the gradients with central finite differences, as a simple reference.

It shifts the weights and inputs themselves, so the chain rule is included automatically. It is the reference that checks the exact rule, in the same way as the NumPy simulator is the reference for qubit circuits.

Parameters:

Name Type Description Default
executor Executor

The executor that evaluates the circuit, for example PercevalBackend().

required
spec PhotonicCircuitSpec

The circuit to differentiate.

required
weights ndarray

The 1-D weight vector.

required
inputs ndarray

The 1-D input vector.

required
observable PhotonicObservable

An observable that is diagonal in the Fock basis.

required
eps float

The step of the central difference. Default: 1e-6.

1e-06
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

Returns:

Name Type Description
weight_jac ndarray | None

The gradient with respect to the weights, as for photonic_parameter_shift_jacobian.

input_jac ndarray | None

The gradient with respect to the inputs, as for photonic_parameter_shift_jacobian.

Raises:

Type Description
ValueError

If observable can never match the output of the circuit.

photonic_parameter_shift_jacobian(executor, spec, weights, inputs, observable, *, order=None, need_weight_grad=True, need_input_grad=True)

Compute the exact d<O>/d(weights) and d<O>/d(inputs) with the photonic parameter-shift rule.

Parameters:

Name Type Description Default
executor Executor

The executor that evaluates each shifted circuit, for example PercevalBackend().

required
spec PhotonicCircuitSpec

The circuit to differentiate.

required
weights ndarray

The 1-D weight vector.

required
inputs ndarray

The 1-D input vector.

required
observable PhotonicObservable

An observable that is diagonal in the Fock basis.

required
order int | None

The degree of the trigonometric polynomial to reconstruct. The default is the total number of photons, which is a safe upper bound: the rule is exact whenever the true degree is at most order. In a circuit with more than two modes, the theta of a beam splitter is reconstructed in theta / 2 with order 2 * order (see the module docstring). order=1 gives the simple ±π/2 estimate, which is in general wrong with more than one photon. The tests use it to show that the rule with more terms is necessary.

None
need_weight_grad bool

If True, compute the gradient with respect to the weights. If False, its shifted evaluations are 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, its shifted evaluations are skipped, and None is returned in its place.

True

Returns:

Name Type Description
weight_jac ndarray | None

The gradient with respect to the weights, of length spec.n_weights, or None if it was not requested.

input_jac ndarray | None

The gradient with respect to the inputs, of length spec.n_inputs, or None if it was not requested.

Raises:

Type Description
ValueError

If observable can never match the output of the circuit.