A Search-Based Decoder for Quantum Error Correction.
Installation – Usage – Python Interface – Paper – Help – Citation – Contact
Tesseract is a Most Likely Error decoder designed for Low Density Parity Check (LDPC) quantum error-correcting codes. It applies pruning heuristics and manifold orientation techniques during a search over the error subsets to identify the most likely error configuration consistent with the observed syndrome. Tesseract achieves significant speed improvements over traditional integer programming-based decoders while maintaining comparable accuracy at moderate physical error rates.
We tested the Tesseract decoder for:
- Surface codes
- Color codes
- Bivariate-bicycle codes
- Transversal CNOT protocols for surface codes
- A* search: deploys A* search while running a Dijkstra algorithm with early stop for high performance.
- Stim and DEM Support: processes Stim circuit files and Detector Error Model (DEM) files with arbitrary error models. Zero-probability error instructions are automatically removed when a DEM is loaded.
- Parallel Decoding: uses multithreading to accelerate the decoding process, making it suitable for large-scale simulations.
- Efficient Beam Search: implements a beam search
algorithm to minimize decoding cost and enhance efficiency.
Sampling and Shot Range Processing: supports sampling shots from circuits. When a detection
error model is provided without an accompanying circuit, Tesseract requires detection events from
files using
--in. The decoder can also process specific shot ranges for flexible experiment setups. - Detailed Statistics: provides comprehensive statistics output, including shot counts, error counts, and processing times.
- Heuristics: includes flexible heuristic options:
--beam,--det-penalty,--beam-climbing,--no-revisit-dets,--at-most-two-errors-per-detector, and--pqlimitto improve performance while maintaining a low logical error rate. To learn more about these options, use./bazel-bin/src/tesseract --help - Visualization tool: open the viz directory in your browser to view decoding results. See viz/README.md for instructions on generating the visualization JSON.
Tesseract relies on the following external libraries:
- argparse: For command-line argument parsing.
- nlohmann/json: For JSON handling (used for statistics output).
- Stim: For quantum circuit simulation and error model handling.
Tesseract uses Bazel as its build system. To build the decoder:
bazel build src:allUnit tests are executed with Bazel. Run the quick test suite using:
bazel test //src:allBy default the tests use reduced parameters and finish in under 30 seconds. To run a more exhaustive suite with additional shots and larger distances, set:
TESSERACT_LONG_TESTS=1 bazel test //src:allThe file tesseract_main.cc provides the main entry point for Tesseract Decoder. It can decode
error events from Stim circuits, DEM files, and pre-existing detection event files.
Basic Usage:
./tesseract --circuit CIRCUIT_FILE.stim --sample-num-shots N --print-statsTo decode pre-generated detection events, provide the input file using
--in SHOTS_FILE --in-format FORMAT.
Example with Advanced Options:
./tesseract \
--pqlimit 1000000 \
--no-revisit-dets \
--det-order-seed 232852747 \
--num-det-orders 24 \
--circuit circuit_file.stim \
--sample-seed 232856747 \
--sample-num-shots 10000 \
--threads 32 \
--print-stats \
--beam 23 --beam-climbing \
--shot-range-begin 582 \
--shot-range-end 583Sampling Shots from a Circuit:
./tesseract --circuit surface_code.stim --sample-num-shots 1000 --out predictions.01 --out-format 01Using a Detection Event File:
./tesseract --in events.01 --in-format 01 --dem surface_code.dem --out decoded.txtUsing a Detection Event File and Observable Flips:
./tesseract --in events.01 --in-format 01 --obs_in obs.01 --obs-in-format 01 --dem surface_code.dem --out decoded.txtTesseract supports reading and writing from all of Stim's standard output formats.
Here are some tips for improving performance:
- Parallelism over shots: increase
--threadsto leverage multicore processors for faster decoding. - Beam Search: use
--beamto control the trade-off between accuracy and speed. Smaller beam sizes result in faster decoding but potentially lower accuracy. - Beam Climbing: enable
--beam-climbingfor enhanced cost-based decoding. - At most two errors per detector: enable
--at-most-two-errors-per-detectorto improve performance. - Priority Queue limit: use
--pqlimitto limit the size of the priority queue. - Error sparsification: enable
--sparsify-errorsto always keep low-degree errors while selectively reactivating high-degree errors per shot. This can improve runtime on DEMs with many high-degree errors, at the cost of a tunable accuracy/speed tradeoff.
Example with error sparsification:
./tesseract \
--circuit circuit_file.stim \
--sample-num-shots 10000 \
--beam 20 \
--beam-climbing \
--sparsify-errors \
--sparsify-base-degree 3 \
--print-stats--sparsify-base-degree K is required when --sparsify-errors is enabled. Errors touching at most
K detectors are always active. Errors above K are optional and are ranked per shot by overlap
with the fired detectors. In the surface code (or other 'mostly graphlike' codes) try K = 2. In the
color code or bivariate bicycle codes, try K = 3. In general, it is recommended to set K to the number
of activated detectors created by a single data qubit error in the bulk, restricting to X or Z errors
only for CSS codes.
--sparsify-reactivate-limit M caps the number of optional high-degree errors reactivated per
shot. If omitted, Tesseract uses round((4.5^(K - 2) / 3) * num_detectors).
--sparsify-max-degree D optionally excludes optional errors above degree D. If omitted, optional
errors are not capped by degree.
- Observable flips output: predictions of logical errors.
- DEM usage frequency output: if
--dem-outis specified, outputs estimated error frequencies. - Statistics output: includes number of shots, errors, low confidence shots, and processing time.
Full Python wrapper documentation
This repository contains the C++ implementation of the Tesseract quantum error correction decoder, along with a Python wrapper. The Python wrapper/interface exposes the decoding algorithms and helper utilities, allowing Python users to leverage this high-performance decoding algorithm.
For installation:
pip install tesseract-decoderThe following example demonstrates how to create and use the Tesseract decoder using the Python interface.
from tesseract_decoder import tesseract
import stim
import numpy as np
# 1. Define a detector error model (DEM)
dem = stim.DetectorErrorModel("""
error(0.1) D0 D1 L0
error(0.2) D1 D2 L1
detector(0, 0, 0) D0
detector(1, 0, 0) D1
detector(2, 0, 0) D2
""")
# 2. Create the decoder configuration
config = tesseract.TesseractConfig(dem=dem, det_beam=50)
# To enable sparse activation for high-degree DEMs:
config = tesseract.TesseractConfig(
dem=dem,
det_beam=50,
sparsify_errors=True,
sparsify_base_degree=3,
sparsify_reactivate_limit=-1, # Use the built-in heuristic, clamped to error count.
)
# 3. Create a decoder instance
decoder = config.compile_decoder()
print(
"Resolved sparsify reactivation limit:",
decoder.config.sparsify_reactivate_limit,
)
# 4. Simulate detector outcomes
syndrome = np.array([0, 1, 1], dtype=bool)
# 5a. Decode to observables
flipped_observables = decoder.decode(syndrome)
print(f"Flipped observables: {flipped_observables}")
# 5b. Alternatively, decode to errors
decoder.decode_to_errors(syndrome)
predicted_errors = decoder.predicted_errors_buffer
# Indices of predicted errors
print(f"Predicted errors indices: {predicted_errors}")
# Print properties of predicted errors
for i in predicted_errors:
print(f" {i}: {decoder.errors[i]}")Tesseract can be easily integrated into Sinter workflows. Sinter is a tool for running and organizing quantum error correction simulations.
Here's an example of how to use Tesseract as a decoder for multiple Sinter tasks:
import stim
import sinter
from tesseract_decoder import make_tesseract_sinter_decoders_dict, TesseractSinterDecoder
import tesseract_decoder
if __name__ == "__main__":
# Define a list of Sinter task(s) with different circuits/decoders.
tasks = []
# Depolarizing noise probability.
p = 0.005
# These are the sensible defaults given by make_tesseract_sinter_decoders_dict().
# Note that `tesseract-short-beam` and `tesseract-long-beam` are the two sets of parameters used in the [Tesseract paper](https://arxiv.org/pdf/2503.10988).
decoders = [
'tesseract',
'tesseract-long-beam',
'tesseract-short-beam',
'tesseract-long-beam-sparsify-color-code-like',
'tesseract-short-beam-sparsify-surface-code-like',
]
decoder_dict = make_tesseract_sinter_decoders_dict()
# You can also make your own custom Tesseract Decoder to-be-used with Sinter.
decoders.append('custom-tesseract-decoder')
decoder_dict['custom-tesseract-decoder'] = TesseractSinterDecoder(
det_beam=10,
beam_climbing=True,
no_revisit_dets=True,
merge_errors=True,
pqlimit=1_000,
num_det_orders=5,
det_order_method=tesseract_decoder.utils.DetOrder.DetIndex,
seed=2384753,
sparsify_errors=True,
sparsify_base_degree=3,
sparsify_reactivate_limit=-1,
)
for distance in [3, 5, 7]:
for decoder in decoders:
circuit = stim.Circuit.generated(
"surface_code:rotated_memory_x",
distance=distance,
rounds=3,
after_clifford_depolarization=p
)
tasks.append(sinter.Task(
circuit=circuit,
decoder=decoder,
json_metadata={"d": distance, "decoder": decoder},
))
# Collect decoding outcomes per task from Sinter.
results = sinter.collect(
num_workers=8,
tasks=tasks,
max_shots=10_000,
decoders=decoders,
custom_decoders=decoder_dict,
print_progress=True,
)
# Print samples as CSV data.
print(sinter.CSV_HEADER)
for sample in results:
print(sample.to_csv_line())should get something like:
shots, errors, discards, seconds,decoder,strong_id,json_metadata,custom_counts
10000, 42, 0, 0.071,tesseract,1b3fce6286e438f38c00c8f6a5005947373515ab08e6446a7dd9ecdbef12d4cc,"{""d"":3,""decoder"":""tesseract""}",
10000, 49, 0, 0.546,custom-tesseract-decoder,7b082bec7541be858e239d7828a432e329cd448356bbdf051b8b8aa76c86625a,"{""d"":3,""decoder"":""custom-tesseract-decoder""}",
10000, 13, 0, 7.64,tesseract-long-beam,217a3542f56319924576658a6da7081ea2833f5167cf6d77fbc7071548e386a9,"{""d"":5,""decoder"":""tesseract-long-beam""}",
10000, 14, 0, 4.12,tesseract-long-beam-sparsify-color-code-like,14fa5f9f08381d760f6c1f59805b75f2c70cfb83e50d9f1f40d92820a20eeb13,"{""d"":5,""decoder"":""tesseract-long-beam-sparsify-color-code-like""}",
10000, 42, 0, 0.743,tesseract-short-beam,cf4a4b0ce0e4c7beec1171f58eddffe403ed7359db5016fca2e16174ea577057,"{""d"":3,""decoder"":""tesseract-short-beam""}",
10000, 34, 0, 0.924,tesseract-long-beam,8cfa0f2e4061629e13bc98fe213285dc00eb90f21bba36e08c76bcdf213a1c09,"{""d"":3,""decoder"":""tesseract-long-beam""}",
10000, 35, 0, 0.681,tesseract-long-beam-sparsify-color-code-like,f41bdb1bde3f5cf4893a9a9e33fc7d4c47d742f22b13dfec9195347e780119bc,"{""d"":3,""decoder"":""tesseract-long-beam-sparsify-color-code-like""}",
10000, 10, 0, 0.439,tesseract,8274ea5ffec15d6e71faed5ee1057cdd7e497cbaee4c6109784f8a74669d7f96,"{""d"":5,""decoder"":""tesseract""}",
10000, 8, 0, 3.93,custom-tesseract-decoder,8e4f5ab5dde00fec74127eea39ea52d5a98ae6ccfc277b5d9be450f78acc1c45,"{""d"":5,""decoder"":""custom-tesseract-decoder""}",
10000, 10, 0, 5.74,tesseract-short-beam,bf696535d62a25720c3a0c624ec5624002efe3f6cb0468963eee702efb48abc1,"{""d"":5,""decoder"":""tesseract-short-beam""}",
10000, 5, 0, 1.27,tesseract,3f94c61f1503844df6cf0d200b74ac01bfbc5e29e70cedbfc2faad67047e7887,"{""d"":7,""decoder"":""tesseract""}",
10000, 4, 0, 25.0,tesseract-long-beam,4d510f0acf511e24a833a93c956b683346696d8086866fadc73063fb09014c23,"{""d"":7,""decoder"":""tesseract-long-beam""}",
10000, 4, 0, 14.8,tesseract-long-beam-sparsify-color-code-like,80868acc6e43c62cb73b242b66ae27d3ea08fe970ea879db5a8425c2454fc8a1,"{""d"":7,""decoder"":""tesseract-long-beam-sparsify-color-code-like""}",
10000, 1, 0, 18.6,tesseract-short-beam,75782ce4593022fcedad4c73104711f05c9c635db92869531f78da336945b121,"{""d"":7,""decoder"":""tesseract-short-beam""}",
10000, 4, 0, 11.6,custom-tesseract-decoder,48f256a28fff47c58af7bffdf98fdee1d41a721751ee965c5d3c5712ac795dc8,"{""d"":7,""decoder"":""custom-tesseract-decoder""}",
This example runs simulations for a repetition code with different distances [3, 5, 7] with different Tesseract default decoders.
Sinter can also be used at the command line. Here is an example of this using Tesseract:
sinter collect \
--circuits "example_circuit.stim" \
--decoders tesseract \
--custom_decoders_module_function "tesseract_decoder:make_tesseract_sinter_decoders_dict" \
--max_shots 100_000 \
--max_errors 100
--processes auto \
--save_resume_filepath "stats.csv" \Sinter efficiently manages the execution of these tasks, and Tesseract is used for decoding. For more usage examples, see the tests in src/py/tesseract_sinter_compat_test.py.
The Tesseract paper recommends two setup for starting your exploration with tesseract:
(1) Long-beam setup:
tesseract_config = tesseract.TesseractConfig(
dem=dem,
pqlimit=1_000_000,
det_beam=20,
beam_climbing=True,
det_orders=tesseract_decoder.utils.build_det_orders(
dem=dem,
num_det_orders=21,
method=tesseract_decoder.utils.DetOrder.DetIndex,
),
no_revisit_dets=True,
)
(2) Short-beam setup:
tesseract_config = tesseract.TesseractConfig(
dem=dem,
pqlimit=200_000,
det_beam=15,
beam_climbing=True,
det_orders=tesseract_decoder.utils.build_det_orders(
dem=dem,
num_det_orders=16,
method=tesseract_decoder.utils.DetOrder.DetIndex,
),
no_revisit_dets=True,
)
DetIndex is the default detector ordering. You can also pass DetBFS or DetCoordinate
explicitly.
Detector orders are complete detector-ID permutations in traversal order:
order[position] = detector_id.
These values balance decoding speed and accuracy across the benchmarks reported in the paper and can be adjusted for specific use cases.
The Sinter decoder dictionary also provides sparsified variants:
tesseract-long-beam-sparsify-color-code-like,
tesseract-long-beam-sparsify-surface-code-like,
tesseract-short-beam-sparsify-color-code-like, and
tesseract-short-beam-sparsify-surface-code-like.
As a quick rule of thumb, use the non-sparsified decoders as the safest baseline. Use the
surface-code-like variants for surface-code-like or mostly graphlike DEMs, and use the
color-code-like variants for color-code,
bivariate-bicycle-code, or other DEMs where a typical bulk data error activates about three
detectors. Within either family, prefer the long-beam variants when accuracy matters more and the
short-beam variants when runtime matters more. See the
Performance Optimization section for the full sparsification details.
Equivalent Python configs can enable sparsification with sparsify_errors=True,
sparsify_base_degree=2 or 3, and sparsify_reactivate_limit=-1 to use the built-in heuristic
clamped to the compiled error count.
- Do you have a feature request or want to report a bug? Open an issue on GitHub to report it!
- Do you have a code contribution? Read our contribution guidelines, then open a pull request!
We are committed to providing a friendly, safe, and welcoming environment for all. Please read and respect our Code of Conduct.
When publishing articles or otherwise writing about Tesseract Decoder, please cite the following:
@misc{beni2025tesseractdecoder,
title={Tesseract: A Search-Based Decoder for Quantum Error Correction},
author = {Aghababaie Beni, Laleh and Higgott, Oscar and Shutty, Noah},
year={2025},
eprint={2503.10988},
archivePrefix={arXiv},
primaryClass={quant-ph},
doi = {10.48550/arXiv.2503.10988},
url={https://arxiv.org/abs/2503.10988},
}To install your own build of Tesseract python module locally so that you can easily modify and hack on it, use something like the following:
bazel build --define ABI_TAG="cp313" --define TARGET_VERSION="cp3.13" --define VERSION="v0.0.0dev" :tesseract_decoder_wheel
pip uninstall --y tesseract_decoder
pip install bazel-bin/tesseract_decoder-0.0.0.dev0-py3.12.9-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
python testscript.pyFor any questions or concerns not addressed here, please email tesseract-decoder-dev@google.com.
Tesseract Decoder is not an officially supported Google product. This project is not eligible for the Google Open Source Software Vulnerability Rewards Program.
Copyright 2025 Google LLC.