Skip to content

Commit

Permalink
[CQT-259] Integrate wait and barrier directives in cQASMv1 exporter (#…
Browse files Browse the repository at this point in the history
…407)

* Restore SGMQ notation for barrier links.
Add tests for barrier links.

---------

Co-authored-by: Roberto Turrado Camblor <rturrado@gmail.com>
  • Loading branch information
elenbaasc and rturrado authored Jan 21, 2025
1 parent 4a98e09 commit 961f903
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
with:
python-version: "3.11"
- name: Install poetry
uses: abatilo/actions-poetry@v4
uses: abatilo/actions-poetry@v3
with:
poetry-version: "1.8.3"

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
with:
python-version: "3.11"
- name: Install poetry
uses: abatilo/actions-poetry@v4
uses: abatilo/actions-poetry@v3
with:
poetry-version: "1.3.2"
- name: Install tox
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install poetry
uses: abatilo/actions-poetry@v4
uses: abatilo/actions-poetry@v3
with:
poetry-version: "1.3.2"
- name: Install tox
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Changelog

All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

### Types of changes:
* **Added** for new features.
* **Changed** for changes in existing functionality.
* **Fixed** for any bug fixes.
* **Removed** for now removed features.


## [ 0.2.0 ] - [ xxxx-yy-zz ]

### Added
- Restore SGMQ notation for barrier groups in cQASMv1 Exporter.
55 changes: 43 additions & 12 deletions opensquirrel/passes/exporter/cqasmv1_exporter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import re
from typing import TYPE_CHECKING, SupportsFloat, SupportsInt

from opensquirrel.exceptions import UnsupportedGateError
Expand All @@ -10,16 +11,18 @@
from opensquirrel.register_manager import RegisterManager


class CqasmV1ExporterParseError(Exception):
pass


class _CQASMv1Creator(IRVisitor):
# Precision used when writing out a float number
FLOAT_PRECISION = 8

def __init__(self, register_manager: RegisterManager) -> None:
self.register_manager = register_manager
qubit_register_size = self.register_manager.get_qubit_register_size()
self.cqasmv1_string = "version 1.0\n\n{}\n\n".format(
f"qubits {qubit_register_size}" if qubit_register_size > 0 else ""
)
self.output = "version 1.0\n\n{}\n\n".format(f"qubits {qubit_register_size}" if qubit_register_size > 0 else "")

def visit_qubit(self, qubit: Qubit) -> str:
qubit_register_name = self.register_manager.get_qubit_register_name()
Expand All @@ -35,24 +38,24 @@ def visit_float(self, f: SupportsFloat) -> str:

def visit_measure(self, measure: Measure) -> None:
qubit_argument = measure.arguments[0].accept(self) # type: ignore[index]
self.cqasmv1_string += f"{measure.name}_z {qubit_argument}\n"
self.output += f"{measure.name}_z {qubit_argument}\n"

def visit_init(self, init: Init) -> None:
qubit_argument = init.arguments[0].accept(self) # type: ignore[index]
self.cqasmv1_string += f"prep_z {qubit_argument}\n"
self.output += f"prep_z {qubit_argument}\n"

def visit_reset(self, reset: Reset) -> None:
qubit_argument = reset.arguments[0].accept(self) # type: ignore[index]
self.cqasmv1_string += f"prep_z {qubit_argument}\n"
self.output += f"prep_z {qubit_argument}\n"

def visit_barrier(self, barrier: Barrier) -> None:
qubit_argument = barrier.arguments[0].accept(self) # type: ignore[index]
self.cqasmv1_string += f"barrier {qubit_argument}\n"
self.output += f"barrier {qubit_argument}\n"

def visit_wait(self, wait: Wait) -> None:
qubit_argument = wait.arguments[0].accept(self) # type: ignore[index]
parameter = wait.arguments[1].accept(self) # type: ignore[index]
self.cqasmv1_string += f"wait {qubit_argument}, {parameter}\n"
self.output += f"wait {qubit_argument}, {parameter}\n"

