From 07a3d7febede0bd7926e8201f00c621fa2ea4f4e Mon Sep 17 00:00:00 2001 From: nate stemen Date: Tue, 6 Aug 2024 09:40:53 -0700 Subject: [PATCH 1/7] set seed to eliminate statistical failure --- mitiq/rem/tests/test_inverse_confusion_matrix.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mitiq/rem/tests/test_inverse_confusion_matrix.py b/mitiq/rem/tests/test_inverse_confusion_matrix.py index bd3371c468..ab38e82b9f 100644 --- a/mitiq/rem/tests/test_inverse_confusion_matrix.py +++ b/mitiq/rem/tests/test_inverse_confusion_matrix.py @@ -29,8 +29,9 @@ def test_sample_probability_vector_single_qubit(): bitstrings = sample_probability_vector(np.array([0, 1]), 10) assert all([b == [1] for b in bitstrings]) + np.random.seed(0) bitstrings = sample_probability_vector(np.array([0.5, 0.5]), 1000) - assert isclose(sum([b[0] for b in bitstrings]), 500, rel_tol=0.1) + assert sum(b[0] for b in bitstrings) == 483 def test_sample_probability_vector_two_qubits(): From e6c24e72c3ea8a562e9c81facec5148b251b0822 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Fri, 9 Aug 2024 00:03:56 -0400 Subject: [PATCH 2/7] parametrize test to run multiple times instead of for loop --- .../tests/test_inverse_confusion_matrix.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/mitiq/rem/tests/test_inverse_confusion_matrix.py b/mitiq/rem/tests/test_inverse_confusion_matrix.py index ab38e82b9f..258dc59cca 100644 --- a/mitiq/rem/tests/test_inverse_confusion_matrix.py +++ b/mitiq/rem/tests/test_inverse_confusion_matrix.py @@ -65,20 +65,20 @@ def test_bitstrings_to_probability_vector(): assert (pv == np.array([0, 0, 0, 1])).all() -def test_probability_vector_roundtrip(): - for _ in range(10): - pv = np.random.rand(4) - pv /= np.sum(pv) - assert isclose( - np.linalg.norm( - pv - - bitstrings_to_probability_vector( - sample_probability_vector(pv, 1000) - ) - ), - 0, - abs_tol=0.1, - ) +@pytest.mark.parametrize("repeats", range(10)) +def test_probability_vector_roundtrip(repeats): + pv = np.random.rand(4) + pv /= np.sum(pv) + assert isclose( + np.linalg.norm( + pv + - bitstrings_to_probability_vector( + sample_probability_vector(pv, 1000) + ) + ), + 0, + abs_tol=0.1, + ) def test_generate_inverse_confusion_matrix(): From fb2ce6243344755e0136e20ab283ce98224a48ca Mon Sep 17 00:00:00 2001 From: nate stemen Date: Fri, 9 Aug 2024 00:04:23 -0400 Subject: [PATCH 3/7] typo --- mitiq/rem/inverse_confusion_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mitiq/rem/inverse_confusion_matrix.py b/mitiq/rem/inverse_confusion_matrix.py index ac52910fd1..f23bc2054f 100644 --- a/mitiq/rem/inverse_confusion_matrix.py +++ b/mitiq/rem/inverse_confusion_matrix.py @@ -60,7 +60,7 @@ def bitstrings_to_probability_vector( bitstrings: All measured bitstrings. Returns: - A probabiity vector corresponding to the measured bitstrings. + A probability vector corresponding to the measured bitstrings. """ pv = np.zeros(2 ** len(bitstrings[0])) for bs in bitstrings: From df71a3998e33175136f3cba6118e567e62d1f156 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Fri, 9 Aug 2024 00:05:37 -0400 Subject: [PATCH 4/7] refactor --- mitiq/rem/inverse_confusion_matrix.py | 35 ++++++++----------- .../tests/test_inverse_confusion_matrix.py | 23 +++++++----- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/mitiq/rem/inverse_confusion_matrix.py b/mitiq/rem/inverse_confusion_matrix.py index f23bc2054f..8ab2fae320 100644 --- a/mitiq/rem/inverse_confusion_matrix.py +++ b/mitiq/rem/inverse_confusion_matrix.py @@ -14,38 +14,31 @@ def sample_probability_vector( - probability_vector: npt.NDArray[np.float64], samples: int -) -> List[Bitstring]: + probability_vector: Sequence[float], samples: int +) -> list[str]: """Generate a number of samples from a probability distribution as bitstrings. Args: probability_vector: A probability vector. + samples: The number of samples to generate. Returns: A list of sampled bitstrings. """ - # sample using the probability distribution given num_values = len(probability_vector) - choices = np.random.choice(num_values, size=samples, p=probability_vector) - - # convert samples to binary strings - bit_width = int(np.log2(num_values)) - binary_repr_vec = np.vectorize(np.binary_repr) - binary_strings = binary_repr_vec(choices, width=bit_width) - - # split the binary strings into an array of ints - bitstrings = ( - np.apply_along_axis( # type: ignore - func1d=np.fromstring, # type: ignore - axis=1, - arr=binary_strings[:, None], - dtype="U1", # type: ignore + if not np.log2(num_values).is_integer(): + raise ValueError( + "The length of the probability vector must be a power of 2." ) - .astype(np.uint8) - .tolist() + + sampled_indices = np.random.choice( + num_values, size=samples, p=probability_vector ) + bit_width = int(np.log2(num_values)) + bitstrings = [format(index, f"0{bit_width}b") for index in sampled_indices] + return bitstrings @@ -132,7 +125,7 @@ def generate_tensored_inverse_confusion_matrix( def closest_positive_distribution( quasi_probabilities: npt.NDArray[np.float64], -) -> npt.NDArray[np.float64]: +) -> List[float]: """Given the input quasi-probability distribution returns the closest positive probability distribution (with respect to the total variation distance). @@ -163,7 +156,7 @@ def distance(probabilities: npt.NDArray[np.float64]) -> np.float64: raise ValueError( "REM failed to determine the closest positive distribution." ) - return result.x + return result.x.tolist() def mitigate_measurements( diff --git a/mitiq/rem/tests/test_inverse_confusion_matrix.py b/mitiq/rem/tests/test_inverse_confusion_matrix.py index 258dc59cca..66fcb9636c 100644 --- a/mitiq/rem/tests/test_inverse_confusion_matrix.py +++ b/mitiq/rem/tests/test_inverse_confusion_matrix.py @@ -22,30 +22,35 @@ ) +def test_sample_probability_vector_invalid_size(): + with pytest.raises(ValueError, match="power of 2"): + sample_probability_vector([1 / 3, 1 / 3, 1 / 3], 3) + + def test_sample_probability_vector_single_qubit(): bitstrings = sample_probability_vector(np.array([1, 0]), 10) - assert all([b == [0] for b in bitstrings]) + assert all(b == "0" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 1]), 10) - assert all([b == [1] for b in bitstrings]) + assert all(b == "1" for b in bitstrings) np.random.seed(0) bitstrings = sample_probability_vector(np.array([0.5, 0.5]), 1000) - assert sum(b[0] for b in bitstrings) == 483 + assert sum(int(b) for b in bitstrings) == 483 def test_sample_probability_vector_two_qubits(): bitstrings = sample_probability_vector(np.array([1, 0, 0, 0]), 10) - assert all([b == [0, 0] for b in bitstrings]) + assert all(b == "00" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 1, 0, 0]), 10) - assert all([b == [0, 1] for b in bitstrings]) + assert all(b == "01" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 0, 1, 0]), 10) - assert all([b == [1, 0] for b in bitstrings]) + assert all(b == "10" for b in bitstrings) bitstrings = sample_probability_vector(np.array([0, 0, 0, 1]), 10) - assert all([b == [1, 1] for b in bitstrings]) + assert all(b == "11" for b in bitstrings) def test_bitstrings_to_probability_vector(): @@ -138,12 +143,12 @@ def test_generate_tensored_inverse_confusion_matrix( num_qubits, confusion_matrices ) else: - assert np.isclose( + assert np.allclose( generate_tensored_inverse_confusion_matrix( num_qubits, confusion_matrices ), expected, - ).all() + ) def test_mitigate_measurements(): From 757070415406383c3eccb40336ff4e5073fc8c86 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Tue, 13 Aug 2024 15:59:00 -0400 Subject: [PATCH 5/7] use underscore for repeat indicator --- mitiq/rem/tests/test_inverse_confusion_matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mitiq/rem/tests/test_inverse_confusion_matrix.py b/mitiq/rem/tests/test_inverse_confusion_matrix.py index 66fcb9636c..7e982469f6 100644 --- a/mitiq/rem/tests/test_inverse_confusion_matrix.py +++ b/mitiq/rem/tests/test_inverse_confusion_matrix.py @@ -70,8 +70,8 @@ def test_bitstrings_to_probability_vector(): assert (pv == np.array([0, 0, 0, 1])).all() -@pytest.mark.parametrize("repeats", range(10)) -def test_probability_vector_roundtrip(repeats): +@pytest.mark.parametrize("_", range(10)) +def test_probability_vector_roundtrip(_): pv = np.random.rand(4) pv /= np.sum(pv) assert isclose( From f8725ebffde52a493b91876057d914f0773fb539 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Tue, 13 Aug 2024 16:04:49 -0400 Subject: [PATCH 6/7] add example of function usage --- mitiq/rem/inverse_confusion_matrix.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mitiq/rem/inverse_confusion_matrix.py b/mitiq/rem/inverse_confusion_matrix.py index 8ab2fae320..b9e125ba88 100644 --- a/mitiq/rem/inverse_confusion_matrix.py +++ b/mitiq/rem/inverse_confusion_matrix.py @@ -25,6 +25,10 @@ def sample_probability_vector( Returns: A list of sampled bitstrings. + + Example: + >>> sample_probability_vector([0, 1/2, 1/4, 1/4], 4) + ['01', '10', '11', '11'] """ num_values = len(probability_vector) if not np.log2(num_values).is_integer(): From 65bef0a63df97a2b1d6b6247df766c5a67a43e97 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Thu, 15 Aug 2024 08:38:25 -0400 Subject: [PATCH 7/7] use list vs List consistently --- mitiq/rem/inverse_confusion_matrix.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mitiq/rem/inverse_confusion_matrix.py b/mitiq/rem/inverse_confusion_matrix.py index b9e125ba88..96e7271558 100644 --- a/mitiq/rem/inverse_confusion_matrix.py +++ b/mitiq/rem/inverse_confusion_matrix.py @@ -4,7 +4,7 @@ # LICENSE file in the root directory of this source tree. from functools import reduce -from typing import List, Sequence +from typing import Sequence import numpy as np import numpy.typing as npt @@ -97,7 +97,7 @@ def generate_inverse_confusion_matrix( def generate_tensored_inverse_confusion_matrix( - num_qubits: int, confusion_matrices: List[npt.NDArray[np.float64]] + num_qubits: int, confusion_matrices: list[npt.NDArray[np.float64]] ) -> npt.NDArray[np.float64]: """ Generates the inverse confusion matrix utilizing the supplied @@ -129,7 +129,7 @@ def generate_tensored_inverse_confusion_matrix( def closest_positive_distribution( quasi_probabilities: npt.NDArray[np.float64], -) -> List[float]: +) -> list[float]: """Given the input quasi-probability distribution returns the closest positive probability distribution (with respect to the total variation distance).