Repository: PennyLaneAI/pennylane
Pull request #5919 (2024-06-28 21:41:42)

turn QuantumScript.hash into a cached property


**Context:**

When investigating some performance issues, I noticed that we weren't caching `QuantumScript.hash`, even though calculating it is a known performance bottleneck.

**Description of the Change:**

Make `QuantumScript.hash` a cached property.

**Benefits:**

Much lower classical overhead when we are using caching.

<img width="839" alt="Screenshot 2024-06-28 at 5 38 15 PM" src="https://github.com/PennyLaneAI/pennylane/assets/6364575/9d25401d-af74-455b-8a18-04fc7dc07c33">

**Possible Drawbacks:**

If someone does bad things and mutates the tape in place, they will have to deal with the resulting consequences.

**Related GitHub Issues:**


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5919?dropdown=coverage&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:
> Project coverage is 99.66%. Comparing base [(`e74c9bf`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/e74c9bf821518196ce3bc1c8a740d63179cef9c3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`1e9eec2`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/1e9eec2dd4b18b2896c2eb9514ce2a7ccf463124?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

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


```diff
@@            Coverage Diff             @@
##           master    #5919      +/-   ##
==========================================
- Coverage   99.67%   99.66%   -0.01%     
==========================================
  Files         425      425              
  Lines       40772    40477     -295     
==========================================
- Hits        40640    40343     -297     
- Misses        132      134       +2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5919?dropdown=coverage&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).


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 22f268d7b..60b40c8dd 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -8,6 +8,9 @@
   
 <h3>Improvements 🛠</h3>
 
+* `QuantumScript.hash` is now cached, leading to performance improvements.
+  [(#5919)](https://github.com/PennyLaneAI/pennylane/pull/5919)
+
 <h3>Breaking changes 💔</h3>
 
 <h3>Deprecations 👋</h3>
@@ -20,4 +23,5 @@
 
 This release contains contributions from (in alphabetical order):
 
-Yushao Chen.
\ No newline at end of file
+Yushao Chen,
+Christina Lee,
\ No newline at end of file
diff --git a/pennylane/tape/qscript.py b/pennylane/tape/qscript.py
index dbf9bc5f2..2d1182f51 100644
--- a/pennylane/tape/qscript.py
+++ b/pennylane/tape/qscript.py
@@ -188,7 +188,7 @@ class QuantumScript:
     def __repr__(self):
         return f"<{self.__class__.__name__}: wires={self.wires.tolist()}, params={self.num_params}>"
 
-    @property
+    @cached_property
     def hash(self):
         """int: returns an integer hash uniquely representing the quantum script"""
         fingerprint = []
@@ -378,11 +378,13 @@ class QuantumScript:
         """Update all internal metadata regarding processed operations and observables"""
         self._graph = None
         self._specs = None
+        self._trainable_params = None
 
         try:
             # Invalidate cached properties so they get recalculated
             del self.wires
             del self.par_info
+            del self.hash
         except AttributeError:
             pass
 


Test patch:
diff --git a/tests/tape/test_qscript.py b/tests/tape/test_qscript.py
index 1d86486ad..f72b9f986 100644
--- a/tests/tape/test_qscript.py
+++ b/tests/tape/test_qscript.py
@@ -199,11 +199,16 @@ class TestUpdate:
         ]
         m = [qml.expval(qml.Hermitian(2 * np.eye(2), wires=0))]
         qs = QuantumScript(ops, m)
+        assert qs.wires == qml.wires.Wires((0,))
+        assert isinstance(qs.par_info, list) and len(qs.par_info) > 0
+        old_hash = qs.hash
+        assert qs.trainable_params == [0, 1, 2, 3, 4, 5, 6, 7]
         qs._ops = []
         qs._measurements = []
         qs._update()
         assert qs.wires == qml.wires.Wires([])
         assert isinstance(qs.par_info, list) and len(qs.par_info) == 0
+        assert QuantumScript([], []).hash == qs.hash and qs.hash != old_hash
 
     # pylint: disable=unbalanced-tuple-unpacking
     def test_get_operation(self):