ModeRotation

class ModeRotation(rotation_unitary, *, synthesis=None)

Bases: BosonicGate

Implements a passive linear-optical mode transformation.

Given an \(M \times M\) unitary matrix \(U\) (rotation_unitary), this gate implements the basis change that maps the creation operators as

\[b^\dagger_i \mapsto \sum_j U_{ji} \, b^\dagger_j \, ,\]

equivalently the many-body unitary

\[\exp\left(\sum_{ij} \log(U)_{ij} \, b^\dagger_i b_j\right) \, .\]

The number of bosonic modes the gate acts on is the dimension \(M\) of rotation_unitary.

Action on the occupation sectors

Restricted to the single-particle sector, the gate is the \(M \times M\) matrix \(U\) itself. It acts on every occupation sector at once, however: the two-particle block is the symmetric square of \(U\) (carrying the \(\sqrt{2}\) factors), and so on up to the truncation.

Synthesis

Breaking this gate down in bosonic space (into a circuit of smaller Evolution gates) is what the synthesis attribute governs. It is None by default, which leaves the gate whole: there is no preferred decomposition, and a method such as BosonicClementsDecomposition is chosen explicitly, either here or for a whole circuit with the B2BSynthesis transpiler pass. The circuit it produces implements the same unitary as the gate (on the untruncated Fock space), and it is the definition of the gate.

A gate that is left whole reaches the boson-to-qubit stage, which needs a plugin registered for ModeRotation to lower it. The library ships none, because the transpiler preset decomposes the gate in bosonic space first. A plugin that maps the generator of to_operator() directly is a few lines long; see Lower a rotation without decomposing it. The two-mode beamsplitter() follows the convention of Clements et al. [1]

Warning

Because this gate mixes occupation sectors within a mode, a truncation that merely bounds the initial per-mode occupation is not sufficient. Acting on \(\lvert 1, 1 \rangle\), a two-mode rotation populates \(\lvert 2, 0 \rangle\), so a cutoff of max_occupation=1 silently produces a wrong state. The cutoff must bound the total boson number reachable in any single mode. This matters most when the modes have different cutoffs: a rotation moves bosons between modes, so each mode that can receive them needs a cutoff that covers what it can receive, not only what it starts with.

This gate is passive: it conserves the total boson number (it is generated by number-conserving \(b^\dagger_i b_j\) terms). The active Gaussian operations – squeezing and displacement – do not conserve boson number and are not expressible as a ModeRotation.

Initializing an instance of this gate can be done with the arguments listed below.

Parameters:
  • rotation_unitary (np.ndarray) – the \(M \times M\) unitary matrix \(U\) defining the mode transformation via \(b^\dagger_i \mapsto \sum_j U_{ji} b^\dagger_j\). It must be square and unitary; this is the caller’s responsibility and is not verified.

  • synthesis (B2BSynthesisPlugin | None) – the boson-to-boson synthesis method with which to decompose this gate, when it gets decomposed at all. If None (the default), the gate has no bosonic decomposition and decompose() leaves it in place.

Attributes

rotation_unitary

The unitary matrix representing the mode-transformation coefficients.

Methods

classmethod beamsplitter(theta, phi=0.0)

Constructs the two-mode beamsplitter.

Following the Clements convention, [1] the two-mode transfer matrix is

\[T(\theta, \phi) = \begin{pmatrix} e^{i\phi}\cos\theta & -\sin\theta \\ e^{i\phi}\sin\theta & \cos\theta \end{pmatrix} \, ,\]

which together with the single-mode phase shifts generates any \(U(M)\) when arranged in the Clements mesh.

Parameters:
  • theta (float) – the mixing angle \(\theta\). A value of \(\pi/4\) gives the balanced 50:50 beamsplitter.

  • phi (float) – the relative phase \(\phi\).

Returns:

A two-mode ModeRotation.

Return type:

ModeRotation

classmethod from_generator(generator)

Constructs the rotation from an anti-Hermitian generator.

Returns the gate whose rotation_unitary is \(\exp(K)\) for the given \(K\). For the result to be unitary, \(K\) must be anti-Hermitian (\(K^\dagger = -K\)); this is the caller’s responsibility and is not verified.

Parameters:

generator (ndarray) – the \(M \times M\) anti-Hermitian generator \(K\).

Returns:

A ModeRotation with rotation_unitary \(= \exp(K)\).

Return type:

ModeRotation

generator()

Returns the anti-Hermitian generator \(\log(U)\) of this rotation.

This is the matrix \(K\) such that the many-body unitary is \(\exp(\sum_{ij} K_{ij} b^\dagger_i b_j)\), which is what a synthesis plugin exponentiates.

Return type:

ndarray

inverse(annotated=False)

Returns the inverse of this gate, whose rotation unitary is \(U^\dagger\).

Parameters:

annotated (bool) – ignored. The inverse of this gate is another ModeRotation, so it never needs to be an AnnotatedOperation.

Returns:

A ModeRotation with the same synthesis method.

Return type:

ModeRotation

classmethod phase_shift(phases)

Constructs a product of single-mode phase shifts.

Each mode \(m\) acquires \(b^\dagger_m \mapsto e^{i \varphi_m} b^\dagger_m\), that is, the rotation unitary is \(\mathrm{diag}(e^{i\varphi_0}, \ldots)\).

Parameters:

phases (Sequence[float]) – the phase \(\varphi_m\) applied to each mode.

Returns:

A ModeRotation that is diagonal in the mode basis.

Return type:

ModeRotation

to_operator(tol=1e-12)

Returns the Hermitian generator of this gate as a BosonOperator.

Builds \(H = \sum_{ij} \mathrm{i} \log(U)_{ij} \, b^\dagger_i b_j\), the Hermitian operator for which \(e^{-\mathrm{i} H}\) is this gate. That is exactly the form of an Evolution gate at unit time, which makes this the bridge from the gate’s matrix form to the operator algebra, and hence to any boson-to-qubit encoding.

The matrix logarithm of a unitary is anti-Hermitian only up to round-off, so the result is built from the anti-Hermitian part of \(\log(U)\) and is exactly Hermitian, as a PauliEvolutionGate requires of the mapped operator.

Parameters:

tol (float) – entries of \(H\) with magnitude at or below this are dropped. logm of a sparse or block-diagonal unitary leaves round-off in nominally-zero entries; keeping them would bloat the operator with meaningless terms.

Returns:

The Hermitian generator as a BosonOperator.