Metrics

The qtest.metrics subpackage groups two complementary toolkits used internally by the assertions:

  • qtest.metrics.distances — deterministic distance / divergence functions over probability distributions, quantum states, and operators (total variation, Hellinger, fidelity, trace distance, Hilbert–Schmidt distance).

  • qtest.metrics.statistical_tests — frequentist hypothesis tests and shot-noise tolerance helpers (Pearson \(\chi^2\), Kolmogorov–Smirnov, auto_tolerance()).

  • qtest.metrics.entanglement — entanglement and information measures (partial trace, purity, von Neumann entropy, entanglement entropy).

All functions are pure: they validate input, never mutate their arguments, and raise ValueError on malformed input.

Public API

total_variation_distance

Total variation distance between two discrete distributions.

hellinger_distance

Hellinger distance between two discrete distributions.

fidelity

Quantum fidelity between two states.

trace_distance

Trace distance between two quantum states.

hilbert_schmidt_distance

Hilbert–Schmidt distance between two operators.

chi_square_test

Pearson \(\chi^{2}\) goodness-of-fit test for measurement counts.

kolmogorov_smirnov_test

One-sample Kolmogorov–Smirnov test against a continuous CDF.

auto_tolerance

Shot-noise-aware default tolerance for distribution assertions.

partial_trace

Partial trace of a state / density matrix, keeping keep qubits.

purity

Purity \(\mathrm{tr}(\rho^2)\) of a state / density matrix.

von_neumann_entropy

Von Neumann entropy \(S(\rho) = -\mathrm{tr}(\rho \log \rho)\).

entanglement_entropy

Entanglement entropy of subsystem qubits versus the rest.

Reference

Metric and statistical-test functions used by qtest’s assertions.

This package gathers two complementary toolkits:

  • qtest.metrics.distances — deterministic distance / divergence functions over probability distributions, quantum states, and operators (TVD, Hellinger, fidelity, trace distance, Hilbert–Schmidt distance).

  • qtest.metrics.statistical_tests — frequentist hypothesis tests and shot-noise tolerance helpers consumed by assert_distribution_close and friends (Pearson \(\chi^2\), Kolmogorov–Smirnov, auto_tolerance).

All functions are pure: they perform input validation, never mutate their arguments, and raise ValueError on malformed input.

qtest.metrics.auto_tolerance(shots, confidence=0.99)[source]

Shot-noise-aware default tolerance for distribution assertions.

Uses the Wald-style normal approximation

\[\tau \;=\; z_{1 - \alpha / 2} \cdot \sqrt{ \frac{1}{N} }\]

where \(N\) is the shot count, \(\alpha = 1 - \text{confidence}\) is the two-sided significance level, and \(z_{1 - \alpha/2}\) is the corresponding standard normal quantile.

Parameters:
  • shots (int) – Number of measurement shots. Must be positive.

  • confidence (float, optional) – Two-sided confidence level in (0, 1). Defaults to 0.99.

Returns:

Suggested tolerance for assert_distribution_close-style checks.

Return type:

float

Raises:

ValueError – If shots is non-positive or confidence is outside (0, 1).

qtest.metrics.chi_square_test(observed, expected, shots)[source]

Pearson \(\chi^{2}\) goodness-of-fit test for measurement counts.

\[\chi^{2} \;=\; \sum_{x} \frac{ (O_{x} - E_{x})^{2} }{ E_{x} }\]

where \(O_{x}\) is the observed count for outcome x and \(E_{x} = N \cdot P_{\text{expected}}(x)\) is the expected count under the null hypothesis at \(N\) shots.

Outcomes missing from either mapping are treated as zero. Bins with both observed and expected equal to zero are dropped. A bin with E = 0 but O > 0 is a hard rejection — that case raises rather than returning an infinite statistic.

Parameters:
  • observed (Mapping[str, float]) – Observed counts per outcome (integer-valued; floats are tolerated and passed through to scipy).

  • expected (Mapping[str, float]) – Expected probabilities per outcome. Must form a probability distribution (non-negative, summing to one).

  • shots (int) – Total number of measurement shots used to convert expected probabilities into expected counts.

