QEC messages
Stim as a framework
Section titled “Stim as a framework”Stim stabilizer circuits are a first-class kernel framework, reachable two ways:
- Python mode — code that
import stimand buildsstim.Circuitobjects runs through the ordinaryparse/executepipeline (framework detection, last-circuit-in-namespace,params/seed/record_metricall work). This is the power path for parameterized experiments. - Raw
.stimtext — sendlanguage: "stim"and thecodefield is parsed withstim.Circuit(text)directly, never through Pythonexec(the same source-mode routing Q# uses). This is the interchange path: researchers share bare.stimfiles constantly.
Simulation uses Stim’s compiled sampler. An explicit integer seed is
always honored (seed_honored: true) — with Stim’s documented scope:
same seed + same stim version + same machine reproduces identical
samples, but results can differ across CPUs (SIMD width) and stim
versions. Manifests remain honest either way. Because stabilizer circuits
routinely exceed statevector sizes, the result is honest about being a
sampler: state_vector, probabilities, and bloch_coords are empty,
and measurements keys are measurement-record bitstrings (first recorded
measurement rightmost, matching the Python frameworks’ bit order).
qec_generate
Section titled “qec_generate”Generates one of Stim’s built-in QEC circuits and returns its text. The desktop app writes the result into a real project file — the circuit you run is always a file you can read and edit, never hidden state.
{ "type": "qec_generate", "code": "repetition_code:memory", "distance": 3, "rounds": 2, "noise": { "before_measure_flip_probability": 0.01 }}[ { "type": "qec_generated", "code": "repetition_code:memory", "distance": 3, "rounds": 2, "circuit_text": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nSHIFT_COORDS(0, 1)\nDETECTOR(1, 0) rec[-2] rec[-4]\nDETECTOR(3, 0) rec[-1] rec[-3]\nX_ERROR(0.01) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]" }]Request fields:
| Field | Type | Notes |
|---|---|---|
code | string | One of repetition_code:memory, surface_code:rotated_memory_x, surface_code:rotated_memory_z, surface_code:unrotated_memory_x, surface_code:unrotated_memory_z, color_code:memory_xyz |
distance | int ≥ 2 | Code distance (stim itself rejects invalid combinations, e.g. even distances for surface codes) |
rounds | int ≥ 1 | Syndrome-extraction rounds |
noise | object, optional | Any of stim’s generator noise arguments: after_clifford_depolarization, before_round_data_depolarization, before_measure_flip_probability, after_reset_flip_probability — each a probability in [0, 1] |
Validation failures answer with an error of code qec_generate_invalid
whose message is safe to show verbatim. A kernel without Stim installed
answers missing_dependency (install it from the setup wizard’s Research
group).
The qec_snapshot sidecar
Section titled “The qec_snapshot sidecar”When parse or execute produces a Stim circuit, one extra response
follows the snapshot (and precedes the result): the QEC sidecar with
detector/observable counts, qubit and detector coordinates, and the
Detector Error Model reduced to a graph.
{ "type": "qec_snapshot", "data": { "num_qubits": 5, "num_detectors": 4, "num_observables": 1, "num_ticks": 6, "coords": { "qubits": [[1.0, 0.0], null, "…"], "detectors": [[1.0, 0.0, 0.0], "…"] }, "dem": { "nodes": 4, "edge_count": 5, "boundary_edge_count": 2, "hyperedges_count": 0, "truncated": false, "edges": [{ "d1": 0, "d2": 1, "obs": [], "p": 0.01 }], "boundary_edges": [{ "d": 2, "obs": [0], "p": 0.0066 }] }, "sample_decode": null }}Reading it honestly:
- The DEM is extracted with
decompose_errors=True, so edges are the matchable components decoders like PyMatching consume. Parallel error mechanisms on the same detector pair are merged with the XOR rulep = p₁(1−p₂) + p₂(1−p₁). - Circuits whose errors don’t decompose (non-matchable codes) fall back to
the undecomposed model with
hyperedges_count > 0— render the pairwise projection with a badge, don’t pretend it’s a matching graph. - Circuits with no valid DEM at all (e.g. a non-deterministic observable)
keep their stats but carry
dem: nullplus adem_errorstring. - Payload cap: above 5,000 total edges the lists are emptied,
truncated: true, and the counts remain — never a silently missing graph. Re-request with a higher cap to render anyway (below). dem_text(present only whentruncated: true): the flattened DEM as text (str(dem.flattened())). The frontend parses it client-side (a WASM parser) to render the full graph with no kernel-side edge cap — so arbitrarily large detector graphs render without the kernel serializing a huge edge list. Absent when the graph fits, so the common case pays nothing.coordsarrays are index-aligned (entry i = qubit/detector i),nullwhere stim has no coordinates.sample_decodeis reserved for the campaign protocol increment.
qec_snapshot as a request
Section titled “qec_snapshot as a request”The same payload is available on demand — with code (stateless, any
.stim text or stim-building Python), or without code to reuse the last
Stim circuit this connection parsed or executed, optionally at a different
DEM cap. That second form is the graph view’s “render anyway” button.
The full loop — generate, execute (watch the sidecar arrive between
snapshot and result), then re-request the DEM at a deliberately tiny
cap to see truncation:
[ { "request": { "type": "qec_generate", "code": "repetition_code:memory", "distance": 3, "rounds": 2, "noise": { "before_measure_flip_probability": 0.01 } }, "responses": [ { "type": "qec_generated", "code": "repetition_code:memory", "distance": 3, "rounds": 2, "circuit_text": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nSHIFT_COORDS(0, 1)\nDETECTOR(1, 0) rec[-2] rec[-4]\nDETECTOR(3, 0) rec[-1] rec[-3]\nX_ERROR(0.01) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]" } ] }, { "request": { "type": "execute", "code": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nSHIFT_COORDS(0, 1)\nDETECTOR(1, 0) rec[-2] rec[-4]\nDETECTOR(3, 0) rec[-1] rec[-3]\nX_ERROR(0.01) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]", "shots": 128, "language": "stim", "seed": 42 }, "responses": [ { "type": "snapshot", "data": { "framework": "stim", "qubit_count": 5, "classical_bit_count": 7, "depth": 7, "gates": [ { "type": "Reset", "targets": [ 0 ], "controls": [], "params": [], "layer": 0 }, { "type": "Reset", "targets": [ 1 ], "controls": [], "params": [], "layer": 0 }, { "type": "Reset", "targets": [ 2 ], "controls": [], "params": [], "layer": 0 }, { "type": "Reset", "targets": [ 3 ], "controls": [], "params": [], "layer": 0 }, { "type": "Reset", "targets": [ 4 ], "controls": [], "params": [], "layer": 0 }, { "type": "CNOT", "targets": [ 1 ], "controls": [ 0 ], "params": [], "layer": 1 }, { "type": "CNOT", "targets": [ 3 ], "controls": [ 2 ], "params": [], "layer": 1 }, { "type": "CNOT", "targets": [ 1 ], "controls": [ 2 ], "params": [], "layer": 2 }, { "type": "CNOT", "targets": [ 3 ], "controls": [ 4 ], "params": [], "layer": 2 }, { "type": "NOISE:X_ERROR", "targets": [ 1 ], "controls": [], "params": [ 0.01 ], "layer": 3 }, { "type": "NOISE:X_ERROR", "targets": [ 3 ], "controls": [], "params": [ 0.01 ], "layer": 3 }, { "type": "MR", "targets": [ 1 ], "controls": [], "params": [], "layer": 3 }, { "type": "MR", "targets": [ 3 ], "controls": [], "params": [], "layer": 3 }, { "type": "DETECTOR", "targets": [], "controls": [], "params": [ 1.0, 0.0 ], "layer": 3 }, { "type": "DETECTOR", "targets": [], "controls": [], "params": [ 3.0, 0.0 ], "layer": 3 }, { "type": "CNOT", "targets": [ 1 ], "controls": [ 0 ], "params": [], "layer": 4 }, { "type": "CNOT", "targets": [ 3 ], "controls": [ 2 ], "params": [], "layer": 4 }, { "type": "CNOT", "targets": [ 1 ], "controls": [ 2 ], "params": [], "layer": 5 }, { "type": "CNOT", "targets": [ 3 ], "controls": [ 4 ], "params": [], "layer": 5 }, { "type": "NOISE:X_ERROR", "targets": [ 1 ], "controls": [], "params": [ 0.01 ], "layer": 6 }, { "type": "NOISE:X_ERROR", "targets": [ 3 ], "controls": [], "params": [ 0.01 ], "layer": 6 }, { "type": "MR", "targets": [ 1 ], "controls": [], "params": [], "layer": 6 }, { "type": "MR", "targets": [ 3 ], "controls": [], "params": [], "layer": 6 }, { "type": "DETECTOR", "targets": [], "controls": [], "params": [ 1.0, 1.0 ], "layer": 6 }, { "type": "DETECTOR", "targets": [], "controls": [], "params": [ 3.0, 1.0 ], "layer": 6 }, { "type": "NOISE:X_ERROR", "targets": [ 0 ], "controls": [], "params": [ 0.01 ], "layer": 6 }, { "type": "NOISE:X_ERROR", "targets": [ 2 ], "controls": [], "params": [ 0.01 ], "layer": 6 }, { "type": "NOISE:X_ERROR", "targets": [ 4 ], "controls": [], "params": [ 0.01 ], "layer": 6 }, { "type": "Measure", "targets": [ 0 ], "controls": [], "params": [], "layer": 6 }, { "type": "Measure", "targets": [ 2 ], "controls": [], "params": [], "layer": 6 }, { "type": "Measure", "targets": [ 4 ], "controls": [], "params": [], "layer": 6 }, { "type": "DETECTOR", "targets": [], "controls": [], "params": [ 1.0, 2.0 ], "layer": 6 }, { "type": "DETECTOR", "targets": [], "controls": [], "params": [ 3.0, 2.0 ], "layer": 6 }, { "type": "OBSERVABLE", "targets": [], "controls": [], "params": [ 0.0 ], "layer": 6 } ] } }, { "type": "qec_snapshot", "data": { "num_qubits": 5, "num_detectors": 6, "num_observables": 1, "num_ticks": 6, "coords": { "qubits": [ null, null, null, null, null ], "detectors": [ [ 1.0, 0.0, 0.0 ], [ 3.0, 0.0, 0.0 ], [ 1.0, 1.0, 0.0 ], [ 3.0, 1.0, 0.0 ], [ 1.0, 2.0, 0.0 ], [ 3.0, 2.0, 0.0 ] ] }, "dem": { "nodes": 6, "edge_count": 5, "boundary_edge_count": 2, "hyperedges_count": 0, "truncated": false, "edges": [ { "d1": 0, "d2": 2, "obs": [], "p": 0.01 }, { "d1": 1, "d2": 3, "obs": [], "p": 0.01 }, { "d1": 2, "d2": 4, "obs": [], "p": 0.01 }, { "d1": 3, "d2": 5, "obs": [], "p": 0.01 }, { "d1": 4, "d2": 5, "obs": [], "p": 0.01 } ], "boundary_edges": [ { "d": 4, "obs": [], "p": 0.01 }, { "d": 5, "obs": [ 0 ], "p": 0.01 } ] }, "sample_decode": null } }, { "type": "result", "data": { "state_vector": [], "probabilities": {}, "measurements": "<any>", "bloch_coords": [], "execution_time_ms": "<any>", "shot_count": 128, "metrics": {}, "seed_honored": true } } ] }, { "request": { "type": "qec_snapshot", "max_edges": 5 }, "responses": [ { "type": "qec_snapshot", "data": { "num_qubits": 5, "num_detectors": 6, "num_observables": 1, "num_ticks": 6, "coords": { "qubits": [ null, null, null, null, null ], "detectors": [ [ 1.0, 0.0, 0.0 ], [ 3.0, 0.0, 0.0 ], [ 1.0, 1.0, 0.0 ], [ 3.0, 1.0, 0.0 ], [ 1.0, 2.0, 0.0 ], [ 3.0, 2.0, 0.0 ] ] }, "dem": { "nodes": 6, "edge_count": 5, "boundary_edge_count": 2, "hyperedges_count": 0, "truncated": true, "edges": [], "boundary_edges": [] }, "sample_decode": null, "dem_text": "<any>" } } ] }]qec_estimate
Section titled “qec_estimate”Azure Quantum Resource Estimator over a circuit, wrapping the estimator that
ships in qdk. language: "qsharp" compiles the Q# source and estimates its
entry expression; language: "qasm3" estimates OpenQASM 3 (e.g.
qiskit.qasm3.dumps of the active qiskit circuit). options carries a qubit
model preset, a QEC scheme, and an error budget; the reply’s data leads with
the headline numbers (physical qubits, runtime, code distance, T-factory
count) plus the estimator’s pre-formatted strings and the full document for
export. Available for any framework — not just stim.
{ "type": "qec_estimate", "language": "qsharp", "code": "operation Main() : Result {\n use qs = Qubit[2];\n H(qs[0]);\n CNOT(qs[0], qs[1]);\n T(qs[0]);\n let r = M(qs[0]);\n ResetAll(qs);\n return r;\n}\n", "options": { "qubit_params": "qubit_gate_ns_e3", "qec_scheme": "surface_code", "error_budget": 0.001 }}[ { "type": "qec_estimate_result", "data": { "physical_qubits": "<any>", "runtime_ns": "<any>", "rqops": "<any>", "code_distance": "<any>", "logical_error_rate": "<any>", "num_tfactories": "<any>", "physical_qubits_algorithm": "<any>", "physical_qubits_tfactories": "<any>", "qubit_params": "<any>", "qec_scheme": "<any>", "formatted": "<any>", "full": "<any>" } }]Campaigns
Section titled “Campaigns”A campaign is a sinter Monte Carlo collection — tasks (circuit × decoder), adaptive shot allocation, multiprocess workers — wrapped as a managed kernel job. This is the deliberate exception to the “frontend orchestrates, kernel stays dumb” rule: sinter is the orchestrator.
qec_campaign_start
Section titled “qec_campaign_start”{ "type": "qec_campaign_start", "campaign_id": "surface-vs-repetition", "tasks": [ { "circuit_text": "R 0 1 2 3 4\n…", "decoder": "pymatching", "json_metadata": { "code": "repetition", "d": 3, "p": 0.02 } } ], "collect": { "max_shots": 50000, "max_errors": 500 }, "workers": "auto", "progress_interval_s": 1.0, "existing_stats_csv": " shots, errors, …"}- At least one of
collect.max_shots/collect.max_errorsis required — an unbounded campaign never terminates. workers: "auto"(default) means CPU count − 1. Task count is capped at 2,000 (an explicit error above — sinter’s adaptive allocation makes large grids sane, but not unbounded ones).- One campaign at a time per kernel: a second start answers an
errorwith codecampaign_already_running(409-style) naming the running id. - Resume: pass the previous run’s
csvtext asexisting_stats_csv. It loads through sinter’s ownexisting_datamachinery, counts toward the collection bounds, and completed tasks are never re-sampled — the result’ssampled_shotsreports newly collected shots only (0 for an already-complete campaign). Kernel restarts kill workers by design; resume is what makes that acceptable. - No
seedfield.sinter.collecthas no seeding API — Monte Carlo campaigns are not shot-for-shot reproducible, and the protocol refuses to imply otherwise. (Single runs andqec_decode_sampledo seed.)
The ack is qec_campaign_started {campaign_id, tasks_total, workers};
progress and the final result then stream on the same connection:
qec_campaign_progress(throttled — at most one perprogress_interval_s, default 1s):tasksholds accumulated totals for tasks whose numbers changed since the last update (merge client-side bystrong_id), plustasks_complete/tasks_totaland sinter’s free-formstatus_message(its ETA text, version-dependent — display, don’t parse).qec_campaign_result:partial(true after cancel or a worker failure),sampled_shots,statsrows, andcsv— the stats in sinter’s native CSV format, bit-for-bit loadable by existing researcher scripts. Write it to disk as-is; never invent a private format.
A validation failure is a plain error (code qec_campaign_invalid)
naming exactly what’s wrong — this one omitted the collection bounds:
{ "type": "qec_campaign_start", "campaign_id": "demo", "tasks": [ { "circuit_text": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.02) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nREPEAT 2 {\n TICK\n CX 0 1 2 3\n TICK\n CX 2 1 4 3\n TICK\n X_ERROR(0.02) 1 3\n MR 1 3\n SHIFT_COORDS(0, 1)\n DETECTOR(1, 0) rec[-2] rec[-4]\n DETECTOR(3, 0) rec[-1] rec[-3]\n}\nX_ERROR(0.02) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]", "decoder": "pymatching", "json_metadata": { "d": 3 } } ]}[ { "type": "error", "message": "at least one of collect.max_shots / collect.max_errors is required \u2014 an unbounded campaign never terminates", "code": "qec_campaign_invalid", "phase": "qec_campaign" }]qec_campaign_cancel
Section titled “qec_campaign_cancel”{type: "qec_campaign_cancel", campaign_id} → immediate ack
qec_campaign_cancelled {campaign_id, accepted}, then a graceful sinter
shutdown: everything already collected (plus any resumed prior data) comes
back as a qec_campaign_result with "partial": true.
A full captured session — start, one progress update, cancel, partial
result. Illustrative (timing-dependent message counts; the semantics are
pinned by kernel/tests/test_qec_campaign.py instead):
[ { "request": { "type": "qec_campaign_start", "campaign_id": "surface-vs-repetition", "tasks": [ { "circuit_text": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.02) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nREPEAT 2 {\n TICK\n CX 0 1 2 3\n TICK\n CX 2 1 4 3\n TICK\n X_ERROR(0.02) 1 3\n MR 1 3\n SHIFT_COORDS(0, 1)\n DETECTOR(1, 0) rec[-2] rec[-4]\n DETECTOR(3, 0) rec[-1] rec[-3]\n}\nX_ERROR(0.02) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]", "decoder": "pymatching", "json_metadata": { "code": "repetition", "d": 3, "p": 0.02, "i": 0 } }, { "circuit_text": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.02) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nREPEAT 2 {\n TICK\n CX 0 1 2 3\n TICK\n CX 2 1 4 3\n TICK\n X_ERROR(0.02) 1 3\n MR 1 3\n SHIFT_COORDS(0, 1)\n DETECTOR(1, 0) rec[-2] rec[-4]\n DETECTOR(3, 0) rec[-1] rec[-3]\n}\nX_ERROR(0.02) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]", "decoder": "pymatching", "json_metadata": { "code": "repetition", "d": 3, "p": 0.02, "i": 1 } } ], "collect": { "max_shots": 50000, "max_errors": 500 }, "workers": 2, "progress_interval_s": 0.3 }, "responses": [ { "type": "qec_campaign_started", "campaign_id": "surface-vs-repetition", "tasks_total": 30, "workers": 2 }, { "type": "qec_campaign_progress", "campaign_id": "surface-vs-repetition", "tasks": [ { "strong_id": "533523ae9d5717332fe8905449e21bbbdee0562c4ffa0bb2ca99066612a9d80b", "decoder": "pymatching", "json_metadata": { "code": "repetition", "d": 3, "p": 0.02, "i": 1 }, "shots": 50000, "errors": 37, "discards": 0, "seconds": 0.004, "custom_counts": {} } ], "tasks_complete": 1, "tasks_total": 30, "status_message": "29 tasks left:\n workers decoder eta shots_left errors_left json_metadata \n 1 pymatching ? 50000 500 code=repetition,d=3,p=0.02,i=0 \n 1 pymatching ? 50000 500 code=repetition,d=3,p=0.02,i=2 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=3 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=4 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=5 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=6 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=7 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=8 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=9 \n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=10\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=11\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=12\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=13\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=14\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=15\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=16\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=17\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=18\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=19\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=20\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=21\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=22\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=23\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=24\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=25\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=26\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=27\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=28\n 0 pymatching ?\u00b7\u221e 50000 500 code=repetition,d=3,p=0.02,i=29" } ] }, { "request": { "type": "qec_campaign_cancel", "campaign_id": "surface-vs-repetition" }, "responses": [ { "type": "qec_campaign_cancelled", "campaign_id": "surface-vs-repetition", "accepted": true }, { "type": "qec_campaign_result", "campaign_id": "surface-vs-repetition", "partial": true, "sampled_shots": 250000, "stats": [ { "strong_id": "533523ae9d5717332fe8905449e21bbbdee0562c4ffa0bb2ca99066612a9d80b", "decoder": "pymatching", "json_metadata": { "code": "repetition", "d": 3, "p": 0.02, "i": 1 }, "shots": 50000, "errors": 37, "discards": 0, "seconds": 0.004, "custom_counts": {} }, { "strong_id": "8c8ed2950a964d713970cb7ff54fe27c771bbd8c3c31908a1a95cbccbe259ce3", "decoder": "pymatching", "json_metadata": { "code": "repetition", "d": 3, "p": 0.02, "i": 0 }, "shots": 50000, "errors": 58, "discards": 0, "seconds": 0.004, "custom_counts": {} } ], "csv": " shots, errors, discards, seconds,decoder,strong_id,json_metadata,custom_counts\n 50000, 37, 0, 0.004,pymatching,533523ae9d5717332fe8905449e21bbbdee0562c4ffa0bb2ca99066612a9d80b,\"{\"\"code\"\":\"\"repetition\"\",\"\"d\"\":3,\"\"i\"\":1,\"\"p\"\":0.02}\",\n 50000, 58, 0, 0.004,pymatching,8c8ed2950a964d713970cb7ff54fe27c771bbd8c3c31908a1a95cbccbe259ce3,\"{\"\"code\"\":\"\"repetition\"\",\"\"d\"\":3,\"\"i\"\":0,\"\"p\"\":0.02}\",\n..." } ] }]qec_materialize
Section titled “qec_materialize”Campaigns whose source is a Python entry: file provide circuits via the
nuclei_circuits(noise) contract: the file defines
nuclei_circuits(noise: dict) -> dict[str, stim.Circuit], the runner sends
the file’s code plus one grid point’s resolved noise dict, and the kernel
returns {label: circuit_text} (qec_circuits). Arbitrary custom noise
stays possible — the user applies the dict however they like. Contract
violations (missing function, wrong return shape, non-Circuit values) are
qec_materialize_invalid errors with the exact complaint.
{ "type": "qec_materialize", "code": "import stim\n\ndef nuclei_circuits(noise):\n p = noise[\"p\"]\n return {\n \"rep_d3\": stim.Circuit.generated(\n \"repetition_code:memory\", distance=3, rounds=2,\n before_measure_flip_probability=p,\n ),\n }\n", "noise": { "p": 0.01 }}[ { "type": "qec_circuits", "circuits": { "rep_d3": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.01) 1 3\nMR 1 3\nSHIFT_COORDS(0, 1)\nDETECTOR(1, 0) rec[-2] rec[-4]\nDETECTOR(3, 0) rec[-1] rec[-3]\nX_ERROR(0.01) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]" } }]qec_decode_sample
Section titled “qec_decode_sample”One sampled shot, decoded — the detector-graph overlay’s “sample a shot”
button. Cheap, synchronous, and deterministic for a given seed. v1 decodes
with pymatching only (campaigns support every decoder sinter knows);
matched_edges pairs detector indices, with d2: null for boundary
matches.
{ "type": "qec_decode_sample", "circuit_text": "R 0 1 2 3 4\nTICK\nCX 0 1 2 3\nTICK\nCX 2 1 4 3\nTICK\nX_ERROR(0.02) 1 3\nMR 1 3\nDETECTOR(1, 0) rec[-2]\nDETECTOR(3, 0) rec[-1]\nREPEAT 2 {\n TICK\n CX 0 1 2 3\n TICK\n CX 2 1 4 3\n TICK\n X_ERROR(0.02) 1 3\n MR 1 3\n SHIFT_COORDS(0, 1)\n DETECTOR(1, 0) rec[-2] rec[-4]\n DETECTOR(3, 0) rec[-1] rec[-3]\n}\nX_ERROR(0.02) 0 2 4\nM 0 2 4\nDETECTOR(1, 1) rec[-2] rec[-3] rec[-5]\nDETECTOR(3, 1) rec[-1] rec[-2] rec[-4]\nOBSERVABLE_INCLUDE(0) rec[-1]", "decoder": "pymatching", "seed": 3}[ { "type": "qec_decode_sample", "num_detectors": 8, "syndrome": "<any>", "matched_edges": "<any>", "predicted_observable_flips": "<any>", "actual_observable_flips": "<any>" }]A concrete response, captured on one machine (values are seed-and-machine deterministic — like all stim sampling, they vary across CPUs, which is why the replayed fixture above pins the shape rather than the values):
{ "type": "qec_decode_sample", "num_detectors": 8, "syndrome": [5, 7], "matched_edges": [{ "d1": 5, "d2": 7 }], "predicted_observable_flips": [0], "actual_observable_flips": [0]}