diff --git a/qiskit_experiments/library/characterization/analysis/readout_angle_analysis.py b/qiskit_experiments/library/characterization/analysis/readout_angle_analysis.py index 503d9e8529..d2c674296d 100644 --- a/qiskit_experiments/library/characterization/analysis/readout_angle_analysis.py +++ b/qiskit_experiments/library/characterization/analysis/readout_angle_analysis.py @@ -13,9 +13,11 @@ Readout Angle Analysis class. """ +from typing import List, Optional import numpy as np -from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData +from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData, Options +from qiskit_experiments.framework.matplotlib import get_non_gui_ax class ReadoutAngleAnalysis(BaseAnalysis): @@ -23,22 +25,73 @@ class ReadoutAngleAnalysis(BaseAnalysis): A class to analyze readout angle experiments """ + @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): angles = [] + radii = [] + centers = [] for i in range(2): center = complex(*experiment_data.data(i)["memory"][0]) angles.append(np.angle(center)) + radii.append(np.absolute(center)) + centers.append(center) angle = (angles[0] + angles[1]) / 2 if (np.abs(angles[0] - angles[1])) % (2 * np.pi) > np.pi: angle += np.pi + extra_results = {} + extra_results["readout_angle_0"] = angles[0] + extra_results["readout_angle_1"] = angles[1] + extra_results["readout_radius_0"] = radii[0] + extra_results["readout_radius_1"] = radii[1] + analysis_results = [ - AnalysisResultData( - name="ReadoutAngle", - value=angle, - extra={"angle_ground": angles[0], "angle_excited": angles[1]}, - ) + AnalysisResultData(name="readout_angle", value=angle, extra=extra_results) ] - return analysis_results, [] + if self.options.plot: + ax = self._format_plot(centers, ax=self.options.ax) + figures = [ax.get_figure()] + else: + figures = None + + return analysis_results, figures + + @staticmethod + def _format_plot(centers: List[complex], ax: Optional["matplotlib.pyplot.AxesSubplot"] = None): + """Format the readout_angle plot + + Args: + centers: the two centers of the level 1 measurements for 0 and for 1. + ax: matplotlib axis to add plot to. + + Returns: + AxesSubPlot: the matplotlib axes containing the plot. + """ + largest_extent = ( + np.max([np.max(np.abs(np.real(centers))), np.max(np.abs(np.imag(centers)))]) * 1.1 + ) + + ax = get_non_gui_ax() + ax.plot(np.real(centers[0]), np.imag(centers[0]), "ro", markersize=24) + ax.plot(np.real(centers[1]), np.imag(centers[1]), "bo", markersize=24) + ax.set_xlim([-largest_extent, largest_extent]) + ax.set_ylim([-largest_extent, largest_extent]) + ax.set_xlabel("I [arb.]") + ax.set_ylabel("Q [arb.]") + ax.set_title("Centroid Positions") + ax.legend(["0", "1"]) + return ax