Returns:

  • statistic (float) – The \(\chi^{2}\) test statistic.

  • p_value (float) – Two-sided p-value under the null hypothesis.

Raises:

ValueError – If shots is non-positive, either mapping is empty, expected is not a valid probability distribution, or a non-empty observed bin has zero expected probability.

Return type:

tuple[float, float]

qtest.metrics.entanglement_entropy(state, qubits, num_qubits=None, base=2.0)[source]

Entanglement entropy of subsystem qubits versus the rest.

For a pure global state this is the von Neumann entropy of the reduced density matrix on qubits; it is 0 iff the bipartition is a product state (no entanglement across the cut) and positive otherwise.

Parameters:
Return type:

float

qtest.metrics.fidelity(state1, state2)[source]

Quantum fidelity between two states.

For pure state vectors \(|\psi\rangle, |\phi\rangle\):

\[F(\psi, \phi) \;=\; |\langle \psi | \phi \rangle|^{2}.\]

For general (mixed) density matrices \(\rho, \sigma\) the Uhlmann–Jozsa fidelity is used:

\[F(\rho, \sigma) \;=\; \left( \mathrm{tr}\, \sqrt{ \sqrt{\rho}\, \sigma\, \sqrt{\rho} } \right)^{2}.\]

The two arguments may independently be state vectors or density matrices; state vectors are lifted to rank-1 projectors on the fly. The output is clipped to \([0, 1]\) to absorb floating-point overshoot.

