Open
Description
Bug report by https://github.com/randyshee/yquantum/blob/main/2025/team_solutions/charlie_and_delta/bug_report.ipynb
QASM2 supports a measurement operation followed by classical conditional logic on those measurement results. Bloqade fails to lower this logic if there is a boolean operation as seen in the if-statement below.
from bloqade import qasm2
from bloqade.qasm2.emit import QASM2 # the QASM2 target
from bloqade.qasm2.parse import pprint # the QASM2 pretty printer
@qasm2.extended
def circ_bool():
qr = qasm2.qreg(4)
cr = qasm2.creg(4)
qasm2.measure(qr, cr)
cond = cr[0] == 0 and cr[1] == 1 # Fails to lower boolean operation on measurement results
if cond:
qasm2.x(qr[3])
# But this works instead
#if cr[0] == 0:
# qasm2.creg(0) # Dummy line to try to fix issue #157
# if cr[1] == 1:
# qasm2.x(qr[3])
return qr
target = QASM2()
ast = target.emit(circ_bool)
pprint(ast)
Error:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[1], line 29
25 return qr
28 target = QASM2()
---> 29 ast = target.emit(circ_bool)
30 pprint(ast)
File .../.venv/lib/python3.12/site-packages/bloqade/qasm2/emit/target.py:103, in QASM2.emit(self, entry)
101 Py2QASM(entry.dialects)(entry)
102 target_main = EmitQASM2Main(self.main_target)
--> 103 target_main.run(
104 entry, tuple(ast.Name(name) for name in entry.arg_names[1:])
105 ).expect()
107 main_program = target_main.output
108 assert main_program is not None, f"failed to emit {entry.sym_name}"
File .../.venv/lib/python3.12/site-packages/kirin/interp/base.py:146, in BaseInterpreter.run(self, mt, args, kwargs)
144 args = self.get_args(mt.arg_names[len(args) + 1 :], args, kwargs)
145 try:
--> 146 _, results = self.run_method(mt, args)
147 except InterpreterError as e:
148 # NOTE: initialize will create new State
149 # so we don't need to copy the frames.
150 return Err(e, self.state.frames)
File .../.venv/lib/python3.12/site-packages/bloqade/qasm2/emit/base.py:44, in EmitQASM2Base.run_method(self, method, args)
41 def run_method(
42 self, method: ir.Method, args: tuple[ast.Node | None, ...]
43 ) -> tuple[EmitQASM2Frame[StmtType], ast.Node | None]:
---> 44 return self.run_callable(method.code, (ast.Name(method.sym_name),) + args)
File .../.venv/lib/python3.12/site-packages/kirin/interp/base.py:220, in BaseInterpreter.run_callable(self, code, args)
218 return self.state.pop_frame(), self.void
219 frame.set_values(body.blocks[0].args, args)
--> 220 results = self.run_callable_region(frame, code, body)
221 return self.state.pop_frame(), results
File .../.venv/lib/python3.12/site-packages/kirin/emit/abc.py:26, in EmitABC.run_callable_region(self, frame, code, region)
23 def run_callable_region(
24 self, frame: FrameType, code: ir.Statement, region: ir.Region
25 ) -> ValueType:
---> 26 results = self.eval_stmt(frame, code)
27 if isinstance(results, tuple):
28 if len(results) == 0:
File .../.venv/lib/python3.12/site-packages/kirin/interp/base.py:335, in BaseInterpreter.eval_stmt(self, frame, stmt)
333 method = self.lookup_registry(frame, stmt)
334 if method is not None:
--> 335 results = method(self, frame, stmt)
336 if self.debug and not isinstance(results, (tuple, SpecialValue)):
337 raise InterpreterError(
338 f"method must return tuple or SpecialResult, got {results}"
339 )
File .../.venv/lib/python3.12/site-packages/kirin/registry.py:19, in StatementImpl.__call__(self, interp, frame, stmt)
18 def __call__(self, interp, frame, stmt: "Statement"):
---> 19 return self.impl(self.parent, interp, frame, stmt)
File .../.venv/lib/python3.12/site-packages/bloqade/qasm2/emit/main.py:26, in Func.emit_func(self, emit, frame, stmt)
20 @interp.impl(func.Function)
21 def emit_func(
22 self, emit: EmitQASM2Main, frame: EmitQASM2Frame, stmt: func.Function
23 ):
24 from bloqade.qasm2.dialects import glob, noise, parallel
---> 26 emit.run_ssacfg_region(frame, stmt.body)
27 if emit.dialects.data.intersection(
28 (parallel.dialect, glob.dialect, noise.dialect)
29 ):
30 header = ast.Kirin([dialect.name for dialect in emit.dialects])
File .../.venv/lib/python3.12/site-packages/kirin/emit/abc.py:41, in EmitABC.run_ssacfg_region(self, frame, region)
37 frame.worklist.append(
38 interp.Successor(region.blocks[0], frame.get_values(region.blocks[0].args))
39 )
40 while (succ := frame.worklist.pop()) is not None:
---> 41 block_header = self.emit_block(frame, succ.block)
42 frame.block_ref[succ.block] = block_header
43 return ()
File .../.venv/lib/python3.12/site-packages/bloqade/qasm2/emit/base.py:48, in EmitQASM2Base.emit_block(self, frame, block)
46 def emit_block(self, frame: EmitQASM2Frame, block: ir.Block) -> ast.Node | None:
47 for stmt in block.stmts:
---> 48 result = self.eval_stmt(frame, stmt)
49 if isinstance(result, tuple):
50 frame.set_values(stmt.results, result)
File .../.venv/lib/python3.12/site-packages/kirin/interp/base.py:345, in BaseInterpreter.eval_stmt(self, frame, stmt)
341 elif stmt.dialect not in self.dialects:
342 # NOTE: we should terminate the interpreter because this is a
343 # deveoper error, not a user error.
344 name = stmt.dialect.name if stmt.dialect else "None"
--> 345 raise ValueError(f"dialect {name} is not supported by {self.dialects}")
347 return self.eval_stmt_fallback(frame, stmt)
ValueError: dialect py.boolop is not supported by DialectGroup([func, scf, qasm2.uop, qasm2.indexing, qasm2.core, lowering.func, lowering.call, qasm2.expr])