Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9f82fc7
merging locally
weinbe58 Oct 17, 2025
74d530e
merging main
weinbe58 Oct 18, 2025
62e221b
adding fallback to const prop
weinbe58 Oct 21, 2025
dc51d3f
updating lattice
weinbe58 Oct 21, 2025
b9ec5c5
finalizing analysis impl
weinbe58 Oct 22, 2025
3129392
implementing joint analysis method
weinbe58 Oct 23, 2025
a65181c
refactor implmentation
weinbe58 Oct 23, 2025
522174d
Fixing bugs
weinbe58 Oct 23, 2025
966712a
fixing bug in call mix-in
weinbe58 Oct 24, 2025
693a94c
Fixing tests
weinbe58 Oct 24, 2025
39cb5d9
fixing more tests, working on stim
weinbe58 Oct 24, 2025
dad13b8
Merge branch 'main' into phil/new-address-analysis
weinbe58 Oct 24, 2025
9476d62
ficing lint
weinbe58 Oct 24, 2025
d4e9ebf
fixing stim rewrite
weinbe58 Oct 24, 2025
dd3c8d1
updating lock file
weinbe58 Oct 24, 2025
e416038
Adding doc strings to lattice
weinbe58 Oct 24, 2025
5eb5b36
Merge branch 'phil/new-address-analysis' of https://github.com/QuEraC…
weinbe58 Oct 24, 2025
47cffd5
Merge branch 'main' into phil/new-address-analysis
weinbe58 Oct 27, 2025
fe8a00d
Apply suggestion from @weinbe58
weinbe58 Oct 27, 2025
cbae0c1
Apply suggestion from @weinbe58
weinbe58 Oct 27, 2025
f42a0c1
Apply suggestion from @weinbe58
weinbe58 Oct 27, 2025
38c3391
updating lock file
weinbe58 Oct 27, 2025
f01c04b
Fixing tests
weinbe58 Oct 27, 2025
204f8a1
Adding test of partial tuple constant and fixing bug
weinbe58 Oct 27, 2025
5cc6a5e
Adding another test for partial tuple
weinbe58 Oct 27, 2025
c36c8b1
Fixing interface for extracting iterable values
weinbe58 Oct 27, 2025
b95646f
Fixing more bugs
weinbe58 Oct 27, 2025
478350d
Merge branch 'main' into phil/new-address-analysis
weinbe58 Oct 28, 2025
5992cb0
Adding more doc strings
weinbe58 Oct 28, 2025
05fab4a
adding TODOs
weinbe58 Oct 29, 2025
4dbee48
adding methods to bottom if joining different types
weinbe58 Oct 29, 2025
ae2bddf
moving mixin methods to interpreter
weinbe58 Oct 29, 2025
d0e04e7
Removing dup code
weinbe58 Oct 29, 2025
770cdb6
Apply suggestion from @david-pl
weinbe58 Oct 29, 2025
5163f6d
removing unneccesary line update
weinbe58 Oct 29, 2025
6b1f9ba
Merge branch 'phil/new-address-analysis' of https://github.com/QuEraC…
weinbe58 Oct 29, 2025
62e7f2a
simplifying some logic with new interp method
weinbe58 Oct 29, 2025
1168242
Merge branch 'main' into phil/new-address-analysis
david-pl Oct 29, 2025
93b41b9
renaming variable
weinbe58 Oct 30, 2025
a259ba4
Merge branch 'main' into phil/new-address-analysis
weinbe58 Oct 30, 2025
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
12 changes: 8 additions & 4 deletions src/bloqade/analysis/address/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from . import impls as impls
from .lattice import (
Bottom as Bottom,
Address as Address,
NotQubit as NotQubit,
Unknown as Unknown,
AddressReg as AddressReg,
AnyAddress as AnyAddress,
AddressWire as AddressWire,
UnknownReg as UnknownReg,
ConstResult as ConstResult,
AddressQubit as AddressQubit,
AddressTuple as AddressTuple,
PartialIList as PartialIList,
PartialTuple as PartialTuple,
UnknownQubit as UnknownQubit,
PartialLambda as PartialLambda,
)
from .analysis import AddressAnalysis as AddressAnalysis
148 changes: 119 additions & 29 deletions src/bloqade/analysis/address/analysis.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from typing import TypeVar
from typing import Any, Type, TypeVar
from dataclasses import field

from kirin import ir, interp
from kirin import ir, types, interp
from kirin.analysis import Forward, const
from kirin.dialects.ilist import IList
from kirin.analysis.forward import ForwardFrame
from kirin.analysis.const.lattice import PartialLambda

from bloqade.types import QubitType

from .lattice import Address
from .lattice import Address, AddressReg, ConstResult, PartialIList, PartialTuple


