tesseract-decoder

Python Interface

tesseract_decoder.tesseract Module

The tesseract_decoder.tesseract module provides the Tesseract decoder, which employs the A* search to decode a most-likely error configuration from the measured syndrome.

Class tesseract.TesseractConfig

This class holds the configuration parameters that control the behavior of the Tesseract decoder.

Explanation of configuration arguments:

Module-level helper:

Example Usage:

import tesseract_decoder.tesseract as tesseract
import stim
dem = stim.DetectorErrorModel("""
    error(0.1) D0 D1
    error(0.2) D1 D2 L0
    detector(0, 0, 0) D0
    detector(1, 0, 0) D1
    detector(2, 0, 0) D2
""")

# Basic configuration
config1 = tesseract.TesseractConfig(dem=dem)
print(f"Basic configuration detection beam: {config1.det_beam}")
print(f"Basic configuration beam climbing: {config1.det_beam}")
print(f"Basic configuration no-revisit detection events: {config1.det_beam}")
print(f"Basic configuration pqlimit: {config1.det_beam}")
print(f"Basic configuration verbose: {config1.det_beam}")
print(f"Basic configuration detection penalty: {config1.det_beam}")

# Configuration with custom parameters
config2 = tesseract.TesseractConfig(
    dem=dem,
    det_beam=50,
    beam_climbing=True,
    no_revisit_dets=True,
    pqlimit=10000,
    verbose=True,
    det_penalty=0.1
)
print(f"Custom configuration detection beam: {config2.det_beam}")
print(f"Custom configuration beam climbing: {config2.beam_climbing}")
print(f"Custom configuration no-revisit detection events: {config2.no_revisit_dets}")
print(f"Custom configuration pqlimit: {config2.pqlimit}")
print(f"Custom configuration verbose: {config2.verbose}")
print(f"Custom configuration detection penalty: {config2.det_penalty}")

# Configuration with error sparsification
config3 = tesseract.TesseractConfig(
    dem=dem,
    det_beam=20,
    beam_climbing=True,
    sparsify_errors=True,
    sparsify_base_degree=3,
    sparsify_reactivate_limit=-1,
)
decoder = config3.compile_decoder()
print(
    "Resolved sparsify reactivation limit:",
    decoder.config.sparsify_reactivate_limit,
)

Class tesseract.TesseractDecoder

This is the main class that implements the Tesseract decoding logic.

Explanation of each method:

decode_to_errors(syndrome: np.ndarray)

Decodes a single measurement shot to predict a list of errors.

decode_to_errors(syndrome: np.ndarray, det_order: int, det_beam: int)

An overloaded version of the decode_to_errors method that allows for a different decoding strategy.

get_observables_from_errors(predicted_errors: list[int]) -> list[bool]

Converts a list of predicted error indices into a list of flipped logical observables.

cost_from_errors(predicted_errors: list[int]) -> float

Calculates the total logarithmic probability cost for a given set of predicted errors. The cost is a measure of how likely a set of errors is.

decode_from_detection_events(detections: list[int]) -> numpy.ndarray

This method decodes a single shot from a list of detection events. This is an alternative to the decode method that takes a NumPy array.

decode(syndrome: numpy.ndarray) -> numpy.ndarray

A convenience function that decodes a single shot and returns the flipped logical observables directly. It combines the functionality of decode_to_errors and get_observables_from_errors.

decode_batch(syndromes: numpy.ndarray) -> numpy.ndarray

Decodes a batch of shots at once.

Example Usage:

import tesseract_decoder.tesseract as tesseract
import stim
import numpy as np

# Create a DEM and a configuration
dem = stim.DetectorErrorModel("""
    error(0.1) D0
    error(0.2) D1 L0
    detector(0, 0, 0) D0
    detector(1, 0, 0) D1
""")
config = tesseract.TesseractConfig(dem=dem)

# Create the decoder
decoder = tesseract.TesseractDecoder(config)

