stim

repository·main·Indexed 21 days ago

https://github.com/quantumlib/stim

A high-performance quantum stabilizer circuit simulator optimized for quantum error correction (QEC) research. It provides fast sampling, detector error model generation, and low-level stabilizer primitives. The package includes integration with Cirq via stimcirq for circuit conversion and sampling, as well as Crumble, an interactive tool for exploring, editing, and propagating Paulis in 2D stabilizer circuits.

Tokens
148.2K
Snippets
502
Records
605
Agent score
73%

What's inside stim

  1. What is Stim?

    main

    Stim is a high-performance simulator and analysis tool for quantum stabilizer circuits, specifically designed for quantum error correction (QEC).

    Key Features

    • Fast Simulation: Uses stim.Circuit.compile_sampler() to produce objects capable of sampling shots at kilohertz rates after an initial analysis.
    • Decoder Configuration: stim.Circuit.detector_error_model() converts noisy circuits into a detector error model (Tanner graph) for configuring decoders. Using decompose_operations=True helps decompose hyper errors into graphlike errors for matching-based decoders.
    • Stabilizer Building Blocks: Provides utilities like stim.PauliString, stim.Tableau, and stim.TableauSimulator.

    Limitations

    • No non-Clifford gates: Only stabilizer operations are supported (no T gates or Toffoli gates).
    • Pauli noise only in stim.Circuit: stim.Circuit does not support non-Pauli noise (like amplitude decay). For complex noise, you must use stim.TableauSimulator manually.
    • Feedback constraints: stim.Circuit only supports single-control Pauli feedback. Multi-control or non-Pauli feedback requires manual driving of a stim.TableauSimulator.
  2. Understand the Stim Circuit File Format (.stim)

    main

    A .stim file is a human-readable specification for an annotated stabilizer circuit. It is used to define:

    • Gates: Quantum operations applied to qubits.
    • Noise Processes: Stochastic processes applied during simulations.
    • Annotations: Metadata for tasks like drawing circuits or sampling detection events.

    Files must be encoded using UTF-8. Non-ASCII characters are only permitted within comments and tags.

  3. What is a FlipSimulator and when to use it

    main

    A stim.FlipSimulator is a simulator that tracks whether qubits are flipped rather than tracking their actual values. This approach is significantly more efficient than unitary or tableau simulators, requiring only $O(1)$ work per gate (compared to $O(n)$ or $O(n^2)$ for other methods).

    It is ideal for high-performance error propagation studies and large-scale simulations where tracking the state of many instances in parallel is required.

    import stim
    sim = stim.FlipSimulator(batch_size=256)
  4. What is a stimflow.Chunk?

    main

    A stimflow.Chunk represents a quantum circuit paired with its accompanying stabilizer flow assertions. It is designed to be immutable; while some fields are editable types, you should not modify the circuit or flows after the chunk is created (e.g., do not append to the circuit).

    import stimflow as sf
    import stim
    chunk = sf.Chunk(
        circuit=stim.Circuit('''
            QUBIT_COORDS(1, 2) 0
            H 0
        '''),
        flows=[
            sf.Flow(start=sf.PauliMap({1+2j: "X"}), end=sf.PauliMap({1+2j: "Z"})),
        ],
    )
    chunk.verify()
  5. Use stim.Tableau to represent Clifford operations

    main

    A stim.Tableau represents a stabilizer tableau, which explicitly stores how a Clifford operation conjugates Pauli group generators.

    Common ways to create a Tableau:

    • stim.Tableau(num_qubits): Creates an identity tableau for the specified number of qubits.
    • stim.Tableau.from_named_gate(name): Creates a tableau from a standard gate (e.g., "H", "S", "CNOT", "CZ").
    • stim.Tableau.from_circuit(circuit): Converts a stim.Circuit into an equivalent stabilizer tableau.
    • stim.Tableau.random(num_qubits): Creates a random Clifford tableau.

    Key operations:

    • Conjugation: Calling a tableau on a PauliString (e.g., t(p)) returns the conjugated Pauli string $Q = C P C^{-1}$.
    • Multiplication: t1 * t2 represents the composition of two Clifford operations (applying t2 then t1).
    • Power: t**n raises the tableau to an integer power $n$ (efficiently handles large powers via repeated squaring; negative powers invert the tableau).
    • Direct Sum: t1 + t2 performs a diagonal concatenation of two tableaus.
    import stim
    
    # Create a CNOT tableau
    t = stim.Tableau.from_named_gate("CNOT")
    p = stim.PauliString("XX")
    
    # Conjugate the Pauli string
    result = t(p)
    print(result)  # Output: +X_
    
    # Multiply tableaus (composition)
    t1 = stim.Tableau.random(4)
    t2 = stim.Tableau.random(4)
    t3 = t2 * t1
  6. Understand the Stim simulator state space

    main

    A Stim simulator maintains three primary components:

    1. The Qubits: All qubits start in the $|0\rangle$ state. The number of qubits is implicitly determined by the largest qubit index used in the circuit.
    2. The Measurement Record: An immutable log of all measurement results. Results are appended as bits. Instructions can use rec[index] as controls (e.g., CZ rec[-1] 5 applies a Z gate to qubit 5 if the last measurement was TRUE).
    3. The 'Correlated Error Occurred' Flag: A hidden boolean flag used to track whether a CORRELATED_ERROR instruction occurred, which determines the behavior of subsequent ELSE_CORRELATED_ERROR instructions.
  7. Handle stabilizer mismatches with add_discarded_flow_output and add_discarded_flow_input

    main

    When compiling chunks, ChunkCompiler normally requires that the output flows of one chunk match the input flows of the next. If a mismatch is intentional (e.g., a transversal preparation that produces X stabilizers when the next chunk expects Z), you must explicitly declare these as discarded flows to prevent compilation errors.

    • Use add_discarded_flow_output(flow) to annotate that an output stabilizer will not be used.
    • Use add_discarded_flow_input(flow) to annotate that an input stabilizer is expected but will be ignored.
    import stimflow as sf
    
    # Example: A chunk that produces X stabilizers that are intentionally not used by the next chunk
    xx = sf.PauliMap.from_xs([0, 1])
    zz = sf.PauliMap.from_zs([0, 1])
    
    init_builder = sf.ChunkBuilder()
    init_builder.append("R", [0, 1])
    init_builder.add_flow(end=zz)
    # Explicitly mark X stabilizers as discarded so the compiler doesn't error when the next chunk starts with Z
    init_builder.add_discarded_flow_output(xx)
    init_chunk = init_builder.finish_chunk()
  8. Understand instruction broadcasting

    main

    When an instruction is provided with multiple targets, Stim applies it via broadcasting:

    • Single-qubit operations (e.g., H, DEPOLARIZE1): Applied to each target in order. H 0 1 2 is equivalent to H 0, H 1, and H 2 sequentially.
    • Two-qubit operations (e.g., CNOT, DEPOLARIZE2): Applied to aligned pairs of targets in order. CNOT 0 1 1 2 2 0 is equivalent to CNOT 0 1, CNOT 1 2, and CNOT 2 0.

    Note: Providing an odd number of targets to a two-qubit operation is an error.

  9. Convert measurements to detection events with CompiledMeasurementsToDetectionEventsConverter

    main

    The CompiledMeasurementsToDetectionEventsConverter is a tool for converting raw measurement data into detection events (and optionally observable flip data) based on a specific circuit's logic.

    Initialization: When initialized, the converter uses a noiseless reference sample (collected via Stim's Tableau simulator) as a baseline to determine expected detector values.

    • Use skip_reference_sample=True if you want to initialize the reference sample to all-zeroes instead of collecting it (only if all-zeroes is a known valid noiseless result).

    The convert method: Converts a numpy array of measurements into detection events.

    • measurements: A numpy array. Its dtype determines if it is bit-packed (uint8) or unpacked (bool_).
    • separate_observables: If True, returns a tuple (detection_events, observable_flips). If False, returns only the detection events.
    • append_observables: If True, treats circuit observables as additional detectors and appends them to the end of the detection event data.
    • sweep_bits: Optional array containing sweep data for sweep[k] controls.
    import stim
    import numpy as np
    
    # Create a converter using the circuit's built-in method
    converter = stim.Circuit('''
       X 0
       M 0 1
       DETECTOR rec[-1]
       DETECTOR rec[-2]
       OBSERVABLE_INCLUDE(0) rec[-2]
     ''').compile_m2d_converter()
    
    # Convert measurements
    dets, obs = converter.convert(
        measurements=np.array([[1, 0], [1, 0], [1, 0], [0, 0], [1, 0]], dtype=np.bool_), 
        separate_observables=True
    )
  10. Use instruction tags for custom metadata

    main
    Instruction tags (e.g., TICK[100ns]) have no effect on the functional execution of a circuit. They are intended for users and tools to specify custom behavior or hints that Stim does not natively process. Tools should attempt to propagate these tags through transformations and ignore them if they do not recognize the specific tag content.
  11. Apply controlled gates in `stim.TableauSimulator`

    main

    The TableauSimulator class provides methods for applying controlled gates to the simulator's state. For zcx, zcy, and zcz, the *targets argument must contain an even number of qubit indices. The gate is applied to the first two targets, then the next two, and so on.

    import stim
    s = stim.TableauSimulator()
    s.zcx(0, 1, 2, 3)  # Applies ZCX to (0,1) and (2,3)
    s.zcy(0, 1, 2, 3)  # Applies ZCY to (0,1) and (2,3)
    s.zcz(0, 1, 2, 3)  # Applies ZCZ to (0,1) and (2,3)