Skip to content

Commit

Permalink
Fix padding of zeros in keys of quasi-distribution and probabilities (#…
Browse files Browse the repository at this point in the history
…6554)

* reno

* black

* review comments

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Matthew Treinish <[email protected]>
  • Loading branch information
3 people committed Jun 11, 2021
1 parent 4b27c63 commit d209806
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 20 deletions.
17 changes: 11 additions & 6 deletions qiskit/result/distributions/probability.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
class ProbDistribution(dict):
"""A generic dict-like class for probability distributions."""

bitstring_regex = re.compile(r"^[01]+$")
_bitstring_regex = re.compile(r"^[01]+$")

def __init__(self, data, shots=None):
"""Builds a probability distribution object.
Expand All @@ -34,8 +34,7 @@ def __init__(self, data, shots=None):
The keys can be one of several formats:
* A hexadecimal string of the form ``"0x4a"``
* A bit string prefixed with ``0b`` for example
``'0b1011'``
* A bit string e.g. ``'0b1011'`` or ``"01011"``
* An integer
shots (int): Number of shots the distribution was derived from.
Expand All @@ -56,7 +55,7 @@ def __init__(self, data, shots=None):
elif first_key.startswith("0b"):
bin_raw = data
data = {int(key, 0): value for key, value in bin_raw.items()}
elif self.bitstring_regex.search(first_key):
elif self._bitstring_regex.search(first_key):
bin_raw = data
data = {int("0b" + key, 0): value for key, value in bin_raw.items()}
else:
Expand All @@ -69,14 +68,20 @@ def __init__(self, data, shots=None):
raise TypeError("Input data's keys are of invalid type, must be str or int")
super().__init__(data)

def binary_probabilities(self):
def binary_probabilities(self, num_bits=None):
"""Build a probabilities dictionary with binary string keys
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.
Returns:
dict: A dictionary where the keys are binary strings in the format
``"0110"``
"""
return {bin(key)[2:]: value for key, value in self.items()}
n = len(bin(max(self.keys(), default=0))) - 2 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):
"""Build a probabilities dictionary with hexadecimal string keys
Expand Down
21 changes: 13 additions & 8 deletions qiskit/result/distributions/quasi.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
class QuasiDistribution(dict):
"""A dict-like class for representing qasi-probabilities."""

bitstring_regex = re.compile(r"^[01]+$")
_bitstring_regex = re.compile(r"^[01]+$")

def __init__(self, data, shots=None):
"""Builds a quasiprobability distribution object.
Expand All @@ -38,8 +38,7 @@ def __init__(self, data, shots=None):
The keys can be one of several formats:
* A hexadecimal string of the form ``"0x4a"``
* A bit string prefixed with ``0b`` for example
``'0b1011'``
* A bit string e.g. ``'0b1011'`` or ``"01011"``
* An integer
shots (int): Number of shots the distribution was derived from.
Expand All @@ -60,7 +59,7 @@ def __init__(self, data, shots=None):
elif first_key.startswith("0b"):
bin_raw = data
data = {int(key, 0): value for key, value in bin_raw.items()}
elif self.bitstring_regex.search(first_key):
elif self._bitstring_regex.search(first_key):
bin_raw = data
data = {int("0b" + key, 0): value for key, value in bin_raw.items()}
else:
Expand Down Expand Up @@ -106,17 +105,23 @@ def nearest_probability_distribution(self, return_distance=False):
return ProbDistribution(new_probs, self.shots), sqrt(diff)
return ProbDistribution(new_probs, self.shots)

def binary_probabilities(self):
"""Build a probabilities dictionary with binary string keys
def binary_probabilities(self, num_bits=None):
"""Build a quasi-probabilities dictionary with binary string keys
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.
Returns:
dict: A dictionary where the keys are binary strings in the format
``"0110"``
"""
return {bin(key)[2:]: value for key, value in self.items()}
n = len(bin(max(self.keys(), default=0))) - 2 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):
"""Build a probabilities dictionary with hexadecimal string keys
"""Build a quasi-probabilities dictionary with hexadecimal string keys
Returns:
dict: A dictionary where the keys are hexadecimal strings in the
Expand Down
27 changes: 24 additions & 3 deletions test/python/result/test_probability.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ def test_bin_probs(self):
expected = {0: 2 / 7, 1: 1 / 7, 2: 1 / 7, 3: 1 / 7, 4: 2 / 7}
self.assertEqual(expected, probs)

def test_bin_probs_no_0b(self):
"""Test binary input without 0b in front."""
in_probs = {"000": 2 / 7, "001": 1 / 7, "010": 1 / 7, "011": 1 / 7, "100": 2 / 7}
probs = ProbDistribution(in_probs)
expected = {0: 2 / 7, 1: 1 / 7, 2: 1 / 7, 3: 1 / 7, 4: 2 / 7}
self.assertEqual(expected, probs)

def test_bin_probs2(self):
"""Test binary input."""
in_probs = {"000": 2 / 7, "001": 1 / 7, "010": 1 / 7, "011": 1 / 7, "100": 2 / 7}
probs = ProbDistribution(in_probs)
expected = {0: 2 / 7, 1: 1 / 7, 2: 1 / 7, 3: 1 / 7, 4: 2 / 7}
self.assertEqual(expected, probs)

