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

assert_unitary

Two circuits do the same thing

assert_circuits_equivalent

Same circuit, different frameworks

assert_cross_platform_equivalent

Output state fidelity

assert_state_fidelity_above

Measurement distribution (shot-noise-safe)

assert_measurement_distribution

Noisy density matrix

assert_density_matrix_close

Quantum channel is physically valid

assert_channel_is_cptp

VQE / QAOA converged

assert_vqe_converges

Circuit didn’t regress

assert_unitary_snapshot

Circuit uses hardware-native gates only

assert_gates_in_basis_set

No mid-circuit measurements (hardware constraint)

assert_no_mid_circuit_measurement

Backend meets quantum volume spec

assert_quantum_volume

Gate error from RB experiment

assert_randomized_benchmarking

T1 / T2 / T2* coherence times

assert_t1_above / assert_t2_above / assert_t2star_above

XEB fidelity (Google-style benchmark)

assert_xeb_fidelity_above

VQA circuit is expressive enough

assert_expressibility_above

No barren plateaus in VQA training

assert_no_barren_plateau

Error mitigation improves result

assert_zne_reduces_error

Logical error rate of QEC code

assert_stim_logical_error_rate_below


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)