Changelog¶
Changelog¶
All notable changes to qb-compiler, the open-source quantum circuit compiler by QubitBoost, will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.12.0 - 2026-08-20¶
Full notes: docs/release/RELEASE_NOTES_0.12.0.md.
FIXED
- selection_receipt now records the executed layout. previously recorded the
recommendation regardless of what ran. pass
executed_layout=, get the layout that ran, the
recommendation, and score_penalty_vs_recommended.
schema stays qb.selection_receipt.v1, additions additive;
score_breakdown comes back empty with a note when
executed differs from recommended, because the breakdown
belongs to the recommendation.
- signing one persistent ed25519 key at ~/.qb-compiler/signing_key
(0600, or QBC_SIGNING_KEY), fingerprint in the receipt.
previously sign=True minted a keypair per call and
embedded its public half in the receipt it signed, which
attests to nothing; treat no 0.9.0 to 0.11.0 signature as
evidence of origin, re-issue anything load bearing.
sign=True raises when it cannot sign.
- calibration age calibration_freshness reads ISO strings, datetimes and
epochs. previously datetimes only, so most snapshots
reported no age. timestamp_status: measured / absent /
unreadable / synthetic / clock-skewed.
- qec_preflight matching_faithfulness field, always "not_assessed".
nothing here checks whether matching is faithful on the
model it was handed. the gap is named, not closed.
- badges coverage names the enforced floor and links to it,
previously a hardcoded 60 percent behind an empty link.
docs badge points at the docs site. unsigned receipt
string says "unsigned", the SDK pitch is gone.
ADDED (all free)
- qbc verify-receipt offline signature check for any receipt this package
emits. VERIFIED / UNSIGNED / NO_KEY / INVALID_SIGNATURE /
LEGACY_SELF_SIGNED / MALFORMED, only VERIFIED passes.
pure python ed25519 fallback, works on a base install.
- qbc chem-audit five integrity checks on a qubit hamiltonian file.
ACCEPT / INCOMPLETE / REFUSE, --strict, --json. reads
grouped JSON, flat terms, OpenFermion, SparsePauliOp.
- qbc measure-plan measurable terms, QWC settings, grouping factor, largest
group, shots at your rate. structural counts only.
corpus it was built against: setting count spans 1.79x
at 12 qubits, 1.59x at 16; qubit count does not predict
where a molecule lands.
- qbc corpus list / show / verify. pinned sha256, publisher URL, DOI
and citation for public QEC datasets, hard refusal when
your copy does not match. nothing redistributed.
- rank_layouts CalibrationMapper.rank_layouts() and score_layout(),
public API. max_overlap drops the near-duplicate that is
the same hardware with the labels permuted.
- calibration_freshness on every selection receipt, with tolerance_basis
stating the 30 minute tolerance is a builtin default,
not a measurement of your device.
- qbc doctor names the qiskit 1.x + qiskit-ibm-runtime >= 0.40
pairing that raises TranspilerError on a plain
transpile, prints the workaround.
- github action action.yml runs the free checks in CI.
docs/github-action.md.
- citation CITATION.cff, paper.md, paper.bib. DOI not minted yet.
- docs receipts, chemistry, corpora, github-action pages, new
commands in the CLI reference.
0.11.0 - 2026-07-27¶
Minor rather than patch: two CLI options and a new public field on BackendValue.
CHANGED
- qbc when neutral cross-vendor advisor. ranks backends by
predicted fidelity per dollar across every configured
vendor rather than one, which is the comparison a
vendor SDK cannot give you. two new options:
--backend/-b, repeatable, names the set to rank, and
--json for a machine-readable advice receipt
(qb.cross_vendor_advice.v1), unsigned and
community-tier like the other --json receipts.
- BackendValue new validation field, shown as a Data column, so every
ranked row states the provenance of its own numbers:
validated where the fidelity model is validated against
that backend's real hardware, UNVALIDATED where an
adapter exists but has not been proven on the device,
fixture-only, or no-adapter. unvalidated rows also
carry a note saying so in words. only IBM is validated
today, so a cross-vendor table is mostly UNVALIDATED,
and it says so rather than letting a model estimate sit
next to a measurement as though they were the same kind
of thing. the labels derive from the calibration
registry's live status, so they cannot drift from what
qbc backends reports. tests pin both the derivation and
the fact that no vendor is currently labelled validated
except IBM.
0.10.0 - 2026-07-27¶
First release published to PyPI since 0.7.0, so installing it brings everything in 0.8.0
(ObservableGate, the QEC decoder-input correctness audit, and its qbc dem-audit /
qbc dem-canonicalize commands) and 0.9.0 (selection receipts for calibration-aware layout) as
well as the changes below. See those entries further down for detail.
Minor rather than patch: this adds public API and changes one returned value. BudgetOptimizer
now recommends budget_optimal / depth_optimal where it previously returned speed_optimal /
cost_optimal. Anything reading that string sees different values; anything passing it to
QBCompiler, which is what it is for, now works where it previously raised.
FIXED
- entry points check_viability() and QBCompiler.compile() accept the
same circuit. previously check_viability took a Qiskit
QuantumCircuit and compile took a
qb_compiler.QBCircuit, so running both on one object,
which is what the tutorials describe, raised either an
AttributeError thrown from inside Qiskit's transpiler
or an InvalidCircuitError naming a type the caller
never chose. both now accept a Qiskit circuit, the
public QBCircuit, or the IR circuit, via the new
any_to_qiskit and any_to_compiler_circuit converters.
unconvertible input still raises, with a message naming
what is accepted.
- BudgetOptimizer its recommendations can be passed to the compiler.
previously it returned strategy names from the
qb_compiler.strategies registry (speed_optimal,
cost_optimal) that QBCompiler does not accept, so the
documented "find the cheapest backend, then compile
with the recommended strategy" flow raised ValueError
for any backend under $0.10 per shot, which is every
IBM device. recommendations now use the compiler's own
vocabulary, mapping the three cost tiers onto its three
optimization levels.
- ML layout predictor used only on the hardware it was trained on. only
ibm_heron weights ship, but the predictor was loaded
for every backend. because it narrows the candidate
qubit set before scoring, patterns learned on a fixed
coupling superconducting device were filtering layouts
on all to all trapped ion hardware. everything else now
uses the full calibration-driven search.
- layout selection _get_two_qubit_error rescanned the whole gate map on
every call, tens of thousands of times per compile. it
is now indexed once by qubit pair, which cut layout
selection from roughly 61s to 3s for a small circuit on
an all to all backend. results are unchanged: the
indexed lookup was checked against the previous one
across every qubit pair on four backends.
- qbc backends works on installs without the optional vendor SDKs. the
availability probes for Quantinuum and Azure passed a
dotted module path to importlib.util.find_spec, which
imports the parent package in order to locate a
submodule and therefore raises ModuleNotFoundError when
that parent is absent, rather than returning None.
every vendor SDK is an optional extra, so a default
pip install qb-compiler has none of them, and qbc
backends exited 1 with a traceback instead of listing
the backends. the probes now report an absent SDK as
unavailable, so the backend is still listed with
deps: no. covered by a test that simulates each vendor
SDK being missing, so the result no longer depends on
what the test machine has installed.
CHANGED
- package root exports 64 names instead of 50. since 0.7.0 the
headline additions (ObservableGate, selection receipts,
the multi-vendor calibration registry) were reachable
only by full module path, so dir(qb_compiler) showed
none of them. added: audit_dem, audit_matrices,
canonicalize_dem, preflight_dem_gate,
ObservableAuditResult, CalibrationMapper,
CalibrationMapperConfig, selection_receipt,
calibration_fingerprint, get_calibration_provider,
all_backend_statuses, get_backend_status,
any_to_qiskit and any_to_compiler_circuit.
- import cost names resolve on first use (PEP 562) rather than at
import, so the larger surface costs nothing. import
qb_compiler went from about 490ms to about 70ms and no
longer pulls numpy or package metadata. a type-checking
block re-states every export so editors and mypy still
resolve them, and the test suite asserts that the
export map and __all__ agree and that every entry
resolves.
- FidelityCircuit qb_compiler.noise.QBCircuit renamed. three unrelated
classes shared the name QBCircuit, and this one was
exported, so from qb_compiler.noise import QBCircuit
returned a fidelity descriptor with none of the public
circuit's methods.
0.9.0 - 2026-07-27¶
ADDED
- selection_receipt qb_compiler.passes.mapping.selection_receipt. a
signed-passport-ready record of the layout
CalibrationMapper chose and why: its layout, score, and
per-signal breakdown (gate error, coherence, readout,
T1 asymmetry, temporal correlation) plus a stable
calibration fingerprint. derived purely from the
mapper's PassResult, so it never re-implements the
layout objective. unsigned by default; sign=True uses
the QubitBoost SDK's Ed25519 signer, soft-imported only
if present, and degrades to an unsigned receipt with a
pointer otherwise. Apache-2.0, zero paid-SDK
dependency.
- exports CalibrationMapper and CalibrationMapperConfig from
qb_compiler.passes.mapping. previously importable only
by full module path.
FIXED
- native gate test test_qb_transpile_reduces_to_native_gates asserted cx
as the IBM Fez native 2Q gate. Fez is a Heron r2 device
whose native 2Q gate is cz. the test now derives the
allowed set from IBM_HERON_BASIS, the codebase's own
basis definition, so it cannot drift from the
transpiler target.
0.8.0 - 2026-06-27¶
The correctness-preflight release. qb-compiler gains ObservableGate, a QEC decoder-input correctness audit. A stim DEM error mechanism carries detectors, logical-observable masks, and a probability. A decoder-input canonicalization that merges by detector signature alone can collapse detector-identical but logical-distinct mechanisms and erase the logical frame, which can inflate the logical error rate. ObservableGate detects that condition before decoding and offers an observable-preserving canonical form.
ADDED
- observable_gate audit_matrices (pure-numpy invariant, no stim),
audit_dem, canonicalize_dem, preflight_dem_gate, and
ObservableAuditResult, a PASS/WARN/FAIL receipt with
mixed-group count, mixed probability mass and
worst-mask ratio.
- qbc dem-audit model.dem, CI-safe exit codes: 0 PASS, 1 WARN with
--strict, 2 FAIL. --json emits a machine-readable
community-tier receipt.
- qbc dem-canonicalize
model.dem -o safe.dem, the observable-preserving
canonical form. merges only exact
(detectors, observables) duplicates, keeps
detector-identical / logical-distinct mechanisms
separate.
- qec_preflight attaches an observable_audit receipt to its result and
prints its status.
- tests the pure-numpy core runs in base CI without the
[ising] extra.
SCOPE (honest)
- standard paths surface/repetition and the full bivariate-bicycle /
Gross family ([[72,12,6]] ... [[144,12,12]] ...
[[288,12,18]], X and Z basis) audit PASS. decomposed
DEMs are XOR-benign.
- the hazard real and measured: color_code:memory_xyz d3, ~60%
relative LER inflation under naive detector-only
merging, on graphlike DEMs with genuine
detector-identical / logical-distinct mechanisms.
- licensing ObservableGate and its receipts are free and open
source. signed receipts, batch reports, shared
dashboards and CI policy bundles are QubitBoost Pro.
see docs/open-core.md.
HARDENED
- ML checkpoint unsafe-deserialization path in the optional ML-decoder
loader fixed with torch.load(weights_only=True), plus a
CI guard against future regressions.
- qbc compile a resource-exhaustion case is now bounded.
- CLI clean errors and exit codes for malformed or
wrong-type inputs and bad output paths.
- gates 709 tests, unit plus security plus adversarial. ruff
and mypy clean.
0.7.0 - 2026-06-12¶
Folds the planned 0.5.3, 0.6 and 0.7 roadmap buckets into one release, plus the QEC experiment preflight pulled forward from 0.8.
The trust-layer release: every number qb-compiler prints now carries its uncertainty, its provenance, or both, and the package gets a memory.
ADDED
- error budget viability results break fidelity loss down by source,
two-qubit gates against readout, with pct-of-loss
rendering.
- fidelity band estimates print with a typical-abs-error band derived
from the committed Fez hardware validation pairs (n=6,
GHZ family, model runs optimistic; provenance comment
in code).
- calibration age on preflight, with a suggestion when the snapshot is
stale.
- verify mode build_mirror / run_mirror / verify_viability compare
the prediction against a mirror-circuit measurement, a
success proxy, honestly disclaimed. qbc verify runs it
on aer. records grow a local predicted-vs-actual
accuracy log; ideal-sim runs are tagged and excluded
from accuracy_summary by default.
- compilation receipts
a passport per compile: predicted fidelity plus band,
error budget, calibration age, versions, seed, layout,
in a local jsonl store. regression_check flags a drop
only beyond the combined error bands and never blocks
anything.
- best-of-n qb_transpile n_seeds sweeps the transpiler and returns
the candidate with the best calibration-aware fidelity
score. return_candidates exposes the per-seed evidence;
the fallback path returns a tagged single candidate.
- rank_value fidelity-per-dollar ranking, exposed as qbc when, with
a naive calibration trend per backend. explicitly no
forecasting.
- shot budgets shots_for_expectation, shots_for_rate.
- backend discovery from the user's own runtime service, plus pub-aware
preflight.
- qec preflight memory-experiment preflight on stim + pymatching:
projected ler band, detector fraction,
shots-for-confidence.
- ising telemetry IsingDecodeResult, bounded opt-in harness telemetry,
provenance hashes. closes the v0.5.0 design doc.
- bundled snapshots a small calibration snapshot set ships in the wheel, so
qbc when and fixture-based preflight work from a pip
install. point QBC_CALIBRATION_DIR at your own
snapshots for fresh data.
- py.typed the typing claim in pyproject is now true.
FIXED
- heron basis gates corrected to cz in BACKEND_CONFIGS (fez, torino,
marrakesh). the 0.5.2 entry below says heron r2 uses
ecr. that was wrong; heron's native two-qubit gate is
cz.
- gate registry stale marketing claims removed. the old optgate
multiplier was retracted in april and should not have
still been shipping. safetygate qualifier neutralised.
- ionq prices aria 0.03, forte 0.08 usd. 0.30 is braket's per-task
fee, now modelled separately. the price table gains a
last-reviewed stamp and a staleness warning.
- local store corrupt lines no longer brick reads. they are skipped
with a logged count.
CHANGED
- ci tests qiskit 1.4 and 2.3.
- ising extra requires pymatching >= 2.3 (enable_correlations).
[0.5.2] - 2026-04-30¶
CHANGED
- qb_transpile accepts a Qiskit backend object directly, not only a
string name. qb_transpile(circuit, backend=...) used to
require a string in BACKEND_CONFIGS; it now also takes
a Qiskit BackendV1 / BackendV2 instance, in which case
basis_gates and coupling_map are pulled from
.configuration() / .target / .basis_gates at runtime.
this closes the class of bug where the registry's
hardcoded basis_gates drifts from the real device:
BACKEND_CONFIGS["ibm_fez"] shipped cx as the native 2q
gate, IBM Heron r2 (Fez, Marrakesh) uses ecr, so the
routed circuit emitted cx and IBM Runtime rejected it.
the same trap waits for any future Heron-family
gate-set update. querying the live backend avoids it
permanently.
- backward compat the string path is unchanged. callers passing
backend="ibm_fez" keep the legacy registry behaviour.
the registry entries for ibm_fez, ibm_marrakesh and
ibm_torino were left as-is on purpose; the object path
makes them advisory rather than load-bearing for
transpilation.
- tests 3 new integration tests: the object path, the legacy
string path unchanged, and the error case where a
backend object exposes none of the inspected
attributes.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.backend("ibm_fez")
# v0.5.1 and earlier (still works, still emits stale cx for Heron):
compiled = qb_transpile(circuit, backend="ibm_fez", ...)
# v0.5.2 (recommended): pulls live ecr basis from the backend itself
compiled = qb_transpile(circuit, backend=backend, ...)
[0.5.1] - 2026-04-27¶
Connectivity-aware chain selection. Closes the v0.5.0 UCCSD/HEA regression.
v0.5.0 had three latent bugs that combined to make the live-calibration path underperform the v0.4 static-fixture path on dense-1q workloads (UCCSD, hardware-efficient ansatzes). v0.5.1 fixes all three. Headline benchmark result on IBM Fez (n=30 random seeds, paired Wilcoxon signed-rank, Bonferroni-adjusted, classical noise-aware fidelity scoring against a fresh live calibration snapshot):
Comparison |
v0.5.0 (broken) |
v0.5.1 (fixed) |
|---|---|---|
v0.5 vs v0.4 fixture path |
2W / 3L / 3T |
0W / 0L / 8T |
v0.5 vs Qiskit |
3W / 5L / 0T |
5W / 2L / 1T |
UCCSD-H4 vs Qiskit (median delta) |
-3.9 % (loss) |
+12.3 % (p<0.0001) |
QAOA-8 ring p=2 vs Qiskit |
+13.9 % |
+8.8 % (p<0.0001) |
HEA-8 d=4 vs Qiskit |
-5.5 % |
+0.3 % |
Full circuit suite + raw data:
QubitBoost-internal/experiments/qb_compiler_v0_5_benchmarks/.
Hardware companion (re-run, n=16 supersedes initial n=4)¶
The initial release (2026-04-27 17:24 UTC) reported a hardware companion result of “v0.5.1 lands 6.4 mHa closer to E_RHF than Qiskit opt=3” on the H2O 4e4o HF state on IBM Fez at n=4 reps per arm. A 90-minute follow-up at n=16 reps reversed the verdict: same circuit, same layouts, but Qiskit opt=3 came in at |delta E_RHF| = 7.21 mHa against v0.5.1’s 12.33 mHa. The 5.12 mHa gap at n=16 is below 1 sigma of the combined SEMs (~12.5 mHa), so the honest verdict is statistically equivalent on this single circuit at p=0.05. The initial n=4 win was a tail event; the n>=5, ideally n=8, hardware-claim rule applies and n=4 was below threshold for any defensible single-circuit verdict.
The classical n=30-seed benchmark above is unaffected: different sample-size regime, paired comparison rather than absolute, drift-isolated by scoring against a single fresh snapshot for both arms. The +12.3% UCCSD-H4 estimated-fidelity result vs Qiskit stands.
A larger statistical-power hardware run (n>=32, multi-window) is scheduled for v0.5.2 to make a hardware-validated absolute claim defensible.
FIXED
- chain selection the load-bearing fix. QBCalibrationLayout previously
picked the N best-scoring physical qubits regardless of
whether they formed a connected subgraph on the device
coupling map. on dense-2q circuits that picked qubits
scattered across the chip, forcing the downstream
router to insert many SWAPs and crashing post-routing
fidelity. v0.5.1 adds _vf2_calibration_aware(), which
uses rustworkx.vf2_mapping to enumerate subgraph
isomorphisms of the circuit's 2q interaction graph onto
the device coupling map and scores each candidate by
sum(per-qubit scores) + sum(per-edge gate errors x
interaction count). falls back to the v0.5.0
topology-blind path if VF2 finds no mapping, for
instance when the circuit has no 2q interactions.
- gate-error pooling v0.5.0's _build_qubit_scores pooled single-qubit and
two-qubit gate errors into a single arithmetic mean.
with the v0.5 live calibration's full coverage the
small 1q errors (~1e-4) diluted the larger 2q errors
(~5e-3) by ~5x, distorting score ordering. v0.5.1
tracks gate_error_1q and gate_error_2q on separate
score keys with weights w_2q=0.40, w_1q=0.00. the 1q
signal is captured but weighted at zero, because the
connectivity-aware scorer above does not usefully
consume it without per-edge 1q modelling; that is
scheduled for v0.6.
- _provider_to_dict LiveCalibrationProvider unwrapping. called with a
LiveCalibrationProvider, the materializer's
getattr(provider, "_props") returned None, because the
BackendProperties lives at provider._snapshot._props,
one level deeper. the materialised calibration dict was
missing coupling_map, n_qubits and basis_gates, and
without coupling_map the new VF2 path was a silent
no-op. fixed by drilling into _snapshot._props when
_props is absent at the top level.
DEPRECATED
- nothing v0.5.1 is wire-compatible with v0.5.0.
Notes on prior v0.5.0 release notes¶
The v0.5.0 entry below carries a workload-dependent regression disclosure that is now obsolete. The regression is closed in v0.5.1. Treat v0.5.1 as the authoritative version; v0.5.0 is retained below for historical context.
[0.5.0] - 2026-04-26¶
Live calibration end-to-end against real backends. The LiveCalibrationProvider path stops being
a stub: it delegates to a working qubitboost_sdk.calibration.CalibrationHub.
Architectural scope of v0.5: the “in-process tier”¶
SHIPS
- CalibrationHub a Python class that runs inside whatever process
imports qubitboost_sdk.calibration: qb-compiler, a
notebook, a script. on-demand fetch from IBM Quantum
via qiskit-ibm-runtime. per-user disk JSON cache at
~/.cache/qubitboost/calibration/ with 30-min TTL. each
consuming process holds its own hub instance and its
own view of the disk cache.
DOES NOT SHIP
- background daemon no polling daemon.
- shared cache no Redis or other shared cache, no cross-process
synchronisation.
- HTTP surface no FastAPI endpoint, no Hardware Observatory page
consumer.
- rate-limit budget not shared across consumers. today two parallel
processes can both fetch the same backend within
seconds of each other. for the demo that is fine.
The PM2-managed daemon, Redis cache, FastAPI endpoint and Observatory page are scoped at
QubitBoost-internal sales/CALIBRATION_HUB_DESIGN.md and are scheduled for a follow-on release.
They are not implied or required by v0.5.
Authentication¶
LiveCalibrationProvider (and CalibrationHub) authenticate to IBM via a saved-credential
profile resolved by QiskitRuntimeService(name=...). The default profile name is
"qubitboost_cloud". External users must save their own IBM credentials before invoking the live
provider::
from qiskit_ibm_runtime import QiskitRuntimeService
QiskitRuntimeService.save_account(
name="my_account", channel="ibm_quantum", token="...",
)
then pass account="my_account" to either constructor. No tokens are embedded in the package. No
environment variable is read in v0.5. Profile resolution is identical to plain
QiskitRuntimeService use.
Freshness contract (locked)¶
- cache age 0-30 min default cache_ttl_minutes=30. get_latest serves the
cached snapshot. no IBM contact.
- cache age > 30 min get_latest attempts a fresh fetch. on success the new
snapshot replaces the cache and is returned. on failure
(IBM unreachable, 5xx, network error) the previous
stale cached snapshot is returned with a UserWarning of
the form "CalibrationHub: fresh fetch for {backend}
failed (...); serving stale cache (age N min)".
- fetch() always bypasses cache, always performs a fresh vendor
call. on failure it raises, with no stale fallback. use
it for explicit pre-experiment freshness guarantees.
- stale-fallback age no upper bound inside the hub. callers needing a hard
floor, a demo harness that refuses to launch on
>24h-old calibration for instance, must enforce it
themselves by reading provider.timestamp and comparing
to datetime.now(timezone.utc).
The 30-min TTL is a small fraction of IBM Heron-class devices’ ~12h calibration cadence on Fez/Torino/Marrakesh/Kingston: long enough to amortise IBM API traffic across multiple compilations of the same circuit, short enough to catch event-driven recalibrations within ~30 min of IBM publishing them.
Coverage improvement vs v0.4 fixture path¶
LiveCalibrationProvider returns snapshots with the full property surface IBM exposes. For
ibm_fez specifically:
- v0.4 fixture hand-fetched 2026-03-14. 156 qubit_properties (T1, T2,
readout_error_0to1, readout_error_1to0,
frequency=None) plus 352 gate_properties, 2-qubit ECR
errors per coupling only.
- v0.5 live fetch the same 156 qubit_properties plus IBM-specific fields
(prob_meas0_prep1, prob_meas1_prep0, readout_length)
plus 1796 gate_properties, ~5x, adding per-basis-gate
single-qubit error rates for id, sx, rz, x.
The added 1444 entries are per-basis-gate single-qubit error rates that v0.4’s fixture-based path did not capture. The practical effect on chain selection is small, single-qubit errors are typically much smaller than 2-qubit, but the data is now complete.
Field-format reconciliation¶
- disk cache JSON dual-format. legacy readout_error_0to1 /
readout_error_1to0 aliases and modern
prob_meas1_prep0 / prob_meas0_prep1 fields are both
present per qubit. gate parameters are written both
flat (gate_error: ...) and nested under
parameters: {...}.
- _provider_to_dict() the snapshot it materialises for QBCalibrationLayout
uses the legacy field-name convention internally,
because qb-compiler's BackendProperties dataclass keeps
only the legacy fields after parsing.
QBCalibrationLayout consumes via the nested
parameters.gate_error path, which _provider_to_dict()
emits. verified working.
Benchmarks (re-run 2026-04-26)¶
v0.5’s layout-selection algorithm was benchmarked against the v0.4 static-fixture path and against
Qiskit optimization_level=3 on a fixed circuit set on IBM Fez calibration data. n=30 random
seeds per circuit, paired Wilcoxon signed-rank with Bonferroni correction across 3 comparisons per
circuit, classical noise-aware fidelity scoring, no QPU execution. The QPU companion is deferred
to v0.5.1.
Circuit set: GHZ-{4,8,12}, QAOA-8 ring p={1,2}, UCCSD-H4 4e4o, HEA-{8,12} d=4.
Headline finding: v0.5 is workload-dependent, not uniformly better.
Workload class |
v0.5 vs v0.4 fixture |
v0.5 vs Qiskit opt=3 |
|---|---|---|
Ring QAOA (sparse 1q, dense 2q) |
+4 to +4 % median fid (p<0.05) |
+5 to +14 % median fid (p<0.05) |
GHZ (mostly 2q) |
tied |
mixed (1 win at 12q, 2 losses at 4q/8q) |
UCCSD / HEA (dense 1q + 2q) |
-5 to -7 % median fid (p<0.0001) |
-3 to -6 % median fid (p<0.0001) |
Tally for v0.5 vs Qiskit opt=3: 3 wins, 5 losses, 0 ties (8 circuits). Tally for v0.5 vs v0.4: 2 wins, 3 losses, 3 ties (8 circuits).
Interpretation: the added single-qubit gate-error data in v0.5 appears to distort qb-compiler’s chain-scoring on dense-single-qubit workloads (UCCSD, HEA), and that distortion costs more than the QAOA-side gains on most circuit classes. An algorithm-level retune of the chain-scoring weights between single-qubit and 2-qubit error contributions is scheduled for v0.5.1; the goal is uniformly equal-or-better-than-v0.4 across all circuit classes.
Practical guidance for v0.5 callers:
QAOA-style workloads: use
LiveCalibrationProvider, the v0.5 default.Dense-single-qubit workloads (UCCSD, HEA, generic VQE ansatz): use
calibration_path=...with the v0.4-style fixture format until v0.5.1 ships the algorithm fix. The live data path is correct; the chain-scoring algorithm using it is not yet tuned for this workload class.
Full results: QubitBoost-internal experiments/qb_compiler_v0_5_benchmarks/results/.
README claim correction (line 222)¶
The wording “calibration-aware layout selection that matches or exceeds Qiskit’s default on hardware-validated benchmarks” is workload-dependent under v0.5: 5 of 8 circuits regress vs Qiskit opt=3 in the benchmark above. Recommended replacement wording on the README:
“calibration-aware layout selection that matches or exceeds Qiskit’s default on QAOA-style hardware workloads. Performance is workload- dependent; see CHANGELOG v0.5.0 for the full benchmark table including circuit classes where qb-compiler currently underperforms (UCCSD-style chemistry ansatzes and dense hardware-efficient ansatzes).”
ADDED
- calibration_provider qb_transpile(..., calibration_provider=...) accepts any
CalibrationProvider instance directly, including the
live one. previously only calibration_path and
calibration_data were supported.
- _provider_to_dict materialises a provider's snapshot into the calibration
dict QBCalibrationLayout consumes, so the live provider
can drive the layout pass without bespoke wiring.
- account= LiveCalibrationProvider(..., account="...") so external
users can pass their saved-credential profile name.
defaults to "qubitboost_cloud" for backward
compatibility.
CHANGED
- refresh() LiveCalibrationProvider.refresh() calls hub.fetch()
directly to bypass the cache TTL. previously it called
hub.get_latest(), which silently served cached data
inside the TTL window, contradicting the docstring's
"force re-fetch" promise.
- cache_ttl_minutes propagated down to the hub LiveCalibrationProvider
constructs, so both layers agree on freshness. v0.5 dev
versions briefly defaulted LiveCalibrationProvider to
30 min while the underlying hub defaulted to 60. fixed.
- ImportError LiveCalibrationProvider no longer raises it when
qubitboost-sdk>=2.6 is installed. earlier versions
pointed at qubitboost_sdk.calibration.CalibrationHub,
which did not exist; v2.6 of the SDK ships that module.
HONEST DISCLOSURE ON PRIOR VERSIONS
- v0.1-v0.4 the "calibration-aware" claims operated on real IBM
backend properties (audit at QubitBoost-internal
sales/QB_COMPILER_FIXTURE_PROVENANCE.md), but the
shipped fixture files carried 2-qubit gate errors only
and dropped per-basis-gate single-qubit error data.
from v0.5 the live fetch via CalibrationHub provides
the full property surface. for chain selection the
practical impact of the prior partial coverage was
small, single-qubit errors are typically much smaller
than 2-qubit errors, but v0.5 closes the gap.
- README claim "Calibration data can be loaded from local JSON files
or fetched from vendor APIs" was incorrect on v0.4 and
earlier: the vendor-API path raised ImportError. it is
correct on v0.5 with pip install
qb-compiler[qubitboost].
[0.4.0b1] - 2026-04-22¶
Beta release. Stim-validated only, no hardware runs yet. API may shift.
ADDED
- qb_compiler.ising first Qiskit-side integration for NVIDIA's
Ising-Decoder-SurfaceCode-1 model family, released
2026-04-14. converts rotated-surface-code memory
experiments (Qiskit or stim) into the 4-channel
(B, 4, T, D, D) float32 tensor the pretrained decoder
consumes. public API: SurfaceCodePatchSpec,
build_ising_tensor, PyMatchingDecoder (MWPM baseline),
IsingDecoderWrapper (pre-decoder plus residual-MWPM
chain; users bring their own gated-HF weights and
NVIDIA's Apache-2.0 model definition, qb-compiler does
not vendor NVIDIA code or weights),
evaluate_logical_error_rate harness. install
qb-compiler[ising] for the PyMatching baseline,
qb-compiler[ising-nvidia] to add torch + safetensors
for the NVIDIA pre-decoder. see
src/qb_compiler/ising/README.md.
- benchmark harness benchmarks/ising/run_pymatching_sweep.py sweeps
(distance, rounds, p_error, basis) to establish the
baseline any pre-decoder must beat.
- extras ising, ising-nvidia.
[0.3.0] - 2026-04-16¶
ADDED
- qiskit 2.x compatibility. the qiskit dependency widens to
>=1.0,<3.0, and CI runs the suite against both Qiskit
1.4 and Qiskit 2.0 in matrix.
- QBCalibrationLayoutPlugin
a proper qiskit.transpiler.layout stage plugin. invoke
via generate_preset_pass_manager(
layout_method="qb_calibration") with the
QB_CALIBRATION_PATH env var set. discoverable through
Qiskit's entry-point system.
CHANGED
- qb_transpile() injects QBCalibrationLayout into the pass manager's
pre_layout stage instead of layout. on Qiskit 2.x the
previous approach triggered an ApplyLayout KeyError and
silently fell back to stock qiskit.transpile, bypassing
calibration-aware layout. the custom pipeline is now
the primary code path on both Qiskit versions.
- QBTranspilerPlugin entry-point group corrected from the non-existent
qiskit.transpiler.stage to qiskit.transpiler.layout,
now pointing at QBCalibrationLayoutPlugin. the plugin
was previously undiscoverable via Qiskit's loader.
DEPRECATED
- get_pass_manager() QBTranspilerPlugin.get_pass_manager(
calibration_data=...) emits a DeprecationWarning and
will be removed in 0.4.0. migrate to
generate_preset_pass_manager(
layout_method="qb_calibration") with
QB_CALIBRATION_PATH set, or call qb_transpile()
directly.
FIXED
- ci.yml triggers on master as well as main. the repo's default
branch is master, so the workflow had been dormant.
- CI install the phantom [qiskit] optional-dependency extra removed
from the install commands. it did not exist in
pyproject.toml and was silently ignored.
[0.1.0] - 2026-03-13¶
ADDED
- core IR QBCircuit, QBDag, QBGate, QBMeasure, QBBarrier.
- converters Qiskit and OpenQASM 2.0.
- CalibrationMapper VF2-based calibration-weighted qubit placement.
- NoiseAwareRouter Dijkstra shortest-error-path SWAP routing.
- NoiseAwareScheduler ALAP scheduling with T1/T2 urgency scoring.
- GateDecomposition native basis decomposition. IBM ECR/RZ/SX/X, Rigetti
CZ/RX/RZ, IonQ MS/GPI/GPI2, IQM CZ/PRX.
- ErrorBudgetEstimator
pre-execution fidelity prediction.
- T1 asymmetry readout-scaled penalty for qubits with high |1> decay.
- temporal correlation
Pearson correlation across calibration snapshots.
- calibration StaticCalibrationProvider, CachedCalibrationProvider,
BackendProperties.
- noise modelling EmpiricalNoiseModel, FidelityEstimator.
- backends IBM Heron, Rigetti Ankaa, IonQ Aria/Forte, IQM
Garnet/Emerald.
- cost estimation with vendor pricing.
- qiskit plugin QBCalibrationLayout, qb_transpile(), QBPassManager.
- CLI qbc compile, qbc info, qbc calibration show.
- optimisation passes gate cancellation and commutation analysis.
- analysis passes depth and gate count.
- ML phase 2 XGBoost layout predictor. AUC=0.94, 454KB, +5.4%
fidelity on GHZ-8.
- ML phase 3 GNN layout predictor. dual-graph GCN, 42KB, +6.5%
fidelity on QAOA-8.
- ML phase 4 RL SWAP router. PPO actor-critic, 190KB,
calibration-aware routing.
- ML infrastructure data generator, feature extraction, model training
scripts.
- tests 461, covering all passes, IR, calibration, backends and
the ML pipeline.
- CI/CD GitHub Actions for lint, typecheck and the test matrix,
Python 3.10-3.12.
- examples 10 scripts demonstrating key features.
- benchmarks comparing all ML phases.