Repository: PennyLaneAI/pennylane
Pull request #5177 (2024-02-08 10:06:51)

[BUGFIX] qml.equal via pauli_rep
**Potential** fix for https://github.com/PennyLaneAI/pennylane/issues/5162 and many other related issues  
Simply compares the pauli_rep between operators, when both have one.

[sc-56466]


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 742ac7838..88bf65588 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -446,6 +446,10 @@
 * `default.mixed` no longer throws `ValueError` when applying a state vector that is not of type `complex128` when used with tensorflow.
   [(#5155)](https://github.com/PennyLaneAI/pennylane/pull/5155)
 
+* Comparing `Prod` and `Sum` objects now works regardless of nested structure with `qml.equal` if the
+  operators have a valid `pauli_rep` property.
+  [(#5177)](https://github.com/PennyLaneAI/pennylane/pull/5177)
+
 <h3>Contributors ✍️</h3>
 
 This release contains contributions from (in alphabetical order):
diff --git a/pennylane/ops/functions/equal.py b/pennylane/ops/functions/equal.py
index d0bc56e41..9b4e10b61 100644
--- a/pennylane/ops/functions/equal.py
+++ b/pennylane/ops/functions/equal.py
@@ -269,12 +269,12 @@ def _equal_operators(
 # pylint: disable=unused-argument, protected-access
 def _equal_prod_and_sum(op1: CompositeOp, op2: CompositeOp, **kwargs):
     """Determine whether two Prod or Sum objects are equal"""
-    if len(op1.operands) != len(op2.operands):
-        return False
-
     if op1.pauli_rep is not None and (op1.pauli_rep == op2.pauli_rep):  # shortcut check
         return True
 
+    if len(op1.operands) != len(op2.operands):
+        return False
+
     # organizes by wire indicies while respecting commutation relations
     sorted_ops1 = op1._sort(op1.operands)
     sorted_ops2 = op2._sort(op2.operands)


Test patch:
diff --git a/tests/ops/functions/test_equal.py b/tests/ops/functions/test_equal.py
index 79067bca1..8df050a1f 100644
--- a/tests/ops/functions/test_equal.py
+++ b/tests/ops/functions/test_equal.py
@@ -1702,6 +1702,15 @@ class TestProdComparisons:
         op2 = qml.prod(*base_list2)
         assert qml.equal(op1, op2) == res
 
+    def test_prod_of_prods(self):
+        """Test that prod of prods and just an equivalent Prod get compared correctly"""
+        X = qml.PauliX
+        qml.operation.enable_new_opmath()
+        op1 = (0.5 * X(0)) @ (0.5 * X(1)) @ (0.5 * X(2)) @ (0.5 * X(3)) @ (0.5 * X(4))
+        op2 = qml.prod(*[0.5 * X(i) for i in range(5)])
+        assert qml.equal(op1, op2)
+        qml.operation.disable_new_opmath()
+
 
 class TestSumComparisons:
     """Tests comparisons between Sum operators"""
@@ -1795,6 +1804,26 @@ class TestSumComparisons:
 
         assert true_res == res
 
+    def test_sum_of_sums(self):
+        """Test that sum of sums and just an equivalent sum get compared correctly"""
+        X = qml.PauliX
+        qml.operation.enable_new_opmath()
+        op1 = (
+            0.5 * X(0)
+            + 0.5 * X(1)
+            + 0.5 * X(2)
+            + 0.5 * X(3)
+            + 0.5 * X(4)
+            + 0.5 * X(5)
+            + 0.5 * X(6)
+            + 0.5 * X(7)
+            + 0.5 * X(8)
+            + 0.5 * X(9)
+        )
+        op2 = qml.sum(*[0.5 * X(i) for i in range(10)])
+        assert qml.equal(op1, op2)
+        qml.operation.disable_new_opmath()
+
 
 def f1(p, t):
     return np.polyval(p, t)