Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
8cc90c4
bring record analysis work over
johnzl-777 Nov 9, 2025
4e56293
save wip test_record_analysis
johnzl-777 Nov 10, 2025
7c1c306
Merge branch 'main' into john/repeat-support
johnzl-777 Nov 10, 2025
8115bf4
Merge branch 'main' into john/repeat-support
johnzl-777 Nov 11, 2025
eee712d
update to new kirin
johnzl-777 Nov 11, 2025
4e1da7b
almost there, still a problem with invariance checking
johnzl-777 Nov 17, 2025
09130c6
loop invariance support complete
johnzl-777 Nov 18, 2025
cf5cb1f
add a set observable just to really make sure things work properly
johnzl-777 Nov 18, 2025
43675fd
fix variable names in test
johnzl-777 Nov 20, 2025
d19f3ef
move record analysis prototype into measure id analysis
johnzl-777 Nov 21, 2025
6bbf11f
add REPEAT statement in stim
johnzl-777 Nov 21, 2025
ca354af
save work before trying to hammer down obnoxious ownership issues
johnzl-777 Nov 23, 2025
3f7ad41
corrected logic in record analysis prototype with Kai's advice, can s…
johnzl-777 Nov 24, 2025
d4113e6
get rid of a bunch of debug print statements
johnzl-777 Nov 24, 2025
4920273
remove more record analysis debug prints and move everything into mea…
johnzl-777 Nov 24, 2025
a8da761
try to merge in latest work
johnzl-777 Dec 4, 2025
79880d4
Merge branch 'main' into john/repeat-support
johnzl-777 Dec 7, 2025
a39a126
latest attempt to try to reconcile type lattices
johnzl-777 Dec 8, 2025
8c5f453
Merge branch 'main' into john/repeat-support
johnzl-777 Dec 8, 2025
b4c4559
still need to find a solution to proper IfElse handling
johnzl-777 Dec 9, 2025
5aa3bfc
Merge branch 'main' into john/repeat-support
johnzl-777 Dec 9, 2025
927fcd8
figured out way to handle scf.IfElse with new lattice
johnzl-777 Dec 9, 2025
4d62a47
get decent portion of qubit and annotate to stim tests fully working,…
johnzl-777 Dec 10, 2025
5013602
delay CSE due to getitem uniqueness issues
johnzl-777 Dec 10, 2025
4da0eed
Merge branch 'main' into john/repeat-support
johnzl-777 Dec 10, 2025
9be26cd
fix coordinate conversion with ilist as the new type for set_detector
johnzl-777 Dec 11, 2025
782ca80
caught some problems with coordinate rewrite as well as address analy…
johnzl-777 Dec 12, 2025
356dcfd
need to remove dead measures, confirm full repetition code works
johnzl-777 Dec 12, 2025
d4858d3
remove debug print statement
johnzl-777 Dec 12, 2025
1b4fbd4
fix test failures with coordinates
johnzl-777 Dec 15, 2025
32a6582
restore test functionality, missed the measurement inbetween to get t…
johnzl-777 Dec 15, 2025
0fec8ba
finish turning development examples into proper tests
johnzl-777 Dec 16, 2025
9ec62d4
finally let go of the original record analysis. Farewell old friend
johnzl-777 Dec 16, 2025
d4f2bb3
Merge branch 'main' into john/repeat-support
weinbe58 Dec 17, 2025
41c663f
make emit test cleaner
johnzl-777 Jan 8, 2026
7312190
Merge branch 'main' into john/repeat-support
johnzl-777 Jan 8, 2026
3ff722e
get Rafael's latest work to pass with current infrastructure
johnzl-777 Jan 8, 2026
54d3d15
resolve merge conflict with main
johnzl-777 Jan 20, 2026
0c57b70
work with explicit observable idx
johnzl-777 Jan 20, 2026
c0e9203
add surface code memory test
johnzl-777 Jan 22, 2026
8e61f22
add fix for unrolling things that live in REPEATable loops
johnzl-777 Jan 25, 2026
216331b
completed surface code memory test. Coordinates need some additional …
johnzl-777 Jan 25, 2026
42253ed
update pre-commit to stay in line with CI
johnzl-777 Jan 25, 2026
2d0e023
update pre-commit to stay in line with CI
johnzl-777 Jan 25, 2026
26b2477
fix incorrect coordinate format causing test to fail
johnzl-777 Jan 25, 2026
4756567
resolve merge conflict with main
johnzl-777 Jan 27, 2026
2bb15cf
get rid of unnecessary comment
johnzl-777 Jan 29, 2026
e6a2444
add missing logic for predicate handling
johnzl-777 Jan 29, 2026
9f44948
Merge branch 'main' into john/repeat-support
johnzl-777 Jan 29, 2026
4cdc9dc
get rid of unnecessary test
johnzl-777 Jan 29, 2026
92f2d4d
get another test to pass
johnzl-777 Jan 29, 2026
6e05d6f
remove another xfail test, unroll works as expected with REPEAT
johnzl-777 Jan 29, 2026
8e4f8f6
get multiple ifs on same predicated measurement to work
johnzl-777 Feb 2, 2026
dc88348
change the MeasurementIDAnalysis type lattice so there is no need for…
johnzl-777 Feb 2, 2026
85c7bba
Merge branch 'main' into john/repeat-support
johnzl-777 Feb 2, 2026
afc09ba
get rid of old comment
johnzl-777 Feb 3, 2026
f49b915
Merge branch 'main' into john/repeat-support
johnzl-777 Feb 3, 2026
41e7427
provide information on reason for xfail marker
johnzl-777 Feb 3, 2026
711e7dd
re-enable ALL CSE, remove dependence on special CSE ordering altogether
johnzl-777 Feb 3, 2026
5ee87d0
remove old redundant unit test
johnzl-777 Feb 4, 2026
d6ef809
add additional logic to restore IfElse measurement ID analysis
johnzl-777 Feb 4, 2026
ae23aa5
resolve merge conflict with new analysis from Phil
johnzl-777 Feb 5, 2026
146cc4b
rename some things I didn't agree with Claude/Cursor on
johnzl-777 Feb 5, 2026
39595a9
implement the easier revisions from Phil's feedback
johnzl-777 Feb 5, 2026
36a54d8
decouple the types from the GlobalRecordState tracking
johnzl-777 Feb 5, 2026
f10e37d
incorporate more feedback
johnzl-777 Feb 5, 2026
7f63fe3
remove assertions in ScfForToStim
johnzl-777 Feb 5, 2026
47d2bff
add coordinate propogation infrastructure to measurement ID analysis
johnzl-777 Feb 6, 2026
f44f025
get rid of index filling logic, it doesn't match the semantics proper…
johnzl-777 Feb 10, 2026
a49959e
tidy some of the helper functions up
johnzl-777 Feb 13, 2026
2df087c
get support for accumulation semantics completed
johnzl-777 Feb 16, 2026
cd62823
Merge branch 'main' into john/repeat-support
johnzl-777 Feb 16, 2026
f3312ea
add some clarifying comments
johnzl-777 Feb 17, 2026
cf95d84
allow negative indices + detectors to work in loop
johnzl-777 Feb 17, 2026
37f916e
correct the color code init test, gates definitely wrong for subkernel
johnzl-777 Feb 18, 2026
fe1487d
Merge branch 'main' into john/repeat-support
johnzl-777 Feb 18, 2026
5007f01
Merge branch 'main' into john/repeat-support
johnzl-777 Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ mch = "mch"
IY = "IY"
ket = "ket"
typ = "typ"
anc_qs = "anc_qs" # "ancilla qubits" variable abbreivation - used to be autocorrected to AND_QS! :sigh:
OT = "OT"
2 changes: 1 addition & 1 deletion src/bloqade/analysis/measure_id/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import impls as impls
from . import util as util, impls as impls
from .analysis import (
MeasureIDFrame as MeasureIDFrame,
MeasurementIDAnalysis as MeasurementIDAnalysis,
Expand Down
110 changes: 110 additions & 0 deletions src/bloqade/analysis/measure_id/accumulator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""
Helper functions for detecting accumulator semantics
(chiefly the pattern of using lists to store a growing number of measurements)
and expanding them with the correct number of RawMeasureId objects in the correct order.
"""

from kirin import ir
from kirin.dialects import py, scf

from .util import get_scf_for_repeat_count
from .lattice import (
MeasureId,
MutableIdx,
AnyMeasureId,
RawMeasureId,
MeasureIdTuple,
)
from .analysis import MeasureIDFrame


def is_growing_accumulator(
init_var: MeasureId,
first_var: MeasureId,
second_var: MeasureId,
) -> bool:
if not (
isinstance(init_var, MeasureIdTuple)
and isinstance(first_var, MeasureIdTuple)
and isinstance(second_var, MeasureIdTuple)
):
return False

first_growth = len(first_var.data) - len(init_var.data)
second_growth = len(second_var.data) - len(first_var.data)
return first_growth > 0 and first_growth == second_growth


def detect_append_order_from_ir(
stmt: scf.stmts.For,
var_idx: int,
) -> bool | None:
"""Detect append vs prepend by inspecting the py.Add in the loop body.

Returns True for append (acc + ms), False for prepend (ms + acc),
or None if the pattern cannot be determined from the IR.
"""
body_block = stmt.body.blocks[0]
acc_block_arg = body_block.args[var_idx + 1]

yield_stmt = body_block.last_stmt
if not isinstance(yield_stmt, scf.stmts.Yield):
return None

yielded_val = yield_stmt.values[var_idx]
if not isinstance(yielded_val, ir.ResultValue):
return None

add_stmt = yielded_val.owner
if not isinstance(add_stmt, py.Add):
return None

if add_stmt.lhs is acc_block_arg:
return True
if add_stmt.rhs is acc_block_arg:
return False
return None


def expand_accumulator(
stmt: scf.stmts.For,
frame: MeasureIDFrame,
init_var: MeasureIdTuple,
first_var: MeasureIdTuple,
is_append: bool,
) -> MeasureId:
"""Expand a growing accumulator to its final concrete MeasureIdTuple.

Uses the statically known loop count to compute the total number of
accumulated measurements and constructs the final tuple with correct
MutableIdx values registered in the parent GlobalRecordState.
"""
num_iterations = get_scf_for_repeat_count(stmt)
if num_iterations is None:
return AnyMeasureId()

measurements_per_iter = len(first_var.data) - len(init_var.data)
total_new_measurements = num_iterations * measurements_per_iter

frame.global_record_state.offset_existing_records(total_new_measurements)

new_mutable_idxs = [MutableIdx(-i) for i in range(total_new_measurements, 0, -1)]
frame.global_record_state.buffer.extend(new_mutable_idxs)
new_raw_ids = [RawMeasureId(idx) for idx in new_mutable_idxs]

if is_append:
final_data = init_var.data + tuple(new_raw_ids)
else:
# you're prepending here, so your ordering is "staggered"
# ex: acc = ms + acc where acc is (-3, -2, -1)
# the ms happens on 3 qubits that bumps your existing acc to (-6, -5, -4)
# then you need to add your (-3, -2, -1) to the FRONT of the tuple so it looks like (-3, -2, -1, -6, -5, -4)
chunks: list[RawMeasureId] = []
for i in range(num_iterations - 1, -1, -1):
chunk = new_raw_ids[
i * measurements_per_iter : (i + 1) * measurements_per_iter
]
chunks.extend(chunk)
final_data = tuple(chunks) + init_var.data

return MeasureIdTuple(data=final_data, obj_type=first_var.obj_type)
81 changes: 75 additions & 6 deletions src/bloqade/analysis/measure_id/analysis.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,95 @@
from typing import Iterable
from dataclasses import field, dataclass

from kirin import ir
from kirin.analysis import ForwardExtra
from kirin.dialects import ilist
from typing_extensions import Self
from kirin.analysis.forward import ForwardFrame

from .lattice import MeasureId, NotMeasureId
from .lattice import (
MeasureId,
MutableIdx,
NotMeasureId,
RawMeasureId,
MeasureIdTuple,
)


@dataclass
class GlobalRecordState:
type_for_scf_conds: dict[ir.Statement, MeasureId] = field(default_factory=dict)
buffer: list[MutableIdx] = field(default_factory=list)

def add_record_idxs(self, num_new_records: int) -> MeasureIdTuple:
# Adjust all previous indices
for mutable_idx in self.buffer:
mutable_idx.value -= num_new_records

# Generate new MutableIdx entries and add to buffer
new_mutable_idxs = [MutableIdx(-i) for i in range(num_new_records, 0, -1)]
self.buffer += new_mutable_idxs

# Create RawMeasureIds referencing these MutableIdxs
new_record_idxs = [RawMeasureId(idx) for idx in new_mutable_idxs]
return MeasureIdTuple(data=tuple(new_record_idxs), obj_type=ilist.IList)

def clone_measure_id_tuple(
self, measure_id_tuple: MeasureIdTuple
) -> MeasureIdTuple:
cloned_members = []
for measure_id in measure_id_tuple.data:
cloned_measure_id = self.clone_measure_ids(measure_id)
cloned_members.append(cloned_measure_id)
return MeasureIdTuple(
data=tuple(cloned_members),
obj_type=measure_id_tuple.obj_type,
)

def clone_raw_measure_id(self, raw_measure_id: RawMeasureId) -> RawMeasureId:
# Create new MutableIdx for independent tracking
new_mutable_idx = MutableIdx(raw_measure_id.idx)
self.buffer.append(new_mutable_idx)

# Return RawMeasureId with same predicate, referencing new MutableIdx
return RawMeasureId(new_mutable_idx, predicate=raw_measure_id.predicate)

def clone_measure_ids(self, measure_id_type: MeasureId) -> MeasureId:
if isinstance(measure_id_type, RawMeasureId):
return self.clone_raw_measure_id(measure_id_type)
elif isinstance(measure_id_type, MeasureIdTuple):
return self.clone_measure_id_tuple(measure_id_type)
return None

def offset_existing_records(self, offset: int):
for mutable_idx in self.buffer:
mutable_idx.value -= offset

def add_unique_mutable_idxs(self, mutable_idxs: Iterable[MutableIdx]) -> None:
"""Add MutableIdx objects to buffer, skipping duplicates."""
existing = set(self.buffer)
for idx in mutable_idxs:
if idx not in existing:
existing.add(idx)
self.buffer.append(idx)


@dataclass
class MeasureIDFrame(ForwardFrame[MeasureId]):
num_measures_at_stmt: dict[ir.Statement, int] = field(default_factory=dict)
# Keeps track of every measurement's record index, containing methods to
# update and offset record indices as new measurements/loops are encountered.
global_record_state: GlobalRecordState = field(default_factory=GlobalRecordState)
# Associates with each scf.IfElse statement encountered the lattice element
# of the condition, which is detached from the GlobalRecordState to
# prevent further mutation as more measurements are encountered.
type_for_scf_conds: dict[ir.Statement, MeasureId] = field(default_factory=dict)
measure_count_offset: int = 0


class MeasurementIDAnalysis(ForwardExtra[MeasureIDFrame, MeasureId]):

keys = ["measure_id"]
lattice = MeasureId
# for every kind of measurement encountered, increment this
# then use this to generate the negative values for target rec indices
measure_count = 0
detector_count = 0
observable_count = 0
Expand All @@ -34,8 +105,6 @@ def initialize_frame(
) -> MeasureIDFrame:
return MeasureIDFrame(node, has_parent_access=has_parent_access)

# Still default to bottom,
# but let constants return the softer "NoMeasureId" type from impl
def eval_fallback(
self, frame: ForwardFrame[MeasureId], node: ir.Statement
) -> tuple[MeasureId, ...]:
Expand Down
Loading
Loading