Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Counts object #4501

Merged
merged 40 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9740b29
Add Counts object
mtreinish May 27, 2020
46254a0
Fix docs build
mtreinish May 27, 2020
57c251c
Fix lint
mtreinish May 27, 2020
10a7b7b
Merge branch 'master' into add-counts-object
mtreinish May 27, 2020
f433cbd
Add missing test file
mtreinish May 27, 2020
b72a2e1
Merge branch 'add-counts-object' of github.com:mtreinish/qiskit-core …
mtreinish May 27, 2020
47d1484
Merge branch 'master' into add-counts-object
mtreinish May 27, 2020
9d0d9af
Merge branch 'master' into add-counts-object
mtreinish May 28, 2020
a4e5ce5
Switch custom exception class to QiskitError
mtreinish May 28, 2020
3a07fc7
Cleanup release note
mtreinish May 28, 2020
0525230
Add comment about dict subclassing
mtreinish May 28, 2020
aea7d97
Merge branch 'add-counts-object' of github.com:mtreinish/qiskit-core …
mtreinish May 28, 2020
442db9f
Tweak comment wording slightly
mtreinish May 28, 2020
4bdd9a4
Merge branch 'master' into add-counts-object
mtreinish May 28, 2020
8c73ea2
Merge branch 'master' into add-counts-object
mtreinish May 28, 2020
f3eaf08
Add support for int and binary input keys and handle qudits
mtreinish Jun 2, 2020
a51dd21
Merge branch 'master' into add-counts-object
mtreinish Jun 2, 2020
c249b64
Fix tests
mtreinish Jun 3, 2020
0612298
Fix lint
mtreinish Jun 3, 2020
5a09207
Handle 0b prefixed binary strings
mtreinish Jun 3, 2020
e0c1e8c
Update init docs
mtreinish Jun 3, 2020
3acb00b
Fix docs again
mtreinish Jun 3, 2020
c870623
Add tests for 0b prefixed input
mtreinish Jun 3, 2020
b299e2e
Merge branch 'master' into add-counts-object
mtreinish Jun 9, 2020
07b0397
Merge branch 'master' into add-counts-object
mtreinish Jun 12, 2020
f0ffda3
Merge branch 'master' into add-counts-object
mtreinish Jun 16, 2020
c1d7860
Remove metadata from Counts object
mtreinish Jun 16, 2020
b6a5e05
Use Counts instead of dict for Results.get_counts() output
mtreinish Jun 16, 2020
f53cede
Merge branch 'master' into add-counts-object
mtreinish Jun 16, 2020
91cf8fb
Remove other circuit level metadata
mtreinish Jun 29, 2020
c3f1508
Merge branch 'master' into add-counts-object
mtreinish Jun 29, 2020
dc1efdc
Update results to not include removed metadata
mtreinish Jun 29, 2020
6c95b65
Fix tests
mtreinish Jun 29, 2020
791c1e7
Really remove metadata from class
mtreinish Jun 29, 2020
cef4a2a
Merge branch 'master' into add-counts-object
mtreinish Jun 30, 2020
96b1672
Merge branch 'master' into add-counts-object
mtreinish Jun 30, 2020
03da057
Merge branch 'master' into add-counts-object
mtreinish Jun 30, 2020
5b0c74c
Merge branch 'master' into add-counts-object
Jul 7, 2020
e142763
Merge branch 'master' into add-counts-object
Jul 7, 2020
31009d2
Merge branch 'master' into add-counts-object
mergify[bot] Jul 7, 2020
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
5 changes: 3 additions & 2 deletions qiskit/quantum_info/states/quantum_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from qiskit.quantum_info.operators.base_operator import BaseOperator
from qiskit.quantum_info.operators.operator import Operator
from qiskit.quantum_info.operators.predicates import ATOL_DEFAULT, RTOL_DEFAULT
from qiskit.result.counts import Counts


class QuantumState(ABC):
Expand Down Expand Up @@ -383,7 +384,7 @@ def sample_counts(self, shots, qargs=None):
subsystems (Default: None).

Returns:
dict: sampled counts dictionary.
Counts: sampled counts dictionary.

Additional Information:

Expand All @@ -400,7 +401,7 @@ def sample_counts(self, shots, qargs=None):

# Combine all samples into a counts dictionary
inds, counts = np.unique(samples, return_counts=True)
return dict(zip(inds, counts))
return Counts(zip(inds, counts))

def measure(self, qargs=None):
"""Measure subsystems and return outcome and post-measure state.
Expand Down
4 changes: 4 additions & 0 deletions qiskit/result/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@

Result
ResultError
Counts
NoMostFrequentCount
"""

