Skip to main content

Statistical, pytest-native testing for quantum circuits.

Project description

qtest

Statistical, pytest-native testing for quantum circuits.

CI PyPI version Python versions License: MIT codecov Documentation Status

qtest is an open-source Python library that brings the discipline of modern software testing to quantum programs. It plugs straight into pytest, gives you statistical assertions designed for noisy and probabilistic outputs, and integrates with Hypothesis so you can do property-based testing on quantum circuits — without writing a single line of plumbing.


Why qtest?

Testing quantum software is hard for reasons classical testing tools were never designed to handle:

  • Outputs are distributions, not values. A measurement gives you counts that are statistically close to the truth, never exact. assertEqual is the wrong tool.
  • State and unitary comparisons are global-phase-insensitive. Two correct implementations of the same gate can differ by a phase that doesn't matter physically — but does matter to numpy.allclose.
  • Shot noise and seed drift make tests flaky. Without a principled tolerance and a controlled seed, your CI lights up red at random.
  • Property-based testing fits quantum perfectly — laws like unitarity, reversibility, and Bell-state symmetry are universal — but the standard Hypothesis strategies don't know about QuantumCircuit.

qtest solves these directly. It gives you tolerance-aware statistical assertions, a Hypothesis strategy set tuned for circuits, gates, and states, and a pytest plugin that exposes --qtest-shots, --qtest-tolerance, and --qtest-seed as first-class CLI flags.


Quickstart

Install:

pip install qtest-quantum

Write your first quantum test — a Bell state should produce a 50/50 distribution over 00 and 11:

from qiskit import QuantumCircuit
from qtest import assert_distribution_close

def test_bell_state_is_balanced():
    qc = QuantumCircuit(2)
    qc.h(0)
    qc.cx(0, 1)
    qc.measure_all()

    assert_distribution_close(
        qc,
        expected={"00": 0.5, "11": 0.5},
        shots=4096,
        tolerance=0.03,
    )

Run it:

pytest test_bell.py -v

Expected output:

test_bell.py::test_bell_state_is_balanced PASSED                         [100%]

============================== 1 passed in 0.42s ===============================

That's it. No mocking, no manual shot-count math, no hand-rolled tolerance checks.


Features

  • Statistical assertions built for quantum. assert_distribution_close, assert_state_close, assert_unitary, and assert_circuit_equivalent — each one understands shot noise, fidelity, trace distance, and global phase.
  • Resource/cost assertions. assert_max_depth, assert_max_gate_count, assert_max_two_qubit_count, and assert_max_t_count lock in the gains of an optimisation or transpiler pass and fail the build when depth, two-qubit-gate count, or T-count regress — pure static analysis, no simulation.
  • Noise-aware testing. Inject realistic error channels (depolarizing, bit/phase flip, T1/T2 relaxation, readout) into any assertion with noise_model=, or sweep increasing noise with assert_robust_to_noise to guard error-mitigation regressions.
  • Native pytest plugin. Drop qtest into any existing test suite. CLI flags --qtest-shots, --qtest-tolerance, and --qtest-seed let you tune every run without touching code.
  • Property-based testing for circuits. Ready-made Hypothesis strategies for random circuits, gates, state vectors, and Haar-random unitaries.
  • Backend-agnostic by design. Ships with Qiskit (default), Cirq, and PennyLane backends behind one Backend protocol — switch per call or globally with --qtest-backend. Install extras with qtest-quantum[cirq] / qtest-quantum[pennylane].
  • OpenQASM interop. load_qasm / load_qasm_file parse OpenQASM 2.0/3.0 into circuits you can assert on directly (OpenQASM 3.0 via qtest-quantum[qasm3]).
  • Snapshot (golden-file) testing. Lock in a circuit's distribution with the qtest_snapshot fixture; refresh deliberately with --qtest-snapshot-update.
  • Visualisation & reporting. qtest.viz plots expected-vs-measured distributions (qtest-quantum[viz]), and --qtest-summary reports measured-distance statistics across the run.
  • Deterministic by default. A controlled seed pipeline means a test that passes locally passes on CI.
  • Zero ceremony fixtures. bell_state, plus_state, minus_state, ghz_3 / ghz_4 / ghz_5, hadamard_circuit, and random_clifford come included.

More examples

State-vector assertions with global-phase tolerance

import numpy as np
from qiskit import QuantumCircuit
from qtest import assert_state_close

def test_hadamard_produces_plus_state():
    qc = QuantumCircuit(1)
    qc.h(0)

    expected = np.array([1, 1]) / np.sqrt(2)
    assert_state_close(qc, expected_state=expected, tolerance=1e-9)

    # qtest also ships a named state library, so the line below is
    # equivalent — global phase is ignored by default.
    assert_state_close(qc, expected_state="plus", tolerance=1e-9)

Property-based testing with Hypothesis

from hypothesis import given, settings
from qtest import assert_unitary
from qtest.strategies import quantum_circuits

