Source code for qarp._types
"""Type definitions or aliases for OpenQARP."""
from dataclasses import dataclass, field
from enum import Enum
from functools import cached_property
import numpy as np
# Type alias for dictionary of sampling results
SamplingDictionary = dict[tuple[int, ...], float]
[docs]
class Shots(Enum):
"""Non-integer shot-count sentinels for the ``n_shots`` knob.
``Shots.EXACT`` (re-exported as ``qarp.EXACT``) requests the ∞-shot limit
of a sampling primitive: the engine computes the exact Born distribution
|ψ|² instead of drawing samples. Deliberately a plain ``Enum``, never
``IntEnum``: arithmetic or ordering on a leaked sentinel must fail loudly
with ``TypeError``, not compute garbage. Enum members survive ``deepcopy``
with identity intact (``copy.n_shots is EXACT`` keeps working through the
QSE/MonteCarlo/QMEGS deepcopy paths — a plain ``object()`` sentinel would
not).
"""
EXACT = "exact"
[docs]
@dataclass
class ExactResult:
"""Exact Born distribution duck-typing ``qx.SamplingResult``.
``keys`` (sorted int64 outcome bitmasks) and ``probs`` carry the
distribution, pruned below ~1e-12 so it sums to 1 − O(pruned) — compare
distributions at ~1e-10, never exactly. ``counts`` is the same data as a
dict, built on first access: estimators that can consume the arrays
(:func:`outcome_arrays`) never pay for a 2^n-entry dict. ``n_shots = 1``
is a normalization trick so every estimator's ``count / n_shots`` yields
the probability unchanged — it is NOT a statistical claim: never infer
shot-noise error bars or adaptive shot budgets from it (the variance of
these numbers is exactly 0). Check ``is_exact`` to discriminate from a
sampled result.
"""
n_qubits: int
keys: np.ndarray
probs: np.ndarray
n_shots: int = 1
is_exact: bool = field(default=True, init=False)
@cached_property
def counts(self) -> dict[int, float]:
return dict(zip(self.keys.tolist(), self.probs.tolist(), strict=True))
def outcome_arrays(result) -> tuple[np.ndarray, np.ndarray]:
"""``(outcomes, counts)`` int64/float64 arrays of a sampling-shaped result.
An :class:`ExactResult` hands over its arrays; a ``qx.SamplingResult``
(whose ``counts`` property converts the C++ map on every access) is read
once. Outcomes are packed into int64, so registers wider than 63 qubits
must take the dict path instead.
"""
keys = getattr(result, "keys", None)
if keys is not None:
return keys, result.probs
counts = result.counts
return (
np.fromiter(counts.keys(), dtype=np.int64, count=len(counts)),
np.fromiter(counts.values(), dtype=np.float64, count=len(counts)),
)
[docs]
class Consumes(Enum):
"""What raw engine output a primitive's estimator consumes.
Declared class-level on each primitive; engines dispatch on it. Lives
here, below both layers, so engines can read it without importing from
``qarp.algorithms``.
Attributes:
COUNTS: Measurement statistics from sampled circuits (protocol
primitives: Sampler, HadamardTest, PauliAveraging, ...).
AMPLITUDES: Simulator statevectors, contracted directly (StateVector).
Requires an engine with amplitude access — noiseless simulation.
"""
COUNTS = 0
AMPLITUDES = 1