Repository: PennyLaneAI/pennylane
Pull request #4869 (2023-11-22 07:56:45)

Cleans up code and refactors tests for single qubit unitary decompositions
**Context:**

As I am implementing the XZX single qubit decomposition (https://github.com/PennyLaneAI/pennylane/pull/4862), I noticed some issues with the the code and the tests for single qubit unitary decompositions.

**Description of the Change:**

Cleans up ```transforms/decompositions/single_qubit_unitary.py```. 

- Removes duplicate code,
- Fixes doc examples that were untrue
- Adds support for returning global phase for the `rot` decomposition.
- Normalize all rotation angles to the range $[0, 4\pi]$

Refactors ```tests/transforms/test_decompositions.py```. 

- Generalizes logic for obtaining decompositions and asserting their correctness such that all types of decompositions share the same assertion logic. Removes duplicate code.
- Adds testing for when ```return_global_phase=False``` as well as when ```return_global_phase=True```
- Update test cases to use analytical instead of numerical values when possible.

**Benefits:**

Cleaner code.

**Related GitHub Issues:**

https://github.com/PennyLaneAI/pennylane/issues/4868
https://github.com/PennyLaneAI/pennylane/pull/4862


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4869?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`34aba12`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/34aba1216116dca37fed5afde7cb41d820a042b9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.65% compared to head [(`37b6e83`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4869?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.64%.
> Report is 1 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #4869      +/-   ##
==========================================
- Coverage   99.65%   99.64%   -0.01%     
==========================================
  Files         383      383              
  Lines       34518    34286     -232     
==========================================
- Hits        34398    34165     -233     
- Misses        120      121       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4869?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

rmoyard: Hi @astralcai  thanks a lot for this follow up PR. I am not sure it is possible to change the base of this PR because you are external, but that would be nice. Currently it is hard to distinguish your two PRs. If it is not possible, I will wait for the first one to be merged before reviewing.
astralcai: > Hi @astralcai thanks a lot for this follow up PR. I am not sure it is possible to change the base of this PR because you are external, but that would be nice. Currently it is hard to distinguish your two PRs. If it is not possible, I will wait for the first one to be merged before reviewing.

@rmoyard Hi, I have made another update to this PR, now normalizing all rotation angles to $[-2\pi, 2\pi]$ for consistency and easy of use (previously different types composition deals with this differently). I have also updated all test cases for single-qubit unitary decompositions to use analytical values as expected parameters when possible.
rmoyard: Hi @astralcai ! Once you solve the conflicts I will review this PR. One thing about angles for rotation, our canonical range is `[0, 4pi]`, same as Numpy. So it would be best to remove your angle change from this PR. Thanks :+1: 
astralcai: > Hi @astralcai ! Once you solve the conflicts I will review this PR. One thing about angles for rotation, our canonical range is `[0, 4pi]`, same as Numpy. So it would be best to remove your angle change from this PR. Thanks 👍

@rmoyard In this case, I will update the code for the `rot` decomposition to normalize its rotation angles to the `[0, 4pi]` range instead. 

Can I ask why this range is chosen? 
astralcai: > > Hi @astralcai ! Once you solve the conflicts I will review this PR. One thing about angles for rotation, our canonical range is `[0, 4pi]`, same as Numpy. So it would be best to remove your angle change from this PR. Thanks 👍
> 
> @rmoyard In this case, I will update the code for the `rot` decomposition to normalize its rotation angles to the `[0, 4pi]` range instead.
> 
> Can I ask why this range is chosen?

@rmoyard I have updated the PR to remove the angle change. It is ready for review now.
rmoyard: Thank you I will take a look between today and tomorrow. From what I remember, we wanted to follow the convention by numpy.mod and % where the rest is not negative. We thought it was a better software friendly convention compared to the other one which comes more from physics.
astralcai: > Thanks @astralcai, feel free to merge the PR 💯

Hi, thank you. I don't think I have permission to merge this PR.

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index bbc53b8dd..84949092d 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -99,6 +99,9 @@
 * `GlobalPhase` now decomposes to nothing, in case devices do not support global phases.
   [(#4855)](https://github.com/PennyLaneAI/pennylane/pull/4855)
 
+* The `rot` decomposition now has support for returning a global phase.
+  [(#4869)](https://github.com/PennyLaneAI/pennylane/pull/4869)
+
 <h3>Breaking changes 💔</h3>
 
 * The decomposition of `GroverOperator` now has an additional global phase operation.
@@ -126,6 +129,9 @@
 * Specifying `control_values` passed to `qml.ctrl` as a string is no longer supported.
   [(#4816)](https://github.com/PennyLaneAI/pennylane/pull/4816)
 
+* The `rot` decomposition will now normalize its rotation angles to the range `[0, 4pi]` for consistency
+  [(#4869)](https://github.com/PennyLaneAI/pennylane/pull/4869)
+
 <h3>Deprecations 👋</h3>
 
 * `qml.grad` and `qml.jacobian` now explicitly raise errors if trainable parameters are integers.
diff --git a/pennylane/transforms/decompositions/single_qubit_unitary.py b/pennylane/transforms/decompositions/single_qubit_unitary.py
index ef420acf1..7497e1570 100644
--- a/pennylane/transforms/decompositions/single_qubit_unitary.py
+++ b/pennylane/transforms/decompositions/single_qubit_unitary.py
@@ -26,29 +26,73 @@ def _convert_to_su2(U, return_global_phase=False):
 
     Args:
         U (array[complex]): A matrix with a batch dimension, presumed to be
-        of shape :math:`n \times 2 \times 2` and unitary for any positive integer n.
-        return_global_phase (bool): If `True`, the return will include
-        the global phase. If `False`, only the :math:`SU(2)` representative
-        is returned.
+            of shape :math:`n \times 2 \times 2` and unitary for any positive integer n.
+        return_global_phase (bool): If `True`, the return will include the global phase.
+            If `False`, only the :math:`SU(2)` representation is returned.
 
     Returns:
         array[complex]: A :math:`n \times 2 \times 2` matrix in :math:`SU(2)` that is
-        equivalent to U up to a global phase. If ``return_global_phase=True``,
-        a 2-element tuple is returned, with the first element being the
-        :math:`SU(2)` equivalent and the second, the global phase.
+            equivalent to U up to a global phase. If ``return_global_phase=True``, a
+            2-element tuple is returned, with the first element being the :math:`SU(2)`
+            equivalent and the second, the global phase.
     """
+
     # Compute the determinants
     U = qml.math.cast(U, "complex128")
-    dets = math.linalg.det(U)
-    exp_angles = math.angle(dets) / 2
-    U_SU2 = math.cast_like(U, dets) * math.exp(-1j * math.cast_like(exp_angles, 1j))[:, None, None]
+    determinants = math.linalg.det(U)
+    phase = math.angle(determinants) / 2
+    U = math.cast_like(U, determinants) * math.exp(-1j * math.cast_like(phase, 1j))[:, None, None]
+
+    return (U, phase) if return_global_phase else U
+
+
+def _zyz_get_rotation_angles(U):
+    r"""Computes the rotation angles :math:`\phi`, :math:`\theta`, :math:`\omega`
+    for a unitary :math:`U` that is :math:`SU(2)`
 
-    return (U_SU2, exp_angles) if return_global_phase else U_SU2
+    Args:
+        U (array[complex]): A matrix that is :math:`SU(2)`
+
+    Returns:
+        tuple[array[float]]: A tuple containing the rotation angles
+            :math:`\phi`, :math:`\theta`, :math:`\omega`
 
+    """
+
+    # For batched U or single U with non-zero off-diagonal, compute the
+    # normal decomposition instead
+    off_diagonal_elements = math.clip(math.abs(U[:, 0, 1]), 0, 1)
+    thetas = 2 * math.arcsin(off_diagonal_elements)
 
-def _rot_decomposition(U, wire):
-    r"""Recover the decomposition of a single-qubit matrix :math:`U` in terms of
-    elementary operations.
+    # Compute phi and omega from the angles of the top row; use atan2 to keep
+    # the angle within -np.pi and np.pi, and add very small value to the real
+    # part to avoid division by zero.
+    epsilon = 1e-64
+    angles_U00 = math.arctan2(
+        math.imag(U[:, 0, 0]),
+        math.real(U[:, 0, 0]) + epsilon,
+    )
+    angles_U10 = math.arctan2(
+        math.imag(U[:, 1, 0]),
+        math.real(U[:, 1, 0]) + epsilon,
+    )
+
+    phis = -angles_U10 - angles_U00
+    omegas = angles_U10 - angles_U00
+
+    phis, thetas, omegas = map(math.squeeze, [phis, thetas, omegas])
+
+    # Normalize the angles
+    phis = phis % (4 * np.pi)
+    thetas = thetas % (4 * np.pi)
+    omegas = omegas % (4 * np.pi)
+
+    return phis, thetas, omegas
+
+
+def _rot_decomposition(U, wire, return_global_phase=False):
+    r"""Compute the decomposition of a single-qubit matrix :math:`U` in terms of
+    elementary operations, as a single :class:`.RZ` gate or a :class:`.Rot` gate.
 
     Diagonal operations can be converted to a single :class:`.RZ` gate, while non-diagonal
     operations will be converted to a :class:`.Rot` gate that implements the original operation
@@ -62,66 +106,56 @@ def _rot_decomposition(U, wire):
     Args:
         U (tensor): A 2 x 2 unitary matrix.
         wire (Union[Wires, Sequence[int] or int]): The wire on which to apply the operation.
+        return_global_phase (bool): Whether to return the global phase ``qml.GlobalPhase(-alpha)``
+            as the last element of the returned list of operations.
 
     Returns:
         list[qml.Operation]: A ``Rot`` gate on the specified wire that implements ``U``
-        up to a global phase, or an equivalent ``RZ`` gate if ``U`` is diagonal.
+            up to a global phase, or an equivalent ``RZ`` gate if ``U`` is diagonal. If
+            `return_global_phase=True`, the global phase is included as the last element.
 
     **Example**
 
     Suppose we would like to apply the following unitary operation:
 
-    .. code-block:: python3
-
-        U = np.array([
-            [-0.28829348-0.78829734j,  0.30364367+0.45085995j],
-            [ 0.53396245-0.10177564j,  0.76279558-0.35024096j]
-        ])
+    >>> U = np.array([
+    ...     [-0.28829348-0.78829734j, 0.30364367+0.45085995j],
+    ...     [ 0.53396245-0.10177564j, 0.76279558-0.35024096j]
+    ... ])
 
     For PennyLane devices that cannot natively implement ``QubitUnitary``, we
     can instead recover a ``Rot`` gate that implements the same operation, up
     to a global phase:
 
-    >>> decomp = _rot_decomposition(U, 0)
-    >>> decomp
-    [Rot(-0.24209529417800013, 1.14938178234275, 1.7330581433950871, wires=[0])]
+    >>> decompositions = _rot_decomposition(U, 0)
+    >>> decompositions
+    [Rot(12.32427531154459, 1.1493817771511354, 1.733058145303424, wires=[0])]
     """
 
     # Cast to batched format for more consistent code
     U = math.expand_dims(U, axis=0) if len(U.shape) == 2 else U
 
-    U = _convert_to_su2(U)
+    # Convert to SU(2) format and extract global phase
+    U_det1, alphas = _convert_to_su2(U, return_global_phase=True)
 
     # If U is only one unitary and its value is not abstract, we can include a conditional
     # statement that will check if the off-diagonal elements are 0; if so, just use one RZ
-    if len(U) == 1 and not math.is_abstract(U[0]):
-        if math.allclose(U[0, 0, 1], 0.0):
-            return [qml.RZ(2 * math.angle(U[0, 1, 1]), wires=wire)]
-
-    # For batched U or single U with non-zero off-diagonal, compute the
-    # Rot operator decomposition instead
-    off_diagonal_elements = math.clip(math.abs(U[:, 0, 1]), 0, 1)
-    thetas = 2 * math.arcsin(off_diagonal_elements)
-
-    # Compute phi and omega from the angles of the top row; use atan2 to keep
-    # the angle within -np.pi and np.pi, and add very small value to the real
-    # part to avoid division by zero.
-    epsilon = 1e-64
-    angles_U00 = math.arctan2(
-        math.imag(U[:, 0, 0]),
-        math.real(U[:, 0, 0]) + epsilon,
-    )
-    angles_U10 = math.arctan2(
-        math.imag(U[:, 1, 0]),
-        math.real(U[:, 1, 0]) + epsilon,
-    )
-
-    phis = -angles_U10 - angles_U00
-    omegas = angles_U10 - angles_U00
-
-    phis, thetas, omegas = map(math.squeeze, [phis, thetas, omegas])
-
-    return [qml.Rot(phis, thetas, omegas, wires=wire)]
+    if len(U_det1) == 1 and not math.is_abstract(U_det1[0]):
+        if math.allclose(U_det1[0, 0, 1], 0.0):
+            angle = 2 * math.angle(U_det1[0, 1, 1]) % (4 * np.pi)
+            operations = [qml.RZ(angle, wires=wire)]
+            if return_global_phase:
+                operations.append(qml.GlobalPhase(-alphas))
+            return operations
+
+    # Compute the zyz rotation angles
+    phis, thetas, omegas = _zyz_get_rotation_angles(U_det1)
+
+    operations = [qml.Rot(phis, thetas, omegas, wires=wire)]
+    if return_global_phase:
+        alphas = math.squeeze(alphas)
+        operations.append(qml.GlobalPhase(-alphas))
+    return operations
 
 
 def _zyz_decomposition(U, wire, return_global_phase=False):
@@ -141,57 +175,38 @@ def _zyz_decomposition(U, wire, return_global_phase=False):
             as the last element of the returned list of operations.
 
     Returns:
-        list[Operation]: Returns a list of gates, an ``RZ``, an ``RY`` and
-        another ``RZ`` gate, which when applied in the order of appearance in the list is
-        equivalent to the unitary :math:`U` up to a global phase. If `return_global_phase=True`,
-        the global phase is returned as the last element of the list.
+        list[Operation]: Returns a list of gates, an ``RZ``, an ``RY`` and another ``RZ`` gate,
+            which when applied in the order of appearance in the list is equivalent to the
+            unitary :math:`U` up to a global phase. If `return_global_phase=True`, the global
+            phase is returned as the last element of the list.
 
     **Example**
 
-    >>> U = np.array([[-0.28829348-0.78829734j,  0.30364367+0.45085995j],
-    ...               [ 0.53396245-0.10177564j,  0.76279558-0.35024096j]])
-    >>> decomp = _zyz_decomposition(U, 0, return_global_phase=True)
-    >>> decomp
+    >>> U = np.array([
+    ...     [-0.28829348-0.78829734j, 0.30364367+0.45085995j],
+    ...     [ 0.53396245-0.10177564j, 0.76279558-0.35024096j]
+    ... ])
+    >>> decompositions = _zyz_decomposition(U, 0, return_global_phase=True)
+    >>> decompositions
     [RZ(12.32427531154459, wires=[0]),
-     RY(1.1493817771511352, wires=[0]),
+     RY(1.1493817771511354, wires=[0]),
      RZ(1.733058145303424, wires=[0]),
-     GlobalPhase((1.1759220332464762-0j), wires=[0])]
+     GlobalPhase(1.1759220332464762, wires=[])]
+
     """
 
     # Cast to batched format for more consistent code
     U = math.expand_dims(U, axis=0) if len(U.shape) == 2 else U
 
+    # Convert to SU(2) format and extract global phase
     U_det1, alphas = _convert_to_su2(U, return_global_phase=True)
 
-    # For batched U or single U with non-zero off-diagonal, compute the
-    # normal decomposition instead
-    off_diagonal_elements = math.clip(math.abs(U_det1[:, 0, 1]), 0, 1)
-    thetas = 2 * math.arcsin(off_diagonal_elements)
-
-    # Compute phi and omega from the angles of the top row; use atan2 to keep
-    # the angle within -np.pi and np.pi, and add very small value to the real
-    # part to avoid division by zero.
-    epsilon = 1e-64
-    angles_U00 = math.arctan2(
-        math.imag(U_det1[:, 0, 0]),
-        math.real(U_det1[:, 0, 0]) + epsilon,
-    )
-    angles_U10 = math.arctan2(
-        math.imag(U_det1[:, 1, 0]),
-        math.real(U_det1[:, 1, 0]) + epsilon,
-    )
-
-    phis = -angles_U10 - angles_U00
-    omegas = angles_U10 - angles_U00
-
-    phis, thetas, omegas, alphas = map(math.squeeze, [phis, thetas, omegas, alphas])
-
-    phis = phis % (4 * np.pi)
-    thetas = thetas % (4 * np.pi)
-    omegas = omegas % (4 * np.pi)
+    # Compute the zyz rotation angles
+    phis, thetas, omegas = _zyz_get_rotation_angles(U_det1)
 
     operations = [qml.RZ(phis, wire), qml.RY(thetas, wire), qml.RZ(omegas, wire)]
     if return_global_phase:
+        alphas = math.squeeze(alphas)
         operations.append(qml.GlobalPhase(-alphas))
 
     return operations
@@ -209,21 +224,23 @@ def _xyx_decomposition(U, wire, return_global_phase=False):
             as the last element of the returned list of operations.
 
     Returns:
-        list[Operation]: Returns a list of of gates, an ``RX``, an ``RY`` and
-        another ``RX`` gate, which when applied in the order of appearance in the list is equivalent
-        to the unitary :math:`U` up to global phase. If `return_global_phase=True`,
-        the global phase is returned as the last element of the list.
+        list[Operation]: Returns a list of gates, an ``RX``, an ``RY`` and another ``RX`` gate,
+            which when applied in the order of appearance in the list is equivalent to the unitary
+            :math:`U` up to global phase. If `return_global_phase=True`, the global phase is returned
+            as the last element of the list.
 
     **Example**
 
-    >>> U = np.array([[-0.28829348-0.78829734j,  0.30364367+0.45085995j],
-    ...               [ 0.53396245-0.10177564j,  0.76279558-0.35024096j]])
-    >>> decomp = _xyx_decomposition(U, 0, return_global_phase=True)
-    >>> decomp
+    >>> U = np.array([
+    ...     [-0.28829348-0.78829734j, 0.30364367+0.45085995j],
+    ...     [ 0.53396245-0.10177564j, 0.76279558-0.35024096j]
+    ... ])
+    >>> decompositions = _xyx_decomposition(U, 0, return_global_phase=True)
+    >>> decompositions
     [RX(10.845351366405708, wires=[0]),
-     RY(1.3974974118006174, wires=[0]),
+     RY(1.3974974118006183, wires=[0]),
      RX(0.45246583660683803, wires=[0]),
-     GlobalPhase((1.1759220332464762-0j), wires=[0])]
+     GlobalPhase(1.1759220332464762, wires=[])]
     """
 
     # Small number to add to denominators to avoid division by zero
@@ -280,16 +297,16 @@ def _xzx_decomposition(U, wire, return_global_phase=False):
 
     **Example**
 
-    >>> U = np.array(
-    ...     [[-0.28829348-0.78829734j,  0.30364367+0.45085995j],
-    ...      [ 0.53396245-0.10177564j,  0.76279558-0.35024096j]]
-    ... )
+    >>> U = np.array([
+    ...     [-0.28829348-0.78829734j, 0.30364367+0.45085995j],
+    ...     [ 0.53396245-0.10177564j, 0.76279558-0.35024096j]
+    ... ])
     >>> decompositions = _xzx_decomposition(U, 0, return_global_phase=True)
     >>> decompositions
     [RX(12.416147693665032, wires=[0]),
      RZ(1.3974974090935608, wires=[0]),
      RX(11.448040119199066, wires=[0]),
-     GlobalPhase((1.1759220332464762-0j), wires=[0])]
+     GlobalPhase(1.1759220332464762, wires=[])]
 
     """
 
@@ -298,7 +315,7 @@ def _xzx_decomposition(U, wire, return_global_phase=False):
 
     # Choose gamma such that exp(-i*gamma)*U is special unitary (detU==1).
     U = math.expand_dims(U, axis=0) if len(U.shape) == 2 else U
-    U_det1, gammas = _convert_to_su2(U, return_global_phase=return_global_phase)
+    U_det1, gammas = _convert_to_su2(U, return_global_phase=True)
 
     # Compute \phi, \theta and \lambda after analytically solving for them from
     # U_det1 = RX(\phi) RZ(\theta) RX(\lambda)
@@ -344,20 +361,23 @@ def _zxz_decomposition(U, wire, return_global_phase=False):
 
     Returns:
         list[Operation]: Returns a list of gates, an ``RZ``, an ``RX`` and
-        another ``RZ`` gate, which when applied in the order of appearance in the list is
-        equivalent to the unitary :math:`U` up to a global phase. If `return_global_phase=True`,
-        the global phase is returned as the last element of the list.
+            another ``RZ`` gate, which when applied in the order of appearance in the list is
+            equivalent to the unitary :math:`U` up to a global phase. If `return_global_phase=True`,
+            the global phase is returned as the last element of the list.
 
     **Example**
 
-    >>> U = np.array([[-0.28829348-0.78829734j,  0.30364367+0.45085995j],
-    ...               [ 0.53396245-0.10177564j,  0.76279558-0.35024096j]])
-    >>> decomp = _zxz_decomposition(U, 0, return_global_phase=True)
-    >>> decomp
-        [RZ(10.753478981934784, wires=[0]),
-         RX(1.1493817777940705, wires=[0]),
-         RZ(3.3038544749132295, wires=[0]),
-         GlobalPhase((1.1759220332464762-0j), wires=[0])]
+    >>> U = np.array([
+    ...     [-0.28829348-0.78829734j, 0.30364367+0.45085995j],
+    ...     [ 0.53396245-0.10177564j, 0.76279558-0.35024096j]
+    ... ])
+    >>> decompositions = _zxz_decomposition(U, 0, return_global_phase=True)
+    >>> decompositions
+    [RZ(10.753478981934784, wires=[0]),
+     RX(1.1493817777940707, wires=[0]),
+     RZ(3.3038544749132295, wires=[0]),
+     GlobalPhase(1.1759220332464762, wires=[])]
+
     """
 
     # Small number to add to denominators to avoid division by zero
@@ -405,7 +425,7 @@ def one_qubit_decomposition(U, wire, rotations="ZYZ", return_global_phase=False)
     Any one qubit unitary operation can be implemented up to a global phase by composing RX, RY,
     and RZ gates.
 
-    Currently supported values for ``rotations`` are "ZYZ", "XYX", "XZX", and "ZXZ".
+    Currently supported values for ``rotations`` are "rot", "ZYZ", "XYX", "XZX", and "ZXZ".
 
     Args:
         U (tensor): A :math:`2 \times 2` unitary matrix.
@@ -416,20 +436,23 @@ def one_qubit_decomposition(U, wire, rotations="ZYZ", return_global_phase=False)
 
     Returns:
         list[Operation]: Returns a list of gates which when applied in the order of appearance in
-        the list is equivalent to the unitary :math:`U` up to a global phase. If ``return_global_phase=True``,
-        the global phase is returned as the last element of the list.
+            the list is equivalent to the unitary :math:`U` up to a global phase. If
+            ``return_global_phase=True``, the global phase is returned as the last element of the list.
 
     **Example**
 
-    >>> U = np.array([[-0.28829348-0.78829734j,  0.30364367+0.45085995j],
-    ...               [ 0.53396245-0.10177564j,  0.76279558-0.35024096j]])
-    >>> decomp = one_qubit_decomposition(U, 0, "ZXZ", return_global_phase=True)
-    >>> decomp
-        [RZ(10.753478981934784, wires=[0]),
-         RX(1.1493817777940705, wires=[0]),
-         RZ(3.3038544749132295, wires=[0]),
-        (0.38469215914523336-0.9230449299422961j)*(Identity(wires=[0]))]
+    >>> U = np.array([
+    ...     [-0.28829348-0.78829734j, 0.30364367+0.45085995j],
+    ...     [ 0.53396245-0.10177564j, 0.76279558-0.35024096j]
+    ... ])
+    >>> decompositions = one_qubit_decomposition(U, 0, "ZXZ", return_global_phase=True)
+    >>> decompositions
+    [RZ(10.753478981934784, wires=[0]),
+     RX(1.1493817777940707, wires=[0]),
+     RZ(3.3038544749132295, wires=[0]),
+     GlobalPhase(1.1759220332464762, wires=[])]
     """
+
     supported_rotations = {
         "rot": _rot_decomposition,
         "ZYZ": _zyz_decomposition,
@@ -439,8 +462,6 @@ def one_qubit_decomposition(U, wire, rotations="ZYZ", return_global_phase=False)
     }
 
     if rotations in supported_rotations:
-        if rotations == "rot":
-            return supported_rotations[rotations](U, wire)
         return supported_rotations[rotations](U, wire, return_global_phase)
 
     raise ValueError(


Test patch:
diff --git a/tests/devices/default_qubit/test_default_qubit_preprocessing.py b/tests/devices/default_qubit/test_default_qubit_preprocessing.py
index 2a6cde2fe..4cbfcd470 100644
--- a/tests/devices/default_qubit/test_default_qubit_preprocessing.py
+++ b/tests/devices/default_qubit/test_default_qubit_preprocessing.py
@@ -678,6 +678,7 @@ class TestAdjointDiffTapeValidation:
 
     def test_trainable_params_decomposed(self):
         """Test that the trainable parameters of a tape are updated when it is expanded"""
+
         ops = [
             qml.QubitUnitary([[0, 1], [1, 0]], wires=0),
             qml.CNOT([0, 1]),
diff --git a/tests/ops/qubit/test_matrix_ops.py b/tests/ops/qubit/test_matrix_ops.py
index 5fe6bc5f7..d4915c22a 100644
--- a/tests/ops/qubit/test_matrix_ops.py
+++ b/tests/ops/qubit/test_matrix_ops.py
@@ -33,6 +33,7 @@ class TestQubitUnitary:
 
     def test_qubit_unitary_noninteger_pow(self):
         """Test QubitUnitary raised to a non-integer power raises an error."""
+
         U = np.array(
             [[0.98877108 + 0.0j, 0.0 - 0.14943813j], [0.0 - 0.14943813j, 0.98877108 + 0.0j]]
         )
@@ -45,6 +46,7 @@ class TestQubitUnitary:
 
     def test_qubit_unitary_noninteger_pow_broadcasted(self):
         """Test broadcasted QubitUnitary raised to a non-integer power raises an error."""
+
         U = np.array(
             [
                 [[0.98877108 + 0.0j, 0.0 - 0.14943813j], [0.0 - 0.14943813j, 0.98877108 + 0.0j]],
@@ -60,6 +62,7 @@ class TestQubitUnitary:
     @pytest.mark.parametrize("n", (1, 3, -1, -3))
     def test_qubit_unitary_pow(self, n):
         """Test qubit unitary raised to an integer power."""
+
         U = np.array(
             [[0.98877108 + 0.0j, 0.0 - 0.14943813j], [0.0 - 0.14943813j, 0.98877108 + 0.0j]]
         )
@@ -78,6 +81,7 @@ class TestQubitUnitary:
     @pytest.mark.parametrize("n", (1, 3, -1, -3))
     def test_qubit_unitary_pow_broadcasted(self, n):
         """Test broadcasted qubit unitary raised to an integer power."""
+
         U = np.array(
             [
                 [[0.98877108 + 0.0j, 0.0 - 0.14943813j], [0.0 - 0.14943813j, 0.98877108 + 0.0j]],
@@ -133,6 +137,7 @@ class TestQubitUnitary:
     def test_qubit_unitary_torch(self, U, num_wires):
         """Test that the unitary operator produces the correct output and
         catches incorrect input with torch."""
+
         import torch
 
         U = torch.tensor(U)
@@ -165,6 +170,7 @@ class TestQubitUnitary:
     def test_qubit_unitary_tf(self, U, num_wires):
         """Test that the unitary operator produces the correct output and
         catches incorrect input with tensorflow."""
+
         import tensorflow as tf
 
         U = tf.Variable(U)
@@ -192,6 +198,7 @@ class TestQubitUnitary:
     @pytest.mark.tf
     def test_qubit_unitary_int_pow_tf(self):
         """Test that QubitUnitary.pow works with tf and int z values."""
+
         import tensorflow as tf
 
         mat = tf.Variable([[1, 0], [0, tf.exp(1j)]])
@@ -206,6 +213,7 @@ class TestQubitUnitary:
     def test_qubit_unitary_jax(self, U, num_wires):
         """Test that the unitary operator produces the correct output and
         catches incorrect input with jax."""
+
         from jax import numpy as jnp
 
         U = jnp.array(U)
@@ -236,6 +244,7 @@ class TestQubitUnitary:
     )
     def test_qubit_unitary_jax_jit(self, U, num_wires):
         """Tests that QubitUnitary works with jitting."""
+
         import jax
         from jax import numpy as jnp
 
@@ -258,7 +267,7 @@ class TestQubitUnitary:
             (
                 qml.matrix(qml.RZ(-0.5, wires=0)),
                 (qml.RZ, qml.RY, qml.RZ),
-                [12.316370614359172, 0.0, 12.316370614359172],
+                [4 * np.pi - 0.25, 0.0, 4 * np.pi - 0.25],
             ),
             (
                 np.array(
@@ -271,16 +280,16 @@ class TestQubitUnitary:
                 [12.382273469673908, np.pi, 0.18409714468526372],
             ),
             (H, (qml.RZ, qml.RY, qml.RZ), [np.pi, np.pi / 2, 0.0]),
-            (X, (qml.RZ, qml.RY, qml.RZ), [np.pi / 2, np.pi, 10.995574287564276]),
+            (X, (qml.RZ, qml.RY, qml.RZ), [np.pi / 2, np.pi, 7 * np.pi / 2]),
             (
                 qml.matrix(qml.Rot(0.2, 0.5, -0.3, wires=0)),
                 (qml.RZ, qml.RY, qml.RZ),
-                [0.2, 0.5, 12.266370614359172],
+                [0.2, 0.5, 4 * np.pi - 0.3],
             ),
             (
                 np.exp(1j * 0.02) * qml.matrix(qml.Rot(-1.0, 2.0, -3.0, wires=0)),
                 (qml.RZ, qml.RY, qml.RZ),
-                [11.566370614359172, 2.0, 9.566370614359172],
+                [4 * np.pi - 1.0, 2.0, 4 * np.pi - 3.0],
             ),
             # An instance of a broadcast unitary
             (
@@ -295,6 +304,7 @@ class TestQubitUnitary:
     )
     def test_qubit_unitary_decomposition(self, U, expected_gates, expected_params):
         """Tests that single-qubit QubitUnitary decompositions are performed."""
+
         decomp = qml.QubitUnitary.compute_decomposition(U, wires=0)
         decomp2 = qml.QubitUnitary(U, wires=0).decomposition()
 
diff --git a/tests/transforms/test_decompositions.py b/tests/transforms/test_decompositions.py
index 45965047e..36a7a1afa 100644
--- a/tests/transforms/test_decompositions.py
+++ b/tests/transforms/test_decompositions.py
@@ -38,25 +38,22 @@ from pennylane.transforms.decompositions.two_qubit_unitary import (
 def _run_assertions(U, expected_gates, expected_params, obtained_gates):
     """Runs assertions for unitary decomposition transforms."""
 
-    assert len(obtained_gates) == 4, "Incorrect number of gates"
-
-    # Check initial 3 gates
-    for i in range(3):
-        assert isinstance(obtained_gates[i], expected_gates[i]), "Incorrect type of gate"
-        assert obtained_gates[i].wires == Wires("a"), "Incorrect wire"
-
-    # Check the global phase
-    assert isinstance(obtained_gates[3], expected_gates[3]), "Incorrect type of gate"
-    assert qml.math.allclose(
-        qml.math.unwrap(obtained_gates[3].parameters[:1]), expected_params[3]
-    ), "Incorrect global phase"
-
-    # Now we check the rotation angles
-    assert qml.math.allclose(
-        [qml.math.unwrap(o.parameters)[0] for o in obtained_gates[:3]],
-        expected_params[:3],
-        atol=1e-7,
-    ), "Incorrect rotation angles"
+    assert len(obtained_gates) == len(expected_gates), "Incorrect number of gates"
+
+    for i, gate in enumerate(obtained_gates):
+        # Check type of gate
+        assert isinstance(gate, expected_gates[i]), "Incorrect type of gate"
+
+        # Check the wire that the gates act on
+        if not isinstance(gate, qml.GlobalPhase):
+            assert gate.wires == Wires("a"), "Incorrect wire"
+
+        # Check gate parameters
+        assert qml.math.allclose(
+            qml.math.unwrap(gate.parameters),
+            expected_params[i],
+            atol=1e-7,
+        ), "Incorrect gate parameters"
 
     obtained_mat = reduce(
         np.matmul, [op.matrix(wire_order=["a"]) for op in reversed(obtained_gates)]
@@ -72,31 +69,32 @@ def _run_assertions(U, expected_gates, expected_params, obtained_gates):
     )
 
 
+def _test_decomposition(U, rotation_type, expected_gates, expected_params):
+    """Tests the unitary decomposition transforms"""
+
+    obtained_gates = one_qubit_decomposition(U, Wires("a"), rotation_type, return_global_phase=True)
+    _run_assertions(U, expected_gates, expected_params, obtained_gates)
+
+    obtained_gates = one_qubit_decomposition(
+        U, Wires("a"), rotation_type, return_global_phase=False
+    )
+    _run_assertions(U, expected_gates[:-1], expected_params[:-1], obtained_gates)
+
+
 typeof_gates_zyz = (qml.RZ, qml.RY, qml.RZ, qml.GlobalPhase)
-single_qubit_decomps_zyz = [
-    (I, typeof_gates_zyz, [0.0, 0.0, 0.0, 0]),
-    (Z, typeof_gates_zyz, [np.pi / 2, 0.0, np.pi / 2, -1.5707963267948966]),
-    (
-        S,
-        typeof_gates_zyz,
-        [np.pi / 4, 0.0, np.pi / 4, -0.7853981633974483],
-    ),
-    (
-        T,
-        typeof_gates_zyz,
-        [np.pi / 8, 0.0, np.pi / 8, -0.39269908047469393],
-    ),
-    (qml.RZ(0.3, wires=0).matrix(), typeof_gates_zyz, [0.15, 0.0, 0.15, 0]),
-    (
-        qml.RZ(-0.5, wires=0).matrix(),
-        typeof_gates_zyz,
-        [12.316370614359172, 0.0, 12.316370614359172, 0],
-    ),
-    (
-        qml.Rot(0.2, 0.5, -0.3, wires=0).matrix(),
-        typeof_gates_zyz,
-        [0.2, 0.5, 12.266370614359172, 0],
-    ),
+test_cases_zyz = [
+    # Special unitaries
+    (I, [0.0, 0.0, 0.0, 0]),
+    (Z, [np.pi / 2, 0.0, np.pi / 2, -np.pi / 2]),
+    (S, [np.pi / 4, 0.0, np.pi / 4, -np.pi / 4]),
+    (T, [np.pi / 8, 0.0, np.pi / 8, -np.pi / 8]),
+    (H, [np.pi, np.pi / 2, 0.0, -np.pi / 2]),
+    (X, [np.pi / 2, np.pi, 7 * np.pi / 2, -np.pi / 2]),
+    # Single rotations
+    (qml.RZ(0.3, wires=0).matrix(), [0.15, 0.0, 0.15, 0]),
+    (qml.RZ(-0.5, wires=0).matrix(), [4 * np.pi - 0.25, 0.0, 4 * np.pi - 0.25, 0]),
+    (qml.Rot(0.2, 0.5, -0.3, wires=0).matrix(), [0.2, 0.5, 4 * np.pi - 0.3, 0]),
+    # Other random unitaries
     (
         np.array(
             [
@@ -104,30 +102,19 @@ single_qubit_decomps_zyz = [
                 [9.831019270939975e-01 + 0.1830590094588862j, 0],
             ]
         ),
-        typeof_gates_zyz,
         [12.382273469673908, np.pi, 0.18409714468526372, 0],
     ),
-    (H, typeof_gates_zyz, [np.pi, np.pi / 2, 0.0, -1.5707963267948966]),
-    (X, typeof_gates_zyz, [np.pi / 2, np.pi, 10.995574287564276, -1.5707963267948966]),
     (
         np.exp(1j * 0.02) * qml.Rot(-1.0, 2.0, -3.0, wires=0).matrix(),
-        typeof_gates_zyz,
-        [
-            11.566370614359172,
-            2.0,
-            9.566370614359172,
-            -0.020000000000000042,
-        ],
+        [4 * np.pi - 1.0, 2.0, 4 * np.pi - 3.0, -0.02],
     ),
-    # Add two instances of broadcasted unitaries, one coming from RZ and another from Rot
+    # Broadcasted unitaries, one coming from RZ and another from Rot
     (
         qml.QubitUnitary(qml.RZ.compute_matrix(np.array([np.pi, np.pi / 2])), wires=0).matrix(),
-        typeof_gates_zyz,
         [[np.pi / 2, np.pi / 4], [0.0, 0.0], [np.pi / 2, np.pi / 4], [0, 0]],
     ),
     (
         qml.Rot(np.array([1.2, 2.3]), np.array([1.2, 2.3]), np.array([1.2, 2.3]), wires=0).matrix(),
-        typeof_gates_zyz,
         [[1.2, 2.3], [1.2, 2.3], [1.2, 2.3], [0, 0]],
     ),
 ]
@@ -136,38 +123,35 @@ single_qubit_decomps_zyz = [
 class TestQubitUnitaryZYZDecomposition:
     """Test that the ZYZ decomposition is correct."""
 
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zyz)
-    def test_zyz_decomposition(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zyz)
+    def test_zyz_decomposition(self, U, expected_params):
         """Test that a one-qubit matrix in isolation is correctly decomposed."""
 
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZYZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZYZ", typeof_gates_zyz, expected_params)
 
     @pytest.mark.torch
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zyz)
-    def test_zyz_decomposition_torch(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zyz)
+    def test_zyz_decomposition_torch(self, U, expected_params):
         """Test that a one-qubit operation in Torch is correctly decomposed."""
 
         import torch
 
         U = torch.tensor(U, dtype=torch.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZYZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZYZ", typeof_gates_zyz, expected_params)
 
     @pytest.mark.tf
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zyz)
-    def test_zyz_decomposition_tf(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zyz)
+    def test_zyz_decomposition_tf(self, U, expected_params):
         """Test that a one-qubit operation in Tensorflow is correctly decomposed."""
 
         import tensorflow as tf
 
         U = tf.Variable(U, dtype=tf.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZYZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZYZ", typeof_gates_zyz, expected_params)
 
     @pytest.mark.jax
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zyz)
-    def test_zyz_decomposition_jax(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zyz)
+    def test_zyz_decomposition_jax(self, U, expected_params):
         """Test that a one-qubit operation in JAX is correctly decomposed."""
 
         import jax
@@ -179,15 +163,14 @@ class TestQubitUnitaryZYZDecomposition:
         config.update("jax_enable_x64", True)
 
         U = jax.numpy.array(U, dtype=jax.numpy.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZYZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZYZ", typeof_gates_zyz, expected_params)
 
         # Reset the configuration
         config.update("jax_enable_x64", remember)
 
 
 typeof_gates_xyx = (qml.RX, qml.RY, qml.RX, qml.GlobalPhase)
-single_qubit_decomps_xyx = [
+test_cases_xyx = [
     # Try a random dense unitary
     (
         np.array(
@@ -196,29 +179,21 @@ single_qubit_decomps_xyx = [
                 [0.53396245 - 0.10177564j, 0.76279558 - 0.35024096j],
             ]
         ),
-        typeof_gates_xyx,
-        (
-            10.845351366405708,
-            1.3974974118006183,
-            0.45246583660683803,
-            1.1759220332464762,
-        ),
+        [10.845351366405708, 1.3974974118006183, 0.45246583660683803, 1.1759220332464762],
     ),
     # Try a few specific special unitaries
-    (I, typeof_gates_xyx, [0, 0, 0, 0]),  # This triggers the if conditional trivially
-    (X, typeof_gates_xyx, [4.71238898038469, 0.0, 10.995574287564276, -1.5707963267948966]),
-    (Y, typeof_gates_xyx, [1 / 2 * np.pi, np.pi, 1 / 2 * np.pi, -1.5707963267948966]),
-    (Z, typeof_gates_xyx, [10.995574287564276, np.pi, 1 / 2 * np.pi, -1.5707963267948966]),
+    (I, [0, 0, 0, 0]),  # This triggers the if-conditional trivially
+    (X, [np.pi * 3 / 2, 0.0, 7 * np.pi / 2, -np.pi / 2]),
+    (Y, [np.pi / 2, np.pi, np.pi / 2, -np.pi / 2]),
+    (Z, [7 * np.pi / 2, np.pi, np.pi / 2, -np.pi / 2]),
     # Add two instances of broadcasted unitaries, one coming from RZ and another from Rot
     (
         qml.QubitUnitary(qml.RZ.compute_matrix(np.array([np.pi, np.pi / 2])), wires=0).matrix(),
-        typeof_gates_xyx,
-        [[10.99557429, 10.99557429], [np.pi, np.pi / 2], [np.pi / 2, np.pi / 2], [0, 0]],
+        [[7 * np.pi / 2, 7 * np.pi / 2], [np.pi, np.pi / 2], [np.pi / 2, np.pi / 2], [0, 0]],
     ),
     (
-        # This triggers the if conditional non-trivially
+        # This triggers the if-conditional non-trivially
         qml.Rot(np.array([1.2, 1.5]), np.array([1.2, 1.5]), np.array([1.2, 1.5]), wires=0).matrix(),
-        typeof_gates_xyx,
         [
             [11.62877054, 11.74682533],
             [2.53416365, 3.03803113],
@@ -232,38 +207,35 @@ single_qubit_decomps_xyx = [
 class TestQubitUnitaryXYXDecomposition:
     """Test that the XYX decomposition is correct."""
 
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xyx)
-    def test_xyx_decomposition(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xyx)
+    def test_xyx_decomposition(self, U, expected_params):
         """Test that a one-qubit matrix in isolation is correctly decomposed."""
 
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XYX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XYX", typeof_gates_xyx, expected_params)
 
     @pytest.mark.torch
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xyx)
-    def test_xyx_decomposition_torch(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xyx)
+    def test_xyx_decomposition_torch(self, U, expected_params):
         """Test that a one-qubit operation in Torch is correctly decomposed."""
 
         import torch
 
         U = torch.tensor(U, dtype=torch.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XYX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XYX", typeof_gates_xyx, expected_params)
 
     @pytest.mark.tf
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xyx)
-    def test_xyx_decomposition_tf(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xyx)
+    def test_xyx_decomposition_tf(self, U, expected_params):
         """Test that a one-qubit operation in Tensorflow is correctly decomposed."""
 
         import tensorflow as tf
 
         U = tf.Variable(U, dtype=tf.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XYX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XYX", typeof_gates_xyx, expected_params)
 
     @pytest.mark.jax
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xyx)
-    def test_xyx_decomposition_jax(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xyx)
+    def test_xyx_decomposition_jax(self, U, expected_params):
         """Test that a one-qubit operation in JAX is correctly decomposed."""
 
         import jax
@@ -275,15 +247,14 @@ class TestQubitUnitaryXYXDecomposition:
         config.update("jax_enable_x64", True)
 
         U = jax.numpy.array(U, dtype=jax.numpy.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XYX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XYX", typeof_gates_xyx, expected_params)
 
         # Reset the configuration
         config.update("jax_enable_x64", remember)
 
 
 typeof_gates_xzx = (qml.RX, qml.RZ, qml.RX, qml.GlobalPhase)
-single_qubit_decomps_xzx = [
+test_cases_xzx = [
     (
         np.array(
             [
@@ -291,22 +262,19 @@ single_qubit_decomps_xzx = [
                 [0.53396245 - 0.10177564j, 0.76279558 - 0.35024096j],
             ]
         ),
-        typeof_gates_xzx,
         [12.416147693665032, 1.3974974090935608, 11.448040119199066, 1.1759220332464762],
     ),
-    (I, typeof_gates_xzx, [0, 0, 0, 0]),
-    (X, typeof_gates_xzx, [np.pi / 2, 0, np.pi / 2, -np.pi / 2]),
-    (Y, typeof_gates_xzx, [np.pi / 2, np.pi, 7 / 2 * np.pi, -np.pi / 2]),
-    (Z, typeof_gates_xzx, [0, np.pi, 0, -np.pi / 2]),
-    (H, typeof_gates_xzx, [np.pi / 2, np.pi / 2, np.pi / 2, -np.pi / 2]),
+    (I, [0, 0, 0, 0]),
+    (X, [np.pi / 2, 0, np.pi / 2, -np.pi / 2]),
+    (Y, [np.pi / 2, np.pi, 7 * np.pi / 2, -np.pi / 2]),
+    (Z, [0, np.pi, 0, -np.pi / 2]),
+    (H, [np.pi / 2, np.pi / 2, np.pi / 2, -np.pi / 2]),
     (
         qml.QubitUnitary(qml.RZ.compute_matrix(np.array([np.pi, np.pi / 2])), wires=0).matrix(),
-        typeof_gates_xzx,
         [[0, 0], [np.pi, np.pi / 2], [0, 0], [0, 0]],
     ),
     (
         qml.Rot(np.array([1.2, 1.5]), np.array([1.2, 1.5]), np.array([1.2, 1.5]), wires=0).matrix(),
-        typeof_gates_xzx,
         [
             [0.63319625, 0.75125105],
             [2.53416365, 3.03803113],
@@ -320,38 +288,35 @@ single_qubit_decomps_xzx = [
 class TestQubitUnitaryXZXDecomposition:
     """Test that the XZX decomposition is correct."""
 
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xzx)
-    def test_xzx_decomposition(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xzx)
+    def test_xzx_decomposition(self, U, expected_params):
         """Test that a one-qubit matrix in isolation is correctly decomposed."""
 
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XZX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XZX", typeof_gates_xzx, expected_params)
 
     @pytest.mark.torch
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xzx)
-    def test_xzx_decomposition_torch(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xzx)
+    def test_xzx_decomposition_torch(self, U, expected_params):
         """Test that a one-qubit operation in Torch is correctly decomposed."""
 
         import torch
 
         U = torch.tensor(U, dtype=torch.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XZX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XZX", typeof_gates_xzx, expected_params)
 
     @pytest.mark.tf
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xzx)
-    def test_xzx_decomposition_tf(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xzx)
+    def test_xzx_decomposition_tf(self, U, expected_params):
         """Test that a one-qubit operation in Tensorflow is correctly decomposed."""
 
         import tensorflow as tf
 
         U = tf.Variable(U, dtype=tf.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XZX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XZX", typeof_gates_xzx, expected_params)
 
     @pytest.mark.jax
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_xzx)
-    def test_xzx_decomposition_jax(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_xzx)
+    def test_xzx_decomposition_jax(self, U, expected_params):
         """Test that a one-qubit operation in JAX is correctly decomposed."""
 
         import jax
@@ -363,38 +328,23 @@ class TestQubitUnitaryXZXDecomposition:
         config.update("jax_enable_x64", True)
 
         U = jax.numpy.array(U, dtype=jax.numpy.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "XZX", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "XZX", typeof_gates_xzx, expected_params)
 
         # Reset the configuration
         config.update("jax_enable_x64", remember)
 
 
 typeof_gates_zxz = (qml.RZ, qml.RX, qml.RZ, qml.GlobalPhase)
-single_qubit_decomps_zxz = [
-    (I, typeof_gates_zxz, [0.0, 0.0, 0.0, 0]),
-    (Z, typeof_gates_zxz, [np.pi / 2, 0.0, np.pi / 2, -1.5707963267948966]),
-    (
-        S,
-        typeof_gates_zxz,
-        [np.pi / 4, 0.0, np.pi / 4, -0.7853981633974483],
-    ),
-    (
-        T,
-        typeof_gates_zxz,
-        [np.pi / 8, 0.0, np.pi / 8, -0.39269908047469393],
-    ),
-    (qml.RZ(0.3, wires=0).matrix(), typeof_gates_zxz, [0.15, 0.0, 0.15, 0]),
-    (
-        qml.RZ(-0.5, wires=0).matrix(),
-        typeof_gates_zxz,
-        [12.316370614359172, 0.0, 12.316370614359172, 0],
-    ),
-    (
-        qml.Rot(0.2, 0.5, -0.3, wires=0).matrix(),
-        typeof_gates_zxz,
-        [11.195574287564275, 0.5, 1.27079633, 0],
-    ),
+test_cases_zxz = [
+    (I, [0.0, 0.0, 0.0, 0]),
+    (Z, [np.pi / 2, 0.0, np.pi / 2, -np.pi / 2]),
+    (S, [np.pi / 4, 0.0, np.pi / 4, -np.pi / 4]),
+    (T, [np.pi / 8, 0.0, np.pi / 8, -np.pi / 8]),
+    (H, [np.pi / 2, np.pi / 2, np.pi / 2, -np.log(1j) / 1j]),
+    (X, [0, np.pi, 4 * np.pi, -np.log(1j) / 1j]),
+    (qml.RZ(0.3, wires=0).matrix(), [0.15, 0.0, 0.15, 0]),
+    (qml.RZ(-0.5, wires=0).matrix(), [4 * np.pi - 0.25, 0.0, 4 * np.pi - 0.25, 0]),
+    (qml.Rot(0.2, 0.5, -0.3, wires=0).matrix(), [11.195574287564275, 0.5, 1.2707963267948965, 0]),
     (
         np.array(
             [
@@ -402,14 +352,10 @@ single_qubit_decomps_zxz = [
                 [9.831019270939975e-01 + 0.1830590094588862j, 0],
             ]
         ),
-        typeof_gates_zxz,
         [10.811477142879012, np.pi, 1.7548934714801607, 0],
     ),
-    (H, typeof_gates_zxz, [np.pi / 2, np.pi / 2, np.pi / 2, -np.log(1j) / 1j]),
-    (X, typeof_gates_zxz, [0, np.pi, 4 * np.pi, -np.log(1j) / 1j]),
     (
         np.exp(1j * 0.02) * qml.Rot(-1.0, 2.0, -3.0, wires=0).matrix(),
-        typeof_gates_zxz,
         [
             9.995574287564276,
             2.0,
@@ -420,12 +366,10 @@ single_qubit_decomps_zxz = [
     # Add two instances of broadcasted unitaries, one coming from RZ and another from Rot
     (
         qml.QubitUnitary(qml.RZ.compute_matrix(np.array([np.pi, np.pi / 2])), wires=0).matrix(),
-        typeof_gates_zxz,
         [[np.pi / 2, np.pi / 4], [0.0, 0.0], [np.pi / 2, np.pi / 4], [0, 0]],
     ),
     (
         qml.Rot(np.array([1.2, 2.3]), np.array([1.2, 2.3]), np.array([1.2, 2.3]), wires=0).matrix(),
-        typeof_gates_zxz,
         [
             [12.19557429, 0.72920367],
             [1.2, 2.3],
@@ -439,38 +383,35 @@ single_qubit_decomps_zxz = [
 class TestQubitUnitaryZXZDecomposition:
     """Test that the ZXZ decomposition is correct."""
 
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zxz)
-    def test_zxz_decomposition(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zxz)
+    def test_zxz_decomposition(self, U, expected_params):
         """Test that a one-qubit matrix in isolation is correctly decomposed."""
 
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZXZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZXZ", typeof_gates_zxz, expected_params)
 
     @pytest.mark.torch
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zxz)
-    def test_zxz_decomposition_torch(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zxz)
+    def test_zxz_decomposition_torch(self, U, expected_params):
         """Test that a one-qubit operation in Torch is correctly decomposed."""
 
         import torch
 
         U = torch.tensor(U, dtype=torch.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZXZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZXZ", typeof_gates_zxz, expected_params)
 
     @pytest.mark.tf
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zxz)
-    def test_zxz_decomposition_tf(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zxz)
+    def test_zxz_decomposition_tf(self, U, expected_params):
         """Test that a one-qubit operation in Tensorflow is correctly decomposed."""
 
         import tensorflow as tf
 
         U = tf.Variable(U, dtype=tf.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZXZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZXZ", typeof_gates_zxz, expected_params)
 
     @pytest.mark.jax
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps_zxz)
-    def test_zxz_decomposition_jax(self, U, expected_gates, expected_params):
+    @pytest.mark.parametrize("U,expected_params", test_cases_zxz)
+    def test_zxz_decomposition_jax(self, U, expected_params):
         """Test that a one-qubit operation in JAX is correctly decomposed."""
 
         import jax
@@ -482,56 +423,29 @@ class TestQubitUnitaryZXZDecomposition:
         config.update("jax_enable_x64", True)
 
         U = jax.numpy.array(U, dtype=jax.numpy.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "ZXZ", return_global_phase=True)
-        _run_assertions(U, expected_gates, expected_params, obtained_gates)
+        _test_decomposition(U, "ZXZ", typeof_gates_zxz, expected_params)
 
         # Restore the configuration
         config.update("jax_enable_x64", remember)
 
 
-single_qubit_decomps_rot = [
+test_cases_rot = [
     # These will be decomposed to RZ
-    (I, qml.RZ, [0.0]),
-    (Z, qml.RZ, [np.pi]),
-    (S, qml.RZ, [np.pi / 2]),
-    (T, qml.RZ, [np.pi / 4]),
-    (qml.RZ(0.3, wires=0).matrix(), qml.RZ, [0.3]),
-    (qml.RZ(-0.5, wires=0).matrix(), qml.RZ, [-0.5]),
-    # This will be decomposed to Rot
+    (I, [qml.RZ, qml.GlobalPhase], [0.0, 0.0]),
+    (Z, [qml.RZ, qml.GlobalPhase], [np.pi, -np.pi / 2]),
+    (S, [qml.RZ, qml.GlobalPhase], [np.pi / 2, -np.pi / 4]),
+    (T, [qml.RZ, qml.GlobalPhase], [np.pi / 4, -np.pi / 8]),
+    (qml.RZ(0.3, wires=0).matrix(), [qml.RZ, qml.GlobalPhase], [0.3, 0.0]),
+    (qml.RZ(-0.5, wires=0).matrix(), [qml.RZ, qml.GlobalPhase], [4 * np.pi - 0.5, 0.0]),
+    # # This will be decomposed to Rot
     (
         qml.Rot(0.2, 0.5, -0.3, wires=0).matrix(),
-        qml.Rot,
-        [0.2, 0.5, -0.3],
+        [qml.Rot, qml.GlobalPhase],
+        [[0.2, 0.5, 4 * np.pi - 0.3], 0.0],
     ),
 ]
 
 
-def _run_assertions_rot(U, expected_gate, expected_params, obtained_gates):
-    """Runs assertions for the rot decomposition."""
-
-    assert len(obtained_gates) == 1, "Incorrect number of gates"
-    assert isinstance(obtained_gates[0], expected_gate), "Incorrect type of gate"
-    assert obtained_gates[0].wires == Wires("a"), "Incorrect wire"
-
-    # Now we check the rotation angles
-    assert qml.math.allclose(
-        qml.math.unwrap(obtained_gates[0].parameters),
-        expected_params,
-        atol=1e-7,
-    ), "Incorrect rotation angles"
-
-    obtained_mat = obtained_gates[0].matrix()
-
-    if len(obtained_mat.shape) == 2:
-        U = [U]
-        obtained_mat = [obtained_mat]
-
-    assert all(
-        check_matrix_equivalence(curr_obtained_mat, curr_U, atol=1e-7)
-        for curr_obtained_mat, curr_U in zip(obtained_mat, qml.math.unwrap(U))
-    )
-
-
 class TestOneQubitRotDecomposition:
     """Test that one qubit unitaries are correctly decomposed."""
 
@@ -542,39 +456,35 @@ class TestOneQubitRotDecomposition:
         with pytest.raises(ValueError):
             one_qubit_decomposition(U, Wires("a"), "nonsense string", return_global_phase=True)
 
-    @pytest.mark.parametrize("U,expected_gate,expected_params", single_qubit_decomps_rot)
-    def test_one_qubit_decomposition_rot(self, U, expected_gate, expected_params):
+    @pytest.mark.parametrize("U,expected_gates,expected_params", test_cases_rot)
+    def test_one_qubit_decomposition_rot(self, U, expected_gates, expected_params):
         """Test that a one-qubit matrix in isolation is correctly decomposed to RZ or Rot gate."""
 
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "rot", return_global_phase=True)
-
-        _run_assertions_rot(U, expected_gate, expected_params, obtained_gates)
+        _test_decomposition(U, "rot", expected_gates, expected_params)
 
     @pytest.mark.torch
-    @pytest.mark.parametrize("U,expected_gate,expected_params", single_qubit_decomps_rot)
-    def test_rot_decomposition_torch(self, U, expected_gate, expected_params):
+    @pytest.mark.parametrize("U,expected_gates,expected_params", test_cases_rot)
+    def test_rot_decomposition_torch(self, U, expected_gates, expected_params):
         """Test that a one-qubit operation in Torch is correctly decomposed."""
 
         import torch
 
         U = torch.tensor(U, dtype=torch.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "rot", return_global_phase=True)
-        _run_assertions_rot(U, expected_gate, expected_params, obtained_gates)
+        _test_decomposition(U, "rot", expected_gates, expected_params)
 
     @pytest.mark.tf
-    @pytest.mark.parametrize("U,expected_gate,expected_params", single_qubit_decomps_rot)
-    def test_rot_decomposition_tf(self, U, expected_gate, expected_params):
+    @pytest.mark.parametrize("U,expected_gates,expected_params", test_cases_rot)
+    def test_rot_decomposition_tf(self, U, expected_gates, expected_params):
         """Test that a one-qubit operation in Tensorflow is correctly decomposed."""
 
         import tensorflow as tf
 
         U = tf.Variable(U, dtype=tf.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "rot", return_global_phase=True)
-        _run_assertions_rot(U, expected_gate, expected_params, obtained_gates)
+        _test_decomposition(U, "rot", expected_gates, expected_params)
 
     @pytest.mark.jax
-    @pytest.mark.parametrize("U,expected_gate,expected_params", single_qubit_decomps_rot)
-    def test_rot_decomposition_jax(self, U, expected_gate, expected_params):
+    @pytest.mark.parametrize("U,expected_gates,expected_params", test_cases_rot)
+    def test_rot_decomposition_jax(self, U, expected_gates, expected_params):
         """Test that a one-qubit operation in JAX is correctly decomposed."""
 
         import jax
@@ -586,8 +496,7 @@ class TestOneQubitRotDecomposition:
         config.update("jax_enable_x64", True)
 
         U = jax.numpy.array(U, dtype=jax.numpy.complex128)
-        obtained_gates = one_qubit_decomposition(U, Wires("a"), "rot", return_global_phase=True)
-        _run_assertions_rot(U, expected_gate, expected_params, obtained_gates)
+        _test_decomposition(U, "rot", expected_gates, expected_params)
 
         # Restore the configuration
         config.update("jax_enable_x64", remember)
diff --git a/tests/transforms/test_unitary_to_rot.py b/tests/transforms/test_unitary_to_rot.py
index 53e635b54..e229ab90e 100644
--- a/tests/transforms/test_unitary_to_rot.py
+++ b/tests/transforms/test_unitary_to_rot.py
@@ -26,18 +26,20 @@ from pennylane.wires import Wires
 from pennylane.transforms import unitary_to_rot
 
 typeof_gates_zyz = (qml.RZ, qml.RY, qml.RZ)
-single_qubit_decomps = [
+single_qubit_decompositions = [
     (I, typeof_gates_zyz, [0.0, 0.0, 0.0]),
     (Z, typeof_gates_zyz, [np.pi / 2, 0.0, np.pi / 2]),
     (S, typeof_gates_zyz, [np.pi / 4, 0.0, np.pi / 4]),
     (T, typeof_gates_zyz, [np.pi / 8, 0.0, np.pi / 8]),
+    (H, typeof_gates_zyz, [np.pi, np.pi / 2, 0.0]),
+    (X, typeof_gates_zyz, [np.pi / 2, np.pi, 7 * np.pi / 2]),
     (qml.RZ(0.3, wires=0).matrix(), typeof_gates_zyz, [0.15, 0.0, 0.15]),
     (
         qml.RZ(-0.5, wires=0).matrix(),
         typeof_gates_zyz,
-        [12.316370614359172, 0.0, 12.316370614359172],
+        [4 * np.pi - 0.25, 0.0, 4 * np.pi - 0.25],
     ),
-    (qml.Rot(0.2, 0.5, -0.3, wires=0).matrix(), typeof_gates_zyz, [0.2, 0.5, 12.266370614359172]),
+    (qml.Rot(0.2, 0.5, -0.3, wires=0).matrix(), typeof_gates_zyz, [0.2, 0.5, 4 * np.pi - 0.3]),
     (
         np.array(
             [
@@ -46,14 +48,12 @@ single_qubit_decomps = [
             ]
         ),
         typeof_gates_zyz,
-        [12.382273469673908, np.pi, 0.18409714468526372],
+        [12.382273469673908, np.pi, 0.18409714468526417],
     ),
-    (H, typeof_gates_zyz, [np.pi, np.pi / 2, 0.0]),
-    (X, typeof_gates_zyz, [np.pi / 2, np.pi, 10.995574287564276]),
     (
         np.exp(1j * 0.02) * qml.Rot(-1.0, 2.0, -3.0, wires=0).matrix(),
         typeof_gates_zyz,
-        [11.566370614359172, 2.0, 9.566370614359172],
+        [4 * np.pi - 1.0, 2.0, 4 * np.pi - 3.0],
     ),
 ]
 
@@ -86,7 +86,7 @@ class TestDecomposeSingleQubitUnitaryTransform:
         res = circuit(U)
         assert np.allclose(res_trans, res)
 
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps)
+    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decompositions)
     def test_unitary_to_rot(self, U, expected_gates, expected_params):
         """Test that the transform works in the autograd interface."""
         transformed_qfunc = unitary_to_rot(qfunc)
@@ -137,7 +137,7 @@ class TestDecomposeSingleQubitUnitaryTransform:
         assert ops[3].wires == Wires(["a", "b", "c"])
 
     @pytest.mark.torch
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps)
+    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decompositions)
     def test_unitary_to_rot_torch(self, U, expected_gates, expected_params):
         """Test that the transform works in the torch interface."""
         import torch
@@ -164,7 +164,7 @@ class TestDecomposeSingleQubitUnitaryTransform:
         assert ops[4].wires == Wires(["b", "a"])
 
     @pytest.mark.tf
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps)
+    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decompositions)
     def test_unitary_to_rot_tf(self, U, expected_gates, expected_params):
         """Test that the transform works in the Tensorflow interface."""
         import tensorflow as tf
@@ -191,7 +191,7 @@ class TestDecomposeSingleQubitUnitaryTransform:
         assert ops[4].wires == Wires(["b", "a"])
 
     @pytest.mark.jax
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps)
+    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decompositions)
     def test_unitary_to_rot_jax(self, U, expected_gates, expected_params):
         """Test that the transform works in the JAX interface."""
         import jax
@@ -223,7 +223,7 @@ class TestDecomposeSingleQubitUnitaryTransform:
         assert ops[4].wires == Wires(["b", "a"])
 
     @pytest.mark.jax
-    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decomps)
+    @pytest.mark.parametrize("U,expected_gates,expected_params", single_qubit_decompositions)
     def test_unitary_to_rot_jax_jit(self, U, expected_gates, expected_params):
         """Test that the transform works in the JAX interface with JIT."""
         # pylint: disable=unused-argument