def visit_gate(self, gate: Gate) -> None:
gate_name = gate.name.lower()
Expand All @@ -62,9 +65,37 @@ def visit_gate(self, gate: Gate) -> None:
if any(not isinstance(arg, Qubit) for arg in gate.arguments): # type: ignore[union-attr]
params = [arg.accept(self) for arg in gate.arguments if not isinstance(arg, Qubit)] # type: ignore[union-attr]
qubit_args = (arg.accept(self) for arg in gate.arguments if isinstance(arg, Qubit)) # type: ignore[union-attr]
self.cqasmv1_string += "{} {}{}\n".format(
gate_name, ", ".join(qubit_args), ", " + ", ".join(params) if params else ""
)
self.output += "{} {}{}\n".format(gate_name, ", ".join(qubit_args), ", " + ", ".join(params) if params else "")


def post_process(output: str) -> str:
return _merge_barrier_groups(output)


def _merge_barrier_groups(output: str) -> str:
ret: str = ""
barrier_group_indices: list[int] = []
for line in output.split("\n"):
if not line.startswith("barrier"):
if barrier_group_indices:
ret += _dump_barrier_group(barrier_group_indices)
barrier_group_indices = []
ret += f"{line}\n"
else:
barrier_group_indices.append(_get_barrier_index(line))
return ret


def _dump_barrier_group(indices: list[int]) -> str:
return "barrier q[{}]\n".format(", ".join(map(str, indices)) if len(indices) != 0 else "")


def _get_barrier_index(line: str) -> int:
barrier_index_match = re.search("\d+", line)
if not barrier_index_match:
msg = "expecting a barrier index but found none"
raise CqasmV1ExporterParseError(msg)
return int(barrier_index_match.group(0))


def export(circuit: Circuit) -> str:
Expand All @@ -73,4 +104,4 @@ def export(circuit: Circuit) -> str:
circuit.ir.accept(cqasmv1_creator)

# Remove all trailing lines and leave only one
return cqasmv1_creator.cqasmv1_string.rstrip() + "\n"
return post_process(cqasmv1_creator.output).rstrip() + "\n"
50 changes: 50 additions & 0 deletions test/exporter/test_cqasmv1_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,53 @@ def test_anonymous_gates(gate: Gate) -> None:
with pytest.raises(UnsupportedGateError, match="not supported"): # noqa: PT012
qc = builder.to_circuit()
qc.export(fmt=ExportFormat.CQASM_V1)


@pytest.mark.parametrize(
("program", "expected_output"),
[
(
"version 3.0; qubit[1] q; barrier q[0];",
"version 1.0\n\nqubits 1\n\nbarrier q[0]\n",
),
(
"version 3.0; qubit[3] q; barrier q[0:2]",
"version 1.0\n\nqubits 3\n\nbarrier q[0, 1, 2]\n",
),
(
"version 3.0; qubit[1] q; barrier q[0, 0]",
"version 1.0\n\nqubits 1\n\nbarrier q[0, 0]\n",
),
(
"version 3.0; qubit[6] q; barrier q[0:2, 5, 3, 4, 1]",
"version 1.0\n\nqubits 6\n\nbarrier q[0, 1, 2, 5, 3, 4, 1]\n",
),
(
"version 3.0; qubit[5] q; barrier q[0]; H q[1]; barrier q[1:2]; X q[2]; barrier q[3:4, 1]; Y q[3];"
"barrier q[0]; barrier q[2]",
"version 1.0\n\nqubits 5\n\nbarrier q[0]\nh q[1]\nbarrier q[1, 2]\nx q[2]\nbarrier q[3, 4, 1]\ny q[3]\n"
"barrier q[0, 2]\n",
),
(
"version 3.0; qubit[3] q; barrier q[0]; barrier q[1]; X q[2]; barrier q[1]; X q[2]",
"version 1.0\n\nqubits 3\n\nbarrier q[0, 1]\nx q[2]\nbarrier q[1]\nx q[2]\n",
),
(
"version 3.0; qubit[20] q; barrier q[14]; barrier q[9:12]; X q[2]; barrier q[19]",
"version 1.0\n\nqubits 20\n\nbarrier q[14, 9, 10, 11, 12]\nx q[2]\nbarrier q[19]\n",
),
],
ids=[
"no_group",
"single_group",
"repeated_index",
"preserve_order",
"with_instructions",
"group_consecutive_barriers",
"double_digit_register_size",
],
)
def test_barrier_groups(program: str, expected_output: str) -> None:
qc = Circuit.from_string(program)
output = qc.export(fmt=ExportFormat.CQASM_V1)
assert output == expected_output

0 comments on commit 961f903

Please sign in to comment.