diff --git a/qiskit_experiments/framework/composite/composite_analysis.py b/qiskit_experiments/framework/composite/composite_analysis.py index b37fbc04b3..ff54121a2f 100644 --- a/qiskit_experiments/framework/composite/composite_analysis.py +++ b/qiskit_experiments/framework/composite/composite_analysis.py @@ -14,6 +14,7 @@ """ from typing import List, Dict +import numpy as np from qiskit.result import marginal_counts from qiskit_experiments.framework import BaseAnalysis, ExperimentData, AnalysisResultData from qiskit_experiments.database_service.device_component import Qubit @@ -159,6 +160,13 @@ def _marginalize_data(self, composite_data: List[Dict]) -> List[Dict]: sub_data["counts"] = marginal_counts(datum["counts"], composite_clbits[i]) else: sub_data["counts"] = datum["counts"] + if "memory" in datum: + if composite_clbits is not None: + sub_data["memory"] = ( + np.array(datum["memory"])[composite_clbits[i]] + ).tolist() + else: + sub_data["memory"] = datum["memory"] marginalized_data[index].append(sub_data) # Sort by index diff --git a/qiskit_experiments/library/__init__.py b/qiskit_experiments/library/__init__.py index 5eb0ff5150..d243b7670d 100644 --- a/qiskit_experiments/library/__init__.py +++ b/qiskit_experiments/library/__init__.py @@ -73,6 +73,7 @@ ~characterization.Rabi ~characterization.EFRabi ~characterization.RamseyXY + ~characterization.ReadoutAngle .. _calibration: @@ -135,6 +136,7 @@ class instance to manage parameters and pulse schedules. FineXAmplitude, FineSXAmplitude, RamseyXY, + ReadoutAngle, ) from .randomized_benchmarking import StandardRB, InterleavedRB from .tomography import StateTomography, ProcessTomography diff --git a/qiskit_experiments/library/characterization/__init__.py b/qiskit_experiments/library/characterization/__init__.py index e16a352280..00680c5fa6 100644 --- a/qiskit_experiments/library/characterization/__init__.py +++ b/qiskit_experiments/library/characterization/__init__.py @@ -36,6 +36,7 @@ FineSXAmplitude RamseyXY RoughDrag + ReadoutAngle FineDrag FineXDrag FineSXDrag @@ -57,6 +58,7 @@ analysis.FineAmplitudeAnalysis analysis.FineXAmplitudeAnalysis analysis.RamseyXYAnalysis + analysis.ReadoutAngleAnalysis """ from .analysis import ( @@ -69,6 +71,7 @@ T2RamseyAnalysis, T1Analysis, CrossResonanceHamiltonianAnalysis, + ReadoutAngleAnalysis, ) from .t1 import T1 @@ -81,4 +84,5 @@ from .fine_amplitude import FineAmplitude, FineXAmplitude, FineSXAmplitude from .ramsey_xy import RamseyXY from .drag import RoughDrag +from .readout_angle import ReadoutAngle from .fine_drag import FineDrag, FineXDrag, FineSXDrag diff --git a/qiskit_experiments/library/characterization/analysis/__init__.py b/qiskit_experiments/library/characterization/analysis/__init__.py index 02f2aeec27..9d52c65770 100644 --- a/qiskit_experiments/library/characterization/analysis/__init__.py +++ b/qiskit_experiments/library/characterization/analysis/__init__.py @@ -21,3 +21,4 @@ from .t2ramsey_analysis import T2RamseyAnalysis from .t1_analysis import T1Analysis from .cr_hamiltonian_analysis import CrossResonanceHamiltonianAnalysis +from .readout_angle_analysis import ReadoutAngleAnalysis diff --git a/qiskit_experiments/library/characterization/analysis/readout_angle_analysis.py b/qiskit_experiments/library/characterization/analysis/readout_angle_analysis.py new file mode 100644 index 0000000000..b850ed1f49 --- /dev/null +++ b/qiskit_experiments/library/characterization/analysis/readout_angle_analysis.py @@ -0,0 +1,45 @@ +# 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 Angle Analysis class. +""" + +import numpy as np + +from qiskit_experiments.framework import BaseAnalysis, AnalysisResultData + + +class ReadoutAngleAnalysis(BaseAnalysis): + """ + A class to analyze readout angle experiments + """ + + # pylint: disable=unused-argument + def _run_analysis(self, experiment_data, **kwargs): + angles = [] + for i in range(2): + center = complex(*experiment_data.data(i)["memory"][0]) + angles.append(np.angle(center)) + + angle = (angles[0] + angles[1]) / 2 + if (np.abs(angles[0] - angles[1])) % (2 * np.pi) > np.pi: + angle += np.pi + + analysis_results = [ + AnalysisResultData( + name="ReadoutAngle", + value=angle, + extra={"angle_ground": angles[0], "angle_excited": angles[1]}, + ) + ] + + return analysis_results, [] diff --git a/qiskit_experiments/library/characterization/readout_angle.py b/qiskit_experiments/library/characterization/readout_angle.py new file mode 100644 index 0000000000..25a694110e --- /dev/null +++ b/qiskit_experiments/library/characterization/readout_angle.py @@ -0,0 +1,101 @@ +# 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 Angle Experiment class. +""" + +from typing import List, Optional + +from qiskit.circuit import QuantumCircuit +from qiskit.qobj.utils import MeasLevel +from qiskit.providers.backend import Backend + +from qiskit_experiments.framework import BaseExperiment, Options +from qiskit_experiments.library.characterization.analysis.readout_angle_analysis import ( + ReadoutAngleAnalysis, +) + + +class ReadoutAngle(BaseExperiment): + r""" + Readout angle experiment class + + # section: overview + + Design and analyze experiments for estimating readout angle of the qubit. + The readout angle is the average of two angles: the angle of the IQ + cluster center of the ground state, and the angle of the IQ cluster center + of the excited state. + + Each experiment consists of the following steps: + + 1. Circuits generation: two circuits, the first circuit measures the qubit + in the ground state, the second circuit sets the qubit in the excited state + and measures it. Measurements are in level 1 (kerneled). + + 2. Backend execution: actually running the circuits on the device + (or a simulator that supports level 1 measurements). The backend returns + the cluster centers of the ground and excited states. + + 3. Analysis of results: return the average of the angles of the two centers. + + """ + + __analysis_class__ = ReadoutAngleAnalysis + + @classmethod + def _default_run_options(cls) -> Options: + """Default run options.""" + options = super()._default_run_options() + + options.meas_level = MeasLevel.KERNELED + options.meas_return = "avg" + + return options + + def __init__( + self, + qubit: int, + backend: Optional[Backend] = None, + ): + """ + Initialize the readout angle experiment class + + Args: + qubit: the qubit whose readout angle is to be estimated + backend: Optional, the backend to run the experiment on. + """ + # Initialize base experiment + super().__init__([qubit], backend=backend) + + def circuits(self) -> List[QuantumCircuit]: + """ + Return a list of experiment circuits + + Returns: + The experiment circuits + """ + circ0 = QuantumCircuit(1, 1) + circ0.measure(0, 0) + + circ1 = QuantumCircuit(1, 1) + circ1.x(0) + circ1.measure(0, 0) + + for i, circ in enumerate([circ0, circ1]): + circ.metadata = { + "experiment_type": self._type, + "qubit": self.physical_qubits[0], + "xval": i, + } + + return [circ0, circ1] diff --git a/releasenotes/notes/readout-angle-4d3be1d584ba5ac1.yaml b/releasenotes/notes/readout-angle-4d3be1d584ba5ac1.yaml new file mode 100644 index 0000000000..5e0bd32925 --- /dev/null +++ b/releasenotes/notes/readout-angle-4d3be1d584ba5ac1.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + A new readout angle experiment. The experiment computes the average of the angles of the IQ clusters of the ground and excited states. diff --git a/test/test_readout_angle.py b/test/test_readout_angle.py new file mode 100644 index 0000000000..3f75f3fb65 --- /dev/null +++ b/test/test_readout_angle.py @@ -0,0 +1,51 @@ +# 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. +""" +Test readout angle experiment +""" + +import numpy as np + +from qiskit.test import QiskitTestCase +from qiskit_experiments.library import ReadoutAngle +from qiskit_experiments.test.mock_iq_backend import MockIQBackend + + +class ReadoutAngleBackend(MockIQBackend): + """ + Mock IQ backend tailored to the readout angle test + """ + + def _compute_probability(self, circuit): + return 1 - circuit.metadata["xval"] + + +class TestReadoutAngle(QiskitTestCase): + """ + Test the readout angle experiment + """ + + def test_readout_angle_end2end(self): + """ + Test readout angle experiment using a simulator. + """ + backend = ReadoutAngleBackend(iq_cluster_centers=(5.0, 5.0, -3.0, 3.0)) + exp = ReadoutAngle(0) + expdata = exp.run(backend, shots=100000).block_for_results() + res = expdata.analysis_results(0) + self.assertAlmostEqual(res.value % (2 * np.pi), np.pi / 2, places=2) + + backend = ReadoutAngleBackend(iq_cluster_centers=(5.0, 5.0, 0, -3.0)) + exp = ReadoutAngle(0) + expdata = exp.run(backend, shots=100000).block_for_results() + res = expdata.analysis_results(0) + self.assertAlmostEqual(res.value % (2 * np.pi), 15 * np.pi / 8, places=2)