Repository: PennyLaneAI/pennylane
Pull request #3331 (2022-11-11 16:54:55)

Pauli rot bug fix
**Context:**
The `has_decomposition` and `decomposition` methods to decompose an `Exp` into `PauliRot` returns incorrect results if the base operator for the `Exp` is a Tensor object with multiple Pauli operators acting on the same wire, i.e. `qml.PauliX(0) @ qml.PauliY(0)`.

**Description of the Change:**
The `has_decomposition` method now includes a check for overlapping wires if the base observable is a Tensor object. It raises a NotImplementedError if there is overlap in the wires.

github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3331?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> :exclamation: No coverage uploaded for pull request base (`v0.27.0-rc0@e70acc4`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#section-missing-base-commit).
> The diff coverage is `n/a`.

```diff
@@              Coverage Diff               @@
##             v0.27.0-rc0    #3331   +/-   ##
==============================================
  Coverage               ?   99.70%           
==============================================
  Files                  ?      299           
  Lines                  ?    26139           
  Branches               ?        0           
==============================================
  Hits                   ?    26062           
  Misses                 ?       77           
  Partials               ?        0           
```



Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)


Patch:
diff --git a/pennylane/ops/op_math/exp.py b/pennylane/ops/op_math/exp.py
index 61794a873..a63549fac 100644
--- a/pennylane/ops/op_math/exp.py
+++ b/pennylane/ops/op_math/exp.py
@@ -23,6 +23,7 @@ from pennylane.operation import (
     DecompositionUndefinedError,
     expand_matrix,
     OperatorPropertyUndefined,
+    Tensor,
 )
 from pennylane.pauli import is_pauli_word, pauli_word_to_string
 from pennylane.wires import Wires
@@ -170,6 +171,14 @@ class Exp(SymbolicOp):
     # pylint: disable=arguments-renamed, invalid-overridden-method
     @property
     def has_decomposition(self):
+        if isinstance(self.base, Tensor):
+            all_wires = [obs.wires for obs in self.base.obs]
+            if len(all_wires) != len(set(all_wires)):
+                raise NotImplementedError(
+                    "Unable to determine if the exponential has a decomposition "
+                    "when the base operator is a Tensor object with overlapping wires. "
+                    f"Received base {self.base}."
+                )
         return math.real(self.coeff) == 0 and is_pauli_word(self.base)
 
     def decomposition(self):
@@ -184,7 +193,7 @@ class Exp(SymbolicOp):
         Returns:
             list[PauliRot]: decomposition of the operator
         """
-        if math.real(self.coeff) != 0 or not is_pauli_word(self.base):
+        if not self.has_decomposition:
             raise DecompositionUndefinedError
         new_coeff = math.real(2j * self.coeff)
         string_base = pauli_word_to_string(self.base)


Test patch:
diff --git a/tests/ops/op_math/test_exp.py b/tests/ops/op_math/test_exp.py
index 37e3d5073..a6622dc33 100644
--- a/tests/ops/op_math/test_exp.py
+++ b/tests/ops/op_math/test_exp.py
@@ -280,18 +280,30 @@ class TestDecomposition:
     @pytest.mark.parametrize("coeff", (1, 1 + 0.5j))
     def test_non_imag_no_decomposition(self, coeff):
         """Tests that the decomposition doesn't exist if the coefficient has a real component."""
-        op = Exp(coeff, qml.PauliX(0))
+        op = Exp(qml.PauliX(0), coeff)
         assert not op.has_decomposition
         with pytest.raises(DecompositionUndefinedError):
             op.decomposition()
 
     def test_non_pauli_word_base_no_decomposition(self):
         """Tests that the decomposition doesn't exist if the base is not a pauli word."""
-        op = Exp(-0.5j, qml.S(0))
+        op = Exp(qml.S(0), -0.5j)
         assert not op.has_decomposition
         with pytest.raises(DecompositionUndefinedError):
             op.decomposition()
 
+    def test_nontensor_tensor_raises_error(self):
+        """Checks that accessing the decomposition throws an error if the base is a Tensor
+        object that is not a mathematical tensor"""
+        base_op = qml.PauliX(0) @ qml.PauliZ(0)
+        op = Exp(base_op, 1j)
+
+        with pytest.raises(NotImplementedError):
+            op.has_decomposition()
+
+        with pytest.raises(NotImplementedError):
+            op.decomposition()
+
     @pytest.mark.parametrize(
         "base, base_string",
         (