From f3b227df4911ec429d036e835e972c6ed14fb062 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Fri, 20 Jan 2023 10:50:16 -0500 Subject: [PATCH 1/2] Add backend kwarg to readout error experiments * Adds `backend` kwarg to LocalReadoutError and CorrelatedReadoutError experiment inits. If this is set without specifying physical qubits all qubits on the backend will be used for the experiment. * Renames the `qubits` init kwarg of LocalReadoutError and CorrelatedReadoutError to `physical_qubits. --- .../correlated_readout_error.py | 44 +++++++++++++++++-- .../characterization/local_readout_error.py | 44 +++++++++++++++++-- .../notes/readout-error-c95b99ae5a6ba7ac.yaml | 13 ++++++ .../characterization/test_readout_error.py | 34 ++++++++++---- 4 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml diff --git a/qiskit_experiments/library/characterization/correlated_readout_error.py b/qiskit_experiments/library/characterization/correlated_readout_error.py index 79bf7c52a6..dd99882624 100644 --- a/qiskit_experiments/library/characterization/correlated_readout_error.py +++ b/qiskit_experiments/library/characterization/correlated_readout_error.py @@ -12,8 +12,11 @@ """ Correlated readout error calibration experiment class. """ -from typing import Iterable, List +import warnings +from typing import Iterable, List, Optional from qiskit import QuantumCircuit +from qiskit.providers.backend import BackendV2, Backend +from qiskit.exceptions import QiskitError from qiskit_experiments.framework import BaseExperiment from qiskit_experiments.library.characterization.analysis.correlated_readout_error_analysis import ( CorrelatedReadoutErrorAnalysis, @@ -74,13 +77,46 @@ class CorrelatedReadoutError(BaseExperiment): .. ref_arxiv:: 1 2006.14044 """ - def __init__(self, qubits: Iterable[int]): + def __init__( + self, + physical_qubits: Optional[Iterable[int]] = None, + backend: Optional[Backend] = None, + qubits: Optional[Iterable[int]] = None, + ): """Initialize a correlated readout error characterization experiment. Args: - qubits: The qubits being characterized for readout error + physical_qubits: Optional, the backend qubits being characterized + for readout error. If None all qubits on the provided backend + will be characterized. + backend: Optional, the backend to characterize. + qubits: DEPRECATED, equivalent to ``physical_qubits``. + + Raises: + QiskitError: if args are not valid. """ - super().__init__(qubits) + # Deprecated qubits kwarg + if qubits is not None: + physical_qubits = qubits + warnings.warn( + "The `qubits` kwarg has been renamed to `physical_qubits`." + " It will be removed in a future release.", + DeprecationWarning, + stacklevel=2, + ) + if physical_qubits is None: + if backend is None: + raise QiskitError("`physical_qubits` and `backend` kwargs cannot both be None.") + num_qubits = 0 + if isinstance(backend, BackendV2): + num_qubits = backend.target.num_qubits + elif isinstance(backend, Backend): + num_qubits = backend.configuration().num_qubits + if num_qubits: + physical_qubits = range(num_qubits) + else: + raise QiskitError(f"Cannot infer backend qubits from backend {backend}") + super().__init__(physical_qubits, backend=backend) self.analysis = CorrelatedReadoutErrorAnalysis() def circuits(self) -> List[QuantumCircuit]: diff --git a/qiskit_experiments/library/characterization/local_readout_error.py b/qiskit_experiments/library/characterization/local_readout_error.py index 77efdfb5b1..e0998da0f9 100644 --- a/qiskit_experiments/library/characterization/local_readout_error.py +++ b/qiskit_experiments/library/characterization/local_readout_error.py @@ -12,8 +12,11 @@ """ Local readout error calibration experiment class. """ -from typing import Iterable, List +import warnings +from typing import Iterable, List, Optional from qiskit import QuantumCircuit +from qiskit.providers.backend import BackendV2, Backend +from qiskit.exceptions import QiskitError from qiskit_experiments.framework import BaseExperiment from qiskit_experiments.library.characterization.analysis.local_readout_error_analysis import ( LocalReadoutErrorAnalysis, @@ -63,13 +66,46 @@ class LocalReadoutError(BaseExperiment): .. ref_arxiv:: 1 2006.14044 """ - def __init__(self, qubits: Iterable[int]): + def __init__( + self, + physical_qubits: Optional[Iterable[int]] = None, + backend: Optional[Backend] = None, + qubits: Optional[Iterable[int]] = None, + ): """Initialize a local readout error characterization experiment. Args: - qubits: The qubits being characterized for readout error + physical_qubits: Optional, the backend qubits being characterized + for readout error. If None all qubits on the provided backend + will be characterized. + backend: Optional, the backend to characterize. + qubits: DEPRECATED, equivalent to ``physical_qubits``. + + Raises: + QiskitError: if args are not valid. """ - super().__init__(qubits) + # Deprecated qubits kwarg + if qubits is not None: + physical_qubits = qubits + warnings.warn( + "The `qubits` kwarg has been renamed to `physical_qubits`." + " It will be removed in a future release.", + DeprecationWarning, + stacklevel=2, + ) + if physical_qubits is None: + if backend is None: + raise QiskitError("`physical_qubits` and `backend` kwargs cannot both be None.") + num_qubits = 0 + if isinstance(backend, BackendV2): + num_qubits = backend.target.num_qubits + elif isinstance(backend, Backend): + num_qubits = backend.configuration().num_qubits + if num_qubits: + physical_qubits = range(num_qubits) + else: + raise QiskitError(f"Cannot infer backend qubits from backend {backend}") + super().__init__(physical_qubits, backend=backend) self.analysis = LocalReadoutErrorAnalysis() def circuits(self) -> List[QuantumCircuit]: diff --git a/releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml b/releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml new file mode 100644 index 0000000000..2eace074e5 --- /dev/null +++ b/releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + Adds a ``backend`` init kwarg to the :class:`.LocalReadoutError` and + :class:`.CorrelatedReadoutError` experiments, and makes the + ``physical_qubits`` kwarg optional. If a backend is supplied without + specifying phyiscal qubits, the experiment will be initialized on all + qubits for the backend. +deprecations: + - | + The ``qubits`` kwarg of the following experiments has been deprecated and + renamed to ``physical_qubits`` :class:`.LocalReadoutError`, + :class:`.CorrelatedReadoutError`. diff --git a/test/library/characterization/test_readout_error.py b/test/library/characterization/test_readout_error.py index 0ff06361a9..8616ab86db 100644 --- a/test/library/characterization/test_readout_error.py +++ b/test/library/characterization/test_readout_error.py @@ -33,17 +33,35 @@ class TestReadoutError(QiskitExperimentsTestCase): def test_local_analysis_ideal(self): """Tests local mitigator generation from ideal data""" - backend = AerSimulator() - physical_qubits = range(3) - exp = LocalReadoutError(physical_qubits) + num_qubits = 3 + # Note: using num_qubits instead of n_qubits kwarg here raises an Aer exception + backend = AerSimulator(n_qubits=num_qubits) + exp = LocalReadoutError(backend=backend) expdata = exp.run(backend) + self.assertExperimentDone(expdata) mitigator = expdata.analysis_results(0).value - self.assertEqual(len(physical_qubits), mitigator._num_qubits) - self.assertEqual(list(physical_qubits), list(mitigator._qubits)) - self.assertTrue( - matrix_equal([np.eye(2) for _ in physical_qubits], mitigator._assignment_mats) - ) + qubits = list(range(num_qubits)) + self.assertEqual(mitigator._num_qubits, num_qubits) + self.assertEqual(list(mitigator._qubits), qubits) + for qubit in qubits: + self.assertTrue(matrix_equal(mitigator.assignment_matrix([qubit]), np.eye(2))) + self.assertTrue(matrix_equal([np.eye(2) for _ in qubits], mitigator._assignment_mats)) + + def test_correlated_analysis_ideal(self): + """Tests local mitigator generation from ideal data""" + num_qubits = 2 + # Note: using num_qubits instead of n_qubits kwarg here raises an Aer exception + backend = AerSimulator(n_qubits=num_qubits) + exp = CorrelatedReadoutError(backend=backend) + expdata = exp.run(backend) + self.assertExperimentDone(expdata) + mitigator = expdata.analysis_results(0).value + + qubits = list(range(num_qubits)) + self.assertEqual(mitigator._num_qubits, num_qubits) + self.assertEqual(list(mitigator._qubits), qubits) + self.assertTrue(matrix_equal(mitigator.assignment_matrix(qubits), np.eye(2**num_qubits))) def test_local_analysis(self): """Tests local mitigator generation from experimental data""" From ae0fed6ca69451eca1e0dcd54e3293dad736de56 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Fri, 20 Jan 2023 15:33:57 -0500 Subject: [PATCH 2/2] Update releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml Co-authored-by: Ian Hincks --- releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml b/releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml index 2eace074e5..be670b3bab 100644 --- a/releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml +++ b/releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml @@ -9,5 +9,5 @@ features: deprecations: - | The ``qubits`` kwarg of the following experiments has been deprecated and - renamed to ``physical_qubits`` :class:`.LocalReadoutError`, + renamed to ``physical_qubits``: :class:`.LocalReadoutError`, :class:`.CorrelatedReadoutError`.