Skip to content

Detector sampler raises IndexError when a non-Clifford block feeds no detector #260

Closed

Description

@rafaelha

Hi! While fuzzing the statevector backend before 1.0.0, we found a small circuit that compile_detector_sampler can't sample. The non-Clifford part on qubits 0–1 isn't read by any detector or observable:

import tsim

c = tsim.Circuit("""
U3(0.25, -0.25, 0.825) 0
YCY 1 0
U3(0.125, 0.595, 0.5) 0
SQRT_YY 1 0
MR 1
X_ERROR(0.1) 2
M 2
DETECTOR rec[-1]
""")
c.compile_sampler(seed=0).sample(3)           # works
c.compile_detector_sampler(seed=0).sample(5)  # IndexError: index -1 is out of bounds for axis 0 with size 0

Expected: 5 rows with one detector, which fires about 10% of the time.

What seems to happen: compile_program (src/tsim/compile/pipeline.py) still compiles the unread component, which then has rotations but no outputs and no symbols. lane_program (src/tsim/statevector/shared.py, lane_dormant = dormant & read[branch]) then indexes the empty read array with a branch of -1.

When the component does have symbols, read[-1] just reads an unrelated entry, and dormant masks it out. So the visible effect is the crash; we found no case of wrong samples.

Possible fixes: skip components without outputs when compiling for the detector sampler, or guard the index (e.g. read[np.maximum(branch, 0)] together with the existing dormant mask).

Random circuits hit this about once in 20k. It reproduces on main (e3a465b) and on #259 (a9af140).

🤖 Generated with Claude Code

Activity

  1. added a commit that references this issue on Nov 4, 2026
    5fbfa0f
  2. rafaelha commented on Nov 4, 2026

    @rafaelha
    CollaboratorAuthor

    A small correction and a few more triggers, from a 5-hour fuzzing run:

    • compile_sampler can crash too. It does when the circuit has no measurements at all. The fuzzer's smallest reproducer raises the same IndexError from both samplers for any seed:
      I[U3(theta=0.25*pi, phi=0.1*pi, lambda=1e-06*pi)] 3
      I[U3(theta=0.25*pi, phi=-1.3454*pi, lambda=0.339*pi)] 1
      SPP[R_PAULI(theta=0.25*pi)] Y3*Z1
      CY 3 1
      
    • Other shapes that hit the same line (detector sampler):
      • classical feedback (CY rec[-2] 2) onto a non-Clifford qubit that is never measured again;
      • SPP_DAG products on such qubits;
      • a heralded channel (HERALDED_ERASE, HERALDED_PAULI_CHANNEL_1, even at p=0) on a non-Clifford qubit that is never measured again. The fuzzer reported this one; I haven't reproduced it myself yet.

    In every case the cause is read[branch] with branch == -1 in lane_program (src/tsim/statevector/shared.py:208). The fuzzer saw 18 crashes, 17 of them on this line. Across about 96k circuits checked against an exact oracle, it found no wrong samples.

    🤖 Generated with Claude Code

  3. added a commit that references this issue on Nov 4, 2026
    d6b69a3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions