Repository: PennyLaneAI/pennylane
Pull request #3456 (2022-12-01 20:28:12)

Allow devices to specify if they want to disregard grouping
### Before submitting

Please complete the following checklist when submitting a PR:

- [x] All new features must include a unit test.
      If you've fixed a bug or added code that should be tested, add a test to the
      test directory!

- [x] All new functions and code must be clearly commented and documented.
      If you do make documentation changes, make sure that the docs build and
      render correctly by running `make docs`.

- [x] Ensure that the test suite passes, by running `make test`.

- [x] Add a new entry to the `doc/releases/changelog-dev.md` file, summarizing the
      change, and including a link back to the PR.

- [x] The PennyLane source code conforms to
      [PEP8 standards](https://www.python.org/dev/peps/pep-0008/).
      We check all of our code against [Pylint](https://www.pylint.org/).
      To lint modified files, simply `pip install pylint`, and then
      run `pylint pennylane/path/to/file.py`.

When all the above are checked, delete everything above the dashed
line and fill in the pull request template.

------------------------------------------------------------------------------------------------------------

**Context:**

Add variable in `execute` to allow devices to request that Hamiltonians **not** be expanded using grouped indices, even if they are known.

**Description of the Change:**

Default behaviour is unchanged. If a device does have a `use_grouping` attr, it can specify that grouping not be used.

**Benefits:**

Allows devices to control whether one expectation value or many are calculated. Avoids unexpected conversion of one observable (a Hamiltonian) into a list of observables.

**Possible Drawbacks:**

Degraded performance if device developers choose badly when setting the default for their device.

**Related GitHub Issues:**


codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#3456](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (b350e25) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/fbdf80cda31681094ee7d9f7133ef888aaad852b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (fbdf80c) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head b350e25 differs from pull request most recent head 3fff485. Consider uploading reports for the commit 3fff485 to get more accurate results

```diff
@@           Coverage Diff           @@
##           master    #3456   +/-   ##
=======================================
  Coverage   99.68%   99.68%           
=======================================
  Files         299      300    +1     
  Lines       26853    26919   +66     
=======================================
+ Hits        26768    26834   +66     
  Misses         85       85           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/\_device.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL19kZXZpY2UucHk=) | `98.17% <100.00%> (+<0.01%)` | :arrow_up: |
| [pennylane/pauli/utils.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3BhdWxpL3V0aWxzLnB5) | `100.00% <0.00%> (ø)` | |
| [pennylane/pauli/\_\_init\_\_.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3BhdWxpL19faW5pdF9fLnB5) | `100.00% <0.00%> (ø)` | |
| [pennylane/qinfo/transforms.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3FpbmZvL3RyYW5zZm9ybXMucHk=) | `100.00% <0.00%> (ø)` | |
| [pennylane/transforms/metric\_tensor.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3RyYW5zZm9ybXMvbWV0cmljX3RlbnNvci5weQ==) | `100.00% <0.00%> (ø)` | |
| [pennylane/pauli/conversion.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3456/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3BhdWxpL2NvbnZlcnNpb24ucHk=) | `100.00% <0.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)

kshyatt-aws: > Do you want me to write a unittest for this flag?

Yes please. My Python monkeypatching knowledge is minimal at best.
albi3ro: I thought adding it in to the default configuration would be painless. Looks like it is not. Feel free to remove it from the default configuration dictionary if it breaks too many tests.

If this were a long-term solution, I would want it in the configuration dictionary. But we will be moving away from `Device` soon enough that I'm not as concerned about the effect of technical debt.
kshyatt-aws: Thank you @albi3ro for the test.

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 09229425d..b5a63207d 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -300,6 +300,9 @@
 * Replace (almost) all instances of `with QuantumTape()` with `QuantumScript` construction.
   [(#3454)](https://github.com/PennyLaneAI/pennylane/pull/3454)
 
+* Adds support for devices disregarding observable grouping indices in Hamiltonians through
+  the optional `use_grouping` attribute.
+  [(#3456)](https://github.com/PennyLaneAI/pennylane/pull/3456)
 
 <h4>Return types project</h4>
 
@@ -566,6 +569,7 @@ Astral Cai
 Isaac De Vlugt
 Pieter Eendebak
 Lillian M. A. Frederiksen
+Katharine Hyatt
 Soran Jahangiri
 Edward Jiang
 Christina Lee
diff --git a/pennylane/_device.py b/pennylane/_device.py
index 89d1ad015..92f91ee58 100644
--- a/pennylane/_device.py
+++ b/pennylane/_device.py
@@ -731,16 +731,17 @@ class Device(abc.ABC):
             for obs in circuit.observables
             if obs.name == "Hamiltonian"
         )
+        # device property present in braket plugin
+        use_grouping = getattr(self, "use_grouping", True)
 
         hamiltonian_in_obs = "Hamiltonian" in [obs.name for obs in circuit.observables]
 
         return_types = [m.return_type for m in circuit.observables]
 
         is_shadow = ShadowExpval in return_types
+        hamiltonian_unusable = not supports_hamiltonian or (finite_shots and not is_shadow)
 
-        if hamiltonian_in_obs and (
-            (not supports_hamiltonian or (finite_shots and not is_shadow)) or grouping_known
-        ):
+        if hamiltonian_in_obs and (hamiltonian_unusable or (use_grouping and grouping_known)):
             # If the observable contains a Hamiltonian and the device does not
             # support Hamiltonians, or if the simulation uses finite shots, or
             # if the Hamiltonian explicitly specifies an observable grouping,


Test patch:
diff --git a/tests/test_device.py b/tests/test_device.py
index 80a92ad01..798e6f660 100644
--- a/tests/test_device.py
+++ b/tests/test_device.py
@@ -1022,3 +1022,40 @@ class TestBatchExecution:
         assert np.allclose(
             res[0], dev.execute(empty_tape.operations, empty_tape.observables), rtol=tol, atol=0
         )
+
+
+class TestGrouping:
+    """Tests for the use_grouping option for devices."""
+
+    @pytest.mark.parametrize("use_grouping", (True, False))
+    def test_batch_transform_checks_use_grouping_property(self, use_grouping):
+        """If the device specifies `use_grouping=False`, the batch transform
+        method won't expand the hamiltonian when the measured hamiltonian has
+        grouping indices.
+        """
+
+        class TestDevice(qml.Device):
+            name = ""
+            short_name = ""
+            pennylane_requires = ""
+            version = ""
+            author = ""
+            operations = ""
+            observables = ""
+            apply = lambda *args, **kwargs: 0
+            expval = lambda *args, **kwargs: 0
+            reset = lambda *args, **kwargs: 0
+            supports_observable = lambda *args, **kwargs: True
+
+        H = qml.Hamiltonian([1.0, 1.0], [qml.PauliX(0), qml.PauliY(0)], grouping_type="qwc")
+        qs = qml.tape.QuantumScript(measurements=[qml.expval(H)])
+
+        dev = TestDevice()
+        dev.shots = None
+        dev.use_grouping = use_grouping
+        new_qscripts, post_proc_fn = dev.batch_transform(qs)
+
+        if use_grouping:
+            assert len(new_qscripts) == 2
+        else:
+            assert len(new_qscripts) == 1