Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/quantumlib/Stim/llms.txt

Use this file to discover all available pages before exploring further.

Stim is designed from the ground up to support quantum error correction workflows. The .stim circuit format provides two annotations — DETECTOR and OBSERVABLE_INCLUDE — that bridge the gap between a low-level stabilizer circuit and the high-level objects that decoders consume. Understanding these annotations, and the detector error model (DEM) derived from them, is essential for using Stim in a QEC pipeline.

Detectors

A DETECTOR annotation marks a parity check formed from one or more measurement results. Specifically, a detector declares a combination of bits from the measurement record that should be deterministic when no noise is present. If those bits XOR to 1 on a given shot, an error occurred somewhere, and the detector is said to have “fired” (produced a detection event).
# Example: a single-qubit Z-basis measurement immediately preceded by a reset.
# The measurement should always yield 0. If it yields 1, something went wrong.
R 0
X_ERROR(0.01) 0
M 0
DETECTOR rec[-1]
Detectors can reference multiple measurements by listing multiple rec[-n] targets. The detector fires when the XOR of all listed bits equals 1:
# Two consecutive measurements of the same stabilizer.
# They should agree. Disagreement signals a detection event.
MR 1 3 5
MR 1 3 5
DETECTOR rec[-3] rec[-6]   # Compare current round to previous round
DETECTOR rec[-2] rec[-5]
DETECTOR rec[-1] rec[-4]
Detectors may also carry coordinate metadata for visualization and decoder configuration:
DETECTOR(1, 0) rec[-3]    # Detector at spacetime coordinate (1, 0)
DETECTOR(3, 0) rec[-2]
DETECTOR(5, 0) rec[-1]
Coordinates are purely annotations — they have no effect on simulation. Decoders such as PyMatching use them to construct a matching graph with the correct geometry.

Observables

An OBSERVABLE_INCLUDE annotation declares which measurements, when XORed together, track the state of a logical qubit. The argument is an integer index identifying which logical observable the measurements contribute to.
# After measuring all data qubits, declare qubit 0's measurement as
# contributing to logical observable 0.
M 0 2 4 6
OBSERVABLE_INCLUDE(0) rec[-1]
Observables can be spread across multiple instructions and multiple rounds. All OBSERVABLE_INCLUDE(k) contributions for the same index k are XORed together.
A circuit can have multiple logical observables. circuit.num_observables returns one more than the largest observable index declared. Sampling all observables at once via compile_detector_sampler(skip_reference_sample=False) returns both detection events and observable flips.

Detection events and syndromes

When Stim samples detection events from a noisy circuit, each shot produces a bit vector whose length equals circuit.num_detectors. A True (1) entry in this vector means the corresponding detector fired. The full vector is the syndrome for that shot.
import stim

circuit = stim.Circuit.generated(
    "repetition_code:memory",
    distance=4,
    rounds=100,
    after_clifford_depolarization=0.005,
)

sampler = circuit.compile_detector_sampler()

# Sample 1000 shots; each row is a syndrome bit-vector
detection_events, observable_flips = sampler.sample(
    shots=1000,
    separate_observables=True,
)

print(detection_events.shape)   # (1000, num_detectors)
print(observable_flips.shape)   # (1000, num_observables)

Detector error models (DEMs)

A detector error model (DEM) is a compact description of how individual physical error mechanisms cause detectors to fire and observables to flip. Stim derives a DEM automatically from an annotated circuit:
import stim

circuit = stim.Circuit.generated(
    "repetition_code:memory",
    distance=4,
    rounds=10,
    after_clifford_depolarization=0.01,
)

dem = circuit.detector_error_model()
print(dem)
Each line in the DEM is an error mechanism with a probability and a list of detectors and observables it affects:
error(0.01) D0 D1
error(0.01) D1 D2
error(0.01) D2 D3
...
DEMs are the primary input format for matching-based decoders such as PyMatching. They abstract away the details of the circuit and expose only the information needed to do decoding.
Call circuit.detector_error_model(decompose_errors=True) to decompose correlated errors into hyperedges that can be handled by graph-based decoders. This is required for most matching decoders.

Annotated repetition code example

The following is a complete, annotated noisy repetition code with DETECTOR and OBSERVABLE_INCLUDE. Data qubits are at indices 0, 2, 4, 6; measurement qubits are 1, 3, 5.
# Generated repetition_code circuit (distance 4, 1000 rounds, p=0.001)
R 0 1 2 3 4 5 6
TICK
CX 0 1 2 3 4 5
DEPOLARIZE2(0.001) 0 1 2 3 4 5
TICK
CX 2 1 4 3 6 5
DEPOLARIZE2(0.001) 2 1 4 3 6 5
TICK
MR 1 3 5
DETECTOR(1, 0) rec[-3]
DETECTOR(3, 0) rec[-2]
DETECTOR(5, 0) rec[-1]
REPEAT 999 {
    TICK
    CX 0 1 2 3 4 5
    DEPOLARIZE2(0.001) 0 1 2 3 4 5
    TICK
    CX 2 1 4 3 6 5
    DEPOLARIZE2(0.001) 2 1 4 3 6 5
    TICK
    MR 1 3 5
    SHIFT_COORDS(0, 1)
    DETECTOR(1, 0) rec[-3] rec[-6]
    DETECTOR(3, 0) rec[-2] rec[-5]
    DETECTOR(5, 0) rec[-1] rec[-4]
}
M 0 2 4 6
DETECTOR(1, 1) rec[-3] rec[-4] rec[-7]
DETECTOR(3, 1) rec[-2] rec[-3] rec[-6]
DETECTOR(5, 1) rec[-1] rec[-2] rec[-5]
OBSERVABLE_INCLUDE(0) rec[-1]
In the first round, there are no prior measurements to compare against. Each detector references only the current measurement, asserting it should be 0 (since we reset before measuring). From the second round on, each detector compares the current measurement (rec[-n]) with the one from the previous round (rec[-n-3]), flagging any change.
It declares that the last data qubit measurement (qubit 6’s Z-basis result) contributes to logical observable 0. In a repetition code, the logical Z observable is any single data qubit. A decoder that correctly identifies all errors will predict whether observable_flips[shot, 0] is True or False.
Stim runs a noiseless reference simulation of the circuit, computes the XOR of the measurement bits each detector references, and checks that the result is always 0. This is what circuit.reference_sample() returns. A detector whose reference parity is 1 must have its measurement targets adjusted (e.g., by prefixing one target with ! to invert its contribution).

Build docs developers (and LLMs) love