mirror of https://github.com/Qiskit/qiskit.git
Update `ProbDistribution` and `QuasiDistribution` to store the number of bits if bitstrings w/o prefix are given (#8464)
* Update ProbDistribution and QuasiDistribution to store data as bit-strings * fix sampler tests * Revert the internal data from bitstrings to integers * Store the information of number of bits when bitstrings w/o prefix, e.g., "00101", are given. * update reno * Revised `num_bits`, comments, and tests. Co-authored-by: Julien Gacon <gaconju@gmail.com> Co-authored-by: Julien Gacon <gaconju@gmail.com> Co-authored-by: Paul Nation <nonhermitian@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
418b7cc802
commit
007cc25bc4
|
@ -45,20 +45,23 @@ class ProbDistribution(dict):
|
|||
ValueError: If the string format of the keys is incorrect
|
||||
"""
|
||||
self.shots = shots
|
||||
self._num_bits = 0
|
||||
if data:
|
||||
first_key = next(iter(data.keys()))
|
||||
if isinstance(first_key, int):
|
||||
pass
|
||||
# `self._num_bits` is not always the exact number of qubits measured,
|
||||
# but the number of bits to represent the largest key.
|
||||
self._num_bits = len(bin(max(data.keys()))) - 2
|
||||
elif isinstance(first_key, str):
|
||||
if first_key.startswith("0x"):
|
||||
hex_raw = data
|
||||
data = {int(key, 0): value for key, value in hex_raw.items()}
|
||||
elif first_key.startswith("0b"):
|
||||
bin_raw = data
|
||||
data = {int(key, 0): value for key, value in bin_raw.items()}
|
||||
if first_key.startswith("0x") or first_key.startswith("0b"):
|
||||
data = {int(key, 0): value for key, value in data.items()}
|
||||
# `self._num_bits` is not always the exact number of qubits measured,
|
||||
# but the number of bits to represent the largest key.
|
||||
self._num_bits = len(bin(max(data.keys()))) - 2
|
||||
elif self._bitstring_regex.search(first_key):
|
||||
bin_raw = data
|
||||
data = {int("0b" + key, 0): value for key, value in bin_raw.items()}
|
||||
# `self._num_bits` is the exact number of qubits measured.
|
||||
self._num_bits = max(len(key) for key in data)
|
||||
data = {int(key, 2): value for key, value in data.items()}
|
||||
else:
|
||||
raise ValueError(
|
||||
"The input keys are not a valid string format, must either "
|
||||
|
@ -74,14 +77,17 @@ class ProbDistribution(dict):
|
|||
|
||||
Parameters:
|
||||
num_bits (int): number of bits in the binary bitstrings (leading
|
||||
zeros will be padded). If None, the length will be derived
|
||||
from the largest key present.
|
||||
zeros will be padded). If None, a default value will be used.
|
||||
If keys are given as integers or strings with binary or hex prefix,
|
||||
the default value will be derived from the largest key present.
|
||||
If keys are given as bitstrings without prefix,
|
||||
the default value will be derived from the largest key length.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary where the keys are binary strings in the format
|
||||
``"0110"``
|
||||
"""
|
||||
n = len(bin(max(self.keys(), default=0))) - 2 if num_bits is None else num_bits
|
||||
n = self._num_bits if num_bits is None else num_bits
|
||||
return {format(key, "b").zfill(n): value for key, value in self.items()}
|
||||
|
||||
def hex_probabilities(self):
|
||||
|
|
|
@ -50,20 +50,23 @@ class QuasiDistribution(dict):
|
|||
"""
|
||||
self.shots = shots
|
||||
self._stddev_upper_bound = stddev_upper_bound
|
||||
self._num_bits = 0
|
||||
if data:
|
||||
first_key = next(iter(data.keys()))
|
||||
if isinstance(first_key, int):
|
||||
pass
|
||||
# `self._num_bits` is not always the exact number of qubits measured,
|
||||
# but the number of bits to represent the largest key.
|
||||
self._num_bits = len(bin(max(data.keys()))) - 2
|
||||
elif isinstance(first_key, str):
|
||||
if first_key.startswith("0x"):
|
||||
hex_raw = data
|
||||
data = {int(key, 0): value for key, value in hex_raw.items()}
|
||||
elif first_key.startswith("0b"):
|
||||
bin_raw = data
|
||||
data = {int(key, 0): value for key, value in bin_raw.items()}
|
||||
if first_key.startswith("0x") or first_key.startswith("0b"):
|
||||
data = {int(key, 0): value for key, value in data.items()}
|
||||
# `self._num_bits` is not always the exact number of qubits measured,
|
||||
# but the number of bits to represent the largest key.
|
||||
self._num_bits = len(bin(max(data.keys()))) - 2
|
||||
elif self._bitstring_regex.search(first_key):
|
||||
bin_raw = data
|
||||
data = {int("0b" + key, 0): value for key, value in bin_raw.items()}
|
||||
# `self._num_bits` is the exact number of qubits measured.
|
||||
self._num_bits = max(len(key) for key in data)
|
||||
data = {int(key, 2): value for key, value in data.items()}
|
||||
else:
|
||||
raise ValueError(
|
||||
"The input keys are not a valid string format, must either "
|
||||
|
@ -112,14 +115,17 @@ class QuasiDistribution(dict):
|
|||
|
||||
Parameters:
|
||||
num_bits (int): number of bits in the binary bitstrings (leading
|
||||
zeros will be padded). If None, the length will be derived
|
||||
from the largest key present.
|
||||
zeros will be padded). If None, a default value will be used.
|
||||
If keys are given as integers or strings with binary or hex prefix,
|
||||
the default value will be derived from the largest key present.
|
||||
If keys are given as bitstrings without prefix,
|
||||
the default value will be derived from the largest key length.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary where the keys are binary strings in the format
|
||||
``"0110"``
|
||||
"""
|
||||
n = len(bin(max(self.keys(), default=0))) - 2 if num_bits is None else num_bits
|
||||
n = self._num_bits if num_bits is None else num_bits
|
||||
return {format(key, "b").zfill(n): value for key, value in self.items()}
|
||||
|
||||
def hex_probabilities(self):
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
Updated :class:`~qiskit.result.ProbDistribution` and :class:`~qiskit.result.QuasiDistribution`
|
||||
to store the information of the number of bits if bitstrings without prefix "0b" are given.
|
||||
:meth:`~qiskit.result.ProbDistribution.binary_probabilities` and
|
||||
:meth:`~qiskit.result.QuasiDistribution.binary_probabilities` use the stored number of bits
|
||||
as the default value of the number of bits.
|
||||
|
||||
.. code-block: python
|
||||
|
||||
import qiskit.result import ProbDistribution, QuasiDistribution
|
||||
|
||||
prob = ProbDistribution({"00": 0.5, "01": 0.5})
|
||||
quasi = QuasiDistribution({"00": 0.5, "01": 0.5})
|
||||
|
||||
print(prob.binary_probabilities())
|
||||
# {'00': 0.5, '01': 0.5}
|
||||
|
||||
print(quasi.binary_probabilities())
|
||||
# {'00': 0.5, '01': 0.5}
|
|
@ -93,6 +93,59 @@ class TestProbDistribution(QiskitTestCase):
|
|||
probs = ProbDistribution(in_probs)
|
||||
self.assertEqual(in_probs, probs.binary_probabilities())
|
||||
|
||||
def test_bin_no_prefix_w_heading_zeros_probs_bin_out(self):
|
||||
"""Test binary input without a 0b prefix with heading 0 and binary output."""
|
||||
in_probs = {"00000": 2 / 7, "00001": 1 / 7, "00010": 1 / 7, "00011": 1 / 7, "00100": 2 / 7}
|
||||
probs = ProbDistribution(in_probs)
|
||||
self.assertEqual(in_probs, probs.binary_probabilities())
|
||||
|
||||
def test_bin_no_prefix_w_diff_heading_zero_probs_bin_out(self):
|
||||
"""Test binary input without a 0b prefix with heading 0 of different sizes and binary output."""
|
||||
in_probs = {
|
||||
"0": 3 / 5,
|
||||
"01": 1 / 2,
|
||||
"10": 7 / 20,
|
||||
"011": 1 / 10,
|
||||
"00100": -11 / 20,
|
||||
}
|
||||
probs = ProbDistribution(in_probs)
|
||||
expected = {
|
||||
"00000": 3 / 5,
|
||||
"00001": 1 / 2,
|
||||
"00010": 7 / 20,
|
||||
"00011": 1 / 10,
|
||||
"00100": -11 / 20,
|
||||
}
|
||||
self.assertEqual(expected, probs.binary_probabilities())
|
||||
|
||||
def test_bin_no_prefix_w_diff_heading_zero_probs_bin_out_padded(self):
|
||||
"""Test binary input without a 0b prefix with heading 0 of different sizes and binary output,
|
||||
padded with zeros."""
|
||||
in_probs = {
|
||||
"0": 3 / 5,
|
||||
"01": 1 / 2,
|
||||
"10": 7 / 20,
|
||||
"011": 1 / 10,
|
||||
"00100": -11 / 20,
|
||||
}
|
||||
probs = ProbDistribution(in_probs)
|
||||
expected = {
|
||||
"0000000": 3 / 5,
|
||||
"0000001": 1 / 2,
|
||||
"0000010": 7 / 20,
|
||||
"0000011": 1 / 10,
|
||||
"0000100": -11 / 20,
|
||||
}
|
||||
self.assertEqual(expected, probs.binary_probabilities(7))
|
||||
|
||||
def test_bin_no_prefix_out_padded(self):
|
||||
"""Test binary input without a 0b prefix, padded with zeros."""
|
||||
n = 5
|
||||
in_probs = {"0": 1}
|
||||
probs = ProbDistribution(in_probs)
|
||||
expected = {"0" * n: 1}
|
||||
self.assertEqual(expected, probs.binary_probabilities(num_bits=n))
|
||||
|
||||
def test_hex_probs_bin_out_padded(self):
|
||||
"""Test hexadecimal input and binary output, padded with zeros."""
|
||||
in_probs = {"0x0": 2 / 7, "0x1": 1 / 7, "0x2": 1 / 7, "0x3": 1 / 7, "0x4": 2 / 7}
|
||||
|
@ -115,6 +168,11 @@ class TestProbDistribution(QiskitTestCase):
|
|||
probs = ProbDistribution({})
|
||||
self.assertEqual(probs.binary_probabilities(), {})
|
||||
|
||||
def test_empty_bin_out_padding(self):
|
||||
"""Test empty input with binary output and padding."""
|
||||
probs = ProbDistribution({})
|
||||
self.assertEqual(probs.binary_probabilities(5), {})
|
||||
|
||||
def test_invalid_keys(self):
|
||||
"""Test invalid key type raises."""
|
||||
with self.assertRaises(TypeError):
|
||||
|
|
|
@ -84,6 +84,65 @@ class TestQuasi(QiskitTestCase):
|
|||
quasi = QuasiDistribution(qprobs)
|
||||
self.assertEqual(qprobs, quasi.binary_probabilities())
|
||||
|
||||
def test_bin_no_prefix_w_heading_zero_quasi_bin_out(self):
|
||||
"""Test binary input without a 0b prefix with heading 0 and binary output."""
|
||||
qprobs = {
|
||||
"00000": 3 / 5,
|
||||
"00001": 1 / 2,
|
||||
"00010": 7 / 20,
|
||||
"00011": 1 / 10,
|
||||
"00100": -11 / 20,
|
||||
}
|
||||
quasi = QuasiDistribution(qprobs)
|
||||
self.assertEqual(qprobs, quasi.binary_probabilities())
|
||||
|
||||
def test_bin_no_prefix_w_diff_heading_zero_quasi_bin_out(self):
|
||||
"""Test binary input without a 0b prefix with heading 0 of different sizes and binary output."""
|
||||
qprobs = {
|
||||
"0": 3 / 5,
|
||||
"01": 1 / 2,
|
||||
"10": 7 / 20,
|
||||
"011": 1 / 10,
|
||||
"00100": -11 / 20,
|
||||
}
|
||||
quasi = QuasiDistribution(qprobs)
|
||||
expected = {
|
||||
"00000": 3 / 5,
|
||||
"00001": 1 / 2,
|
||||
"00010": 7 / 20,
|
||||
"00011": 1 / 10,
|
||||
"00100": -11 / 20,
|
||||
}
|
||||
self.assertEqual(expected, quasi.binary_probabilities())
|
||||
|
||||
def test_bin_no_prefix_w_diff_heading_zero_quasi_bin_out_padded(self):
|
||||
"""Test binary input without a 0b prefix with heading 0 of different sizes and binary output,
|
||||
padded with zeros."""
|
||||
qprobs = {
|
||||
"0": 3 / 5,
|
||||
"01": 1 / 2,
|
||||
"10": 7 / 20,
|
||||
"011": 1 / 10,
|
||||
"00100": -11 / 20,
|
||||
}
|
||||
quasi = QuasiDistribution(qprobs)
|
||||
expected = {
|
||||
"0000000": 3 / 5,
|
||||
"0000001": 1 / 2,
|
||||
"0000010": 7 / 20,
|
||||
"0000011": 1 / 10,
|
||||
"0000100": -11 / 20,
|
||||
}
|
||||
self.assertEqual(expected, quasi.binary_probabilities(7))
|
||||
|
||||
def test_bin_no_prefix_out_padded(self):
|
||||
"""Test binary input without a 0b prefix, padded with zeros."""
|
||||
n = 5
|
||||
qprobs = {"0": 1}
|
||||
quasi = QuasiDistribution(qprobs)
|
||||
expected = {"0" * n: 1}
|
||||
self.assertEqual(expected, quasi.binary_probabilities(num_bits=n))
|
||||
|
||||
def test_hex_quasi_bin_out_padded(self):
|
||||
"""Test hexadecimal input and binary output, padded with zeros."""
|
||||
qprobs = {"0x0": 3 / 5, "0x1": 1 / 2, "0x2": 7 / 20, "0x3": 1 / 10, "0x4": -11 / 20}
|
||||
|
@ -106,6 +165,11 @@ class TestQuasi(QiskitTestCase):
|
|||
quasi = QuasiDistribution({})
|
||||
self.assertEqual(quasi.binary_probabilities(), {})
|
||||
|
||||
def test_empty_bin_out_padding(self):
|
||||
"""Test empty input with binary output and padding."""
|
||||
quasi = QuasiDistribution({})
|
||||
self.assertEqual(quasi.binary_probabilities(5), {})
|
||||
|
||||
def test_invalid_keys(self):
|
||||
"""Test invalid key type raises."""
|
||||
with self.assertRaises(TypeError):
|
||||
|
|
Loading…
Reference in New Issue