Skip to content
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

Add backend kwarg to readout error experiments #1019

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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]:
Expand Down
44 changes: 40 additions & 4 deletions qiskit_experiments/library/characterization/local_readout_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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]:
Expand Down
13 changes: 13 additions & 0 deletions releasenotes/notes/readout-error-c95b99ae5a6ba7ac.yaml
Original file line number Diff line number Diff line change
@@ -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`.
34 changes: 26 additions & 8 deletions test/library/characterization/test_readout_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down