Skip to content

PyQrack fails to simulate qasm2.parallel.u ONLY IF qreg is passed as is (works with IList of qubits) #161

Open
@cduck

Description

@cduck

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpyqrackissues related to pyqrack target or upstream pyqrack

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions