Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
c4c1fb5
counts of raw samples
Jun 10, 2022
d353fdc
polish
Jun 10, 2022
0e801e8
polish
Jun 10, 2022
76ad5e1
polish
Jun 10, 2022
bbf4743
polishing and adapting to pep8
Jun 11, 2022
bd07da0
unit test for Counts measurement type
Jun 11, 2022
02623a2
merging with the current version
Jun 11, 2022
e95eac2
added the counts into the documentation
Jun 11, 2022
c23dd95
added changelog
Jun 11, 2022
907fbd0
black formatting done
Jun 11, 2022
2a0705a
docs corrected
Jun 11, 2022
f1a838d
add my name to contributors
Jun 13, 2022
13d7cc3
merge
Jun 14, 2022
fce7090
test: black formatting
Jun 14, 2022
9d8c7d5
merge doc
Jun 14, 2022
6a23634
Merge branch 'master' into master
theodotk Jun 15, 2022
a322d0b
Merge branch 'master' into master
Jaybsoni Jun 16, 2022
0fa4b4f
Update doc/introduction/measurements.rst
theodotk Jun 16, 2022
a1c6c04
Update pennylane/measurements.py
theodotk Jun 16, 2022
51cb59b
added example to changelog
Jun 16, 2022
a4b6b29
del trailing white space
Jun 16, 2022
99733c5
changelog: better formulation
theodotk Jun 17, 2022
640dc71
changelog: another better formulation
Jun 17, 2022
156ccc2
Merge branch 'master' into master
theodotk Jun 17, 2022
e731c61
Update doc/introduction/measurements.rst
theodotk Jun 20, 2022
5738847
doc/introduction/measurements.rst: sample counts
theodotk Jun 20, 2022
251185b
doc/introduction/measurements.rst: sample counts
theodotk Jun 20, 2022
c00665d
doc/releases/changelog-dev.md: sample counts description improved
theodotk Jun 20, 2022
b97fbf4
doc/releases/changelog-dev.md: sample counts desc improved
theodotk Jun 20, 2022
ab08132
pennylane/measurements.py: sample counts docstring rephrased
theodotk Jun 20, 2022
97adf95
doc/releases/changelog-dev.md: sample counts desc improved
theodotk Jun 20, 2022
a7261d8
doc/releases/changelog-dev.md: sample counts desc improved
theodotk Jun 20, 2022
d63d7a9
pennylane/_qubit_device.py: sample counts docstring formatting
theodotk Jun 20, 2022
de4b384
pennylane/_qubit_device.py: sample counts docstring formatting
theodotk Jun 20, 2022
db29a2f
measurements.py sample counts docstring
Jun 21, 2022
85cd604
Merge remote-tracking branch 'upstream/master'
Jun 21, 2022
0bcd931
no shape for sample measurement type
Jun 21, 2022
51104cb
formatting
Jun 21, 2022
b9dc8ef
break long assertion
Jun 21, 2022
9f20cff
adapt tests to new return types
Jun 21, 2022
2a39713
Merge branch 'PennyLaneAI:master' into master
theodotk Jun 21, 2022
6059d29
Merge branch 'master' of https://github.com/theodotk/pennylane
Jun 21, 2022
092226d
upd docs: sample counts return objects
Jun 21, 2022
fbd3d00
Merge branch 'master' into master
theodotk Jun 21, 2022
65f0761
sample counts by bins
Jun 21, 2022
8596571
Update doc/introduction/measurements.rst: sample counts
theodotk Jun 21, 2022
2a5aa6a
Update pennylane/measurements.py: docstring
theodotk Jun 21, 2022
9b74afd
test binned counts and more general implementation
Jun 22, 2022
26da159
doc/introduction/measurements.rst: sample counts
theodotk Jun 21, 2022
be2ed9f
formatting
Jun 22, 2022
4b726f6
Merge branch 'master' into master
theodotk Jun 22, 2022
a1921c4
shot vector support improved for sample counts
Jun 22, 2022
15f5bf9
Merge branch 'master' of https://github.com/theodotk/pennylane
Jun 22, 2022
97ace52
add unit test for sample shot vector
Jun 22, 2022
bb74216
Merge branch 'master' into master
theodotk Jun 22, 2022
a552e01
Merge branch 'master' into master
theodotk Jun 23, 2022
80fcd81
Merge branch 'master' into master
antalszava Jun 23, 2022
ea15d23
pennylane/_qubit_device.py: add comment
theodotk Jun 23, 2022
41dde26
Merge branch 'master' into master
antalszava Jun 23, 2022
b3fb571
adapt to jax
Jun 24, 2022
5c70231
Merge branch 'master' into master
theodotk Jun 24, 2022
3c996c9
satisfying pylint
Jun 24, 2022
892ff0f
Merge branch 'master' into master
theodotk Jun 24, 2022
d7ce0c8
pennylane/_qubit_device.py sample counts comment expanded
Jun 24, 2022
7632b02
Merge branch 'master' into master
theodotk Jun 24, 2022
da2887e
sample counts test interfaces
Jun 24, 2022
7aba9cf
polishing
Jun 24, 2022
0b8da69
Merge branch 'master' of https://github.com/theodotk/pennylane
Jun 24, 2022
ec43d77
sample counts test interfaces shot vec
Jun 24, 2022
6ba6928
Merge branch 'master' into master
theodotk Jun 24, 2022
0fb203b
docstrings; test case names
antalszava Jun 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion doc/introduction/measurements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ the measurement results always coincide, and the lists are therefore equal:
>>> np.all(result[0] == result[1])
True