class AddressAnalysis(Forward[Address]):
Expand All @@ -16,12 +16,15 @@ class AddressAnalysis(Forward[Address]):
"""

keys = ["qubit.address"]
_const_prop: const.Propagate
lattice = Address
next_address: int = field(init=False)

def initialize(self):
super().initialize()
self.next_address: int = 0
self._const_prop = const.Propagate(self.dialects)
self._const_prop.initialize()
return self

@property
Expand All @@ -31,30 +34,117 @@ def qubit_count(self) -> int:

T = TypeVar("T")

def get_const_value(self, typ: type[T], value: ir.SSAValue) -> T:
if isinstance(hint := value.hints.get("const"), const.Value):
data = hint.data
if isinstance(data, typ):
return hint.data
raise interp.InterpreterError(
f"Expected constant value <type = {typ}>, got {data}"
)
raise interp.InterpreterError(
f"Expected constant value <type = {typ}>, got {value}"
)

def eval_stmt_fallback(
self, frame: ForwardFrame[Address], stmt: ir.Statement
) -> tuple[Address, ...] | interp.SpecialValue[Address]:
return tuple(
(
self.lattice.top()
if result.type.is_subseteq(QubitType)
else self.lattice.bottom()
)
for result in stmt.results
)
def to_address(self, result: const.Result):
return ConstResult(result)

def try_eval_const_prop(
self,
frame: ForwardFrame[Address],
stmt: ir.Statement,
args: tuple[ConstResult, ...],
) -> interp.StatementResult[Address]:
_frame = self._const_prop.initialize_frame(frame.code)
_frame.set_values(stmt.args, tuple(x.result for x in args))
result = self._const_prop.eval_stmt(_frame, stmt)

match result:
case interp.ReturnValue(constant_ret):
return interp.ReturnValue(self.to_address(constant_ret))
case interp.YieldValue(constant_values):
return interp.YieldValue(tuple(map(self.to_address, constant_values)))
case interp.Successor(block, block_args):
return interp.Successor(block, *map(self.to_address, block_args))
case tuple():
return tuple(map(self.to_address, result))
case _:
return result

def unpack_iterable(self, iterable: Address):
"""Extract the values of a container lattice element.

Args:
iterable: The lattice element representing a container.

Returns:
A tuple of the container type and the contained values.

"""

def from_constant(constant: const.Result) -> Address:
return ConstResult(constant)

def from_literal(literal: Any) -> Address:
return ConstResult(const.Value(literal))

match iterable:
case PartialIList(data):
return PartialIList, data
case PartialTuple(data):
return PartialTuple, data
case AddressReg():
return PartialIList, iterable.qubits
case ConstResult(const.Value(IList() as data)):
return PartialIList, tuple(map(from_literal, data))
case ConstResult(const.Value(tuple() as data)):
return PartialTuple, tuple(map(from_literal, data))
case ConstResult(const.PartialTuple(data)):
return PartialTuple, tuple(map(from_constant, data))
case _:
return None, ()

def run_lattice(
self,
callee: Address,
inputs: tuple[Address, ...],
kwargs: tuple[str, ...],
) -> Address:
"""Run a callable lattice element with the given inputs and keyword arguments.

Args:
callee (Address): The lattice element representing the callable.
inputs (tuple[Address, ...]): The input lattice elements.
kwargs (tuple[str, ...]): The keyword argument names.

Returns:
Address: The resulting lattice element after invoking the callable.

"""

match callee:
case PartialLambda(code=code, argnames=argnames):
_, ret = self.run_callable(
code, (callee,) + self.permute_values(argnames, inputs, kwargs)
)
return ret
case ConstResult(const.Value(ir.Method() as method)):
_, ret = self.run_method(
method,
self.permute_values(method.arg_names, inputs, kwargs),
)
return ret
case _:
return Address.top()

def get_const_value(self, addr: Address, typ: Type[T]) -> T | None:
if not isinstance(addr, ConstResult):
return None

if not isinstance(result := addr.result, const.Value):
return None

if not isinstance(value := result.data, typ):
return None

return value

def eval_stmt_fallback(self, frame: ForwardFrame[Address], stmt: ir.Statement):
args = frame.get_values(stmt.args)
if types.is_tuple_of(args, ConstResult):
return self.try_eval_const_prop(frame, stmt, args)

return tuple(Address.from_type(result.type) for result in stmt.results)

def run_method(self, method: ir.Method, args: tuple[Address, ...]):
# NOTE: we do not support dynamic calls here, thus no need to propagate method object
return self.run_callable(method.code, (self.lattice.bottom(),) + args)
self_mt = ConstResult(const.Value(method))
return self.run_callable(method.code, (self_mt,) + args)
Loading