# --- Decode a single shot using detection events (list of integers) ---
detections = [1]
flipped_observables_events = decoder.decode_from_detection_events(detections)
print(f"Decoded (from events) flipped observables for detections {detections}: {flipped_observables_events}")

# Access predicted errors
predicted_errors = decoder.predicted_errors_buffer
print(f"\nPredicted errors after single-shot decode: {predicted_errors}")

# Calculate cost for predicted errors
cost = decoder.cost_from_errors(predicted_errors)
print(f"Cost of predicted errors: {cost}")

# Check the low confidence flag
print(f"Decoder low confidence: {decoder.low_confidence_flag}")

# --- Decode a single shot using a syndrome array (NumPy array of booleans) ---
syndrome_array = np.array([False, True])
flipped_observables_syndrome = decoder.decode(syndrome_array)
print(f"Decoded (from syndrome) flipped observables for syndrome {syndrome_array}: {flipped_observables_syndrome}")

# --- Decode a batch of shots using a syndrome array (2D NumPy array of booleans) ---
syndromes_batch = np.array([[False, True], [True, False]])
flipped_observables_batch = decoder.decode_batch(syndromes_batch)
print(f"Decoded (batch) flipped observables for syndromes:\n{syndromes_batch}\nResult:\n{flipped_observables_batch}")

tesseract_decoder.simplex Module

The tesseract_decoder.simplex module provides the Simplex-based decoder, which solves the decoding problem using an integer linear program.

Class simplex.SimplexConfig

This class holds the configuration parameters that control the behavior of the Simplex decoder.

Example Usage:

import tesseract_decoder.simplex as simplex
import stim

dem = stim.DetectorErrorModel("""
    # Example DEM
    error(0.1) D0
    error(0.2) D1 L0
""")

config = simplex.SimplexConfig(
    dem=dem,
    parallelize=False,
    window_length=10,
    window_slide_length=5,
    verbose=True
)

print(f"Configuration parallelize enabled: {config.parallelize}");
print(f"Configuration window length: {config.window_length}")
print(f"Configuration window slide length: {config.window_length}")
print(f"Configuration windowing enabled: {config.windowing_enabled()}")
print(f"Configuration verbose enabled: {config.verbose}")

Class simplex.SimplexDecoder

This is the main class for performing decoding using the Simplex algorithm.

Example Usage:

import tesseract_decoder.simplex as simplex
import stim
import tesseract_decoder.common as common
import numpy as np

# Create a DEM and a configuration
dem = stim.DetectorErrorModel("""
    error(0.1) D0
    error(0.2) D1 L0
    detector(0, 0, 0) D0
    detector(1, 0, 0) D1
""")
config = simplex.SimplexConfig(dem=dem)

# Create and initialize the decoder
decoder = simplex.SimplexDecoder(config)
decoder.init_ilp()

# Decode a shot where detector D1 fired
syndrome = np.array([0, 1], dtype=bool)
flipped_observables = decoder.decode(syndrome)
print(f"Flipped observables for syndrome {syndrome.tolist()}: {flipped_observables}")

# Access predicted errors
predicted_error_indices = decoder.predicted_errors_buffer
print(f"Predicted error indices: {predicted_error_indices}")

# Calculate cost from the predicted errors
cost = decoder.cost_from_errors(predicted_error_indices)
print(f"Cost of predicted errors: {cost}")

tesseract_decoder.utils Module

The tesseract_decoder.utils module provides various helper functions used throughout the entire project.

Functions

Example Usage:

import tesseract_decoder.utils as utils
import stim

dem = stim.DetectorErrorModel("""
    detector(0, 0, 0) D0
    detector(1, 0, 0) D1
    detector(0, 1, 0) D2
""")
coords = utils.get_detector_coords(dem)
print("Detector Coordinates:")
for i, coord in enumerate(coords):
    print(f"D{i}: ({coord[0]:.2f}, {coord[1]:.2f}, {coord[2]:.2f})")

