ZKSF logo, a neon quantum brainZKSF

SDK Documentation

Everything runs through one Python package. Write circuits in Qiskit, Cirq, PennyLane, pyQuil, or Braket, and we convert them for you; we handle where and how they execute.

1. Install

pip install qsim-sdk

2. Get a token

Create an account at app.zksf.org. Your API token is on the dashboard.

3. Run your first circuit

import qsim_sdk
from qiskit import QuantumCircuit

qc = QuantumCircuit(60)          # yes, sixty qubits
qc.h(0)
for i in range(59):
    qc.cx(i, i + 1)

client = qsim_sdk.Client(token="YOUR_TOKEN")
job = client.run(qc, shots=1000)

print(job["result"]["counts"])      # the answer
print(job["result"]["error_info"])  # how much to trust it

The router inspected your circuit, saw it was Clifford, and ran it with a stabilizer engine: exactly, in milliseconds, for a fraction of a cent. A 60-qubit statevector would have needed 16 million terabytes of RAM.

4. Estimate before you spend

est = client.estimate(qc)
# {'engine': 'mps.quimb.cpu', 'predicted_seconds': 5.1,
#  'predicted_cost_usd': 0.00014, 'reason': '60 qubits, low
#  entangling density -> MPS attempt with convergence check'}

Estimates are free, instant, and deliberately conservative. No job runs without you knowing the ceiling.

5. Understand the error certificate (ZCC-v0.1)

Every approximate result carries a ZCC-v0.1 statement of how far it can be from the truth. By default it is a fast convergence check; pass certified=True for a measured, single-run bound.

job["result"]["error_info"]
# {'method': 'MPS (quimb)', 'max_bond': 64,
#  'convergence_deviation': 0.0, 'converged': True}
client.run(qc, engine="mps.quimb.cpu", certified=True)
# error_info = {'protocol': 'ZCC-v0.1',
#   'method': 'MPS, measured discarded-weight bound',
#   'truncation_weight': 3.2e-08,   # exact discarded SVD weight
#   'error_bound': 2.5e-04,         # max per-outcome prob error
#   'certified': True}
  • convergence_deviation: how much the top outcome probabilities moved when we re-ran at double the bond dimension. 0.0 means the approximation captured the state.
  • certified=True: builds the state with renormalization off and reads the accumulated discarded SVD weight from the norm deficit. The per-outcome probability error is then bounded by sqrt(2 · truncation_weight), measured in a single run rather than extrapolated. Each truncation is optimal by Eckart-Young and the weight is read from the state, not estimated. The bound holds under incoherent accumulation of the individual truncation errors, a condition deep circuits do not always satisfy, so it is stated as an empirically supported bound rather than a worst-case guarantee: it was never exceeded across 334 runs checked against exact simulation, 290 of them constructed to falsify it, which reach 20 qubits, the largest size at which an exact reference can be computed.
  • Pauli propagation reports the same protocol: error_bound is the total discarded coefficient mass, which bounds the expectation-value error.
  • Exact engines (statevector, stabilizer) report truncation_error: 0.0; only shot noise applies.

Any finished job can be exported as a downloadable PDF certificate with a public verification link, so a result you report can be independently checked by anyone. See the methodology write-up for the full protocol, or read the paper for the derivations and the falsification testing behind it.

6. Understand the hardware certificate (ZHF-v0.1)

QPU results carry a different statement: not how close an approximation is to the truth, but how closely a real device’s measured counts track the exact ideal distribution. That is ZHF-v0.1.

client.run(qc, engine="qpu.ionq", shots=100)
# certificate = {'protocol': 'ZHF-v0.1',
#   'device': 'IonQ Forte-1',
#   'fidelity_mode': 'direct',
#   'fidelity': 0.9774}
  • fidelity: the Hellinger fidelity between the measured hardware counts and the exact ideal distribution computed for the same circuit, in [0, 1]. 1.0 would mean the measured counts matched the ideal distribution exactly.
  • fidelity_mode: "direct": the circuit was small enough (≤ 24 qubits) to also simulate exactly, so the fidelity is measured directly against that ideal reference, not asserted from a vendor specification.
  • Above that qubit count, direct verification is not yet available; the certificate says so honestly rather than reporting a number it cannot back up.

Like ZCC-v0.1, any finished QPU job can be exported as a downloadable PDF certificate with a public verification link. See the methodology write-up for both protocols.

7. Choose an engine explicitly (optional)

