API Reference¶
Complete auto-generated API reference for all public modules.
Top-level package¶
pytest-quantum: A cross-framework pytest plugin for quantum program testing.
Public API — import anything you need directly from pytest_quantum:
from pytest_quantum import (
# Unitary
assert_unitary,
assert_circuits_equivalent,
# States
assert_normalized,
assert_state_fidelity_above,
assert_states_close,
# Density matrices (v0.2.0)
assert_density_matrix_close,
assert_trace_distance_below,
assert_purity_above,
assert_partial_trace_close,
# Observables / expectation values (v0.2.0)
assert_expectation_value_close,
assert_ground_state_energy_close,
# VQE / variational optimization (v0.3.0)
assert_vqe_converges,
assert_cost_decreases,
# Distributions
assert_measurement_distribution,
assert_counts_close,
# Primitives (v0.2.0)
assert_sampler_distribution,
assert_estimator_close,
# Structure
assert_circuit_depth,
assert_circuit_width,
assert_gate_count,
assert_gates_in_basis_set,
assert_circuit_is_clifford,
assert_has_diagram,
assert_no_mid_circuit_measurement,
# Snapshots (v0.2.0)
assert_unitary_snapshot,
assert_distribution_snapshot,
# Channels / operators (v0.3.0)
assert_hermitian,
assert_positive_semidefinite,
assert_commutes_with,
assert_channel_is_cptp,
assert_process_fidelity_above,
assert_noise_fidelity_above,
# Entanglement (v0.3.0)
assert_entanglement_entropy_below,
assert_bloch_sphere_close,
assert_schmidt_rank_at_most,
# Information theory (v0.3.0)
assert_hellinger_close,
assert_kl_divergence_below,
assert_cross_entropy_below,
# QASM / serialization round-trip (v0.3.0+)
assert_qasm_roundtrip,
assert_qasm2_roundtrip,
# Transpilation (v0.3.0)
assert_transpilation_preserves_semantics,
# Stim / QEC (v0.3.0)
assert_stim_logical_error_rate_below,
assert_stim_detector_error_rate_below,
assert_stabilizer_state,
# Sweeps (v0.4.0)
assert_circuit_sweep,
assert_circuit_sweep_states,
assert_parametrized_unitary_continuous,
# Compilation (v0.4.0)
assert_transpilation_equivalent,
assert_transpilation_depth_below,
assert_gate_count_after_transpilation,
# Mitiq error mitigation (v0.4.0)
assert_zne_expectation_close,
assert_zne_reduces_error,
assert_cdr_reduces_error,
assert_mitigation_improves_fidelity,
assert_pec_reduces_error,
assert_pec_expectation_close,
assert_error_mitigation_benchmark,
# Benchmarking (v0.5.0)
assert_quantum_volume,
assert_randomized_benchmarking,
assert_t1_above,
assert_t2_above,
assert_t2star_above,
assert_interleaved_rb,
assert_gate_fidelity_above,
# Cross-platform equivalence (v0.5.0)
assert_cross_platform_equivalent,
assert_qiskit_cirq_equivalent,
assert_qiskit_pytket_equivalent,
# Noise channel assertions (v0.5.0)
assert_depolarizing_channel,
assert_amplitude_damping_channel,
assert_dephasing_channel,
assert_no_leakage,
assert_channel_preserves_trace,
assert_channel_diamond_norm_below,
# Hardware assertions (v0.5.0)
assert_backend_calibration,
assert_backend_executes,
assert_circuit_fits_backend,
assert_mirror_fidelity,
assert_real_counts_close,
# Random generators (v0.3.0)
random_statevector,
random_density_matrix,
random_unitary,
random_kraus_channel,
depolarizing_kraus,
# Stats
min_shots,
recommended_shots,
fidelity,
tvd,
tvd_from_counts,
chi_square_test,
)
Fixtures (aer_simulator, cirq_simulator, cirq_sampler,
qiskit_sampler, qiskit_estimator, pytket_circuit_factory,
stim_sampler, quantum_benchmark, shot_budget, etc.) are injected
automatically by pytest — no import needed, just declare them as test
parameters.
- pytest_quantum.assert_amplitude_damping_channel(channel_matrices, expected_gamma, *, atol=0.01)[source]¶
Assert Kraus operators represent an amplitude damping channel with parameter gamma.
The canonical amplitude damping Kraus operators are:
K0 = [[1, 0], [0, sqrt(1 - gamma)]] K1 = [[0, sqrt(gamma)], [0, 0]]
The function estimates gamma from the Choi matrix of the channel. Specifically, the (1, 1) element of the Choi matrix (in the column-vectorised convention) encodes
1 - gamma, so:gamma_est = 1 - Re(choi[1, 1]) / Re(choi[0, 0])
- Parameters:
- Raises:
AssertionError – If |estimated_gamma - expected_gamma| > atol.
ValueError – If operators are not 2x2 or the list is empty.
- Return type:
- pytest_quantum.assert_backend_calibration(backend, *, max_gate_error=0.01, max_readout_error=0.05)[source]¶
Assert backend calibration data meets quality thresholds.
Reads the latest calibration data from backend.properties() and checks that all readout errors and 2-qubit gate errors are within the specified bounds. Useful as a prerequisite test to skip an entire suite if the device is under-performing.
- Parameters:
- Raises:
AssertionError – If any error rate exceeds the threshold, or if the backend exposes no calibration data.
- Return type:
Example:
from pytest_quantum import assert_backend_calibration def test_device_quality(ibm_backend): assert_backend_calibration( ibm_backend, max_gate_error=0.005, max_readout_error=0.03, )
- pytest_quantum.assert_backend_executes(circuit, backend, *, shots=1024, timeout=300.0, transpile=True, optimization_level=1)[source]¶
Assert a circuit executes successfully on a backend and return counts.
Submits the circuit (optionally transpiling it first), waits for the job to complete, and returns the measurement counts. Raises AssertionError if the job fails, is cancelled, or does not finish within timeout seconds.
- Parameters:
circuit (
Any) – Qiskit QuantumCircuit to execute.backend (
Any) – Qiskit-compatible backend (IBM, AerSimulator, …).shots (
int) – Number of shots (default 1024).timeout (
float) – Maximum seconds to wait for job completion (default 300).transpile (
bool) – Transpile to backend before running (default True).optimization_level (
int) – Qiskit transpilation level 0–3 (default 1).
- Returns:
Measurement counts keyed by bitstring.
- Return type:
- Raises:
AssertionError – If the job fails, is cancelled, or times out.
ImportError – If qiskit is not installed.
Example:
from pytest_quantum import assert_backend_executes def test_h_gate(ibm_backend): from qiskit import QuantumCircuit qc = QuantumCircuit(1, 1) qc.h(0) qc.measure(0, 0) counts = assert_backend_executes(qc, ibm_backend, shots=2048) assert counts.get("0", 0) + counts.get("1", 0) == 2048
- pytest_quantum.assert_bloch_sphere_close(statevector, expected_theta, expected_phi, *, atol=0.1)[source]¶
Assert single-qubit state is close to expected Bloch sphere position.
Bloch vector: (sin(theta)cos(phi), sin(theta)sin(phi), cos(theta)) theta in [0, pi]: polar angle (0=|0>, pi=|1>) phi in [0, 2pi): azimuthal angle
- Parameters:
- Raises:
AssertionError – If Bloch vector distance > atol.
ValueError – If statevector is not length 2.
- Return type:
- pytest_quantum.assert_cdr_reduces_error(circuit, executor, simulator, *, num_training_circuits=10, atol=None)[source]¶
Assert Clifford Data Regression (CDR) reduces estimation error.
CDR trains a regression model using near-Clifford circuits simulated classically, then uses the model to mitigate the noisy result.
- Parameters:
circuit (
object) – Quantum circuit (should contain near-Clifford gates).executor (
Callable[...,float]) – Callable(circuit) -> float for noisy execution.simulator (
Callable[...,float]) – Callable(circuit) -> float for exact classical simulation.num_training_circuits (
int) – Number of near-Clifford training circuits (default 10).atol (
float|None) – If provided, assert |mitigated - simulated| <= atol.
- Return type:
- Returns:
Tuple of (unmitigated_value, mitigated_value).
- Raises:
AssertionError – If atol provided and mitigated result is not within tolerance.
ImportError – If mitiq is not installed.
- pytest_quantum.assert_channel_diamond_norm_below(channel_a_kraus, channel_b_kraus, max_diamond_norm, *, atol=0.0001)[source]¶
Assert the diamond-norm distance between two channels is below a threshold.
The diamond norm (completely bounded trace norm) is the operationally meaningful distance between quantum channels: it equals the maximum distinguishing advantage over all input states and ancillae.
When cvxpy is available the function solves the exact SDP formulation of Watrous (2009) and the result is precise.
Fallback (cvxpy not installed): the function uses the operator (spectral) norm of the difference of the normalised Choi matrices as a proxy distance. For single-qubit channels this proxy is within a constant factor of the true diamond norm and gives a conservative bound.
- Parameters:
channel_a_kraus (
list[ndarray[tuple[Any,...],dtype[cdouble]]]) – Kraus operators for channel A (square arrays, equal shape).channel_b_kraus (
list[ndarray[tuple[Any,...],dtype[cdouble]]]) – Kraus operators for channel B (same dimension as A).max_diamond_norm (
float) – Maximum acceptable diamond-norm distance.atol (
float) – Absolute tolerance added to the threshold (default 1e-4).
- Raises:
AssertionError – If the diamond-norm distance exceeds
max_diamond_norm + atol, showing the estimated distance and the method used.ValueError – If the Kraus lists are empty or have mismatched dimensions.
- Return type:
- pytest_quantum.assert_channel_is_cptp(kraus_ops, *, atol=1e-08)[source]¶
Assert Kraus operators satisfy completeness: sum_i K_i† K_i == I.
This is the necessary and sufficient condition for a channel to be completely positive and trace-preserving (CPTP).
- Parameters:
- Raises:
AssertionError – If the completeness relation is violated, showing Frobenius norm of the deviation.
ValueError – If the list is empty, operators have different shapes, or operators are not square.
- Return type:
- pytest_quantum.assert_channel_preserves_trace(channel_matrices, *, atol=1e-06)[source]¶
Assert that Kraus operators satisfy the trace-preserving (TP) condition.
A quantum channel is trace-preserving iff:
sum_i K_i^dagger @ K_i == I
This is equivalent to demanding that the channel maps every valid density matrix to another density matrix with the same trace.
- Parameters:
- Raises:
AssertionError – If the completeness relation is violated, showing the Frobenius norm of the deviation and the tolerance.
ValueError – If the list is empty, operators have different shapes, or operators are not square.
- Return type:
- pytest_quantum.assert_circuit_depth(circuit, *, max_depth=None, min_depth=None)[source]¶
Assert that a circuit’s depth is within the specified bounds.
At least one of max_depth or min_depth must be provided.
Supported frameworks: Qiskit, Cirq, Amazon Braket.
- Parameters:
- Raises:
AssertionError – If the depth is outside the specified bounds.
TypeError – If the circuit type is not supported.
ValueError – If neither bound is provided.
- Return type:
Example:
def test_circuit_depth(): from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) assert_circuit_depth(qc, max_depth=3)
- pytest_quantum.assert_circuit_fits_backend(circuit, backend, *, optimization_level=3, max_depth=None, max_2q_gates=None)[source]¶
Assert a circuit transpiles to the backend and stays within resource limits.
Transpiles circuit for backend and checks optional depth and 2-qubit gate count constraints. Even without constraints this is useful to assert the circuit is compatible with the backend’s basis gates and connectivity.
- Parameters:
- Return type:
- Returns:
dict with keys ‘depth’, ‘num_2q_gates’, ‘num_qubits’, ‘ops’.
- Raises:
AssertionError – If constraints are violated.
ImportError – If qiskit is not installed.
Example:
from pytest_quantum import assert_circuit_fits_backend def test_grover_fits(ibm_backend): info = assert_circuit_fits_backend( grover_circuit, ibm_backend, max_depth=200, max_2q_gates=50, ) print( f"Transpiled depth: {info['depth']}, 2Q gates: {info['num_2q_gates']}" )
- pytest_quantum.assert_circuit_is_clifford(circuit)[source]¶
Assert a circuit uses only Clifford gates (H, S, S†, X, Y, Z, CNOT, CZ, SWAP).
Clifford circuits are classically efficiently simulable. Supported: Qiskit, Cirq.
- Raises:
AssertionError – If non-Clifford gates found.
NotImplementedError – If framework not supported.
- Parameters:
circuit (object)
- Return type:
None
Example:
def test_is_clifford(): from qiskit import QuantumCircuit from pytest_quantum import assert_circuit_is_clifford qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) assert_circuit_is_clifford(qc)
- pytest_quantum.assert_circuit_sweep(circuit_fn, param_values, expected_fn, *, atol=1e-06, allow_global_phase=True)[source]¶
Assert a parametrized circuit matches expected unitary for all parameter values.
Calls circuit_fn(**params) for every combination of param_values, computes the unitary, and compares to expected_fn(**params).
- Parameters:
circuit_fn (
Callable[...,Any]) – Callable that accepts keyword parameter arguments and returns a quantum circuit.param_values (
dict[str,list[float] |ndarray[tuple[Any,...],dtype[double]]]) – Dict mapping parameter name to list/array of values. All combinations are tested (cartesian product).expected_fn (
Callable[...,ndarray[tuple[Any,...],dtype[cdouble]]]) – Callable that accepts the same keyword arguments and returns the expected unitary as a numpy array.atol (
float) – Absolute tolerance for unitary comparison (default 1e-6).allow_global_phase (
bool) – If True, ignore global phase differences (default True).
- Raises:
AssertionError – If any parameter combination fails.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_circuit_sweep from qiskit import QuantumCircuit def rx_circuit(theta): qc = QuantumCircuit(1) qc.rx(theta, 0) return qc def rx_expected(theta): c, s = np.cos(theta / 2), np.sin(theta / 2) return np.array([[c, -1j * s], [-1j * s, c]]) assert_circuit_sweep( rx_circuit, {"theta": np.linspace(0, 2 * np.pi, 8)}, rx_expected, )
- pytest_quantum.assert_circuit_sweep_states(circuit_fn, initial_state, param_values, expected_fn, *, min_fidelity=0.99)[source]¶
Assert a parametrized circuit produces expected output states for all params.
Applies circuit_fn(**params) to initial_state and compares the output statevector fidelity to expected_fn(**params).
- Parameters:
circuit_fn (
Callable[...,Any]) – Callable(**params) -> circuit.initial_state (
ndarray[tuple[Any,...],dtype[cdouble]]) – Input statevector, shape (2**n,).param_values (
dict[str,list[float] |ndarray[tuple[Any,...],dtype[double]]]) – Dict of parameter name -> list of values.expected_fn (
Callable[...,ndarray[tuple[Any,...],dtype[cdouble]]]) – Callable(**params) -> expected output statevector.min_fidelity (
float) – Minimum fidelity |<actual|expected>|^2 (default 0.99).
- Raises:
AssertionError – If any combination fails the fidelity check.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_circuit_sweep_states psi0 = np.array([1, 0], dtype=complex) def rz_circuit(phi): from qiskit import QuantumCircuit qc = QuantumCircuit(1) qc.rz(phi, 0) return qc def expected_state(phi): return np.array([np.exp(-1j * phi / 2), 0]) assert_circuit_sweep_states( rz_circuit, psi0, {"phi": [0, np.pi / 2, np.pi]}, expected_state )
- pytest_quantum.assert_circuit_width(circuit, expected_qubits)[source]¶
Assert that a circuit acts on exactly expected_qubits qubits.
Supported frameworks: Qiskit, Cirq, Amazon Braket, PennyLane.
- Parameters:
- Raises:
AssertionError – If the qubit count does not match.
TypeError – If the circuit type is not supported.
- Return type:
Example:
def test_circuit_width(): from qiskit import QuantumCircuit qc = QuantumCircuit(3) qc.h(0) qc.cx(0, 1) qc.cx(1, 2) assert_circuit_width(qc, expected_qubits=3)
- pytest_quantum.assert_circuits_equivalent(circuit_a, circuit_b, *, atol=1e-06)[source]¶
Assert that two circuits implement the same unitary, up to global phase.
Works across frameworks — you can compare a Qiskit circuit against a Cirq circuit, a Braket circuit, or a PennyLane QNode.
For two Qiskit circuits,
mqt.qcecis used automatically when installed (faster, exact verification via decision diagrams / ZX-calculus). For cross-framework comparison the circuits are both converted to numpy matrices and compared numerically.- Parameters:
- Raises:
AssertionError – If the circuits implement different unitaries.
TypeError – If either argument is not a recognised circuit type.
- Return type:
Example:
from pytest_quantum import assert_circuits_equivalent def test_cnot_cross_framework(): import cirq from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.cx(0, 1) q0, q1 = cirq.LineQubit.range(2) cc = cirq.Circuit(cirq.CNOT(q0, q1)) assert_circuits_equivalent(qc, cc)
- pytest_quantum.assert_commutes_with(op_a, op_b, *, atol=1e-08)[source]¶
Assert two square matrices commute: AB == BA.
- Parameters:
- Raises:
AssertionError – If [A, B] = AB - BA has any element > atol, showing max(|AB - BA|).
ValueError – If matrices are not square or have mismatched sizes.
- Return type:
- pytest_quantum.assert_cost_decreases(cost_history, *, min_decrease=0.0, atol=1e-06)[source]¶
Assert an optimization cost history shows overall decrease.
Checks that
cost_history[-1] < cost_history[0] - min_decrease.- Parameters:
- Raises:
AssertionError – If cost did not decrease sufficiently.
ValueError – If cost_history has fewer than 2 entries.
- Return type:
Example:
history = [] for step in range(50): cost = run_vqe_step(params) history.append(cost) assert_cost_decreases(history, min_decrease=0.1)
- pytest_quantum.assert_counts_close(counts_a, counts_b, *, max_tvd=0.05)[source]¶
Assert that two count dictionaries are statistically close.
Computes the Total Variation Distance (TVD) between the normalised distributions and fails if it exceeds max_tvd.
Useful for comparing two backends, or checking that transpilation has not changed a circuit’s output distribution.
- Parameters:
- Raises:
AssertionError – If TVD exceeds max_tvd.
- Return type:
Example:
def test_transpile_preserves_distribution(aer_simulator): from qiskit import QuantumCircuit, transpile qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) qc.measure_all() # ideal vs noise-free transpiled qc_t = transpile(qc, aer_simulator, optimization_level=3) counts_ideal = aer_simulator.run(qc, shots=2000).result().get_counts() counts_transpiled = ( aer_simulator.run(qc_t, shots=2000).result().get_counts() ) assert_counts_close(counts_ideal, counts_transpiled, max_tvd=0.05)
- pytest_quantum.assert_cross_entropy_below(counts, expected_probs, *, max_ce=1.0)[source]¶
Assert cross-entropy H(P, Q) = -sum_x P(x) log2 Q(x) <= max_ce.
Used in quantum supremacy experiments as XEB (cross-entropy benchmarking).
- Parameters:
- Raises:
AssertionError – If H(P, Q) > max_ce.
ValueError – If any observed outcome has zero expected probability.
- Return type:
- pytest_quantum.assert_cross_platform_equivalent(circuit_a, circuit_b, *, atol=1e-06, allow_global_phase=True, framework_a=None, framework_b=None)[source]¶
Assert that two circuits implement the same unitary, up to global phase.
Converts both circuits to numpy unitary matrices using
pytest_quantum.converters.to_unitary.to_unitary()and compares them numerically, optionally ignoring a global phase factore^{iθ}.Qubit-ordering conventions are normalised automatically: Qiskit uses little-endian while Cirq and pytket use big-endian, so comparing a Qiskit circuit against a Cirq or pytket circuit applies the appropriate permutation before the element-wise check.
- Parameters:
circuit_a (
object) – First circuit (any supported framework).circuit_b (
object) – Second circuit (any supported framework).atol (
float) – Absolute tolerance for element-wise comparison (default1e-6).allow_global_phase (
bool) – IfTrue(default), circuits that differ only by a global phasee^{iθ}are considered equivalent.framework_a (
str|None) – Optional hint like"qiskit"or"cirq"used only to improve error messages.framework_b (
str|None) – Optional hint for the second circuit.
- Raises:
AssertionError – If the circuits implement different unitaries, with a message showing the max element-wise difference and the framework names.
ValueError – If either circuit cannot be converted to a unitary (e.g. it contains measurements or is not a pure-unitary circuit).
- Return type:
None
Example:
:rtype: :sphinx_autodoc_typehints_type:`\:py\:obj\:\`None\``
from qiskit import QuantumCircuit from pytest_quantum.assertions.cross_platform import (
assert_cross_platform_equivalent,
)
qc1 = QuantumCircuit(1) qc1.h(0)
qc2 = QuantumCircuit(1) qc2.h(0)
assert_cross_platform_equivalent(qc1, qc2)
- pytest_quantum.assert_density_matrix_close(rho, sigma, *, atol=1e-06)[source]¶
Assert two density matrices are close element-wise after normalisation.
- Parameters:
- Raises:
AssertionError – If matrices differ by more than atol.
ValueError – If shapes mismatch or matrices are invalid.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_density_matrix_close rho = np.array([[0.5, 0.5], [0.5, 0.5]], dtype=complex) assert_density_matrix_close(rho, rho)
- pytest_quantum.assert_dephasing_channel(channel_matrices, expected_rate, *, atol=0.01)[source]¶
Assert Kraus operators represent a dephasing (phase damping) channel.
The canonical dephasing Kraus operators are:
K0 = [[1, 0], [0, sqrt(1 - p)]] K1 = [[0, 0], [0, sqrt(p)]]
The dephasing rate p controls how quickly off-diagonal coherences decay. It is estimated by applying the channel to the superposition state
|+><+|and reading off the off-diagonal survival. The output off-diagonal element is0.5 * sqrt(1 - p), so:p_est = 1 - (2 * |rho_out[0, 1]|) ** 2
- Parameters:
- Raises:
AssertionError – If |estimated_rate - expected_rate| > atol.
ValueError – If operators are not 2x2 or the list is empty.
- Return type:
- pytest_quantum.assert_depolarizing_channel(channel_matrices, expected_error_rate, *, atol=0.01)[source]¶
Assert Kraus operators represent a depolarizing channel with a given error rate.
For the standard single-qubit depolarizing channel with error rate p:
K0 = sqrt(1 - p) * I K1 = sqrt(p / 3) * X K2 = sqrt(p / 3) * Y K3 = sqrt(p / 3) * Z
The function recovers p from the average gate fidelity of the channel. Only K0 = sqrt(1-p)*I has nonzero trace, so:
F_proc = (1 - p) F_avg = (d * F_proc + 1) / (d + 1) [Nielsen 2002] = (3 - 2p) / 3 for d = 2 p = (1 - F_avg) * (d + 1) / 2 [for d=2: p = (1 - F_avg) * 3/2]
- Parameters:
- Raises:
AssertionError – If |estimated_p - expected_error_rate| > atol, showing both values.
ValueError – If
channel_matricesis empty or operators are non-square / mismatched.
- Return type:
- pytest_quantum.assert_distribution_snapshot(counts, name, *, update=False, max_tvd=0.05)[source]¶
Assert that a measurement distribution matches its saved snapshot.
Saves the normalised probability distribution derived from counts. Comparison uses Total Variation Distance (TVD).
- Parameters:
- Raises:
AssertionError – If TVD from snapshot exceeds max_tvd.
ValueError – If counts is empty.
- Return type:
Example:
def test_distribution_stable(aer_simulator): counts = run_bell(aer_simulator, shots=4000) from pytest_quantum import assert_distribution_snapshot assert_distribution_snapshot(counts, "bell_distribution")
- pytest_quantum.assert_entanglement_capability_above(ansatz_fn, num_qubits, num_params, target_capability, *, num_samples=200)[source]¶
Assert Meyer-Wallach entanglement capability of a parameterised ansatz.
Computes the average Meyer-Wallach entanglement measure Q over random parameter samples. Q ∈ [0, 1]: 0 = no entanglement, 1 = maximally entangled.
- Parameters:
- Returns:
Average Meyer-Wallach Q value.
- Return type:
- Raises:
AssertionError – If average Q < target_capability.
ImportError – If qiskit is not installed.
- pytest_quantum.assert_entanglement_entropy_below(statevector, partition, max_entropy, *, n_qubits=None)[source]¶
Assert von Neumann entanglement entropy S(rho_A) <= max_entropy.
For a pure state |psi>, partition qubits into subsystem A (partition indices) and subsystem B (rest). Computes S(rho_A) = -Tr(rho_A log2 rho_A).
max_entropy=0 means the state is separable (product state) on this partition. max_entropy=1 means at most 1 ebit of entanglement.
- Parameters:
statevector (
object) – Pure state amplitudes (1D complex array of length 2^n).partition (
list[int]) – Qubit indices to keep for subsystem A (big-endian).max_entropy (
float) – Maximum entanglement entropy in bits (nats if log2 is replaced — here we use log2 so units are bits).n_qubits (
int|None) – Total qubit count (inferred from len if not given).
- Raises:
AssertionError – If S(rho_A) > max_entropy.
ValueError – If statevector length is not a power of 2.
- Return type:
- pytest_quantum.assert_error_mitigation_benchmark(circuit, ideal_executor, noisy_executor, *, methods=None, atol=0.1)[source]¶
Benchmark multiple ZNE mitigation methods against the ideal value.
Runs ZNE with each specified method and asserts all methods achieve results within atol of the ideal value.
- Parameters:
circuit (
object) – Quantum circuit.ideal_executor (
Callable[...,float]) – Callable(circuit) -> float for ideal (noiseless) execution.noisy_executor (
Callable[...,float]) – Callable(circuit) -> float for noisy execution.methods (
list[str] |None) – List of method names to benchmark. Supported values: “zne_richardson”, “zne_linear”. Defaults to both.atol (
float) – Absolute tolerance from ideal value (default 0.1).
- Return type:
- Returns:
Dict mapping method_name -> mitigated_value.
- Raises:
ImportError – If mitiq is not installed.
AssertionError – If any method exceeds atol from the ideal value.
Example:
from pytest_quantum import assert_error_mitigation_benchmark results = assert_error_mitigation_benchmark( circuit, ideal_executor, noisy_executor, atol=0.05 )
- pytest_quantum.assert_estimator_close(result, expected, *, atol=0.1, pub_idx=0)[source]¶
Assert a Qiskit Estimator result is close to the expected value.
- Parameters:
- Raises:
AssertionError – If |actual - expected| > atol.
- Return type:
Example:
def test_estimator_z(qiskit_estimator): from qiskit.circuit import QuantumCircuit from qiskit.quantum_info import SparsePauliOp from pytest_quantum import assert_estimator_close qc = QuantumCircuit(1) # |0>, <Z> = 1.0 obs = SparsePauliOp("Z") result = qiskit_estimator.run([(qc, obs)]).result() assert_estimator_close(result, expected=1.0, atol=0.01)
- pytest_quantum.assert_expectation_value_close(result_or_value, expected, *, atol=0.1)[source]¶
Assert a measured expectation value is close to expected.
Accepts: plain float/int, numpy scalar, Qiskit EstimatorResult/PrimitiveResult, or PennyLane measurement result.
- Parameters:
- Raises:
AssertionError – If |actual - expected| > atol.
TypeError – If result type is not recognised.
- Return type:
Example:
from pytest_quantum import assert_expectation_value_close assert_expectation_value_close(0.95, expected=1.0, atol=0.1)
- pytest_quantum.assert_expressibility_above(ansatz_fn, num_qubits, num_params, target_expressibility, *, num_samples=200, num_bins=75)[source]¶
Assert circuit expressibility (frame potential / KL divergence from Haar).
Measures how uniformly an ansatz samples the unitary group compared to the Haar measure. Expressibility is quantified as:
expr = 1 - KL(P_ansatz || P_Haar) (normalized to [0, 1])
Higher = more expressive. A Haar-random circuit has expressibility ≈ 1.0.
- Parameters:
ansatz_fn (
Callable[...,Any]) – Callable(params: np.ndarray) -> QuantumCircuit. Returns a parameterised Qiskit QuantumCircuit.num_qubits (
int) – Number of qubits in the ansatz.num_params (
int) – Number of parameters the ansatz accepts.target_expressibility (
float) – Minimum required expressibility score (0.0–1.0).num_samples (
int) – Number of random parameter samples (default 200).num_bins (
int) – Histogram bins for fidelity distribution (default 75).
- Returns:
Expressibility score (higher = better, 1.0 = Haar-equivalent).
- Return type:
- Raises:
AssertionError – If expressibility < target_expressibility.
ImportError – If qiskit is not installed.
- pytest_quantum.assert_gate_count(circuit, gate_name, expected)[source]¶
Assert that a circuit contains exactly expected occurrences of gate_name.
Supported frameworks: Qiskit, Cirq, PennyLane.
- Parameters:
- Raises:
AssertionError – If the actual count differs from expected.
NotImplementedError – If the framework is not yet supported.
- Return type:
Example:
def test_t_count(): from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.t(0) qc.t(1) qc.cx(0, 1) assert_gate_count(qc, "t", 2) assert_gate_count(qc, "cx", 1)
- pytest_quantum.assert_gate_count_after_transpilation(circuit, gate_name, *, max_count=None, min_count=None, basis_gates=None, optimization_level=3)[source]¶
Assert the count of a specific gate after transpilation is within bounds.
- Parameters:
- Return type:
- Returns:
The actual gate count after transpilation.
- Raises:
AssertionError – If count is outside [min_count, max_count].
ValueError – If neither max_count nor min_count is provided.
Example:
from pytest_quantum import assert_gate_count_after_transpilation # Assert Toffoli is decomposed into at most 6 CNOT gates assert_gate_count_after_transpilation( toffoli_circuit, "cx", max_count=6, basis_gates=["cx", "rz", "sx", "x"] )
- pytest_quantum.assert_gate_fidelity_above(backend, gate_name, qubits, target_fidelity, *, shots=2048, timeout=300.0)[source]¶
Assert gate fidelity (from calibration data) is at least target_fidelity.
Reads the gate error rate from
backend.properties()for the specified gate on the specified qubits, computesfidelity = 1 - error_rate, and asserts the result meets the threshold. This is a fast, passive check against existing calibration data — no circuit is executed.- Parameters:
backend (
Any) – IBM backend (or any backend with a.properties()method that exposes per-gate error rates).gate_name (
str) – Name of the gate (e.g."cx","ecr","x").qubits (
list[int] |tuple[int,...]) – Qubit indices the gate acts on (e.g.[0, 1]).target_fidelity (
float) – Minimum required fidelity (0.0–1.0).shots (
int) – Unused; kept for API consistency with other assertions.timeout (
float) – Unused; kept for API consistency with other assertions.
- Returns:
Measured gate fidelity (1 - error_rate).
- Return type:
- Raises:
AssertionError – If fidelity < target_fidelity, or if gate/qubit data is not available in calibration properties.
Example:
from pytest_quantum.assertions.benchmarking import assert_gate_fidelity_above def test_cx_fidelity(ibm_backend): fidelity = assert_gate_fidelity_above( ibm_backend, gate_name="cx", qubits=[0, 1], target_fidelity=0.99, ) print(f"CX fidelity: {fidelity:.4f}")
- pytest_quantum.assert_gates_in_basis_set(circuit, basis_gates, *, case_sensitive=False)[source]¶
Assert every gate in the circuit belongs to the specified basis gate set.
Useful for verifying that a transpiled circuit only uses a target backend’s native gate set (e.g. after
qiskit.transpilewithbasis_gates=[...]).- Parameters:
- Raises:
AssertionError – Lists every non-basis gate found.
NotImplementedError – For unsupported frameworks.
- Return type:
Example:
from qiskit import QuantumCircuit, transpile from qiskit_aer import AerSimulator from pytest_quantum import assert_gates_in_basis_set qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) transpiled = transpile(qc, basis_gates=["cx", "u3"]) assert_gates_in_basis_set(transpiled, {"cx", "u3"})
- pytest_quantum.assert_ground_state_energy_close(result_or_value, expected_energy, *, atol=0.1)[source]¶
Assert VQE/QAOA result is close to the known ground state energy.
- Parameters:
- Raises:
AssertionError – If measured energy differs from expected by more than atol.
- Return type:
Example:
from pytest_quantum import assert_ground_state_energy_close # H2 ground state energy (Hartree) assert_ground_state_energy_close(-1.85, expected_energy=-1.8572, atol=0.05)
- pytest_quantum.assert_has_diagram(circuit, expected, *, strict=False)[source]¶
Assert circuit’s text representation contains expected pattern.
For Qiskit: uses
circuit.draw('text'). For Cirq: usesstr(circuit)(circuit.to_text_diagram()).- Parameters:
circuit (
object) – Any supported framework circuit.expected (
str) – Expected string (exact if strict isTrue, substring otherwise).strict (
bool) – IfTrue, require exact match after stripping leading / trailing whitespace. IfFalse(default), just check that expected is a substring of the diagram.
- Raises:
AssertionError – If diagram doesn’t match.
NotImplementedError – For frameworks without text diagram support.
- Return type:
Example:
from qiskit import QuantumCircuit from pytest_quantum import assert_has_diagram qc = QuantumCircuit(1) qc.h(0) assert_has_diagram(qc, "H")
- pytest_quantum.assert_hellinger_close(counts_a, counts_b, *, max_distance=0.1)[source]¶
Assert Hellinger distance H(p,q) <= max_distance.
H(p,q) = (1/sqrt(2)) * ||sqrt(p) - sqrt(q)||_2 Range: [0, 1]. H=0 identical, H=1 disjoint support.
More symmetric and bounded than KL divergence.
- Parameters:
- Raises:
AssertionError – If H(p,q) > max_distance, with a per-key table.
ValueError – If both count dictionaries are empty.
- Return type:
- pytest_quantum.assert_hermitian(matrix, *, atol=1e-08)[source]¶
Assert matrix is Hermitian: A == A†.
- Parameters:
- Raises:
AssertionError – If the matrix is not Hermitian, with shape info and maximum deviation from Hermiticity.
ValueError – If the matrix is not square.
- Return type:
- pytest_quantum.assert_interleaved_rb(backend, qubit, gate_name, gate_circuit, *, clifford_lengths=None, num_sequences=20, shots=1024, min_gate_fidelity=0.999, timeout=300.0)[source]¶
Assert interleaved randomized benchmarking (IRB) gate fidelity.
Runs two RB experiments: 1. Standard RB to get reference decay rate p_ref. 2. Interleaved RB where each Clifford is followed by the target gate_circuit
to get p_irb.
Gate fidelity:
F_gate = 1 - (d-1)/d * (1 - p_irb/p_ref)whered = 2for single-qubit gates.- Parameters:
backend (
Any) – Qiskit-compatible backend.qubit (
int) – Index of the qubit to benchmark.gate_name (
str) – Human-readable name for the gate (for error messages).gate_circuit (
Any) – A single-qubit QuantumCircuit implementing the gate.clifford_lengths (
list[int] |None) – Sequence lengths. Default:[1, 5, 10, 20, 50].num_sequences (
int) – Random sequences per length (default 20).shots (
int) – Shots per circuit (default 1024).min_gate_fidelity (
float) – Minimum acceptable gate fidelity (default 0.999).timeout (
float) – Maximum seconds for all jobs (default 300).
- Return type:
- Returns:
dict with keys
'fidelity','p_ref','p_irb','lengths','ref_survival','irb_survival'.- Raises:
AssertionError – If gate fidelity < min_gate_fidelity.
ImportError – If qiskit is not installed.
- pytest_quantum.assert_kl_divergence_below(counts, expected_probs, *, max_kl=0.1)[source]¶
Assert KL divergence D_KL(observed || expected) <= max_kl.
D_KL(P||Q) = sum_x P(x) * log2(P(x) / Q(x))
Note: KL is asymmetric and infinite if Q(x)=0 but P(x)>0. Raises ValueError if expected_probs has zero probability for any observed outcome.
- Parameters:
- Raises:
AssertionError – If D_KL > max_kl.
ValueError – If any outcome with non-zero observed count has zero expected probability (KL would be infinite).
- Return type:
- pytest_quantum.assert_measurement_distribution(counts, expected_probs, *, significance=0.05, min_expected_per_bucket=5)[source]¶
Assert that measured counts match the expected probability distribution.
Uses a chi-square goodness-of-fit test — the standard statistical tool for this exact problem. The test fails only when the deviation is statistically significant (p < significance), so occasional random fluctuations do not cause false failures.
- Parameters:
counts (
dict[str,int]) – Measured counts dict, e.g.{"00": 489, "11": 511}. Keys are bitstring labels; values are integer counts.expected_probs (
dict[str,float]) – Expected probability dict, e.g.{"00": 0.5, "11": 0.5}. Must sum to 1.0 (within 1e-6). Outcomes not present are assumed to have zero expected probability.significance (
float) – P-value threshold below which the test fails (default0.05).min_expected_per_bucket (
int) – Chi-square requires expected count >= 5 per non-zero cell for valid results. AUserWarningis raised (but the test does not fail) if this is violated; consider increasing shots.
- Raises:
AssertionError – If
p_value < significance, with a per-state breakdown of observed vs expected probabilities.ValueError – If expected_probs does not sum to 1.0, or counts is empty.
- Return type:
None
Example:
:rtype: :sphinx_autodoc_typehints_type:`\:py\:obj\:\`None\``
- def test_bell_distribution(aer_simulator):
from qiskit import QuantumCircuit, transpile from pytest_quantum import assert_measurement_distribution
qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) qc.measure_all() qc_t = transpile(qc, aer_simulator) counts = aer_simulator.run(qc_t, shots=2000).result().get_counts()
- assert_measurement_distribution(
counts, expected_probs={“00”: 0.5, “11”: 0.5},
)
- pytest_quantum.assert_mirror_fidelity(circuit, backend, *, shots=4096, min_fidelity=0.5, timeout=300.0)[source]¶
Assert a circuit’s mirror achieves sufficient |0…0⟩ return fidelity.
Appends the circuit’s inverse to itself (circuit ∘ circuit†), measures all qubits, and checks that the |0…0⟩ outcome fraction meets min_fidelity. This is a lightweight form of mirror benchmarking that works for any invertible circuit without needing a classical simulator reference.
- Parameters:
circuit (
Any) – QuantumCircuit without measurements (must be invertible).backend (
Any) – Qiskit-compatible backend.shots (
int) – Number of shots (default 4096).min_fidelity (
float) – Minimum required |0…0⟩ return fraction (default 0.5). Reduce this for deeper circuits or noisier devices.timeout (
float) – Max seconds to wait for job completion (default 300).
- Returns:
Measured |0…0⟩ fraction (fidelity proxy).
- Return type:
- Raises:
AssertionError – If fidelity < min_fidelity.
ImportError – If qiskit is not installed.
Example:
from pytest_quantum import assert_mirror_fidelity def test_cx_mirror(ibm_backend): from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) fidelity = assert_mirror_fidelity(qc, ibm_backend, min_fidelity=0.5) print(f"Mirror fidelity: {fidelity:.3f}")
- pytest_quantum.assert_mitigation_improves_fidelity(noisy_dm, mitigated_dm, ideal_state, *, atol=0.0)[source]¶
Assert that a mitigated density matrix is closer to ideal than the noisy one.
Compares trace distances: T(mitigated, ideal) < T(noisy, ideal).
- Parameters:
noisy_dm (
object) – Density matrix from noisy (unmitigated) execution.mitigated_dm (
object) – Density matrix after error mitigation.ideal_state (
object) – Ideal target state (statevector or density matrix).atol (
float) – Require mitigated_distance <= noisy_distance - atol (default 0: any improvement accepted).
- Raises:
AssertionError – If mitigated result is not closer to ideal.
- Return type:
Example:
from pytest_quantum import assert_mitigation_improves_fidelity assert_mitigation_improves_fidelity(noisy_dm, mitigated_dm, ideal_state)
- pytest_quantum.assert_no_barren_plateau(ansatz_fn, num_qubits, num_params, observable=None, *, num_samples=100, min_gradient_variance=0.0001)[source]¶
Assert no barren plateau: gradient variance is above min_gradient_variance.
Detects barren plateaus by computing the variance of parameter-shift gradients over random parameter initializations. A barren plateau manifests as an exponentially vanishing gradient variance.
- Parameters:
ansatz_fn (
Callable[...,Any]) – Callable(params: np.ndarray) -> QuantumCircuit.num_qubits (
int) – Number of qubits.num_params (
int) – Number of parameters.observable (
Any|None) – Optional Qiskit SparsePauliOp. Defaults to Z⊗Z⊗…⊗Z.num_samples (
int) – Random parameter initializations (default 100).min_gradient_variance (
float) – Minimum acceptable variance (default 1e-4).
- Returns:
Gradient variance (higher = no barren plateau).
- Return type:
- Raises:
AssertionError – If gradient variance < min_gradient_variance.
ImportError – If qiskit is not installed.
- pytest_quantum.assert_no_leakage(density_matrix, computational_subspace_dim, *, max_leakage=0.01)[source]¶
Assert a density matrix has negligible population outside the computational subspace.
Leakage is defined as the probability of being in states outside the first
computational_subspace_dimbasis states:leakage = 1 - Tr(P_comp @ rho @ P_comp) = 1 - sum_{i=0}^{d_comp-1} rho[i, i]
where
P_compis the projector onto the firstcomputational_subspace_dimbasis states. This is particularly useful for qutrit or multi-level system simulations where leakage to non-computational levels is undesirable.- Parameters:
- Return type:
- Returns:
The measured leakage as a float in [0, 1].
- Raises:
AssertionError – If leakage > max_leakage, showing measured vs allowed leakage.
ValueError – If the density matrix is not square or
computational_subspace_dimexceeds the matrix dimension.
- pytest_quantum.assert_no_mid_circuit_measurement(circuit)[source]¶
Assert a circuit has no mid-circuit measurements (all measurements are terminal).
Mid-circuit measurements (measurements followed by further gate operations) are not supported on all hardware backends. This assertion verifies that all measurements occur after all gate operations — i.e., measurements only appear in the final layer.
Supported frameworks: Qiskit, Cirq.
- Parameters:
circuit (
object) – A quantum circuit from a supported framework.- Raises:
AssertionError – If mid-circuit measurements are detected.
NotImplementedError – If framework is not supported.
- Return type:
Example:
from qiskit import QuantumCircuit from pytest_quantum import assert_no_mid_circuit_measurement qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure_all() assert_no_mid_circuit_measurement(qc) # passes — measurements are terminal
- pytest_quantum.assert_noise_fidelity_above(noisy_dm, ideal_state, threshold=0.99)[source]¶
Assert state fidelity between noisy density matrix and ideal state.
F(rho, |psi>) = <psi|rho|psi> F(rho, sigma) = Tr(sigma @ rho)
- Parameters:
- Raises:
AssertionError – If fidelity < threshold.
ValueError – If shapes are inconsistent.
- Return type:
- pytest_quantum.assert_normalized(statevector, *, atol=1e-06)[source]¶
Assert statevector has unit norm: ||ψ||₂ = 1.
A common bug in manual statevector construction is forgetting to normalize.
- Parameters:
- Raises:
AssertionError – If ||sv||₂ is not within atol of 1.0, showing the actual norm.
- Return type:
Example:
>>> import numpy as np >>> sv = np.array([1, 0, 0, 0], dtype=complex) # |00> >>> assert_normalized(sv) # passes >>> sv_bad = np.array([1, 1], dtype=complex) # NOT normalized >>> assert_normalized(sv_bad) # fails: norm = 1.4142
- pytest_quantum.assert_parametrized_unitary_continuous(circuit_fn, param_name, param_range, *, n_samples=20, max_jump=0.5)[source]¶
Assert that a parametrized circuit’s unitary varies continuously.
Detects discontinuities (e.g. branch cut issues, phase jumps) by checking that consecutive unitary matrices are close in Frobenius norm.
- Parameters:
circuit_fn (
Callable[...,Any]) – Callable(param_name=value) -> circuit.param_name (
str) – Name of the parameter to sweep.param_range (
tuple[float,float]) – (start, end) range for the parameter.n_samples (
int) – Number of evenly-spaced sample points (default 20).max_jump (
float) – Maximum allowed Frobenius norm change between adjacent samples (default 0.5).
- Raises:
AssertionError – If any discontinuity exceeds max_jump.
- Return type:
Example:
assert_parametrized_unitary_continuous( lambda theta: rx_circuit(theta), "theta", (0, 2 * np.pi) )
- pytest_quantum.assert_partial_trace_close(rho, keep_qubits, expected, *, atol=1e-06)[source]¶
Assert the reduced density matrix (partial trace) is close to expected.
Traces out all qubits NOT in keep_qubits, then compares the resulting reduced density matrix to expected.
- Parameters:
- Raises:
AssertionError – If reduced density matrix differs from expected.
ValueError – If dimensions are inconsistent.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_partial_trace_close # Bell state: |00> + |11> / sqrt(2) — partial trace gives I/2 bell = np.array([[1], [0], [0], [1]], dtype=complex) / np.sqrt(2) rho = bell @ bell.conj().T mixed = np.eye(2, dtype=complex) / 2 assert_partial_trace_close(rho, keep_qubits=[0], expected=mixed)
- pytest_quantum.assert_pec_expectation_close(circuit, executor, noise_model, expected, *, num_samples=100, atol=0.1)[source]¶
Assert PEC-mitigated expectation value is close to expected.
- Parameters:
circuit (
object) – Quantum circuit.executor (
Callable[...,float]) – Callable(circuit) -> float.noise_model (
object) – PEC representations.expected (
float) – Expected ideal expectation value.num_samples (
int) – Number of PEC samples (default 100).atol (
float) – Absolute tolerance (default 0.1).
- Raises:
AssertionError – If mitigated value differs from expected by more than atol.
ImportError – If mitiq is not installed.
- Return type:
Example:
from pytest_quantum import assert_pec_expectation_close assert_pec_expectation_close( circuit, noisy_executor, representations, expected=1.0, atol=0.05 )
- pytest_quantum.assert_pec_reduces_error(circuit, executor, noise_model, *, num_samples=100, threshold_improvement=0.0)[source]¶
Assert that Probabilistic Error Cancellation (PEC) produces a valid result.
Runs the circuit with and without PEC and returns both values.
- Parameters:
circuit (
object) – Quantum circuit.executor (
Callable[...,float]) – Callable(circuit) -> float for noisy execution.noise_model (
object) – PEC representations (quasi-probability representations).num_samples (
int) – Number of PEC samples (default 100).threshold_improvement (
float) – Unused threshold parameter (reserved for future use).
- Return type:
- Returns:
Tuple of (unmitigated_value, mitigated_value).
- Raises:
ImportError – If mitiq is not installed.
AssertionError – If PEC does not return a valid float.
Example:
from pytest_quantum import assert_pec_reduces_error unmitigated, mitigated = assert_pec_reduces_error( circuit, noisy_executor, representations )
- pytest_quantum.assert_positive_semidefinite(matrix, *, atol=1e-08)[source]¶
Assert matrix is positive semi-definite: all eigenvalues >= -atol.
Validates Hermiticity first, then checks that the smallest eigenvalue is >= -atol.
- Parameters:
- Raises:
AssertionError – If any eigenvalue is < -atol.
ValueError – If the matrix is not square.
- Return type:
- pytest_quantum.assert_process_fidelity_above(channel_a, channel_b, threshold=0.99, *, atol=1e-08)[source]¶
Assert process fidelity F_process(A, B) >= threshold.
Supports: - List of numpy Kraus operators - Qiskit Kraus/Choi/SuperOp objects (uses qiskit.quantum_info.process_fidelity) - numpy unitary matrices (converts to single-Kraus channel)
Process fidelity for unitary channels: F = |Tr(A† B)|² / d² For general channels uses Choi matrix inner product.
- Parameters:
- Raises:
AssertionError – If process fidelity < threshold.
TypeError – If channel types are not supported.
- Return type:
- pytest_quantum.assert_purity_above(rho, *, min_purity=0.95)[source]¶
Assert purity Tr(ρ²) ≥ min_purity.
Pure state: Tr(ρ²)=1.0. Maximally mixed d×d state: Tr(ρ²)=1/d.
- Parameters:
- Raises:
AssertionError – If Tr(ρ²) < min_purity.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_purity_above psi = np.array([[1], [0]], dtype=complex) rho = psi @ psi.conj().T assert_purity_above(rho, min_purity=0.99)
- pytest_quantum.assert_qasm2_roundtrip(circuit, *, atol=1e-06, allow_global_phase=True)[source]¶
Assert a Qiskit circuit survives an OpenQASM 2.0 export/import round-trip.
Exports the circuit via
qiskit.qasm2.dumpsand re-imports it viaqiskit.qasm2.loads, then compares unitaries.Note
QASM 2.0 has limited gate support. Circuits with custom/parametrised gates that lack QASM 2 definitions may fail — use
assert_qasm_roundtrip(QASM 3) for those circuits.- Parameters:
- Raises:
AssertionError – If re-imported circuit has a different unitary.
NotImplementedError – For non-Qiskit circuits.
ImportError – If qiskit is not installed.
- Return type:
Example:
from qiskit import QuantumCircuit from pytest_quantum import assert_qasm2_roundtrip qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) assert_qasm2_roundtrip(qc)
- pytest_quantum.assert_qasm_roundtrip(circuit, *, atol=1e-06, allow_global_phase=True)[source]¶
Assert that a circuit survives an export/import round-trip.
For Qiskit: uses OpenQASM 3 (
qiskit.qasm3.dumps/qasm3.loads). For Cirq: uses Cirq’s native JSON serialisation (cirq.to_json/cirq.read_json), which provides an exact identity round-trip for all standard Cirq circuits.- Parameters:
- Raises:
AssertionError – If re-imported circuit has a different unitary.
NotImplementedError – For unsupported frameworks.
ImportError – If the required package is missing.
- Return type:
- pytest_quantum.assert_qiskit_cirq_equivalent(qiskit_circuit, cirq_circuit, *, atol=1e-06, allow_global_phase=True)[source]¶
Assert that a Qiskit circuit and a Cirq circuit implement the same unitary.
Convenience wrapper around
assert_cross_platform_equivalent()with the framework labels pre-set to"qiskit"and"cirq".Qubit-ordering is normalised automatically (Qiskit is little-endian, Cirq is big-endian), so you can compare circuits using their natural qubit indices in each framework without manual reversal.
- Parameters:
qiskit_circuit (
object) – Aqiskit.QuantumCircuitinstance.cirq_circuit (
object) – Acirq.Circuitinstance.atol (
float) – Absolute tolerance for element-wise comparison (default1e-6).allow_global_phase (
bool) – IfTrue(default), circuits that differ only by a global phase are considered equivalent.
- Raises:
AssertionError – If the circuits implement different unitaries.
ValueError – If either circuit cannot be converted to a unitary.
- Return type:
Example:
import cirq from qiskit import QuantumCircuit from pytest_quantum.assertions.cross_platform import ( assert_qiskit_cirq_equivalent, ) qc = QuantumCircuit(1) qc.h(0) q = cirq.LineQubit.range(1) cc = cirq.Circuit(cirq.H(q[0])) assert_qiskit_cirq_equivalent(qc, cc)
- pytest_quantum.assert_qiskit_pytket_equivalent(qiskit_circuit, pytket_circuit, *, atol=1e-06, allow_global_phase=True)[source]¶
Assert that a Qiskit circuit and a pytket circuit implement the same unitary.
Convenience wrapper around
assert_cross_platform_equivalent()with the framework labels pre-set to"qiskit"and"pytket".The pytket unitary is obtained via
circuit.get_unitary()(exposed bypytest_quantum.converters.to_unitary.to_unitary()). Qubit-ordering is normalised automatically (Qiskit is little-endian, pytket is big-endian).- Parameters:
qiskit_circuit (
object) – Aqiskit.QuantumCircuitinstance.pytket_circuit (
object) – Apytket.Circuitinstance.atol (
float) – Absolute tolerance for element-wise comparison (default1e-6).allow_global_phase (
bool) – IfTrue(default), circuits that differ only by a global phase are considered equivalent.
- Raises:
AssertionError – If the circuits implement different unitaries.
ValueError – If either circuit cannot be converted to a unitary.
- Return type:
Example:
from pytket import Circuit as TketCircuit from qiskit import QuantumCircuit from pytest_quantum.assertions.cross_platform import ( assert_qiskit_pytket_equivalent, ) qc = QuantumCircuit(1) qc.h(0) tk = TketCircuit(1) tk.H(0) assert_qiskit_pytket_equivalent(qc, tk)
- pytest_quantum.assert_quantum_volume(backend, target_qv, *, num_trials=100, shots=1024, confidence=0.97, timeout=300.0)[source]¶
Assert a backend achieves at least target_qv quantum volume.
Runs IBM’s quantum volume (QV) protocol: for each width n from 1 up to
log2(target_qv), generate num_trials random square circuits of depth n x n using random SU(4) two-qubit unitaries, execute them, and check that the heavy output probability (HOP) exceeds 2/3 with the given statistical confidence (one-sided binomial test). The measured QV is2 ** nfor the largest n that passes.For IBM backends (
qiskit_ibm_runtime.IBMBackend) the circuits are run usingSamplerV2; all other backends fall back tobackend.run().- Parameters:
backend (
Any) – Qiskit-compatible backend to benchmark.target_qv (
int) – Minimum required quantum volume (must be a power of 2).num_trials (
int) – Number of random circuits per width (default 100).shots (
int) – Shots per circuit (default 1024).confidence (
float) – Required statistical confidence for HOP > 2/3 test (default 0.97).timeout (
float) – Maximum seconds to wait for all jobs (default 300).
- Returns:
The measured quantum volume (largest passing power of 2).
- Return type:
- Raises:
AssertionError – If the measured QV < target_qv, with details about heavy output probability, confidence, and trial count.
ImportError – If qiskit is not installed.
Example:
from pytest_quantum.assertions.benchmarking import assert_quantum_volume from qiskit_aer import AerSimulator def test_simulator_qv(): backend = AerSimulator() qv = assert_quantum_volume(backend, target_qv=4, num_trials=20) assert qv >= 4
- pytest_quantum.assert_randomized_benchmarking(backend, qubit, *, clifford_lengths=None, num_sequences=20, shots=1024, min_fidelity_per_clifford=0.999, timeout=300.0)[source]¶
Assert 1-qubit randomized benchmarking fidelity meets a minimum threshold.
For each sequence length m in clifford_lengths, generates num_sequences random 1-qubit Clifford sequences followed by the recovery Clifford that maps the state back to |0⟩. The survival probability (fraction of |0⟩ outcomes) is averaged across sequences for each length. An exponential decay
A * p^m + Bis fit to the length–survival-probability curve. The average gate fidelity isF = 1 - (1-p)/2.For IBM backends the circuits are run using
SamplerV2.- Parameters:
backend (
Any) – Qiskit-compatible backend.qubit (
int) – Index of the qubit to benchmark.clifford_lengths (
list[int] |None) – List of Clifford sequence lengths to test. Default:[1, 10, 20, 50, 100].num_sequences (
int) – Number of random sequences per length (default 20).shots (
int) – Shots per circuit (default 1024).min_fidelity_per_clifford (
float) – Minimum average gate fidelity (default 0.999).timeout (
float) – Maximum seconds for all jobs (default 300).
- Returns:
'fidelity'(float): Measured average gate fidelity.'decay_rate'(float): Fitted decay parameter p.'lengths'(list[int]): Sequence lengths used.'survival_probs'(list[float]): Mean survival probability per length.
- Return type:
- Raises:
AssertionError – If measured fidelity < min_fidelity_per_clifford.
ImportError – If qiskit is not installed.
Example:
from pytest_quantum.assertions.benchmarking import ( assert_randomized_benchmarking, ) from qiskit_aer import AerSimulator def test_rb_fidelity(): backend = AerSimulator() result = assert_randomized_benchmarking( backend, qubit=0, clifford_lengths=[1, 5, 10], num_sequences=5 ) assert result["fidelity"] >= 0.99
- pytest_quantum.assert_real_counts_close(circuit, backend, expected_probs, *, shots=4096, max_tvd=0.15, timeout=300.0, optimization_level=1)[source]¶
Assert real hardware counts match expected probabilities within TVD tolerance.
Runs the circuit on the backend and compares the empirical distribution to expected_probs using Total Variation Distance (TVD). The default TVD threshold (0.15) is intentionally more lenient than simulator tests because real hardware has noise.
- Parameters:
circuit (
Any) – QuantumCircuit with measurements.backend (
Any) – Qiskit-compatible backend.expected_probs (
dict[str,float]) – Dict mapping bitstring → ideal probability (must sum to ~1).shots (
int) – Number of shots (default 4096; more = less sampling noise).max_tvd (
float) – Maximum allowed TVD (default 0.15).timeout (
float) – Max seconds to wait for job completion (default 300).optimization_level (
int) – Transpilation level (default 1).
- Returns:
Actual measurement counts.
- Return type:
- Raises:
AssertionError – If TVD exceeds max_tvd.
Example:
from pytest_quantum import assert_real_counts_close def test_bell_state(ibm_backend): from qiskit import QuantumCircuit qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure_all() assert_real_counts_close( qc, ibm_backend, expected_probs={"00": 0.5, "11": 0.5}, max_tvd=0.15, )
- pytest_quantum.assert_sampler_distribution(result, expected_probs, *, pub_idx=0, significance=0.05)[source]¶
Assert a Qiskit Sampler result matches expected probability distribution.
Uses chi-square goodness-of-fit (same as assert_measurement_distribution).
- Parameters:
- Raises:
AssertionError – If distribution doesn’t match.
- Return type:
Example:
def test_sampler_bell(qiskit_sampler): from qiskit.circuit import QuantumCircuit from pytest_quantum import assert_sampler_distribution qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure([0, 1], [0, 1]) result = qiskit_sampler.run([(qc,)]).result() assert_sampler_distribution(result, {"00": 0.5, "11": 0.5})
- pytest_quantum.assert_schmidt_rank_at_most(statevector, partition, max_rank, *, n_qubits=None, tol=1e-10)[source]¶
Assert Schmidt rank of bipartite pure state partition is at most max_rank.
Schmidt rank = 1 means separable (product state) on this partition.
- Parameters:
- Raises:
AssertionError – If Schmidt rank > max_rank.
ValueError – If statevector length is not a power of 2.
- Return type:
- pytest_quantum.assert_stabilizer_state(tableau_simulator, expected_stabilizers)[source]¶
Assert a Stim TableauSimulator is in the expected stabilizer state.
- Parameters:
- Raises:
AssertionError – If any stabilizer is not satisfied (expectation ≠ +1).
ImportError – If stim is not installed.
- Return type:
Example:
import stim from pytest_quantum import assert_stabilizer_state sim = stim.TableauSimulator() sim.h(0) sim.cnot(0, 1) assert_stabilizer_state(sim, ["+XX", "+ZZ"])
- pytest_quantum.assert_state_fidelity_above(actual, target, threshold=0.99)[source]¶
Assert that two pure quantum states have fidelity at or above threshold.
Fidelity \(F = |\langle\text{actual}|\text{target}\rangle|^2\) equals 1.0 for identical states (up to global phase) and 0.0 for orthogonal states.
This is the primary assertion for MBQC / Graphix tests where the circuit does not have a fixed unitary representation.
- Parameters:
- Raises:
AssertionError – If
fidelity(actual, target) < threshold.ValueError – If the arrays have incompatible sizes.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_state_fidelity_above BELL = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2) def test_bell_graphix(graphix_backend): from graphix.transpiler import Circuit circuit = Circuit(2) circuit.h(0) circuit.cnot(0, 1) pattern = circuit.transpile().pattern output = graphix_backend.run_pattern(pattern) assert_state_fidelity_above(output, BELL, threshold=0.999)
- pytest_quantum.assert_states_close(actual, target, *, atol=1e-06)[source]¶
Assert that two statevectors are element-wise close, up to global phase.
Stricter than
assert_state_fidelity_above()— use for exact simulator-to-simulator comparisons where you want bit-for-bit agreement.- Parameters:
- Raises:
AssertionError – If any element differs by more than atol after removing the global phase.
- Return type:
Example:
def test_plus_state(aer_statevector_simulator): from qiskit import QuantumCircuit, transpile qc = QuantumCircuit(1) qc.h(0) qc.save_statevector() qc_t = transpile(qc, aer_statevector_simulator) sv = aer_statevector_simulator.run(qc_t).result().get_statevector() PLUS = np.array([1, 1]) / np.sqrt(2) assert_states_close(sv.data, PLUS)
- pytest_quantum.assert_stim_detector_error_rate_below(circuit, max_error_rate, *, shots=10000, seed=None)[source]¶
Assert that the mean detector error rate is below threshold.
Useful for verifying that a noise model produces errors at the expected rate.
- Parameters:
- Raises:
AssertionError – If mean detector error rate exceeds max_error_rate.
ImportError – If stim is not installed.
ValueError – If the circuit has no detectors.
- Return type:
- pytest_quantum.assert_stim_logical_error_rate_below(circuit, max_error_rate, *, shots=10000, seed=None)[source]¶
Assert that a Stim QEC circuit has logical error rate below threshold.
The circuit must contain DETECTOR and OBSERVABLE_INCLUDE instructions.
- Parameters:
- Raises:
AssertionError – If logical error rate exceeds max_error_rate.
ImportError – If stim is not installed.
ValueError – If the circuit has no observables.
- Return type:
Example:
import stim from pytest_quantum import assert_stim_logical_error_rate_below c = stim.Circuit.generated( "repetition_code:memory", rounds=3, distance=3, after_clifford_depolarization=0.001, ) assert_stim_logical_error_rate_below(c, max_error_rate=0.05, shots=1000)
- pytest_quantum.assert_t1_above(backend, qubit, target_t1_us, *, shots=1024, timeout=300.0)[source]¶
Assert qubit T1 relaxation time is at least target_t1_us microseconds.
Prepares the qubit in |1⟩ and waits variable
delaydurations (using Qiskit’sDelaygate), then measures. The |1⟩ survival probability is fit to an exponential decayexp(-t / T1)to extract T1.Note
Accurate T1 measurement requires pulse-level backend support and a backend that honours
Delaygates (e.g. a real IBM device orAerSimulatorwith a noise model). On an ideal simulator T1 will appear infinite.- Parameters:
- Returns:
Measured T1 in microseconds.
- Return type:
- Raises:
AssertionError – If measured T1 < target_t1_us.
ImportError – If qiskit is not installed.
Example:
from pytest_quantum.assertions.benchmarking import assert_t1_above def test_t1(ibm_backend): t1 = assert_t1_above(ibm_backend, qubit=0, target_t1_us=50.0) print(f"Measured T1: {t1:.1f} µs")
- pytest_quantum.assert_t2_above(backend, qubit, target_t2_us, *, shots=1024, timeout=300.0)[source]¶
Assert qubit T2 (Hahn echo) coherence time is at least target_t2_us µs.
Implements a Hahn echo sequence: X/2 – delay/2 – X – delay/2 – X/2 – measure. Fits |0⟩ survival probability to exp(-t / T2) to extract T2.
- Parameters:
- Returns:
Measured T2 in microseconds.
- Return type:
- Raises:
AssertionError – If measured T2 < target_t2_us.
ImportError – If qiskit is not installed.
- pytest_quantum.assert_t2star_above(backend, qubit, target_t2star_us, *, shots=1024, timeout=300.0)[source]¶
Assert qubit T2* (free induction decay) is at least target_t2star_us µs.
Implements a Ramsey sequence: Rx(π/2) – delay – Rx(π/2) – measure. Fits |0⟩ survival to a decaying cosine; the envelope gives T2*.
- Parameters:
- Returns:
Measured T2* in microseconds.
- Return type:
- Raises:
AssertionError – If measured T2* < target_t2star_us.
ImportError – If qiskit is not installed.
- pytest_quantum.assert_trace_distance_below(rho, sigma, *, max_distance=0.01)[source]¶
Assert trace distance T(ρ,σ) = ½ Tr(|ρ-σ|) is at most max_distance.
T=0 means identical states, T=1 means perfectly distinguishable. Physical meaning: maximum probability of distinguishing the two states in any single measurement equals T(ρ,σ).
- Parameters:
- Raises:
AssertionError – If T(ρ,σ) > max_distance.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_trace_distance_below rho = np.eye(2, dtype=complex) / 2 assert_trace_distance_below(rho, rho, max_distance=0.01)
- pytest_quantum.assert_transpilation_depth_below(circuit, max_depth, basis_gates=None, *, optimization_level=3)[source]¶
Assert that a circuit transpiled to a basis set has depth <= max_depth.
Useful as a regression test to detect when compiler changes silently increase circuit depth.
- Parameters:
- Raises:
AssertionError – If transpiled depth exceeds max_depth.
ImportError – If qiskit is not installed.
- Return type:
Example:
from pytest_quantum import assert_transpilation_depth_below assert_transpilation_depth_below( qc, max_depth=5, basis_gates=["cx", "rz", "sx", "x"] )
- pytest_quantum.assert_transpilation_equivalent(circuit, basis_gates_a, basis_gates_b=None, *, optimization_level=1, atol=1e-06, allow_global_phase=True)[source]¶
Assert that a circuit compiled to different basis sets is unitarily equivalent.
Transpiles the circuit to basis_gates_a (and optionally basis_gates_b), then verifies the unitaries match.
- Parameters:
circuit (
object) – Qiskit QuantumCircuit to transpile.basis_gates_b (
list[str] |None) – Second target basis gate set (default: original circuit).optimization_level (
int) – Qiskit transpile optimization level 0-3 (default 1).atol (
float) – Absolute tolerance for unitary comparison (default 1e-6).allow_global_phase (
bool) – If True, ignore global phase differences (default True).
- Raises:
AssertionError – If transpiled circuits are not equivalent.
ImportError – If qiskit is not installed.
- Return type:
Example:
from pytest_quantum import assert_transpilation_equivalent from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) assert_transpilation_equivalent( qc, basis_gates_a=["cx", "u"], basis_gates_b=["ecr", "rz", "sx", "x"], )
- pytest_quantum.assert_transpilation_preserves_semantics(circuit, backend, *, optimization_level=1, atol=1e-06)[source]¶
Assert that transpiling a Qiskit circuit preserves its unitary.
Transpiles circuit for backend and verifies the resulting circuit implements the same unitary (up to global phase).
- Parameters:
- Raises:
AssertionError – If transpiled circuit has different unitary.
NotImplementedError – For non-Qiskit circuits.
ImportError – If qiskit is not installed.
- Return type:
Example:
from qiskit import QuantumCircuit from qiskit.providers.fake_provider import GenericBackendV2 from pytest_quantum import assert_transpilation_preserves_semantics qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) backend = GenericBackendV2(num_qubits=2) assert_transpilation_preserves_semantics(qc, backend)
- pytest_quantum.assert_unitary(circuit, expected, *, atol=1e-06, allow_global_phase=True)[source]¶
Assert that circuit implements the expected unitary matrix.
- Parameters:
circuit (
object) – Any supported quantum circuit (Qiskit, Cirq, Braket, PennyLane).expected (
ndarray[tuple[Any,...],dtype[cdouble]]) – Target unitary as a numpy array, shape(2**n, 2**n).atol (
float) – Absolute tolerance for element-wise comparison (default1e-6).allow_global_phase (
bool) – IfTrue(default), circuits that differ only by a global phasee^{iθ}are considered equivalent. This is physically correct because global phase is not observable.
- Raises:
AssertionError – If the circuit’s unitary does not match expected.
TypeError – If circuit is not a recognised framework type.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_unitary HADAMARD = np.array([[1, 1], [1, -1]]) / np.sqrt(2) def test_h_gate(): from qiskit import QuantumCircuit qc = QuantumCircuit(1) qc.h(0) assert_unitary(qc, HADAMARD)
- pytest_quantum.assert_unitary_snapshot(circuit, name, *, update=False, atol=1e-06, allow_global_phase=True)[source]¶
Assert that a circuit’s unitary matches its saved snapshot.
First run: saves the unitary to .pytest-quantum-snapshots/<name>.npy. Subsequent runs: loads and compares. Pass –quantum-update-snapshots on the CLI to regenerate all snapshots.
- Parameters:
circuit (
object) – Any supported quantum circuit (Qiskit, Cirq, Braket, PennyLane).name (
str) – Unique snapshot name (used as filename, no path separators).update (
bool) – If True, overwrite existing snapshot.atol (
float) – Absolute tolerance (default 1e-6).allow_global_phase (
bool) – If True (default), ignore global phase differences.
- Raises:
AssertionError – If the unitary differs from the snapshot.
TypeError – If the circuit type is not supported.
- Return type:
Example:
def test_compiler_stable(compiled_circuit): from pytest_quantum import assert_unitary_snapshot assert_unitary_snapshot(compiled_circuit, "my_compiler_bell")
- pytest_quantum.assert_vqe_converges(cost_function, initial_params, *, method='COBYLA', max_iterations=200, expected_minimum=None, atol=0.1, rtol=0.0)[source]¶
Assert a VQE / variational optimization converges.
Runs a full optimization loop and checks: 1. The final cost is lower than the initial cost (energy decreased). 2. If
expected_minimumis given, the result is withinatolof it.- Parameters:
cost_function (
Any) – Callable mapping parameter array -> float (e.g. a QNode returningqml.expval(H)).initial_params (
Any) – Starting parameter vector (list or numpy array).method (
str) – SciPy optimizer (default"COBYLA"; gradient-free, good for noisy quantum hardware).max_iterations (
int) – Maximum optimizer iterations (default 200).expected_minimum (
float|None) – Known ground-state energy to compare against.atol (
float) – Absolute tolerance when comparing against expected_minimum.rtol (
float) – Relative tolerance (default 0, i.e. absolute only).
- Raises:
AssertionError – If energy did not decrease or result misses expected_minimum.
- Return type:
Example:
import pennylane as qml import numpy as np from pytest_quantum import assert_vqe_converges dev = qml.device("default.qubit", wires=1) @qml.qnode(dev) def circuit(theta): qml.RY(theta, wires=0) return qml.expval(qml.PauliZ(0)) # Ground state of Z is |1>, energy = -1 assert_vqe_converges(circuit, [0.5], expected_minimum=-1.0, atol=0.05)
- pytest_quantum.assert_xeb_fidelity_above(backend, num_qubits, target_fidelity, *, num_circuits=20, depth=10, shots=1024)[source]¶
Assert cross-entropy benchmarking (XEB) fidelity is at least target_fidelity.
XEB measures how well a noisy backend reproduces the ideal output distribution of random circuits. For each random circuit, the XEB fidelity is:
F_XEB = D * <P_ideal(x)>_measured - 1
where
D = 2**num_qubitsand<P_ideal(x)>is the average ideal probability of observed bitstrings. Perfect fidelity = 1.0; random noise = 0.0.- Parameters:
backend (
Any) – Qiskit-compatible backend (AerSimulator, IBM, etc.).num_qubits (
int) – Number of qubits to benchmark.target_fidelity (
float) – Minimum acceptable XEB fidelity (0.0–1.0).num_circuits (
int) – Number of random circuits to average over (default 20).depth (
int) – Circuit depth in random layers (default 10).shots (
int) – Shots per circuit (default 1024).
- Returns:
Average XEB fidelity across all circuits.
- Return type:
- Raises:
AssertionError – If XEB fidelity < target_fidelity.
ImportError – If qiskit is not installed.
Example:
from pytest_quantum.assertions.quantum_ml import assert_xeb_fidelity_above from qiskit_aer import AerSimulator def test_xeb(aer_simulator): fidelity = assert_xeb_fidelity_above( aer_simulator, num_qubits=2, target_fidelity=0.9 ) assert fidelity >= 0.9
- pytest_quantum.assert_zne_expectation_close(circuit, executor, expected, *, scale_factors=None, atol=0.1, noise_extrapolation='richardson')[source]¶
Assert ZNE-mitigated expectation value is close to expected.
- Parameters:
circuit (
object) – Quantum circuit.executor (
Callable[...,float]) – Callable(circuit) -> float.expected (
float) – Expected ideal expectation value.scale_factors (
list[float] |None) – Noise scale factors (default [1.0, 2.0, 3.0]).atol (
float) – Absolute tolerance (default 0.1).noise_extrapolation (
str) – Extrapolation method (default “richardson”).
- Raises:
AssertionError – If mitigated value differs from expected by more than atol.
ImportError – If mitiq is not installed.
- Return type:
Example:
from pytest_quantum import assert_zne_expectation_close assert_zne_expectation_close(circuit, noisy_executor, expected=1.0, atol=0.05)
- pytest_quantum.assert_zne_reduces_error(circuit, executor, observable=None, *, scale_factors=None, noise_extrapolation='richardson', threshold_improvement=0.0)[source]¶
Assert that Zero-Noise Extrapolation (ZNE) reduces the estimation error.
Runs the circuit at multiple noise scale factors, extrapolates to zero noise, and asserts the mitigated result is closer to the ideal than the unmitigated.
- Parameters:
circuit (
object) – Quantum circuit (Qiskit QuantumCircuit supported).executor (
Callable[...,float]) – Callable that takes a circuit and returns a float expectation value. Should handle noise internally.observable (
object|None) – Observable to measure (optional, passed to executor).scale_factors (
list[float] |None) – Noise scale factors (default [1.0, 2.0, 3.0]).noise_extrapolation (
str) – Extrapolation method: “richardson”, “linear”, “poly2” (default “richardson”).threshold_improvement (
float) – Minimum required improvement ratio (default 0 = any improvement is accepted).
- Return type:
- Returns:
Tuple of (unmitigated_value, mitigated_value).
- Raises:
AssertionError – If ZNE does not improve the result.
ImportError – If mitiq is not installed.
Example:
from pytest_quantum import assert_zne_reduces_error def noisy_executor(circuit): # run circuit on noisy simulator and return expectation value ... mitigated, unmitigated = assert_zne_reduces_error( circuit, noisy_executor, ideal_value=0.0 )
- pytest_quantum.chi_square_test(observed, expected_probs, total_shots=None)[source]¶
Chi-square goodness-of-fit test for quantum measurement distributions.
Tests whether observed counts are consistent with expected_probs.
- Parameters:
observed (
dict[str,int] |ndarray[tuple[Any,...],dtype[double]]) – Either a count dict{"00": 489, "11": 511}or a 1-D numpy array of observed counts.expected_probs (
dict[str,float] |ndarray[tuple[Any,...],dtype[double]]) – Either a probability dict{"00": 0.5, "11": 0.5}(must sum to 1) or a 1-D numpy array of expected probabilities. When using numpy arrays, provide total_shots so expected counts can be computed.total_shots (
int|None) – Required when both inputs are numpy arrays. Ignored when dict inputs are used (total is inferred from observed).
- Return type:
- Returns:
(statistic, pvalue)— the chi-square statistic and the p-value. Reject the null hypothesis (distributions match) whenpvalue < significance.- Raises:
ValueError – If inputs are inconsistent (different keys, missing total_shots for array inputs, etc.).
Example:
>>> stat, p = chi_square_test({"00": 495, "11": 505}, {"00": 0.5, "11": 0.5}) >>> p > 0.05 # consistent with Bell state True
- pytest_quantum.depolarizing_kraus(n_qubits, error_rate)[source]¶
Return Kraus operators for the single-qubit depolarising channel.
Channel definition:
E(ρ) = (1−p)ρ + (p/3)(XρX + YρY + ZρZ)
- Parameters:
- Returns:
[√(1−p)·I, √(p/3)·X, √(p/3)·Y, √(p/3)·Z].- Return type:
- Raises:
ValueError – If n_qubits ≠ 1 or error_rate is not in [0, 1].
Example:
from pytest_quantum.random import depolarizing_kraus from pytest_quantum import assert_channel_is_cptp assert_channel_is_cptp(depolarizing_kraus(1, 0.1))
- pytest_quantum.fidelity(psi, phi)[source]¶
Pure-state fidelity \(|\langle\psi|\phi\rangle|^2\).
Both arrays are flattened and normalised before computation, so minor normalisation errors from simulators do not affect the result.
- Parameters:
- Return type:
- Returns:
Float in
[0.0, 1.0].1.0means the states are identical (up to global phase).0.0means orthogonal.- Raises:
ValueError – If psi and phi have different sizes.
Example:
>>> import numpy as np >>> zero = np.array([1, 0], dtype=complex) >>> plus = np.array([1, 1], dtype=complex) / np.sqrt(2) >>> fidelity(zero, plus) 0.5
- pytest_quantum.min_shots(epsilon, alpha=0.05, power=0.8)[source]¶
Minimum shots to detect a total-variation distance of epsilon.
Based on two-sample statistical power analysis:
\[N = \lceil (z_{1-\alpha/2} + z_{\text{power}})^2 \; / \; (2\varepsilon^2) \rceil\]- Parameters:
epsilon (
float) – Minimum detectable total variation distance (TVD). For example,0.01means the test can reliably catch a 1 % deviation from the expected distribution.alpha (
float) – Significance level (default0.05→ 95 % confidence).power (
float) – Statistical power, i.e. probability of detecting a real error (default0.80→ 80 % power).
- Return type:
- 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
- pytest_quantum.random_density_matrix(n_qubits, rank=None, *, seed=None)[source]¶
Generate a random valid density matrix (PSD, trace 1).
- Parameters:
- Return type:
- Returns:
Complex128 array of shape
(2**n_qubits, 2**n_qubits).
Example:
from pytest_quantum.random import random_density_matrix rho = random_density_matrix(1, seed=42) assert rho.shape == (2, 2)
- pytest_quantum.random_kraus_channel(n_qubits, n_kraus=4, *, seed=None)[source]¶
Generate random valid Kraus operators for a CPTP channel.
Constructs a random Stinespring isometry and extracts Kraus operators that satisfy the completeness relation
∑ K†K = I.- Parameters:
- Return type:
- Returns:
List of n_kraus complex128 arrays of shape
(2**n_qubits, 2**n_qubits).
Example:
from pytest_quantum.random import random_kraus_channel from pytest_quantum import assert_channel_is_cptp kraus = random_kraus_channel(1, seed=7) assert_channel_is_cptp(kraus)
- pytest_quantum.random_statevector(n_qubits, *, seed=None)[source]¶
Generate a random normalised statevector (Haar-random pure state).
- Parameters:
- Return type:
- Returns:
Normalised complex128 array of shape
(2**n_qubits,).
Example:
from pytest_quantum.random import random_statevector sv = random_statevector(2, seed=0) assert sv.shape == (4,) assert abs(sum(abs(sv) ** 2) - 1) < 1e-12
- pytest_quantum.random_unitary(n_qubits, *, seed=None)[source]¶
Generate a Haar-random unitary matrix (CUE).
Uses QR decomposition of the Ginibre ensemble with phase correction to guarantee an exact Haar distribution.
- Parameters:
- Return type:
- Returns:
Unitary complex128 array of shape
(2**n_qubits, 2**n_qubits).
Example:
from pytest_quantum.random import random_unitary import numpy as np U = random_unitary(2, seed=0) assert np.allclose(U @ U.conj().T, np.eye(4), atol=1e-12)
- pytest_quantum.recommended_shots(expected_probs, min_expected_per_bucket=5)[source]¶
Recommend shots so every bucket gets enough expected counts for chi-square.
The chi-square goodness-of-fit test requires each cell to have an expected count of at least 5 (a common rule of thumb) to be statistically valid. This function returns the shot count that satisfies that requirement for the rarest outcome in expected_probs.
- Parameters:
- Return type:
- Returns:
Recommended shot count as an integer.
- Raises:
ValueError – If expected_probs is empty or all probabilities are zero.
Example:
>>> recommended_shots({"00": 0.499, "01": 0.001, "11": 0.5}) 5000 >>> recommended_shots({"0": 0.5, "1": 0.5}) 10
- pytest_quantum.tvd(p, q)[source]¶
Total Variation Distance between two probability distributions.
\[\text{TVD}(p, q) = \frac{1}{2} \sum_x |p(x) - q(x)|\]- Parameters:
- Return type:
- Returns:
Float in
[0.0, 1.0].0.0means identical distributions;1.0means disjoint support.
Example:
>>> import numpy as np >>> tvd(np.array([0.5, 0.5]), np.array([0.6, 0.4])) 0.1
Assertions¶
Unitary assertions¶
Unitary-level assertions for quantum circuits.
These functions compare circuits at the level of their unitary matrix — the most rigorous form of correctness check for deterministic quantum operations.
- pytest_quantum.assertions.unitary.assert_unitary(circuit, expected, *, atol=1e-06, allow_global_phase=True)[source]¶
Assert that circuit implements the expected unitary matrix.
- Parameters:
circuit (
object) – Any supported quantum circuit (Qiskit, Cirq, Braket, PennyLane).expected (
ndarray[tuple[Any,...],dtype[cdouble]]) – Target unitary as a numpy array, shape(2**n, 2**n).atol (
float) – Absolute tolerance for element-wise comparison (default1e-6).allow_global_phase (
bool) – IfTrue(default), circuits that differ only by a global phasee^{iθ}are considered equivalent. This is physically correct because global phase is not observable.
- Raises:
AssertionError – If the circuit’s unitary does not match expected.
TypeError – If circuit is not a recognised framework type.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_unitary HADAMARD = np.array([[1, 1], [1, -1]]) / np.sqrt(2) def test_h_gate(): from qiskit import QuantumCircuit qc = QuantumCircuit(1) qc.h(0) assert_unitary(qc, HADAMARD)
- pytest_quantum.assertions.unitary.assert_circuits_equivalent(circuit_a, circuit_b, *, atol=1e-06)[source]¶
Assert that two circuits implement the same unitary, up to global phase.
Works across frameworks — you can compare a Qiskit circuit against a Cirq circuit, a Braket circuit, or a PennyLane QNode.
For two Qiskit circuits,
mqt.qcecis used automatically when installed (faster, exact verification via decision diagrams / ZX-calculus). For cross-framework comparison the circuits are both converted to numpy matrices and compared numerically.- Parameters:
- Raises:
AssertionError – If the circuits implement different unitaries.
TypeError – If either argument is not a recognised circuit type.
- Return type:
Example:
from pytest_quantum import assert_circuits_equivalent def test_cnot_cross_framework(): import cirq from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.cx(0, 1) q0, q1 = cirq.LineQubit.range(2) cc = cirq.Circuit(cirq.CNOT(q0, q1)) assert_circuits_equivalent(qc, cc)
- pytest_quantum.assertions.unitary.assert_transpilation_preserves_semantics(circuit, backend, *, optimization_level=1, atol=1e-06)[source]¶
Assert that transpiling a Qiskit circuit preserves its unitary.
Transpiles circuit for backend and verifies the resulting circuit implements the same unitary (up to global phase).
- Parameters:
- Raises:
AssertionError – If transpiled circuit has different unitary.
NotImplementedError – For non-Qiskit circuits.
ImportError – If qiskit is not installed.
- Return type:
Example:
from qiskit import QuantumCircuit from qiskit.providers.fake_provider import GenericBackendV2 from pytest_quantum import assert_transpilation_preserves_semantics qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) backend = GenericBackendV2(num_qubits=2) assert_transpilation_preserves_semantics(qc, backend)
State assertions¶
Statevector-level assertions for quantum tests.
Use these when you have the full output statevector from a simulator (Aer statevector mode, Cirq simulator, or the Graphix backend) and want to verify the quantum state directly — more informative than comparing shot distributions.
- pytest_quantum.assertions.states.assert_state_fidelity_above(actual, target, threshold=0.99)[source]¶
Assert that two pure quantum states have fidelity at or above threshold.
Fidelity \(F = |\langle\text{actual}|\text{target}\rangle|^2\) equals 1.0 for identical states (up to global phase) and 0.0 for orthogonal states.
This is the primary assertion for MBQC / Graphix tests where the circuit does not have a fixed unitary representation.
- Parameters:
- Raises:
AssertionError – If
fidelity(actual, target) < threshold.ValueError – If the arrays have incompatible sizes.
- Return type:
Example:
import numpy as np from pytest_quantum import assert_state_fidelity_above BELL = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2) def test_bell_graphix(graphix_backend): from graphix.transpiler import Circuit circuit = Circuit(2) circuit.h(0) circuit.cnot(0, 1) pattern = circuit.transpile().pattern output = graphix_backend.run_pattern(pattern) assert_state_fidelity_above(output, BELL, threshold=0.999)
- pytest_quantum.assertions.states.assert_normalized(statevector, *, atol=1e-06)[source]¶
Assert statevector has unit norm: ||ψ||₂ = 1.
A common bug in manual statevector construction is forgetting to normalize.
- Parameters:
- Raises:
AssertionError – If ||sv||₂ is not within atol of 1.0, showing the actual norm.
- Return type:
Example:
>>> import numpy as np >>> sv = np.array([1, 0, 0, 0], dtype=complex) # |00> >>> assert_normalized(sv) # passes >>> sv_bad = np.array([1, 1], dtype=complex) # NOT normalized >>> assert_normalized(sv_bad) # fails: norm = 1.4142
- pytest_quantum.assertions.states.assert_states_close(actual, target, *, atol=1e-06)[source]¶
Assert that two statevectors are element-wise close, up to global phase.
Stricter than
assert_state_fidelity_above()— use for exact simulator-to-simulator comparisons where you want bit-for-bit agreement.- Parameters:
- Raises:
AssertionError – If any element differs by more than atol after removing the global phase.
- Return type:
Example:
def test_plus_state(aer_statevector_simulator): from qiskit import QuantumCircuit, transpile qc = QuantumCircuit(1) qc.h(0) qc.save_statevector() qc_t = transpile(qc, aer_statevector_simulator) sv = aer_statevector_simulator.run(qc_t).result().get_statevector() PLUS = np.array([1, 1]) / np.sqrt(2) assert_states_close(sv.data, PLUS)
Distribution assertions¶
Shot-distribution assertions for quantum tests.
These assertions test the statistical output of a circuit — the probability distribution over measurement outcomes — using principled chi-square tests rather than ad-hoc tolerances.
- pytest_quantum.assertions.distributions.assert_measurement_distribution(counts, expected_probs, *, significance=0.05, min_expected_per_bucket=5)[source]¶
Assert that measured counts match the expected probability distribution.
Uses a chi-square goodness-of-fit test — the standard statistical tool for this exact problem. The test fails only when the deviation is statistically significant (p < significance), so occasional random fluctuations do not cause false failures.
- Parameters:
counts (
dict[str,int]) – Measured counts dict, e.g.{"00": 489, "11": 511}. Keys are bitstring labels; values are integer counts.expected_probs (
dict[str,float]) – Expected probability dict, e.g.{"00": 0.5, "11": 0.5}. Must sum to 1.0 (within 1e-6). Outcomes not present are assumed to have zero expected probability.significance (
float) – P-value threshold below which the test fails (default0.05).min_expected_per_bucket (
int) – Chi-square requires expected count >= 5 per non-zero cell for valid results. AUserWarningis raised (but the test does not fail) if this is violated; consider increasing shots.
- Raises:
AssertionError – If
p_value < significance, with a per-state breakdown of observed vs expected probabilities.ValueError – If expected_probs does not sum to 1.0, or counts is empty.
- Return type:
None
Example:
:rtype: :sphinx_autodoc_typehints_type:`\:py\:obj\:\`None\``
- def test_bell_distribution(aer_simulator):
from qiskit import QuantumCircuit, transpile from pytest_quantum import assert_measurement_distribution
qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) qc.measure_all() qc_t = transpile(qc, aer_simulator) counts = aer_simulator.run(qc_t, shots=2000).result().get_counts()
- assert_measurement_distribution(
counts, expected_probs={“00”: 0.5, “11”: 0.5},
)
- pytest_quantum.assertions.distributions.assert_counts_close(counts_a, counts_b, *, max_tvd=0.05)[source]¶
Assert that two count dictionaries are statistically close.
Computes the Total Variation Distance (TVD) between the normalised distributions and fails if it exceeds max_tvd.
Useful for comparing two backends, or checking that transpilation has not changed a circuit’s output distribution.
- Parameters:
- Raises:
AssertionError – If TVD exceeds max_tvd.
- Return type:
Example:
def test_transpile_preserves_distribution(aer_simulator): from qiskit import QuantumCircuit, transpile qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) qc.measure_all() # ideal vs noise-free transpiled qc_t = transpile(qc, aer_simulator, optimization_level=3) counts_ideal = aer_simulator.run(qc, shots=2000).result().get_counts() counts_transpiled = ( aer_simulator.run(qc_t, shots=2000).result().get_counts() ) assert_counts_close(counts_ideal, counts_transpiled, max_tvd=0.05)
Structure assertions¶
Circuit structure assertions.
These assertions check static properties of a circuit — depth, gate counts, qubit width — without executing it. Useful for catching regressions in compiler output or ensuring a circuit meets hardware constraints.
- pytest_quantum.assertions.structure.assert_circuit_depth(circuit, *, max_depth=None, min_depth=None)[source]¶
Assert that a circuit’s depth is within the specified bounds.
At least one of max_depth or min_depth must be provided.
Supported frameworks: Qiskit, Cirq, Amazon Braket.
- Parameters:
- Raises:
AssertionError – If the depth is outside the specified bounds.
TypeError – If the circuit type is not supported.
ValueError – If neither bound is provided.
- Return type:
Example:
def test_circuit_depth(): from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) assert_circuit_depth(qc, max_depth=3)
- pytest_quantum.assertions.structure.assert_circuit_width(circuit, expected_qubits)[source]¶
Assert that a circuit acts on exactly expected_qubits qubits.
Supported frameworks: Qiskit, Cirq, Amazon Braket, PennyLane.
- Parameters:
- Raises:
AssertionError – If the qubit count does not match.
TypeError – If the circuit type is not supported.
- Return type:
Example:
def test_circuit_width(): from qiskit import QuantumCircuit qc = QuantumCircuit(3) qc.h(0) qc.cx(0, 1) qc.cx(1, 2) assert_circuit_width(qc, expected_qubits=3)
- pytest_quantum.assertions.structure.assert_gate_count(circuit, gate_name, expected)[source]¶
Assert that a circuit contains exactly expected occurrences of gate_name.
Supported frameworks: Qiskit, Cirq, PennyLane.
- Parameters:
- Raises:
AssertionError – If the actual count differs from expected.
NotImplementedError – If the framework is not yet supported.
- Return type:
Example:
def test_t_count(): from qiskit import QuantumCircuit qc = QuantumCircuit(2) qc.t(0) qc.t(1) qc.cx(0, 1) assert_gate_count(qc, "t", 2) assert_gate_count(qc, "cx", 1)
- pytest_quantum.assertions.structure.assert_gates_in_basis_set(circuit, basis_gates, *, case_sensitive=False)[source]¶
Assert every gate in the circuit belongs to the specified basis gate set.
Useful for verifying that a transpiled circuit only uses a target backend’s native gate set (e.g. after
qiskit.transpilewithbasis_gates=[...]).- Parameters:
- Raises:
AssertionError – Lists every non-basis gate found.
NotImplementedError – For unsupported frameworks.
- Return type:
Example:
from qiskit import QuantumCircuit, transpile from qiskit_aer import AerSimulator from pytest_quantum import assert_gates_in_basis_set qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) transpiled = transpile(qc, basis_gates=["cx", "u3"]) assert_gates_in_basis_set(transpiled, {"cx", "u3"})
- pytest_quantum.assertions.structure.assert_circuit_is_clifford(circuit)[source]¶
Assert a circuit uses only Clifford gates (H, S, S†, X, Y, Z, CNOT, CZ, SWAP).
Clifford circuits are classically efficiently simulable. Supported: Qiskit, Cirq.
- Raises:
AssertionError – If non-Clifford gates found.
NotImplementedError – If framework not supported.
- Parameters:
circuit (object)
- Return type:
None
Example:
def test_is_clifford(): from qiskit import QuantumCircuit from pytest_quantum import assert_circuit_is_clifford qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) assert_circuit_is_clifford(qc)
- pytest_quantum.assertions.structure.assert_no_mid_circuit_measurement(circuit)[source]¶
Assert a circuit has no mid-circuit measurements (all measurements are terminal).
Mid-circuit measurements (measurements followed by further gate operations) are not supported on all hardware backends. This assertion verifies that all measurements occur after all gate operations — i.e., measurements only appear in the final layer.
Supported frameworks: Qiskit, Cirq.
- Parameters:
circuit (
object) – A quantum circuit from a supported framework.- Raises:
AssertionError – If mid-circuit measurements are detected.
NotImplementedError – If framework is not supported.
- Return type:
Example:
from qiskit import QuantumCircuit from pytest_quantum import assert_no_mid_circuit_measurement qc = QuantumCircuit(2, 2) qc.h(0) qc.cx(0, 1) qc.measure_all() assert_no_mid_circuit_measurement(qc) # passes — measurements are terminal
- pytest_quantum.assertions.structure.assert_has_diagram(circuit, expected, *, strict=False)[source]¶
Assert circuit’s text representation contains expected pattern.
For Qiskit: uses
circuit.draw('text'). For Cirq: usesstr(circuit)(circuit.to_text_diagram()).- Parameters:
circuit (
object) – Any supported framework circuit.expected (
str) – Expected string (exact if strict isTrue, substring otherwise).strict (
bool) – IfTrue, require exact match after stripping leading / trailing whitespace. IfFalse(default), just check that expected is a substring of the diagram.
- Raises:
AssertionError – If diagram doesn’t match.
NotImplementedError – For frameworks without text diagram support.
- Return type:
Example:
from qiskit import QuantumCircuit from pytest_quantum import assert_has_diagram qc = QuantumCircuit(1) qc.h(0) assert_has_diagram(qc, "H")
Statistics¶
Shot calculators¶
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.
- pytest_quantum.stats.shots.min_shots(epsilon, alpha=0.05, power=0.8)[source]¶
Minimum shots to detect a total-variation distance of epsilon.
Based on two-sample statistical power analysis:
\[N = \lceil (z_{1-\alpha/2} + z_{\text{power}})^2 \; / \; (2\varepsilon^2) \rceil\]- Parameters:
epsilon (
float) – Minimum detectable total variation distance (TVD). For example,0.01means the test can reliably catch a 1 % deviation from the expected distribution.alpha (
float) – Significance level (default0.05→ 95 % confidence).power (
float) – Statistical power, i.e. probability of detecting a real error (default0.80→ 80 % power).
- Return type:
- 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
- pytest_quantum.stats.shots.recommended_shots(expected_probs, min_expected_per_bucket=5)[source]¶
Recommend shots so every bucket gets enough expected counts for chi-square.
The chi-square goodness-of-fit test requires each cell to have an expected count of at least 5 (a common rule of thumb) to be statistically valid. This function returns the shot count that satisfies that requirement for the rarest outcome in expected_probs.
- Parameters:
- Return type:
- Returns:
Recommended shot count as an integer.
- Raises:
ValueError – If expected_probs is empty or all probabilities are zero.
Example:
>>> recommended_shots({"00": 0.499, "01": 0.001, "11": 0.5}) 5000 >>> recommended_shots({"0": 0.5, "1": 0.5}) 10
Statistical tests¶
Statistical test primitives for quantum output validation.
All functions are pure numpy/scipy — no quantum SDK required.
- pytest_quantum.stats.tests.fidelity(psi, phi)[source]¶
Pure-state fidelity \(|\langle\psi|\phi\rangle|^2\).
Both arrays are flattened and normalised before computation, so minor normalisation errors from simulators do not affect the result.
- Parameters:
- Return type:
- Returns:
Float in
[0.0, 1.0].1.0means the states are identical (up to global phase).0.0means orthogonal.- Raises:
ValueError – If psi and phi have different sizes.
Example:
>>> import numpy as np >>> zero = np.array([1, 0], dtype=complex) >>> plus = np.array([1, 1], dtype=complex) / np.sqrt(2) >>> fidelity(zero, plus) 0.5
- pytest_quantum.stats.tests.tvd(p, q)[source]¶
Total Variation Distance between two probability distributions.
\[\text{TVD}(p, q) = \frac{1}{2} \sum_x |p(x) - q(x)|\]- Parameters:
- Return type:
- Returns:
Float in
[0.0, 1.0].0.0means identical distributions;1.0means disjoint support.
Example:
>>> import numpy as np >>> tvd(np.array([0.5, 0.5]), np.array([0.6, 0.4])) 0.1
- pytest_quantum.stats.tests.tvd_from_counts(counts_a, counts_b)[source]¶
Compute TVD between two shot-count dictionaries.
Normalises each dict to a probability distribution before computing TVD.
- pytest_quantum.stats.tests.chi_square_test(observed, expected_probs, total_shots=None)[source]¶
Chi-square goodness-of-fit test for quantum measurement distributions.
Tests whether observed counts are consistent with expected_probs.
- Parameters:
observed (
dict[str,int] |ndarray[tuple[Any,...],dtype[double]]) – Either a count dict{"00": 489, "11": 511}or a 1-D numpy array of observed counts.expected_probs (
dict[str,float] |ndarray[tuple[Any,...],dtype[double]]) – Either a probability dict{"00": 0.5, "11": 0.5}(must sum to 1) or a 1-D numpy array of expected probabilities. When using numpy arrays, provide total_shots so expected counts can be computed.total_shots (
int|None) – Required when both inputs are numpy arrays. Ignored when dict inputs are used (total is inferred from observed).
- Return type:
- Returns:
(statistic, pvalue)— the chi-square statistic and the p-value. Reject the null hypothesis (distributions match) whenpvalue < significance.- Raises:
ValueError – If inputs are inconsistent (different keys, missing total_shots for array inputs, etc.).
Example:
>>> stat, p = chi_square_test({"00": 495, "11": 505}, {"00": 0.5, "11": 0.5}) >>> p > 0.05 # consistent with Bell state True