Repository: PennyLaneAI/pennylane
Pull request #3940 (2023-03-23 20:34:38)

fix tensor validation in the new qubit device expand_fn
I've been tempted by this too, but you gotta use `elif` for the second line or else my test fails badly (I confirmed this locally before adding the fix).

If you're wondering: It fails because it checks if `obs.name` is in a list of accepted names, but `Tensor.name` is itself a list, and python can't check if a list is in another list (nor should it in this case) because lists are not hashable!

I noticed it in @albi3ro's [commit](https://github.com/PennyLaneAI/pennylane/pull/3903/commits/cd4fbce3451887fc4bdbac1bfe7b1c3acff2c512) that snuck into [a different PR](https://github.com/PennyLaneAI/pennylane/pull/3921/commits/24e0e1e249cce7312fc4e580536cdb58ac038140#diff-dc464aff6b718b265eaa3cde65e0510c41b500e73ee0026f11ec343aa6ae60f7), and I'm thankful it did! The offending bug first appeared in #3857 (which is not in 0.29.1), so there's no real changelog entry or anything needed bc it's never been in a release.

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/3940?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#3940](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3940?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (ca477c5) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/2038cee0085ae6175098f982fe8b5964bc635327?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (2038cee) will **decrease** coverage by `0.01%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master    #3940      +/-   ##
==========================================
- Coverage   99.74%   99.73%   -0.01%     
==========================================
  Files         344      344              
  Lines       30154    30155       +1     
==========================================
  Hits        30076    30076              
- Misses         78       79       +1     
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/ops/qubit/parametric\_ops\_multi\_qubit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wcy9xdWJpdC9wYXJhbWV0cmljX29wc19tdWx0aV9xdWJpdC5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/devices/default\_qubit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdC5weQ==) | `99.69% <100.00%> (-0.01%)` | :arrow_down: |
| [pennylane/devices/qubit/preprocess.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvcXViaXQvcHJlcHJvY2Vzcy5weQ==) | `100.00% <100.00%> (ø)` | |
| [pennylane/ops/op\_math/evolution.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wcy9vcF9tYXRoL2V2b2x1dGlvbi5weQ==) | `97.43% <100.00%> (-2.57%)` | :arrow_down: |
| [pennylane/ops/op\_math/exp.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wcy9vcF9tYXRoL2V4cC5weQ==) | `100.00% <100.00%> (ø)` | |

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/devices/qubit/preprocess.py b/pennylane/devices/qubit/preprocess.py
index 735baa7d4..e20c9ee91 100644
--- a/pennylane/devices/qubit/preprocess.py
+++ b/pennylane/devices/qubit/preprocess.py
@@ -112,11 +112,10 @@ def expand_fn(circuit: qml.tape.QuantumScript) -> qml.tape.QuantumScript:
         circuit = qml.tape.QuantumScript(new_ops, circuit.measurements, circuit._prep)
 
     for observable in circuit.observables:
-        if isinstance(observable, Tensor) and any(
-            o.name not in _observables for o in observable.obs
-        ):
-            raise DeviceError(f"Observable {observable} not supported on DefaultQubit2")
-        if observable.name not in _observables:
+        if isinstance(observable, Tensor):
+            if any(o.name not in _observables for o in observable.obs):
+                raise DeviceError(f"Observable {observable} not supported on DefaultQubit2")
+        elif observable.name not in _observables:
             raise DeviceError(f"Observable {observable} not supported on DefaultQubit2")
 
     # change this once shots are supported


Test patch:
diff --git a/tests/devices/qubit/test_preprocess.py b/tests/devices/qubit/test_preprocess.py
index 7af0abc54..60f17c733 100644
--- a/tests/devices/qubit/test_preprocess.py
+++ b/tests/devices/qubit/test_preprocess.py
@@ -145,6 +145,14 @@ class TestExpandFnValidation:
         with pytest.raises(DeviceError, match="Observable expval"):
             expand_fn(tape)
 
+    def test_expand_fn_valid_tensor_observable(self):
+        """Test that expand_fn passes when a tensor includes only valid obserables."""
+        tape = QuantumScript(
+            ops=[qml.PauliX(0), qml.PauliY(1)],
+            measurements=[qml.expval(qml.PauliZ(0) @ qml.PauliX(1))],
+        )
+        expand_fn(tape)
+
     def test_state_prep_only_one(self):
         """Test that a device error is raised if the script has multiple state prep ops."""
         qs = QuantumScript(prep=[qml.BasisState([0], wires=0), qml.BasisState([1], wires=1)])