Schemas — what you benchmark
CircuitSpec, BackendSpec, ExecutionOptions
and QuantumResult are plain Pydantic v2 models. They serialize to JSON,
validate on construction, and never import a quantum SDK.
Quantum benchmarking · one record format
QPUBench runs quantum benchmarks and records every result in one common
format, so runs from different SDKs, machines and even different quantum
computing paradigms can be compared side by side. You describe what to run as
plain typed data, execute it through a pluggable backend adapter, and every run
is stored as the same self-describing BenchmarkRecord — readable
without any quantum SDK installed.
Read the guide Install Supported packages
pydantic is the only required dependencyfrom qpubench import BenchmarkRunner, CircuitSpec
circuit = CircuitSpec(
num_qubits=2,
serialized=bell_qasm,
observables=[zz],
)
runner = BenchmarkRunner(store="results/bell.ndjson")
runner.register(AerAdapter(), name="aer")
record = runner.run(circuit, "aer", shots=4096)
ev = record.result.expectation_values[0]
# <ZZ> = 1.0000 ± 0.0000
Swap "aer" for "ibm", "iqm",
"braket" or a stub — the rest of the script, and the record it produces,
do not change.
CircuitSpec, BackendSpec, ExecutionOptions
and QuantumResult are plain Pydantic v2 models. They serialize to JSON,
validate on construction, and never import a quantum SDK.
Any object with spec, validate() and run()
is a backend. Libraries that build their own circuits implement the sibling
AlgorithmAdapter protocol instead. No base class to inherit.
NDJSONStore (append-only, zero-dependency), ParquetStore
(columnar, for pandas) and S3Store (one object per record, safe for
distributed sweeps) share one interface.
Every project below is reachable from QPUBench — either as a runnable adapter that executes circuits, or as a typed schema bridge that captures its run configuration and results in the shared record format. Schema bridges never import the external library, so installing QPUBench pulls in none of them. Follow a tile for the QPUBench documentation, or “upstream” for the project itself.
Simulators and QPUs with a runnable adapter in qpubench.backends. Register one with a BenchmarkRunner and every run lands in the same record format.
Packages whose run configurations, wavefunctions and results are mirrored as typed schemas, so their output is directly comparable with everything else in the store.
Beyond gate-based superconducting qubits. ComputingModel and QubitModality are independent axes on every record, so these runs share one store with everything above.
The layers around execution: making a noisy run usable, making a large circuit fit, and running the whole campaign as scheduled compute.
Marks shown here are generated placeholder wordmarks, not vendor artwork — see docs/site/README for how to drop in a real logo.
A note on “chemical accuracy”. QPUBench reports an energy error — the gap between a run and a classically computed reference — and flags whether it is below 1.6 mHartree. That is a numerical-convergence check against a computed value, not agreement with an experimentally measured quantity. Treat the stored reference as a computed baseline, not as ground truth.
An adapter is a plain class. Two rules keep the ecosystem healthy: SDK imports live
inside methods, so importing your adapter never requires the SDK; and recoverable
failures return status=FAILED rather than raising, so one bad point does not
kill a sweep.
class MySimulatorAdapter:
@property
def spec(self) -> BackendSpec:
return BackendSpec(name="my_sim", provider="me", simulator=True)
def validate(self, circuit: CircuitSpec) -> list[str]:
return [] if circuit.num_qubits <= 30 else ["max 30 qubits"]
def run(self, circuit: CircuitSpec, options: ExecutionOptions) -> QuantumResult:
import my_sdk # SDK imports stay inside methods
counts = my_sdk.sample(circuit.serialized, shots=options.shots)
return QuantumResult(
computing_model=circuit.computing_model,
shots=ShotResult(num_qubits=circuit.num_qubits,
num_shots=options.shots, counts=counts),
status=JobStatus.SUCCEEDED,
)
Templates for both protocols live in integrations/template/, and Backends & adapters walks through testing an adapter with the SDK mocked out.