32
32
33
33
from pennylane .measurements import (
34
34
Counts ,
35
+ AllCounts ,
35
36
Expectation ,
36
37
MeasurementProcess ,
37
38
MutualInfo ,
@@ -319,7 +320,9 @@ def execute(self, circuit, **kwargs):
319
320
self ._samples = self .generate_samples ()
320
321
321
322
ret_types = [m .return_type for m in circuit .measurements ]
322
- counts_exist = any (ret is qml .measurements .Counts for ret in ret_types )
323
+ counts_exist = any (
324
+ ret in (qml .measurements .Counts , qml .measurements .AllCounts ) for ret in ret_types
325
+ )
323
326
324
327
# compute the required statistics
325
328
if not self .analytic and self ._shot_vector is not None :
@@ -448,7 +451,9 @@ def shot_vec_statistics(self, circuit):
448
451
s1 = 0
449
452
450
453
ret_types = [m .return_type for m in circuit .measurements ]
451
- counts_exist = any (ret is qml .measurements .Counts for ret in ret_types )
454
+ counts_exist = any (
455
+ ret in (qml .measurements .Counts , qml .measurements .AllCounts ) for ret in ret_types
456
+ )
452
457
single_measurement = len (circuit .measurements ) == 1
453
458
454
459
for shot_tuple in self ._shot_vector :
@@ -545,7 +550,7 @@ def _multi_meas_with_counts_shot_vec(self, circuit, shot_tuple, r):
545
550
else :
546
551
result = r_ [idx ]
547
552
548
- if not circuit .observables [idx2 ].return_type is Counts :
553
+ if not circuit .observables [idx2 ].return_type in ( Counts , AllCounts ) :
549
554
result = self ._asarray (result .T )
550
555
551
556
result_group .append (result )
@@ -745,7 +750,7 @@ def statistics(self, observables, shot_range=None, bin_size=None, circuit=None):
745
750
self .sample (obs , shot_range = shot_range , bin_size = bin_size , counts = False )
746
751
)
747
752
748
- elif obs .return_type is Counts :
753
+ elif obs .return_type in ( Counts , AllCounts ) :
749
754
results .append (
750
755
self .sample (obs , shot_range = shot_range , bin_size = bin_size , counts = True )
751
756
)
@@ -896,7 +901,7 @@ def statistics_new(self, observables, shot_range=None, bin_size=None):
896
901
samples = self .sample (obs , shot_range = shot_range , bin_size = bin_size , counts = False )
897
902
result = qml .math .squeeze (samples )
898
903
899
- elif obs .return_type is Counts :
904
+ elif obs .return_type in ( Counts , AllCounts ) :
900
905
result = self .sample (obs , shot_range = shot_range , bin_size = bin_size , counts = True )
901
906
902
907
elif obs .return_type is Probability :
@@ -1598,6 +1603,75 @@ def var(self, observable, shot_range=None, bin_size=None):
1598
1603
# TODO: do we need to squeeze here? Maybe remove with new return types
1599
1604
return np .squeeze (np .var (samples , axis = axis ))
1600
1605
1606
+ def _samples_to_counts (self , samples , obs , num_wires ):
1607
+ """Groups the samples into a dictionary showing number of occurences for
1608
+ each possible outcome.
1609
+
1610
+ The format of the dictionary depends on obs.return_type, which is set when
1611
+ calling measurements.counts by setting the kwarg all_outcomes (bool). Per default,
1612
+ the dictionary will only contain the observed outcomes. Optionally (all_outcomes=True)
1613
+ the dictionary will instead contain all possible outcomes, with a count of 0
1614
+ for those not observed. See example.
1615
+
1616
+
1617
+ Args:
1618
+ samples: samples in an array of dimension ``(shots,len(wires))``
1619
+ obs (Observable): the observable sampled
1620
+ num_wires (int): number of wires the sampled observable was performed on
1621
+
1622
+ Returns:
1623
+ dict: dictionary with format ``{'outcome': num_occurences}``, including all
1624
+ outcomes for the sampled observable
1625
+
1626
+ **Example**
1627
+
1628
+ >>> samples
1629
+ tensor([[0, 0],
1630
+ [0, 0],
1631
+ [1, 0]], requires_grad=True)
1632
+
1633
+ Per default, this will return:
1634
+ >>> self._samples_to_counts(samples, obs, num_wires)
1635
+ {'00': 2, '10': 1}
1636
+
1637
+ However, if obs.return_type is AllCounts, this will return:
1638
+ >>> self._samples_to_counts(samples, obs, num_wires)
1639
+ {'00': 2, '01': 0, '10': 1, '11': 0}
1640
+
1641
+ The variable all_outcomes can be set when running measurements.counts, i.e.:
1642
+
1643
+ .. code-block:: python3
1644
+
1645
+ dev = qml.device("default.qubit", wires=2, shots=4)
1646
+
1647
+ @qml.qnode(dev)
1648
+ def circuit(x):
1649
+ qml.RX(x, wires=0)
1650
+ return qml.counts(all_outcomes=True)
1651
+
1652
+
1653
+ """
1654
+
1655
+ outcomes = []
1656
+
1657
+ if isinstance (obs , MeasurementProcess ):
1658
+ # convert samples and outcomes (if using) from arrays to str for dict keys
1659
+ samples = ["" .join ([str (s .item ()) for s in sample ]) for sample in samples ]
1660
+
1661
+ if obs .return_type is AllCounts :
1662
+ outcomes = self .generate_basis_states (num_wires )
1663
+ outcomes = ["" .join ([str (o .item ()) for o in outcome ]) for outcome in outcomes ]
1664
+ elif obs .return_type is AllCounts :
1665
+ outcomes = qml .eigvals (obs )
1666
+
1667
+ # generate empty outcome dict, populate values with state counts
1668
+ outcome_dict = {k : np .int64 (0 ) for k in outcomes }
1669
+ states , counts = np .unique (samples , return_counts = True )
1670
+ for s , c in zip (states , counts ):
1671
+ outcome_dict [s ] = c
1672
+
1673
+ return outcome_dict
1674
+
1601
1675
def sample (self , observable , shot_range = None , bin_size = None , counts = False ):
1602
1676
"""Return samples of an observable.
1603
1677
@@ -1620,28 +1694,6 @@ def sample(self, observable, shot_range=None, bin_size=None, counts=False):
1620
1694
dimension ``(shots,)`` or counts
1621
1695
"""
1622
1696
1623
- def _samples_to_counts (samples , no_observable_provided ):
1624
- """Group the obtained samples into a dictionary.
1625
-
1626
- **Example**
1627
-
1628
- >>> samples
1629
- tensor([[0, 0, 1],
1630
- [0, 0, 1],
1631
- [1, 1, 1]], requires_grad=True)
1632
- >>> self._samples_to_counts(samples)
1633
- {'111':1, '001':2}
1634
- """
1635
- if no_observable_provided :
1636
- # If we describe a state vector, we need to convert its list representation
1637
- # into string (it's hashable and good-looking).
1638
- # Before converting to str, we need to extract elements from arrays
1639
- # to satisfy the case of jax interface, as jax arrays do not support str.
1640
- samples = ["" .join ([str (s .item ()) for s in sample ]) for sample in samples ]
1641
-
1642
- states , counts = np .unique (samples , return_counts = True )
1643
- return dict (zip (states , counts ))
1644
-
1645
1697
# translate to wire labels used by device
1646
1698
device_wires = self .map_wires (observable .wires )
1647
1699
name = observable .name
@@ -1684,16 +1736,16 @@ def _samples_to_counts(samples, no_observable_provided):
1684
1736
f"Cannot compute samples of { observable .name } ."
1685
1737
) from e
1686
1738
1739
+ num_wires = len (device_wires ) if len (device_wires ) > 0 else self .num_wires
1687
1740
if bin_size is None :
1688
1741
if counts :
1689
- return _samples_to_counts (samples , no_observable_provided )
1742
+ return self . _samples_to_counts (samples , observable , num_wires )
1690
1743
return samples
1691
1744
1692
- num_wires = len (device_wires ) if len (device_wires ) > 0 else self .num_wires
1693
1745
if counts :
1694
1746
shape = (- 1 , bin_size , num_wires ) if no_observable_provided else (- 1 , bin_size )
1695
1747
return [
1696
- _samples_to_counts (bin_sample , no_observable_provided )
1748
+ self . _samples_to_counts (bin_sample , observable , num_wires )
1697
1749
for bin_sample in samples .reshape (shape )
1698
1750
]
1699
1751
0 commit comments