Example Usage:

import tesseract_decoder.utils as utils
import stim

dem = stim.DetectorErrorModel("""
    error(0.1) D0 D1
    error(0.2) D1 D2
    error(0.3) D0 D2
    detector(0, 0, 0) D0
    detector(1, 0, 0) D1
    detector(0, 1, 0) D2
""")
graph = utils.build_detector_graph(dem)
print("Detector Graph Adjacency List:")
for i, neighbors in enumerate(graph):
    print(f"D{i}: {neighbors}")

Example Usage:

import tesseract_decoder.utils as utils
import tesseract_decoder.common as common
import stim

dem = stim.DetectorErrorModel("""
    error(0.1) D0 L0
    error(0.05) D1
    error(0) D2
""")
errors = utils.get_errors_from_dem(dem)
print("Errors extracted from DEM:")
for error in errors:
    print(f"Error likelihood cost: {error.likelihood_cost}")
    print(f"Error symptom detectors: {error.symptom.detectors}")

tesseract_decoder.common Module

The tesseract_decoder.common module provides fundamental data structures and utility functions used for decoding quantum circuit shots inside Tessereact. It exposes classes Symptom and Error, which represent error effects and complete error mechanisms, respectively. Additionally, it includes functions for manipulating stim.DetectorErrorModel objects, such as merging identical errors, removing zero-probability errors, and estimating error probabilities from shot counts.

Class common.Symptom

A Python class representing the effect of an error mechanism.

Example Usage:

import tesseract_decoder.common as common
import stim

# Create a symptom with two detectors and one observable
s = common.Symptom(detectors=[0, 1], observables=[2])

# Access detectors
print(f"Detectors: {s.detectors}")
# Access observables
print(f"Observables: {s.observables}")

# Use as_dem_instruction_targets
targets = s.as_dem_instruction_targets()
print(f"DEM targets: {targets}")

# Demonstrate equality and inequality
s2 = common.Symptom(detectors=[0, 1], observables=[2])
s3 = common.Symptom(detectors=[0, 1, 3], observables=[2])
print(f"s == s2: {s == s2}")
print(f"s != s3: {s != s3}")

Class common.Error

A Python class representing a complete error mechanism.

Example Usage:

import tesseract_decoder.common as common
import stim
import math

# Create an empty Error
error = common.Error()
print(f"Error likelihood cost: {error.likelihood_cost}")
print(f"Error symptom detectors: {error.symptom.detectors}")

# Create an Error from a stim.DemInstruction
dem_instruction = stim.DemInstruction(type='error', arg_data=[0.1], target_data=[stim.DemTarget(is_relative_detector_id=True, val=1)])
error2 = common.Error(error=dem_instruction)
print(f"Error likelihood cost: {error2.likelihood_cost}")
print(f"Error symptom detectors: {error2.symptom.detectors}")

# Create an Error with explicit parameters
error3 = common.Error(
    likelihood_cost=-math.log(0.2 / (1 - 0.2)),
    detectors=[1, 2],
    observables=[0],
)
print(f"Error likelihood cost: {error3.likelihood_cost}")
print(f"Error symptom detectors: {error3.symptom.detectors}")

Functions

Example Usage:

import tesseract_decoder.common as common
import stim

original_dem = stim.DetectorErrorModel("""
    error(0.1) D0 D1
    error(0.05) D0 D1
    error(0.2) D2
""")
print("Original DEM:")
print(original_dem)
# This DEM has 3 error instructions.
# Two have the same symptom (D0 D1) with probabilities 0.1 and 0.05.
# The third has a different symptom (D2) with probability 0.2.

merged_dem = common.merge_indistinguishable_errors(original_dem)
print("\nMerged DEM:")
print(merged_dem)
# This merged DEM has 2 error instructions.
# The two errors with the same symptom D0 D1 have been combined into a single instruction.
# The new probability for the D0 D1 error is 0.1+0.05−(0.1×0.05)=0.145.
# The error with symptom D2 remains unchanged.

