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 provides two distinct simulator classes for different use cases. stim.TableauSimulator is a general-purpose stabilizer simulator that tracks the full quantum state as an inverse stabilizer tableau and supports interactive one-shot simulation. stim.FlipSimulator is a frame-based bulk sampler that represents quantum states only as collections of Pauli error frames, making it extremely fast for batch sampling but limited to computing error propagation rather than full wavefunction dynamics.

TableauSimulator

Full stabilizer state tracking. Use for interactive simulation, post-selection, syndrome decoding experiments, and circuits where you need the quantum state at intermediate steps.

FlipSimulator

Error-frame-only tracking over many parallel instances. Use for bulk sampling of detection events and observables when high throughput is the priority.

stim.TableauSimulator

A stabilizer circuit simulator that maintains an inverse stabilizer tableau as its internal state. Gates and measurements can be applied interactively, and the state can be inspected at any point.

Constructor

Creates a new stim.TableauSimulator starting in the all-zero state.
def __init__(self, *, seed: Optional[int] = None) -> None
Parameters
NameTypeDescription
seedint or NoneDeterministic RNG seed. Results are consistent only within the same Stim version.
import stim

s = stim.TableauSimulator()
s.h(0)
s.cnot(0, 1)
result = s.measure(0)
print(s.measure(1) == result)  # True (entangled pair)

Gate methods

All single-qubit and two-qubit gate methods accept a variable number of qubit index arguments. Two-qubit gates accept pairs: s.cnot(0, 1, 2, 3) applies CNOT to qubits 0,1 and then to qubits 2,3.
MethodGateFlow
h(*targets)HadamardX↔Z
s(*targets)SQRT_Z (S)X→Y
s_dag(*targets)SQRT_Z_DAGX→-Y
x(*targets)Pauli XZ→-Z
y(*targets)Pauli YX→-X, Z→-Z
z(*targets)Pauli ZX→-X
sqrt_x(*targets)SQRT_XZ→-Y
sqrt_x_dag(*targets)SQRT_X_DAGZ→Y
sqrt_y(*targets)SQRT_YX→-Z
sqrt_y_dag(*targets)SQRT_Y_DAGX→Z
c_xyz(*targets)C_XYZX→Y→Z→X
c_zyx(*targets)C_ZYXZ→Y→X→Z
import stim

s = stim.TableauSimulator()
s.h(0)
s.s(0)        # SQRT_Z
s.sqrt_x(1)

Measurement

Measures a single qubit in the Z basis and returns the result.
def measure(self, target: int) -> bool
import stim

s = stim.TableauSimulator()
s.x(0)
result = s.measure(0)  # True
Measures multiple qubits and returns their results as a list.
def measure_many(self, *targets) -> List[bool]
import stim

s = stim.TableauSimulator()
s.x(1)
results = s.measure_many(0, 1)  # [False, True]
Measures a qubit and returns both the result and the Pauli string that anticommutes with the measurement basis (the “kickback”). The kickback can be used for post-selection or to undo a measurement.
def measure_kickback(self, target: int) -> Tuple[bool, Optional[stim.PauliString]]
Returns (result, kickback) where kickback is None if the measurement was deterministic, or a stim.PauliString if it was random.
import stim

s = stim.TableauSimulator()
s.h(0)
result, kickback = s.measure_kickback(0)
# kickback == stim.PauliString("+X") if random measurement
Measures a multi-qubit Pauli observable (as if by MPP).
def measure_observable(
    self,
    observable: stim.PauliString,
    *,
    flip_probability: float = 0.0,
) -> bool
import stim

s = stim.TableauSimulator()
s.h(0)
s.cnot(0, 1)
s.measure_observable(stim.PauliString("XX"))  # False (deterministic)
s.measure_observable(stim.PauliString("ZZ"))  # True  (deterministic)

State inspection

