From c2ba133609fe0df94331cfa78f47a1475c709d88 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 09:17:44 +0400 Subject: [PATCH 01/34] fix: setter error msg --- src/qibo/models/circuit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/models/circuit.py b/src/qibo/models/circuit.py index 31f8570d88..4bd6562446 100644 --- a/src/qibo/models/circuit.py +++ b/src/qibo/models/circuit.py @@ -299,7 +299,7 @@ def wire_names(self, wire_names: Optional[list]): if len(wire_names) != self.nqubits: raise_error( ValueError, - "Number of wire names must be equal to the number of qubits, " + f"Number of wire names must be equal to the number of qubits ({self.nqubits}), " f"but is {len(wire_names)}.", ) self._wire_names = wire_names.copy() From 0078f7697e15ee1e1f2d11d5f25ea7e35f68dc35 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 09:38:27 +0400 Subject: [PATCH 02/34] fix: docstrings abstract asserts --- src/qibo/transpiler/abstract.py | 22 ++++++++++---- src/qibo/transpiler/asserts.py | 51 ++++++++++++++++----------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/qibo/transpiler/abstract.py b/src/qibo/transpiler/abstract.py index 11e1641759..0210ad2355 100644 --- a/src/qibo/transpiler/abstract.py +++ b/src/qibo/transpiler/abstract.py @@ -7,9 +7,15 @@ class Placer(ABC): + """Maps logical qubits to physical qubits.""" + @abstractmethod def __init__(self, connectivity: nx.Graph, *args): - """A placer implements the initial logical-physical qubit mapping""" + """Initializes the placer. + + Args: + connectivity (nx.Graph): hardware topology. + """ @abstractmethod def __call__(self, circuit: Circuit, *args): @@ -21,9 +27,15 @@ def __call__(self, circuit: Circuit, *args): class Router(ABC): + """Makes the circuit executable on the given topology.""" + @abstractmethod def __init__(self, connectivity: nx.Graph, *args): - """A router implements the mapping of a circuit on a specific hardware.""" + """Initializes the router. + + Args: + connectivity (nx.Graph): hardware topology. + """ @abstractmethod def __call__(self, circuit: Circuit, *args) -> Tuple[Circuit, dict]: @@ -33,19 +45,19 @@ def __call__(self, circuit: Circuit, *args) -> Tuple[Circuit, dict]: circuit (:class:`qibo.models.circuit.Circuit`): circuit to be routed. Returns: - (:class:`qibo.models.circuit.Circuit`): routed circuit. + (:class:`qibo.models.circuit.Circuit`, dict): routed circuit and dictionary containing the final wire_names mapping. """ class Optimizer(ABC): - """An optimizer tries to reduce the number of gates during transpilation.""" + """Reduces the number of gates in the circuit.""" @abstractmethod def __call__(self, circuit: Circuit, *args) -> Circuit: """Optimize transpiled circuit. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be optimized + circuit (:class:`qibo.models.circuit.Circuit`): circuit to be optimized. Returns: (:class:`qibo.models.circuit.Circuit`): circuit with optimized number of gates. diff --git a/src/qibo/transpiler/asserts.py b/src/qibo/transpiler/asserts.py index d988ce6add..1634803ec0 100644 --- a/src/qibo/transpiler/asserts.py +++ b/src/qibo/transpiler/asserts.py @@ -29,12 +29,12 @@ def assert_transpiling( """Check that all transpiler passes have been executed correctly. Args: - original_circuit (qibo.models.Circuit): circuit before transpiling. - transpiled_circuit (qibo.models.Circuit): circuit after transpiling. - connectivity (networkx.Graph): chip qubits connectivity. - final_layout (dict): final physical-logical qubit mapping. - native_gates (NativeGates): native gates supported by the hardware. - check_circuit_equivalence (Bool): use simulations to check if the transpiled circuit is the same as the original. + original_circuit (:class:`qibo.models.circuit.Circuit`): Circuit before transpiling. + transpiled_circuit (:class:`qibo.models.circuit.Circuit`): Circuit after transpiling. + connectivity (:class:`networkx.Graph`): Hardware connectivity. + final_layout (dict): Final logical-physical qubit mapping. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. + check_circuit_equivalence (bool, optional): Check if the transpiled circuit is equivalent to the original one. Defaults to :math:`True`. """ assert_connectivity(circuit=transpiled_circuit, connectivity=connectivity) assert_decomposition( @@ -50,32 +50,32 @@ def assert_transpiling( assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=transpiled_circuit, - final_map=final_layout, + final_layout=final_layout, ) def assert_circuit_equivalence( original_circuit: Circuit, transpiled_circuit: Circuit, - final_map: dict, + final_layout: dict, test_states: Optional[list] = None, ntests: int = 3, ): - """Checks that the transpiled circuit agrees with the original using simulation. + """Checks that the transpiled circuit is equivalent to the original one. Args: - original_circuit (:class:`qibo.models.circuit.Circuit`): Original circuit. - transpiled_circuit (:class:`qibo.models.circuit.Circuit`): Transpiled circuit. - final_map (dict): logical-physical qubit mapping after routing. - test_states (list, optional): states on which the test is performed. + original_circuit (:class:`qibo.models.circuit.Circuit`): Circuit before transpiling. + transpiled_circuit (:class:`qibo.models.circuit.Circuit`): Circuit after transpiling. + final_layout (dict): Final logical-physical qubit mapping. + test_states (list, optional): List of states to test the equivalence. If ``None``, ``ntests`` random states will be tested. Defauts to ``None``. - ntests (int, optional): number of random states tested. Defauts to :math:`3`. + ntests (int, optional): Number of random states to test the equivalence. Defaults to ``3``. """ backend = NumpyBackend() if transpiled_circuit.nqubits != original_circuit.nqubits: raise_error( ValueError, - "Transpiled and original circuit do not have the same number of qubits.", + f"Transpiled circuit ({transpiled_circuit.nqubits}) and original circuit ({original_circuit.nqubits}) do not have the same number of qubits.", ) if test_states is None: @@ -84,7 +84,7 @@ def assert_circuit_equivalence( for _ in range(ntests) ] - ordering = list(final_map.values()) + ordering = list(final_layout.values()) for i, state in enumerate(test_states): target_state = backend.execute_circuit( @@ -106,8 +106,8 @@ def _transpose_qubits(state: np.ndarray, qubits_ordering: np.ndarray): """Reorders qubits of a given state vector. Args: - state (np.ndarray): final state of the circuit. - qubits_ordering (np.ndarray): final qubit ordering. + state (np.ndarray): State vector to reorder. + qubits_ordering (np.ndarray): Final qubit ordering. """ original_shape = state.shape state = np.reshape(state, len(qubits_ordering) * (2,)) @@ -119,13 +119,13 @@ def assert_placement(circuit: Circuit, connectivity: nx.Graph): """Check if the layout of the circuit is consistent with the circuit and connectivity graph. Args: - circuit (:class:`qibo.models.circuit.Circuit`): Circuit model to check. - connectivity (:class:`networkx.Graph`, optional): Chip connectivity. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to check. + connectivity (:class:`networkx.Graph`, optional): Hardware connectivity. """ if connectivity is None: raise_error( ValueError, - "Connectivity graph is missing.", + "Connectivity graph is not provided", ) if circuit.nqubits != len(circuit.wire_names) or circuit.nqubits != len( @@ -151,8 +151,8 @@ def assert_connectivity(connectivity: nx.Graph, circuit: Circuit): All two-qubit operations can be performed on hardware. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit model to check. - connectivity (:class:`networkx.Graph`): chip connectivity. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to check. + connectivity (:class:`networkx.Graph`): Hardware connectivity. """ layout = circuit.wire_names for gate in circuit.queue: @@ -174,9 +174,8 @@ def assert_decomposition( """Checks if a circuit has been correctly decomposed into native gates. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit model to check. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`): - native gates in the transpiled circuit. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to check. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`): Native gates supported by the hardware. """ for gate in circuit.queue: if isinstance(gate, gates.M): From 3d8d98e7002675f02084a60bbc452b4303da040c Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 10:09:08 +0400 Subject: [PATCH 03/34] fix: transpiler docstrings --- src/qibo/transpiler/abstract.py | 18 +++--- src/qibo/transpiler/optimizer.py | 14 +++-- src/qibo/transpiler/pipeline.py | 36 +++++------ src/qibo/transpiler/placer.py | 31 +++++----- src/qibo/transpiler/router.py | 101 +++++++++++++++---------------- src/qibo/transpiler/unroller.py | 40 +++++------- 6 files changed, 115 insertions(+), 125 deletions(-) diff --git a/src/qibo/transpiler/abstract.py b/src/qibo/transpiler/abstract.py index 0210ad2355..58ec054153 100644 --- a/src/qibo/transpiler/abstract.py +++ b/src/qibo/transpiler/abstract.py @@ -14,15 +14,17 @@ def __init__(self, connectivity: nx.Graph, *args): """Initializes the placer. Args: - connectivity (nx.Graph): hardware topology. + connectivity (nx.Graph): Hardware topology. """ @abstractmethod def __call__(self, circuit: Circuit, *args): - """Find initial qubit mapping. Mapping is saved in the circuit. + """Find initial qubit mapping. + + Method works in-place. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be mapped. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be placed. """ @@ -34,7 +36,7 @@ def __init__(self, connectivity: nx.Graph, *args): """Initializes the router. Args: - connectivity (nx.Graph): hardware topology. + connectivity (nx.Graph): Hardware topology. """ @abstractmethod @@ -42,10 +44,10 @@ def __call__(self, circuit: Circuit, *args) -> Tuple[Circuit, dict]: """Match circuit to hardware connectivity. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be routed. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be routed. Returns: - (:class:`qibo.models.circuit.Circuit`, dict): routed circuit and dictionary containing the final wire_names mapping. + (:class:`qibo.models.circuit.Circuit`, dict): Routed circuit and final logical-physical qubit mapping. """ @@ -57,8 +59,8 @@ def __call__(self, circuit: Circuit, *args) -> Circuit: """Optimize transpiled circuit. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be optimized. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be optimized. Returns: - (:class:`qibo.models.circuit.Circuit`): circuit with optimized number of gates. + (:class:`qibo.models.circuit.Circuit`): Optimized circuit. """ diff --git a/src/qibo/transpiler/optimizer.py b/src/qibo/transpiler/optimizer.py index 741078e5e9..11a3ab9827 100644 --- a/src/qibo/transpiler/optimizer.py +++ b/src/qibo/transpiler/optimizer.py @@ -9,10 +9,10 @@ class Preprocessing(Optimizer): - """Match the number of qubits of the circuit with the number of qubits of the chip if possible. + """Pad the circuit with unused qubits to match the number of physical qubits. Args: - connectivity (:class:`networkx.Graph`): hardware chip connectivity. + connectivity (:class:`networkx.Graph`): Hardware connectivity. """ def __init__(self, connectivity: Optional[nx.Graph] = None): @@ -22,7 +22,7 @@ def __call__(self, circuit: Circuit) -> Circuit: if 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.", + "Some wire_names in the circuit are not in the connectivity graph.", ) physical_qubits = self.connectivity.number_of_nodes() @@ -30,8 +30,10 @@ def __call__(self, circuit: Circuit) -> Circuit: if logical_qubits > physical_qubits: raise_error( ValueError, - "The number of qubits in the circuit can't be greater " - + "than the number of physical qubits.", + # "The number of qubits in the circuit can't be greater " + # + "than the number of physical qubits.", + f"The number of qubits in the circuit ({logical_qubits}) " + f"can't be greater than the number of physical qubits ({physical_qubits}).", ) if logical_qubits == physical_qubits: return circuit @@ -50,7 +52,7 @@ class Rearrange(Optimizer): but this has not been tested. Args: - max_qubits (int, optional): maximum number of qubits to fuse gates. + max_qubits (int, optional): Maximum number of qubits to fuse. Defaults to :math:`1`. """ diff --git a/src/qibo/transpiler/pipeline.py b/src/qibo/transpiler/pipeline.py index c1aae06775..c9d284008c 100644 --- a/src/qibo/transpiler/pipeline.py +++ b/src/qibo/transpiler/pipeline.py @@ -20,11 +20,11 @@ def restrict_connectivity_qubits(connectivity: nx.Graph, qubits: list[str]): """Restrict the connectivity to selected qubits. Args: - connectivity (:class:`networkx.Graph`): chip connectivity. - qubits (list): list of physical qubits to be used. + connectivity (:class:`networkx.Graph`): Hardware connectivity. + qubits (list): List of qubits to select. Returns: - (:class:`networkx.Graph`): restricted connectivity. + (:class:`networkx.Graph`): New restricted connectivity. """ if not set(qubits).issubset(set(connectivity.nodes)): raise_error( @@ -48,14 +48,12 @@ class Passes: """Define a transpiler pipeline consisting of smaller transpiler steps that are applied sequentially: Args: - passes (list, optional): list of passes to be applied sequentially. - If ``None``, default transpiler will be used. - Defaults to ``None``. - connectivity (:class:`networkx.Graph`, optional): physical qubits connectivity. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): native gates. - Defaults to :math:`qibo.transpiler.unroller.NativeGates.default`. - on_qubits (list, optional): list of physical qubits to be used. - If "None" all qubits are used. Defaults to ``None``. + passes (list, optional): List of transpiler passes to be applied sequentially. + If ``None``, default transpiler will be used. Defaults to ``None``. + connectivity (:class:`networkx.Graph`, optional): Hardware connectivity. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. + on_qubits (list, optional): List of qubits to be used in the transpiler. + If ``None``, all qubits in the connectivity will be used. Defaults to ``None``. """ def __init__( @@ -72,9 +70,13 @@ def __init__( self.passes = [] if passes is None else passes def __call__(self, circuit): - """ - This function returns the compiled circuits and the dictionary mapping - physical (keys) to logical (values) qubit. + """Apply the transpiler pipeline to the circuit. + + Args: + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be transpiled. + + Returns: + (:class:`qibo.models.circuit.Circuit`, dict): Transpiled circuit and final logical-physical qubit mapping. """ final_layout = None @@ -98,13 +100,13 @@ def __call__(self, circuit): return circuit, final_layout def is_satisfied(self, circuit: Circuit): - """Returns ``True`` if the circuit respects the hardware connectivity and native gates, ``False`` otherwise. + """Check if the circuit respects the hardware connectivity and native gates. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be checked. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be checked. Returns: - (bool): satisfiability condition. + (bool): ``True`` if the circuit respects the hardware connectivity and native gates, ``False`` otherwise. """ try: assert_placement(circuit=circuit, connectivity=self.connectivity) diff --git a/src/qibo/transpiler/placer.py b/src/qibo/transpiler/placer.py index 48e2cbd8ec..1399f85ea2 100644 --- a/src/qibo/transpiler/placer.py +++ b/src/qibo/transpiler/placer.py @@ -17,7 +17,7 @@ def _find_gates_qubits_pairs(circuit: Circuit): Translate circuit into a list of pairs of qubits to be used by the router and placer. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be transpiled. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be translated. Returns: (list): Pairs of qubits targeted by two qubits gates. @@ -45,7 +45,7 @@ class StarConnectivityPlacer(Placer): q Args: - connectivity (:class:`networkx.Graph`): star connectivity graph. + connectivity (:class:`networkx.Graph`): Star connectivity graph. """ def __init__(self, connectivity: Optional[nx.Graph] = None): @@ -111,7 +111,7 @@ class Subgraph(Placer): This initialization method may fail for very short circuits. Attributes: - connectivity (:class:`networkx.Graph`): chip connectivity. + connectivity (:class:`networkx.Graph`): Hardware connectivity. """ def __init__(self, connectivity: Optional[nx.Graph] = None): @@ -122,7 +122,7 @@ def __call__(self, circuit: Circuit): Circuit must contain at least two two-qubit gates to implement subgraph placement. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be transpiled. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be transpiled. """ assert_placement(circuit, self.connectivity) gates_qubits_pairs = _find_gates_qubits_pairs(circuit) @@ -164,8 +164,8 @@ class Random(Placer): gates can be applied without introducing any SWAP gate. Attributes: - connectivity (:class:`networkx.Graph`): chip connectivity. - samples (int, optional): number of initial random layouts tested. + connectivity (:class:`networkx.Graph`): Hardware connectivity. + samples (int, optional): Number of random initializations to try. Defaults to :math:`100`. seed (int or :class:`numpy.random.Generator`, optional): Either a generator of random numbers or a fixed seed to initialize a generator. If ``None``, @@ -219,8 +219,8 @@ def _cost(self, graph: nx.Graph, gates_qubits_pairs: list): Compute the cost associated to an initial layout as the lengh of the reduced circuit. Args: - graph (:class:`networkx.Graph`): current hardware qubit mapping. - gates_qubits_pairs (list): circuit representation. + graph (:class:`networkx.Graph`): Hardware connectivity. + gates_qubits_pairs (list): Circuit representation. Returns: (int): lengh of the reduced circuit. @@ -239,9 +239,9 @@ class ReverseTraversal(Placer): Compatible with all the available ``Router``s. Args: - connectivity (:class:`networkx.Graph`): chip connectivity. - routing_algorithm (:class:`qibo.transpiler.abstract.Router`): routing algorithm. - depth (int, optional): number of two-qubit gates considered before finding initial layout. + connectivity (:class:`networkx.Graph`): Hardware connectivity. + routing_algorithm (:class:`qibo.transpiler.abstract.Router`): Router to be used. + depth (int, optional): Number of two-qubit gates to be considered for routing. If ``None`` just one backward step will be implemented. If depth is greater than the number of two-qubit gates in the circuit, the circuit will be routed more than once. @@ -269,7 +269,7 @@ def __call__(self, circuit: Circuit): """Find the initial layout of the given circuit using Reverse Traversal placement. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be transpiled. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be transpiled. """ assert_placement(circuit, self.connectivity) self.routing_algorithm.connectivity = self.connectivity @@ -283,11 +283,10 @@ def _assemble_circuit(self, circuit: Circuit): using depth :math:`d = 6`, the function will return the circuit :math:`C-D-D-C-B-A`. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be transpiled. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be assembled. Returns: - (:class:`qibo.models.circuit.Circuit`): assembled circuit to perform - Reverse Traversal placement. + (:class:`qibo.models.circuit.Circuit`): Assembled circuit to perform Reverse Traversal placement. """ if self.depth is None: @@ -318,7 +317,7 @@ def _routing_step(self, circuit: Circuit): """Perform routing of the circuit. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be routed. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be routed. """ _, final_mapping = self.routing_algorithm(circuit) diff --git a/src/qibo/transpiler/router.py b/src/qibo/transpiler/router.py index 704e4435b8..1798381bf2 100644 --- a/src/qibo/transpiler/router.py +++ b/src/qibo/transpiler/router.py @@ -28,7 +28,7 @@ class StarConnectivityRouter(Router): by adding SWAP gates when needed. Args: - connectivity (:class:`networkx.Graph`): star connectivity graph. + connectivity (:class:`networkx.Graph`): Star connectivity graph. """ def __init__(self, connectivity: Optional[nx.Graph] = None): @@ -135,10 +135,9 @@ class CircuitMap: Also implements the initial two-qubit block decompositions. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be routed. - blocks (:class:`qibo.transpiler.blocks.CircuitBlocks`, optional): circuit - block representation. If ``None``, the blocks will be computed from the circuit. - Defaults to ``None``. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be routed. + blocks (:class:`qibo.transpiler.blocks.CircuitBlocks`, optional): Circuit block representation. + If ``None``, the blocks will be computed from the circuit. Defaults to ``None``. """ def __init__( @@ -183,7 +182,7 @@ def physical_to_logical(self, p2l_map: list): """Sets the physical to logical qubit mapping and updates the logical to physical mapping. Args: - p2l_map (list): physical to logical mapping. + p2l_map (list): Physical to logical mapping. """ self._p2l = p2l_map.copy() self._l2p = [0] * len(self._p2l) @@ -195,7 +194,7 @@ def logical_to_physical(self, l2p_map: list): """Sets the logical to physical qubit mapping and updates the physical to logical mapping. Args: - l2p_map (list): logical to physical mapping. + l2p_map (list): Logical to physical mapping. """ self._l2p = l2p_map.copy() self._p2l = [0] * len(self._l2p) @@ -206,8 +205,8 @@ def _update_mappings_swap(self, logical_swap: tuple, physical_swap: tuple): """Updates the qubit mappings after applying a SWAP gate. Args: - logical_swap (tuple[int]): the indices of the logical qubits to be swapped. - physical_swap (tuple[int]): the indices of the corresponding physical qubits to be swapped. + logical_swap (tuple[int]): The indices of the logical qubits to be swapped. + physical_swap (tuple[int]): The indices of the corresponding physical qubits to be swapped. """ self._p2l[physical_swap[0]], self._p2l[physical_swap[1]] = ( logical_swap[1], @@ -229,7 +228,7 @@ def execute_block(self, block: Block): Method works in-place. Args: - block (:class:`qibo.transpiler.blocks.Block`): block to be removed. + block (:class:`qibo.transpiler.blocks.Block`): Block to be removed. """ self._routed_blocks.add_block(block.on_qubits(self.get_physical_qubits(block))) self.circuit_blocks.remove_block(block) @@ -238,7 +237,7 @@ def routed_circuit(self, circuit_kwargs: Optional[dict] = None): """Returns the routed circuit. Args: - circuit_kwargs (dict): original circuit ``init_kwargs``. + circuit_kwargs (dict): Original circuit init_kwargs. Returns: :class:`qibo.models.circuit.Circuit`: Routed circuit. @@ -257,7 +256,7 @@ def update(self, logical_swap: tuple): Method works in-place. Args: - swap (tuple): tuple containing the logical qubits to be swapped. + swap (tuple): Tuple containing the logical qubits to be swapped. """ physical_swap = self.logical_pair_to_physical(logical_swap) @@ -283,10 +282,10 @@ def get_physical_qubits(self, block: Union[int, Block]): """Returns the physical qubits where a block is acting on. Args: - block (int or :class:`qibo.transpiler.blocks.Block`): block to be analysed. + block (int or :class:`qibo.transpiler.blocks.Block`): Block to be analysed. Returns: - tuple: physical qubit numbers where a block is acting on. + tuple: Physical qubit numbers where a block is acting on. """ if isinstance(block, int): block = self.circuit_blocks.search_by_index(block) @@ -297,10 +296,10 @@ def logical_pair_to_physical(self, logical_qubits: tuple): """Returns the physical qubits associated to the logical qubit pair. Args: - logical_qubits (tuple): logical qubit pair. + logical_qubits (tuple): Logical qubit pair. Returns: - tuple: physical qubit numbers associated to the logical qubit pair. + tuple: Physical qubit numbers associated to the logical qubit pair. """ return self._l2p[logical_qubits[0]], self._l2p[logical_qubits[1]] @@ -309,8 +308,8 @@ class ShortestPaths(Router): """A class to perform initial qubit mapping and connectivity matching. Args: - connectivity (:class:`networkx.Graph`): chip connectivity. - seed (int, optional): seed for the random number generator. + connectivity (:class:`networkx.Graph`): Chip connectivity. + seed (int, optional): Seed for the random number generator. If ``None``, defaults to :math:`42`. Defaults to ``None``. """ @@ -336,12 +335,10 @@ def __call__(self, circuit: Circuit): """Circuit connectivity matching. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be matched - to hardware connectivity. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be matched to hardware connectivity. Returns: - (:class:`qibo.models.circuit.Circuit`, dict): circut mapped to hardware topology, - and final physical-to-logical qubit mapping. + (:class:`qibo.models.circuit.Circuit`, dict): Routed circuit and final logical-physical qubit mapping. """ assert_placement(circuit, self.connectivity) self._preprocessing(circuit=circuit) @@ -403,8 +400,8 @@ def _add_swaps(candidate: tuple, circuitmap: CircuitMap): Method works in-place. Args: - candidate (tuple): contains path to move qubits and qubit meeting point in the path. - circuitmap (CircuitMap): representation of the circuit. + candidate (tuple): Contains path to move qubits and qubit meeting point in the path. + circuitmap (CircuitMap): Representation of the circuit. """ path = candidate[0] meeting_point = candidate[1] @@ -431,10 +428,10 @@ def _compute_cost(self, candidate: tuple): The cost is computed as minus the number of successive gates that can be executed. Args: - candidate (tuple): contains path to move qubits and qubit meeting point in the path. + candidate (tuple): Contains path to move qubits and qubit meeting point in the path. Returns: - (list, int): best path to move qubits and qubit meeting point in the path. + (list, int): Best path to move qubits and qubit meeting point in the path. """ temporary_circuit = CircuitMap( circuit=Circuit(self.circuit_map.nqubits), @@ -477,7 +474,7 @@ def _check_execution(self): """Check if some blocks in the front layer can be executed in the current configuration. Returns: - (list): executable blocks if there are, ``None`` otherwise. + (list): Executable blocks if there are, ``None`` otherwise. """ executable_blocks = [] for block in self._front_layer: @@ -500,7 +497,7 @@ def _execute_blocks(self, blocklist: list): Method works in-place. Args: - blocklist (list): list of blocks. + blocklist (list): List of blocks. """ for block_id in blocklist: block = self.circuit_map.circuit_blocks.search_by_index(block_id) @@ -527,7 +524,7 @@ def _preprocessing(self, circuit: Circuit): - _front_layer: list containing the blocks to be executed. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be preprocessed. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be preprocessed. """ self.connectivity = nx.relabel_nodes( self.connectivity, {v: i for i, v in enumerate(circuit.wire_names)} @@ -542,10 +539,10 @@ def _detach_final_measurements(self, circuit: Circuit): """Detaches measurement gates at the end of the circuit for separate handling. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuits to be processed. + circuit (:class:`qibo.models.circuit.Circuit`): Circuits to be processed. Returns: - (NoneType or list): list of measurements. If no measurements, returns ``None``. + (NoneType or list): List of measurements. If no measurements, returns ``None``. """ final_measurements = [] for gate in circuit.queue[::-1]: @@ -578,18 +575,18 @@ class Sabre(Router): """Routing algorithm proposed in Ref [1]. Args: - connectivity (:class:`networkx.Graph`): hardware chip connectivity. - lookahead (int, optional): lookahead factor, how many dag layers will be considered + connectivity (:class:`networkx.Graph`): Hardware chip connectivity. + lookahead (int, optional): Lookahead factor, how many dag layers will be considered in computing the cost. Defaults to :math:`2`. - decay_lookahead (float, optional): value in interval :math:`[0, 1]`. + decay_lookahead (float, optional): Value in interval :math:`[0, 1]`. How the weight of the distance in the dag layers decays in computing the cost. Defaults to :math:`0.6`. - delta (float, optional): defines the number of SWAPs vs depth trade-off by deciding + delta (float, optional): Defines the number of SWAPs vs depth trade-off by deciding how the algorithm tends to select non-overlapping SWAPs. Defaults to math:`10^{-3}`. - seed (int, optional): seed for the candidate random choice as tiebraker. + seed (int, optional): Seed for the candidate random choice as tiebraker. Defaults to ``None``. - swap_threshold (float, optional): limits the number of added SWAPs in every routing iteration. + swap_threshold (float, optional): Limits the number of added SWAPs in every routing iteration. This threshold is multiplied by the length of the longest path in the circuit connectivity. If the number of added SWAPs exceeds the threshold before a gate is routed, shortestpath routing is applied. @@ -629,10 +626,10 @@ def __call__(self, circuit: Circuit): """Route the circuit. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be routed. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be routed. Returns: - (:class:`qibo.models.circuit.Circuit`, dict): routed circuit and final layout. + (:class:`qibo.models.circuit.Circuit`, dict): Routed circuit and final logical-physical qubit mapping. """ assert_placement(circuit, self.connectivity) self._preprocessing(circuit=circuit) @@ -682,7 +679,7 @@ def _preprocessing(self, circuit: Circuit): to prevent overlapping swaps. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit to be preprocessed. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be preprocessed. """ self.connectivity = nx.relabel_nodes( @@ -716,10 +713,10 @@ def _append_final_measurements(self, routed_circuit: Circuit): """Appends final measurment gates on the correct qubits conserving the measurement register. Args: - routed_circuit (:class:`qibo.models.circuit.Circuit`): original circuit. + routed_circuit (:class:`qibo.models.circuit.Circuit`): Original circuit. Returns: - (:class:`qibo.models.circuit.Circuit`) routed circuit. + (:class:`qibo.models.circuit.Circuit`) Routed circuit. """ for measurement in self._final_measurements: original_qubits = measurement.qubits @@ -751,12 +748,12 @@ def _get_dag_layer(self, n_layer, qubits=False): """Return the :math:`n`-topological layer of the dag. Args: - n_layer (int): layer number. - qubits (bool, optional): if ``True``, return the target qubits of the blocks in the layer. + n_layer (int): Layer number. + qubits (bool, optional): If ``True``, return the target qubits of the blocks in the layer. If ``False``, return the block numbers. Defaults to ``False``. Returns: - (list): list of block numbers or target qubits. + (list): List of block numbers or target qubits. """ if qubits: @@ -821,7 +818,7 @@ def _swap_candidates(self): with a block in the front layer. Returns: - (list): list of candidates. + (list): List of candidates. """ candidates = [] for block in self._front_layer: @@ -844,7 +841,7 @@ def _check_execution(self): """Check if some blocks in the front layer can be executed in the current configuration. Returns: - (list): executable blocks if there are, ``None`` otherwise. + (list): Executable blocks if there are, ``None`` otherwise. """ executable_blocks = [] for block in self._front_layer: @@ -869,7 +866,7 @@ def _execute_blocks(self, blocklist: list): Method works in-place. Args: - blocklist (list): list of blocks. + blocklist (list): List of blocks. """ for block_id in blocklist: block = self.circuit_map.circuit_blocks.search_by_index(block_id) @@ -915,10 +912,10 @@ def _create_dag(gates_qubits_pairs: list): commutativity relations. Args: - gates_qubits_pairs (list): list of qubits tuples where gates/blocks acts. + gates_qubits_pairs (list): List of qubits tuples where gates/blocks acts. Returns: - (:class:`networkx.DiGraph`): adjoint of the circuit. + (:class:`networkx.DiGraph`): Adjoint of the circuit. """ dag = nx.DiGraph() dag.add_nodes_from(range(len(gates_qubits_pairs))) @@ -948,10 +945,10 @@ def _remove_redundant_connections(dag: nx.DiGraph): Remove redundant connection from a DAG using transitive reduction. Args: - dag (:class:`networkx.DiGraph`): dag to be reduced. + dag (:class:`networkx.DiGraph`): DAG to be reduced. Returns: - (:class:`networkx.DiGraph`): reduced dag. + (:class:`networkx.DiGraph`): Reduced DAG. """ new_dag = nx.DiGraph() new_dag.add_nodes_from(dag.nodes(data=True)) diff --git a/src/qibo/transpiler/unroller.py b/src/qibo/transpiler/unroller.py index 2e875311fd..8d86163ba1 100644 --- a/src/qibo/transpiler/unroller.py +++ b/src/qibo/transpiler/unroller.py @@ -85,16 +85,7 @@ def from_gate(cls, gate): # TODO: Make setting single-qubit native gates more flexible class Unroller: - """Translates a circuit to native gates. - - Args: - native_gates (:class:`qibo.transpiler.unroller.NativeGates`): native gates to use - in the transpiled circuit. - backend (:class:`qibo.backends.Backend`): backend to use for gate matrix. - - Returns: - (:class:`qibo.models.circuit.Circuit`): equivalent circuit with native gates. - """ + """Decomposes a circuit to native gates.""" def __init__( self, @@ -105,13 +96,13 @@ def __init__( self.backend = backend def __call__(self, circuit: Circuit): - """Decomposes a circuit into native gates. + """Decomposes a circuit to native gates. Args: - circuit (:class:`qibo.models.circuit.Circuit`): circuit model to decompose. + circuit (:class:`qibo.models.circuit.Circuit`): Circuit to be decomposed. Returns: - (:class:`qibo.models.circuit.Circuit`): equivalent circuit with native gates. + (:class:`qibo.models.circuit.Circuit`): Decomposed circuit. """ translated_circuit = Circuit(**circuit.init_kwargs) for gate in circuit.queue: @@ -133,10 +124,9 @@ def translate_gate( """Maps gates to a hardware-native implementation. Args: - gate (:class:`qibo.gates.abstract.Gate`): gate to be decomposed. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`): native gates - to use in the decomposition. - backend (:class:`qibo.backends.Backend`): backend to use for gate matrix. + gate (:class:`qibo.gates.abstract.Gate`): Gate to be decomposed. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`): Native gates supported by the hardware. + backend (:class:`qibo.backends.Backend`): Backend to use for gate matrix. Returns: list: List of native gates that decompose the input gate. @@ -174,10 +164,9 @@ def _translate_single_qubit_gates( Maps single qubit gates to a hardware-native implementation. Args: - gate (:class:`qibo.gates.abstract.Gate`): gate to be decomposed. - single_qubit_natives (:class:`qibo.transpiler.unroller.NativeGates`): single - qubit native gates. - backend (:class:`qibo.backends.Backend`): backend to use for gate matrix. + gate (:class:`qibo.gates.abstract.Gate`): Gate to be decomposed. + single_qubit_natives (:class:`qibo.transpiler.unroller.NativeGates`): Single qubit native gates supported by the hardware. + backend (:class:`qibo.backends.Backend`): Backend to use for gate matrix. Returns: list: List of native gates that decompose the input gate. @@ -197,10 +186,9 @@ def _translate_two_qubit_gates(gate: gates.Gate, native_gates: NativeGates, back Maps two qubit gates to a hardware-native implementation. Args: - gate (:class:`qibo.gates.abstract.Gate`): gate to be decomposed. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`): native gates - supported by the quantum hardware. - backend (:class:`qibo.backends.Backend`): backend to use for gate matrix. + gate (:class:`qibo.gates.abstract.Gate`): Gate to be decomposed. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`): Native gates supported by the hardware. + backend (:class:`qibo.backends.Backend`): Backend to use for gate matrix. Returns: list: List of native gates that decompose the input gate. @@ -212,7 +200,7 @@ def _translate_two_qubit_gates(gate: gates.Gate, native_gates: NativeGates, back if gate.__class__ in opt_dec.decompositions: return opt_dec(gate, backend) # Check if the gate has a CZ decomposition - if not gate.__class__ in iswap_dec.decompositions: + if gate.__class__ not in iswap_dec.decompositions: return cz_dec(gate, backend) # Check the decomposition with less 2 qubit gates. From 8d201f7e2430fa3bc99f854e8bb0ef42078ddb14 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 11:13:11 +0400 Subject: [PATCH 04/34] fix: transpiler doc --- doc/source/code-examples/advancedexamples.rst | 95 ++++++------------- 1 file changed, 29 insertions(+), 66 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 32da8b0129..92ad8ed818 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2121,44 +2121,16 @@ rather each term is treated separately every time. How to modify the transpiler? ----------------------------- -Logical quantum circuits for quantum algorithms are hardware agnostic. Usually an all-to-all qubit connectivity -is assumed while most current hardware only allows the execution of two-qubit gates on a restricted subset of qubit -pairs. Moreover, quantum devices are restricted to executing a subset of gates, referred to as native. -This means that, in order to execute circuits on a real quantum chip, they must be transformed into an equivalent, -hardware specific, circuit. The transformation of the circuit is carried out by the transpiler through the resolution -of two key steps: connectivity matching and native gates decomposition. -In order to execute a gate between two qubits that are not directly connected SWAP gates are required. This procedure is called routing. -As on NISQ devices two-qubit gates are a large source of noise, this procedure generates an overall noisier circuit. -Therefore, the goal of an efficient routing algorithm is to minimize the number of SWAP gates introduced. -An important step to ease the connectivity problem, is finding anoptimal initial mapping between logical and physical qubits. -This step is called placement. -The native gates decomposition in the transpiling procedure is performed by the unroller. An optimal decomposition uses the least amount -of two-qubit native gates. It is also possible to reduce the number of gates of the resulting circuit by exploiting -commutation relations, KAK decomposition or machine learning techniques. -Qibo implements a built-in transpiler with customizable options for each step. The main algorithms that can -be used at each transpiler step are reported below with a short description. - -The initial placement can be found with one of the following procedures: -- Random greedy: the best mapping is found within a set of random layouts based on a greedy policy. -- Subgraph isomorphism: the initial mapping is the one that guarantees the execution of most gates at -the beginning of the circuit without introducing any SWAP. -- Reverse traversal: this technique uses one or more reverse routing passes to find an optimal mapping by -starting from a trivial layout. - -The routing problem can be solved with the following algorithms: -- Shortest paths: when unconnected logical qubits have to interact, they are moved on the chip on -the shortest path connecting them. When multiple shortest paths are present, the one that also matches -the largest number of the following two-qubit gates is chosen. -- Sabre: this heuristic routing technique uses a customizable cost function to add SWAP gates -that reduce the distance between unconnected qubits involved in two-qubit gates. - -Qibolab unroller applies recursively a set of hard-coded gates decompositions in order to translate any gate into -single and two-qubit native gates. Single qubit gates are translated into U3, RX, RZ, X and Z gates. It is possible to -fuse multiple single qubit gates acting on the same qubit into a single U3 gate. For the two-qubit native gates it -is possible to use CZ and/or iSWAP. When both CZ and iSWAP gates are available the chosen decomposition is the -one that minimizes the use of two-qubit gates. - -Multiple transpilation steps can be implemented using the :class:`qibo.transpiler.pipeline.Pipeline`: +Quantum hardware has a specific qubit connectivity and a set of native gates that it can execute. Circuit transpilation is the process of converting a quantum circuit into an equivalent one that can be executed on a given hardware configuration. This is done by applying several passes. The main passes are: + +- Optimization: Simplifies the circuit by removing redundant gates. +- Placement: Maps logical qubits to physical qubits to minimize the number of required SWAP gates during routing. +- Routing: Makes all gates executable by adding SWAP gates where necessary. +- Unrolling: Decomposes gates into the native gates of the hardware. + +Each pass has various algorithms. Since transpilation introduces additional gates, it is important for users to select the most efficient algorithms for their specific application. +Qibo provides a built-in transpiler :class:`qibo.transpiler.pipeline.Passes`, which can be customized by adding the desired transpilation algorithms. In this example, we used :class:`qibo.transpiler.optimizer.Preprocessing`, :class:`qibo.transpiler.placer.Random`, :class:`qibo.transpiler.router.ShortestPaths`, and :class:`qibo.transpiler.unroller.Unroller` passes to transpile the circuit on a star-shaped hardware connectivity and a custom set of native gates. + .. testcode:: python @@ -2173,36 +2145,32 @@ Multiple transpilation steps can be implemented using the :class:`qibo.transpile from qibo.transpiler.placer import Random from qibo.transpiler.asserts import assert_transpiling - # Define connectivity as nx.Graph + # Define hardware connectivity using nx.Graph def star_connectivity(): chip = nx.Graph([("q0", "q2"), ("q1", "q2"), ("q2", "q3"), ("q2", "q4")]) return chip - # Define the circuit - # wire_names must match nodes in the connectivity graph. - # The index in wire_names represents the logical qubit number in the circuit. - circuit = Circuit(2, wire_names=["q0", "q1"]) + glist = [gates.GPI2, gates.RZ, gates.Z, gates.CZ] + natives = NativeGates(0).from_gatelist(glist) + + # Create a quantum circuit with 5 qubits + # Define the hardware qubit names to be used in wire_names + circuit = Circuit(5, wire_names=["q0", "q1", "q2", "q3", "q4"]) circuit.add(gates.H(0)) - circuit.add(gates.CZ(0, 1)) - - # Define custom passes as a list - custom_passes = [] - # Preprocessing adds qubits in the original circuit to match the number of qubits in the chip - custom_passes.append(Preprocessing(connectivity=star_connectivity())) - # Placement step - custom_passes.append(Random(connectivity=star_connectivity())) - # Routing step - custom_passes.append(ShortestPaths(connectivity=star_connectivity())) - # Gate decomposition step - custom_passes.append(Unroller(native_gates=NativeGates.default())) - - # Define the general pipeline - custom_pipeline = Passes(custom_passes, connectivity=star_connectivity(), native_gates=NativeGates.default()) - - # Call the transpiler pipeline on the circuit + circuit.add(gates.CZ(0, 2)) + circuit.add(gates.CZ(3, 4)) + circuit.add(gates.X(1)) + + # Define a custom list of passes for the transpiler + custom_passes = [Preprocessing(), Random(), ShortestPaths(), Unroller(native_gates=natives)] + + # Create a transpiler pipeline with hardware configuration + custom_pipeline = Passes(custom_passes, connectivity=star_connectivity()) + + # Transpile the circuit using the custom transpiler pipeline transpiled_circ, final_layout = custom_pipeline(circuit) - # Optinally call assert_transpiling to check that the final circuit can be executed on hardware + # Verify that the transpiled circuit can be executed on the hardware (optional) assert_transpiling( original_circuit=circuit, transpiled_circuit=transpiled_circ, @@ -2211,11 +2179,6 @@ Multiple transpilation steps can be implemented using the :class:`qibo.transpile native_gates=NativeGates.default() ) -In this case circuits will first be transpiled to respect the 5-qubit star connectivity, with qubit 2 as the middle qubit. This will potentially add some SWAP gates. -Then all gates will be converted to native. The :class:`qibo.transpiler.unroller.Unroller` transpiler used in this example assumes Z, RZ, GPI2 or U3 as -the single-qubit native gates, and supports CZ and iSWAP as two-qubit natives. In this case we restricted the two-qubit gate set to CZ only. -The final_layout contains the final logical-physical qubit mapping. - .. _gst_example: How to perform Gate Set Tomography? From 2e42e318ba1b87ad2a5f723c85c1a1474e414ecc Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 11:42:15 +0400 Subject: [PATCH 05/34] fix: final_map -> final_layout --- tests/test_transpiler_asserts.py | 12 +++---- tests/test_transpiler_pipeline.py | 8 ++--- tests/test_transpiler_router.py | 60 +++++++++++++++---------------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/test_transpiler_asserts.py b/tests/test_transpiler_asserts.py index 8b0b268003..9b59befd96 100644 --- a/tests/test_transpiler_asserts.py +++ b/tests/test_transpiler_asserts.py @@ -25,8 +25,8 @@ def test_assert_circuit_equivalence_equal(): circ1.add(gates.CZ(0, 1)) circ2.add(gates.X(0)) circ2.add(gates.CZ(0, 1)) - final_map = {0: 0, 1: 1} - assert_circuit_equivalence(circ1, circ2, final_map=final_map) + final_layout = {0: 0, 1: 1} + assert_circuit_equivalence(circ1, circ2, final_layout=final_layout) def test_assert_circuit_equivalence_swap(): @@ -35,8 +35,8 @@ def test_assert_circuit_equivalence_swap(): circ1.add(gates.X(0)) circ2.add(gates.SWAP(0, 1)) circ2.add(gates.X(1)) - final_map = {0: 1, 1: 0} - assert_circuit_equivalence(circ1, circ2, final_map=final_map) + final_layout = {0: 1, 1: 0} + assert_circuit_equivalence(circ1, circ2, final_layout=final_layout) def test_assert_circuit_equivalence_false(): @@ -45,9 +45,9 @@ def test_assert_circuit_equivalence_false(): circ1.add(gates.X(0)) circ2.add(gates.SWAP(0, 1)) circ2.add(gates.X(1)) - final_map = {0: 0, 1: 1} + final_layout = {0: 0, 1: 1} with pytest.raises(TranspilerPipelineError): - assert_circuit_equivalence(circ1, circ2, final_map=final_map) + assert_circuit_equivalence(circ1, circ2, final_layout=final_layout) def test_assert_placement_true(star_connectivity): diff --git a/tests/test_transpiler_pipeline.py b/tests/test_transpiler_pipeline.py index a8e43bc6aa..e624825059 100644 --- a/tests/test_transpiler_pipeline.py +++ b/tests/test_transpiler_pipeline.py @@ -70,9 +70,9 @@ def test_restrict_qubits(star_connectivity): def test_assert_circuit_equivalence_wrong_nqubits(): circ1 = Circuit(1) circ2 = Circuit(2) - final_map = {0: 0, 1: 1} + final_layout = {0: 0, 1: 1} with pytest.raises(ValueError): - assert_circuit_equivalence(circ1, circ2, final_map=final_map) + assert_circuit_equivalence(circ1, circ2, final_layout=final_layout) @pytest.mark.parametrize("qubits", [3, 5]) @@ -204,11 +204,11 @@ def test_int_qubit_names(star_connectivity): circuit.add(gates.I(0)) circuit.add(gates.H(0)) circuit.add(gates.M(0)) - transpiled_circuit, final_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert_transpiling( original_circuit=circuit, transpiled_circuit=transpiled_circuit, connectivity=connectivity, - final_layout=final_map, + final_layout=final_layout, native_gates=NativeGates.default(), ) diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index 06899e78d3..26afeb3280 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -97,13 +97,13 @@ def test_bell_state_3q(): c = circuit.copy() connectivity = line_connectivity(3, None) router = Sabre(connectivity=connectivity) - routed_circuit, final_map = router(c) + routed_circuit, final_layout = router(c) backend = NumpyBackend() state = np.array([1, 0, 0, 0, 0, 0, 0, 0]) original_state = backend.execute_circuit(circuit, state).state() target_state = backend.execute_circuit(routed_circuit, state).state() - target_state = _transpose_qubits(target_state, list(final_map.values())) + target_state = _transpose_qubits(target_state, list(final_layout.values())) assert np.all(np.isclose(np.real(original_state), np.real(target_state))) @@ -116,7 +116,7 @@ def test_random_circuits_5q(ngates, star_connectivity): circuit = generate_random_circuit(nqubits=5, ngates=ngates) original_circuit = circuit.copy() placer(circuit) - transpiled_circuit, final_qubit_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert transpiler.added_swaps >= 0 assert_connectivity(connectivity, transpiled_circuit) @@ -125,7 +125,7 @@ def test_random_circuits_5q(ngates, star_connectivity): assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) @@ -138,7 +138,7 @@ def test_random_circuits_5q_grid(ngates, grid_connectivity): circuit = generate_random_circuit(nqubits=5, ngates=ngates) original_circuit = circuit.copy() placer(circuit) - transpiled_circuit, final_qubit_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert transpiler.added_swaps >= 0 assert_connectivity(connectivity, transpiled_circuit) @@ -147,7 +147,7 @@ def test_random_circuits_5q_grid(ngates, grid_connectivity): assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) @@ -161,7 +161,7 @@ def test_random_circuits_15q_50g(nqubits, ngates): original_circuit = circuit.copy() placer(circuit) - transpiled_circuit, final_qubit_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert transpiler.added_swaps >= 0 assert_connectivity(connectivity, transpiled_circuit) @@ -170,7 +170,7 @@ def test_random_circuits_15q_50g(nqubits, ngates): assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) @@ -181,7 +181,7 @@ def test_star_circuit(star_connectivity): transpiler = ShortestPaths(connectivity=connectivity) placer(circuit) - transpiled_circuit, final_qubit_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert transpiler.added_swaps == 0 assert_connectivity(star_connectivity(), transpiled_circuit) @@ -189,7 +189,7 @@ def test_star_circuit(star_connectivity): assert_circuit_equivalence( original_circuit=star_circuit(), transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) @@ -198,7 +198,7 @@ def test_star_circuit_custom_map(star_connectivity): circuit = star_circuit() circuit.wire_names = [1, 0, 2, 3, 4] transpiler = ShortestPaths(connectivity=connectivity) - transpiled_circuit, final_qubit_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert transpiler.added_swaps == 1 assert_connectivity(star_connectivity(), transpiled_circuit) @@ -206,7 +206,7 @@ def test_star_circuit_custom_map(star_connectivity): assert_circuit_equivalence( original_circuit=star_circuit(), transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) @@ -217,7 +217,7 @@ def test_routing_with_measurements(star_connectivity): circuit.add(gates.M(0, 2, 3)) transpiler = ShortestPaths(connectivity) - transpiled_circuit, final_qubit_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert transpiled_circuit.ngates == 3 measured_qubits = transpiled_circuit.queue[2].qubits @@ -225,7 +225,7 @@ def test_routing_with_measurements(star_connectivity): assert_circuit_equivalence( original_circuit=circuit, transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) @@ -244,8 +244,8 @@ def test_sabre_looping(): ) # Without reset router_threshold = Sabre(connectivity=connectivity) # With reset - routed_no_threshold, final_mapping_no_threshold = router_no_threshold(loop_circ) - routed_threshold, final_mapping_threshold = router_threshold(loop_circ) + routed_no_threshold, final_layout_no_threshold = router_no_threshold(loop_circ) + routed_threshold, final_layout_threshold = router_threshold(loop_circ) count_no_threshold = router_no_threshold.added_swaps count_threshold = router_threshold.added_swaps @@ -254,12 +254,12 @@ def test_sabre_looping(): assert_circuit_equivalence( original_circuit=loop_circ, transpiled_circuit=routed_no_threshold, - final_map=final_mapping_no_threshold, + final_layout=final_layout_no_threshold, ) assert_circuit_equivalence( original_circuit=loop_circ, transpiled_circuit=routed_threshold, - final_map=final_mapping_threshold, + final_layout=final_layout_threshold, ) @@ -374,14 +374,14 @@ def test_sabre_matched(names, star_connectivity): original_circuit = circuit.copy() router = Sabre(connectivity=connectivity) - routed_circuit, final_map = router(circuit) + routed_circuit, final_layout = router(circuit) assert router.added_swaps == 0 assert_connectivity(circuit=routed_circuit, connectivity=connectivity) assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=routed_circuit, - final_map=final_map, + final_layout=final_layout, ) @@ -393,10 +393,10 @@ def test_sabre_simple(seed, star_connectivity): original_circuit = circ.copy() router = Sabre(connectivity=connectivity, seed=seed) - routed_circuit, final_map = router(circ) + routed_circuit, final_layout = router(circ) assert router.added_swaps == 1 - assert final_map == {0: 2, 1: 1, 2: 0, 3: 3, 4: 4} + assert final_layout == {0: 2, 1: 1, 2: 0, 3: 3, 4: 4} assert routed_circuit.queue[0].qubits == (0, 2) assert isinstance(routed_circuit.queue[0], gates.SWAP) assert isinstance(routed_circuit.queue[1], gates.CZ) @@ -404,7 +404,7 @@ def test_sabre_simple(seed, star_connectivity): assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=routed_circuit, - final_map=final_map, + final_layout=final_layout, ) @@ -421,7 +421,7 @@ def test_sabre_random_circuits(n_gates, look, decay, star_connectivity): router = Sabre(connectivity, lookahead=look, decay_lookahead=decay) placer(circuit) - transpiled_circuit, final_qubit_map = router(circuit) + transpiled_circuit, final_layout = router(circuit) assert router.added_swaps >= 0 assert_connectivity(connectivity, transpiled_circuit) @@ -430,7 +430,7 @@ def test_sabre_random_circuits(n_gates, look, decay, star_connectivity): assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) assert transpiled_circuit.queue[-1].register_name == measurement.register_name @@ -448,7 +448,7 @@ def test_sabre_random_circuits_grid(n_gates, look, decay, grid_connectivity): router = Sabre(connectivity, lookahead=look, decay_lookahead=decay) placer(circuit) - transpiled_circuit, final_qubit_map = router(circuit) + transpiled_circuit, final_layout = router(circuit) assert router.added_swaps >= 0 assert_connectivity(connectivity, transpiled_circuit) @@ -457,7 +457,7 @@ def test_sabre_random_circuits_grid(n_gates, look, decay, grid_connectivity): assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) assert transpiled_circuit.queue[-1].register_name == measurement.register_name @@ -502,7 +502,7 @@ def test_restrict_qubits(router_algorithm, star_connectivity): assert_circuit_equivalence( original_circuit=circ, transpiled_circuit=routed_circ, - final_map=final_layout, + final_layout=final_layout, ) assert_connectivity(restricted_connectivity, routed_circ) assert_placement(routed_circ, connectivity=restricted_connectivity) @@ -558,13 +558,13 @@ def test_star_router( placer = StarConnectivityPlacer(connectivity) placer(circuit) - transpiled_circuit, final_qubit_map = transpiler(circuit) + transpiled_circuit, final_layout = transpiler(circuit) assert_connectivity(connectivity, transpiled_circuit) assert_placement(transpiled_circuit, connectivity) assert_circuit_equivalence( original_circuit=original_circuit, transpiled_circuit=transpiled_circuit, - final_map=final_qubit_map, + final_layout=final_layout, ) From 8f167e78c3db7fd15dae2609ba269c8cd4f0d216 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 13:58:36 +0400 Subject: [PATCH 06/34] fix: backend API ref --- doc/source/api-reference/qibo.rst | 56 +++++++++---------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index 9fd00e71df..30b6c1e422 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -2591,62 +2591,38 @@ size circuits you may benefit from single thread per process, thus set Backends -------- -The main calculation engine is defined in the abstract backend object -:class:`qibo.backends.abstract.Backend`. This object defines the methods -required by all Qibo models to perform simulation. +Backend is the main calculation engine to execute circuits. +Qibo provides backends for quantum simulation on classical hardware and quantum hardware management and control. +For a complete list of available backends, refer to the :ref:`Packages ` section. +To create new backends, inherit from :class:`qibo.backends.abstract.Backend` and implement +its abstract methods. This abstract class defines the required methods for circuit execution. -Qibo currently provides two different calculation backends, one based on -numpy and one based on Tensorflow. It is possible to define new backends by -inheriting :class:`qibo.backends.abstract.Backend` and implementing -its abstract methods. - -An additional backend is shipped as the separate library qibojit. -This backend is supplemented by custom operators defined under which can be -used to efficiently apply gates to state vectors or density matrices. - -We refer to :ref:`Packages ` section for a complete list of the -available computation backends and instructions on how to install each of -these libraries on top of qibo. - -Custom operators are much faster than implementations based on numpy or Tensorflow -primitives, such as ``einsum``, but do not support some features, such as -automatic differentiation for backpropagation of variational circuits which is -only supported by the native ``tensorflow`` backend. - -The user can switch backends using +The user can set the backend using the :func:`qibo.set_backend` function. .. code-block:: python import qibo qibo.set_backend("qibojit") + + # Switch to the numpy backend qibo.set_backend("numpy") -before creating any circuits or gates. The default backend is the first available -from ``qibojit``, ``pytorch``, ``tensorflow``, ``numpy``. -Some backends support different platforms. For example, the qibojit backend +If no backend is specified, the default backend is used. +The default backend is selected from the available options: ``qibojit``, ``numpy``, ``qiboml``, and ``pytorch``. +The list of default backend candidates can be changed using the ``QIBO_BACKEND`` environment variable. + +Some backends support different platforms. For example, the ``qibojit`` backend provides two platforms (``cupy`` and ``cuquantum``) when used on GPU. -The active platform can be switched using +The platform can be specified using the ``platform`` parameter in the :func:`qibo.set_backend` function. .. code-block:: python import qibo qibo.set_backend("qibojit", platform="cuquantum") - qibo.set_backend("qibojit", platform="cupy") -The default backend order is qibojit (if available), tensorflow (if available), -numpy. The default backend can be changed using the ``QIBO_BACKEND`` environment -variable. - -Qibo optionally provides an interface to `qulacs `_ through the :class:`qibo.backends.qulacs.QulacsBackend`. To use ``qulacs`` for simulating a quantum circuit you can globally set the backend as in the other cases - -.. testcode:: python - - import qibo - qibo.set_backend("qulacs") - -.. note:: - GPU simulation through ``qulacs`` is not supported yet. + # Switch to the cupy platform + qibo.set_backend("qibojit", platform="cupy") .. autoclass:: qibo.backends.abstract.Backend :members: From 9aaa1b2c82d03b0d754d9473fd7f539c8cdc9ce5 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 15:24:24 +0400 Subject: [PATCH 07/34] fix: minor change --- doc/source/code-examples/advancedexamples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 92ad8ed818..1bd9bdb6ef 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2118,7 +2118,7 @@ rather each term is treated separately every time. .. _tutorials_transpiler: -How to modify the transpiler? +How to use transpile a circuit? ----------------------------- Quantum hardware has a specific qubit connectivity and a set of native gates that it can execute. Circuit transpilation is the process of converting a quantum circuit into an equivalent one that can be executed on a given hardware configuration. This is done by applying several passes. The main passes are: From 5bda771ebf4f4f9eaf956dab8d593eb36cbb95ec Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 15:50:10 +0400 Subject: [PATCH 08/34] fix: newlines --- doc/source/code-examples/advancedexamples.rst | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 1bd9bdb6ef..66b0658b3d 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -5,13 +5,13 @@ Here are a few short advanced `how to` examples. .. _gpu-examples: -How to select hardware devices? -------------------------------- +How to select classical hardware devices for circuit execution? +---------------------------------------------------------------- -Qibo supports execution on different hardware configurations including CPU with +Qibo supports execution on different classical hardware configurations including CPU with multi-threading, single GPU and multiple GPUs. Here we provide some useful information on how to control the devices that Qibo uses for circuit execution -in order to maximize performance for the available hardware configuration. +in order to maximize performance for the available classical hardware configuration. Switching between CPU and GPU ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,6 +145,14 @@ however the user may create the full state as follows: # ``final_state`` is a ``tf.Tensor`` +How to select quantum hardware devices for circuit execution? +------------------------------------------------------------- + +Qibo supports execution on different quantum hardware configurations + + + + How to use callbacks? --------------------- @@ -2119,17 +2127,26 @@ rather each term is treated separately every time. .. _tutorials_transpiler: How to use transpile a circuit? ------------------------------ +------------------------------- -Quantum hardware has a specific qubit connectivity and a set of native gates that it can execute. Circuit transpilation is the process of converting a quantum circuit into an equivalent one that can be executed on a given hardware configuration. This is done by applying several passes. The main passes are: +Quantum hardware has a specific qubit connectivity and a set of native gates that it can execute. +Circuit transpilation is the process of converting a quantum circuit into an equivalent one +that can be executed on a given hardware configuration. This is done by applying several passes. +The main passes are: - Optimization: Simplifies the circuit by removing redundant gates. - Placement: Maps logical qubits to physical qubits to minimize the number of required SWAP gates during routing. - Routing: Makes all gates executable by adding SWAP gates where necessary. - Unrolling: Decomposes gates into the native gates of the hardware. -Each pass has various algorithms. Since transpilation introduces additional gates, it is important for users to select the most efficient algorithms for their specific application. -Qibo provides a built-in transpiler :class:`qibo.transpiler.pipeline.Passes`, which can be customized by adding the desired transpilation algorithms. In this example, we used :class:`qibo.transpiler.optimizer.Preprocessing`, :class:`qibo.transpiler.placer.Random`, :class:`qibo.transpiler.router.ShortestPaths`, and :class:`qibo.transpiler.unroller.Unroller` passes to transpile the circuit on a star-shaped hardware connectivity and a custom set of native gates. +Each pass has various algorithms. Since transpilation introduces additional gates, +it is important for users to select the most efficient algorithms for their specific application. +Qibo provides a built-in transpiler :class:`qibo.transpiler.pipeline.Passes`, +which can be customized by adding the desired transpilation algorithms. +In this example, we used :class:`qibo.transpiler.optimizer.Preprocessing`, +:class:`qibo.transpiler.placer.Random`, :class:`qibo.transpiler.router.ShortestPaths`, +and :class:`qibo.transpiler.unroller.Unroller` passes to transpile the circuit +on a star-shaped hardware connectivity and a custom set of native gates. .. testcode:: python From dc9beceef0245ed64d037b89dc26b0293e9f3829 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 17:09:35 +0400 Subject: [PATCH 09/34] fix: add hw backend, transpiler, default transpiler --- doc/source/code-examples/advancedexamples.rst | 100 +++++++++++++++++- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 66b0658b3d..e0195cf19b 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -5,7 +5,7 @@ Here are a few short advanced `how to` examples. .. _gpu-examples: -How to select classical hardware devices for circuit execution? +How to select a classical hardware device for circuit execution? ---------------------------------------------------------------- Qibo supports execution on different classical hardware configurations including CPU with @@ -145,12 +145,53 @@ however the user may create the full state as follows: # ``final_state`` is a ``tf.Tensor`` -How to select quantum hardware devices for circuit execution? +How to select a quantum hardware device for circuit execution? +-------------------------------------------------------------- + +Qibolab is the dedicated Qibo backend for quantum hardware control. +For installation instructions, see the `Qibolab Documentation `_. +The ``Platform`` class in Qibolab represents a QPU device controlled by one or more instruments. +By specifying the platform name, the user can select the quantum hardware device for circuit execution. +When executing the circuit, it will be automatically transpiled using the :ref:`Default Transpiler `. + +.. code-block:: python + + import qibo + + # Set the backend and platform + qibo.set_backend("qibolab", platform="qw5q_platinum") + +How to select specific hardware qubits for circuit execution? ------------------------------------------------------------- -Qibo supports execution on different quantum hardware configurations +The :class:`qibo.models.Circuit` has a ``wire_names`` property that stores the physical names of the qubits in the circuit. +The physical qubit name ``wire_names[i]`` is assigned to the ``i`` th qubit in the circuit. +Users can specify the hardware qubits to be used by setting the ``wire_names``. + +.. code-block:: python + + from qibo import Circuit, gates + + # Define the circuit with 4 qubits + circuit = Circuit(4) + circuit.add(gates.H(0)) + circuit.add(gates.CNOT(0, 1)) + circuit.add(gates.CZ(0, 2)) + circuit.add(gates.M(2, 3)) + + # Set the physical qubit names + circuit.wire_names = ["C", "A", "B", "D"] + # This is equivalent to: + # circuit = Circuit(4) + # circuit.add(gates.H("C")) + # circuit.add(gates.CNOT("C", "A")) + # circuit.add(gates.CZ("C", "B")) + # circuit.add(gates.M("B", "D")) +For example, if the user sets ``wire_names`` to ``["C", "A", "B", "D"]``, +it means that the first qubit in the circuit is mapped to the physical qubit named ``C``, +the second qubit is mapped to ``A``, and so on. How to use callbacks? @@ -2126,8 +2167,8 @@ rather each term is treated separately every time. .. _tutorials_transpiler: -How to use transpile a circuit? -------------------------------- +How to transpile a circuit? +--------------------------- Quantum hardware has a specific qubit connectivity and a set of native gates that it can execute. Circuit transpilation is the process of converting a quantum circuit into an equivalent one @@ -2143,6 +2184,7 @@ Each pass has various algorithms. Since transpilation introduces additional gate it is important for users to select the most efficient algorithms for their specific application. Qibo provides a built-in transpiler :class:`qibo.transpiler.pipeline.Passes`, which can be customized by adding the desired transpilation algorithms. + In this example, we used :class:`qibo.transpiler.optimizer.Preprocessing`, :class:`qibo.transpiler.placer.Random`, :class:`qibo.transpiler.router.ShortestPaths`, and :class:`qibo.transpiler.unroller.Unroller` passes to transpile the circuit @@ -2167,6 +2209,7 @@ on a star-shaped hardware connectivity and a custom set of native gates. chip = nx.Graph([("q0", "q2"), ("q1", "q2"), ("q2", "q3"), ("q2", "q4")]) return chip + # Define a custom set of native gates glist = [gates.GPI2, gates.RZ, gates.Z, gates.CZ] natives = NativeGates(0).from_gatelist(glist) @@ -2196,6 +2239,53 @@ on a star-shaped hardware connectivity and a custom set of native gates. native_gates=NativeGates.default() ) +.. _tutorials_set_transpiler: + +How to attach a transpiler to a backend? +---------------------------------------- + +The transpiler can be attached to the current backend using the ``set_transpiler`` method. +Once set, the transpiler will automatically transpile the circuit before each circuit execution. + +.. testcode:: python + + import qibo + from qibo.transpiler.pipeline import Passes + from qibo.transpiler.optimizer import Preprocessing + from qibo.transpiler.router import ShortestPaths + from qibo.transpiler.unroller import Unroller, NativeGates + + # Define hardware connectivity + def star_connectivity(): + chip = nx.Graph([("q0", "q2"), ("q1", "q2"), ("q2", "q3"), ("q2", "q4")]) + return chip + + # Define a custom set of native gates + glist = [gates.GPI2, gates.RZ, gates.Z, gates.CZ] + natives = NativeGates(0).from_gatelist(glist) + + # Define a custom transpiler pipeline + custom_passes = [Preprocessing(), Random(), ShortestPaths(), Unroller(native_gates=natives)] + custom_pipeline = Passes(custom_passes, connectivity=star_connectivity()) + + # Attach the transpiler to the current backend + qibo.set_transpiler(custom_pipeline) + +If the user does not explicitly set a transpiler, the default transpiler is used. + +* For simulator backends, the default transpiler has no passes, so no transpilation is done. + +* For hardware backends, the default transpiler includes the :class:`qibo.transpiler.optimizer.Preprocessing`, :class:`qibo.transpiler.router.Sabre`, and :class:`qibo.transpiler.unroller.Unroller` passes, configured with the backend's connectivity and native gates. + +Setting an empty transpiler is equivalent to disabling transpilation. + +.. testcode:: python + + import qibo + from qibo.transpiler.pipeline import Passes + + qibo.set_transpiler(Passes()) + .. _gst_example: How to perform Gate Set Tomography? From e485312fdc6ee3111338e04ef5ed9f9168b854a1 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Thu, 28 Nov 2024 17:20:21 +0400 Subject: [PATCH 10/34] fix: circuit docstring --- src/qibo/models/circuit.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/qibo/models/circuit.py b/src/qibo/models/circuit.py index 4bd6562446..9c70a61796 100644 --- a/src/qibo/models/circuit.py +++ b/src/qibo/models/circuit.py @@ -120,8 +120,17 @@ class Circuit: A specific backend has to be used for performing calculations. Circuits can be created with a specific number of qubits and wire names. + + - Either ``nqubits`` or ``wire_names`` must be provided. + - If only ``nqubits`` is provided, wire names will default to ``[0, 1, ..., nqubits - 1]``. + - If only ``wire_names`` is provided, ``nqubits`` will be set to the length of ``wire_names``. + - ``nqubits`` and ``wire_names`` must be consistent with each other. + + Example: + .. testcode:: + from qibo import Circuit c = Circuit(5) # Default wire names are [0, 1, 2, 3, 4] c = Circuit(["A", "B", "C", "D", "E"]) @@ -130,12 +139,7 @@ class Circuit: Args: nqubits (int | list, optional): Number of qubits in the circuit or a list of wire names. - wire_names (list, optional): List of wire names. - - Either ``nqubits`` or ``wire_names`` must be provided. - - If only ``nqubits`` is provided, wire names will default to [``0``, ``1``, ..., ``nqubits - 1``]. - - If only ``wire_names`` is provided, ``nqubits`` will be set to the length of ``wire_names``. - - ``nqubits`` and ``wire_names`` must be consistent with each other. - + wire_names (list, optional): List of wire names init_kwargs (dict): a dictionary with the following keys - *nqubits* From 6321b3cf0c74eedd918192b51fd8603bfed0a1c8 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Sat, 30 Nov 2024 16:12:56 +0400 Subject: [PATCH 11/34] fix: eq circuit --- doc/source/code-examples/advancedexamples.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index fb56668f97..6ca69e1055 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -183,11 +183,10 @@ Users can specify the hardware qubits to be used by setting the ``wire_names``. circuit.wire_names = ["C", "A", "B", "D"] # This is equivalent to: - # circuit = Circuit(4) - # circuit.add(gates.H("C")) - # circuit.add(gates.CNOT("C", "A")) - # circuit.add(gates.CZ("C", "B")) - # circuit.add(gates.M("B", "D")) + # H gate on qubit "C" + # CNOT gate on qubits "C" and "A" + # CZ gate on qubits "C" and "B" + # M gate on qubit "B" and "D" For example, if the user sets ``wire_names`` to ``["C", "A", "B", "D"]``, it means that the first qubit in the circuit is mapped to the physical qubit named ``C``, From bb10676904546c5c74a52e7b21304250734b983e Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Sat, 30 Nov 2024 16:34:24 +0400 Subject: [PATCH 12/34] fix: add desc wire_names default transpiler --- doc/source/code-examples/advancedexamples.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 6ca69e1055..d09c0817a1 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -167,6 +167,8 @@ How to select specific hardware qubits for circuit execution? The :class:`qibo.models.Circuit` has a ``wire_names`` property that stores the physical names of the qubits in the circuit. The physical qubit name ``wire_names[i]`` is assigned to the ``i`` th qubit in the circuit. Users can specify the hardware qubits to be used by setting the ``wire_names``. +During circuit execution, Qibolab uses the ``wire_names`` to find the corresponding hardware qubits. + .. code-block:: python @@ -192,6 +194,13 @@ For example, if the user sets ``wire_names`` to ``["C", "A", "B", "D"]``, it means that the first qubit in the circuit is mapped to the physical qubit named ``C``, the second qubit is mapped to ``A``, and so on. +If the circuit with the given ``wire_names`` does not meet the hardware device's constraints +(e.g., connectivity and native gate requirements), +the :ref:`default transpiler ` will automatically modify +the circuit to satisfy these constraints. +In this case, different hardware qubits may be used to execute the circuit. +If the user disables the default transpiler, executing the circuit will result in compilation errors. + How to use callbacks? --------------------- From 35be2c4a38b38f013b06d655381a6c2f37d5b96f Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Sun, 1 Dec 2024 17:27:16 +0900 Subject: [PATCH 13/34] fix: add expln Preprocessing on_qubits --- doc/source/code-examples/advancedexamples.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index d09c0817a1..b39cc5f450 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2209,7 +2209,6 @@ In this example, we used :class:`qibo.transpiler.optimizer.Preprocessing`, and :class:`qibo.transpiler.unroller.Unroller` passes to transpile the circuit on a star-shaped hardware connectivity and a custom set of native gates. - .. testcode:: python import networkx as nx @@ -2258,6 +2257,18 @@ on a star-shaped hardware connectivity and a custom set of native gates. native_gates=NativeGates.default() ) + +In the current Qibo version, transpiler passes require that +``wire_names`` match the qubit names in the given connectivity graph. +This can be done manually or by using :class:`qibo.transpiler.optimizer.Preprocessing` +or the ``on_qubits`` parameter. + +.. note:: + + - :class:`qibo.transpiler.optimizer.Preprocessing` pads the circuit with the remaining qubits from the connectivity graph. + - The ``on_qubits`` parameter in :class:`qibo.transpiler.pipeline.Passes` restricts the connectivity graph. + + .. _tutorials_set_transpiler: How to attach a transpiler to a backend? From ebfe3ae41c4fa48175f7c5f83445753babbdb1e5 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:48:09 +0900 Subject: [PATCH 14/34] Update doc/source/api-reference/qibo.rst Co-authored-by: Renato Mello --- doc/source/api-reference/qibo.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index 593b9f2ed5..cedfc0f9a7 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -2591,7 +2591,7 @@ size circuits you may benefit from single thread per process, thus set Backends -------- -Backend is the main calculation engine to execute circuits. +:class:`qibo.backends.abstract.Backend` is the main calculation engine to execute circuits. Qibo provides backends for quantum simulation on classical hardware and quantum hardware management and control. For a complete list of available backends, refer to the :ref:`Packages ` section. To create new backends, inherit from :class:`qibo.backends.abstract.Backend` and implement From e96c953ebe493b6c5d76c68059999649e58d5ef4 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:48:16 +0900 Subject: [PATCH 15/34] Update doc/source/api-reference/qibo.rst Co-authored-by: Renato Mello --- doc/source/api-reference/qibo.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index cedfc0f9a7..11e777c6e3 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -2592,7 +2592,7 @@ Backends -------- :class:`qibo.backends.abstract.Backend` is the main calculation engine to execute circuits. -Qibo provides backends for quantum simulation on classical hardware and quantum hardware management and control. +Qibo provides backends for quantum simulation on classical hardware, as well as quantum hardware management and control. For a complete list of available backends, refer to the :ref:`Packages ` section. To create new backends, inherit from :class:`qibo.backends.abstract.Backend` and implement its abstract methods. This abstract class defines the required methods for circuit execution. From 58ed798a14253d8d0f431bfe3a21ea640c2f76f7 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Sat, 7 Dec 2024 14:47:16 +0900 Subject: [PATCH 16/34] fix: specify def backend order --- doc/source/api-reference/qibo.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index 593b9f2ed5..5ec8f981f3 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -2610,7 +2610,7 @@ The user can set the backend using the :func:`qibo.set_backend` function. If no backend is specified, the default backend is used. -The default backend is selected from the available options: ``qibojit``, ``numpy``, ``qiboml``, and ``pytorch``. +The default backend is selected in the following order: ``qibojit``, ``numpy``, and ``qiboml``. The list of default backend candidates can be changed using the ``QIBO_BACKEND`` environment variable. Some backends support different platforms. For example, the ``qibojit`` backend From b71d9ab65da13cd3ef26243f927715bb2579fd67 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:07:29 +0900 Subject: [PATCH 17/34] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Renato Mello --- doc/source/code-examples/advancedexamples.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index b39cc5f450..6025d4bede 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2280,10 +2280,13 @@ Once set, the transpiler will automatically transpile the circuit before each ci .. testcode:: python import qibo - from qibo.transpiler.pipeline import Passes - from qibo.transpiler.optimizer import Preprocessing - from qibo.transpiler.router import ShortestPaths - from qibo.transpiler.unroller import Unroller, NativeGates + from qibo.transpiler import ( + NativeGates, + Passes, + Preprocessing, + ShortestPaths, + Unroller, + ) # Define hardware connectivity def star_connectivity(): From 7fd6d35f6f5b37cd81a965016b040a840b64aa91 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:07:56 +0900 Subject: [PATCH 18/34] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Renato Mello --- doc/source/code-examples/advancedexamples.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 6025d4bede..b718857272 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2228,8 +2228,8 @@ on a star-shaped hardware connectivity and a custom set of native gates. return chip # Define a custom set of native gates - glist = [gates.GPI2, gates.RZ, gates.Z, gates.CZ] - natives = NativeGates(0).from_gatelist(glist) + gate_list = [gates.GPI2, gates.RZ, gates.Z, gates.CZ] + natives = NativeGates(0).from_gatelist(gate_list) # Create a quantum circuit with 5 qubits # Define the hardware qubit names to be used in wire_names From e19f044ef5ea7c4d54eaae2fc853cd80b924a7fc Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:08:04 +0900 Subject: [PATCH 19/34] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Renato Mello --- doc/source/code-examples/advancedexamples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index b718857272..138966b296 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2279,7 +2279,7 @@ Once set, the transpiler will automatically transpile the circuit before each ci .. testcode:: python - import qibo + from qibo import set_transpiler from qibo.transpiler import ( NativeGates, Passes, From 5e7f88372bbe42001abe3e093316e8d60d765c8f Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:08:11 +0900 Subject: [PATCH 20/34] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Renato Mello --- doc/source/code-examples/advancedexamples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 138966b296..5ead835a87 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2302,7 +2302,7 @@ Once set, the transpiler will automatically transpile the circuit before each ci custom_pipeline = Passes(custom_passes, connectivity=star_connectivity()) # Attach the transpiler to the current backend - qibo.set_transpiler(custom_pipeline) + set_transpiler(custom_pipeline) If the user does not explicitly set a transpiler, the default transpiler is used. From 996185527251c603d614f7583adc860b19ffb03a Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:08:39 +0900 Subject: [PATCH 21/34] Update src/qibo/transpiler/pipeline.py Co-authored-by: Renato Mello --- src/qibo/transpiler/pipeline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qibo/transpiler/pipeline.py b/src/qibo/transpiler/pipeline.py index c9d284008c..bb1ede80a9 100644 --- a/src/qibo/transpiler/pipeline.py +++ b/src/qibo/transpiler/pipeline.py @@ -51,7 +51,8 @@ class Passes: passes (list, optional): List of transpiler passes to be applied sequentially. If ``None``, default transpiler will be used. Defaults to ``None``. connectivity (:class:`networkx.Graph`, optional): Hardware connectivity. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. + Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. on_qubits (list, optional): List of qubits to be used in the transpiler. If ``None``, all qubits in the connectivity will be used. Defaults to ``None``. """ From 04009a38ae1dba52ea8f2c18369b17b42efc8e33 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:08:48 +0900 Subject: [PATCH 22/34] Update src/qibo/transpiler/optimizer.py Co-authored-by: Renato Mello --- src/qibo/transpiler/optimizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/transpiler/optimizer.py b/src/qibo/transpiler/optimizer.py index 11a3ab9827..c8a79c2dc1 100644 --- a/src/qibo/transpiler/optimizer.py +++ b/src/qibo/transpiler/optimizer.py @@ -33,7 +33,7 @@ def __call__(self, circuit: Circuit) -> Circuit: # "The number of qubits in the circuit can't be greater " # + "than the number of physical qubits.", f"The number of qubits in the circuit ({logical_qubits}) " - f"can't be greater than the number of physical qubits ({physical_qubits}).", + + f"can't be greater than the number of physical qubits ({physical_qubits}).", ) if logical_qubits == physical_qubits: return circuit From 647134b3d0818b2f4b335c85473157a397521679 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:09:03 +0900 Subject: [PATCH 23/34] Update src/qibo/transpiler/optimizer.py Co-authored-by: Renato Mello --- src/qibo/transpiler/optimizer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/qibo/transpiler/optimizer.py b/src/qibo/transpiler/optimizer.py index c8a79c2dc1..4f46325c16 100644 --- a/src/qibo/transpiler/optimizer.py +++ b/src/qibo/transpiler/optimizer.py @@ -30,8 +30,6 @@ def __call__(self, circuit: Circuit) -> Circuit: if logical_qubits > physical_qubits: raise_error( ValueError, - # "The number of qubits in the circuit can't be greater " - # + "than the number of physical qubits.", f"The number of qubits in the circuit ({logical_qubits}) " + f"can't be greater than the number of physical qubits ({physical_qubits}).", ) From f60ebdcc819c1515e05725de53e6a08a6fff2de8 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:09:11 +0900 Subject: [PATCH 24/34] Update src/qibo/transpiler/asserts.py Co-authored-by: Renato Mello --- src/qibo/transpiler/asserts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/transpiler/asserts.py b/src/qibo/transpiler/asserts.py index 1634803ec0..d15dd08273 100644 --- a/src/qibo/transpiler/asserts.py +++ b/src/qibo/transpiler/asserts.py @@ -69,7 +69,7 @@ def assert_circuit_equivalence( final_layout (dict): Final logical-physical qubit mapping. test_states (list, optional): List of states to test the equivalence. If ``None``, ``ntests`` random states will be tested. Defauts to ``None``. - ntests (int, optional): Number of random states to test the equivalence. Defaults to ``3``. + ntests (int, optional): Number of random states to test the equivalence. Defaults to :math: `3`. """ backend = NumpyBackend() if transpiled_circuit.nqubits != original_circuit.nqubits: From ce90ae19705f1aa2230cd6133f79201e69945074 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:09:23 +0900 Subject: [PATCH 25/34] Update src/qibo/transpiler/asserts.py Co-authored-by: Renato Mello --- src/qibo/transpiler/asserts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qibo/transpiler/asserts.py b/src/qibo/transpiler/asserts.py index d15dd08273..d7716b530a 100644 --- a/src/qibo/transpiler/asserts.py +++ b/src/qibo/transpiler/asserts.py @@ -33,8 +33,10 @@ def assert_transpiling( transpiled_circuit (:class:`qibo.models.circuit.Circuit`): Circuit after transpiling. connectivity (:class:`networkx.Graph`): Hardware connectivity. final_layout (dict): Final logical-physical qubit mapping. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. - check_circuit_equivalence (bool, optional): Check if the transpiled circuit is equivalent to the original one. Defaults to :math:`True`. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. + Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. + check_circuit_equivalence (bool, optional): Check if the transpiled circuit is equivalent to the original one. + Defaults to :math:`True`. """ assert_connectivity(circuit=transpiled_circuit, connectivity=connectivity) assert_decomposition( From 723b5233ec7e4f92e7deb54c8b0b139b15b616cd Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:09:37 +0900 Subject: [PATCH 26/34] Update src/qibo/transpiler/asserts.py Co-authored-by: Renato Mello --- src/qibo/transpiler/asserts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qibo/transpiler/asserts.py b/src/qibo/transpiler/asserts.py index d7716b530a..ae3bb6c0cc 100644 --- a/src/qibo/transpiler/asserts.py +++ b/src/qibo/transpiler/asserts.py @@ -77,7 +77,8 @@ def assert_circuit_equivalence( if transpiled_circuit.nqubits != original_circuit.nqubits: raise_error( ValueError, - f"Transpiled circuit ({transpiled_circuit.nqubits}) and original circuit ({original_circuit.nqubits}) do not have the same number of qubits.", + f"Transpiled circuit ({transpiled_circuit.nqubits}) and original circuit " + + f"({original_circuit.nqubits}) do not have the same number of qubits.", ) if test_states is None: From 558a709f8d33935d5711cd5f135e851d444ea68e Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:09:52 +0900 Subject: [PATCH 27/34] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Renato Mello --- doc/source/code-examples/advancedexamples.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 5ead835a87..724a36dceb 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2314,10 +2314,10 @@ Setting an empty transpiler is equivalent to disabling transpilation. .. testcode:: python - import qibo + from qibo import set_transpiler from qibo.transpiler.pipeline import Passes - qibo.set_transpiler(Passes()) + set_transpiler(Passes()) .. _gst_example: From 14e37dfddc8019ff704c971868adcae78eed8b84 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 08:11:21 +0000 Subject: [PATCH 28/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibo/transpiler/asserts.py | 2 +- src/qibo/transpiler/pipeline.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/transpiler/asserts.py b/src/qibo/transpiler/asserts.py index ae3bb6c0cc..d47168b586 100644 --- a/src/qibo/transpiler/asserts.py +++ b/src/qibo/transpiler/asserts.py @@ -33,7 +33,7 @@ def assert_transpiling( transpiled_circuit (:class:`qibo.models.circuit.Circuit`): Circuit after transpiling. connectivity (:class:`networkx.Graph`): Hardware connectivity. final_layout (dict): Final logical-physical qubit mapping. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. check_circuit_equivalence (bool, optional): Check if the transpiled circuit is equivalent to the original one. Defaults to :math:`True`. diff --git a/src/qibo/transpiler/pipeline.py b/src/qibo/transpiler/pipeline.py index bb1ede80a9..689d026aef 100644 --- a/src/qibo/transpiler/pipeline.py +++ b/src/qibo/transpiler/pipeline.py @@ -51,7 +51,7 @@ class Passes: passes (list, optional): List of transpiler passes to be applied sequentially. If ``None``, default transpiler will be used. Defaults to ``None``. connectivity (:class:`networkx.Graph`, optional): Hardware connectivity. - native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. + native_gates (:class:`qibo.transpiler.unroller.NativeGates`, optional): Native gates supported by the hardware. Defaults to :class:`qibo.transpiler.unroller.NativeGates.default()`. on_qubits (list, optional): List of qubits to be used in the transpiler. If ``None``, all qubits in the connectivity will be used. Defaults to ``None``. From 8e903f02983393a18e42d29a32165f053c33c7d9 Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Wed, 11 Dec 2024 17:23:39 +0900 Subject: [PATCH 29/34] fix: unroller docstring / replace qw5q -> dummy / add wire_names initialization --- doc/source/code-examples/advancedexamples.rst | 12 +++++++----- src/qibo/transpiler/unroller.py | 6 ++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 724a36dceb..5de45f1f65 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -174,16 +174,18 @@ During circuit execution, Qibolab uses the ``wire_names`` to find the correspond from qibo import Circuit, gates - # Define the circuit with 4 qubits - circuit = Circuit(4) + # Create a circuit with 4 qubits and assign physical qubit names + circuit = Circuit(4, wire_names=["C", "A", "B", "D"]) + + # wire_names can also be set after circuit initialization + # circuit.wire_names = ["C", "A", "B", "D"] + + # Add gates to the circuit circuit.add(gates.H(0)) circuit.add(gates.CNOT(0, 1)) circuit.add(gates.CZ(0, 2)) circuit.add(gates.M(2, 3)) - # Set the physical qubit names - circuit.wire_names = ["C", "A", "B", "D"] - # This is equivalent to: # H gate on qubit "C" # CNOT gate on qubits "C" and "A" diff --git a/src/qibo/transpiler/unroller.py b/src/qibo/transpiler/unroller.py index 8d86163ba1..602728edde 100644 --- a/src/qibo/transpiler/unroller.py +++ b/src/qibo/transpiler/unroller.py @@ -94,6 +94,12 @@ def __init__( ): self.native_gates = native_gates self.backend = backend + """Initializes the unroller. + + Args: + native_gates (:class:`qibo.transpiler.unroller.NativeGates`): Native gates to use in the transpiled circuit. + backend (:class:`qibo.backends.Backend`): Backend to use for gate matrix. + """ def __call__(self, circuit: Circuit): """Decomposes a circuit to native gates. From c0f55511f7aad59162c96c20d0365f80a574967e Mon Sep 17 00:00:00 2001 From: changsookim <> Date: Wed, 11 Dec 2024 17:25:50 +0900 Subject: [PATCH 30/34] fix: unroller docstring / replace qw5q -> dummy / add wire_names initialization --- doc/source/code-examples/advancedexamples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 5de45f1f65..20c80b076f 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -159,7 +159,7 @@ When executing the circuit, it will be automatically transpiled using the :ref:` import qibo # Set the backend and platform - qibo.set_backend("qibolab", platform="qw5q_platinum") + qibo.set_backend("qibolab", platform="dummy") How to select specific hardware qubits for circuit execution? ------------------------------------------------------------- From d9ef9fe0765093c2563de4c61c2c0e70d1c0d72f Mon Sep 17 00:00:00 2001 From: Renato Mello Date: Thu, 12 Dec 2024 09:31:09 +0400 Subject: [PATCH 31/34] fix doc test --- src/qibo/transpiler/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/transpiler/__init__.py b/src/qibo/transpiler/__init__.py index 40675f0be1..5a37d8818d 100644 --- a/src/qibo/transpiler/__init__.py +++ b/src/qibo/transpiler/__init__.py @@ -7,4 +7,4 @@ Subgraph, ) from qibo.transpiler.router import Sabre, ShortestPaths, StarConnectivityRouter -from qibo.transpiler.unroller import NativeGates +from qibo.transpiler.unroller import NativeGates, Unroller From e11d77d91670541e00beb16f05b321688f3c4eb7 Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:19:18 +0900 Subject: [PATCH 32/34] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Renato Mello --- doc/source/code-examples/advancedexamples.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 20c80b076f..54a8f67283 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2310,7 +2310,9 @@ If the user does not explicitly set a transpiler, the default transpiler is used * For simulator backends, the default transpiler has no passes, so no transpilation is done. -* For hardware backends, the default transpiler includes the :class:`qibo.transpiler.optimizer.Preprocessing`, :class:`qibo.transpiler.router.Sabre`, and :class:`qibo.transpiler.unroller.Unroller` passes, configured with the backend's connectivity and native gates. +* For hardware backends, the default transpiler includes the :class:`qibo.transpiler.optimizer.Preprocessing`, +:class:`qibo.transpiler.router.Sabre`, and :class:`qibo.transpiler.unroller.Unroller` passes, +configured with the backend's connectivity and native gates. Setting an empty transpiler is equivalent to disabling transpilation. From d240da8830cbe366d736d76435d29f07ccf5cdff Mon Sep 17 00:00:00 2001 From: Changsoo Kim <57739683+csookim@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:19:28 +0900 Subject: [PATCH 33/34] Update doc/source/code-examples/advancedexamples.rst Co-authored-by: Renato Mello --- doc/source/code-examples/advancedexamples.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 54a8f67283..8bc5655967 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -156,10 +156,10 @@ When executing the circuit, it will be automatically transpiled using the :ref:` .. code-block:: python - import qibo + from qibo import set_backend # Set the backend and platform - qibo.set_backend("qibolab", platform="dummy") + set_backend("qibolab", platform="dummy") How to select specific hardware qubits for circuit execution? ------------------------------------------------------------- From 9ef4b4052ec874c7318a2974023e3126d7f861c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 06:19:41 +0000 Subject: [PATCH 34/34] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/source/code-examples/advancedexamples.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/code-examples/advancedexamples.rst b/doc/source/code-examples/advancedexamples.rst index 8bc5655967..a54df4ec17 100644 --- a/doc/source/code-examples/advancedexamples.rst +++ b/doc/source/code-examples/advancedexamples.rst @@ -2310,8 +2310,8 @@ If the user does not explicitly set a transpiler, the default transpiler is used * For simulator backends, the default transpiler has no passes, so no transpilation is done. -* For hardware backends, the default transpiler includes the :class:`qibo.transpiler.optimizer.Preprocessing`, -:class:`qibo.transpiler.router.Sabre`, and :class:`qibo.transpiler.unroller.Unroller` passes, +* For hardware backends, the default transpiler includes the :class:`qibo.transpiler.optimizer.Preprocessing`, +:class:`qibo.transpiler.router.Sabre`, and :class:`qibo.transpiler.unroller.Unroller` passes, configured with the backend's connectivity and native gates. Setting an empty transpiler is equivalent to disabling transpilation.