Skip to content

Commit

Permalink
Changes to readout mitigator tests (#7482)
Browse files Browse the repository at this point in the history
* Bugfix in local mitigator and a relevant test

* Name fix

* Refactoring mitigation tests

* Cleanup

* Linting

* Linting

* Small bugfix and a test for it

* Fix to rng usage

* Linting

* Changing test sensitivity

* Added settings property to mitigator to allow JSON encoding when given as experiment result

* Linting

* Linting

* Linting

* Release note relating to the bugfix

* Get the assignment matrices implicitly

* Using more specific assertions

* Removing magic constants

* Make initialization conform to settings

* Linting

* Removing testcase which might fail on some machines

* Removing testcase which might fail on some machines

* Linting

* Reword release note

* Reword release note

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
(cherry picked from commit ff267b5)

# Conflicts:
#	test/python/result/test_mitigators.py
  • Loading branch information
gadial authored and mergify-bot committed Feb 17, 2022
1 parent 4c9df00 commit 7fbdaa5
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 263 deletions.
19 changes: 12 additions & 7 deletions qiskit/result/mitigation/correlated_readout_mitigator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Readout mitigator class based on the A-matrix inversion method
"""

from typing import Optional, List, Tuple, Iterable, Callable, Union
from typing import Optional, List, Tuple, Iterable, Callable, Union, Dict
import numpy as np

from qiskit.exceptions import QiskitError
Expand All @@ -33,20 +33,20 @@ class CorrelatedReadoutMitigator(BaseReadoutMitigator):
:math:`2^N x 2^N` so the mitigation complexity is :math:`O(4^N)`.
"""

def __init__(self, amat: np.ndarray, qubits: Optional[Iterable[int]] = None):
def __init__(self, assignment_matrix: np.ndarray, qubits: Optional[Iterable[int]] = None):
"""Initialize a CorrelatedReadoutMitigator
Args:
amat: readout error assignment matrix.
assignment_matrix: readout error assignment matrix.
qubits: Optional, the measured physical qubits for mitigation.
Raises:
QiskitError: matrix size does not agree with number of qubits
"""
if np.any(amat < 0) or not np.allclose(np.sum(amat, axis=0), 1):
if np.any(assignment_matrix < 0) or not np.allclose(np.sum(assignment_matrix, axis=0), 1):
raise QiskitError("Assignment matrix columns must be valid probability distributions")
amat = np.asarray(amat, dtype=float)
matrix_qubits_num = int(np.log2(amat.shape[0]))
assignment_matrix = np.asarray(assignment_matrix, dtype=float)
matrix_qubits_num = int(np.log2(assignment_matrix.shape[0]))
if qubits is None:
self._num_qubits = matrix_qubits_num
self._qubits = range(self._num_qubits)
Expand All @@ -59,9 +59,14 @@ def __init__(self, amat: np.ndarray, qubits: Optional[Iterable[int]] = None):
self._qubits = qubits
self._num_qubits = len(self._qubits)
self._qubit_index = dict(zip(self._qubits, range(self._num_qubits)))
self._assignment_mat = amat
self._assignment_mat = assignment_matrix
self._mitigation_mats = {}

@property
def settings(self) -> Dict:
"""Return settings."""
return {"assignment_matrix": self._assignment_mat, "qubits": self._qubits}

def expectation_value(
self,
data: Counts,
Expand Down
39 changes: 23 additions & 16 deletions qiskit/result/mitigation/local_readout_mitigator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""


from typing import Optional, List, Tuple, Iterable, Callable, Union
from typing import Optional, List, Tuple, Iterable, Callable, Union, Dict
import numpy as np

from qiskit.exceptions import QiskitError
Expand All @@ -38,43 +38,43 @@ class LocalReadoutMitigator(BaseReadoutMitigator):

def __init__(
self,
amats: Optional[List[np.ndarray]] = None,
assignment_matrices: Optional[List[np.ndarray]] = None,
qubits: Optional[Iterable[int]] = None,
backend=None,
):
"""Initialize a LocalReadoutMitigator
Args:
amats: Optional, list of single-qubit readout error assignment matrices.
assignment_matrices: Optional, list of single-qubit readout error assignment matrices.
qubits: Optional, the measured physical qubits for mitigation.
backend: Optional, backend name.
Raises:
QiskitError: matrices sizes do not agree with number of qubits
"""
if amats is None:
amats = self._from_backend(backend, qubits)
if assignment_matrices is None:
assignment_matrices = self._from_backend(backend, qubits)
else:
amats = [np.asarray(amat, dtype=float) for amat in amats]
for amat in amats:
assignment_matrices = [np.asarray(amat, dtype=float) for amat in assignment_matrices]
for amat in assignment_matrices:
if np.any(amat < 0) or not np.allclose(np.sum(amat, axis=0), 1):
raise QiskitError(
"Assignment matrix columns must be valid probability distributions"
)
if qubits is None:
self._num_qubits = len(amats)
self._num_qubits = len(assignment_matrices)
self._qubits = range(self._num_qubits)
else:
if len(qubits) != len(amats):
if len(qubits) != len(assignment_matrices):
raise QiskitError(
"The number of given qubits ({}) is different than the number of qubits "
"inferred from the matrices ({})".format(len(qubits), len(amats))
"inferred from the matrices ({})".format(len(qubits), len(assignment_matrices))
)
self._qubits = qubits
self._num_qubits = len(self._qubits)

self._qubit_index = dict(zip(self._qubits, range(self._num_qubits)))
self._assignment_mats = amats
self._assignment_mats = assignment_matrices
self._mitigation_mats = np.zeros([self._num_qubits, 2, 2], dtype=float)
self._gammas = np.zeros(self._num_qubits, dtype=float)

Expand All @@ -91,6 +91,11 @@ def __init__(
ainv = np.linalg.pinv(mat)
self._mitigation_mats[i] = ainv

@property
def settings(self) -> Dict:
"""Return settings."""
return {"assignment_matrices": self._assignment_mats, "qubits": self._qubits}

def expectation_value(
self,
data: Counts,
Expand Down Expand Up @@ -231,8 +236,9 @@ def mitigation_matrix(self, qubits: Optional[Union[List[int], int]] = None) -> n
qubits = self._qubits
if isinstance(qubits, int):
qubits = [self._qubits[qubits]]
mat = self._mitigation_mats[qubits[0]]
for i in qubits[1:]:
qubit_indices = [self._qubit_index[qubit] for qubit in qubits]
mat = self._mitigation_mats[qubit_indices[0]]
for i in qubit_indices[1:]:
mat = np.kron(self._mitigation_mats[i], mat)
return mat

Expand All @@ -253,9 +259,10 @@ def assignment_matrix(self, qubits: List[int] = None) -> np.ndarray:
qubits = self._qubits
if isinstance(qubits, int):
qubits = [qubits]
mat = self._assignment_mats[qubits[0]]
for i in qubits[1:]:
mat = np.kron(self._assignment_mats[qubits[i]], mat)
qubit_indices = [self._qubit_index[qubit] for qubit in qubits]
mat = self._assignment_mats[qubit_indices[0]]
for i in qubit_indices[1:]:
mat = np.kron(self._assignment_mats[i], mat)
return mat

def _compute_gamma(self, qubits=None):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
fixes:
- |
:meth:`.LocalReadoutMitigator.assignment_matrix` will now accept lists of
qubits beyond the trivial ``[0, 1, 2, ..., n-1]``.
Loading

0 comments on commit 7fbdaa5

Please sign in to comment.