def test_bin_no_prefix_probs(self):
"""Test binary input without 0b prefix."""
in_probs = {"0": 2 / 7, "1": 1 / 7, "10": 1 / 7, "11": 1 / 7, "100": 2 / 7}
Expand Down Expand Up @@ -63,22 +77,29 @@ def test_hex_probs_bin_out(self):
"""Test hexadecimal input and binary output."""
in_probs = {"0x0": 2 / 7, "0x1": 1 / 7, "0x2": 1 / 7, "0x3": 1 / 7, "0x4": 2 / 7}
probs = ProbDistribution(in_probs)
expected = {"0": 2 / 7, "1": 1 / 7, "10": 1 / 7, "11": 1 / 7, "100": 2 / 7}
expected = {"000": 2 / 7, "001": 1 / 7, "010": 1 / 7, "011": 1 / 7, "100": 2 / 7}
self.assertEqual(expected, probs.binary_probabilities())

def test_bin_probs_bin_out(self):
"""Test binary input and binary output."""
in_probs = {"0b0": 2 / 7, "0b1": 1 / 7, "0b10": 1 / 7, "0b11": 1 / 7, "0b100": 2 / 7}
probs = ProbDistribution(in_probs)
expected = {"0": 2 / 7, "1": 1 / 7, "10": 1 / 7, "11": 1 / 7, "100": 2 / 7}
expected = {"000": 2 / 7, "001": 1 / 7, "010": 1 / 7, "011": 1 / 7, "100": 2 / 7}
self.assertEqual(expected, probs.binary_probabilities())

def test_bin_no_prefix_probs_bin_out(self):
"""Test binary input without a 0b prefix and binary output."""
in_probs = {"0": 2 / 7, "1": 1 / 7, "10": 1 / 7, "11": 1 / 7, "100": 2 / 7}
in_probs = {"000": 2 / 7, "001": 1 / 7, "010": 1 / 7, "011": 1 / 7, "100": 2 / 7}
probs = ProbDistribution(in_probs)
self.assertEqual(in_probs, probs.binary_probabilities())

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}
probs = ProbDistribution(in_probs)
expected = {"0000": 2 / 7, "0001": 1 / 7, "0010": 1 / 7, "0011": 1 / 7, "0100": 2 / 7}
self.assertEqual(expected, probs.binary_probabilities(num_bits=4))

def test_empty(self):
"""Test empty input."""
probs = ProbDistribution({})
Expand Down
19 changes: 16 additions & 3 deletions test/python/result/test_quasi.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ def test_bin_quasi(self):
quasi = QuasiDistribution(qprobs)
self.assertEqual({0: 3 / 5, 1: 1 / 2, 2: 7 / 20, 3: 1 / 10, 4: -11 / 20}, quasi)

def test_bin_quasi_no_0b(self):
"""Test binary input without 0b in front."""
qprobs = {"000": 3 / 5, "001": 1 / 2, "010": 7 / 20, "011": 1 / 10, "100": -11 / 20}
quasi = QuasiDistribution(qprobs)
self.assertEqual({0: 3 / 5, 1: 1 / 2, 2: 7 / 20, 3: 1 / 10, 4: -11 / 20}, quasi)

def test_bin_no_prefix_quasi(self):
"""Test binary input without 0b prefix."""
qprobs = {"0": 3 / 5, "1": 1 / 2, "10": 7 / 20, "11": 1 / 10, "100": -11 / 20}
Expand Down Expand Up @@ -62,22 +68,29 @@ def test_hex_quasi_bin_out(self):
"""Test hexadecimal input and binary output."""
qprobs = {"0x0": 3 / 5, "0x1": 1 / 2, "0x2": 7 / 20, "0x3": 1 / 10, "0x4": -11 / 20}
quasi = QuasiDistribution(qprobs)
expected = {"0": 3 / 5, "1": 1 / 2, "10": 7 / 20, "11": 1 / 10, "100": -11 / 20}
expected = {"000": 3 / 5, "001": 1 / 2, "010": 7 / 20, "011": 1 / 10, "100": -11 / 20}
self.assertEqual(expected, quasi.binary_probabilities())

def test_bin_quasi_bin_out(self):
"""Test binary input and binary output."""
qprobs = {"0b0": 3 / 5, "0b1": 1 / 2, "0b10": 7 / 20, "0b11": 1 / 10, "0b100": -11 / 20}
quasi = QuasiDistribution(qprobs)
expected = {"0": 3 / 5, "1": 1 / 2, "10": 7 / 20, "11": 1 / 10, "100": -11 / 20}
expected = {"000": 3 / 5, "001": 1 / 2, "010": 7 / 20, "011": 1 / 10, "100": -11 / 20}
self.assertEqual(expected, quasi.binary_probabilities())

def test_bin_no_prefix_quasi_bin_out(self):
"""Test binary input without a 0b prefix and binary output."""
qprobs = {"0": 3 / 5, "1": 1 / 2, "10": 7 / 20, "11": 1 / 10, "100": -11 / 20}
qprobs = {"000": 3 / 5, "001": 1 / 2, "010": 7 / 20, "011": 1 / 10, "100": -11 / 20}
quasi = QuasiDistribution(qprobs)
self.assertEqual(qprobs, quasi.binary_probabilities())

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}
quasi = QuasiDistribution(qprobs)
expected = {"0000": 3 / 5, "0001": 1 / 2, "0010": 7 / 20, "0011": 1 / 10, "0100": -11 / 20}
self.assertEqual(expected, quasi.binary_probabilities(num_bits=4))

def test_empty(self):
"""Test empty input."""
quasi = QuasiDistribution({})
Expand Down

0 comments on commit d209806

Please sign in to comment.