Repository: PennyLaneAI/pennylane
Pull request #4941 (2023-12-12 15:58:36)

add `process_counts` method to `SampleMeasurement`
This PR adds a `process_counts` method to `SampleMeasurement`. This method is not implemented by anything and only raises a `NotImplementedError`.

This method is to make it easier to support plugins and remote devices that prefer producing and sending counts histograms instead of the raw samples.

albi3ro: [sc-51501]
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4941?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:
> Comparison is base [(`c325c87`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/c325c87047c33227a1f6584a3a3984081a8909fe?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.66% compared to head [(`66cd86c`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4941?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.58%.
> Report is 1 commits behind head on master.


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


```diff
@@            Coverage Diff             @@
##           master    #4941      +/-   ##
==========================================
- Coverage   99.66%   99.58%   -0.09%     
==========================================
  Files         388      388              
  Lines       35350    35186     -164     
==========================================
- Hits        35233    35041     -192     
- Misses        117      145      +28     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4941?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 8324791eb..84767d6e3 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -326,6 +326,10 @@
 
 <h4>Other improvements</h4>
 
+* `SampleMeasurement` now has an optional method `process_counts` for computing the measurement results from a counts
+  dictionary.
+  [(#4941)](https://github.com/PennyLaneAI/pennylane/pull/4941/)
+
 * A new function called `ops.functions.assert_valid` has been added for checking if an `Operator` class is defined correctly.
   [(#4764)](https://github.com/PennyLaneAI/pennylane/pull/4764)
 
diff --git a/pennylane/measurements/__init__.py b/pennylane/measurements/__init__.py
index a801bb66c..2eaba8832 100644
--- a/pennylane/measurements/__init__.py
+++ b/pennylane/measurements/__init__.py
@@ -24,8 +24,9 @@ from by the :class:`SampleMeasurement`, :class:`StateMeasurement` and :class:`Me
 classes. These classes are subclassed to implement measurements in PennyLane.
 
 * Each :class:`SampleMeasurement` subclass represents a sample-based measurement, which contains a
-  :meth:`SampleMeasurement.process_samples` method that processes the sequence of samples generated
-  by the device. This method should always have the same arguments:
+  :meth:`SampleMeasurement.process_samples` method and a :meth:`SampleMeasurement.process_counts` method
+  that process the sequence of samples generated
+  by the device. ``process_samples`` method should always have the same arguments:
 
   * samples (Sequence[complex]): computational basis samples generated for all wires
   * wire_order (Wires): wires determining the subspace that ``samples`` acts on
@@ -35,6 +36,9 @@ classes. These classes are subclassed to implement measurements in PennyLane.
     measurement statistic separately over each bin. If not provided, the entire shot range is treated
     as a single bin.
 
+  :meth:`SampleMeasurement.process_counts` is currently optional. It accepts a dictionary mapping a string
+  representation of a basis state to an integer and a wire order.
+
   See :class:`CountsMP` for an example.
 
 * Each :class:`StateMeasurement` subclass represents a state-based measurement, which contains a
@@ -146,6 +150,9 @@ obtained of a given state:
             counts = counts_mp.process_samples(samples, wire_order, shot_range, bin_size)
             return float(counts.get(self.state, 0))
 
+        def process_counts(self, counts, wire_order):
+            return float(counts.get(self.state, 0))
+
         def __copy__(self):
             return CountState(state=self.state)
 
diff --git a/pennylane/measurements/measurements.py b/pennylane/measurements/measurements.py
index 721611a40..d6fb8d755 100644
--- a/pennylane/measurements/measurements.py
+++ b/pennylane/measurements/measurements.py
@@ -532,6 +532,17 @@ class SampleMeasurement(MeasurementProcess):
                 provided, the entire shot range is treated as a single bin.
         """
 
+    def process_counts(self, counts: dict, wire_order: Wires):
+        """Calculate the measurement given a counts histogram dictionary.
+
+        Args:
+            counts (dict): a dictionary matching the format returned by :class:`~.CountsMP`
+            wire_order (Wires): the wire order used in producing the counts
+
+        Note that the input dictionary may only contain states with non-zero entries (``all_outcomes=False``).
+        """
+        raise NotImplementedError
+
 
 class StateMeasurement(MeasurementProcess):
     """State-based measurement process.


Test patch:
diff --git a/tests/measurements/test_measurements.py b/tests/measurements/test_measurements.py
index f3ce2de0d..80249a4f8 100644
--- a/tests/measurements/test_measurements.py
+++ b/tests/measurements/test_measurements.py
@@ -581,6 +581,22 @@ class TestSampleMeasurement:
         ):
             circuit()
 
+    def test_process_counts_not_implemented(self):
+        """Test that process_counts is not implemented by default."""
+
+        class MyMeasurement(SampleMeasurement):
+            # pylint: disable=signature-differs
+            def process_samples(self, samples, wire_order, shot_range, bin_size):
+                return qml.math.sum(samples[..., self.wires])
+
+            @property
+            def return_type(self):
+                return Sample
+
+        m = MyMeasurement(wires=[0])
+        with pytest.raises(NotImplementedError):
+            m.process_counts({"0": 10}, wire_order=qml.wires.Wires(0))
+
 
 class TestStateMeasurement:
     """Tests for the SampleMeasurement class."""