Assertions Reference¶
pytest-quantum gives you 80+ assertion functions that understand quantum semantics — statistical noise, global phase, density matrices, hardware constraints, and more.
from pytest_quantum import assert_unitary, assert_measurement_distribution # and 78 more
Every assertion raises AssertionError with a detailed human-readable message when it
fails, so you know exactly what went wrong in CI.
Quick decision guide¶
I want to test… |
Use this |
|---|---|
Gate implements a specific unitary |
|
Two circuits do the same thing |
|
Same circuit, different frameworks |
|
Output state fidelity |
|
Measurement distribution (shot-noise-safe) |
|
Noisy density matrix |
|
Quantum channel is physically valid |
|
VQE / QAOA converged |
|
Circuit didn’t regress |
|
Circuit uses hardware-native gates only |
|
No mid-circuit measurements (hardware constraint) |
|
Backend meets quantum volume spec |
|
Gate error from RB experiment |
|
T1 / T2 / T2* coherence times |
|
XEB fidelity (Google-style benchmark) |
|
VQA circuit is expressive enough |
|
No barren plateaus in VQA training |
|
Error mitigation improves result |
|
Logical error rate of QEC code |
|
Unitary & Circuit Equivalence¶
The most rigorous checks: compare circuits at the matrix level.
assert_unitary¶
Assert a circuit implements an expected unitary matrix. Global-phase safe. Works with Qiskit, Cirq, Amazon Braket, PennyLane, Pytket.
from pytest_quantum import assert_unitary
import numpy as np
def test_hadamard():
from qiskit import QuantumCircuit
qc = QuantumCircuit(1)
qc.h(0)
H = np.array([[1, 1], [1, -1]]) / np.sqrt(2)
assert_unitary(qc, H) # e^(iθ)·H also passes ✓
assert_unitary(qc, H, atol=1e-6) # tighter tolerance
assert_unitary(qc, H, allow_global_phase=False) # strict phase matching
assert_circuits_equivalent¶
Assert two circuits implement the same unitary, up to global phase. Cross-framework: compare a Qiskit circuit against a Cirq circuit.
def test_same_gate_two_ways():
from qiskit import QuantumCircuit
import cirq
qc = QuantumCircuit(1); qc.h(0); qc.t(0)
q = cirq.LineQubit.range(1)
cc = cirq.Circuit(cirq.H(q[0]), cirq.T(q[0]))
assert_circuits_equivalent(qc, cc)
assert_transpilation_preserves_semantics¶
Assert a transpiled/compiled circuit is semantically equivalent to the original.
from qiskit import transpile
from qiskit_aer import AerSimulator
def test_transpile_preserves():
compiled = transpile(my_circuit, AerSimulator(), optimization_level=3)
assert_transpilation_preserves_semantics(my_circuit, compiled)
assert_cross_platform_equivalent¶
Assert two circuits from different frameworks implement the same unitary. Handles qubit-endianness differences between Qiskit (little-endian) and Cirq/Pytket (big-endian) automatically.
assert_cross_platform_equivalent(qiskit_circuit, cirq_circuit)
assert_qiskit_cirq_equivalent(qiskit_circuit, cirq_circuit)
assert_qiskit_pytket_equivalent(qiskit_circuit, pytket_circuit)
State Assertions¶
assert_state_fidelity_above¶
Assert |⟨actual|target⟩|² ≥ threshold. Accepts numpy arrays or statevectors.
from pytest_quantum import assert_state_fidelity_above
def test_rx_pi():
import numpy as np
sv = run_rx_pi_circuit() # should be close to -i|1⟩
assert_state_fidelity_above(sv, np.array([0, -1j]), threshold=0.999)
assert_states_close¶
Elementwise comparison up to global phase and tolerance.
assert_states_close(actual, target, atol=1e-6)
assert_states_close(actual, target, atol=1e-4, allow_global_phase=False)
assert_normalized¶
Assert a statevector has unit norm ‖ψ‖₂ = 1.
assert_normalized(statevector)
assert_normalized(statevector, atol=1e-8)
Measurement Distributions¶
assert_measurement_distribution ⭐¶
The workhorse of quantum testing. Uses a chi-square goodness-of-fit test so it never fails due to shot noise, only when the distribution is genuinely wrong.
from pytest_quantum import assert_measurement_distribution
def test_bell_state(aer_simulator):
counts = run_bell_circuit(aer_simulator, shots=2000)
assert_measurement_distribution(
counts,
expected_probs={"00": 0.5, "11": 0.5},
significance=0.01, # p-value threshold (default 0.01)
)
assert_counts_close¶
Assert two count dicts are close using Total Variation Distance (TVD).
assert_counts_close(counts_a, counts_b, max_tvd=0.05)
Density Matrix Assertions¶
For noisy simulation and open quantum systems.
from pytest_quantum import (
assert_density_matrix_close,
assert_trace_distance_below,
assert_purity_above,
assert_partial_trace_close,
)
def test_noisy_channel():
rho = run_noisy_circuit() # density matrix from noise model
assert_density_matrix_close(rho, ideal_rho, atol=0.01)
assert_trace_distance_below(rho, ideal_rho, max_distance=0.05)
assert_purity_above(rho, min_purity=0.90) # not too noisy
def test_bell_entanglement():
rho_AB = get_bell_density_matrix()
rho_A_expected = np.eye(2) / 2 # maximally mixed subsystem
assert_partial_trace_close(rho_AB, keep_qubits=[0], expected=rho_A_expected)
Quantum Channel Assertions¶
from pytest_quantum import (
assert_hermitian, assert_positive_semidefinite, assert_commutes_with,
assert_channel_is_cptp, assert_process_fidelity_above, assert_noise_fidelity_above,
)
def test_custom_channel(kraus_ops):
assert_channel_is_cptp(kraus_ops) # completely positive + trace preserving
assert_process_fidelity_above(kraus_ops, ideal_channel, threshold=0.99)
def test_hamiltonian(H):
assert_hermitian(H)
assert_positive_semidefinite(H + np.eye(H.shape[0]) * np.abs(np.min(np.linalg.eigvalsh(H))))
def test_pauli_commutation():
X = np.array([[0,1],[1,0]])
Y = np.array([[0,-1j],[1j,0]])
# X and Y anti-commute — this should raise AssertionError:
# assert_commutes_with(X, Y)
Noise Channel Assertions¶
Verify noise model Kraus operators match physical channels.
from pytest_quantum import (
assert_depolarizing_channel,
assert_amplitude_damping_channel,
assert_dephasing_channel,
assert_no_leakage,
assert_channel_preserves_trace,
assert_channel_diamond_norm_below,
)
def test_noise_model(noise_kraus_ops):
assert_channel_preserves_trace(noise_kraus_ops) # always a sanity check
assert_depolarizing_channel(noise_kraus_ops, error_rate=0.01, atol=1e-4)
assert_no_leakage(noise_kraus_ops, computational_dim=2)
def test_channels_are_close(kraus_a, kraus_b):
# Diamond norm: operational distance between two quantum channels
assert_channel_diamond_norm_below(kraus_a, kraus_b, max_norm=0.05)
Observable & Expectation Value¶
from pytest_quantum import (
assert_expectation_value_close,
assert_ground_state_energy_close,
assert_vqe_converges,
assert_cost_decreases,
)
def test_z_expectation(qiskit_estimator):
from qiskit.circuit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
qc = QuantumCircuit(1) # |0⟩ → ⟨Z⟩ = 1.0
result = qiskit_estimator.run([(qc, SparsePauliOp("Z"))]).result()
assert_estimator_close(result, expected=1.0, atol=0.01)
def test_h2_ground_state():
energy = run_vqe_h2()
assert_ground_state_energy_close(energy, expected_energy=-1.137, atol=0.05)
def test_vqe_converges():
assert_vqe_converges(vqe_fn, h2_hamiltonian, target_energy=-1.137, atol=0.05)
assert_cost_decreases(cost_fn, initial_params)
Circuit Structure¶
Static checks on gate counts, depth, and composition. No simulation needed.
from pytest_quantum import (
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,
)
def test_compiled_circuit(backend):
from qiskit import transpile
qc = QuantumCircuit(3); qc.h(0); qc.cx(0,1); qc.cx(1,2)
compiled = transpile(qc, backend, basis_gates=["cx","u3"])
assert_circuit_depth(compiled, max_depth=6)
assert_circuit_width(compiled, expected_qubits=3)
assert_gate_count(compiled, "cx", expected=2)
assert_gates_in_basis_set(compiled, {"cx", "u3"})
assert_circuit_is_clifford(qc) # original is Clifford
def test_no_mid_circuit():
# Fail fast before submitting to hardware that doesn't support reset
assert_no_mid_circuit_measurement(circuit)
Transpilation & Compilation¶
from pytest_quantum import (
assert_transpilation_equivalent,
assert_transpilation_depth_below,
assert_gate_count_after_transpilation,
)
def test_transpilation(aer_simulator):
assert_transpilation_equivalent(my_circuit, aer_simulator)
assert_transpilation_depth_below(my_circuit, aer_simulator, max_depth=20)
assert_gate_count_after_transpilation(my_circuit, aer_simulator, "cx", expected=2)
Sweeps / Parametrised Circuits¶
from pytest_quantum import (
assert_circuit_sweep, assert_circuit_sweep_states,
assert_parametrized_unitary_continuous,
)
def test_ry_sweep(aer_simulator):
from qiskit.circuit import Parameter, QuantumCircuit
theta = Parameter("θ")
qc = QuantumCircuit(1); qc.ry(theta, 0); qc.measure_all()
assert_circuit_sweep(
qc,
param_values=[{"θ": 0}, {"θ": np.pi}, {"θ": np.pi/2}],
expected_probs_list=[{"0": 1.0}, {"1": 1.0}, {"0": 0.5, "1": 0.5}],
backend=aer_simulator,
)
assert_parametrized_unitary_continuous(qc, param_range=(0, 2*np.pi))
Entanglement¶
from pytest_quantum import (
assert_entanglement_entropy_below,
assert_bloch_sphere_close,
assert_schmidt_rank_at_most,
)
def test_bell_entanglement():
sv = get_bell_statevector()
# Bell state has maximum entanglement entropy = 1 ebit
assert_entanglement_entropy_below(sv, partition=[0], max_entropy=1.01)
assert_schmidt_rank_at_most(sv, partition=[0], max_rank=2)
def test_single_qubit_bloch():
sv = get_plus_state() # |+⟩ = (|0⟩+|1⟩)/√2
# θ=π/2, φ=0 on Bloch sphere
assert_bloch_sphere_close(sv, theta=np.pi/2, phi=0.0, atol=0.01)
Information Theory¶
from pytest_quantum import assert_hellinger_close, assert_kl_divergence_below, assert_cross_entropy_below
# Hellinger distance ∈ [0,1]: 0=identical, 1=disjoint
assert_hellinger_close(counts_a, counts_b, max_distance=0.1)
# KL divergence (asymmetric)
assert_kl_divergence_below(counts, expected_probs, max_kl=0.1)
# Cross-entropy
assert_cross_entropy_below(counts, expected_probs, max_ce=1.0)
Snapshots / Golden Files¶
Regression testing for circuits: run once to create the golden file, then future runs compare against it.
from pytest_quantum import assert_unitary_snapshot, assert_distribution_snapshot
def test_no_regression():
# First run: writes .snapshots/my_circuit.npy
# Subsequent runs: compares against it
assert_unitary_snapshot(qc, "my_circuit")
def test_distribution_no_regression(aer_simulator):
counts = run_circuit(aer_simulator, shots=2000)
assert_distribution_snapshot(counts, "my_dist_snapshot", max_tvd=0.05)
Regenerate golden files: pytest --quantum-update-snapshots
QASM Round-Trips¶
from pytest_quantum import assert_qasm_roundtrip, assert_qasm2_roundtrip
def test_qasm3_roundtrip(qc):
assert_qasm_roundtrip(qc) # OpenQASM 3 (Qiskit) or Cirq JSON
def test_qasm2_roundtrip(qc):
assert_qasm2_roundtrip(qc) # OpenQASM 2.0 (Qiskit only)
QEC / Stim¶
from pytest_quantum import (
assert_stim_logical_error_rate_below,
assert_stim_detector_error_rate_below,
assert_stabilizer_state,
)
def test_surface_code(stim_sampler):
import stim
code = stim.Circuit.generated("surface_code:rotated_memory_z", distance=3, rounds=3)
assert_stim_logical_error_rate_below(code, max_error_rate=0.01, shots=100_000)
def test_bell_stabilizer():
sv = get_bell_statevector()
assert_stabilizer_state(sv, ["XX", "ZZ"]) # Bell state stabilizers
Benchmarking (v1.0.0)¶
Quantum Volume¶
def test_qv16(aer_simulator):
qv = assert_quantum_volume(aer_simulator, target_qv=16, num_trials=100)
print(f"Measured QV: {qv}") # 16
Randomized Benchmarking¶
def test_1q_rb(aer_simulator):
result = assert_randomized_benchmarking(
aer_simulator, qubit=0,
clifford_lengths=[1, 10, 20, 50, 100],
num_sequences=20,
min_fidelity_per_clifford=0.999,
)
print(f"Average gate fidelity: {result['fidelity']:.5f}")
T1 / T2 / T2* Characterisation¶
@pytest.mark.quantum_real
def test_coherence_times(ibm_backend):
t1 = assert_t1_above(ibm_backend, qubit=0, target_t1_us=50.0)
t2 = assert_t2_above(ibm_backend, qubit=0, target_t2_us=30.0) # Hahn echo
t2s = assert_t2star_above(ibm_backend, qubit=0, target_t2star_us=20.0) # Ramsey
print(f"T1={t1:.1f}µs T2={t2:.1f}µs T2*={t2s:.1f}µs")
Interleaved RB (single gate fidelity)¶
def test_cx_gate_fidelity(aer_simulator):
from qiskit import QuantumCircuit
x_gate = QuantumCircuit(1); x_gate.x(0)
result = assert_interleaved_rb(
aer_simulator, qubit=0,
gate_name="X", gate_circuit=x_gate,
min_gate_fidelity=0.999,
)
print(f"X gate fidelity: {result['fidelity']:.5f}")
Quantum ML (v1.0.0)¶
Cross-Entropy Benchmarking (XEB)¶
def test_xeb(aer_simulator):
fidelity = assert_xeb_fidelity_above(
aer_simulator, num_qubits=4, target_fidelity=0.9,
num_circuits=50, depth=20,
)
print(f"XEB fidelity: {fidelity:.3f}")
Expressibility¶
Is your variational ansatz expressive enough to represent the solution?
def test_ansatz_expressibility():
def my_ansatz(params):
qc = QuantumCircuit(2)
qc.ry(params[0], 0); qc.ry(params[1], 1); qc.cx(0, 1)
return qc
score = assert_expressibility_above(
my_ansatz, num_qubits=2, num_params=2, target_expressibility=0.3,
)
print(f"Expressibility score: {score:.3f} (1.0 = Haar-random)")
Entanglement Capability¶
def test_ansatz_entanglement():
q = assert_entanglement_capability_above(
my_ansatz, num_qubits=2, num_params=4, target_capability=0.3,
)
print(f"Meyer-Wallach Q: {q:.3f} (1.0 = maximally entangled)")
Barren Plateau Detection¶
def test_no_barren_plateau():
var = assert_no_barren_plateau(
deep_ansatz, num_qubits=8, num_params=64,
min_gradient_variance=1e-4,
)
print(f"Gradient variance: {var:.2e} (higher = trainable)")
Hardware Assertions (v1.0.0)¶
@pytest.mark.quantum_real
def test_backend_ready(ibm_backend):
assert_backend_calibration(ibm_backend, min_t1_us=30.0, min_cx_fidelity=0.99)
assert_circuit_fits_backend(my_circuit, ibm_backend) # qubit count + connectivity
assert_mirror_fidelity(ibm_backend, qubit=0, target_fidelity=0.95)
Error Mitigation (Mitiq) (v1.0.0)¶
from pytest_quantum import (
assert_zne_expectation_close, assert_zne_reduces_error,
assert_cdr_reduces_error, assert_pec_reduces_error,
assert_pec_expectation_close, assert_error_mitigation_benchmark,
)
def test_zne_improves_result():
assert_zne_reduces_error(
circuit, observable, noisy_val=0.85, ideal_val=1.0,
)
assert_zne_expectation_close(
circuit, observable, expected=1.0, atol=0.05,
)
def test_all_methods():
assert_error_mitigation_benchmark(
circuit, observable, methods=["zne", "cdr", "pec"]
)
Primitives (Qiskit 1.0+)¶
from pytest_quantum import assert_sampler_distribution, assert_estimator_close
def test_sampler(qiskit_sampler):
result = qiskit_sampler.run([(bell_circuit,)]).result()
assert_sampler_distribution(result, {"00": 0.5, "11": 0.5})
def test_estimator(qiskit_estimator):
result = qiskit_estimator.run([(ground_state, SparsePauliOp("Z"))]).result()
assert_estimator_close(result, expected=1.0, atol=0.01)