Open
Description
This bug might also apply to other parallel gates; I haven't checked. Bug discovered with the help of chikinnunget and team at YQuantum.
Example:
from bloqade import qasm2
from bloqade.pyqrack import PyQrack
import math
from collections import Counter
pi = math.pi
qft, main = None, None
del qft, main
@qasm2.extended
def qft(qreg: qasm2.QReg, n: int, k: int):
if k == n:
return
qasm2.parallel.u(
# ====== Bug is here ======
qreg, # Directly specifying the register doesn't work
#[qreg[0], qreg[1], qreg[2], qreg[3]], # Manual list works?!
# =========================
theta=math.pi/2, # Specify the theta angle
phi=0, # Specify the phi angle
lam=math.pi # Specify the lambda angle
)
for i in range(k + 1, n, 1):
qasm2.cu1(qreg[i], qreg[k], 2 * pi / 2**i)
qft(qreg, n, k + 1) # recursion
@qasm2.extended
def main():
qreg = qasm2.qreg(4)
creg = qasm2.creg(4)
qft(qreg, 4, 0)
qasm2.measure(qreg[0], creg[0])
qasm2.measure(qreg[1], creg[1])
qasm2.measure(qreg[2], creg[2])
qasm2.measure(qreg[3], creg[3])
return creg
device = PyQrack(dynamic_qubits=True, pyqrack_options={"isBinaryDecisionTree": False})
results = device.multi_run(main, _shots=100)
def to_bitstrings(results):
return Counter(map(lambda result:"".join(map(str, result)), results))
counts = to_bitstrings(results)
for key, value in counts.items():
print(key, value)
Output:
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[3], line 40
37 return creg
39 device = PyQrack(dynamic_qubits=True, pyqrack_options={"isBinaryDecisionTree": False})
---> 40 results = device.multi_run(main, _shots=100)
41 def to_bitstrings(results):
42 return Counter(map(lambda result:"".join(map(str, result)), results))
File .../.venv/lib/python3.12/site-packages/bloqade/pyqrack/target.py:109, in PyQrack.multi_run(self, mt, _shots, *args, **kwargs)
107 batched_results = []
108 for _ in range(_shots):
--> 109 batched_results.append(interpreter.run(mt, args, kwargs).expect())
111 return batched_results
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/kirin/interp/concrete.py:30, in Interpreter.run_method(self, method, args)
27 def run_method(
28 self, method: Method, args: tuple[Any, ...]
29 ) -> tuple[Frame[Any], Any]:
---> 30 return self.run_callable(method.code, (method,) + 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/interp/base.py:234, in BaseInterpreter.run_callable_region(self, frame, code, region)
223 def run_callable_region(
224 self, frame: FrameType, code: Statement, region: Region
225 ) -> ValueType:
226 """A hook defines how to run the callable region given
227 the interpreter context. Frame should be pushed before calling
228 this method and popped after calling this method.
(...) 232 to be compatible with the Python convention.
233 """
--> 234 results = self.run_ssacfg_region(frame, region)
235 if isinstance(results, ReturnValue):
236 return results.value
File .../.venv/lib/python3.12/site-packages/kirin/interp/concrete.py:37, in Interpreter.run_ssacfg_region(self, frame, region)
35 block = region.blocks[0]
36 while block is not None:
---> 37 results = self.run_block(frame, block)
38 if isinstance(results, Successor):
39 block = results.block
File .../.venv/lib/python3.12/site-packages/kirin/interp/concrete.py:55, in Interpreter.run_block(self, frame, block)
53 frame.stmt = stmt
54 frame.lino = stmt.source.lineno if stmt.source else 0
---> 55 stmt_results = self.eval_stmt(frame, stmt)
56 if isinstance(stmt_results, tuple):
57 frame.set_values(stmt._results, stmt_results)
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/kirin/dialects/func/interp.py:30, in Interpreter.invoke(self, interp, frame, stmt)
28 @impl(Invoke)
29 def invoke(self, interp: concrete.Interpreter, frame: Frame, stmt: Invoke):
---> 30 _, result = interp.run_method(
31 stmt.callee,
32 interp.permute_values(
33 stmt.callee.arg_names, frame.get_values(stmt.inputs), stmt.kwargs
34 ),
35 )
36 return (result,)
File .../.venv/lib/python3.12/site-packages/kirin/interp/concrete.py:30, in Interpreter.run_method(self, method, args)
27 def run_method(
28 self, method: Method, args: tuple[Any, ...]
29 ) -> tuple[Frame[Any], Any]:
---> 30 return self.run_callable(method.code, (method,) + 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/interp/base.py:234, in BaseInterpreter.run_callable_region(self, frame, code, region)
223 def run_callable_region(
224 self, frame: FrameType, code: Statement, region: Region
225 ) -> ValueType:
226 """A hook defines how to run the callable region given
227 the interpreter context. Frame should be pushed before calling
228 this method and popped after calling this method.
(...) 232 to be compatible with the Python convention.
233 """
--> 234 results = self.run_ssacfg_region(frame, region)
235 if isinstance(results, ReturnValue):
236 return results.value
File .../.venv/lib/python3.12/site-packages/kirin/interp/concrete.py:37, in Interpreter.run_ssacfg_region(self, frame, region)
35 block = region.blocks[0]
36 while block is not None:
---> 37 results = self.run_block(frame, block)
38 if isinstance(results, Successor):
39 block = results.block
File .../.venv/lib/python3.12/site-packages/kirin/interp/concrete.py:55, in Interpreter.run_block(self, frame, block)
53 frame.stmt = stmt
54 frame.lino = stmt.source.lineno if stmt.source else 0
---> 55 stmt_results = self.eval_stmt(frame, stmt)
56 if isinstance(stmt_results, tuple):
57 frame.set_values(stmt._results, stmt_results)
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/pyqrack/qasm2/parallel.py:34, in PyQrackMethods.ugate(self, interp, frame, stmt)
28 theta, phi, lam = (
29 frame.get(stmt.theta),
30 frame.get(stmt.phi),
31 frame.get(stmt.lam),
32 )
33 for qarg in qargs:
---> 34 if qarg.is_active():
35 interp.memory.sim_reg.u(qarg.addr, theta, phi, lam)
36 return ()
File .../.venv/lib/python3.12/site-packages/bloqade/pyqrack/reg.py:105, in PyQrackQubit.is_active(self)
98 def is_active(self) -> bool:
99 """Check if the qubit is active.
100
101 Returns
102 True if the qubit is active, False otherwise.
103
104 """
--> 105 return self.ref.qubit_state[self.pos] is QubitState.Active
IndexError: list index out of range