@given(circuit=quantum_circuits(min_qubits=2, max_qubits=4, max_depth=10))
@settings(deadline=None, max_examples=50)
def test_any_circuit_is_unitary(circuit):
    assert_unitary(circuit, tolerance=1e-9)

Custom tolerance per test

from qtest import assert_circuit_equivalent

def test_optimizer_preserves_semantics(original_circuit, optimized_circuit):
    assert_circuit_equivalent(
        original_circuit,
        optimized_circuit,
        tolerance=1e-7,
    )

assert_circuit_equivalent compares the two circuits' unitaries via process fidelity, which is global-phase-insensitive by default — no extra flag needed.

Guarding circuit cost across a transpiler pass

from qiskit import transpile
from qtest import assert_circuit_equivalent, assert_max_two_qubit_count

def test_transpilation_is_correct_and_cheaper(original_circuit):
    optimized = transpile(original_circuit, optimization_level=3)

    # 1. The optimiser must not change what the circuit computes.
    assert_circuit_equivalent(original_circuit, optimized, tolerance=1e-9)

    # 2. ...and it must not regress the expensive two-qubit-gate count.
    assert_max_two_qubit_count(optimized, max_count=12)

You can also set tolerance globally for an entire run:

pytest --qtest-shots=8192 --qtest-tolerance=0.01 --qtest-seed=42

Installation

Standard install from PyPI:

pip install qtest-quantum

With Hypothesis (property-based testing) and Aer (high-performance simulator):

pip install "qtest-quantum[hypothesis,aer]"

Or pull in every optional dependency in one go:

pip install "qtest-quantum[all]"

For development — clone the repo and install in editable mode with the dev extras:

git clone https://github.com/metin-5115/qtest.git
cd qtest
python -m venv .venv
source .venv/bin/activate           # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pre-commit install
pytest

Supported Python versions: 3.9, 3.10, 3.11, 3.12.


Documentation

Full documentation, API reference, and guides live at qtest-quantum.readthedocs.io.

Highlights worth bookmarking:


Contributing

Contributions are welcome and appreciated — bug reports, feature requests, documentation fixes, and pull requests of any size. Start with CONTRIBUTING.md for the workflow, coding conventions, and how to run the test suite locally. By participating you agree to abide by our Code of Conduct.

If you'd like to discuss a larger change before opening a PR, open a GitHub discussion or an issue first.


License

qtest is released under the MIT License. You are free to use it in commercial and non-commercial projects — attribution is appreciated.

Citation

If qtest supports your research, please cite it:

@software{qtest,
  author  = {Tuncbilek, Metin},
  title   = {qtest: Statistical, pytest-native testing for quantum circuits},
  year    = {2026},
  url     = {https://github.com/metin-5115/qtest}
}

Acknowledgments

qtest stands on the shoulders of remarkable open-source work:

  • The Qiskit team at IBM Quantum, whose simulators, circuit model, and quantum-information utilities make this library possible.
  • The pytest maintainers, whose plugin architecture is so clean that bolting on a quantum testing layer felt natural.
  • The Hypothesis team, who proved that property-based testing belongs in every serious Python project — including quantum ones.

Thank you to everyone who files an issue, opens a PR, or simply tries qtest on their own circuits. Quantum software deserves the same testing rigor as the classical software we ship every day, and you are helping make that happen.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qtest_quantum-0.1.0.tar.gz (133.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qtest_quantum-0.1.0-py3-none-any.whl (92.9 kB view details)

Uploaded Python 3

File details

Details for the file qtest_quantum-0.1.0.tar.gz.

File metadata

  • Download URL: qtest_quantum-0.1.0.tar.gz
  • Upload date:
  • Size: 133.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qtest_quantum-0.1.0.tar.gz
Algorithm Hash digest
SHA256 457ca5e5a13d23831e0cb09f76b0ea8bbe5af7d3e3e3e7e5cc3d102b2143269d
MD5 3441efd446566aa11aeb36f4634d923a
BLAKE2b-256 027a56d2cc8bc159d96e88b1f507e9746fdcdfcef8ffacdc557a616272f8ce70

See more details on using hashes here.

Provenance

The following attestation bundles were made for qtest_quantum-0.1.0.tar.gz:

Publisher: release.yml on metin-5115/qtest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qtest_quantum-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: qtest_quantum-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 92.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for qtest_quantum-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1bd88a5109cb3495bdf37714a3ddb6ff81a9b796c94fcd8f993f0a1221684019
MD5 d8f99aa40791eb8ac386de29b004be3b
BLAKE2b-256 687a66661ebea5f2fcda82948bd08a505240ee1bbe9578ef144a9d407cb2a9a8

See more details on using hashes here.

Provenance

The following attestation bundles were made for qtest_quantum-0.1.0-py3-none-any.whl:

Publisher: release.yml on metin-5115/qtest

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page