Example Usage:

import tesseract_decoder.common as common
import stim

original_dem = stim.DetectorErrorModel("""
    error(0.1) D0
    error(0) D1
    error(0.2) D2
""")
print("Original DEM:")
print(original_dem)
# This DEM has 3 error instructions, one of them has probability of zero.

cleaned_dem = common.remove_zero_probability_errors(original_dem)
print("\Cleaned DEM:")
print(cleaned_dem)
# This DEM has 2 error instructions, none of them have probability of zero.

Example Usage:

import tesseract_decoder.common as common
import stim

# Original DEM
original_dem = stim.DetectorErrorModel("""
    error(0.1) D0
    error(0.2) D1
    error(0.05) D2
""")
print("Original DEM:")
print(original_dem)

# Simulate some error counts from 1000 shots
# Error at D0 occurred 100 times, D1 occurred 250 times, D2 occurred 40 times
error_counts = [100, 250, 40]
num_shots = 1000

estimated_dem = common.dem_from_counts(original_dem, error_counts, num_shots)
print("\nEstimated DEM:")
print(estimated_dem)
# Expected probabilities: D0 -> 100/1000 = 0.1, D1 -> 250/1000 = 0.25, D2 -> 40/1000 = 0.04

Sinter Integration

The Tesseract Python interface is compatible with the Sinter framework, which is a powerful tool for large-scale decoding, benchmarking, and error-rate estimation.

The TesseractSinterDecoder Object

All Sinter examples rely on this utility function to provide the Sinter-compatible Tesseract decoder. The default decoder dictionary also includes 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 root README’s Performance Optimization section for the full sparsification details.

import sinter
import stim
from sinter._decoding._decoding import sample_decode

from src.tesseract_decoder import tesseract_sinter_compat as tesseract_module
from src import tesseract_decoder

# Define a function that returns a dictionary mapping a decoder name to its
# Sinter-compatible decoder object.
def get_tesseract_decoder_for_sinter():
    return tesseract_module.make_tesseract_sinter_decoders_dict()

Multi-pass Tesseract decoding

MultiPassSinterDecoder partitions a detector error model into exactly two detector components. It accepts one or two passes (default: 2) and uses causal scheduling by default. Its shared automatic classifier checks, in order, top-level measure_basis, md.measure_basis, top-level basis, md.basis, and then the Chromobius fourth-coordinate convention (02 is X and 35 is Z). A reached metadata field with any value other than "X" or "Z" is an error; lower-priority fields are ignored once a basis is found. The coordinate fallback rejects nonintegral values. Every detector must be classified, and exactly two components must result.

Top-level measure_basis is also the canonical convention consumed by the native CLI. tesseract_decoder.demutil.annotate_detector_bases(dem) normalizes legacy tags or coordinates to this convention while preserving the DEM structure and unrelated metadata. It rejects an invalid or conflicting existing top-level measure_basis, but preserves lower-priority metadata even when it differs. A DEM using the previous CLI convention, top-level basis, must be normalized with this helper before CLI decoding. Python and Sinter still classify it automatically without normalization.

Standard Tesseract options and multi-pass wrapper options can be passed directly as keyword arguments. A nonempty det_orders is used directly; when it is empty, num_det_orders, det_order_method, and seed generate component orderings. Two-pass reweighting requires merge_errors=True because its probabilities describe aggregate component symptoms; merge_errors=False remains supported with one pass:

import stim
import tesseract_decoder
from multi_pass_sinter_decoders import MultiPassSinterDecoder

dem = stim.DetectorErrorModel("""
    error(0.1) D0 ^ D1 L0
    error(0.01) D0
    error(0.2) D1 L0
    detector[{"measure_basis": "X"}] D0
    detector[{"measure_basis": "Z"}] D1
    logical_observable L0
""")

