-
Notifications
You must be signed in to change notification settings - Fork 134
Mitigation experiment #415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5743454
9274c65
d18da24
7105122
ca1f05f
ef04532
b9f7315
96f298e
f11ebec
71e2447
3542bee
e02dced
93e01a8
791e7b1
84b4884
48964a4
759423e
51883d8
b4ca5ac
4a9cd33
307f8ac
82237ed
f4d333f
0c2540a
b36d829
15703ba
e1f3baf
99c30e3
3fd67b9
c61dd72
6691790
e26f79a
10f7ee5
4a3a6bd
4e583d0
d1f7102
6e4a088
6a53904
ed4a649
9afe49a
49d2fc5
7408bd4
d77027c
132ec22
a4e1724
2bf4080
e445179
0ad3a8b
c88d1b8
1ab5526
a8073f1
d615116
5bf6cb3
4c33074
6127b99
19c4687
aeb7002
796d961
6ffec24
3baeba4
2e4ce47
034af81
86caa84
5722020
2fdc091
bef39f3
6a07cef
a4f1730
4323a13
6cc59d9
c40399d
a76a9ab
c6bfd69
bbc6fab
06de788
fea0b8b
d2afad0
061d7d5
2d428c6
c5cf27c
ad79c58
c01f8b4
5e1694d
c1f6715
a980cf0
4e53fb3
bb2b401
c63d2dc
8db2f87
9881b7a
ab18872
d000227
f5c2b26
9dcc71e
f16e344
d06d6ce
b103443
29b9765
15a9a20
a220d25
f4627e3
bb2d459
86bdc95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # This code is part of Qiskit. | ||
| # | ||
| # (C) Copyright IBM 2021. | ||
| # | ||
| # This code is licensed under the Apache License, Version 2.0. You may | ||
| # obtain a copy of this license in the LICENSE.txt file in the root directory | ||
| # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
| # | ||
| # Any modifications or derivative works of this code must retain this | ||
| # copyright notice, and modified files need to carry a notice indicating | ||
| # that they have been altered from the originals. | ||
|
|
||
| """ | ||
| =============================================================================================== | ||
| Readout Mitigation Experiments (:mod:`qiskit_experiments.library.mitigation`) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this show up anywhere in the build docs because its not included in any other init files. The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||
| =============================================================================================== | ||
|
|
||
| .. currentmodule:: qiskit_experiments.library.mitigation | ||
|
|
||
| Experiment | ||
| =========== | ||
| .. autosummary:: | ||
| :toctree: ../stubs/ | ||
| :template: autosummary/experiment.rst | ||
|
|
||
| ReadoutMitigationExperiment | ||
|
|
||
|
|
||
| Analysis | ||
| ======== | ||
|
|
||
| .. autosummary:: | ||
| :toctree: ../stubs/ | ||
| :template: autosummary/analysis.rst | ||
|
|
||
| CorrelatedMitigationAnalysis | ||
| LocalMitigationAnalysis | ||
| """ | ||
| from .mitigation_experiment import ReadoutMitigationExperiment | ||
| from .mitigation_analysis import CorrelatedMitigationAnalysis | ||
| from .mitigation_analysis import LocalMitigationAnalysis | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| # This code is part of Qiskit. | ||
| # | ||
| # (C) Copyright IBM 2021. | ||
| # | ||
| # This code is licensed under the Apache License, Version 2.0. You may | ||
| # obtain a copy of this license in the LICENSE.txt file in the root directory | ||
| # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
| # | ||
| # Any modifications or derivative works of this code must retain this | ||
| # copyright notice, and modified files need to carry a notice indicating | ||
| # that they have been altered from the originals. | ||
| """ | ||
| Measurement calibration analysis classes | ||
| """ | ||
| from typing import List, Tuple | ||
| import numpy as np | ||
| import matplotlib.pyplot as plt | ||
| from qiskit.result import CorrelatedReadoutMitigator | ||
| from qiskit.result import LocalReadoutMitigator | ||
| from qiskit.result import marginal_counts | ||
| from qiskit_experiments.framework import ExperimentData | ||
| from qiskit_experiments.framework.matplotlib import get_non_gui_ax | ||
| from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData, Options | ||
|
|
||
|
|
||
| class CorrelatedMitigationAnalysis(BaseAnalysis): | ||
| """ | ||
| Measurement correction analysis for a full calibration | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally this should be more descriptive. On what the analysis is so someone not familiar with the experiment can read it and know what it does. In this case it constructs a readout error assignment matrix for possibly correlated N-qubit measurment outcomes (and you can put in an equation). It should also mention what hte analysis results returned are (in this case the CorrelatedMitigator class and include a link to the terra API docs for that class).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| """ | ||
|
|
||
| def _run_analysis( | ||
| self, experiment_data: ExperimentData, **options | ||
| ) -> Tuple[List[AnalysisResultData], List["matplotlib.figure.Figure"]]: | ||
| data = experiment_data.data() | ||
| qubits = experiment_data.metadata["physical_qubits"] | ||
| labels = [datum["metadata"]["label"] for datum in data] | ||
| matrix = self._generate_matrix(data, labels) | ||
| result_mitigator = CorrelatedReadoutMitigator(matrix, qubits=qubits) | ||
| analysis_results = [AnalysisResultData("Correlated Readout Mitigator", result_mitigator)] | ||
| ax = options.get("ax", None) | ||
| figures = [self._plot_calibration(matrix, labels, ax)] | ||
| return analysis_results, figures | ||
|
|
||
| def _generate_matrix(self, data, labels) -> np.array: | ||
| list_size = len(labels) | ||
| matrix = np.zeros([list_size, list_size], dtype=float) | ||
| # matrix[i][j] is the probability of counting i for expected j | ||
| for datum in data: | ||
| expected_outcome = datum["metadata"]["label"] | ||
| j = labels.index(expected_outcome) | ||
| total_counts = sum(datum["counts"].values()) | ||
| for measured_outcome, count in datum["counts"].items(): | ||
| i = labels.index(measured_outcome) | ||
| matrix[i][j] = count / total_counts | ||
| return matrix | ||
|
|
||
| def _plot_calibration(self, matrix, labels, ax=None) -> "matplotlib.figure.Figure": | ||
| """ | ||
| Plot the calibration matrix (2D color grid plot). | ||
|
|
||
| Args: | ||
| matrix: calibration matrix to plot | ||
| ax (matplotlib.axes): settings for the graph | ||
|
|
||
| Returns: | ||
| The generated plot of the calibration matrix | ||
|
|
||
| Raises: | ||
| QiskitError: if _cal_matrices was not set. | ||
|
|
||
| ImportError: if matplotlib was not installed. | ||
|
|
||
| """ | ||
|
|
||
| if ax is None: | ||
| ax = get_non_gui_ax() | ||
| figure = ax.get_figure() | ||
| ax.matshow(matrix, cmap=plt.cm.binary, clim=[0, 1]) | ||
| ax.set_xlabel("Prepared State") | ||
| ax.xaxis.set_label_position("top") | ||
| ax.set_ylabel("Measured State") | ||
| ax.set_xticks(np.arange(len(labels))) | ||
| ax.set_yticks(np.arange(len(labels))) | ||
| ax.set_xticklabels(labels) | ||
| ax.set_yticklabels(labels) | ||
| return figure | ||
|
|
||
|
|
||
| class LocalMitigationAnalysis(BaseAnalysis): | ||
| """ | ||
| Measurement correction analysis for a full calibration | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. This is missing proper API docs explaining what it does, what it returns, and link to the mitigator class returns.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| """ | ||
|
|
||
| @classmethod | ||
| def _default_options(cls) -> Options: | ||
| """Return default analysis options. | ||
|
|
||
| Analysis Options: | ||
| plot (bool): Set ``True`` to create figure for fit result. | ||
| ax(AxesSubplot): Optional. A matplotlib axis object to draw. | ||
| """ | ||
| options = super()._default_options() | ||
| options.plot = True | ||
| options.ax = None | ||
| return options | ||
|
|
||
| def _run_analysis( | ||
| self, experiment_data: ExperimentData | ||
| ) -> Tuple[List[AnalysisResultData], List["matplotlib.figure.Figure"]]: | ||
| data = experiment_data.data() | ||
| qubits = experiment_data.metadata["physical_qubits"] | ||
| matrices = self._generate_matrices(data) | ||
| result_mitigator = LocalReadoutMitigator(matrices, qubits=qubits) | ||
| analysis_results = [AnalysisResultData("Local Readout Mitigator", result_mitigator)] | ||
| if self.options.plot: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since these classes produce a figure that should also be mentioned in the API docs
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| figure = assignment_matrix_visualization( | ||
| result_mitigator.assignment_matrix(), ax=self.options.ax | ||
| ) | ||
| figures = [figure] | ||
| else: | ||
| figures = None | ||
| return analysis_results, figures | ||
|
|
||
| def _generate_matrices(self, data) -> List[np.array]: | ||
| num_qubits = len(data[0]["metadata"]["label"]) | ||
| counts = [None, None] | ||
| for result in data: | ||
| for i in range(2): | ||
| if result["metadata"]["label"] == str(i) * num_qubits: | ||
| counts[i] = result["counts"] | ||
| matrices = [] | ||
| for k in range(num_qubits): | ||
| matrix = np.zeros([2, 2], dtype=float) | ||
| marginalized_counts = [] | ||
| for i in range(2): | ||
| marginalized_counts.append(marginal_counts(counts[i], [k])) | ||
| # matrix[i][j] is the probability of counting i for expected j | ||
| for i in range(2): | ||
| for j in range(2): | ||
| matrix[i][j] = marginalized_counts[j][str(i)] / sum( | ||
| marginalized_counts[j].values() | ||
| ) | ||
| matrices.append(matrix) | ||
| return matrices | ||
|
|
||
|
|
||
| def assignment_matrix_visualization(assignment_matrix, ax=None): | ||
| """Displays a visualization of the assignment matrix compared to the identity""" | ||
| if ax is None: | ||
| ax = get_non_gui_ax() | ||
| figure = ax.get_figure() | ||
| n = len(assignment_matrix) | ||
| diff = np.abs(assignment_matrix - np.eye(n)) | ||
| im2 = ax.matshow(diff, cmap=plt.cm.Reds, vmin=0, vmax=0.2) | ||
| ax.set_yticks(np.arange(n)) | ||
| ax.set_xticks(np.arange(n)) | ||
| ax.set_yticklabels(n * [""]) | ||
| ax.set_xticklabels(n * [""]) | ||
| ax.set_xlabel(r"$|A - I|$", fontsize=16) | ||
| figure.colorbar(im2, ax=ax) | ||
| return figure | ||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,114 @@ | ||||||||
| # This code is part of Qiskit. | ||||||||
| # | ||||||||
| # (C) Copyright IBM 2021. | ||||||||
| # | ||||||||
| # This code is licensed under the Apache License, Version 2.0. You may | ||||||||
| # obtain a copy of this license in the LICENSE.txt file in the root directory | ||||||||
| # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||||||||
| # | ||||||||
| # Any modifications or derivative works of this code must retain this | ||||||||
| # copyright notice, and modified files need to carry a notice indicating | ||||||||
| # that they have been altered from the originals. | ||||||||
| """ | ||||||||
| Measurement calibration experiment classes. | ||||||||
| """ | ||||||||
| from typing import Iterable, List | ||||||||
|
|
||||||||
| from qiskit import QuantumCircuit | ||||||||
| from qiskit.exceptions import QiskitError | ||||||||
| from qiskit_experiments.framework import BaseExperiment | ||||||||
| from .mitigation_analysis import CorrelatedMitigationAnalysis, LocalMitigationAnalysis | ||||||||
|
|
||||||||
|
|
||||||||
| class ReadoutMitigationExperiment(BaseExperiment): | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really think this is a good name for the experiment because it sounds like you are applying mitigation, when in fact you are characterizing readout error so you can use that to apply mitigation later. It should be I would also have two separate experiment classes for the user
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||||
| """Class for readout mitigation experiments""" | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class is missing API documentation. It should have some description of what the experiment is and a reference to relevant paper and analysis class ie like this |
||||||||
|
|
||||||||
| METHOD_LOCAL = "local" | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do these need to be stored as a class variable? You can just put the value in the method and allowed values should be listed in the API docs. If you make two separate experiment classes you also dont need it
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that making two separate experiments makes things much simpler; this is also addressed in #611 |
||||||||
| METHOD_CORRELATED = "correlated" | ||||||||
| ALL_METHODS = [METHOD_LOCAL, METHOD_CORRELATED] | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This doesn't need to be public attribute, also unneeded with separate experiments |
||||||||
|
|
||||||||
| def __init__(self, qubits: Iterable[int], method=METHOD_LOCAL): | ||||||||
| """Initialize a mitigation experiment. | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a mitigation experiment, its a characterization experiment
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think it should be moved to the |
||||||||
|
|
||||||||
| Args: | ||||||||
| qubits: The qubits being mitigated | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are not being mitigated. This should be something like The qubit measurements to characterize readout error
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||||
| method: A string denoting mitigation method | ||||||||
|
|
||||||||
| Raises: | ||||||||
| QiskitError: if the given mitigation method is not recoginzed | ||||||||
|
|
||||||||
| Additional info: | ||||||||
| The currently supported mitigation methods are: | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though you can just remove this by making two experiments and putting all the relevent info into the experiments API docs |
||||||||
| * "local": each qubit is mitigated by itself; this is the default method, | ||||||||
| and assumes readout errors are independent for each qubits | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not indented correctly
Suggested change
|
||||||||
| * "correlated": All the qubits are mitigated together; this results in an exponentially | ||||||||
| large mitigation matrix and so is useable only for a small number of qubits, | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here |
||||||||
| but might be more accurate than local mitigation. | ||||||||
| """ | ||||||||
| super().__init__(qubits) | ||||||||
| if method not in self.ALL_METHODS: | ||||||||
| raise QiskitError("Method {} not recognized".format(method)) | ||||||||
| if method == self.METHOD_LOCAL: | ||||||||
| self.helper = LocalMitigationHelper(self.num_qubits) | ||||||||
| if method == self.METHOD_CORRELATED: | ||||||||
| self.helper = CorrelatedMitigationHelper(self.num_qubits) | ||||||||
|
|
||||||||
| self.analysis = self.helper.analysis() | ||||||||
|
|
||||||||
| def circuits(self) -> List[QuantumCircuit]: | ||||||||
| """Returns the experiment's circuits""" | ||||||||
| return [self._calibration_circuit(self.num_qubits, label) for label in self.helper.labels()] | ||||||||
|
|
||||||||
| @staticmethod | ||||||||
| def _calibration_circuit(num_qubits: int, label: str) -> QuantumCircuit: | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With separate experiments, this could just be a function use in both, rather than a method, since it doesn't access
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||||
| """Return a calibration circuit. | ||||||||
|
|
||||||||
| This is an N-qubit circuit where N is the length of the label. | ||||||||
| The circuit consists of X-gates on qubits with label bits equal to 1, | ||||||||
| and measurements of all qubits. | ||||||||
| """ | ||||||||
| circ = QuantumCircuit(num_qubits, name="meas_mit_cal_" + label) | ||||||||
| for i, val in enumerate(reversed(label)): | ||||||||
| if val == "1": | ||||||||
| circ.x(i) | ||||||||
| circ.measure_all() | ||||||||
| circ.metadata = {"label": label} | ||||||||
| return circ | ||||||||
|
|
||||||||
|
|
||||||||
| class CorrelatedMitigationHelper: | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These classes aren't necessary. The analysis classes should be set directly in the experiment, there is no need to wrap it in a trivial method. Similarly for the labels function (which with separate experiments can just be put as a function).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||||
| """Helper class for correlated mitigation experiment data""" | ||||||||
|
|
||||||||
| def __init__(self, num_qubits: int): | ||||||||
| """Creates the helper class | ||||||||
| Args: | ||||||||
| num_qubits: The number of qubits being mitigated | ||||||||
| """ | ||||||||
| self.num_qubits = num_qubits | ||||||||
|
|
||||||||
| def analysis(self): | ||||||||
| """Returns the analysis class for the mitigation""" | ||||||||
| return CorrelatedMitigationAnalysis() | ||||||||
|
|
||||||||
| def labels(self) -> List[str]: | ||||||||
| """Returns the labels dictating the generation of the mitigation circuits""" | ||||||||
| return [bin(j)[2:].zfill(self.num_qubits) for j in range(2 ** self.num_qubits)] | ||||||||
|
|
||||||||
|
|
||||||||
| class LocalMitigationHelper: | ||||||||
| """Helper class for local mitigation experiment data""" | ||||||||
|
|
||||||||
| def __init__(self, num_qubits: int): | ||||||||
| """Creates the helper class | ||||||||
| Args: | ||||||||
| num_qubits: The number of qubits being mitigated | ||||||||
| """ | ||||||||
| self.num_qubits = num_qubits | ||||||||
|
|
||||||||
| def analysis(self): | ||||||||
| """Returns the analysis class for the mitigation""" | ||||||||
| return LocalMitigationAnalysis() | ||||||||
|
|
||||||||
| def labels(self) -> List[str]: | ||||||||
| """Returns the labels dictating the generation of the mitigation circuits""" | ||||||||
| return ["0" * self.num_qubits, "1" * self.num_qubits] | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| features: | ||
| - | | ||
| Added a new experiment (`qiskit_experiments.library import ReadoutMitigationExperiment`) | ||
| for calibrating readout mitigators. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The readout error experiments needsto be added to the doc string of this init file to show up in the API docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do they fit in? Characterization experiments?