RustQEC 0.3 · Development

The experiment at a glance

Atom loss removes a qubit until reset. A loss flag records whether its measurement is missing; a decoder uses these flags and surviving measurements to predict the logical flip.

  1. 01Generatecircuit.stim
  2. 02Samplepublic + private
  3. 03Decodepublic → predictions
  4. 04Checkpredictions + answers

Data boundary Only public records enter the decoder. Private answers are used at the final scoring step.

Run the downloaded example

The example ZIP contains run.sh, check.py and a README. Extract it, then run all four steps:

sh run.sh
Decoder support and scope

Envelope matching is Beta unless publication verification succeeds within its declared Mid-SWAP scope. This tutorial exercises one generated case. Decoder choices and exact support limits →

The loss model and stored record

LOSS(p) removes each present target independently with probability p. A lost atom stays absent until reset. Single-qubit gates skip it; a two-qubit gate is skipped when either partner is absent. These are the simulator's explicit model choices.

ML stores two bits per target: loss flag first, then value. A flag of 0 means the value is a measurement outcome. A flag of 1 means the stored value is a placeholder; it must not become ordinary syndrome evidence. MRL reports the same pair and then resets, restoring the atom.

cat > loss-record.stim <<'STIM'
R 0
LOSS(1) 0
ML 0
R 0
ML 0
STIM
rstim circuit sample --in loss-record.stim --shots 1 --seed 7 \
  --out-format 01 --out loss-record.01 > /dev/null
cat loss-record.01
1100

The first 11 is a lost flag plus placeholder. After reset, 00 is a present atom measured in |0⟩. This small example demonstrates sampling semantics; it does not meet the native decoder's full circuit contract.

A heralded loss readout identifies an interval in which loss occurred, rather than its exact physical time. Keep flags alongside values and use surviving checks. The instruction reference separates simulator semantics from the native decoder's Z-only, flat-circuit acceptance rules.

What the Mid-SWAP gadget changes

The generator alternates the order of four CX layers. After the first physical layer in each round it exchanges the logical data/check roles of the participating wires; later gates and readout use the updated mapping. Reading and resetting the current check roles helps expose losses that would otherwise remain on a data role.

Mid-SWAP round schedule: first CX layer, role exchange, remaining CX layers, and check-role loss-visible measurement and reset
This is a schedule diagram. Physical wire identities stay fixed; the generated circuit records role exchanges with MIDSWAP_SHUTTLE comments and does not insert a literal SWAP instruction.

The model keeps physical loss state attached to its wire until reset; changing a logical role does not itself restore a lost atom. The generated circuit below is the executable definition. Inspect it with rstim render_svg or Shot Lab to see actual gates and paired flag/value records.

1. Generate the circuit

Create a distance-3, two-round Mid-SWAP memory circuit. Mid-SWAP exchanges data/check roles during the checks so readout can reveal loss.

rstim circuit gen \
  --code surface_code --task rotated_memory_z_midswap \
  --distance 3 --rounds 2 --after-clifford-depolarization 0.002 \
  --operation-loss-probability 0.002 \
  --measurement-loss-probability 0.003 \
  --out circuit.stim

--after-clifford-depolarization sets Pauli noise after Clifford gates. --operation-loss-probability sets loss on gate targets; --measurement-loss-probability sets loss at readout. All four steps use circuit.stim.

2. Sample 64 shots

Export loss-visible measurements and keep the answer key in a separate bundle.

mkdir -p data
rstim dataset export \
  --circuit circuit.stim --shots 64 --seed 7 \
  --mode measurements_blinded --logical-x-qubits 1,8,15 \
  --public-out data/public --private-out data/private
DECODER INPUT

data/public/

Circuit, manifest and measurement rows in shots.b8.

SCORING ONLY

data/private/

Logical-error targets in answers.b8, input masks and a manifest.

Changing the layout? The logical-X support 1,8,15 is specific to this circuit. Recompute it and regenerate both bundles together.

3. Decode public records

Envelope matching builds a graph conditioned on the observed loss pattern, then predicts one logical flip per shot from the public records.

rstim decode --decoder envelope-matching \
  --dataset data/public \
  --out predictions.b8 --stats-out decode-stats.json

Creates predictions.b8 and decode-stats.json.

What is in the output?

The prediction file contains one byte per shot in this one-observable example. Statistics report shot counts, distinct loss patterns, compilation and decoding time, and failures.

Compare matching and exact MLE →

4. Check the predictions

Save check.py ↓ in your working directory (also included in the example ZIP), then run:

python3 check.py
Decoded shots: 64
Loss patterns: 10
Logical errors: 0 / 64

Each disagreement between a prediction and its private answer counts as one logical error.

Or copy the complete check into your terminal
python3 - <<'PY'
import json
from pathlib import Path

public = json.loads(Path('data/public/manifest.json').read_text())
private = json.loads(Path('data/private/manifest.json').read_text())
stats = json.loads(Path('decode-stats.json').read_text())
predictions = Path('predictions.b8').read_bytes()
answers = Path('data/private/answers.b8').read_bytes()
assert public['dataset_id'] == private['dataset_id'], 'Mismatched datasets'
assert public['circuit']['observables'] == 1, 'This check expects one observable'
assert (
    len(predictions) == len(answers) == public['shots'] == stats['shot_count']
), 'Incomplete rows'
assert all(bit in (0, 1) for bit in predictions + answers), 'Invalid padding bits'
errors = sum(predicted != answer for predicted, answer in zip(predictions, answers))
print(f"Decoded shots: {stats['shot_count']}")
print(f"Loss patterns: {stats['distinct_loss_patterns']}")
print(f"Logical errors: {errors} / {public['shots']}")
PY

Interpretation Zero errors in 64 shots checks this example. It does not establish a zero logical error rate.

Decoder choices and method sources

envelope-matching is included in the default CLI and uses matching on a loss-conditioned graph. envelope-mle requires the optional ilp build and searches for the most likely fault configuration in the implemented envelope model. It is not a claim of optimal decoding for an arbitrary physical noise model.

These implementations reproduce methods described in Liu et al., Achieving Optimal-Distance Atom-Loss Correction via Pauli Envelope (v2). That pinned version is the reproduction reference; subsequent paper revisions may differ. For delayed loss detection background, see Baranes et al., Leveraging Qubit Loss Detection in Fault Tolerant Quantum Algorithms. Algorithm derivations are left to the original papers.

Envelope matching is Beta unless publication verification succeeds; its release evidence is not published and verified yet. Envelope MLE is Beta unless publication verification succeeds; its release evidence is not published and verified yet.

Matching and MLE have independently verified release evidence and different supported workloads. See the support table and measured accuracy and timing. A decoder accepting a circuit does not establish a Supported claim for every distance, loss rate or batch size.

Troubleshooting & support

The decoder reports unsupported_circuit

The input falls outside the compiler’s acceptance rules. Start with the exact circuit above, then compare your changes with the support contract. A rejection is not a logical prediction.

The output differs from the example

Check the CLI version, seed 7, 64 shots, circuit parameters and decoder choice. Use a fresh directory and rerun all four steps. Do not score a failed or incomplete decoding run.

The check reports mismatched datasets or incomplete rows

Regenerate the public and private bundles together, then rerun decoding. Both manifests must share a dataset_id, and prediction, answer and statistics counts must agree. Do not combine files from different runs.

Should I apply the private input masks again?

No. The physical logical-error targets already account for the hidden logical-input masks. Compare predictions directly with answers.b8.

Take the next step

Inspect a circuit visually →