qarp

OpenQARP — quantum algorithm research platform.

Cross-cutting names only: config, EXACT / Shots, the result types and post-selection. Public depth: every subpackage is a namespace (absorb, algorithms, blocks, cutting, devices, emit, engines, factories, graphs, operators, optimizers, plotting, resources, utils), plus two modules — errors (the exception surface, imported qualified: qarp.errors.CapabilityError) and endianness (the MSB-boundary conversions, §1 of the conventions). None of them is imported here, so import qarp stays thin; from qarp import * pulls them all.

class qarp.Consumes(*values)[source]

Bases: 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.

Variables:
  • 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.

AMPLITUDES = 1
COUNTS = 0
class qarp.ExactResult(n_qubits: int, keys: ndarray, probs: ndarray, n_shots: int = 1)[source]

Bases: object

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 (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.

property counts: dict[int, float]
is_exact: bool = True
keys: ndarray
n_qubits: int
n_shots: int = 1
probs: ndarray
class qarp.MPIConfig[source]

Bases: object

MPI launcher detection — nothing more.

OpenQARP does not implement MPI parallelism: a composite algorithm constructed under a multi-rank launcher raises CapabilityError rather than running the same serial work on every rank. Detection is environment-only so no mpi4py import (and no MPI_Init_thread) ever happens inside OpenQARP.

Set QARP_DISABLE_MPI=1 to silence the detection (e.g. one rank of a job that drives OpenQARP serially on purpose).

static is_disabled() → bool[source]

Check if MPI is explicitly disabled via QARP_DISABLE_MPI.

classmethod is_mpi_env() → bool[source]

Check if running under an MPI launcher (env-only, no mpi4py import).

classmethod world_size() → int[source]

Get MPI world size from environment (no mpi4py import).

class qarp.PostSelected(distribution: SamplingDistribution, success_rate: float)[source]

Bases: object

Conditioned distribution plus the probability mass that survived.

distribution: SamplingDistribution
success_rate: float
class qarp.PostSelection(conditions: Mapping[int, int])[source]

Bases: object

Immutable post-selection condition on a subset of qubits.

Construct with a {qubit index: required bit} mapping for fixed-bit conditions, or via the sector constructors hamming_weight() / parity(). Hashable: fixed-bit specs compare by their conditions, sector specs by predicate identity — either way a spec can key a cache across a parameter sweep.

apply(distribution: Mapping[tuple[int, ...], float]) → PostSelected[source]

Condition a distribution keyed by LSB-first bit tuples.

Keys must share one width covering every selected qubit. Fixed-bit specs drop the (now-constant) selected positions from the output keys; sector specs keep full-width keys. Zero surviving mass yields an empty distribution and a success rate of 0 — no raise, so parameter sweeps survive nodes with vanishing support.

apply_statevector(statevector, n_qubits: int) → Tuple[ndarray, float][source]

Project a statevector onto the condition; renormalise.

Returns (conditional_state, success_probability) with success = ‖P|ψ⟩‖². Fixed-bit specs return the state of the surviving qubits (ascending order, 2^(n-k) amplitudes — the selected qubits collapsed to a product basis state and factor out). Sector specs return the projected state on the FULL register (2^n): a subspace projection leaves the selected qubits entangled with the rest, so no reduction exists. Zero success returns the zero vector of the appropriate size with p = 0.0.

classmethod hamming_weight(qubits: Sequence[int], k: int) → PostSelection[source]

Keep outcomes with exactly k ones across qubits (particle-number sector under Jordan-Wigner).

property is_fixed: bool

True for fixed-bit conditions (selected qubits are removed from the output); False for sector conditions (full width preserved).

classmethod parity(qubits: Sequence[int], even: bool = True) → PostSelection[source]

Keep outcomes whose bit-sum over qubits is even (or odd).

qubits: Tuple[int, ...]
success_rate(distribution: Mapping[tuple[int, ...], float]) → float[source]

Kept probability mass only (shortcut for apply(...).success_rate).

class qarp.SamplingDistribution(outcomes, probabilities, n_bits_measured: int, *, n_shots: int | None = None)[source]

Bases: Mapping[tuple[int, …], float]

Read-only sampling distribution over LSB-first bit tuples.

Reads like a {bits-tuple: probability} dict, iterating in ascending order of the packed integer. outcomes and probabilities give the same data as aligned read-only arrays; probability_of(k) looks one up by its packed integer; to_dict() makes a plain dict, faster than dict(d), which looks each key up again. Positions passed to the analysis methods index the key tuple, not physical qubits.

Parameters:
  • outcomes – Packed integers Σ_i b_i · 2**i of the keys, strictly ascending, each below 2**n_bits_measured.

  • probabilities – One probability per outcome.

  • n_bits_measured – Bits per key — the number of measured qubits.

  • n_shots – Shots behind a sampled distribution; None for an exact one. Equality and iteration ignore it.

counts() → ndarray[source]

Shot counts aligned with outcomes. ValueError when exact.

classmethod from_dict(mapping: Mapping, *, n_bits_measured: int | None = None, n_shots: int | None = None) → SamplingDistribution[source]

A distribution from a {bits-tuple: probability} mapping.

Keys must be 0/1 tuples of one width. n_bits_measured gives the width of an empty mapping and must match the keys otherwise. A SamplingDistribution comes back as it is unless n_shots is given.

hellinger_fidelity(other: Mapping) → float[source]

(Σ √(p q))², qiskit’s definition, probabilities as given.

items() → a set-like object providing a view on D's items[source]
marginal(positions) → SamplingDistribution[source]

The distribution over positions: bit i of a new key is key position positions[i]. Keeps n_shots.

most_likely() → tuple[tuple[int, ...], float][source]

The most probable (bits, probability); the smaller packed outcome wins a tie. ValueError when empty.

property n_bits_measured: int

the number of measured qubits.

Type:

Bits per key

property n_shots: int | None

Shots behind a sampled distribution; None for an exact one.

property outcomes: ndarray

Packed keys Σ_i b_i · 2**i, strictly ascending, read-only.

parity_expectation(positions) → float[source]

Σ p · (−1)^(parity of the bits at positions): the expectation of the product of Z over positions, probabilities as given. Empty positions give the total probability.

property probabilities: ndarray

Probabilities aligned with outcomes, read-only.

probability_of(outcome: int) → float[source]

Probability of the outcome whose packed integer is outcome.

outcome is Σ_i b_i · 2**i of the key tuple; an outcome that never occurred has probability 0.0. Raises ValueError outside [0, 2**n_bits_measured).

sample(n_shots: int, seed: int | Generator | None = None) → SamplingDistribution[source]

A multinomial draw of n_shots from the probabilities renormalised to sum to 1 (exact results prune below 1e-12). Outcomes drawn zero times are dropped.

standard_errors() → ndarray[source]

Binomial standard error sqrt(p(1 - p) / n_shots) per outcome; zeros for an exact distribution, whose values carry no shot noise.

to_dense(max_bits: int = 28) → ndarray[source]

The length 2**n_bits_measured probability vector, indexed by packed outcome. ValueError past max_bits, which bounds the allocation (2**28 doubles are 2 GiB).

to_dict() → dict[tuple[int, ...], float][source]

A plain {bits-tuple: probability} dict, in the same order.

top(k: int) → list[tuple[tuple[int, ...], float]][source]

The k most probable (bits, probability) pairs, most probable first; ties go to the smaller packed outcome.

total_variation(other: Mapping) → float[source]

½ Σ |p − q| over the union of outcomes, probabilities as given.

values() → an object providing a view on D's values[source]
class qarp.Shots(*values)[source]

Bases: 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'
qarp.EXACT = Shots.EXACT

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).