client.run(qc, engine="mps.quimb.cpu", max_bond=128)  # force MPS
client.run(qc, engine="exact.cpu")                     # force exact
EngineBest forLimits
exact.cpuGround truth, any gate set≤ 30 qubits
exact.gpuExact results, GPU-accelerated≤ 32 qubits
cliffordStabilizer/error-correction circuitsClifford gates only, 5,000+ qubits
mps.quimb.cpuQAOA, ansätze, structured circuitsentanglement-dependent, ~128 qubits; certified error bounds
mps.aer.cpuIndependent tensor-network cross-checkentanglement-dependent
pauli.cpuExpectation values of large circuitsneeds an observable; 100 to 200 qubits, no bitstring counts
noisy.cpuPreview hardware noise before you paydevice-noise model; optional error mitigation (ZNE)
qpu.rigettiReal superconducting hardwareRigetti Cepheus 108 qubits; scheduled queue, physical noise
qpu.ionqReal trapped-ion hardwareIonQ Forte-1 36 qubits, 100-5000 shots; scheduled queue

8. Error mitigation (opt-in)

Any run that carries an observable can request zero-noise error mitigation. The circuit is evaluated at several amplified noise levels and the observable is extrapolated back to the zero-noise limit, which removes much of the modelled gate noise from the reported value. It applies to the noisy.cpu simulator and to the qpu.rigetti and qpu.ionq hardware engines. On hardware it submits three folded circuits, so it bills three times the single-run cost.

client.run(qc, engine="qpu.rigetti", shots=200,
           observable=[[1.0, "ZZ"]], mitigate=True)
# error_info: { protocol: "ZCC-Estimate-v0.1",
#   technique: "zero-noise extrapolation",
#   raw_expectation: 0.81, mitigated_expectation: 0.82,
#   error_bound: 5.0e-02, estimated: true, certified: false }

The returned figure is a mitigated value with a ZCC-Estimate-v0.1 uncertainty: the larger of the shot noise propagated through the extrapolation and the disagreement between the linear, quadratic, and Richardson extrapolations of the same measured data. It is computed without reference to the true answer, so the same statement carries over to hardware. Because an extrapolated value cannot be given a hard mathematical bound the way the exact, stabilizer, and tensor-network paths can, certified reads false by design: the result is reported as a well-founded statistical estimate, not as a guarantee, and the exported certificate states this explicitly.

9. When we say no

qsim_sdk.JobRejected: intractable classically at this structure:
60 qubits, depth 99, 826 entangling gates (13.8/qubit).
Options: reduce entangling density below ~8/qubit for MPS,
restrict to Clifford gates, or shrink to <= 30 qubits for
exact simulation.

Deep, wide, highly entangled circuits are intractable for every classical method, and beyond today’s noisy hardware too. We reject them with the reason and your options, instead of billing you for results that cannot be trusted.

10. Worked example: real test runs on every tier

Everything above, shown on real jobs: five runs across CPU, GPU, and both quantum processors, with the environment, the results, and what they cost. You can reproduce them from the console or the SDK.

Run A (CPU): the benchmark suite, on a laptop

Environment: an ordinary consumer laptop (Intel i7-12700H, 32 GB RAM), CPU only: no GPU, no cluster, no quantum hardware. Circuits were submitted with the engine field left blank, so the router chose the method each time.

CircuitQubitsDepthEngine pickedWall timeAccuracy
GHZ (Clifford)5,0005,000clifford0.56 sexact
QAOA MaxCut, p=3100304mps.quimb.cpu5.9 sconverged (dev 0.0)
Layered ansatz809mps.quimb.cpu4.3 sconverged (dev 0.0)
Exact statevector269exact.cpu2.7 sexact (ground truth)

Reading the accuracy column: exact means the method makes no approximation at all, so only shot noise applies. Converged (dev 0.0) means the job was automatically re-run at double the bond dimension and the top outcome probabilities did not move; the compression captured the state. Every approximate job you run gets this same check, in its error_info.

Run B (GPU): 8-qubit GHZ, production cloud

Environment: our production GPU tier, submitted through the console exactly as any signed-in user would. The circuit is an 8-qubit GHZ state: one Hadamard, a chain of CNOTs, measure everything.

OPENQASM 2.0;
include "qelib1.inc";
qreg q[8];
creg c[8];
h q[0];
cx q[0],q[1];
cx q[1],q[2];
cx q[2],q[3];
cx q[3],q[4];
cx q[4],q[5];
cx q[5],q[6];
cx q[6],q[7];
measure q -> c;

Steps: paste the circuit, set shots to 1,000, pick exact.gpu from the engine menu, run. The result:

counts:     {"00000000": 502, "11111111": 498}
error_info: {"method": "exact statevector (GPU)",
             "truncation_error": 0, "shot_noise_only": true,
             "shots": 1000}
resources:  {"device": "GPU", "wall_seconds": 0.51}

A GHZ state should collapse to all-zeros or all-ones with equal probability, so the ideal split over 1,000 shots is 500/500; the measured 502/498 is textbook shot noise. Execution took half a second on the GPU. The first GPU job after a quiet period can add a short warm-up delay while a worker spins up; it is not billed.