Tensor observables
------------------

Expand All @@ -125,6 +124,50 @@ accept observables as arguments,
including :func:`~.pennylane.expval`, :func:`~.pennylane.var`,
and :func:`~.pennylane.sample`.

Counts
------

To avoid dealing with long arrays for the larger numbers of shots, one can pass an argument counts=True
to :func:`~pennylane.sample`. In this case the result will be a dictionnary indicating numbers of occurences for each
unique sample. The previous example will be modified as follows:

.. code-block:: python

dev = qml.device("default.qubit", wires=2, shots=1000)

@qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
# passing the counts flag
return qml.sample(qml.PauliZ(0), counts=True), qml.sample(qml.PauliZ(1), counts=True)

After executing the circuit we can directly see how often each value was measured:

>>> result = circuit()
>>> print(result)
[tensor({-1: 526, 1: 474}, dtype=object, requires_grad=True)
tensor({-1: 526, 1: 474}, dtype=object, requires_grad=True)]

Similarly, if the observable is not provided, the occurencies of each state are returned.

.. code-block:: python

dev = qml.device("default.qubit", wires=2, shots=1000)

@qml.qnode(dev)
def circuit():
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
# passing the counts flag
return qml.sample(counts=True)

And the result is:

>>> result = circuit()
>>> print(result)
{'00': 495, '11': 505}

Probability
-----------