qarp.SamplingDictionary

A Mapping is a generic container for associating key/value pairs.

This class provides concrete generic implementations of all methods except for __getitem__, __iter__, and __len__.

alias of Mapping[tuple[int, …], float]

class qarp.SamplingDistribution(outcomes, probabilities, n_bits_measured: int, *, n_shots: int | None = None)[source]

Read-only sampling distribution over LSB-first bit tuples.

Reads like a {bits-tuple: probability} dict, iterating in ascending order of the packed integer. outcomes and probabilities give the same data as aligned read-only arrays; probability_of(k) looks one up by its packed integer; to_dict() makes a plain dict, faster than dict(d), which looks each key up again. Positions passed to the analysis methods index the key tuple, not physical qubits.

Parameters:
  • outcomes – Packed integers Σ_i b_i · 2**i of the keys, strictly ascending, each below 2**n_bits_measured.

  • probabilities – One probability per outcome.

  • n_bits_measured – Bits per key — the number of measured qubits.

  • n_shots – Shots behind a sampled distribution; None for an exact one. Equality and iteration ignore it.

counts() → ndarray[source]

Shot counts aligned with outcomes. ValueError when exact.

classmethod from_dict(mapping: Mapping, *, n_bits_measured: int | None = None, n_shots: int | None = None) → SamplingDistribution[source]

A distribution from a {bits-tuple: probability} mapping.

Keys must be 0/1 tuples of one width. n_bits_measured gives the width of an empty mapping and must match the keys otherwise. A SamplingDistribution comes back as it is unless n_shots is given.

hellinger_fidelity(other: Mapping) → float[source]

(Σ √(p q))², qiskit’s definition, probabilities as given.

marginal(positions) → SamplingDistribution[source]

The distribution over positions: bit i of a new key is key position positions[i]. Keeps n_shots.

most_likely() → tuple[tuple[int, ...], float][source]

The most probable (bits, probability); the smaller packed outcome wins a tie. ValueError when empty.

property n_bits_measured: int

the number of measured qubits.

Type:

Bits per key

property n_shots: int | None

Shots behind a sampled distribution; None for an exact one.

property outcomes: ndarray

Packed keys Σ_i b_i · 2**i, strictly ascending, read-only.

parity_expectation(positions) → float[source]

Σ p · (−1)^(parity of the bits at positions): the expectation of the product of Z over positions, probabilities as given. Empty positions give the total probability.

property probabilities: ndarray

Probabilities aligned with outcomes, read-only.

probability_of(outcome: int) → float[source]

Probability of the outcome whose packed integer is outcome.

outcome is Σ_i b_i · 2**i of the key tuple; an outcome that never occurred has probability 0.0. Raises ValueError outside [0, 2**n_bits_measured).

sample(n_shots: int, seed: int | Generator | None = None) → SamplingDistribution[source]

A multinomial draw of n_shots from the probabilities renormalised to sum to 1 (exact results prune below 1e-12). Outcomes drawn zero times are dropped.

standard_errors() → ndarray[source]

Binomial standard error sqrt(p(1 - p) / n_shots) per outcome; zeros for an exact distribution, whose values carry no shot noise.

to_dense(max_bits: int = 28) → ndarray[source]

The length 2**n_bits_measured probability vector, indexed by packed outcome. ValueError past max_bits, which bounds the allocation (2**28 doubles are 2 GiB).

to_dict() → dict[tuple[int, ...], float][source]

A plain {bits-tuple: probability} dict, in the same order.

top(k: int) → list[tuple[tuple[int, ...], float]][source]

The k most probable (bits, probability) pairs, most probable first; ties go to the smaller packed outcome.

total_variation(other: Mapping) → float[source]

½ Σ |p − q| over the union of outcomes, probabilities as given.

qarp.config = <qarp._config.__ConfigObject__ object>