Source code for qarp.algorithms._primitives.sampler
"""``Sampler`` primitive — consumes a :class:`qarp.blocks.AnyBlock` and returns
a :class:`~qarp.SamplingDistribution`: probabilities normalised by n_shots,
keyed by LSB-first bit tuples over the measured qubits.
"""
from typing import Optional, Union
from ..._sampling_distribution import SamplingDistribution, distribution_from_result
from ..._types import Shots
from ...blocks import AnyBlock
from .primitive_algorithm import PrimitiveAlgorithm
from .target import Target
[docs]
class Sampler(PrimitiveAlgorithm):
"""qarpx-backed sampling primitive.
Args:
ket: Block to execute.
n_shots: Number of measurement shots (default: use engine default).
``qarp.EXACT`` returns the exact Born distribution ``|ψ|²`` — note:
probabilities, not amplitudes (phases are discarded; raw
amplitudes are simulator internals, ``sim.statevector``).
measured_qubits: Qubit indices whose outcomes appear in the output
tuple. Defaults to ``range(ket.n_qubits)``. Non-measured
qubits are marginalised out by summing probabilities over their
bit values.
initial_state: Optional LSB-indexed amplitudes seeding the register
instead of ``|0…0⟩`` (length ``2**n_qubits``, unit norm within
``1e-10`` — ``ValueError`` at run otherwise). QarpEngine only;
other engines raise ``CapabilityError`` at build. Mutable
between runs for step → snapshot → re-seed loops.
"""
supported_targets = frozenset({Target.SAMPLING})
accepts_initial_state = True
def __init__(
self,
ket: Optional[AnyBlock] = None,
n_shots: Optional[Union[int, Shots]] = None,
measured_qubits: Optional[list[int]] = None,
initial_state=None,
):
super().__init__(ket=ket, n_shots=n_shots, target=Target.SAMPLING)
self.measured_qubits = measured_qubits
self.initial_state = initial_state
self.result: Optional[SamplingDistribution] = None
[docs]
def circuits(self) -> list:
if self.ket is None:
raise ValueError("Sampler requires a ket Block.")
# The ket is itself a Block, so it is this sampler's sole circuit.
self.n_qubits = self.ket.n_qubits
if self.measured_qubits is None:
self.measured_qubits = list(range(self.n_qubits))
return [self.ket]
[docs]
def run(self, results: list) -> SamplingDistribution:
"""Marginal of the first result (``qx.SamplingResult``, or the
duck-typed ``ExactResult`` under ``n_shots=qarp.EXACT``) on
``measured_qubits``."""
sr = results[0]
self.result = distribution_from_result(sr, self.measured_qubits or range(sr.n_qubits))
return self.result