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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Types of changes:
## Unreleased

### Added
- Added support for standalone measurements that do not store the result in a classical register ([#141](https://github.com/qBraid/pyqasm/pull/141)).

### Improved / Modified
- Re-wrote the `QasmAnalyzer.extract_qasm_version` method so that it extracts the program version just by looking at the [first non-comment line](https://github.com/openqasm/openqasm/blob/bb923eb9a84fdffe1ba6fc3c20d0b47a131523d9/source/language/comments.rst#version-string), instead of parsing the entire program ([#140](https://github.com/qBraid/pyqasm/pull/140)).
Expand Down
92 changes: 55 additions & 37 deletions src/pyqasm/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,11 @@ def _get_op_bits(
original_size_map = reg_size_map

if isinstance(operation, qasm3_ast.QuantumMeasurementStatement):
assert operation.target is not None
bit_list = [operation.measure.qubit] if qubits else [operation.target]
if qubits:
bit_list = [operation.measure.qubit]
else:
assert operation.target is not None
bit_list = [operation.target]
elif isinstance(operation, qasm3_ast.QuantumPhase) and operation.qubits is None:
for reg_name, reg_size in reg_size_map.items():
bit_list.append(
Expand Down Expand Up @@ -440,7 +443,6 @@ def _visit_measurement( # pylint: disable=too-many-locals

source = statement.measure.qubit
target = statement.target
assert source and target

# # TODO: handle in-function measurements
source_name: str = (
Expand All @@ -453,51 +455,67 @@ def _visit_measurement( # pylint: disable=too-many-locals
span=statement.span,
)

target_name: str = (
target.name if isinstance(target, qasm3_ast.Identifier) else target.name.name
)
if target_name not in self._global_creg_size_map:
raise_qasm3_error(
f"Missing register declaration for {target_name} in measurement "
f"operation {statement}",
span=statement.span,
)

source_ids = self._get_op_bits(
statement, reg_size_map=self._global_qreg_size_map, qubits=True
)
target_ids = self._get_op_bits(
statement, reg_size_map=self._global_creg_size_map, qubits=False
)

if len(source_ids) != len(target_ids):
raise_qasm3_error(
f"Register sizes of {source_name} and {target_name} do not match "
"for measurement operation",
span=statement.span,
)
unrolled_measurements = []
for src_id, tgt_id in zip(source_ids, target_ids):
unrolled_measure = qasm3_ast.QuantumMeasurementStatement(
measure=qasm3_ast.QuantumMeasurement(qubit=src_id), target=tgt_id

if not target:
for src_id in source_ids:
unrolled_measurements.append(
qasm3_ast.QuantumMeasurementStatement(
measure=qasm3_ast.QuantumMeasurement(qubit=src_id), target=None
)
)
src_name, src_id = src_id.name.name, src_id.indices[0][0].value # type: ignore
qubit_node = self._module._qubit_depths[(src_name, src_id)]
qubit_node.depth += 1
qubit_node.num_measurements += 1
else:
target_name: str = (
target.name if isinstance(target, qasm3_ast.Identifier) else target.name.name
)
src_name, src_id = src_id.name.name, src_id.indices[0][0].value # type: ignore
tgt_name, tgt_id = tgt_id.name.name, tgt_id.indices[0][0].value # type: ignore
if target_name not in self._global_creg_size_map:
raise_qasm3_error(
f"Missing register declaration for {target_name} in measurement "
f"operation {statement}",
span=statement.span,
)

qubit_node, clbit_node = (
self._module._qubit_depths[(src_name, src_id)],
self._module._clbit_depths[(tgt_name, tgt_id)],
target_ids = self._get_op_bits(
statement, reg_size_map=self._global_creg_size_map, qubits=False
)
qubit_node.depth += 1
qubit_node.num_measurements += 1

clbit_node.depth += 1
clbit_node.num_measurements += 1
if len(source_ids) != len(target_ids):
raise_qasm3_error(
f"Register sizes of {source_name} and {target_name} do not match "
"for measurement operation",
span=statement.span,
)

for src_id, tgt_id in zip(source_ids, target_ids):
unrolled_measure = qasm3_ast.QuantumMeasurementStatement(
measure=qasm3_ast.QuantumMeasurement(qubit=src_id),
target=tgt_id if target else None,
)
src_name, src_id = src_id.name.name, src_id.indices[0][0].value # type: ignore
tgt_name, tgt_id = tgt_id.name.name, tgt_id.indices[0][0].value # type: ignore

qubit_node, clbit_node = (
self._module._qubit_depths[(src_name, src_id)],
self._module._clbit_depths[(tgt_name, tgt_id)],
)
qubit_node.depth += 1
qubit_node.num_measurements += 1

clbit_node.depth += 1
clbit_node.num_measurements += 1

qubit_node.depth = max(qubit_node.depth, clbit_node.depth)
clbit_node.depth = max(qubit_node.depth, clbit_node.depth)
qubit_node.depth = max(qubit_node.depth, clbit_node.depth)
clbit_node.depth = max(qubit_node.depth, clbit_node.depth)

unrolled_measurements.append(unrolled_measure)
unrolled_measurements.append(unrolled_measure)

if self._check_only:
return []
Expand Down
23 changes: 22 additions & 1 deletion tests/qasm3/test_measurement.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,28 @@ def test_init_measure():

module = loads(qasm3_string)
module.unroll()
print(dumps(module))
check_unrolled_qasm(dumps(module), expected_qasm)


def test_standalone_measurement():
qasm3_string = """
OPENQASM 3.0;
qubit[2] q;
h q;
measure q;
"""

expected_qasm = """
OPENQASM 3.0;
qubit[2] q;
h q[0];
h q[1];
measure q[0];
measure q[1];
"""

module = loads(qasm3_string)
module.unroll()
check_unrolled_qasm(dumps(module), expected_qasm)


Expand Down