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
stim.TableauSimulator(seed=None)
Creates a new stim.TableauSimulator starting in the all-zero state. def __init__ ( self , * , seed : Optional[ int ] = None ) -> None
Parameters Name Type Description 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.
Single-qubit gates
Two-qubit gates
Noise methods
Method Gate Flow h(*targets)Hadamard X↔Z s(*targets)SQRT_Z (S) X→Y s_dag(*targets)SQRT_Z_DAG X→-Y x(*targets)Pauli X Z→-Z y(*targets)Pauli Y X→-X, Z→-Z z(*targets)Pauli Z X→-X sqrt_x(*targets)SQRT_X Z→-Y sqrt_x_dag(*targets)SQRT_X_DAG Z→Y sqrt_y(*targets)SQRT_Y X→-Z sqrt_y_dag(*targets)SQRT_Y_DAG X→Z c_xyz(*targets)C_XYZ X→Y→Z→X c_zyx(*targets)C_ZYX Z→Y→X→Z
import stim
s = stim.TableauSimulator()
s.h( 0 )
s.s( 0 ) # SQRT_Z
s.sqrt_x( 1 )
Method Gate cnot(*targets) or cx(*targets)Controlled-X (CNOT) cy(*targets)Controlled-Y cz(*targets)Controlled-Z swap(*targets)SWAP iswap(*targets)ISWAP iswap_dag(*targets)ISWAP_DAG xcx(*targets)X-controlled-X xcy(*targets)X-controlled-Y xcz(*targets)X-controlled-Z ycx(*targets)Y-controlled-X ycy(*targets)Y-controlled-Y ycz(*targets)Y-controlled-Z zcx(*targets)Z-controlled-X zcy(*targets)Z-controlled-Y zcz(*targets)Z-controlled-Z
import stim
s = stim.TableauSimulator()
s.h( 0 )
s.cnot( 0 , 1 ) # Bell pair on qubits 0,1
s.cz( 1 , 2 )
Method Description x_error(p, *targets)X error with probability p y_error(p, *targets)Y error with probability p z_error(p, *targets)Z error with probability p depolarize1(p, *targets)Single-qubit depolarizing noise depolarize2(p, *targets)Two-qubit depolarizing noise
import stim
s = stim.TableauSimulator()
s.h( 0 )
s.depolarize1( 0.01 , 0 )
Measurement
measure() — measure in Z basis
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
measure_many() — measure multiple qubits
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]
measure_kickback() — measure with entanglement info
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
measure_observable() — Pauli product 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
peek_bloch() — single-qubit Bloch vector
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
Return State +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)
peek_observable_expectation() — Pauli expectation
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)
peek_x(), peek_y(), peek_z() — single-axis expectation
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
current_inverse_tableau() — internal state as tableau
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()
current_measurement_record() — all measurement results
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]
canonical_stabilizers() — current stabilizer generators
Returns a standardized list of stabilizer generators of the current state. def canonical_stabilizers ( self ) -> List[stim.PauliString]
Post-selection
postselect_z(), postselect_x(), postselect_y() — force measurement outcomes
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
postselect_observable() — force Pauli product outcome
Post-selects a Pauli observable into a desired eigenstate. def postselect_observable (
self ,
observable : stim.PauliString,
* ,
desired_value : bool = False ,
) -> None
Running circuits
do() — apply a circuit or instruction
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 \n CNOT 0 1 \n M 0 1" ))
print (s.current_measurement_record())
State management
set_inverse_tableau() — overwrite the internal state
Replaces the simulator’s internal state with the given inverse tableau. def set_inverse_tableau ( self , new_inverse_tableau : stim.Tableau) -> None
set_num_qubits() — resize the tracked state
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
num_qubits — current tracked count
@ 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
stim.FlipSimulator(batch_size, ...)
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 Name Type Description 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
get_measurement_flips() — raw measurement 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_.
get_detector_flips() — detection event results
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).
get_observable_flips() — logical observable results
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).
peek_pauli_flips() — current error frames
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
set_pauli_flip() — inject a Pauli error
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_
broadcast_pauli_errors() — bulk injection
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 )
You need to sample thousands or millions of shots
You only care about measurement, detector, or observable results
You are benchmarking decoders
import stim
circuit = stim.Circuit.generated(
"surface_code:rotated_memory_x" ,
distance = 5 ,
rounds = 10 ,
after_clifford_depolarization = 0.001 ,
)
# Compiled sampler (fastest for pure sampling)
sampler = circuit.compile_detector_sampler()
dets, obs = sampler.sample( shots = 100_000 , separate_observables = True )