Expand Down
38 changes: 37 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,43 @@
[(#2709)](https://github.com/PennyLaneAI/pennylane/pull/2709)

<h3>Improvements</h3>

* Added the possibility to group samples into counts providing `counts=True` flag in `qml.sample` function. This required
creation of a new measurement type `Counts` in `measurements.py`.
[(#2686)](https://github.com/PennyLaneAI/pennylane/pull/2686)

It can be used with states:

```pycon
>>> dev = qml.device("default.qubit", wires=2, shots=1000)

>>> @qml.qnode(dev)
>>> def circuit():
... qml.Hadamard(wires=0)
... qml.CNOT(wires=[0, 1])
... # passing the counts flag
... return qml.sample(counts=True)
>>> result = circuit()
>>> print(result)
{'00': 495, '11': 505}
```

And with operators:

```pycon
>>> dev = qml.device("default.qubit", wires=2, shots=1000)

>>> @qml.qnode(dev)
>>> def circuit():
... qml.Hadamard(wires=0)
... qml.CNOT(wires=[0, 1])
... return qml.sample(qml.PauliZ(0), counts=True), qml.sample(qml.PauliZ(1), counts=True)
>>> result = circuit()
>>> print(result)
[tensor({-1: 526, 1: 474}, dtype=object, requires_grad=True)
tensor({-1: 526, 1: 474}, dtype=object, requires_grad=True)]
```

* Adds a new function to compare operators. `qml.equal` can be used to compare equality of parametric operators taking into account their interfaces and trainability.
[(#2651)](https://github.com/PennyLaneAI/pennylane/pull/2651)

Expand All @@ -58,4 +94,4 @@

This release contains contributions from (in alphabetical order):

Ankit Khandelwal, Ixchel Meza Chavez, Moritz Willmann
Ankit Khandelwal, Bogdan Reznychenko, Ixchel Meza Chavez, Moritz Willmann
37 changes: 35 additions & 2 deletions pennylane/_qubit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
from pennylane.operation import operation_derivative
from pennylane.measurements import (
Sample,
Counts,
Variance,
Expectation,
Probability,
State,
VnEntropy,
MutualInfo,
)

from pennylane import Device
from pennylane.math import sum as qmlsum
from pennylane.math import multiply as qmlmul
Expand Down Expand Up @@ -301,6 +303,8 @@ def execute(self, circuit, **kwargs):
if circuit.measurements[0].return_type is qml.measurements.State:
# State: assumed to only be allowed if it's the only measurement
results = self._asarray(results, dtype=self.C_DTYPE)
elif circuit.measurements[0].return_type is qml.measurements.Counts:
results = self._asarray(results)
else:
# Measurements with expval, var or probs
results = self._asarray(results, dtype=self.R_DTYPE)
Expand Down Expand Up @@ -475,7 +479,14 @@ def statistics(self, observables, shot_range=None, bin_size=None):
results.append(self.var(obs, shot_range=shot_range, bin_size=bin_size))

elif obs.return_type is Sample:
results.append(self.sample(obs, shot_range=shot_range, bin_size=bin_size))
results.append(
self.sample(obs, shot_range=shot_range, bin_size=bin_size, counts=False)
)

elif obs.return_type is Counts:
results.append(
self.sample(obs, shot_range=shot_range, bin_size=bin_size, counts=True)
)

elif obs.return_type is Probability:
results.append(
Expand Down Expand Up @@ -960,7 +971,27 @@ def var(self, observable, shot_range=None, bin_size=None):
samples = self.sample(observable, shot_range=shot_range, bin_size=bin_size)
return np.squeeze(np.var(samples, axis=0))

def sample(self, observable, shot_range=None, bin_size=None):
def _samples_to_counts(self, samples):
"""Group the obtained samples into a dictionary.

Example:
>>> samples
tensor([[0, 0, 1],
[0, 0, 1],
[1, 1, 1]], requires_grad=True)
>>> self._samples_to_counts(samples)
{'111':1, '001':2}
"""
try:
# Express the states as strings
samples = ["".join(self._asarray(sample, dtype=str)) for sample in samples]
except TypeError:
# Evaluation of an observable
pass
states, counts = np.unique(samples, return_counts=True)
return dict(zip(states, counts))

def sample(self, observable, shot_range=None, bin_size=None, counts=False):

# translate to wire labels used by device
device_wires = self.map_wires(observable.wires)
Expand Down Expand Up @@ -1000,6 +1031,8 @@ def sample(self, observable, shot_range=None, bin_size=None):
) from e

if bin_size is None:
if counts:
return self._samples_to_counts(samples)
return samples

return samples.reshape((bin_size, -1))
Expand Down
17 changes: 12 additions & 5 deletions pennylane/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class ObservableReturnTypes(Enum):
"""Enumeration class to represent the return types of an observable."""

Sample = "sample"
Counts = "counts"
Variance = "var"
Expectation = "expval"
Probability = "probs"
Expand All @@ -54,6 +55,10 @@ def __repr__(self):
Sample = ObservableReturnTypes.Sample
"""Enum: An enumeration which represents sampling an observable."""

Counts = ObservableReturnTypes.Counts
"""Enum: An enumeration which represents returning the number of times
each sample was obtained."""

Variance = ObservableReturnTypes.Variance
"""Enum: An enumeration which represents returning the variance of
an observable on specified wires."""
Expand Down Expand Up @@ -236,7 +241,7 @@ def shape(self, device=None):
return shape

# Then: handle return types that require a device; no shot vector
if device is None and self.return_type in (Probability, State, Sample):
if device is None and self.return_type in (Probability, State, Sample, Counts):
raise MeasurementShapeError(
f"The device argument is required to obtain the shape of the measurement process; got return type {self.return_type}."
)
Expand Down Expand Up @@ -578,7 +583,7 @@ def circuit(x):
return MeasurementProcess(Variance, obs=op)


def sample(op=None, wires=None):
def sample(op=None, wires=None, counts=False):
r"""Sample from the supplied observable, with the number of shots
determined from the ``dev.shots`` attribute of the corresponding device.
If no observable is provided then basis state samples are returned directly
Expand All @@ -590,6 +595,7 @@ def sample(op=None, wires=None):
Args:
op (Observable or None): a quantum observable object
wires (Sequence[int] or int or None): the wires we wish to sample from, ONLY set wires if op is None
counts (bool): return the result as numbers of counts

Raises:
QuantumFunctionError: `op` is not an instance of :class:`~.Observable`
Expand Down Expand Up @@ -655,16 +661,17 @@ def circuit(x):
f"{op.name} is not an observable: cannot be used with sample"
)

sample_or_counts = Counts if counts else Sample

if wires is not None:
if op is not None:
raise ValueError(
"Cannot specify the wires to sample if an observable is "
"provided. The wires to sample will be determined directly from the observable."
)
return MeasurementProcess(sample_or_counts, obs=op, wires=qml.wires.Wires(wires))

return MeasurementProcess(Sample, obs=op, wires=qml.wires.Wires(wires))

return MeasurementProcess(Sample, obs=op)
return MeasurementProcess(sample_or_counts, obs=op)


def probs(wires=None, op=None):
Expand Down
Loading