Repository: PennyLaneAI/pennylane
Pull request #1392 (2021-06-08 15:31:38)

Fix bug where shots does not reset to None when temporarily changed
 **Context:**

In PR #1075 we introduced a way to temporarily change the shots per call of a qnode. Due to a simple bug, the resetting to original shots value did not work if it was `None`.

**Description of the Change:**

Fix bug, add test.

**Possible Drawbacks:**

The test checks for analytic vs non-analytic behaviour by comparing the variance of a Hadamard circuit's expectation run 100 times, and has a vanishingly small probability of failing (I assume it is 2^100 or so :) - the probability of observing 100 ones or zeros in a coin toss)


github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [.github/CHANGELOG.md](/PennyLaneAI/pennylane/blob/master/.github/CHANGELOG.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/1392?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#1392](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (dfd806e) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/0dcbed53b470eb7f6f93b54077e36ca6287235cf?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (0dcbed5) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@           Coverage Diff           @@
##           master    #1392   +/-   ##
=======================================
  Coverage   98.18%   98.18%           
=======================================
  Files         156      156           
  Lines       11685    11685           
=======================================
  Hits        11473    11473           
  Misses        212      212           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/qnode.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3Fub2RlLnB5) | `97.82% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [0dcbed5...dfd806e](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1392?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

mariaschuld: @albi3ro should be fixed now :)

Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index b2bf741e5..7aef99260 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -418,6 +418,10 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
 
 <h3>Bug fixes</h3>
 
+* Fixes bug where `shots=None` was not reset when changing shots temporarily in a QNode call 
+  like `circuit(0.1, shots=3)`.
+  [(#1392)](https://github.com/XanaduAI/pennylane/pull/1392)
+  
 * Fixes floating point errors with `diff_method="finite-diff"` and `order=1` when parameters are `float32`.
 [(#1381)](https://github.com/PennyLaneAI/pennylane/pull/1381)
 
diff --git a/pennylane/qnode.py b/pennylane/qnode.py
index 5ddf8a57e..49af83b31 100644
--- a/pennylane/qnode.py
+++ b/pennylane/qnode.py
@@ -556,7 +556,7 @@ class QNode:
         # If shots specified in call but not in qfunc signature,
         # interpret it as device shots value for this call.
         # TODO: make this more functional by passing shots as qtape.execute(.., shots=shots).
-        original_shots = None
+        original_shots = -1
         if "shots" in kwargs and not self._qfunc_uses_shots_arg:
             original_shots = self.device.shots  # remember device shots
             # remove shots from kwargs and temporarily change on device
@@ -569,7 +569,8 @@ class QNode:
         # execute the tape
         res = self.qtape.execute(device=self.device)
 
-        if original_shots is not None:
+        # if shots was changed
+        if original_shots != -1:
             # reinstate default on device
             self.device.shots = original_shots
 


Test patch:
diff --git a/tests/tape/test_qnode.py b/tests/tape/test_qnode.py
index e64ba70b0..cacdedc25 100644
--- a/tests/tape/test_qnode.py
+++ b/tests/tape/test_qnode.py
@@ -873,8 +873,8 @@ class TestMutability:
 class TestShots:
     """Unittests for specifying shots per call."""
 
-    def test_specify_shots_per_call(self):
-        """Tests that shots can be set per call."""
+    def test_specify_shots_per_call_sample(self):
+        """Tests that shots can be set per call for a sample return type."""
         dev = qml.device("default.qubit", wires=1, shots=10)
 
         @qml.qnode(dev)
@@ -887,6 +887,30 @@ class TestShots:
         assert len(circuit(0.8, shots=3178)) == 3178
         assert len(circuit(0.8)) == 10
 
+    def test_specify_shots_per_call_expval(self):
+        """Tests that shots can be set per call for an expectation value.
+        Note: this test has a vanishingly small probability to fail."""
+        dev = qml.device("default.qubit", wires=1, shots=None)
+
+        @qml.qnode(dev)
+        def circuit():
+            qml.Hadamard(wires=0)
+            return qml.expval(qml.PauliZ(wires=0))
+
+        # check that the circuit is analytic
+        res1 = [circuit() for _ in range(100)]
+        assert np.std(res1) == 0.0
+        assert circuit.device._shots is None
+
+        # check that the circuit is temporary non-analytic
+        res1 = [circuit(shots=1) for _ in range(100)]
+        assert np.std(res1) != 0.0
+
+        # check that the circuit is analytic again
+        res1 = [circuit() for _ in range(100)]
+        assert np.std(res1) == 0.0
+        assert circuit.device._shots is None
+
     def test_no_shots_per_call_if_user_has_shots_qfunc_kwarg(self):
         """Tests that the per-call shots overwriting is suspended if user
         has a shots keyword argument, but a warning is raised."""