Conversation
Convert `for _ in range(N)` patterns to Stim `REPEAT N { ... }` blocks
when the loop body is iteration-invariant, avoiding expensive unrolling.
Key components:
- stim_cf.Repeat dialect op with emission support
- Selective Flatten that preserves REPEAT-eligible outermost loops
- MeasurementIDAnalysis scf.For handler using lattice joins for invariance
- ScfForToRepeat rewrite with unresolved record idx safety check
- HintConstInLoopBodies for propagating hints/types into loop bodies
- RemoveDeadNonStimStatements for cleanup
Passing: gates-only, rep codes, feedforward, color code, accumulators,
nested inner loops. 2 xfail: surface code memory and nested unroll
(post-loop measurement ref analysis needs further work).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…re any kind of flattening can mutate the IR
…ssing the analysis entries around
☂️ Python Coverage
Overall Coverage
New Files
Modified Files
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
… into list are accessible, not just most recent/oldest
…surement of 0 qubits
There was a problem hiding this comment.
Pull request overview
This PR completes REPEAT support by preserving REPEAT-eligible scf.For loops through the Squin→Stim pipeline, then converting them into a new stim.cf.Repeat IR node that emits Stim REPEAT { ... } blocks. It also refactors address handling to pass AddressAnalysis results directly into rewrite rules (removing the prior “wrap hints” mechanism) and extends measurement ID analysis to behave correctly with preserved loops.
Changes:
- Add
stim.cf.Repeatdialect + emitter and aScfForToRepeatrewrite to lower eligiblescf.Forloops into StimREPEATblocks. - Update the Squin→Stim pipeline to preserve outer REPEAT-eligible loops, propagate const hints/types inside those loops, and support loop-aware measurement ID analysis.
- Expand/adjust Stim golden tests and reference programs to validate REPEAT emission across multiple patterns (feedforward, accumulators, nested loops).
Reviewed changes
Copilot reviewed 42 out of 42 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/stim/passes/test_squin_noise_to_stim.py | Updates tests to pass address_analysis directly into SquinNoiseToStim (no more address wrapping). |
| test/stim/passes/test_scf_for_to_repeat.py | New end-to-end tests validating scf.For → Stim REPEAT emission across multiple loop patterns. |
| test/stim/passes/test_record_idx_helper.py | Updates helper tests to use the new IfToStimPartial(address_analysis=...) API. |
| test/stim/passes/stim_reference_programs/scf_for/test_nested_unroll.stim | New golden Stim output for nested loop unroll + outer REPEAT behavior. |
| test/stim/passes/stim_reference_programs/scf_for/surface_code_memory.stim | New golden Stim output for a larger REPEAT-based surface-code style loop. |
| test/stim/passes/stim_reference_programs/scf_for/repeat_on_gates_only.stim | New golden Stim output for gate-only loops lowered to REPEAT. |
| test/stim/passes/stim_reference_programs/scf_for/rep_code_structure.stim | New golden Stim output for repetition-code structure lowered to REPEAT. |
| test/stim/passes/stim_reference_programs/scf_for/rep_code.stim | New golden Stim output for a full repetition-code style kernel. |
| test/stim/passes/stim_reference_programs/scf_for/feedforward_inside_loop.stim | New golden Stim output validating feedforward lowering inside REPEAT loops. |
| test/stim/passes/stim_reference_programs/scf_for/color_code_init.stim | New golden Stim output validating REPEAT emission in a color-code init pattern. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_prepend_initialized_all_iters.stim | New golden Stim output validating prepend accumulator semantics across all iterations. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_prepend_initialized.stim | New golden Stim output validating prepend accumulator semantics. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_prepend_empty_init_all_iters.stim | New golden Stim output validating prepend accumulator with empty init across all iterations. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_prepend_empty_init.stim | New golden Stim output validating prepend accumulator with empty init. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_append_initialized_all_iters.stim | New golden Stim output validating append accumulator semantics across all iterations. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_append_initialized.stim | New golden Stim output validating append accumulator semantics. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_append_empty_init_all_iters.stim | New golden Stim output validating append accumulator with empty init across all iterations. |
| test/stim/passes/stim_reference_programs/scf_for/accumulator_append_empty_init.stim | New golden Stim output validating append accumulator with empty init. |
| test/stim/passes/stim_reference_programs/annotate/nested_for.stim | Updates annotate golden to emit REPEAT rather than duplicated blocks. |
| src/bloqade/stim/rewrite/util.py | Switches qubit index insertion helper to consume Address directly (no AddressAttribute). |
| src/bloqade/stim/rewrite/squin_noise.py | Refactors noise rewrite to use passed-in address_analysis instead of wrapped hints. |
| src/bloqade/stim/rewrite/squin_measure.py | Refactors measurement rewrite to use passed-in address_analysis instead of wrapped hints. |
| src/bloqade/stim/rewrite/scf_for_to_repeat.py | New rewrite lowering eligible scf.For loops to stim.cf.Repeat. |
| src/bloqade/stim/rewrite/qubit_to_stim.py | Refactors gate/reset rewrites to use passed-in address_analysis instead of wrapped hints. |
| src/bloqade/stim/rewrite/ifs_to_stim_partial.py | Refactors IfElse partial lowering to use passed-in address_analysis. |
| src/bloqade/stim/rewrite/init.py | Exports ScfForToRepeat. |
| src/bloqade/stim/passes/squin_to_stim.py | Reorders/extends pipeline: preserve loops, hint const in loops, add REPEAT lowering + cleanup. |
| src/bloqade/stim/passes/repeat_eligible.py | New helper for determining REPEAT eligibility and extracting constant loop ranges. |
| src/bloqade/stim/passes/hint_const_in_loops.py | New pass to propagate const hints/types within preserved loop bodies. |
| src/bloqade/stim/passes/flatten.py | Updates flattening/unrolling strategy to preserve outer REPEAT-eligible loops. |
| src/bloqade/stim/passes/constprop_override.py | New scf.For constprop override with early termination to avoid O(N) analysis time. |
| src/bloqade/stim/passes/cleanup_non_stim.py | New cleanup rewrite to delete dead non-stim statements after conversion. |
| src/bloqade/stim/groups.py | Adds stim_cf dialect to the Stim dialect group. |
| src/bloqade/stim/dialects/stim_cf/stmts.py | New stim.cf.Repeat statement definition. |
| src/bloqade/stim/dialects/stim_cf/emit.py | Adds Stim emitter support for Repeat → REPEAT { ... }. |
| src/bloqade/stim/dialects/stim_cf/_dialect.py | Defines the stim.cf dialect. |
| src/bloqade/stim/dialects/stim_cf/init.py | Exposes stim.cf dialect components. |
| src/bloqade/stim/dialects/init.py | Includes stim_cf in the dialect export list. |
| src/bloqade/stim/analysis/from_squin_validation/analysis.py | Extends validation to inspect func.Invoke callees for unsupported predicates. |
| src/bloqade/squin/rewrite/wrap_analysis.py | Removes the prior address-wrapping rewrite mechanism. |
| src/bloqade/squin/rewrite/init.py | Removes exports of the deleted wrap-analysis helpers. |
| src/bloqade/analysis/measure_id/impls.py | Adds empty-list neutral concat handling and a loop-aware scf.For analysis implementation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
david-pl
left a comment
There was a problem hiding this comment.
Overall, looks very good! Just some minor comments and two slightly larger things:
- I think the
counton theRepeatstatement should be aninfo.attribute. That might make a few things simpler. - I'm a bit worried about the pass that deletes statements from other dialects left at the end of the pass.
…non_stim.py), make comments self-consistent
This will be addressed in a separate issue. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The second part to #723 , finally accomplishes the desired REPEAT support behavior that has been so elusive.
Some rough edges I'm not too happy with:
squin.measureis fine, but declaring an empty list (even with explicit typing) falls apart. That's why you might see this weirdmeasure(qalloc(0))pattern.I had to override the default constprop behavior which gives a huge performance boost but the way that override occurs does not seem idiomatic to me.