decoder = MultiPassSinterDecoder(
    num_passes=2,
    det_beam=20,
    beam_climbing=True,
    pqlimit=1_000_000,
    merge_errors=True,
    num_det_orders=21,
    det_order_method=tesseract_decoder.utils.DetectorOrderMethod.Index,
)
compiled_decoder = decoder.compile_decoder_for_dem(dem=dem)

For another convention, pass a detector_basis_classifier with signature (detector_index, coordinates, tag) -> "X" | "Z" | None. Explicit Stim surface-code parity and Chromobius-coordinate X/Z adapters are available from tesseract_decoder.demutil. The generic last-coordinate compatibility adapter is also exported for component-based APIs, but intentionally does not claim that its labels are X/Z bases. For example:

decoder = MultiPassSinterDecoder(
    detector_basis_classifier=(
        tesseract_decoder.demutil.stim_surface_code_detector_basis_classifier
    )
)

The old detector_classifier keyword remains available for callbacks returning two arbitrary nonnegative integer component labels. New code should use the X/Z interface above.

For Oscar’s ordinary workflow, no callback is needed:

from multi_pass_sinter_decoders import MultiPassSinterDecoder, get_sinter_decoders

decoder = MultiPassSinterDecoder()
custom_decoders = get_sinter_decoders()

get_sinter_decoders() preserves the tesseract-long-beam-mono, tesseract-long-beam-multipass-1pass, and tesseract-long-beam-multipass-2pass registry names. Low-confidence multipass shots propagate through Sinter’s discard byte and are counted as discards, not successful shots.

Decoding with sinter.collect

sinter.collect is a powerful function for running many decoding jobs in parallel and collecting the results for large-scale benchmarking.

# Create a repetition code circuit to test the decoder.
circuit = stim.Circuit.generated(
    'repetition_code:memory',
    distance=3,
    rounds=3,
    after_clifford_depolarization=0.01
)

# Use sinter.collect to run the decoding task.
results, = sinter.collect(
    num_workers=1,
    tasks=[sinter.Task(circuit=circuit)],
    decoders=["tesseract"],
    max_shots=1000,
    custom_decoders=get_tesseract_decoder_for_sinter(),
)

# Print a summary of the decoding results.
print("Basic Repetition Code Decoding Results:")
print(f"Shots run: {results.shots}")
print(f"Observed errors: {results.errors}")
print(f"Logical error rate: {results.errors / results.shots}")

Running with multiple workers

This example demonstrates how to use multiple worker threads to speed up the simulation.

# Use sinter.collect with multiple workers for faster decoding.
results, = sinter.collect(
    num_workers=4,
    tasks=[sinter.Task(circuit=circuit)],
    decoders=["tesseract"],
    max_shots=10000,
    custom_decoders=get_tesseract_decoder_for_sinter(),
)

print("\nDecoding with 4 worker threads:")
print(f"Shots run: {results.shots}")
print(f"Observed errors: {results.errors}")
print(f"Logical error rate: {results.errors / results.shots}")

Decoding with sinter.sample_decode

sinter.sample_decode is a simpler, non-parallel function for directly decoding a single circuit. It’s useful for quick tests and debugging without the overhead of the sinter.collect framework.

# Create a repetition code circuit.
circuit = stim.Circuit.generated('repetition_code:memory', distance=5, rounds=5)

# Use sinter.sample_decode for a direct decoding run.
result = sample_decode(
    circuit_obj=circuit,
    dem_obj=circuit.detector_error_model(),
    num_shots=1000,
    decoder="tesseract",
    custom_decoders=get_tesseract_decoder_for_sinter(),
)

print("Basic sample_decode Results:")
print(f"Shots run: {result.shots}")
print(f"Observed errors: {result.errors}")
print(f"Logical error rate: {result.errors / result.shots}")

tesseract_decoder.demutil Module

