Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 23 additions & 5 deletions src/bloqade/pyqrack/qasm2/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,30 @@
def measure(
self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: core.Measure
):
qarg: PyQrackQubit = frame.get(stmt.qarg)
carg: CBitRef = frame.get(stmt.carg)
if qarg.is_active():
carg.set_value(Measurement(qarg.sim_reg.m(qarg.addr)))
qarg: PyQrackQubit | PyQrackReg = frame.get(stmt.qarg)
carg: CBitRef | CRegister = frame.get(stmt.carg)

if isinstance(qarg, PyQrackQubit) and isinstance(carg, CBitRef):
if qarg.is_active():
carg.set_value(Measurement(qarg.sim_reg.m(qarg.addr)))
else:
carg.set_value(interp.loss_m_result)
elif isinstance(qarg, PyQrackReg) and isinstance(carg, CRegister):
# TODO: clean up iteration after PyQrackReg is refactored
for i in range(qarg.size):
qubit = qarg[i]

# TODO: make this consistent with PyQrackReg __getitem__ ?
cbit = CBitRef(carg, i)

if qubit.is_active():
cbit.set_value(Measurement(qarg.sim_reg.m(qubit.addr)))
else:
cbit.set_value(interp.loss_m_result)

Check warning on line 73 in src/bloqade/pyqrack/qasm2/core.py

View check run for this annotation

Codecov / codecov/patch

src/bloqade/pyqrack/qasm2/core.py#L73

Added line #L73 was not covered by tests
else:
carg.set_value(interp.loss_m_result)
raise RuntimeError(

Check warning on line 75 in src/bloqade/pyqrack/qasm2/core.py

View check run for this annotation

Codecov / codecov/patch

src/bloqade/pyqrack/qasm2/core.py#L75

Added line #L75 was not covered by tests
f"Expected measure call on either a single qubit and classical bit, or two registers, but got the types {type(qarg)} and {type(carg)}"
)

return ()

Expand Down
20 changes: 19 additions & 1 deletion src/bloqade/qasm2/_wrappers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import overload

from kirin.lowering import wraps

from .types import Bit, CReg, QReg, Qubit
Expand Down Expand Up @@ -58,7 +60,7 @@
...


@wraps(core.Measure)
@overload
def measure(qarg: Qubit, cbit: Bit) -> None:
"""
Measure the qubit `qarg` and store the result in the classical bit `cbit`.
Expand All @@ -70,6 +72,22 @@
...


@overload
def measure(qarg: QReg, carg: CReg) -> None:
"""
Measure each qubit in the quantum register `qarg` and store the result in the classical register `carg`.

Args:
qarg: The quantum register to measure.
carg: The classical bit to store the result in.
"""
...

Check warning on line 84 in src/bloqade/qasm2/_wrappers.py

View check run for this annotation

Codecov / codecov/patch

src/bloqade/qasm2/_wrappers.py#L84

Added line #L84 was not covered by tests


@wraps(core.Measure)
def measure(qarg, carg) -> None: ...


@wraps(uop.CX)
def cx(ctrl: Qubit, qarg: Qubit) -> None:
"""
Expand Down
19 changes: 15 additions & 4 deletions src/bloqade/qasm2/dialects/core/stmts.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,21 @@ class Measure(ir.Statement):

name = "measure"
traits = frozenset({lowering.FromPythonCall()})
qarg: ir.SSAValue = info.argument(QubitType)
"""qarg (Qubit): The qubit to measure."""
carg: ir.SSAValue = info.argument(BitType)
"""carg (Bit): The bit to store the result in."""
qarg: ir.SSAValue = info.argument(QubitType | QRegType)
"""qarg (Qubit | QReg): The qubit or quantum register to measure."""
carg: ir.SSAValue = info.argument(BitType | CRegType)
"""carg (Bit | CReg): The bit or register to store the result in."""

def check_type(self) -> None:
qarg_is_qubit = self.qarg.type.is_subseteq(QubitType)
carg_is_bit = self.carg.type.is_subseteq(BitType)
if (qarg_is_qubit and not carg_is_bit) or (not qarg_is_qubit and carg_is_bit):
raise ir.TypeCheckError(
self,
"Can't perform measurement with single (qu)bit and an entire register!",
help="Instead of `measure(qreg[i], creg)` or `measure(qreg, creg[i])`"
"use `measure(qreg[i], creg[j])` or `measure(qreg, creg)`, respectively.",
)


@statement(dialect=dialect)
Expand Down
38 changes: 37 additions & 1 deletion test/pyqrack/test_target.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import math

import pytest
from kirin import ir

from bloqade import qasm2
from bloqade.pyqrack import PyQrack, reg

Expand Down Expand Up @@ -111,4 +114,37 @@ def multiple_registers():
assert True


test_target_glob()
def test_measurement():

@qasm2.main
def measure_register():
q = qasm2.qreg(2)
c = qasm2.creg(2)
qasm2.x(q[0])
qasm2.cx(q[0], q[1])
qasm2.measure(q, c)
return c

@qasm2.main
def measure_single_qubits():
q = qasm2.qreg(2)
c = qasm2.creg(2)
qasm2.x(q[0])
qasm2.cx(q[0], q[1])
qasm2.measure(q[0], c[0])
qasm2.measure(q[1], c[1])
return c

target = PyQrack(2)
result_single = target.run(measure_single_qubits)
result_reg = target.run(measure_register)

assert result_single == result_reg == [reg.Measurement.One, reg.Measurement.One]

with pytest.raises(ir.ValidationError):

@qasm2.main
def measurement_that_errors():
q = qasm2.qreg(1)
c = qasm2.creg(1)
qasm2.measure(q[0], c)