Skip to content

Commit 231268c

Browse files
rares1609Oanceaelenbaasc
authored
[CQT-266] Routing checker pass (#411)
* Added a Routing Checker pass * Delete openosq12 directory (virtualenv) * Added Routing Checker pass * Fixed errors * Fixed errors * Fixed errors * Fixed errors * Implemented feedback --------- Co-authored-by: Oancea <rares.oancea@tno.nl> Co-authored-by: Chris Elenbaas <chris.elenbaas@tno.nl>
1 parent f341ed7 commit 231268c

File tree

5 files changed

+99
-0
lines changed

5 files changed

+99
-0
lines changed

opensquirrel/circuit.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from opensquirrel.passes.decomposer import Decomposer
1111
from opensquirrel.passes.mapper import Mapper
1212
from opensquirrel.passes.merger.general_merger import Merger
13+
from opensquirrel.passes.router.general_router import Router
1314
from opensquirrel.register_manager import RegisterManager
1415

1516

@@ -86,6 +87,10 @@ def qubit_register_name(self) -> str:
8687
def bit_register_name(self) -> str:
8788
return self.register_manager.get_bit_register_name()
8889

90+
def route(self, router: Router) -> None:
91+
"""Generic router pass. It applies the given Router to the circuit."""
92+
router.route(self.ir)
93+
8994
def merge(self, merger: Merger) -> None:
9095
"""Generic merge pass. It applies the given merger to the circuit."""
9196
merger.merge(self.ir, self.qubit_register_size)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from abc import ABC, abstractmethod
2+
3+
from opensquirrel.ir import IR
4+
5+
6+
class Router(ABC):
7+
@abstractmethod
8+
def route(self, ir: IR) -> None:
9+
raise NotImplementedError
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from typing import cast
2+
3+
from opensquirrel.ir import IR, Instruction, Qubit
4+
from opensquirrel.passes.router.general_router import Router
5+
6+
7+
class RoutingChecker(Router):
8+
def __init__(self, backend_connectivity_diagram: dict[int, list[int]]) -> None:
9+
self.backend_connectivity_diagram = backend_connectivity_diagram
10+
11+
def route(self, ir: IR) -> None:
12+
non_executable_interactions = []
13+
for statement in ir.statements:
14+
instruction: Instruction = cast(Instruction, statement)
15+
args = instruction.arguments
16+
if args and len(args) > 1 and all(isinstance(arg, Qubit) for arg in args):
17+
qubit_args = [arg for arg in args if isinstance(arg, Qubit)]
18+
qubit_index_pairs = [(q0.index, q1.index) for q0, q1 in zip(qubit_args[:-1], qubit_args[1:])]
19+
for i, j in qubit_index_pairs:
20+
if j not in self.backend_connectivity_diagram.get(i, []):
21+
non_executable_interactions.append((i, j))
22+
23+
if non_executable_interactions:
24+
error_message = (
25+
f"The following qubit interactions in the circuit prevent a 1-to-1 mapping:"
26+
f"{set(non_executable_interactions)}"
27+
)
28+
raise ValueError(error_message)

test/router/test_routing_checker.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Tests for routing checker pass
2+
3+
import pytest
4+
5+
from opensquirrel import CircuitBuilder
6+
from opensquirrel.circuit import Circuit
7+
from opensquirrel.passes.router.routing_checker import RoutingChecker
8+
9+
10+
@pytest.fixture(name="router")
11+
def router_fixture() -> RoutingChecker:
12+
backend_connectivity_diagram = {0: [1, 2], 1: [0, 2, 3], 2: [0, 1, 4], 3: [1, 4], 4: [2, 3]}
13+
return RoutingChecker(backend_connectivity_diagram)
14+
15+
16+
@pytest.fixture
17+
def circuit1() -> Circuit:
18+
builder = CircuitBuilder(5)
19+
builder.H(0)
20+
builder.CNOT(0, 1)
21+
builder.H(2)
22+
builder.CNOT(1, 2)
23+
builder.CNOT(2, 4)
24+
builder.CNOT(3, 4)
25+
return builder.to_circuit()
26+
27+
28+
@pytest.fixture
29+
def circuit2() -> Circuit:
30+
builder = CircuitBuilder(5)
31+
builder.H(0)
32+
builder.CNOT(0, 1)
33+
builder.CNOT(0, 3)
34+
builder.H(2)
35+
builder.CNOT(1, 2)
36+
builder.CNOT(1, 3)
37+
builder.CNOT(2, 3)
38+
builder.CNOT(3, 4)
39+
builder.CNOT(0, 4)
40+
return builder.to_circuit()
41+
42+
43+
def test_routing_checker_possible_1to1_mapping(router: RoutingChecker, circuit1: Circuit) -> None:
44+
try:
45+
router.route(circuit1.ir)
46+
except ValueError:
47+
pytest.fail("route() raised ValueError unexpectedly")
48+
49+
50+
def test_routing_checker_impossible_1to1_mapping(router: RoutingChecker, circuit2: Circuit) -> None:
51+
with pytest.raises(ValueError, match="The following qubit interactions in the circuit prevent a 1-to-1 mapping:.*"):
52+
router.route(circuit2.ir)

test/test_integration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
)
1717
from opensquirrel.passes.exporter import ExportFormat
1818
from opensquirrel.passes.merger.single_qubit_gates_merger import SingleQubitGatesMerger
19+
from opensquirrel.passes.router.routing_checker import RoutingChecker
1920

2021

2122
def test_spin_backend() -> None:
@@ -44,6 +45,10 @@ def test_spin_backend() -> None:
4445
""",
4546
)
4647

48+
# Check whether the above algorithm can be mapped to a dummy chip topology
49+
connectivity = {0: [1, 2], 1: [0], 2: [0, 3], 3: [2]}
50+
51+
qc.route(router=RoutingChecker(connectivity))
4752
# Decompose 2-qubit gates to a decomposition where the 2-qubit interactions are captured by CNOT gates
4853
qc.decompose(decomposer=CNOTDecomposer())
4954

0 commit comments

Comments
 (0)