Returns the single-qubit Bloch vector of a qubit as a stim.PauliString stabilizer. Returns I if the qubit is entangled.
def peek_bloch(self, target: int) -> stim.PauliString
ReturnState
+Z|0⟩
-Z|1⟩
+X|+⟩
-X|−⟩
+Y|i⟩
-Y|−i⟩
Ientangled
import stim

s = stim.TableauSimulator()
s.peek_bloch(0)   # +Z  (in |0> state)
s.h(0)
s.peek_bloch(0)   # +X  (in |+> state)
s.cnot(0, 1)
s.peek_bloch(0)   # +_  (entangled)
Returns the deterministic expectation value (+1, -1, or 0) of a Pauli observable.
def peek_observable_expectation(self, observable: stim.PauliString) -> int
import stim

s = stim.TableauSimulator()
s.h(0)
s.cnot(0, 1)
s.peek_observable_expectation(stim.PauliString("XX"))  # +1
s.peek_observable_expectation(stim.PauliString("ZI"))  # 0 (random)
Returns the expected value of a single qubit’s X, Y, or Z observable (+1, -1, or 0).
def peek_x(self, target: int) -> int
def peek_y(self, target: int) -> int
def peek_z(self, target: int) -> int
Returns a copy of the simulator’s internal inverse stabilizer tableau.
def current_inverse_tableau(self) -> stim.Tableau
import stim

s = stim.TableauSimulator()
s.h(0)
s.cnot(0, 1)
inv_tab = s.current_inverse_tableau()
Returns a copy of the complete measurement record since the simulator was created.
def current_measurement_record(self) -> List[bool]
import stim

s = stim.TableauSimulator()
s.measure(0)  # False
s.x(0)
s.measure(0)  # True
s.current_measurement_record()  # [False, True]
Returns a standardized list of stabilizer generators of the current state.
def canonical_stabilizers(self) -> List[stim.PauliString]

Post-selection

Forces qubits into a specific eigenstate by post-selection. Raises ValueError if the postselection is impossible (the qubit was deterministically in the opposite state).
def postselect_z(
    self,
    targets: Union[int, Iterable[int]],
    *,
    desired_value: bool,
) -> None

def postselect_x(
    self,
    targets: Union[int, Iterable[int]],
    *,
    desired_value: bool,
) -> None

def postselect_y(
    self,
    targets: Union[int, Iterable[int]],
    *,
    desired_value: bool,
) -> None
import stim

s = stim.TableauSimulator()
s.h(0)
s.postselect_z(0, desired_value=True)  # force |1> outcome
s.peek_bloch(0)  # -Z
Post-selects a Pauli observable into a desired eigenstate.
def postselect_observable(
    self,
    observable: stim.PauliString,
    *,
    desired_value: bool = False,
) -> None

Running circuits

Applies a circuit, a single instruction, or a repeat block to the simulator’s state.
def do(
    self,
    obj: Union[stim.Circuit, stim.CircuitInstruction, stim.CircuitRepeatBlock],
) -> None
import stim

s = stim.TableauSimulator()
s.do(stim.Circuit("H 0\nCNOT 0 1\nM 0 1"))
print(s.current_measurement_record())

State management

Replaces the simulator’s internal state with the given inverse tableau.
def set_inverse_tableau(self, new_inverse_tableau: stim.Tableau) -> None
Resizes the simulator’s tracked qubit range. Qubits outside the new range are implicitly reset to |0⟩.
def set_num_qubits(self, new_num_qubits: int) -> None
@property
def num_qubits(self) -> int
The number of qubits currently tracked. Grows automatically when higher-indexed qubits are touched.

stim.FlipSimulator

A frame-based simulator that tracks many simulation instances in parallel. Each instance is a Pauli error frame over qubits — a compact description of which Pauli errors have accumulated. This enables very fast bulk sampling of measurement flips, detector events, and observable flips, but does not support general quantum state queries.
stim.FlipSimulator only tracks error frames, not full stabilizer states. It cannot perform post-selection or return Bloch vectors. Use stim.TableauSimulator when you need full state information.

Constructor

