Source code for uniqc.circuit_builder.qubit

"""
Named quantum register and qubit types.

This module provides:
- Qubit: Named reference to a single qubit within a register
- QReg: Named quantum register with indexing and slicing support
- QRegSlice: Slice view of a QReg for multi-qubit operations

Example usage::

    qr = QReg(name="a", size=4, base_index=0)
    q0 = qr[0]           # Qubit(name="a[0]", index=0, base_index=0)
    q_slice = qr[1:3]    # QRegSlice with qubits at indices 1, 2
    int(q0)              # 0 (physical qubit index)
    int(qr[2])           # 2
"""

from __future__ import annotations

from collections.abc import Iterator
from dataclasses import dataclass
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    pass

__all__ = ["Qubit", "QReg", "QRegSlice"]


[docs] @dataclass class Qubit: """Named reference to a single qubit within a quantum register. Attributes: name: Full name of the qubit, e.g., "a[0]" index: Index within the parent register base_index: Starting physical qubit index for the parent register Example: >>> q = Qubit(name="a[2]", index=2, base_index=10) >>> int(q) 12 """ name: str index: int base_index: int def __int__(self) -> int: """Return the physical qubit index (base_index + index).""" return self.base_index + self.index def __eq__(self, other: object) -> bool: """Two Qubits are equal if they have the same name.""" if not isinstance(other, Qubit): return NotImplemented return self.name == other.name def __hash__(self) -> int: """Hash based on name for use in sets and dicts.""" return hash(self.name) def __repr__(self) -> str: return f"Qubit({self.name!r}, index={self.index}, base_index={self.base_index})"
[docs] class QRegSlice: """Slice view of a QReg for multi-qubit operations. This class provides iteration and indexing over a subset of qubits from a parent register. Attributes: register: Parent QReg indices: List of indices within the parent register """ def __init__(self, register: QReg, indices: list[int]) -> None: self._register = register self._indices = indices @property def register(self) -> QReg: return self._register @property def indices(self) -> list[int]: return self._indices.copy() def __len__(self) -> int: return len(self._indices) def __iter__(self) -> Iterator[Qubit]: for idx in self._indices: yield self._register[idx] def __getitem__(self, key: int) -> Qubit: """Get a specific Qubit from the slice.""" return self._register[self._indices[key]] def __repr__(self) -> str: indices_str = ", ".join(map(str, self._indices)) return f"QRegSlice({self._register.name}[{indices_str}])"
[docs] class QReg: """Named quantum register with indexing and slicing support. QReg provides named access to qubits within a circuit, supporting integer indexing, negative indexing, and slicing. Attributes: name: Name of the register size: Number of qubits in the register base_index: Starting physical qubit index (set when mapped to circuit) Example: >>> qr = QReg(name="a", size=4, base_index=0) >>> qr[0] Qubit('a[0]', index=0, base_index=0) >>> qr[1:3] QRegSlice(a[1, 2]) >>> len(qr) 4 """ def __init__( self, name: str, size: int, base_index: int = 0, ) -> None: if size <= 0: raise ValueError(f"QReg size must be positive, got {size}") self._name = name self._size = size self._base_index = base_index @property def name(self) -> str: return self._name @property def size(self) -> int: return self._size @property def base_index(self) -> int: return self._base_index @base_index.setter def base_index(self, value: int) -> None: self._base_index = value def __len__(self) -> int: return self._size def __getitem__(self, key: int | slice) -> Qubit | QRegSlice: """Index or slice the register. Args: key: Integer index or slice Returns: Qubit for single index, QRegSlice for slice Raises: IndexError: If index is out of range """ if isinstance(key, slice): # Convert slice to list of indices start, stop, step = key.indices(self._size) indices = list(range(start, stop, step)) return QRegSlice(self, indices) else: # Handle negative indexing if key < 0: key = self._size + key if key < 0 or key >= self._size: raise IndexError(f"QReg index {key} out of range for size {self._size}") return Qubit( name=f"{self._name}[{key}]", index=key, base_index=self._base_index, ) @property def qubits(self) -> list[Qubit]: """Return list of all Qubit objects in this register.""" return [self[i] for i in range(self._size)] def __repr__(self) -> str: return f"QReg({self._name!r}, size={self._size}, base_index={self._base_index})"