Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
20 changes: 19 additions & 1 deletion src/bloqade/pyqrack/qasm2/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from typing import Any

from kirin import interp
from kirin.dialects import ilist

from bloqade.pyqrack.reg import (
CBitRef,
Expand All @@ -9,7 +12,7 @@
PyQrackQubit,
)
from bloqade.pyqrack.base import PyQrackInterpreter
from bloqade.qasm2.dialects import core
from bloqade.qasm2.dialects import core, glob


@core.dialect.register(key="pyqrack")
Expand Down Expand Up @@ -77,3 +80,18 @@ def creg_eq(
return (False,)

return (all(left is right for left, right in zip(lhs, rhs)),)

@interp.impl(glob.UGate)
def ugate(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: glob.UGate):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method needs to be implemented in a method table registered by glob.dialect. The convention is to put a method table inside a module with the same name as the dialect.

registers: ilist.IList[PyQrackReg, Any] = frame.get(stmt.registers)
theta, phi, lam = (
frame.get(stmt.theta),
frame.get(stmt.phi),
frame.get(stmt.lam),
)

for qreg in registers:
for qarg in qreg:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also implement the __iter__ magic method for PyQrackReg to get the correct behavior here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any preference? I mean, it works just fine with using __getitem__ and an IndexError, but it's probably not the cleanest implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a TODO to clean up the whole class entirely I think we could rework the implementation to use list as a base class to avoid all of these issues. But this should be a lower priority.

add something by PyQrackReg to TODO: Clean up implementation with list base class.

if qarg.is_active():
interp.memory.sim_reg.u(qarg.addr, theta, phi, lam)
return ()
4 changes: 3 additions & 1 deletion src/bloqade/pyqrack/reg.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class CBitRef:
pos: int
"""The position of this bit in the classical register."""

def set_value(self, value: bool):
def set_value(self, value: Measurement):
self.ref[self.pos] = value

def get_value(self):
Expand Down Expand Up @@ -72,6 +72,8 @@ def drop(self, pos: int):
self.qubit_state[pos] = QubitState.Lost

def __getitem__(self, pos: int):
if not 0 <= pos < self.size:
raise IndexError("Qubit index out of bounds of register.")
return PyQrackQubit(self, pos)


Expand Down
2 changes: 1 addition & 1 deletion src/bloqade/qasm2/glob.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

@wraps(glob.UGate)
def u(
theta: float, phi: float, lam: float, registers: ilist.IList[QReg, Any] | list
registers: ilist.IList[QReg, Any] | list, theta: float, phi: float, lam: float
) -> None:
"""Apply a U gate to all qubits in the input registers.

Expand Down
71 changes: 71 additions & 0 deletions test/pyqrack/test_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,74 @@ def ghz():
assert math.isclose(out[0].real, val, abs_tol=abs_tol)
assert math.isclose(out[-1].real, val, abs_tol=abs_tol)
assert all(math.isclose(ele.real, 0.0, abs_tol=abs_tol) for ele in out[1:-1])


def test_target_glob():
@qasm2.extended
def global_h():
q = qasm2.qreg(3)

# rotate around Y by pi/2, i.e. perform a hadamard
qasm2.glob.u([q], math.pi / 2.0, 0, 0)

return q

target = PyQrack(3)
q = target.run(global_h)

assert isinstance(q, reg.PyQrackReg)

out = q.sim_reg.out_ket()

# remove global phase introduced by pyqrack
phase = out[0] / abs(out[0])
out = [ele / phase for ele in out]

for element in out:
assert math.isclose(element.real, 1 / math.sqrt(8), abs_tol=2.2e-7)
assert math.isclose(element.imag, 0, abs_tol=2.2e-7)

@qasm2.extended
def multiple_registers():
q1 = qasm2.qreg(2)
q2 = qasm2.qreg(2)
q3 = qasm2.qreg(2)

# hadamard on first register
qasm2.glob.u(
[q1],
math.pi / 2.0,
0,
0,
)

# apply hadamard to the other two
qasm2.glob.u(
[q2, q3],
math.pi / 2.0,
0,
0,
)

# rotate all of them back down
qasm2.glob.u(
[q1, q2, q3],
-math.pi / 2.0,
0,
0,
)

return q1

target = PyQrack(6)
q1 = target.run(multiple_registers)

assert isinstance(q1, reg.PyQrackReg)

out = q1.sim_reg.out_ket()

assert out[0] == 1
for i in range(1, len(out)):
assert out[i] == 0

assert True
2 changes: 1 addition & 1 deletion test/qasm2/passes/test_heuristic_noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def test_global_noise():
def test_method():
q0 = qasm2.qreg(1)
q1 = qasm2.qreg(1)
glob.UGate([q0, q1], 0.1, 0.2, 0.3)
qasm2.glob.u([q0, q1], 0.1, 0.2, 0.3)

px = 0.01
py = 0.01
Expand Down