Run C (GPU): 32 qubits, the memory wall

The GPU tier’s exact statevector reaches 32 qubits, the point where a 32 GB laptop runs out of memory. A 32-qubit GHZ circuit (100 shots) holds a full 64 GiB statevector on the GPU, well past what a consumer machine can allocate.

counts:     {"00000000000000000000000000000000": 52,
             "11111111111111111111111111111111": 48}
error_info: {"method": "exact statevector (GPU)",
             "truncation_error": 0, "shot_noise_only": true,
             "shots": 100}
resources:  {"device": "GPU", "qubits": 32,
             "statevector": "64 GiB", "wall_seconds": 8.2}

52/48 across the two GHZ states is the expected 50/50 split within shot noise, computed exactly (no approximation, truncation error 0) in 8.2 seconds. This is the crossover the platform is built for: the same SDK line that ran on a laptop scales onto GPU memory a laptop does not have.

Run D (QPU): Rigetti superconducting hardware

The same experiment on a real quantum processor: a 3-qubit GHZ circuit, 50 shots, engine qpu.rigetti, submitted through the console. Hardware runs are scheduled on the physical device, so this one returned after roughly 53 minutes in the queue; you can close the page and the result attaches to the job. The result:

counts:     {"000": 23, "111": 22,
             "011": 3, "010": 1, "001": 1}
error_info: {"method": "quantum hardware (Rigetti)",
             "device": "Cepheus-1-108Q",
             "shot_noise_and_device_noise": true,
             "shots": 50,
             "note": "raw hardware counts; no error mitigation applied"}

45 of 50 shots landed in the two GHZ states, and 5 shots show bit-flip errors from real device noise: the physical signature of superconducting qubits, reported exactly as measured. Comparing this against the simulator runs above is itself a useful experiment; the same circuit can also be submitted with zero-noise error mitigation (section 8) to return a mitigated observable with a stated uncertainty, at three times the single-run cost.

Run E (QPU): IonQ Forte-1 trapped-ion hardware

The same family of experiment on a different kind of quantum computer: a 2-qubit GHZ (Bell) circuit, 100 shots, engine qpu.ionq, on IonQ’s Forte-1 trapped-ion processor. Trapped-ion queues are scheduled the same way; this run returned after about 5 hours. The result:

counts:     {"00": 54, "11": 44, "01": 2}
error_info: {"method": "quantum hardware (IonQ)",
             "device": "Forte-1",
             "shot_noise_and_device_noise": true,
             "shots": 100,
             "note": "raw hardware counts; no error mitigation applied"}

98 of 100 shots landed in the two GHZ states (00 and 11), with just 2 shots of device noise, so about 98% of shots fell in the ideal outcomes. Trapped-ion qubits carry different noise characteristics from superconducting ones; running the identical circuit on both, next to the exact simulator, is exactly the kind of comparison the platform is meant to make easy.

Run F (CPU): how to reproduce a 192-qubit result for a tenth of a cent

Statevector simulation stops around 34 qubits because memory doubles with every qubit. Pauli propagation does not track the full state; it evolves the observable backward through the circuit, so its cost scales with circuit structure rather than qubit count. That reaches well past 100 qubits on an ordinary CPU. Here is a 192-qubit GHZ circuit with an all-Z observable, engine pauli.cpu, which you can run yourself from the SDK in three lines:

import qsim_sdk
from qiskit import QuantumCircuit

n = 192
qc = QuantumCircuit(n)
qc.h(0)
for i in range(n - 1):
    qc.cx(i, i + 1)               # a 192-qubit GHZ state

client = qsim_sdk.Client(token="YOUR_TOKEN")
job = client.run(
    qc,
    engine="pauli.cpu",
    observable=[[1.0, "Z" * n]],  # measure <Z...Z> across all 192 qubits
)

print(job["result"]["expectation"])   # 1.0
print(job["result"]["error_info"])     # certified, error_bound 0
expectation: 1.0
error_info:  {"protocol": "ZCC-v0.1",
             "method": "Pauli propagation (Heisenberg picture)",
             "truncated_coefficient_mass": 0, "error_bound": 0,
             "certified": true, "converged": true}

The exact answer is known analytically: for a GHZ state the all-Z expectation value is exactly +1, and the engine returns 1.0 with a ZCC-v0.1 error bound of 0 (no Pauli terms were truncated). The run completed in under a minute and cost $0.001, a tenth of a cent, at the standard pauli.cpu rate. The result carries a public, independently verifiable certificate: api.zksf.org/certify/5b8b2c4309d44d41.

Summary: one circuit family, every tier

