Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1ucian0 Jul 7, 2020
e142763
Merge branch 'master' into add-counts-object
1ucian0 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
2 changes: 2 additions & 0 deletions qiskit/result/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@

Result
ResultError
Counts
"""

from .result import Result
from .exceptions import ResultError
from .utils import marginal_counts
from .counts import Counts
175 changes: 175 additions & 0 deletions qiskit/result/counts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# -*- 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."""

import re

from qiskit.result import postprocess
from qiskit import exceptions


# NOTE: A dict subclass should not overload any dunder methods like __getitem__
# this can cause unexpected behavior and issues as the cPython dict
# implementation has many standard methods in C for performance and the dunder
# methods are not always used as expected. For example, update() doesn't call
# __setitem__ so overloading __setitem__ would not always provide the expected
# result
class Counts(dict):
Comment thread
taalexander marked this conversation as resolved.
"""A class to store a counts result from a circuit execution."""

def __init__(self, data, time_taken=None, creg_sizes=None,
memory_slots=None):
"""Build a counts object

Args:
data (dict): The dictionary input for the counts. Where the keys
represent a measured classical value and the value is an
integer the number of shots with that result.
The keys can be one of several formats:

* A hexademical string of the form ``"0x4a"``
* A bit string prefixed with ``0b`` for example ``'0b1011'``
* A bit string formatted across register and memory slots.
For example, ``'00 10'``.
* A dit string, for example ``'02'``. Note for objects created
with dit strings the ``creg_sizes``and ``memory_slots``
kwargs don't work and :meth:`hex_outcomes` and
:meth:`int_outcomes` also do not work.

time_taken (float): The duration of the experiment that generated
Comment thread
mtreinish marked this conversation as 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.
Raises:
TypeError: If the input key type is not an int or string
QiskitError: If a dit string key is input with creg_sizes and/or
memory_slots
"""
bin_data = None
data = dict(data)
first_key = next(iter(data.keys()))
if isinstance(first_key, int):
self.int_raw = data
self.hex_raw = {
hex(key): value for key, value in self.int_raw.items()}
elif isinstance(first_key, str):
if first_key.startswith('0x'):
self.hex_raw = data
self.int_raw = {
int(key, 0): value for key, value in self.hex_raw.items()}
elif first_key.startswith('0b'):
self.int_raw = {
int(key, 0): value for key, value in data.items()}
self.hex_raw = {
hex(key): value for key, value in self.int_raw.items()}
else:
if not creg_sizes and not memory_slots:
self.hex_raw = None
self.int_raw = None
bin_data = data
else:
bitstring_regex = re.compile(r'^[01\s]+$')
hex_dict = {}
int_dict = {}
for bitstring, value in data.items():
if not bitstring_regex.search(bitstring):
raise exceptions.QiskitError(
'Counts objects with dit strings do not '
'currently support dit string formatting parameters '
'creg_sizes or memory_slots')
int_key = int(bitstring.replace(" ", ""), 2)
int_dict[int_key] = value
hex_dict[hex(int_key)] = value
self.hex_raw = hex_dict
self.int_raw = int_dict
else:
raise TypeError("Invalid input key type %s, must be either an int "
"key or string key with hexademical value or bit string")
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
if not bin_data:
bin_data = postprocess.format_counts(self.hex_raw, header=header)
super().__init__(bin_data)
self.time_taken = time_taken

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

Returns:
str: The bit string for the most frequent result
Raises:
QiskitError: 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.QiskitError(
"Multiple values have the same maximum counts: %s" %
','.join(max_values_counts))
return max_values_counts[0]

def hex_outcomes(self):
"""Return a counts dictionary with hexademical string keys

Returns:
dict: A dictionary with the keys as hexadecimal strings instead of
bitstrings
Raises:
QiskitError: If the Counts object contains counts for dit strings
"""
if self.hex_raw:
return {key.lower(): value for key, value in self.hex_raw.items()}
else:
bitstring_regex = re.compile(r'^[01\s]+$')
out_dict = {}
for bitstring, value in self.items():
if not bitstring_regex.search(bitstring):
raise exceptions.QiskitError(
'Counts objects with dit strings do not '
'currently support conversion to hexadecimal')
int_key = int(bitstring.replace(" ", ""), 2)
out_dict[hex(int_key)] = value
return out_dict

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 bitstrings
Raises:
QiskitError: If the Counts object contains counts for dit strings
"""
if self.int_raw:
return self.int_raw
else:
bitstring_regex = re.compile(r'^[01\s]+$')
out_dict = {}
for bitstring, value in self.items():
if not bitstring_regex.search(bitstring):
raise exceptions.QiskitError(
'Counts objects with dit strings do not '
'currently support conversion to integer')
int_key = int(bitstring.replace(" ", ""), 2)
out_dict[int_key] = value
return out_dict
13 changes: 10 additions & 3 deletions qiskit/result/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
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.result.counts import Counts
from qiskit.qobj.utils import MeasLevel
from qiskit.qobj import QobjHeader

Expand Down Expand Up @@ -242,10 +243,16 @@ def get_counts(self, experiment=None):
header = None

if 'counts' in self.data(key).keys():
dict_list.append(postprocess.format_counts(self.data(key)['counts'], header))
if header:
counts_header = {
k: v for k, v in header.items() if k in {
'time_taken', 'creg_sizes', 'memory_slots'}}
else:
counts_header = {}
dict_list.append(Counts(self.data(key)['counts'], **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
18 changes: 18 additions & 0 deletions releasenotes/notes/add-counts-class-7c75bd94d12a161a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
features:
- |
A new class, :class:`qiskit.result.Counts` has been added. This class
is a subclass of ``dict`` and can be interacted with like any other
dictionary. But, it includes helper methods and attributes for dealing
with counts results from experiments and also handles post processing
and formatting of binary strings at object initialization. A ``Counts``
object can be created by passing a dictionary 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`.
Loading