diff --git a/src/qibo/backends/__init__.py b/src/qibo/backends/__init__.py index f0777b24e1..c4c52c58af 100644 --- a/src/qibo/backends/__init__.py +++ b/src/qibo/backends/__init__.py @@ -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]), ], ) diff --git a/src/qibo/models/error_mitigation.py b/src/qibo/models/error_mitigation.py index 9dce0017ff..2a31ca2571 100644 --- a/src/qibo/models/error_mitigation.py +++ b/src/qibo/models/error_mitigation.py @@ -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: diff --git a/src/qibo/tomography/gate_set_tomography.py b/src/qibo/tomography/gate_set_tomography.py index d3b4286f32..5a8331f527 100644 --- a/src/qibo/tomography/gate_set_tomography.py +++ b/src/qibo/tomography/gate_set_tomography.py @@ -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 @@ -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 = [] diff --git a/src/qibo/transpiler/optimizer.py b/src/qibo/transpiler/optimizer.py index a30f117757..3b93e5843a 100644 --- a/src/qibo/transpiler/optimizer.py +++ b/src/qibo/transpiler/optimizer.py @@ -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: diff --git a/src/qibo/transpiler/placer.py b/src/qibo/transpiler/placer.py index 4601fadf7d..e162cf80aa 100644 --- a/src/qibo/transpiler/placer.py +++ b/src/qibo/transpiler/placer.py @@ -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. @@ -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() @@ -96,6 +91,17 @@ 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.""" @@ -103,7 +109,7 @@ class Trivial(Placer): 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: @@ -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 @@ -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): @@ -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): @@ -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 @@ -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 diff --git a/src/qibo/transpiler/router.py b/src/qibo/transpiler/router.py index d4a782740b..027b17119b 100644 --- a/src/qibo/transpiler/router.py +++ b/src/qibo/transpiler/router.py @@ -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. @@ -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) @@ -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` @@ -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 @@ -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, diff --git a/src/qibo/transpiler/utils.py b/src/qibo/transpiler/utils.py index 89bd367d79..d988ce6add 100644 --- a/src/qibo/transpiler/utils.py +++ b/src/qibo/transpiler/utils.py @@ -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 ): diff --git a/tests/test_backends_global.py b/tests/test_backends_global.py index 8e4e07610d..dc0b6dfeaa 100644 --- a/tests/test_backends_global.py +++ b/tests/test_backends_global.py @@ -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()), ], ) diff --git a/tests/test_tomography_gate_set_tomography.py b/tests/test_tomography_gate_set_tomography.py index c2a40d944f..be0c4d4fc6 100644 --- a/tests/test_tomography_gate_set_tomography.py +++ b/tests/test_tomography_gate_set_tomography.py @@ -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), ], ) diff --git a/tests/test_transpiler_optimizer.py b/tests/test_transpiler_optimizer.py index cd55115a9b..eefcd2c1fb 100644 --- a/tests/test_transpiler_optimizer.py +++ b/tests/test_transpiler_optimizer.py @@ -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) diff --git a/tests/test_transpiler_pipeline.py b/tests/test_transpiler_pipeline.py index 30f0cddfbe..61f843f7e9 100644 --- a/tests/test_transpiler_pipeline.py +++ b/tests/test_transpiler_pipeline.py @@ -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, @@ -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, @@ -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()), ], ) diff --git a/tests/test_transpiler_placer.py b/tests/test_transpiler_placer.py index 4117c6bc49..1e8193ec2a 100644 --- a/tests/test_transpiler_placer.py +++ b/tests/test_transpiler_placer.py @@ -201,7 +201,7 @@ 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) @@ -209,7 +209,7 @@ def test_reverse_traversal(ngates, names, star_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) @@ -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) diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index 2a09e23725..5883d22abe 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -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]) diff --git a/tests/test_transpiler_utils.py b/tests/test_transpiler_utils.py index 02a1e08670..7476e1d80f 100644 --- a/tests/test_transpiler_utils.py +++ b/tests/test_transpiler_utils.py @@ -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):