Creates a flip simulator with the given number of parallel instances.
def __init__(
    self,
    batch_size: int,
    *,
    disable_stabilizer_randomization: bool = False,
    num_qubits: int = 0,
    seed: Optional[int] = None,
) -> None
Parameters
NameTypeDescription
batch_sizeintNumber of parallel simulation instances.
disable_stabilizer_randomizationboolWhen True, initial Z stabilizer flips are suppressed. Useful for testing.
num_qubitsintInitial number of qubits to track.
seedint or NoneDeterministic RNG seed.
import stim

sim = stim.FlipSimulator(batch_size=256)
sim.do(stim.Circuit('''
    X_ERROR(0.01) 0 1 2
    M 0 1 2
    DETECTOR rec[-1] rec[-2]
    DETECTOR rec[-2] rec[-3]
'''))
flips = sim.get_detector_flips()  # shape (2, 256)

Applying operations

Applies a circuit, instruction, or repeat block to all simulation instances.
def do(
    self,
    obj: Union[stim.Circuit, stim.CircuitInstruction, stim.CircuitRepeatBlock],
) -> None
import stim

sim = stim.FlipSimulator(batch_size=1024)
sim.do(stim.Circuit.generated(
    "surface_code:rotated_memory_x",
    distance=5,
    rounds=10,
    after_clifford_depolarization=0.001,
))
obs = sim.get_observable_flips()  # shape (1, 1024)

Reading results

Returns measurement flip data as a numpy array.
def get_measurement_flips(
    self,
    *,
    record_index: Optional[int] = None,
    instance_index: Optional[int] = None,
    bit_packed: bool = False,
) -> np.ndarray
By default returns a 2D array of shape (num_measurements, batch_size) with dtype=bool_.
Returns detection event flip data.
def get_detector_flips(
    self,
    *,
    detector_index: Optional[int] = None,
    instance_index: Optional[int] = None,
    bit_packed: bool = False,
) -> np.ndarray
By default returns a 2D array of shape (num_detectors, batch_size).
Returns logical observable flip data.
def get_observable_flips(
    self,
    *,
    observable_index: Optional[int] = None,
    instance_index: Optional[int] = None,
    bit_packed: bool = False,
) -> np.ndarray
By default returns a 2D array of shape (num_observables, batch_size).
Returns the current Pauli error frames as a list of stim.PauliString objects, one per instance.
def peek_pauli_flips(
    self,
    *,
    instance_index: Optional[int] = None,
) -> Union[stim.PauliString, List[stim.PauliString]]

Injecting errors

Sets a Pauli error on a specific qubit in a specific simulation instance.
def set_pauli_flip(
    self,
    pauli: Union[str, int],
    *,
    qubit_index: int,
    instance_index: int,
) -> None
The pauli argument accepts 'I', 'X', 'Y', 'Z', '_', or integers 0–3.
import stim

sim = stim.FlipSimulator(batch_size=4, num_qubits=3,
                         disable_stabilizer_randomization=True)
sim.set_pauli_flip('X', qubit_index=1, instance_index=2)
print(sim.peek_pauli_flips(instance_index=2))  # +_X_
Injects Pauli errors into all instances at once, sampling independently.
def broadcast_pauli_errors(
    self,
    pauli: Union[str, int],
    mask: np.ndarray,
) -> None

Properties

batch_size

int — Number of parallel simulation instances.

num_qubits

int — Number of qubits currently tracked.

num_measurements

int — Measurements accumulated so far.

num_detectors

int — Detector events accumulated so far.

num_observables

int — Observables currently tracked.

Choosing a simulator

  • You need full quantum state queries (Bloch vectors, expectation values)
  • You want interactive step-by-step simulation
  • You need post-selection
  • You are simulating one or a small number of circuits
  • You need the measurement kickback for adaptive circuits
import stim

s = stim.TableauSimulator()
s.h(0)
s.cnot(0, 1)
if s.measure_kickback(0)[0]:
    s.x(1)

Build docs developers (and LLMs) love