Verify LLM-generated quantum code (Qiskit / OpenQASM) statically and safely.
Project description
qcheck
AI writes quantum code. qcheck reviews it.
qcheck is a lightweight review layer for AI-generated Qiskit, OpenQASM and
PennyLane snippets. It catches common issues early - removed-in-1.0 APIs, unsafe patterns,
missing measurements, parse errors - so agents and developers can improve quantum
code before it reaches humans, CI, or simulators. Tiny, dependency-free, and it
reviews code without ever executing it.
Why it matters: LLMs write quantum code that fails to run 40-70% of the time one-shot (QuanBench+ 2026: Qiskit 59.5% / PennyLane 42.9% pass; QCoder 2026: ~70% one-shot failure). qcheck catches the avoidable share of that early.
qcheck verify circuit.qasm
qcheck verify snippet.py --json
Why this exists
An LLM agent (or a developer pasting from a chat assistant) produces a Qiskit/QASM snippet.
Will it run? Is it using an API that was removed in Qiskit 1.0? Does it measure?
Is it even safe to touch? Today you find out by running it - wasting time, and
in an agent loop, running untrusted model output. qcheck answers in
milliseconds, statically.
Install
pip install qcheck-quantum
The PyPI distribution name is qcheck-quantum (the bare qcheck name is
taken on PyPI); the installed command and the import package are both
qcheck. v0 has zero runtime dependencies (standard library only).
To work on qcheck itself, install from source:
git clone https://github.com/JCQuankey/qcheck && cd qcheck
pip install -e ".[dev]" # editable install + pytest
Release process: docs/RELEASING.md.
Quickstart
qcheck verify examples/broken_qiskit_execute.py # one file
qcheck verify examples/ # a whole directory (recursive)
qcheck verify a.py b.qasm circuits/ # several paths at once
cat snippet.py | qcheck verify - # stdin (for agents)
Example output:
qcheck 0.8.0 [FAIL] examples/broken_qiskit_execute.py (qiskit)
[error] QISKIT-REMOVED-IMPORT: 'from qiskit import execute' was removed in Qiskit 1.0
[warning] QISKIT-DEPRECATED-GATE: QuantumCircuit.cnot() is deprecated; use .cx().
fix -> Replace execute() with a primitive (Sampler/Estimator) or backend.run().
Reviewing multiple files prints a per-file summary and exits with the worst
result found (unsafe > failed > passed). Directory recursion skips virtualenvs,
VCS, caches, and build output (.venv, node_modules, .git, site-packages,
build, dist, ...) so it reviews your code, not your dependencies. To review a
file inside one of those, pass it explicitly.
JSON output (for agents & CI)
qcheck verify snippet.py --json
For a single file, returns {status, framework, syntax_valid, unsafe, static_checks, errors, warnings, suggested_fixes, confidence, runnable_in_simulator, qcheck_version}. For multiple files or a directory,
returns an envelope {qcheck_version, results: [<per-file object + "path">...], summary: {files, passed, failed, unsafe, read_errors}}. Designed to be parsed by
an LLM agent that just generated the code, or by a CI gate. Exit codes: 0 pass,
1 verification failed, 2 unsafe/unsupported, 3 internal error.
Use it in CI (GitHub Action)
qcheck ships a composite GitHub Action. In your repo's
.github/workflows/qcheck.yml:
- uses: actions/checkout@v4
- uses: JCQuankey/qcheck@v0.8.0
with:
paths: "." # or a folder, e.g. "circuits/"
For paths containing spaces, pass one path per line:
- uses: JCQuankey/qcheck@v0.8.0
with:
paths: |
my circuit.py
openqasm sample.qasm
The step fails the job when qcheck finds errors or unsafe code. See
examples/github-action.yml.
SARIF output (GitHub Code Scanning)
qcheck can emit SARIF 2.1.0 so findings show up as code scanning alerts on the Security tab and inline on pull requests, instead of only in the log:
qcheck verify . --format sarif --output qcheck.sarif
In a workflow, generate the SARIF and let the caller upload it (upload needs
security-events: write, best granted by the consuming repo):
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v4
- uses: JCQuankey/qcheck@v0.8.0
with:
format: sarif
output: qcheck.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: qcheck.sarif
SARIF reports static qcheck findings (rule id, level, file, line). stdin input
uses a synthetic stdin URI and is not meant for code-scanning upload.
Rule catalog (explain your findings)
qcheck ships 46 rules, and every finding carries a stable rule id (for
example QISKIT-REMOVED-IMPORT). Each id is backed by catalog metadata - a
title, category, default severity, a plain-language summary, why it matters, and
a recommended next step - so a developer or an agent can act on a finding right
away and triage faster.
Browse the catalog from the CLI:
qcheck rules # table: id, level, category, summary
qcheck rules --json # full metadata, for agents and CI
The same metadata enriches SARIF driver.rules[], so code-scanning alerts show
each rule's description, severity and guidance inline. Rule ids are stable across
releases, which keeps CI gates and agent loops consistent as qcheck grows.
qcheck's JSON, qcheck rules --json, SARIF and exit codes are documented as
stable output contracts for agents and CI in docs/CONTRACTS.md. For agent/CI consumption examples, see docs/AGENTS.md.
What v0 checks
OpenQASM 2/3: missing header, undeclared, duplicate and zero-sized
registers, index-out-of-range, malformed measurements, missing measurement,
two-qubit gates on a single qubit, OpenQASM 2/3 syntax mismatches, unsupported
includes, suspicious non-QASM content.
Qiskit Python: Python syntax, missing QuantumCircuit import, zero-qubit
circuits, qubit/classical-bit indices out of range, measure() with no
classical bits, two-qubit gates on one qubit, missing measurement,
get_counts() on an unmeasured circuit, and Qiskit-1.0 breaking changes LLMs
still emit (execute(), assemble(), from qiskit import Aer/execute,
deprecated gate aliases like cnot->cx).
PennyLane: qml used without import, zero/negative device wires, and
QNodes with no return.
Run qcheck rules to see the full catalog with guidance for each rule.
Safety policy
qcheck never executes the input. Qiskit snippets are analyzed with the
Python ast module (parse, don't run). Any filesystem/network/process/dynamic-
exec construct (os, subprocess, eval, open, ...) marks the snippet
unsafe and exits 2. QASM input is text-scanned. This is deliberate: an
agent-facing verifier that ran untrusted model output would be a remote-code-
execution vector (see Qiskit CVE-2025-2000 for the QPY/pickle precedent). See
SECURITY.md for the full threat model.
Scope
qcheck v0 focuses on static review signals for Qiskit and OpenQASM: API usage (including Qiskit 1.0 removals), unsafe patterns, missing measurements, parse issues, and common LLM-generated mistakes. It reviews code without executing it, so it's safe to run on untrusted model output inside an agent loop or CI.
It's a fast first-pass reviewer - pair it with your tests and simulators for the rest. For methodology and scope details, see the leaderboard methodology.
Roadmap
- v0 (shipped, on PyPI): CLI, Qiskit + OpenQASM static checks, JSON + SARIF output, safety screen, GitHub Action. Zero runtime deps.
- Next: PennyLane + Cirq, more rules with a documented rule catalog, LLM-powered fix suggestions, MCP server (
verify_quantum_code), sandboxed simulation (opt-in). - Public static-check leaderboard for LLM-generated quantum code (see
leaderboard/) + anonymized error dataset.
Leaderboard
qcheck includes a static review benchmark for AI-generated quantum code: it
tracks how often model outputs pass qcheck's current review checks
(static_pass_rate) on a small public Qiskit/OpenQASM task set - an early quality
signal for agents and LLM workflows. The rows shown today are labelled SAMPLE/demo.
leaderboard/README.md- how to add a submission and run itleaderboard/methodology.md- scope and methodologyleaderboard/site/leaderboard.md- the generated table
Contributing
Issues and PRs welcome - especially new failure fixtures (a real LLM-generated snippet that should fail but currently passes, or vice versa). Each fixture makes qcheck sharper and feeds the public error taxonomy.
Contact
- Technical questions / maintainer contact: dev@quankey.xyz
- Security issues: security@quankey.xyz (see
SECURITY.md)
Maintained by JCQuankey / qcheck contributors. qcheck runs locally, sends no telemetry, and reviews code without executing it.
License
Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qcheck_quantum-0.8.0.tar.gz.
File metadata
- Download URL: qcheck_quantum-0.8.0.tar.gz
- Upload date:
- Size: 46.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b27bcc0c745f7e7d3b51f4af13ced9fd5e38f6dd162180d628ae638006e26091
|
|
| MD5 |
32dd0b783b80d7438490ecaa8b3e2ae5
|
|
| BLAKE2b-256 |
b6d3d226aa73b354d2e0595bf61b844884a89b7f7bd7c22ac8ee9615bd87c9a1
|
Provenance
The following attestation bundles were made for qcheck_quantum-0.8.0.tar.gz:
Publisher:
release.yml on JCQuankey/qcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qcheck_quantum-0.8.0.tar.gz -
Subject digest:
b27bcc0c745f7e7d3b51f4af13ced9fd5e38f6dd162180d628ae638006e26091 - Sigstore transparency entry: 2071749296
- Sigstore integration time:
-
Permalink:
JCQuankey/qcheck@72e294aefad2a0dfe5bdf5aaf812af8f66647def -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/JCQuankey
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@72e294aefad2a0dfe5bdf5aaf812af8f66647def -
Trigger Event:
release
-
Statement type:
File details
Details for the file qcheck_quantum-0.8.0-py3-none-any.whl.
File metadata
- Download URL: qcheck_quantum-0.8.0-py3-none-any.whl
- Upload date:
- Size: 34.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
387a4d33629c01cee99123c099cc42eaf5dfd66eff8f4b839971da4a56e9d026
|
|
| MD5 |
80efeffaf7f0dd9b340d95ca02358e1c
|
|
| BLAKE2b-256 |
dd9e51707e75fa44169fa4e50414082e2e083f7486feb4ddb67d51e5c2da69e6
|
Provenance
The following attestation bundles were made for qcheck_quantum-0.8.0-py3-none-any.whl:
Publisher:
release.yml on JCQuankey/qcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qcheck_quantum-0.8.0-py3-none-any.whl -
Subject digest:
387a4d33629c01cee99123c099cc42eaf5dfd66eff8f4b839971da4a56e9d026 - Sigstore transparency entry: 2071749302
- Sigstore integration time:
-
Permalink:
JCQuankey/qcheck@72e294aefad2a0dfe5bdf5aaf812af8f66647def -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/JCQuankey
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@72e294aefad2a0dfe5bdf5aaf812af8f66647def -
Trigger Event:
release
-
Statement type: