Source code for qarp.blocks._prepares_known_state

"""The ``|0…0⟩``-column declaration shared by state-preparing blocks."""

from __future__ import annotations

from typing import TYPE_CHECKING, Dict, Optional, Tuple

import numpy as np

from .._postselection import PostSelection

# Names a Python class always carries that must not overwrite the block's own.
_SKIP_ATTRS = frozenset(
    {
        "__dict__",
        "__weakref__",
        "__module__",
        "__qualname__",
        "__doc__",
        "__annotations__",
        "__class__",
    }
)

_DECLARING: Dict[str, type] = {}


class _DeclarationMeta(type):
    """Keeps ``isinstance`` working for a declaration that is attached, not inherited."""

    def __instancecheck__(cls, obj) -> bool:
        return getattr(type(obj), "declares_known_state", False)

    def __subclasscheck__(cls, sub) -> bool:
        return getattr(sub, "declares_known_state", False)


[docs] class PreparesKnownState(metaclass=_DeclarationMeta): r"""Declares the state a block leaves behind when applied to ``|0…0⟩``. A primitive is specified by its whole unitary; a block that *prepares* a state is specified by a single column of it, ``U|0…0⟩``, with the remaining ``2^n - 1`` columns free. :class:`~qarp.blocks.AmplitudeAmplificationBlock` is the canonical consumer: ``Q = A·S_0·A†·S_χ`` is correct for *any* unitary ``A`` whose zeroth column is ``|ψ⟩``, because ``A S_0 A† = 2|ψ⟩⟨ψ| - I`` regardless of the rest. So no unitary oracle exists for such a block — §18's exact-equality comparison has nothing to compare against — and this column is the contract instead. Declare it with the :func:`prepares_known_state` decorator, which *attaches* these members rather than inheriting them: every block class has exactly one base, its nanobind C++ counterpart, and a second base raises ``nb_type_init(): invalid number of bases``. This mirrors ``_BlockMixin`` and ``@_attach_mixin`` in ``_block.py``. ``isinstance(block, PreparesKnownState)`` still answers correctly. Declaring is opt-in and orthogonal to the block hierarchy (§13): decorate a ``SimpleBlock`` leaf or a ``CompositeBlockBase`` tree alike, wherever the block happens to live. Parameterized ansätze must *not* declare it — ``UCCBlock``, ``HEABlock``, ``SPABlock``, ``QAOABlock`` have no fixed column, since their output depends on symbol values and on the reference state they are applied to. Two rules keep the declaration honest: * :meth:`target_statevector` is derived from the block's *mathematical definition*, never read back from its own commands. A declaration computed from the circuit compares the implementation with itself and answers the §18 reviewer check with *no*. * It carries **global phase**. Standalone a prep's global phase is unobservable, but it becomes a physical relative phase the moment the block sits under ``ControlledBlock`` — which is exactly what ``AmplitudeEstimationBlock`` does to it (§13). The declaration says nothing about the other columns, so it neither implies nor requires that the block be applied first; it is a statement about one input, not about circuit position. """ declares_known_state = True if TYPE_CHECKING: # Supplied by the decorated block's qarpx base / _BlockMixin. Declared # here (never at runtime) so the attached members type-check. n_qubits: int def statevector(self, initial_state: "np.ndarray | None" = None) -> np.ndarray: ... @property def state_qubits(self) -> Tuple[int, ...]: """Block-local qubit indices carrying the prepared state, ascending. Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do. """ return tuple(range(self.n_qubits)) @property def ancilla_qubits(self) -> Tuple[int, ...]: """The complement of :attr:`state_qubits`, ascending.""" state = set(self.state_qubits) return tuple(q for q in range(self.n_qubits) if q not in state) @property def is_exact(self) -> bool: """``True`` (the default) if :meth:`target_statevector` is met exactly. ``False`` marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks :meth:`prepared_statevector` against :meth:`target_statevector` by infidelity against :attr:`error_bound` rather than by ``atol=1e-10`` elementwise equality. Most blocks never touch this; override alongside :attr:`error_bound`. """ return True @property def error_bound(self) -> float: """Upper bound on ``1 - |⟨target|prepared⟩|²`` (infidelity). Meaningless — and unchecked — when :attr:`is_exact` is ``True`` (the default ``0.0`` here is never read). An approximate block overrides both together; the bound should be computable from the block's own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact. """ return 0.0 @property def ancilla_postselection(self) -> Optional[PostSelection]: """Condition under which the prepared state appears; ``None`` if none. ``None`` means every ancilla returns to ``|0⟩`` with probability 1 — the block is deterministic, and therefore safe under ``ControlledBlock`` and safe to hand to ``validate_amplification_blocks``. A returned condition must fix exactly :attr:`ancilla_qubits`: the state then exists only on that branch, the caller must condition on it, and the block is *not* control-safe. Pass it straight to a ``Sampler`` result — :meth:`PostSelection.apply` speaks that currency already. """ return None
[docs] def target_statevector(self) -> np.ndarray: """The declared state on :attr:`state_qubits`. ``2**len(state_qubits)`` amplitudes, LSB-indexed (bit ``i`` is ``state_qubits[i]``), unit norm, global phase included. """ raise NotImplementedError( f"{type(self).__name__} declares PreparesKnownState but does not " "implement target_statevector()." )
[docs] def prepared_statevector(self) -> Tuple[np.ndarray, float]: """What the built circuit actually leaves on :attr:`state_qubits`. Returns ``(state, probability)`` — the counterpart to :meth:`target_statevector`, which the two are asserted equal against. ``probability`` is that of :attr:`ancilla_postselection`, and is ``1.0`` for a deterministic block. Exponential in ``n_qubits``: a validation tool, not a simulation path. """ ancillas = self.ancilla_qubits condition = self.ancilla_postselection # A sector spec keeps the full register width, so the result would not # live on state_qubits; the error belongs here, not in a numpy shape. if condition is not None and ( not condition.is_fixed or tuple(condition.qubits) != tuple(ancillas) ): raise ValueError( f"{type(self).__name__}.ancilla_postselection must be a fixed-bit " f"PostSelection on exactly ancilla_qubits={ancillas}, got {condition!r}" ) psi = self.statevector() if not ancillas: return psi, 1.0 if condition is None: condition = PostSelection(dict.fromkeys(ancillas, 0)) return condition.apply_statevector(psi, self.n_qubits)
[docs] def prepares_known_state(cls: type) -> type: """Attach :class:`PreparesKnownState` to ``cls`` and register it. Members the block defines itself are left alone, so a block overrides ``state_qubits`` / ``ancilla_postselection`` simply by defining them. Raises ``ValueError`` when a *different* class with the same ``__name__`` is already registered: :func:`declaring_blocks` is keyed by bare name, and a silent overwrite would drop the earlier block from the conformance gate. """ key = f"{cls.__module__}.{cls.__qualname__}" for existing in _DECLARING.values(): if existing is not cls and existing.__name__ == cls.__name__: raise ValueError( f"{key} declares PreparesKnownState but the name {cls.__name__!r} is " f"already registered by {existing.__module__}.{existing.__qualname__}" ) for name, attr in vars(PreparesKnownState).items(): if name in _SKIP_ATTRS or name.startswith("_PreparesKnownState"): continue if name in vars(cls): # the block's own override wins continue setattr(cls, name, attr) _DECLARING[key] = cls return cls
[docs] def declaring_blocks() -> Dict[str, type]: """Every block class that has declared the contract, by name. Registration happens at class-definition time, so a block appears here once its module is imported — which ``qarp.blocks.__init__`` does for the whole public surface (§15). Only *decorated* classes appear: an undecorated subclass inherits the declaration (``isinstance`` is true) without registering, so a gate over the public surface must also walk ``issubclass(cls, PreparesKnownState)``. """ return {cls.__name__: cls for cls in _DECLARING.values()}