Source code for iq_readout.metrics
"""Readout metrics."""
from typing import List
import numpy as np
from .classifiers import Classifier
[docs]
def get_probs_prep_meas(
classifiers: List[Classifier] | Classifier, *shots: np.ndarray
) -> np.ndarray:
"""
Returns matrix whose element ``probs[s,o]`` corresponds to:
p(measured bitstring o | prepared bitstring s),
with s and o in the corresponding base 2, 3 or 10.
Parameters
----------
classifiers
A list of classifiers from the iq_readout package.
It can also be a single classifier.
shots: list(np.ndarray(num_qubits, num_shots, 2))
Tuple of shots when preparing all the possible bistrings.
The bitstrings should be sorted in the *standard binary order*, e.g.
``000``, ``001``, ``010``, ``011``, ...
In the case that ``classifiers`` is a single classifier (not a list),
each element of this variable can have shape ``(num_shots, 2)``.
Returns
-------
probs: np.ndarray(2**num_qubits, 2**num_qubits) | np.ndarray(3**num_qubits, 3**num_qubits)
The size of the array depends on the number of states that
the classifier can discriminate.
"""
if isinstance(classifiers, Classifier):
classifiers = [classifiers]
shots = [np.array([shots_k]) for shots_k in shots]
if not (isinstance(classifiers, list) or isinstance(classifiers, tuple)):
raise TypeError(
f"'classifiers' must be a list or a tuple, but {type(classifiers)} was given."
)
num_qubits = len(classifiers)
if not isinstance(classifiers[0], Classifier):
raise TypeError(
f"The given object is not a classifier, it is a {type(classifiers[0])}."
)
num_states = classifiers[0]._num_states
for clf in classifiers[1:]:
if not isinstance(clf, Classifier):
raise TypeError(
f"The given object is not a classifier, it is a {type(clf)}."
)
if clf._num_states != num_states:
raise TypeError(
"All given classifiers must have the same number of states."
)
if len(shots) != num_states**num_qubits:
raise ValueError(
"The number of bitstrings in 'shots' must be num_states**num_qubits, "
f"but {len(shots)} != {num_states}**{num_qubits} was given."
)
for k, shots_k in enumerate(shots):
if len(shots_k) != num_qubits:
raise ValueError(
f"Each bitstring must include all qubits ({num_qubits}), "
f"but only {len(shots_k)} were given for bitstring index {k}."
)
probs = np.zeros((num_states**num_qubits, num_states**num_qubits))
powers = num_states ** np.arange(num_qubits)[::-1] # to convert to base 10
for k, shots_k in enumerate(shots):
# shots_k.shape = (num_qubits, num_shots, 2)
num_total = shots_k.shape[1]
outcomes = np.array(
[clf.predict(shots_k[q]) for q, clf in enumerate(classifiers)]
)
unique, counts = np.unique(outcomes.T, axis=0, return_counts=True)
for vec, count in zip(unique, counts):
idx = np.sum(vec * powers) # base 2 or 3 conversion to base 10
probs[k, idx] = count / num_total
return probs