Parameters:
  • state1 (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

  • state2 (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

Returns:

Fidelity in \([0, 1]\).

Return type:

float

Raises:

ValueError – If shapes are incompatible or inputs are neither 1-D nor square 2-D.

qtest.metrics.hellinger_distance(p, q)[source]

Hellinger distance between two discrete distributions.

\[H(P, Q) \;=\; \frac{1}{\sqrt{2}} \sqrt{ \sum_{x} \left( \sqrt{P(x)} - \sqrt{Q(x)} \right)^{2} }\]

Missing keys are treated as zero probability. The result lies in \([0, 1]\), with 0 iff \(P = Q\) and 1 iff the supports are disjoint.

Parameters:
  • p (Mapping[str, float]) – Probability mass functions keyed by outcome label.

  • q (Mapping[str, float]) – Probability mass functions keyed by outcome label.

Returns:

Hellinger distance in \([0, 1]\).

Return type:

float

Raises:

ValueError – If either input is empty or not a valid probability distribution.

qtest.metrics.hilbert_schmidt_distance(op1, op2)[source]

Hilbert–Schmidt distance between two operators.

\[d_{\mathrm{HS}}(A, B) \;=\; \sqrt{ \mathrm{tr}\!\left( (A - B)^{\dagger} (A - B) \right) } \;=\; \| A - B \|_{\mathrm{F}}\]

i.e. the Frobenius norm of the difference.

Parameters:
  • op1 (np.ndarray) – Two-dimensional operators of identical shape.

  • op2 (np.ndarray) – Two-dimensional operators of identical shape.

Returns:

Non-negative HS distance.

Return type:

float

Raises:

ValueError – If either input is not 2-D, or if shapes differ.

qtest.metrics.kolmogorov_smirnov_test(observed, expected_cdf)[source]

One-sample Kolmogorov–Smirnov test against a continuous CDF.

\[D_{n} \;=\; \sup_{x} \left| F_{n}(x) - F(x) \right|\]

where \(F_{n}\) is the empirical CDF of observed and \(F\) is expected_cdf.

Parameters:
  • observed (Sequence[float]) – Samples drawn from the candidate distribution.

  • expected_cdf (Callable[[float], float]) – Cumulative distribution function of the reference distribution.

Returns:

  • statistic (float) – KS test statistic \(D_{n}\).

  • p_value (float) – Two-sided p-value under the null hypothesis that the samples are drawn from the distribution described by expected_cdf.

Raises:

ValueError – If observed is empty or expected_cdf is not callable.

Return type:

tuple[float, float]

qtest.metrics.partial_trace(matrix, keep, num_qubits=None)[source]

Partial trace of a state / density matrix, keeping keep qubits.

Parameters:
  • matrix (ndarray) – A state vector (1-D, lifted to a projector) or a density matrix (2-D, square) over num_qubits qubits.

  • keep (list[int]) – Qubit indices (0-based) of the subsystem to keep; the rest are traced out. Order of the returned subsystem follows ascending qubit index.

  • num_qubits (int | None) – Total qubit count. Inferred from the matrix dimension when None.

Returns:

Reduced density matrix of shape (2**k, 2**k) with k = len(keep).

Return type:

np.ndarray

qtest.metrics.purity(rho)[source]

Purity \(\mathrm{tr}(\rho^2)\) of a state / density matrix.

Equals 1 for a pure state and < 1 for a mixed one (minimum 1/d for the maximally mixed state).

Parameters:

rho (ndarray)

Return type:

float

qtest.metrics.total_variation_distance(p, q)[source]

Total variation distance between two discrete distributions.

\[\mathrm{TVD}(P, Q) \;=\; \tfrac{1}{2} \sum_{x} \left| P(x) - Q(x) \right|\]

Missing keys are treated as zero probability. Both inputs must individually be valid probability distributions (non-negative, summing to one).

Parameters:
  • p (Mapping[str, float]) – Probability mass functions keyed by outcome label.

  • q (Mapping[str, float]) – Probability mass functions keyed by outcome label.

Returns:

TVD in \([0, 1]\).

Return type:

float

Raises:

ValueError – If either input is empty or not a valid probability distribution.

qtest.metrics.trace_distance(rho, sigma)[source]

Trace distance between two quantum states.

\[D(\rho, \sigma) \;=\; \tfrac{1}{2}\, \mathrm{tr}\, |\rho - \sigma|\]

where \(|A| \equiv \sqrt{A^{\dagger} A}\). For Hermitian rho - sigma this reduces to half the sum of the absolute eigenvalues.

Either argument may be a state vector (lifted to a rank-1 projector) or a density matrix.

Parameters:
  • rho (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

  • sigma (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

Returns:

Trace distance in \([0, 1]\).

Return type:

float

Raises:

ValueError – If shapes are incompatible.

qtest.metrics.von_neumann_entropy(rho, base=2.0)[source]

Von Neumann entropy \(S(\rho) = -\mathrm{tr}(\rho \log \rho)\).

Parameters:
  • rho (ndarray) – State vector (entropy 0) or density matrix.

  • base (float) – Logarithm base. 2 (default) gives the entropy in bits — a single qubit’s maximally mixed state then has entropy 1.

Return type:

float

Distances

Distance and divergence measures for distributions, states, and operators.

All distance functions are symmetric in their arguments, return a non-negative float, and raise ValueError for malformed input.

qtest.metrics.distances.total_variation_distance(p, q)[source]

Total variation distance between two discrete distributions.

\[\mathrm{TVD}(P, Q) \;=\; \tfrac{1}{2} \sum_{x} \left| P(x) - Q(x) \right|\]

Missing keys are treated as zero probability. Both inputs must individually be valid probability distributions (non-negative, summing to one).

Parameters:
  • p (Mapping[str, float]) – Probability mass functions keyed by outcome label.

  • q (Mapping[str, float]) – Probability mass functions keyed by outcome label.

Returns:

TVD in \([0, 1]\).

Return type:

float

Raises:

ValueError – If either input is empty or not a valid probability distribution.

qtest.metrics.distances.hellinger_distance(p, q)[source]

Hellinger distance between two discrete distributions.

\[H(P, Q) \;=\; \frac{1}{\sqrt{2}} \sqrt{ \sum_{x} \left( \sqrt{P(x)} - \sqrt{Q(x)} \right)^{2} }\]

Missing keys are treated as zero probability. The result lies in \([0, 1]\), with 0 iff \(P = Q\) and 1 iff the supports are disjoint.

Parameters:
  • p (Mapping[str, float]) – Probability mass functions keyed by outcome label.

  • q (Mapping[str, float]) – Probability mass functions keyed by outcome label.

Returns:

Hellinger distance in \([0, 1]\).

Return type:

float

Raises:

ValueError – If either input is empty or not a valid probability distribution.

qtest.metrics.distances.fidelity(state1, state2)[source]

Quantum fidelity between two states.

For pure state vectors \(|\psi\rangle, |\phi\rangle\):

\[F(\psi, \phi) \;=\; |\langle \psi | \phi \rangle|^{2}.\]

For general (mixed) density matrices \(\rho, \sigma\) the Uhlmann–Jozsa fidelity is used:

\[F(\rho, \sigma) \;=\; \left( \mathrm{tr}\, \sqrt{ \sqrt{\rho}\, \sigma\, \sqrt{\rho} } \right)^{2}.\]

The two arguments may independently be state vectors or density matrices; state vectors are lifted to rank-1 projectors on the fly. The output is clipped to \([0, 1]\) to absorb floating-point overshoot.

Parameters:
  • state1 (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

  • state2 (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

Returns:

Fidelity in \([0, 1]\).

Return type:

float

Raises:

ValueError – If shapes are incompatible or inputs are neither 1-D nor square 2-D.

qtest.metrics.distances.trace_distance(rho, sigma)[source]

Trace distance between two quantum states.

\[D(\rho, \sigma) \;=\; \tfrac{1}{2}\, \mathrm{tr}\, |\rho - \sigma|\]

where \(|A| \equiv \sqrt{A^{\dagger} A}\). For Hermitian rho - sigma this reduces to half the sum of the absolute eigenvalues.

Either argument may be a state vector (lifted to a rank-1 projector) or a density matrix.

Parameters:
  • rho (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

  • sigma (np.ndarray) – State vectors (1-D) or density matrices (2-D, square).

Returns:

Trace distance in \([0, 1]\).

Return type:

float

Raises:

ValueError – If shapes are incompatible.

qtest.metrics.distances.hilbert_schmidt_distance(op1, op2)[source]

Hilbert–Schmidt distance between two operators.

\[d_{\mathrm{HS}}(A, B) \;=\; \sqrt{ \mathrm{tr}\!\left( (A - B)^{\dagger} (A - B) \right) } \;=\; \| A - B \|_{\mathrm{F}}\]

i.e. the Frobenius norm of the difference.

Parameters:
  • op1 (np.ndarray) – Two-dimensional operators of identical shape.

  • op2 (np.ndarray) – Two-dimensional operators of identical shape.

Returns:

Non-negative HS distance.

Return type:

float

Raises:

ValueError – If either input is not 2-D, or if shapes differ.

Statistical tests

Frequentist hypothesis tests and shot-noise helpers used by qtest assertions.

All functions are pure: they validate their inputs and return numerical results without mutating arguments. Tests follow the scipy.stats conventions for (statistic, p_value) tuples.

qtest.metrics.statistical_tests.chi_square_test(observed, expected, shots)[source]

Pearson \(\chi^{2}\) goodness-of-fit test for measurement counts.

\[\chi^{2} \;=\; \sum_{x} \frac{ (O_{x} - E_{x})^{2} }{ E_{x} }\]

where \(O_{x}\) is the observed count for outcome x and \(E_{x} = N \cdot P_{\text{expected}}(x)\) is the expected count under the null hypothesis at \(N\) shots.

Outcomes missing from either mapping are treated as zero. Bins with both observed and expected equal to zero are dropped. A bin with E = 0 but O > 0 is a hard rejection — that case raises rather than returning an infinite statistic.

Parameters:
  • observed (Mapping[str, float]) – Observed counts per outcome (integer-valued; floats are tolerated and passed through to scipy).

  • expected (Mapping[str, float]) – Expected probabilities per outcome. Must form a probability distribution (non-negative, summing to one).

  • shots (int) – Total number of measurement shots used to convert expected probabilities into expected counts.

Returns:

  • statistic (float) – The \(\chi^{2}\) test statistic.

  • p_value (float) – Two-sided p-value under the null hypothesis.

Raises:

ValueError – If shots is non-positive, either mapping is empty, expected is not a valid probability distribution, or a non-empty observed bin has zero expected probability.

Return type:

tuple[float, float]

qtest.metrics.statistical_tests.kolmogorov_smirnov_test(observed, expected_cdf)[source]

One-sample Kolmogorov–Smirnov test against a continuous CDF.

\[D_{n} \;=\; \sup_{x} \left| F_{n}(x) - F(x) \right|\]

where \(F_{n}\) is the empirical CDF of observed and \(F\) is expected_cdf.

Parameters:
  • observed (Sequence[float]) – Samples drawn from the candidate distribution.

  • expected_cdf (Callable[[float], float]) – Cumulative distribution function of the reference distribution.

Returns:

  • statistic (float) – KS test statistic \(D_{n}\).

  • p_value (float) – Two-sided p-value under the null hypothesis that the samples are drawn from the distribution described by expected_cdf.

Raises:

ValueError – If observed is empty or expected_cdf is not callable.

Return type:

tuple[float, float]

qtest.metrics.statistical_tests.auto_tolerance(shots, confidence=0.99)[source]

Shot-noise-aware default tolerance for distribution assertions.

Uses the Wald-style normal approximation

\[\tau \;=\; z_{1 - \alpha / 2} \cdot \sqrt{ \frac{1}{N} }\]

where \(N\) is the shot count, \(\alpha = 1 - \text{confidence}\) is the two-sided significance level, and \(z_{1 - \alpha/2}\) is the corresponding standard normal quantile.

Parameters:
  • shots (int) – Number of measurement shots. Must be positive.

  • confidence (float, optional) – Two-sided confidence level in (0, 1). Defaults to 0.99.

Returns:

Suggested tolerance for assert_distribution_close-style checks.

Return type:

float

Raises:

ValueError – If shots is non-positive or confidence is outside (0, 1).

Entanglement

Entanglement and information-theoretic measures (pure NumPy).

These back the entanglement-aware assertions (assert_entangled / assert_separable). Like qtest.metrics.distances, every function here is pure: it validates input, never mutates its arguments, and works on plain NumPy arrays so it can be used without any quantum SDK.

qtest.metrics.entanglement.partial_trace(matrix, keep, num_qubits=None)[source]

Partial trace of a state / density matrix, keeping keep qubits.

Parameters:
  • matrix (ndarray) – A state vector (1-D, lifted to a projector) or a density matrix (2-D, square) over num_qubits qubits.

  • keep (list[int]) – Qubit indices (0-based) of the subsystem to keep; the rest are traced out. Order of the returned subsystem follows ascending qubit index.

  • num_qubits (int | None) – Total qubit count. Inferred from the matrix dimension when None.

Returns:

Reduced density matrix of shape (2**k, 2**k) with k = len(keep).

Return type:

np.ndarray

qtest.metrics.entanglement.purity(rho)[source]

Purity \(\mathrm{tr}(\rho^2)\) of a state / density matrix.

Equals 1 for a pure state and < 1 for a mixed one (minimum 1/d for the maximally mixed state).

Parameters:

rho (ndarray)

Return type:

float

qtest.metrics.entanglement.von_neumann_entropy(rho, base=2.0)[source]

Von Neumann entropy \(S(\rho) = -\mathrm{tr}(\rho \log \rho)\).

Parameters:
  • rho (ndarray) – State vector (entropy 0) or density matrix.

  • base (float) – Logarithm base. 2 (default) gives the entropy in bits — a single qubit’s maximally mixed state then has entropy 1.

Return type:

float

qtest.metrics.entanglement.entanglement_entropy(state, qubits, num_qubits=None, base=2.0)[source]

Entanglement entropy of subsystem qubits versus the rest.

For a pure global state this is the von Neumann entropy of the reduced density matrix on qubits; it is 0 iff the bipartition is a product state (no entanglement across the cut) and positive otherwise.

Parameters:
Return type:

float