TierCircuitQubitsResultTime
CPUGHZ (Clifford)5,000exact0.56 s
CPUQAOA MaxCut, p=3100converged (dev 0.0)5.9 s
GPUGHZ, 1,000 shots8502/498 (ideal 500/500)0.5 s
GPUGHZ, 100 shots3252/48 (ideal 50/50); 64 GiB statevector8.2 s
QPUGHZ, 50 shots (Rigetti)345/50 in GHZ states, 5 noise~53 min incl. queue
QPUGHZ, 100 shots (IonQ)298/100 in GHZ states, 2 noise~5 h incl. queue
CPUGHZ, Pauli propagation, all-Z192<Z…Z> = 1.0, certified (bound 0)< 1 min

11. API surface (v0)

  • Client(base_url, token): construct once, reuse.
  • client.estimate(circuit, shots): free feasibility + cost forecast.
  • client.run(circuit, shots, engine=None, **params): submit and wait; raises JobRejected / JobFailed.
  • client.submit(...) / client.job(job_id): non-blocking variant.

The SDK is a thin wrapper over a plain HTTP API, so any language can call it directly. The full specification is published in machine-readable form, browsable interactively at api.zksf.org/docs or as raw OpenAPI 3.1 at openapi.json. Certificates are readable as JSON at /certify/{cert_id}/json, alongside the HTML verification page and the PDF, so a third party can check one programmatically rather than by eye.

Questions, bugs, or a circuit you think we should handle but don’t? info@zksf.org. Benchmark challenges welcome.

12. Mobile app (Android)

The ZKSF Android app runs the same platform from a phone, signed in with the same account as the web console, so your jobs, history, and balance stay in sync across both. It is a native app, not a wrapped web page.

What you can do from the app:

  • Run circuits. Paste or upload an OpenQASM 2.0 file (.qasm or .txt), choose an engine (CPU, GPU, or a real Rigetti or IonQ QPU), estimate the cost for free, then run and read the certified result.
  • Error mitigation. Opt into zero-noise error mitigation on a hardware run, returning a mitigated value with a ZCC-Estimate-v0.1 uncertainty (it submits three hardware runs).
  • Job history and certificates. Review past jobs and export the same public, verifiable certificates as on the web.
  • Read offline. The Research articles with their interactive explainer videos, the Dictionary, and these Support Docs are all in the app; the text is offline and the videos stream on tap.

The app is for running one circuit at a time by hand. For programmatic use, parameter sweeps, or wiring results into a larger script, install the qsim-sdk Python package on a desktop or laptop; it cannot run on a phone.

Get it on Google Play

13. Circuit templates (a beginner’s starting point)

Plenty of people want to run a real quantum circuit before they have learned to write OpenQASM. The console ships with six ready-made circuits for exactly that, chosen to sit where the textbook meets real use. Open the app, pick one from the Start from a template menu above the editor, and it loads the circuit, selects the right engine, and fills in any observable it needs.

How to use them, start to finish:

  • Choose a template. The OpenQASM loads into the editor and a live circuit diagram appears beneath it.
  • Read the diagram to see the structure: which gates act on which qubits, and in what order.
  • Press Estimate (free) to see the predicted engine and cost, which for these is a fraction of a cent.
  • Press Run circuit, then read the result and download its certificate.
  • Edit the QASM freely to make it your own. The diagram redraws as you type, so you always see what you are about to run.
TemplateWhat it doesEngineWalkthrough
GHZ / Bell stateEntangles three qubits so they always agree, the hello-world of entanglement.clifford (exact)Read
Grover searchFinds a marked item in an unsorted space; one iteration finds it with certainty.clifford (exact)Read
QAOA MaxCutSplits a four-node ring to cut the most links; real combinatorial optimization.exact.cpuRead
VQE, H2 moleculeFinds hydrogen’s ground-state energy by tuning one angle; lands on -1.137 Ha.pauli.cpuRead
Bernstein-VaziraniReads a hidden bitstring in a single query where a classical computer needs one per bit.clifford (exact)Read
Quantum teleportationMoves a qubit’s state across the circuit using entanglement and corrections alone.exact.cpuRead

Every template runs on the same engines, billing, and certificates as any other job, so nothing you learn here is a toy sandbox: change the circuit, scale it up, and it keeps working. If you are brand new, GHZ is the gentlest place to begin.

14. Citing ZKSF

If a result produced here appears in published work, cite the archived client. The DOI below is the concept DOI: it always resolves to the most recent release, so it does not go stale.

10.5281/zenodo.21836619

The full record, including per-version DOIs, is at doi.org/10.5281/zenodo.21836619. To pin an exact release for reproducibility, use that release’s own version DOI from the record page rather than the concept DOI. The repository also ships a CITATION.cff, so GitHub’s “Cite this repository” button produces BibTeX and APA directly.

Individual jobs are separately citable: every certificate has a permanent public URL under api.zksf.org/certify/ that resolves without an account, so a reviewer can check the exact accuracy statement behind a specific number rather than taking the figure on trust.