From 6f815a1f042cad496d0fcbdd1e3f0950169a2df0 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 10 May 2023 21:20:29 -0400 Subject: [PATCH] Use marginal_distribution() instead of marginal_counts() (#1144) ### Summary This commit updates the Qiskit marginalization function used in qiskit-experiments. Qiskit has 2 different function available for marginalizing counts: `marginal_counts()` [1] which is designed to work with `Counts` or `Result` objects and does **not** respect ordering of the indices passed in. The other is `marginal_distribution()` [2] which is designed to work with `Counts`, `Probabilities`, `QuasiProbabilities`, or a dictionary with string keys and integer or float values that respects the order of the indices passed in (so it can be used to permute the bits). In general if you're not working with a `Result` object it is better to use `marginal_distribution()` because it is written primarily in Rust and 10-100x faster than `marginal_counts()`. ### Details and comments [1] https://qiskit.org/documentation/stubs/qiskit.result.marginal_counts.html [2] https://qiskit.org/documentation/stubs/qiskit.result.marginal_distribution.html (cherry picked from commit 10b61828a28f288b520721694d8c4b586a10742b) --- .../framework/composite/composite_analysis.py | 10 ++++++++-- .../analysis/local_readout_error_analysis.py | 4 ++-- .../library/tomography/fitters/fitter_data.py | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/qiskit_experiments/framework/composite/composite_analysis.py b/qiskit_experiments/framework/composite/composite_analysis.py index e07e606712..eaeedbb8b1 100644 --- a/qiskit_experiments/framework/composite/composite_analysis.py +++ b/qiskit_experiments/framework/composite/composite_analysis.py @@ -15,7 +15,7 @@ from typing import List, Dict, Union, Optional, Tuple import numpy as np -from qiskit.result import marginal_counts +from qiskit.result import marginal_distribution from qiskit.result.postprocess import format_counts_memory from qiskit_experiments.framework import BaseAnalysis, ExperimentData from qiskit_experiments.framework.analysis_result_data import AnalysisResultData @@ -206,7 +206,13 @@ def _marginalized_component_data(self, composite_data: List[Dict]) -> List[List[ sub_data = {"metadata": metadata["composite_metadata"][i]} if "counts" in datum: if composite_clbits is not None: - sub_data["counts"] = marginal_counts(datum["counts"], composite_clbits[i]) + # `marginal_distribution() doesn't support int64 values + # so detect and cast to an int. This can be removed + # when Qiskit/qiskit-terra#9976 is released + counts = datum["counts"] + if any(isinstance(x, np.integer) for x in counts.values()): + counts = {k: int(v) for k, v in counts.items()} + sub_data["counts"] = marginal_distribution(counts, composite_clbits[i]) else: sub_data["counts"] = datum["counts"] if "memory" in datum: diff --git a/qiskit_experiments/library/characterization/analysis/local_readout_error_analysis.py b/qiskit_experiments/library/characterization/analysis/local_readout_error_analysis.py index 9fc0c25776..0bb3263496 100644 --- a/qiskit_experiments/library/characterization/analysis/local_readout_error_analysis.py +++ b/qiskit_experiments/library/characterization/analysis/local_readout_error_analysis.py @@ -16,7 +16,7 @@ import numpy as np import matplotlib.pyplot as plt from qiskit.result import LocalReadoutMitigator -from qiskit.result import marginal_counts +from qiskit.result import marginal_distribution from qiskit_experiments.framework import ExperimentData from qiskit_experiments.framework.matplotlib import get_non_gui_ax from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData, Options @@ -94,7 +94,7 @@ def _generate_matrices(self, data) -> List[np.array]: marginalized_counts = [] shots = [] for i in range(2): - marginal_cts = marginal_counts(counts[i], [k]) + marginal_cts = marginal_distribution(counts[i], [k]) marginalized_counts.append(marginal_cts) shots.append(sum(marginal_cts.values())) diff --git a/qiskit_experiments/library/tomography/fitters/fitter_data.py b/qiskit_experiments/library/tomography/fitters/fitter_data.py index 1ae39dfbd4..8d851a6b94 100644 --- a/qiskit_experiments/library/tomography/fitters/fitter_data.py +++ b/qiskit_experiments/library/tomography/fitters/fitter_data.py @@ -19,7 +19,7 @@ from collections import defaultdict import numpy as np -from qiskit.result import marginal_counts +from qiskit.result import marginal_distribution from qiskit_experiments.exceptions import AnalysisError from qiskit_experiments.library.tomography.basis import MeasurementBasis, PreparationBasis @@ -89,7 +89,10 @@ def tomography_fitter_data( if clbits: count_clbits += clbits if count_clbits: - counts = marginal_counts(counts, count_clbits) + # The input clbits might come in out of order, sort to ensure we + # don't permute the output during marginalization + count_clbits = list(sorted(count_clbits)) + counts = marginal_distribution(counts, count_clbits) # Accumulate counts combined_counts = outcome_dict[basis_key]