The tesseract_decoder.demutil module provides utilities for manipulating stim.DetectorErrorModel objects, specifically for decomposing complex error mechanisms into simpler components and regeneralizing spatial error models.

Functions

Example Usage:

import stim
from tesseract_decoder import demutil

dem = stim.DetectorErrorModel("""
    detector(0, 0, 0) D0
    detector(0, 0, 1) D1
    # Atomic errors for decomposition reference
    error(0.01) D0
    error(0.01) D1
    # Complex error to decompose
    error(0.1) D0 D1
""")

# Re-decompose the errors assuming Stim surface code coordinate convention
nice_matchable_dem = demutil.decompose_errors(dem, method='stim-surfacecode-coords')

# Re-decompose the errors assuming the last-coordinate index indicates the component:
nice_matchable_dem2 = demutil.decompose_errors(dem, method='last-coordinate-index')

# Optionally drop undecomposable complex errors instead of raising.
nice_matchable_dem3 = demutil.decompose_errors(
    dem,
    method='last-coordinate-index',
    strip_undecomposable_errors=True,
)

Command-line decomposition

Like the other DEM utility tools, decompose_errors.py can also be run directly:

python src/py/_tesseract_py_util/decompose_errors.py \
    --method=last-coordinate-index \
    --out output.dem \
    input.dem

The input defaults to standard input, --out defaults to standard output, and --method defaults to stim-surfacecode-coords, so the command can also be used in a pipeline:

python src/py/_tesseract_py_util/decompose_errors.py \
    --method=stim-surfacecode-coords \
    < input.dem > output.dem

Pass --strip-undecomposable-errors to drop errors that cannot be decomposed instead of returning an error.

Example Usage:

import stim
from tesseract_decoder import demutil

# Take one or more DEMs **with detector coordinates**, aggregate the error probabilities
template1 = stim.DetectorErrorModel("""
    detector(0, 0) D0
    error(0.1) D0
""")
template2 = stim.DetectorErrorModel("""
    detector(0, 0) D0
    error(0.2) D0
""")
scaffold = stim.DetectorErrorModel("""
    detector(0, 0) D1
    error(0.99) D1
""")

nice_calibrated_dem = demutil.regeneralize_spatial_dem(
    templates=[template1, template2],
    scaffold=scaffold
)
# Result will have error probability (0.1 + 0.2) / 2 = 0.15

GARI transformed matrices

demutil.gari.circuit_to_gari converts a supported correlated CSS Stim circuit into a GARI matrix DEM. It generates a flattened source DEM with decompose_errors=False. By default it uses the shared automatic basis classifier: detector metadata is checked first, followed by the strict Chromobius fourth-coordinate convention. A custom classifier can be supplied with detector_basis_classifier=.

import stim
from tesseract_decoder import demutil

circuit = stim.Circuit.from_file("circuitFile.stim")
gari_dem = demutil.gari.circuit_to_gari(
    circuit,
    prior_function=demutil.gari.tesseract_xor_prior_probabilities,
)

The returned DEM preserves the source detector IDs as a prefix and appends the virtual detector rows. For matrix analysis, circuit_to_gari(..., row_order="block") instead emits the internal [physical X, physical Z, virtual Z, virtual X] row order. This research form does not accept source syndromes as a direct prefix.

demutil.gari.build_detector_orders(circuit, gari_dem, num_det_orders, ...) uses the source circuit to build BFS, coordinate, or index orders and then appends the virtual detector IDs. The resulting list has the same format as TesseractConfig.det_orders and the Tesseract CLI’s --detector-orders JSON file. It applies to the default source-aligned GARI DEM, not the research-only block form.

Related public APIs:

The returned GARI matrix DEM stores transformed matrices for decoding and must not be sampled. Sample from the original circuit, copy its syndrome into the beginning of a zero-filled GARI syndrome, and leave the virtual suffix zero. See the GARI tutorial for a complete decoding example.