Source code for mimiqcircuits.operations.readouterr

# Copyright © 2022-2023 University of Strasbourg. All Rights Reserved.
# Copyright © 2023-2025 QPerfect. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Readout error model."""

import numpy as np
import symengine as se
import sympy as sp
from mimiqcircuits import Operation


[docs] class ReadoutErr(Operation): r"""Represents a classical readout error applied immediately after a measurement. The readout error is modeled by a 2×2 *confusion matrix*: .. math:: \begin{pmatrix} P(\text{report } 0 | \text{true } 0) & P(\text{report } 1 | \text{true } 0) = p_0 \\ P(\text{report } 0 | \text{true } 1) = p_1 & P(\text{report } 1 | \text{true } 1) \end{pmatrix} Can be initialized either from the error probabilities ``p0``, ``p1`` or directly from a 2×2 confusion matrix. Accepts NumPy, SymPy, or SymEngine matrices. Examples: >>> from mimiqcircuits import * >>> ReadoutErr(0.02, 0.03) RErr(0.02,0.03) >>> import numpy as np >>> ReadoutErr(np.array([[0.98, 0.02], ... [0.03, 0.97]])) RErr(0.02,0.03) >>> c = Circuit() >>> c.push(ReadoutErr(0.01, 0.02),0) 1-bit circuit with 1 instruction: └── RErr(0.01, 0.02) @ c[0] <BLANKLINE> """ _name = "RErr" _num_qubits = 0 _num_bits = 1 _num_zvars = 0 _parnames = ("p0", "p1") _num_cregs = 1 _num_qregs = 0 _num_zregs = 0 _cregsizes = [1]
[docs] def __init__(self, p0, p1=None): # Case 1: confusion matrix if p1 is None: if isinstance(p0, np.ndarray): mat = p0 elif isinstance(p0, se.Matrix): mat = np.array(p0.tolist(), dtype=object) elif isinstance(p0, sp.MatrixBase): mat = np.array(p0.tolist(), dtype=object) else: raise TypeError( f"ReadoutErr expects numpy.ndarray, symengine.Matrix, or sympy.Matrix — got {type(p0)}" ) if mat.shape != (2, 2): raise ValueError("Confusion matrix must be 2×2.") for i in range(2): for j in range(2): val = mat[i, j] if isinstance(val, (sp.Basic, se.Basic)) and not val.is_Number: raise TypeError( f"All elements of confusion matrix must be numeric." ) if np.any((mat < 0) | (mat > 1)): raise ValueError("All entries in confusion matrix must be in [0,1].") for i in range(2): row_sum = np.sum(mat[i, :]) if not np.isclose(row_sum, 1.0, atol=1e-8): raise ValueError( f"Row {i + 1} of confusion matrix must sum to 1 (got {row_sum})." ) p0, p1 = float(mat[0, 1]), float(mat[1, 0]) # Case 2: direct parameters (numeric or symbolic allowed) self.p0 = p0 self.p1 = p1 if not isinstance(self.p0, (sp.Basic, se.Basic)) and not isinstance( self.p1, (sp.Basic, se.Basic) ): if not (0.0 <= self.p0 <= 1.0): raise ValueError(f"p0 must be in [0,1], got {self.p0}") if not (0.0 <= self.p1 <= 1.0): raise ValueError(f"p1 must be in [0,1], got {self.p1}") super().__init__()
[docs] def evaluate(self, d={}): """Evaluate symbolic parameters numerically. Returns a new ReadoutErr instance with evaluated parameters. """ p0_eval = ( float(self.p0.subs(d).evalf()) if hasattr(self.p0, "subs") else self.p0 ) p1_eval = ( float(self.p1.subs(d).evalf()) if hasattr(self.p1, "subs") else self.p1 ) if not (0.0 <= p0_eval <= 1.0) or not (0.0 <= p1_eval <= 1.0): raise ValueError("Readout probabilities must be in [0,1].") return ReadoutErr(p0_eval, p1_eval)
[docs] def matrix(self): """Return the confusion matrix as a symengine.Matrix.""" return se.Matrix( [ [1 - self.p0, self.p0], [self.p1, 1 - self.p1], ] )
@property def opname(self): return self._name @property def parnames(self): return self._parnames def __str__(self): return f"{self._name}({self.p0}, {self.p1})" def __repr__(self): return f"{self._name}({self.p0},{self.p1})"
[docs] def iswrapper(self): pass
__all__ = ["ReadoutErr"]