Skip to content

Commit d2b41d6

Browse files
Adopt qiskit.result.mitigation into qiskit_experiments.data_processing (backport #1484) (#1486)
The readout error mitigator classes `LocalReadoutMitigator` and `CorrelatedReadoutMitigator` have been copied into `qiskit_experiments.data_processing.mitigation` from `qiskit.result.mitigation` in order to prepare for the deprecation of these classes from Qiskit. The experiments that previously used the Qiskit versions of these mitigator classes now use the Experiments versions. The manual has also been updated to use the new class locations. Additionally, the readout mitigation manual was refactored to avoid using private functions and methods. Some adjacent `jupyter-execute` cells were merged because in the rendered docs they look like one cell any way, but they still have individual "copy to clipboard" buttons which is confusing. You think clicking the button will get the merged cell contents but it only gets a section of it.<hr>This is an automatic backport of pull request #1484 done by [Mergify](https://mergify.com). Co-authored-by: Will Shanks <willshanks@us.ibm.com>
1 parent da8e0f7 commit d2b41d6

File tree

14 files changed

+1388
-32
lines changed

14 files changed

+1388
-32
lines changed

docs/manuals/measurement/readout_mitigation.rst

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,16 @@ experiments to generate the corresponding mitigators.
4747
import numpy as np
4848
import matplotlib.pyplot as plt
4949
from qiskit import QuantumCircuit
50+
from qiskit.quantum_info import Operator
5051
from qiskit.visualization import plot_distribution
52+
from qiskit_experiments.data_processing import LocalReadoutMitigator
5153
from qiskit_experiments.library import LocalReadoutError, CorrelatedReadoutError
5254

5355
from qiskit_aer import AerSimulator
5456
from qiskit_ibm_runtime.fake_provider import FakePerth
5557

56-
from qiskit.result.mitigation.utils import (
57-
expval_with_stddev,
58-
str2diag,
59-
counts_probability_vector
60-
)
61-
6258
backend = AerSimulator.from_backend(FakePerth())
6359

64-
.. jupyter-execute::
65-
66-
shots = 1024
67-
qubits = [0,1,2,3]
68-
num_qubits = len(qubits)
6960

7061
Standard mitigation experiment
7162
------------------------------
@@ -76,13 +67,14 @@ circuits, one for all “0” and one for all “1” results.
7667

7768
.. jupyter-execute::
7869

70+
shots = 1024
71+
qubits = [0,1,2,3]
72+
num_qubits = len(qubits)
73+
7974
exp = LocalReadoutError(qubits)
8075
for c in exp.circuits():
8176
print(c)
8277

83-
84-
.. jupyter-execute::
85-
8678
exp.analysis.set_options(plot=True)
8779
result = exp.run(backend)
8880
mitigator = result.analysis_results("Local Readout Mitigator").value
@@ -102,9 +94,9 @@ The individual mitigation matrices can be read off the mitigator.
10294

10395
.. jupyter-execute::
10496

105-
for m in mitigator._mitigation_mats:
106-
print(m)
107-
print()
97+
for qubit in mitigator.qubits:
98+
print(f"Qubit: {qubit}")
99+
print(mitigator.mitigation_matrix(qubits=qubit))
108100

109101

110102
Mitigation example
@@ -118,13 +110,9 @@ Mitigation example
118110
qc.cx(i - 1, i)
119111
qc.measure_all()
120112

121-
.. jupyter-execute::
122-
123113
counts = backend.run(qc, shots=shots, seed_simulator=42, method="density_matrix").result().get_counts()
124114
unmitigated_probs = {label: count / shots for label, count in counts.items()}
125115

126-
.. jupyter-execute::
127-
128116
mitigated_quasi_probs = mitigator.quasi_probabilities(counts)
129117
mitigated_stddev = mitigated_quasi_probs._stddev_upper_bound
130118
mitigated_probs = (mitigated_quasi_probs.nearest_probability_distribution().binary_probabilities())
@@ -144,15 +132,20 @@ Expectation value
144132
.. jupyter-execute::
145133

146134
diagonal_labels = ["ZZZZ", "ZIZI", "IZII", "1ZZ0"]
147-
ideal_expectation = []
148-
diagonals = [str2diag(d) for d in diagonal_labels]
135+
diagonals = [
136+
np.diag(np.real(Operator.from_label(d).to_matrix()))
137+
for d in diagonal_labels
138+
]
139+
140+
# Create a mitigator with no mitigation so that we can use its
141+
# expectation_values method to generate an unmitigated expectation value to
142+
# compare to the mitigated one.
143+
identity_mitigator = LocalReadoutMitigator([np.eye(2) for _ in range(4)])
144+
149145
qubit_index = {i: i for i in range(num_qubits)}
150-
unmitigated_probs_vector, _ = counts_probability_vector(unmitigated_probs, qubit_index=qubit_index)
151-
unmitigated_expectation = [expval_with_stddev(d, unmitigated_probs_vector, shots) for d in diagonals]
146+
unmitigated_expectation = [identity_mitigator.expectation_value(counts, d) for d in diagonals]
152147
mitigated_expectation = [mitigator.expectation_value(counts, d) for d in diagonals]
153148

154-
.. jupyter-execute::
155-
156149
mitigated_expectation_values, mitigated_stddev = zip(*mitigated_expectation)
157150
unmitigated_expectation_values, unmitigated_stddev = zip(*unmitigated_expectation)
158151
legend = ['Mitigated Expectation', 'Unmitigated Expectation']

qiskit_experiments/data_processing/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@
8282
BaseDiscriminator
8383
SkLDA
8484
SkQDA
85+
86+
Mitigators
87+
==========
88+
.. autosummary::
89+
:toctree: ../stubs/
90+
91+
BaseReadoutMitigator
92+
LocalReadoutMitigator
93+
CorrelatedReadoutMitigator
8594
"""
8695

8796
from .data_action import DataAction, TrainableDataAction
@@ -104,4 +113,7 @@
104113

105114
from .data_processor import DataProcessor
106115
from .discriminator import BaseDiscriminator
116+
from .mitigation.base_readout_mitigator import BaseReadoutMitigator
117+
from .mitigation.correlated_readout_mitigator import CorrelatedReadoutMitigator
118+
from .mitigation.local_readout_mitigator import LocalReadoutMitigator
107119
from .sklearn_discriminators import SkLDA, SkQDA
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2017, 2021.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""Readout error mitigation."""
14+
from .base_readout_mitigator import BaseReadoutMitigator
15+
from .correlated_readout_mitigator import CorrelatedReadoutMitigator
16+
from .local_readout_mitigator import LocalReadoutMitigator
17+
from .utils import (
18+
counts_probability_vector,
19+
expval_with_stddev,
20+
stddev,
21+
str2diag,
22+
)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2021
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
"""
13+
Base class for readout error mitigation.
14+
"""
15+
16+
from abc import ABC, abstractmethod
17+
from typing import Optional, List, Iterable, Tuple, Union, Callable
18+
19+
import numpy as np
20+
21+
from qiskit.result.counts import Counts
22+
from qiskit.result.distributions.quasi import QuasiDistribution
23+
24+
25+
class BaseReadoutMitigator(ABC):
26+
"""Base readout error mitigator class."""
27+
28+
@abstractmethod
29+
def quasi_probabilities(
30+
self,
31+
data: Counts,
32+
qubits: Iterable[int] = None,
33+
clbits: Optional[List[int]] = None,
34+
shots: Optional[int] = None,
35+
) -> QuasiDistribution:
36+
"""Convert counts to a dictionary of quasi-probabilities
37+
38+
Args:
39+
data: Counts to be mitigated.
40+
qubits: the physical qubits measured to obtain the counts clbits.
41+
If None these are assumed to be qubits [0, ..., N-1]
42+
for N-bit counts.
43+
clbits: Optional, marginalize counts to just these bits.
44+
shots: Optional, the total number of shots, if None shots will
45+
be calculated as the sum of all counts.
46+
47+
Returns:
48+
QuasiDistribution: A dictionary containing pairs of [output, mean] where "output"
49+
is the key in the dictionaries,
50+
which is the length-N bitstring of a measured standard basis state,
51+
and "mean" is the mean of non-zero quasi-probability estimates.
52+
"""
53+
54+
@abstractmethod
55+
def expectation_value(
56+
self,
57+
data: Counts,
58+
diagonal: Union[Callable, dict, str, np.ndarray],
59+
qubits: Iterable[int] = None,
60+
clbits: Optional[List[int]] = None,
61+
shots: Optional[int] = None,
62+
) -> Tuple[float, float]:
63+
"""Calculate the expectation value of a diagonal Hermitian operator.
64+
65+
Args:
66+
data: Counts object to be mitigated.
67+
diagonal: the diagonal operator. This may either be specified
68+
as a string containing I,Z,0,1 characters, or as a
69+
real valued 1D array_like object supplying the full diagonal,
70+
or as a dictionary, or as Callable.
71+
qubits: the physical qubits measured to obtain the counts clbits.
72+
If None these are assumed to be qubits [0, ..., N-1]
73+
for N-bit counts.
74+
clbits: Optional, marginalize counts to just these bits.
75+
shots: Optional, the total number of shots, if None shots will
76+
be calculated as the sum of all counts.
77+
78+
Returns:
79+
The mean and an upper bound of the standard deviation of operator
80+
expectation value calculated from the current counts.
81+
"""

0 commit comments

Comments
 (0)