Source code for pytest_quantum.stats.shots

"""Shot-count calculators.

Quantum tests require running a circuit many times (``shots``).  Choosing the
right shot count is surprisingly nuanced:

* Too few shots → test is flaky (random failures unrelated to correctness).
* Too many shots → test is slow and wasteful.

This module provides principled formulas so you never have to guess.
"""

from __future__ import annotations

import math


[docs] def min_shots( epsilon: float, alpha: float = 0.05, power: float = 0.80, ) -> int: """Minimum shots to detect a total-variation distance of *epsilon*. Based on two-sample statistical power analysis: .. math:: N = \\lceil (z_{1-\\alpha/2} + z_{\\text{power}})^2 \\; / \\; (2\\varepsilon^2) \\rceil Args: epsilon: Minimum detectable total variation distance (TVD). For example, ``0.01`` means the test can reliably catch a 1 % deviation from the expected distribution. alpha: Significance level (default ``0.05`` → 95 % confidence). power: Statistical power, i.e. probability of detecting a real error (default ``0.80`` → 80 % power). Returns: Minimum recommended shot count as an integer. Raises: ValueError: If any argument is outside its valid range. Examples:: >>> min_shots(0.01) # detect 1% TVD 7299 >>> min_shots(0.05) # detect 5% TVD 293 >>> min_shots(0.10) # detect 10% TVD 74 >>> min_shots(0.01, alpha=0.01, power=0.90) # stricter 11282 """ if not (0 < epsilon < 1): raise ValueError(f"epsilon must be in (0, 1), got {epsilon!r}") if not (0 < alpha < 1): raise ValueError(f"alpha must be in (0, 1), got {alpha!r}") if not (0 < power < 1): raise ValueError(f"power must be in (0, 1), got {power!r}") from scipy.stats import norm # lazy import — scipy is a hard dependency z_alpha = float(norm.ppf(1 - alpha / 2)) z_power = float(norm.ppf(power)) n = (z_alpha + z_power) ** 2 / (2 * epsilon**2) return math.ceil(n)