ATOM LOSS Envelope matching · Beta
Decode with
atom loss.
Run your first loss-aware decoding experiment.
One circuit, 64 shots, four steps from sample to score.
This tutorial uses envelope matching, Beta unless publication verification succeeds, within its verified Mid-SWAP scope. Envelope MLE is separately Beta unless publication verification succeeds, with a declared scope limited to four exact measured points and requiring ILP; this walkthrough is one generated Mid-SWAP case. Supported circuits →
The experiment at a glance
Keep the loss flag alongside each measurement. The decoder uses that information to predict the logical outcome.
- 01Generatecircuit.stim
- 02Samplepublic + private
- 03Decodepublic → predictions
- 04Checkpredictions + answers
Data boundary Only public records enter the decoder. Private answers are used at the final scoring step.
Before you start: install & working directory
Install the CLI below, or use a native binary. The default CLI includes envelope matching; no optional ILP solver is needed.
cargo install --locked rustqec-cli --version 0.3.0
Run the steps in order in a fresh directory, with rustqec on PATH and Python 3 installed. The example ZIP contains run.sh, check.py and a README; after extracting it, run sh run.sh to execute all four steps.
This site is Development · master; the installation command pins v0.3.0. Version details.
STEP 01 / 04
Generate the circuit
Create a distance-3 Mid-SWAP memory circuit. Set Pauli noise and atom-loss probabilities separately.
rustqec circuit gen \
--code surface_code --task rotated_memory_z_midswap \
--distance 3 --rounds 2 --noise 0.002 \
--operation-loss-probability 0.002 \
--measurement-loss-probability 0.003 \
--out circuit.stim
Creates circuit.stim — the circuit used throughout this tutorial.
STEP 02 / 04
Sample 64 shots
Export loss-visible measurements and keep the answer key in a separate bundle.
mkdir -p data
rustqec 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
data/public/
Circuit, manifest and measurement rows in shots.b8.
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.
STEP 03 / 04
Decode public records
Envelope matching reads the public bundle and predicts one logical outcome per shot.
rustqec 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.
STEP 04 / 04
Check the predictions
Save check.py ↓ in your working directory (also included in the example ZIP), then run:
python3 check.py
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
Decoded shots: 64
Loss patterns: 10
Logical errors: 0 / 64
You have completed the full circuit → sample → decode → score workflow.
Interpretation Zero errors in 64 shots checks this example. It does not establish a zero logical error rate.
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.