qml.transforms.single_qubit_fusion¶
- single_qubit_fusion(tape, atol=1e-08, exclude_gates=None)[source]¶
Quantum function transform to fuse together groups of single-qubit operations into a general single-qubit unitary operation (
Rot
).Fusion is performed only between gates that implement the property
single_qubit_rot_angles
. Any sequence of two or more single-qubit gates (on the same qubit) with that property defined will be fused into oneRot
.- Parameters
tape (QNode or QuantumTape or Callable) – A quantum circuit.
atol (float) – An absolute tolerance for which to apply a rotation after fusion. After fusion of gates, if the fused angles \(\theta\) are such that \(|\theta|\leq \text{atol}\), no rotation gate will be applied.
exclude_gates (None or list[str]) – A list of gates that should be excluded from full fusion. If set to
None
, all single-qubit gates that can be fused will be fused.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], Callable]
Example
>>> dev = qml.device('default.qubit', wires=1)
You can apply the transform directly on
QNode
:@qml.transforms.single_qubit_fusion @qml.qnode(device=dev) def qfunc(r1, r2): qml.Hadamard(wires=0) qml.Rot(*r1, wires=0) qml.Rot(*r2, wires=0) qml.RZ(r1[0], wires=0) qml.RZ(r2[0], wires=0) return qml.expval(qml.X(0))
The single qubit gates are fused before execution.
Note
The fused angles between two sets of rotation angles are not always defined uniquely because Euler angles are not unique for some rotations.
single_qubit_fusion
makes a particular choice in this case.Warning
This function is not differentiable everywhere. It has singularities for specific input rotation angles, where the derivative will be NaN.
Warning
This function is numerically unstable at its singular points. It is recommended to use it with 64-bit floating point precision.
Usage Details
Consider the following quantum function.
def qfunc(r1, r2): qml.Hadamard(wires=0) qml.Rot(*r1, wires=0) qml.Rot(*r2, wires=0) qml.RZ(r1[0], wires=0) qml.RZ(r2[0], wires=0) return qml.expval(qml.X(0))
The circuit before optimization:
>>> qnode = qml.QNode(qfunc, dev) >>> print(qml.draw(qnode)([0.1, 0.2, 0.3], [0.4, 0.5, 0.6])) 0: ──H──Rot(0.1, 0.2, 0.3)──Rot(0.4, 0.5, 0.6)──RZ(0.1)──RZ(0.4)──┤ ⟨X⟩
Full single-qubit gate fusion allows us to collapse this entire sequence into a single
qml.Rot
rotation gate.>>> optimized_qfunc = qml.transforms.single_qubit_fusion(qfunc) >>> optimized_qnode = qml.QNode(optimized_qfunc, dev) >>> print(qml.draw(optimized_qnode)([0.1, 0.2, 0.3], [0.4, 0.5, 0.6])) 0: ──Rot(3.57, 2.09, 2.05)──┤ ⟨X⟩
Derivation
The matrix for an individual rotation is given by
\[\begin{split}R(\phi_j,\theta_j,\omega_j) &= \begin{bmatrix} e^{-i(\phi_j+\omega_j)/2}\cos(\theta_j/2) & -e^{i(\phi_j-\omega_j)/2}\sin(\theta_j/2)\\ e^{-i(\phi_j-\omega_j)/2}\sin(\theta_j/2) & e^{i(\phi_j+\omega_j)/2}\cos(\theta_j/2) \end{bmatrix}\\ &= \begin{bmatrix} e^{-i\alpha_j}c_j & -e^{i\beta_j}s_j \\ e^{-i\beta_j}s_j & e^{i\alpha_j}c_j \end{bmatrix},\end{split}\]where we introduced abbreviations \(\alpha_j,\beta_j=\frac{\phi_j\pm\omega_j}{2}\), \(c_j=\cos(\theta_j / 2)\) and \(s_j=\sin(\theta_j / 2)\) for notational brevity. The upper left entry of the matrix product \(R(\phi_2,\theta_2,\omega_2)R(\phi_1,\theta_1,\omega_1)\) reads
\[x = e^{-i(\alpha_2+\alpha_1)} c_2 c_1 - e^{i(\beta_2-\beta_1)} s_2 s_1\]and should equal \(e^{-i\alpha_f}c_f\) for the fused rotation angles. This means that we can obtain \(\theta_f\) from the magnitude of the matrix product entry above, choosing \(c_f=\cos(\theta_f / 2)\) to be non-negative:
\[\begin{split}c_f = |x| &= \left| e^{-i(\alpha_2+\alpha_1)} c_2 c_1 -e^{i(\beta_2-\beta_1)} s_2 s_1 \right| \\ &= \sqrt{c_1^2 c_2^2 + s_1^2 s_2^2 - 2 c_1 c_2 s_1 s_2 \cos(\omega_1 + \phi_2)}.\end{split}\]Now we again make a choice and pick \(\theta_f\) to be non-negative:
\[\theta_f = 2\arccos(|x|).\]We can also extract the angle combination \(\alpha_f\) from \(x\) via \(\operatorname{arg}(x)\), which can be readily computed with \(\arctan\):
\[\alpha_f = -\arctan\left( \frac{-c_1c_2\sin(\alpha_1+\alpha_2)-s_1s_2\sin(\beta_2-\beta_1)} {c_1c_2\cos(\alpha_1+\alpha_2)-s_1s_2\cos(\beta_2-\beta_1)} \right).\]We can use the standard numerical function
arctan2
, which computes \(\arctan(x_1/x_2)\) from \(x_1\) and \(x_2\) while handling special points suitably, to obtain the argument of the underlying complex number \(x_2 + x_1 i\).Finally, to obtain \(\beta_f\), we need a second element of the matrix product from above. We compute the lower-left entry to be
\[y = e^{-i(\beta_2+\alpha_1)} s_2 c_1 + e^{i(\alpha_2-\beta_1)} c_2 s_1,\]which should equal \(e^{-i \beta_f}s_f\). From this, we can compute
\[\beta_f = -\arctan\left( \frac{-c_1s_2\sin(\alpha_1+\beta_2)+s_1c_2\sin(\alpha_2-\beta_1)} {c_1s_2\cos(\alpha_1+\beta_2)+s_1c_2\cos(\alpha_2-\beta_1)} \right).\]From this, we may extract
\[\phi_f = \alpha_f + \beta_f\qquad \omega_f = \alpha_f - \beta_f\]and are done.
Special cases:
There are a number of special cases for which we can skip the computation above and can combine rotation angles directly.
If \(\omega_1=\phi_2=0\), we can simply merge the
RY
rotation angles \(\theta_j\) and obtain \((\phi_1, \theta_1+\theta_2, \omega_2)\).If \(\theta_j=0\), we can merge the two
RZ
rotations of the sameRot
and obtain \((\phi_1+\omega_1+\phi_2, \theta_2, \omega_2)\) or \((\phi_1, \theta_1, \omega_1+\phi_2+\omega_2)\). If bothRY
angles vanish we get \((\phi_1+\omega_1+\phi_2+\omega_2, 0, 0)\).
Note that this optimization is not performed for differentiable input parameters, in order to maintain differentiability.
Mathematical properties:
All functions above are well-defined on the domain we are using them on, if we handle \(\arctan\) via standard numerical implementations such as
np.arctan2
. Based on the choices we made in the derivation above, the fused angles will lie in the intervals\[\phi_f, \omega_f \in [-\pi, \pi],\quad \theta_f \in [0, \pi].\]Close to the boundaries of these intervals,
single_qubit_fusion
exhibits discontinuities, depending on the combination of input angles. These discontinuities also lead to singular (non-differentiable) points as discussed below.Differentiability:
The function derived above is differentiable almost everywhere. In particular, there are two problematic scenarios at which the derivative is not defined. First, the square root is not differentiable at \(0\), making all input angles with \(|x|=0\) singular. Second, \(\arccos\) is not differentiable at \(1\), making all input angles with \(|x|=1\) singular.