Source code for qarp.blocks._primitives.readout_block

from typing import List, Optional, Sequence

from .._block import SimpleBlock


[docs] class ReadoutBlock(SimpleBlock): """Python-layer convenience wrapper that reads out a sequence of qubits. For users composing circuits with the Python block API and ``CompositeBlock``, this is the ergonomic "exit point" that puts ``Measure`` commands into the command stream — visible to QIR / QASM emitters and to anything that inspects the flat command list. Internally it's a ``SimpleBlock`` carrying one ``Measure`` command per ``(qubit, cbit)`` pair. Semantically equivalent to chaining N single-qubit ``MeasureBlock(q, c)`` primitives, but built in one call without the composite-of-N-MeasureBlocks ``n_qubits`` inference foot-gun. Three-role API recap: * ``block.measure(q, c)`` — builder method on ``SimpleBlock``; appends a single ``Measure`` command to the leaf's own buffer. Used when constructing a leaf with measurements inline (e.g. inside this class's own ``build_vanilla``). * ``MeasureBlock(qubit, cbit)`` — single-Command qarpx-native typed primitive. Used for tree-level composition (`add_child` patterns inside e.g. ``SWAPTestBlock``, ``HadamardTestBlock``; body of ``ConditionalBlock``). * ``ReadoutBlock(n_qubits, qubits=..., cbits=...)`` — Python-layer collection wrapper for "read out these qubits in one call." Args: n_qubits: Size of the qubit register this block operates on. qubits: Sequence of qubit indices to read out. Defaults to ``range(n_qubits)`` (read out the full register). cbits: Classical-bit targets, same length as ``qubits``. Defaults to ``list(qubits)`` — i.e. ``cbit_i = qubit_i``. target_qubits: Optional parent-space remap for the qubits this block covers, forwarded to ``Block``. name: Display name; defaults to ``"Readouts"``. """ def __init__( self, n_qubits: int, qubits: Optional[Sequence[int]] = None, cbits: Optional[Sequence[int]] = None, target_qubits: Optional[List[int]] = None, name: str = "Readouts", ): super().__init__(n_qubits, target_qubits=target_qubits, name=name) self.qubits = list(range(n_qubits)) if qubits is None else list(qubits) self.cbits = list(self.qubits) if cbits is None else list(cbits) self._validate_inputs() def _validate_inputs(self) -> None: if self.n_qubits is None or self.n_qubits <= 0: raise ValueError("n_qubits must be a positive integer") if len(self.qubits) != len(self.cbits): raise ValueError( f"qubits and cbits must have the same length " f"(got {len(self.qubits)} qubits, {len(self.cbits)} cbits)" ) if self.qubits and (min(self.qubits) < 0 or max(self.qubits) >= self.n_qubits): raise ValueError( f"qubit indices must lie in [0, {self.n_qubits}); got {list(self.qubits)}" ) if self.target_qubits and len(self.target_qubits) != self.n_qubits: raise ValueError("target_qubits length must match n_qubits if provided")
[docs] def build_vanilla(self) -> None: """Emit one Measure command per (qubit, cbit) pair.""" for q, c in zip(self.qubits, self.cbits, strict=True): self.measure(q, c)
def __repr__(self): return ( f"ReadoutBlock(n_qubits={self.n_qubits}, " f"qubits={self.qubits}, cbits={self.cbits}, " f"target_qubits={self.target_qubits}, name='{self.name}')" )