from .result import Result
from .exceptions import ResultError
from .utils import marginal_counts
from .counts import Counts
from .exceptions import NoMostFrequentCount
83 changes: 83 additions & 0 deletions qiskit/result/counts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""A container class for counts from a circuit execution."""

from qiskit.result import postprocess
from qiskit.result import exceptions


class Counts(dict):
taalexander marked this conversation as resolved.
Show resolved Hide resolved
"""A class to store a counts result from a circuit execution."""

def __init__(self, data, name=None, shots=None, time_taken=None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we storing a lot of extra metadata in this container? There is already an ExperimentResult class for that so it seems to me Counts should just be the counts part of the result.

If I want to add additional return data types in the future (which I do!) like ExpectationValue, or Probabilities, there will be a lot of duplication of unneeded metadat.

In this case I think the only the counts needs is perhaps the creg_sizes so you can add whitespace to bitstrings during conversion, and memory_slots if you don't have creg_sizes so you know number of bits when converting from hex-format. Shots can be inferred from sum of dict elements, and time taken, name, and metadata seem unnecessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The metadata here is used in the v2 providers interface see #4485. In that model the results object will return an OrderedDict of Counts objects instead of a of ExperimentResult objects. So the metadata for an individual run will be stored here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I have issues with the V2 provider then, since it will not be suitable for simulators if there is no circuit level result object other than counts that can store different output data. I can leave those comments somewhere else.

creg_sizes=None, memory_slots=None, **metadata):
"""Build a counts object

Args:
data (dict): The dictionary input for the counts. The key should
be a hexademical string of the form ``"0x4a"`` representing the
measured classical value from the experiment and the
dictionary's value is an integer representing the number of
shots with that result.
name (str): A string name for the counts object
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
shots (int): The number of shots used in the experiment
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
time_taken (float): The duration of the experiment that generated
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
the counts
creg_sizes (list): a nested list where the inner element is a list
of tuples containing both the classical register name and
classical register size. For example,
``[('c_reg', 2), ('my_creg', 4)]``.
memory_slots (int): The number of total ``memory_slots`` in the
experiment.
metadata: Any arbitrary key value metadata passed in as kwargs.
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
"""
self.hex_raw = dict(data)
header = {}
self.creg_sizes = creg_sizes
if self.creg_sizes:
header['creg_sizes'] = self.creg_sizes
self.memory_slots = memory_slots
if self.memory_slots:
header['memory_slots'] = self.memory_slots
bin_data = postprocess.format_counts(self.hex_raw, header=header)
super().__init__(bin_data)
self.name = name
self.shots = shots
self.time_taken = time_taken
self.metadata = metadata

def most_frequent(self):
"""Return the most frequent count

Returns:
str: The bit string for the most frequent result
Raises:
NoMostFrequentCount: when there is >1 count with the same max counts
"""
max_value = max(self.values())
max_values_counts = [x[0] for x in self.items() if x[1] == max_value]
if len(max_values_counts) != 1:
raise exceptions.NoMostFrequentCount(
"Multiple values have the same maximum counts: %s" %
','.join(max_values_counts))
return max_values_counts[0]

def int_outcomes(self):
"""Build a counts dictionary with integer keys instead of count strings

Returns:
dict: A dictionary with the keys as integers instead of
"""
return {int(key, 0): value for key, value in self.hex_raw.items()}
5 changes: 5 additions & 0 deletions qiskit/result/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ def __init__(self, error):

def __str__(self):
return '{}: {}'.format(self.code, self.message)


class NoMostFrequentCount(QiskitError):
"""Error raised by Counts.most_frequent if there is no most frequent result."""
pass
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions qiskit/result/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.pulse.schedule import Schedule
from qiskit.exceptions import QiskitError
from qiskit.quantum_info.states import Statevector
from qiskit.quantum_info.states import statevector
from qiskit.result.models import ExperimentResult
from qiskit.result import postprocess
from qiskit.qobj.utils import MeasLevel
Expand Down Expand Up @@ -245,7 +245,7 @@ def get_counts(self, experiment=None):
dict_list.append(postprocess.format_counts(self.data(key)['counts'], header))
elif 'statevector' in self.data(key).keys():
vec = postprocess.format_statevector(self.data(key)['statevector'])
dict_list.append(Statevector(vec).probabilities_dict(decimals=15))
dict_list.append(statevector.Statevector(vec).probabilities_dict(decimals=15))
else:
raise QiskitError('No counts for experiment "{0}"'.format(key))

Expand Down
14 changes: 14 additions & 0 deletions releasenotes/notes/add-counts-class-7c75bd94d12a161a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
features:
- |
A new class, :class:`qiskit.result.Counts` has been added. This class
is a subclass of ``dict`` and . It is created by passing a dictionary
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
of counts with the keys being hexademical strings of the form ``'0x4a'``
and the value is the number of shots. For example::

from qiskit.result import Counts

counts = Counts({"0x0': 1, '0x1', 3, '0x2': 1020})

A Counts object can be treated as a dictionary of binary string keys
like the output of :meth:`qiskit.result.Result.get_counts`.