Skip to main content

qcoherence

A native pytest plugin for testing and verifying quantum circuits — across Qiskit, Cirq, and PennyLane (via OpenQASM), on simulators and real IBM Quantum hardware.

Why this exists

Only 31% of quantum developers use any quantum-specific testing tooling — most still debug with print statements and circuit visualizations, because observing a quantum state collapses it, so classical debugging habits don't transfer. Several academic tools have tried to close this gap (Muskit, QuCAT, Quito, MorphQ, QuCheck, QUTest), but each requires writing tests as custom classes inheriting from a project-specific runner, each is tied to Qiskit only, and none validates against real hardware.

qcoherence is different on all three counts:

  1. Native pytest. Install it, write def test_x():, run pytest. No base class, no custom runner.
  2. Cross-framework. Accepts Qiskit QuantumCircuits, OpenQASM 2/3 strings/files, Cirq circuits, and PennyLane circuits (via QASM export).
  3. Real hardware, opt-in. Re-run your golden tests on actual IBM Quantum devices with --qc-hardware, with noise-realistic tolerances.

Install

pip install qcoherence            # core (Qiskit + Aer)
pip install qcoherence[cirq]      # + Cirq ingestion
pip install qcoherence[pennylane] # + PennyLane examples
pip install qcoherence[hardware]  # + IBM Quantum real-device validation
pip install qcoherence[all]       # everything

Quickstart

import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
from qcoherence import assert_state_equal, assert_entangled, run_statevector

def bell_circuit():
    qc = QuantumCircuit(2)
    qc.h(0)
    qc.cx(0, 1)
    return qc

def test_bell_state():
    expected = Statevector(np.array([1, 0, 0, 1]) / np.sqrt(2))
    actual = run_statevector(bell_circuit())
    assert_state_equal(actual, expected)
    assert_entangled(actual, qubits=[0])

Run it like any other test suite: pytest.

From Cirq or PennyLane

from qcoherence import to_qiskit, run_statevector

# Cirq: pass the circuit object directly
qc = to_qiskit(my_cirq_circuit)

# PennyLane: export to OpenQASM, pass the string
qasm = qml.workflow.construct_tape(my_qnode)().to_openqasm(measure_all=False)
qc = to_qiskit(qasm)

What's proven, not just claimed

tests/test_bug_demo.py demonstrates the core value proposition: a Bell-state circuit missing its entangling gate is a bug that looks completely fine under naive inspection (it's a valid, normalized quantum state) but is caught immediately by assert_entangled and assert_state_equal:

pytest tests/test_bug_demo.py -v

Assertion API

Function Checks
assert_state_equal(actual, expected, tol=1e-6) State fidelity ≈ 1 (equal up to global phase)
assert_distribution_close(counts, probs, tol=0.05) Total variation distance, simple tolerance
assert_distribution_fits(counts, probs, alpha=0.01) Chi-square goodness-of-fit with a real p-value
assert_entangled(state, qubits) Reduced-state purity < 1
assert_separable(state, qubits) Reduced-state purity ≈ 1
holm_bonferroni(pvalues, alpha) Family-wise error control across a suite of statistical tests

pytest integration

  • pytest -m quantum_test — run only qcoherence-marked tests.
  • pytest --qc-hardware — also run @pytest.mark.hardware tests on real IBM Quantum devices (requires a free IBM Quantum account; skipped by default so CI stays green).
  • --qc-shots N / --qc-seed N — shot count and reproducibility seed.

Real-hardware validation

import pytest
from qcoherence.hardware import run_counts_on_hardware, assert_hardware_distribution_close

@pytest.mark.hardware
def test_bell_on_real_device():
    counts = run_counts_on_hardware(bell_circuit(), shots=4096)
    assert_hardware_distribution_close(counts, {"00": 0.5, "11": 0.5})  # tol=0.15, NISQ-realistic

Extending qcoherence

Register custom assertions/backends programmatically or from a separate pip package via entry points — see qcoherence/registry.py:

from qcoherence.registry import register_assertion

@register_assertion("assert_w_state_like")
def assert_w_state_like(state, *, tol=1e-6):
    ...
# in another package's pyproject.toml
[project.entry-points."qcoherence.assertions"]
assert_my_thing = "mypkg.assertions:assert_my_thing"

Golden benchmarks

tests/golden/ contains verified reference circuits used as both examples and a regression suite: Bell state, GHZ state (3-qubit entanglement), and a VQE ground-state search checked against exact numpy diagonalization computed in-test.

Prior art

This project builds on ideas published in the quantum software testing research literature, particularly Muskit (mutation testing), QuCAT (combinatorial testing), Quito (coverage-guided generation), MorphQ (metamorphic testing), QuCheck (property-based testing and the statistical multiple-testing insight), and QUTest (native assertion testing). Its contribution is a native pytest integration layer with cross-framework ingestion and opt-in hardware validation, not a new testing paradigm.

License

MIT

Metadata

Release files for qcoherence 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qcoherence 1.0.0
File Size Uploaded
qcoherence-1.0.0.tar.gz 18.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qcoherence 1.0.0
File Interpreter ABI Platform
qcoherence-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.6 kB

Release files / qcoherence-1.0.0.tar.gz

Download URL qcoherence-1.0.0.tar.gz
Size 18.3 kB
Tags Source
SHA-256 checksum
How to use checksums
429dfaa85a023e59551f72938514aa634fe79911d07a37f3d485e62327d6cc9c
BLAKE2b-256 checksum
How to use checksums
9a0c31951e366e10d595c6da7de5a48e146c3677b44a672e8f47773223070706
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release files / qcoherence-1.0.0-py3-none-any.whl

Download URL qcoherence-1.0.0-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c5ffe9b8cafec8f7422933decaf74ddb4aa9f2dc83479fd9f660f98ec07c238a
BLAKE2b-256 checksum
How to use checksums
97658c162b01f6594b1a8fa7e64e8f41d03a591ea7fdfb046f3671eb92a05a3e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page