# ModeRotation

### *class* ModeRotation(rotation_unitary, , synthesis=None)

Implements a passive linear-optical mode transformation.

Bases: [`BosonicGate`](qiskit_bosons.circuit.BosonicGate.html.md#qiskit_bosons.circuit.BosonicGate)

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`](qiskit_bosons.circuit.library.Evolution.html.md#qiskit_bosons.circuit.library.Evolution) gates) is
what the [`synthesis`](qiskit_bosons.circuit.BosonicGate.html.md#qiskit_bosons.circuit.BosonicGate.synthesis) attribute governs. It is `None` by default, which
leaves the gate whole: there is no preferred decomposition, and a method such as
[`BosonicClementsDecomposition`](qiskit_bosons.circuit.library.synthesis.BosonicClementsDecomposition.html.md#qiskit_bosons.circuit.library.synthesis.BosonicClementsDecomposition) is chosen explicitly, either here or for a whole circuit
with the [`B2BSynthesis`](qiskit_bosons.transpiler.passes.B2BSynthesis.html.md#qiskit_bosons.transpiler.passes.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()`](#qiskit_bosons.circuit.library.ModeRotation.to_operator) directly is a few lines long; see [Lower a rotation without decomposing it](../guides/mode_rotation.html.md#mode-rotation-direct-explanation).
The two-mode [`beamsplitter()`](#qiskit_bosons.circuit.library.ModeRotation.beamsplitter) follows the convention of Clements et al. <sup>[1](#id2)</sup>

#### 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`.

* <a id='id2'>**[1]**</a> W. R. Clements et al., Optimal design for universal multiport interferometers, Optica 3, 1460 (2016), [arXiv:1603.08788](https://arxiv.org/abs/1603.08788).

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*](qiskit_bosons.circuit.library.synthesis.B2BSynthesisPlugin.html.md#qiskit_bosons.circuit.library.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()`](qiskit_bosons.circuit.BosonicCircuit.html.md#qiskit_bosons.circuit.BosonicCircuit.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, <sup>[1](#id2)</sup> 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*](https://docs.python.org/3/builtins/functions.html#float)) – the mixing angle $\theta$. A value of $\pi/4$ gives the balanced
    50:50 beamsplitter.
  * **phi** ([*float*](https://docs.python.org/3/builtins/functions.html#float)) – the relative phase $\phi$.
* **Returns:**
  A two-mode [`ModeRotation`](#qiskit_bosons.circuit.library.ModeRotation).
* **Return type:**
  [*ModeRotation*](#qiskit_bosons.circuit.library.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*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)) – the $M \times M$ anti-Hermitian generator $K$.
* **Returns:**
  A [`ModeRotation`](#qiskit_bosons.circuit.library.ModeRotation) with `rotation_unitary` $= \exp(K)$.
* **Return type:**
  [*ModeRotation*](#qiskit_bosons.circuit.library.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*](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray)

#### inverse(annotated=False)

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

* **Parameters:**
  **annotated** ([*bool*](https://docs.python.org/3/builtins/functions.html#bool)) – ignored. The inverse of this gate is another [`ModeRotation`](#qiskit_bosons.circuit.library.ModeRotation), so it
  never needs to be an [`AnnotatedOperation`](https://quantum.cloud.ibm.com/docs/api/qiskit/qiskit.circuit.AnnotatedOperation).
* **Returns:**
  A [`ModeRotation`](#qiskit_bosons.circuit.library.ModeRotation) with the same synthesis method.
* **Return type:**
  [*ModeRotation*](#qiskit_bosons.circuit.library.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*](https://docs.python.org/3/library/collections.abc.html#collections.abc.Sequence) *[*[*float*](https://docs.python.org/3/builtins/functions.html#float) *]*) – the phase $\varphi_m$ applied to each mode.
* **Returns:**
  A [`ModeRotation`](#qiskit_bosons.circuit.library.ModeRotation) that is diagonal in the mode basis.
* **Return type:**
  [*ModeRotation*](#qiskit_bosons.circuit.library.ModeRotation)

#### to_operator(tol=1e-12)

Returns the Hermitian generator of this gate as a [`BosonOperator`](qiskit_bosons.operators.BosonOperator.html.md#qiskit_bosons.operators.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`](qiskit_bosons.circuit.library.Evolution.html.md#qiskit_bosons.circuit.library.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`](https://quantum.cloud.ibm.com/docs/api/qiskit/qiskit.circuit.library.PauliEvolutionGate) requires of the mapped operator.

* **Parameters:**
  **tol** ([*float*](https://docs.python.org/3/builtins/functions.html#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`](qiskit_bosons.operators.BosonOperator.html.md#qiskit_bosons.operators.BosonOperator).
