Skip to content

Commit

Permalink
passes move connectivity check to __call__
Browse files Browse the repository at this point in the history
  • Loading branch information
changsookim committed Nov 21, 2024
1 parent ff89c58 commit 824bb76
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 64 deletions.
6 changes: 3 additions & 3 deletions src/qibo/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ def _default_transpiler(cls):
return Passes(
connectivity=connectivity,
passes=[
Preprocessing(connectivity),
Trivial(connectivity),
Sabre(connectivity),
Preprocessing(),
Trivial(),
Sabre(),
Unroller(NativeGates[natives]),
],
)
Expand Down
2 changes: 1 addition & 1 deletion src/qibo/models/error_mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1183,7 +1183,7 @@ def _execute_circuit(circuit, qubit_map, noise_model=None, nshots=10000, backend
connectivity = nx.Graph(connectivity_edges)
transpiler = Passes(
connectivity=connectivity,
passes=[Custom(initial_map=qubit_map, connectivity=connectivity)],
passes=[Custom(initial_map=qubit_map)],
)
circuit, _ = transpiler(circuit)
elif noise_model is not None:
Expand Down
13 changes: 2 additions & 11 deletions src/qibo/tomography/gate_set_tomography.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from functools import cache
from inspect import signature
from itertools import product
from random import Random
from typing import List, Union

import numpy as np
from sympy import S

from qibo import Circuit, gates, symbols
from qibo.backends import _check_backend
from qibo.backends import _check_backend, get_transpiler
from qibo.config import raise_error
from qibo.hamiltonians import SymbolicHamiltonian
from qibo.transpiler.optimizer import Preprocessing
Expand Down Expand Up @@ -261,15 +260,7 @@ def GST(
backend = _check_backend(backend)

if backend.name == "qibolab" and transpiler is None: # pragma: no cover
transpiler = Passes(
connectivity=backend.platform.topology,
passes=[
Preprocessing(backend.platform.topology),
Random(backend.platform.topology),
Sabre(backend.platform.topology),
Unroller(NativeGates.default()),
],
)
transpiler = get_transpiler()

matrices = []
empty_matrices = []
Expand Down
10 changes: 9 additions & 1 deletion src/qibo/transpiler/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ class Preprocessing(Optimizer):
connectivity (:class:`networkx.Graph`): hardware chip connectivity.
"""

def __init__(self, connectivity: nx.Graph):
def __init__(self, connectivity: nx.Graph = None):
self.connectivity = connectivity

def __call__(self, circuit: Circuit) -> Circuit:
if self.connectivity is None or not all(
qubit in self.connectivity.nodes for qubit in circuit.wire_names
):
raise_error(
ValueError,
"The circuit qubits are not in the connectivity graph.",
)

physical_qubits = self.connectivity.number_of_nodes()
logical_qubits = circuit.nqubits
if logical_qubits > physical_qubits:
Expand Down
36 changes: 21 additions & 15 deletions src/qibo/transpiler/placer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,9 @@ class StarConnectivityPlacer(Placer):
connectivity (:class:`networkx.Graph`): star connectivity graph.
"""

def __init__(self, connectivity: nx.Graph):
def __init__(self, connectivity: nx.Graph = None):
self.connectivity = connectivity
for node in self.connectivity.nodes:
if self.connectivity.degree(node) == 4:
self.middle_qubit = node
elif self.connectivity.degree(node) != 1:
raise_error(
ValueError,
"This connectivity graph is not a star graph.",
)
self.middle_qubit = None

def __call__(self, circuit: Circuit):
"""Apply the transpiler transformation on a given circuit.
Expand All @@ -67,6 +60,8 @@ def __call__(self, circuit: Circuit):
Only single qubit gates and two qubits gates are supported by the router.
"""
assert_placement(circuit, self.connectivity)
self._check_star_connectivity()

middle_qubit_idx = circuit.wire_names.index(self.middle_qubit)
wire_names = circuit.wire_names.copy()

Expand Down Expand Up @@ -96,14 +91,25 @@ def __call__(self, circuit: Circuit):

circuit.wire_names = wire_names

def _check_star_connectivity(self):
"""Check if the connectivity graph is a star graph."""
for node in self.connectivity.nodes:
if self.connectivity.degree(node) == 4:
self.middle_qubit = node
elif self.connectivity.degree(node) != 1:
raise_error(
ValueError,
"This connectivity graph is not a star graph.",
)


class Trivial(Placer):
"""Place qubits according to the order of the qubit names that the user provides."""

def __init__(self, connectivity: nx.Graph = None):
self.connectivity = connectivity

def __call__(self, circuit: Circuit = None):
def __call__(self, circuit: Circuit):
"""Find the trivial placement for the circuit.
Args:
Expand All @@ -122,7 +128,7 @@ class Custom(Placer):
- If **list**, it should contain physical qubit names, arranged in the order of the logical qubits.
"""

def __init__(self, initial_map: Union[list, dict], connectivity: nx.Graph):
def __init__(self, initial_map: Union[list, dict], connectivity: nx.Graph = None):
self.initial_map = initial_map
self.connectivity = connectivity

Expand All @@ -141,7 +147,7 @@ def __call__(self, circuit: Circuit):
else:
raise_error(TypeError, "Use dict or list to define mapping.")

assert_placement(circuit, connectivity=self.connectivity)
assert_placement(circuit, self.connectivity)


class Subgraph(Placer):
Expand All @@ -155,7 +161,7 @@ class Subgraph(Placer):
connectivity (:class:`networkx.Graph`): chip connectivity.
"""

def __init__(self, connectivity: nx.Graph):
def __init__(self, connectivity: nx.Graph = None):
self.connectivity = connectivity

def __call__(self, circuit: Circuit):
Expand Down Expand Up @@ -213,7 +219,7 @@ class Random(Placer):
initializes a generator with a random seed. Defaults to ``None``.
"""

def __init__(self, connectivity: nx.Graph, samples: int = 100, seed=None):
def __init__(self, connectivity: nx.Graph = None, samples: int = 100, seed=None):
self.connectivity = connectivity
self.samples = samples
self.seed = seed
Expand Down Expand Up @@ -296,8 +302,8 @@ class ReverseTraversal(Placer):

def __init__(
self,
connectivity: nx.Graph,
routing_algorithm: Router,
connectivity: nx.Graph = None,
depth: Optional[int] = None,
):
self.connectivity = connectivity
Expand Down
26 changes: 16 additions & 10 deletions src/qibo/transpiler/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,7 @@ class StarConnectivityRouter(Router):

def __init__(self, connectivity: nx.Graph):
self.connectivity = connectivity
for node in self.connectivity.nodes:
if self.connectivity.degree(node) == 4:
self.middle_qubit = node
elif self.connectivity.degree(node) != 1:
raise_error(
ValueError,
"This connectivity graph is not a star graph.",
)
self.middle_qubit = None

def __call__(self, circuit: Circuit):
"""Apply the transpiler transformation on a given circuit.
Expand All @@ -49,7 +42,9 @@ def __call__(self, circuit: Circuit):
circuit (:class:`qibo.models.circuit.Circuit`): The original Qibo circuit to transform.
Only single qubit gates and two qubits gates are supported by the router.
"""
self._check_star_connectivity()
assert_placement(circuit, self.connectivity)

middle_qubit_idx = circuit.wire_names.index(self.middle_qubit)
nqubits = circuit.nqubits
new = Circuit(nqubits=nqubits, wire_names=circuit.wire_names)
Expand Down Expand Up @@ -94,6 +89,17 @@ def __call__(self, circuit: Circuit):
new.add(gate.__class__(*routed_qubits, **gate.init_kwargs))
return new, {circuit.wire_names[i]: l2p[i] for i in range(nqubits)}

def _check_star_connectivity(self):
"""Check if the connectivity graph is a star graph."""
for node in self.connectivity.nodes:
if self.connectivity.degree(node) == 4:
self.middle_qubit = node
elif self.connectivity.degree(node) != 1:
raise_error(
ValueError,
"This connectivity graph is not a star graph.",
)


def _find_connected_qubit(qubits, queue, error, mapping):
"""Helper method for :meth:`qibo.transpiler.router.StarConnectivityRouter`
Expand Down Expand Up @@ -308,7 +314,7 @@ class ShortestPaths(Router):
If ``None``, defaults to :math:`42`. Defaults to ``None``.
"""

def __init__(self, connectivity: nx.Graph, seed: Optional[int] = None):
def __init__(self, connectivity: nx.Graph = None, seed: Optional[int] = None):
self.connectivity = connectivity
self._front_layer = None
self.circuit_map = None
Expand Down Expand Up @@ -595,7 +601,7 @@ class Sabre(Router):

def __init__(
self,
connectivity: nx.Graph,
connectivity: nx.Graph = None,
lookahead: int = 2,
decay_lookahead: float = 0.6,
delta: float = 0.001,
Expand Down
6 changes: 6 additions & 0 deletions src/qibo/transpiler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ def assert_placement(circuit: Circuit, connectivity: nx.Graph):
circuit (:class:`qibo.models.circuit.Circuit`): Circuit model to check.
connectivity (:class:`networkx.Graph`, optional): Chip connectivity.
"""
if connectivity is None:
raise_error(
ValueError,
"Connectivity graph is missing.",
)

if circuit.nqubits != len(circuit.wire_names) or circuit.nqubits != len(
connectivity.nodes
):
Expand Down
6 changes: 3 additions & 3 deletions tests/test_backends_global.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ def test_set_get_transpiler():
transpiler = Passes(
connectivity=connectivity,
passes=[
Preprocessing(connectivity),
Random(connectivity, seed=0),
Sabre(connectivity),
Preprocessing(),
Random(seed=0),
Sabre(),
Unroller(NativeGates.default()),
],
)
Expand Down
6 changes: 3 additions & 3 deletions tests/test_tomography_gate_set_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,9 @@ def test_GST_with_transpiler(backend, star_connectivity):
transpiler = Passes(
connectivity=connectivity,
passes=[
Preprocessing(connectivity),
Random(connectivity),
Sabre(connectivity),
Preprocessing(),
Random(),
Sabre(),
Unroller(NativeGates.default(), backend=backend),
],
)
Expand Down
9 changes: 9 additions & 0 deletions tests/test_transpiler_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@

def test_preprocessing_error(star_connectivity):
circ = Circuit(7)

preprocesser = Preprocessing()
with pytest.raises(ValueError):
new_circuit = preprocesser(circuit=circ)

preprocesser = Preprocessing(connectivity=star_connectivity())
with pytest.raises(ValueError):
new_circuit = preprocesser(circuit=circ)

circ = Circuit(5, wire_names=[0, 1, 2, "q3", "q4"])
with pytest.raises(ValueError):
new_circuit = preprocesser(circuit=circ)


def test_preprocessing_same(star_connectivity):
circ = Circuit(5)
Expand Down
24 changes: 11 additions & 13 deletions tests/test_transpiler_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,16 @@ def test_custom_passes(placer, router, ngates, nqubits, star_connectivity):
connectivity = star_connectivity()
circ = generate_random_circuit(nqubits=nqubits, ngates=ngates)
custom_passes = []
custom_passes.append(Preprocessing(connectivity=connectivity))
custom_passes.append(Preprocessing())
if placer == ReverseTraversal:
custom_passes.append(
placer(
connectivity=connectivity,
routing_algorithm=router(connectivity=connectivity),
routing_algorithm=router(),
)
)
else:
custom_passes.append(placer(connectivity=connectivity))
custom_passes.append(router(connectivity=connectivity))
custom_passes.append(placer())
custom_passes.append(router())
custom_passes.append(Unroller(native_gates=NativeGates.default()))
custom_pipeline = Passes(
custom_passes,
Expand All @@ -177,17 +176,16 @@ def test_custom_passes_restrict(
connectivity = star_connectivity()
circ = generate_random_circuit(nqubits=3, ngates=ngates, names=restrict_names)
custom_passes = []
custom_passes.append(Preprocessing(connectivity=connectivity))
custom_passes.append(Preprocessing())
if placer == ReverseTraversal:
custom_passes.append(
placer(
connectivity=connectivity,
routing_algorithm=routing(connectivity=connectivity),
routing_algorithm=routing(),
)
)
else:
custom_passes.append(placer(connectivity=connectivity))
custom_passes.append(routing(connectivity=connectivity))
custom_passes.append(placer())
custom_passes.append(routing())
custom_passes.append(Unroller(native_gates=NativeGates.default()))
custom_pipeline = Passes(
custom_passes,
Expand Down Expand Up @@ -220,9 +218,9 @@ def test_int_qubit_names(star_connectivity):
transpiler = Passes(
connectivity=connectivity,
passes=[
Preprocessing(connectivity),
Random(connectivity, seed=0),
Sabre(connectivity),
Preprocessing(),
Random(seed=0),
Sabre(),
Unroller(NativeGates.default()),
],
)
Expand Down
7 changes: 4 additions & 3 deletions tests/test_transpiler_placer.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,15 @@ def test_reverse_traversal(ngates, names, star_connectivity):
circuit = star_circuit(names=names)
connectivity = star_connectivity(names=names)
routing = ShortestPaths(connectivity=connectivity)
placer = ReverseTraversal(connectivity, routing, depth=ngates)
placer = ReverseTraversal(routing, connectivity, depth=ngates)
placer(circuit)
assert_placement(circuit, connectivity)


def test_reverse_traversal_no_gates(star_connectivity):
connectivity = star_connectivity()
routing = ShortestPaths(connectivity=connectivity)
placer = ReverseTraversal(connectivity, routing, depth=10)
placer = ReverseTraversal(routing, connectivity, depth=10)
circuit = Circuit(5)
with pytest.raises(ValueError):
placer(circuit)
Expand Down Expand Up @@ -261,4 +261,5 @@ def test_star_connectivity_placer_error(first, star_connectivity):
chip = nx.Graph()
chip.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 4)])
with pytest.raises(ValueError):
StarConnectivityPlacer(chip)
placer = StarConnectivityPlacer(chip)
placer(circ)
3 changes: 2 additions & 1 deletion tests/test_transpiler_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,8 @@ def test_star_error_multi_qubit(star_connectivity):
chip = nx.Graph()
chip.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 4)])
with pytest.raises(ValueError):
StarConnectivityRouter(chip)
router = StarConnectivityRouter(chip)
_, _ = router(circuit=circuit)


# @pytest.mark.parametrize("nqubits", [1, 3, 5])
Expand Down
4 changes: 4 additions & 0 deletions tests/test_transpiler_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ def test_assert_placement_false(qubits, names, star_connectivity):
with pytest.raises(PlacementError):
assert_placement(circuit, connectivity)

connectivity = None
with pytest.raises(ValueError):
assert_placement(circuit, connectivity)


@pytest.mark.parametrize("qubits", [10, 1])
def test_assert_placement_error(qubits, star_connectivity):
Expand Down

0 comments on commit 824bb76

Please sign in to comment.