Stim simulates stabilizer circuits, and tells you where its own edges are
A fast stabilizer circuit library.
At a glance
- What is it?
- Stim is a C++ quantum stabilizer circuit simulator shipped as a Python package, a command line tool and a C++ library, built for error correction research. The project states its own limitations plainly: no non-Clifford gates, Pauli noise channels only, and single-control feedback only in stim.Circuit.
- Who is it for?
- Stim fits researchers and engineers working on quantum error correction who need to sample stabilizer circuits quickly, turn a noisy circuit into a detector error model, and build decoder configuration without writing their own simulator. It does not fit work involving non-Clifford gates such as T or Toffoli, amplitude damping style noise, or multi-control feedback, because the project states those require driving stim.TableauSimulator by hand.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A simulator aimed at error correction circuits
Stim is described as a tool for high performance simulation and analysis of quantum stabilizer circuits, especially quantum error correction circuits. The GitHub description calls it a fast stabilizer circuit library, and the primary language is C++.
The audience is narrow and clearly stated. This is for people designing and analysing QEC codes, where you need to sample a circuit thousands or millions of times to estimate a logical error rate, and where the circuit itself is built from stabilizer operations rather than arbitrary gates. The README points to a lightning talk at QPL2021 and to a video of Stim being used to estimate the threshold of the honeycomb code over a weekend, which tells you what a session with the tool looks like.
Three things are described as key features. The first is speed: a circuit with thousands of qubits and millions of operations can be compiled with stim.Circuit.compile_sampler(), which performs a few seconds of analysis and then produces an object that samples shots at kilohertz rates. The second is semi-automatic decoder configuration, where stim.Circuit.detector_error_model() converts a noisy circuit into a detector error model, that is a Tanner graph, which can then be used to configure a decoder. The third is a set of building blocks for working with stabilizers directly.
Stim can be used in three forms. It is primarily a Python package installed with pip, but it also works as a command line tool and as a C++ library.
pip install stimThe building blocks named in the README are the ones you reach for when the circuit abstraction is not what you want.
stim.PauliString
stim.Tableau
stim.TableauSimulatorReference frame sampling turns one simulation into many
The mechanism that produces the headline speed is described in three parts, and the first is the one with the clearest conceptual payoff.
Reference frame sampling is used when bulk sampling. Stim runs a general stabilizer simulator once to get a reference sample, then cheaply derives as many further samples as needed by propagating simulated errors diffed against that reference. The README calls this simple trick far cheaper than the alternative, and characterises the cost difference as constant cost per gate instead of linear or even quadratic cost.
That reframes what a Monte Carlo error rate estimate costs. Instead of simulating a full circuit for every shot, you pay the general simulation cost once and then amortize it. For a circuit where the shot to shot variation comes from a handful of error locations, which is the normal case in a QEC experiment, most of the work is shared between shots.
The other two improvements are lower level. The first is vectorized code: the hot loops use 256 bit wide AVX instructions, and the README gives the figure that Stim can multiply Pauli strings with 100 billion terms in one second. The second is the inverted stabilizer tableau, where the simulator tracks the inverse of the tableau that was historically used, which makes measurements that commute with the current stabilizers take linear time rather than quadratic time.
That last point is not an abstract optimisation. The README explains that error correcting codes benefit because the measurements they perform are usually redundant and therefore commute with the current stabilizers, which is exactly the case the inverted tableau handles in linear time. The design decision and the workload are matched to each other.
Detector error models are how a circuit becomes decoder configuration
The path from a circuit to a working decoder is the practical reason to use this library, and Stim automates most of it.
A detector error model is the intermediate representation between a noisy circuit and a decoder. stim.Circuit.detector_error_model() performs the conversion, and the README notes that the result is a Tanner graph, which is the standard structure for matching based decoders.
The semi-automatic part is decompose_operations=True. Adding that option makes the conversion additionally suggest how hyper errors can be decomposed into graphlike errors, which the README describes as making it easier to configure matching based decoders. Hyper errors are the case where one physical error event corrupts several qubits at once, and the decomposition tells the decoder which graphlike pieces to look for.
So the workflow is: describe the noisy circuit, convert it to a detector error model, inspect or adjust the decomposition, and hand the result to a decoder. The project does not ship a decoder in this description, it produces the configuration a decoder consumes.
For output formats and file handling, the README links reference documents for the circuit file format in .stim files, the detector error model format in .dem files, the results format, the supported gates list, the command line reference and an internal developer reference. There is also a Getting Started Notebook at doc/getting_started.ipynb, which is the documented entry point for learning the API.
The stated limits: no non-Clifford gates, Pauli noise, single-control feedback
The README has a section titled Stim's main limitations, and it is worth reading in full because these three constraints decide whether the tool fits your circuit before you write any code.
First, there is no support for non-Clifford operations such as T gates and Toffoli gates. Only stabilizer operations are supported. A circuit containing any of those cannot be simulated by stim.Circuit at all.
Second, stim.Circuit only supports Pauli noise channels, so amplitude decay and similar mechanisms are outside what it models. For more complex noise you must manually drive a stim.TableauSimulator.
Third, stim.Circuit only supports single-control Pauli feedback. For multi-control feedback, or non-Pauli feedback, you must manually drive a stim.TableauSimulator again.
The pattern across all three is consistent. The high level circuit object is a fast path with a restricted domain, and when you step outside the domain the project routes you to the lower level simulator and accepts that you will write the loop yourself. That is a coherent design, but it means the cost of using Stim is lowest for circuits that stay entirely inside the stabilizer Pauli world and rises sharply the moment they do not.
There is a matching limit on the API contract itself. Stim's Python package uses semantic versioning, and within a major version such as 1.X the project guarantees backwards compatibility of the Python API and of the command line API. The README is explicit that Stim does not guarantee backwards compatibility of the underlying C++ API, so a C++ consumer has to treat each minor release as potentially breaking.
Building it yourself means Bazel, pybind11 and a C++ toolchain
Installing the Python package is the documented path, so most users never see the build system. If you do need to build from source, the repository layout tells you what that involves.
The root has a WORKSPACE file, a BUILD file, a MODULE.bazel file, a .bazelrc and a .bazelversion, so Bazel is the primary build system for the C++ side. The Python packaging lives in setup.py and pyproject.toml, and the build backend is setuptools with pybind11 for the bindings.
[build-system]
requires = ["setuptools", "wheel", "pybind11~=2.11.1"]
build-backend = "setuptools.build_meta"The C++ standard is C++20 on non-Windows platforms, with optimisation flags set to -O3, and separate architecture flags for AVX2 and SSE2.
__version__ = '1.17.dev0'One detail worth knowing before you attempt a Windows build is flagged twice in the packaging files: the Windows compile arguments use /Od instead of /O2 because of bugs in the msvc compiler, with a reference to issue 1078 in the tracker. The pyproject.toml makes the same choice for CI wheels through the CFLAGS and CXXFLAGS environment settings.
The source tree collects files by glob pattern, separating test, performance and main files from the library sources, so the library build can exclude them.
The main.cc files suggest a command line entry point exists in the same tree, which is consistent with the README describing Stim as usable as a command line tool as well as a library.
Performance is treated as a design budget, not a goal
The design philosophy section is unusual enough to quote, and it explains several otherwise puzzling choices.
The stated goal is not to be fast enough but to be fast in an absolute sense. The README makes the point in decibels: the difference between doing one thing per second and doing ten billion things per second is 100 decibels, and because software slowdowns tend to compound exponentially, choices are thought of multiplicatively, as spending or saving decibels. The example given is that under default usage Python is 100 times slower than C++, which the README counts as 20dB of a 100dB budget, a fifth of the multiplicative performance budget allocated to language choice.
That accounting explains why the package ships a C++ core with a Python binding rather than a pure Python implementation, and why the README is frank about the remaining gap to an unrelated FizzBuzz benchmark.
The second principle is bottom up. Stim is intended to be like an assembly language, a mostly straightforward layer on which more complex layers can be built. The user's QEC construction may be defined at a high level, perhaps as a set of stabilizers or a parity check matrix, but these concepts are explained to Stim at a low level as circuits. The README concedes that Stim is not necessarily the abstraction the user wants.
That is a candid framing for a library, and it has a practical consequence. If you arrive with a parity check matrix and expect Stim to consume it directly, you will be writing the circuit yourself, and the payoff is the fast core rather than a friendly high level API.
Maintenance state and the subprojects that sit beside it
The repository is not archived and the last push was on 2026-10-20. The release history shows versions with codenames: v1.14.0 More Flows on 2024-09-24, v1.15.0 Terror of the Tag on 2025-05-07, and v1.16.0 Flowing Circuits on 2026-05-22. The setup.py version string reads 1.17.dev0, so the next major version is in development on the main branch.
The release naming tells you something about the direction of travel. Two of the three tagged releases mention flows, which points at circuit flow analysis as an active area of work in this codebase.
Two subprojects are listed alongside the main library and are worth knowing about, because they solve adjacent problems rather than being part of the simulator itself. The Sinter decoding sampler is a separate project in the glue directory, and the Crumble interactive editor is another, for designing circuits interactively. The README's table of contents links both as subprojects.
The licence is Apache-2.0, which appears in the LICENSE file at the root, in the package.json and in the setup.py header. The project also publishes a CITATION.cff file and a specific citation instruction in the README, which asks researchers using Stim for research to cite the paper describing Stim, hosted in the quantum journal at doi 10.22331/q-2021-07-06-497. A SUPPORT.md and a CODE_OF_CONDUCT.md file are present in the repository root as well.
Editorial conclusion
Stim fits researchers and engineers working on quantum error correction who need to sample stabilizer circuits quickly, turn a noisy circuit into a detector error model, and build decoder configuration without writing their own simulator. It does not fit work involving non-Clifford gates such as T or Toffoli, amplitude damping style noise, or multi-control feedback, because the project states those require driving stim.TableauSimulator by hand. Before adopting, read the two API references separately, since the Python and command line interfaces carry a backwards compatibility guarantee within a major version while the C++ API carries none, and check the gate list in doc/gates.md against the operations your circuit actually uses.
Frequently asked questions
How do I install Stim?
Stim is typically used as a Python package, installed with pip install stim. It can also be used as a command line tool or as a C++ library, and the README points to the Getting Started Notebook at doc/getting_started.ipynb as the introduction to the API.
Does Stim support T gates and other non-Clifford operations?
No. The README states that there is no support for non-Clifford operations such as T gates and Toffoli gates, and that only stabilizer operations are supported.
What is a detector error model in Stim and how do I get one?
stim.Circuit.detector_error_model() converts a noisy circuit into a detector error model, which the README describes as a Tanner graph that can be used to configure decoders. Adding decompose_operations=True additionally suggests how hyper errors can be decomposed into graphlike errors.
What noise and feedback can stim.Circuit model?
stim.Circuit supports Pauli noise channels only, so amplitude decay is not covered, and it supports single-control Pauli feedback only. For more complex noise or for multi-control and non-Pauli feedback, the README says you must manually drive a stim.TableauSimulator.
Is the Stim C++ API backwards compatible between releases?
No. The Python package uses semantic versioning and within a major version the project guarantees backwards compatibility of the Python API and the command line API, but the README explicitly says Stim does not guarantee backwards compatibility of the underlying C++ API.
Why is Stim fast at sampling stabilizer circuits?
The README describes three improvements. Bulk sampling uses reference frame sampling, where one general stabilizer simulation produces a reference and further samples are derived by propagating errors diffed against it, at constant cost per gate. The hot loops use 256 bit wide AVX instructions, and the inverted stabilizer tableau makes commuting measurements linear time rather than quadratic.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/quantumlib-stim)Was this useful?