From 6dbd864142c021d763cf610826c1cf6bbdd63293 Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Mon, 13 Nov 2023 11:22:05 +0400 Subject: [PATCH 01/10] test transpiler and measurements --- src/qibo/transpiler/test_m.py | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/qibo/transpiler/test_m.py diff --git a/src/qibo/transpiler/test_m.py b/src/qibo/transpiler/test_m.py new file mode 100644 index 0000000000..29a45248f4 --- /dev/null +++ b/src/qibo/transpiler/test_m.py @@ -0,0 +1,38 @@ +import networkx as nx + +import qibo +from qibo import gates +from qibo.models import Circuit +from qibo.transpiler.placer import Trivial +from qibo.transpiler.router import Sabre + +# new_gate = gates.M(*qubits, **gate.init_kwargs) +# new_gate.result = gate.result +# new.add(new_gate) + +measurement = gates.M(0, register_name="a") +measurement2 = gates.M(1, register_name="b") + +circ = Circuit(3) +circ.add(gates.H(0)) +circ.add(measurement) +circ.add(measurement2) + +circ2 = Circuit(3) +for gate in circ.queue: + circ2.add(gate) + +connectivity = nx.Graph() +connectivity.add_nodes_from([0, 1, 2]) +connectivity.add_edges_from([(0, 1), (1, 2)]) +router = Sabre(connectivity=connectivity) +initial_layout = {"q0": 0, "q1": 1, "q2": 2} + +routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) + +result2 = routed_circ.execute(nshots=100) +# result2 = circ2.execute(nshots=100) +print(result2.frequencies()) + +print(measurement.result.has_samples()) +print(measurement.result.frequencies()) From e4880acb13cad0192f556ec063e1763a5d2795f6 Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Mon, 13 Nov 2023 17:37:25 +0400 Subject: [PATCH 02/10] modify block.on_qubits --- src/qibo/transpiler/blocks.py | 8 ++++++-- src/qibo/transpiler/test_m.py | 12 +++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/qibo/transpiler/blocks.py b/src/qibo/transpiler/blocks.py index 7c0ec3d902..b7e5b9d501 100644 --- a/src/qibo/transpiler/blocks.py +++ b/src/qibo/transpiler/blocks.py @@ -77,8 +77,12 @@ def on_qubits(self, new_qubits: tuple): new_qubits (tuple): new qubits where the block is acting. """ qubits_dict = dict(zip(self.qubits, new_qubits)) - new_gates = [gate.on_qubits(qubits_dict) for gate in self.gates] - + new_gates = [] + for gate in self.gates: + new_gate = gate.on_qubits(qubits_dict) + if isinstance(gate, gates.M): + new_gate.result = gate.result + new_gates.append(new_gate) return Block(qubits=new_qubits, gates=new_gates, name=self.name) # TODO: use real QM properties to check commutation diff --git a/src/qibo/transpiler/test_m.py b/src/qibo/transpiler/test_m.py index 29a45248f4..e093bed0cb 100644 --- a/src/qibo/transpiler/test_m.py +++ b/src/qibo/transpiler/test_m.py @@ -14,13 +14,14 @@ measurement2 = gates.M(1, register_name="b") circ = Circuit(3) -circ.add(gates.H(0)) circ.add(measurement) circ.add(measurement2) +new_measurement = measurement.on_qubits({0: 1}) +new_measurement.result = measurement.result circ2 = Circuit(3) -for gate in circ.queue: - circ2.add(gate) +circ2.add(gates.H(0)) +circ2.add(new_measurement) connectivity = nx.Graph() connectivity.add_nodes_from([0, 1, 2]) @@ -33,6 +34,7 @@ result2 = routed_circ.execute(nshots=100) # result2 = circ2.execute(nshots=100) print(result2.frequencies()) - -print(measurement.result.has_samples()) +# print(result2.has_samples()) +# print(measurement.result.has_samples()) print(measurement.result.frequencies()) +# print(measurement.result.has_samples()) From a375259ffca2b910270d98d2047aecbdd9ecd6ef Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Mon, 13 Nov 2023 17:49:07 +0400 Subject: [PATCH 03/10] add test of sabre with measurements --- tests/test_transpiler_router.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index 146e11b849..6ce80ada29 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -331,3 +331,17 @@ def test_sabre_memory_map(): router._memory_map = [[1, 0, 2, 3, 4]] value = router._compute_cost((0, 1)) assert value == float("inf") + + +def test_sabre_measurements(): + measurement = gates.M(0) + circ = Circuit(3) + circ.add(measurement) + connectivity = nx.Graph() + connectivity.add_nodes_from([0, 1, 2]) + connectivity.add_edges_from([(0, 1), (1, 2)]) + router = Sabre(connectivity=connectivity) + initial_layout = {"q0": 0, "q1": 1, "q2": 2} + routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) + circuit_result = routed_circ.execute(nshots=100) + assert circuit_result.frequencies() == measurement.result.frequencies() From c13deeb810e52d8d815c2b2a415e6886ee70f50e Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Tue, 14 Nov 2023 16:55:17 +0400 Subject: [PATCH 04/10] overload on_qubits for measurements --- src/qibo/gates/measurements.py | 36 ++++++++++++++++++++++++++++++++++ src/qibo/transpiler/blocks.py | 7 +------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/qibo/gates/measurements.py b/src/qibo/gates/measurements.py index 58aa32842f..20bfa94925 100644 --- a/src/qibo/gates/measurements.py +++ b/src/qibo/gates/measurements.py @@ -211,3 +211,39 @@ def load(cls, payload): args["basis"] = [getattr(gates, g) for g in args["basis"]] args.update(args.pop("init_kwargs")) return cls(*qubits, **args) + + # Overload on_qubits to copy also gate.result, controlled by can be removed for measurements + def on_qubits(self, qubit_map) -> "Gate": + """Creates the same gate targeting different qubits. + + Args: + qubit_map (int): Dictionary mapping original qubit indices to new ones. + + Returns: + A :class:`qibo.gates.Gate` object of the original gate + type targeting the given qubits. + + Example: + + .. testcode:: + + from qibo import models, gates + c = models.Circuit(4) + # Add some CNOT gates + c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(2, 3) + c.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(3, 0) + c.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(1, 3) + c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) # equivalent to gates.CNOT(2, 1) + print(c.draw()) + .. testoutput:: + + q0: ───X───── + q1: ───|─o─X─ + q2: ─o─|─|─o─ + q3: ─X─o─X─── + """ + + qubits = (qubit_map.get(q) for q in self.qubits) + gate = self.__class__(*qubits, **self.init_kwargs) + gate.result = self.result + return gate diff --git a/src/qibo/transpiler/blocks.py b/src/qibo/transpiler/blocks.py index b7e5b9d501..658552fb06 100644 --- a/src/qibo/transpiler/blocks.py +++ b/src/qibo/transpiler/blocks.py @@ -77,12 +77,7 @@ def on_qubits(self, new_qubits: tuple): new_qubits (tuple): new qubits where the block is acting. """ qubits_dict = dict(zip(self.qubits, new_qubits)) - new_gates = [] - for gate in self.gates: - new_gate = gate.on_qubits(qubits_dict) - if isinstance(gate, gates.M): - new_gate.result = gate.result - new_gates.append(new_gate) + new_gates = [gate.on_qubits(qubits_dict) for gate in self.gates] return Block(qubits=new_qubits, gates=new_gates, name=self.name) # TODO: use real QM properties to check commutation From b7eb8d514e15e690021370fd92e29aed0b18981c Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Wed, 15 Nov 2023 14:27:16 +0400 Subject: [PATCH 05/10] completed --- src/qibo/transpiler/blocks.py | 15 +++++--- src/qibo/transpiler/router.py | 62 ++++++++++++++++++++++++++++++--- src/qibo/transpiler/test_m.py | 40 --------------------- tests/test_transpiler_router.py | 23 ++++++++++-- 4 files changed, 89 insertions(+), 51 deletions(-) delete mode 100644 src/qibo/transpiler/test_m.py diff --git a/src/qibo/transpiler/blocks.py b/src/qibo/transpiler/blocks.py index 658552fb06..764213e62f 100644 --- a/src/qibo/transpiler/blocks.py +++ b/src/qibo/transpiler/blocks.py @@ -101,7 +101,7 @@ def kak_decompose(self): # pragma: no cover This should be done only if the block is entangled and the number of two qubit gates is higher than the number after the decomposition. """ - raise_error(NotImplementedError, "") + raise_error(NotImplementedError, "KAK decomposition is not available yet.") class CircuitBlocks: @@ -144,9 +144,16 @@ def add_block(self, block: "Block"): ) self.block_list.append(block) - def circuit(self): - """Return the quantum circuit.""" - circuit = Circuit(self.qubits) + def circuit(self, circuit_kwargs=None): + """Return the quantum circuit. + + Args: + circuit_kwargs (dict): original circuit init_kwargs. + """ + if circuit_kwargs is None: + circuit = Circuit(self.qubits) + else: + circuit = Circuit(**circuit_kwargs) for block in self.block_list: for gate in block.gates: circuit.add(gate) diff --git a/src/qibo/transpiler/router.py b/src/qibo/transpiler/router.py index ebc6571252..08d3af6f3e 100644 --- a/src/qibo/transpiler/router.py +++ b/src/qibo/transpiler/router.py @@ -371,9 +371,13 @@ def execute_block(self, block: Block): self._routed_blocks.add_block(block.on_qubits(self.get_physical_qubits(block))) self.circuit_blocks.remove_block(block) - def routed_circuit(self): - """Returns circuit of the routed circuit.""" - return self._routed_blocks.circuit() + def routed_circuit(self, circuit_kwargs=None): + """Returns circuit of the routed circuit. + + Args: + circuit_kwargs (dict): original circuit init_kwargs. + """ + return self._routed_blocks.circuit(circuit_kwargs=circuit_kwargs) def final_layout(self): """Returns the final physical-circuit qubits mapping.""" @@ -455,6 +459,7 @@ def __init__( self._front_layer = None self.circuit = None self._memory_map = None + self._final_measurements = None random.seed(seed) def __call__(self, circuit: Circuit, initial_layout: dict): @@ -475,7 +480,13 @@ def __call__(self, circuit: Circuit, initial_layout: dict): else: self._find_new_mapping() - return self.circuit.routed_circuit(), self.circuit.final_layout() + routed_circuit = self.circuit.routed_circuit(circuit_kwargs=circuit.init_kwargs) + if self._final_measurements is not None: + routed_circuit = self._append_final_measurements( + routed_circuit=routed_circuit + ) + + return routed_circuit, self.circuit.final_layout() @property def added_swaps(self): @@ -485,6 +496,7 @@ def added_swaps(self): def _preprocessing(self, circuit: Circuit, initial_layout: dict): """The following objects will be initialised: - circuit: class to represent circuit and to perform logical-physical qubit mapping. + - _final_measurements: measurement gates at the end of the circuit. - _dist_matrix: matrix reporting the shortest path lengh between all node pairs. - _dag: direct acyclic graph of the circuit based on commutativity. - _memory_map: list to remember previous SWAP moves. @@ -492,7 +504,9 @@ def _preprocessing(self, circuit: Circuit, initial_layout: dict): - _delta_register: list containing the special weigh added to qubits to prevent overlapping swaps. """ - self.circuit = CircuitMap(initial_layout, circuit) + copy_circuit = self._copy_circuit(circuit) + self._final_measurements = self._detach_final_measurements(copy_circuit) + self.circuit = CircuitMap(initial_layout, copy_circuit) self._dist_matrix = nx.floyd_warshall_numpy(self.connectivity) self._dag = _create_dag(self.circuit.blocks_qubits_pairs()) self._memory_map = [] @@ -500,7 +514,45 @@ def _preprocessing(self, circuit: Circuit, initial_layout: dict): self._update_front_layer() self._delta_register = [1.0 for _ in range(circuit.nqubits)] + @staticmethod + def _copy_circuit(circuit: Circuit): + """Return a copy of the circuit to avoid altering the original circuit. + This copy conserves the registers of the measurement gates.""" + new_circuit = Circuit(circuit.nqubits) + for gate in circuit.queue: + new_circuit.add(gate) + return new_circuit + + def _detach_final_measurements(self, circuit: Circuit): + """Detach measurement gates at the end of the circuit for separate handling.""" + final_measurements = [] + reversed_queue = circuit.queue[::-1] + for gate in reversed_queue: + if isinstance(gate, gates.M): + final_measurements.append(gate) + circuit.queue.remove(gate) + else: + break + print(final_measurements) + if len(final_measurements) == 0: + return None + return final_measurements[::-1] + + def _append_final_measurements(self, routed_circuit: Circuit): + """Append the final measurment gates on the correct qubits conserving the measurement register.""" + for measurement in self._final_measurements: + print(measurement) + original_qubits = measurement.qubits + routed_qubits = ( + self.circuit.circuit_to_physical(qubit) for qubit in original_qubits + ) + routed_circuit.add( + measurement.on_qubits(dict(zip(original_qubits, routed_qubits))) + ) + return routed_circuit + def _update_dag_layers(self): + """Update dag layers and put them in topological order.""" for layer, nodes in enumerate(nx.topological_generations(self._dag)): for node in nodes: self._dag.nodes[node]["layer"] = layer diff --git a/src/qibo/transpiler/test_m.py b/src/qibo/transpiler/test_m.py deleted file mode 100644 index e093bed0cb..0000000000 --- a/src/qibo/transpiler/test_m.py +++ /dev/null @@ -1,40 +0,0 @@ -import networkx as nx - -import qibo -from qibo import gates -from qibo.models import Circuit -from qibo.transpiler.placer import Trivial -from qibo.transpiler.router import Sabre - -# new_gate = gates.M(*qubits, **gate.init_kwargs) -# new_gate.result = gate.result -# new.add(new_gate) - -measurement = gates.M(0, register_name="a") -measurement2 = gates.M(1, register_name="b") - -circ = Circuit(3) -circ.add(measurement) -circ.add(measurement2) - -new_measurement = measurement.on_qubits({0: 1}) -new_measurement.result = measurement.result -circ2 = Circuit(3) -circ2.add(gates.H(0)) -circ2.add(new_measurement) - -connectivity = nx.Graph() -connectivity.add_nodes_from([0, 1, 2]) -connectivity.add_edges_from([(0, 1), (1, 2)]) -router = Sabre(connectivity=connectivity) -initial_layout = {"q0": 0, "q1": 1, "q2": 2} - -routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) - -result2 = routed_circ.execute(nshots=100) -# result2 = circ2.execute(nshots=100) -print(result2.frequencies()) -# print(result2.has_samples()) -# print(measurement.result.has_samples()) -print(measurement.result.frequencies()) -# print(measurement.result.has_samples()) diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index 6ce80ada29..d5a667fab4 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -246,7 +246,7 @@ def test_circuit_map(): circuit_map.execute_block(block_list.search_by_index(1)) circuit_map.execute_block(block_list.search_by_index(2)) circuit_map.execute_block(block_list.search_by_index(3)) - routed_circuit = circuit_map.routed_circuit() + routed_circuit = circuit_map.routed_circuit(circuit_kwargs=None) assert isinstance(routed_circuit.queue[6], gates.CZ) # circuit to logical map: [1,2,0,3]. initial map: {"q0": 2, "q1": 0, "q2": 1, "q3": 3}. assert routed_circuit.queue[6].qubits == (0, 1) # initial circuit qubits (1,2) @@ -333,9 +333,27 @@ def test_sabre_memory_map(): assert value == float("inf") -def test_sabre_measurements(): +def test_sabre_intermediate_measurements(): measurement = gates.M(0) + circ = Circuit(3, density_matrix=True) + circ.add(gates.H(0)) + circ.add(measurement) + circ.add(gates.CNOT(0, 1)) + connectivity = nx.Graph() + connectivity.add_nodes_from([0, 1, 2]) + connectivity.add_edges_from([(0, 1), (1, 2)]) + router = Sabre(connectivity=connectivity) + initial_layout = {"q0": 0, "q1": 1, "q2": 2} + routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) + circuit_result = routed_circ.execute(nshots=100) + assert routed_circ.queue[1].result is measurement.result + + +def test_sabre_final_measurements(): + measurement = gates.M(0, 1, 2) circ = Circuit(3) + circ.add(gates.H(0)) + circ.add(gates.CNOT(0, 2)) circ.add(measurement) connectivity = nx.Graph() connectivity.add_nodes_from([0, 1, 2]) @@ -345,3 +363,4 @@ def test_sabre_measurements(): routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) circuit_result = routed_circ.execute(nshots=100) assert circuit_result.frequencies() == measurement.result.frequencies() + assert routed_circ.queue[-1].result is measurement.result From 3584b366beb96a80b545a389202ac032ac5bf949 Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Wed, 15 Nov 2023 14:34:56 +0400 Subject: [PATCH 06/10] remove prints --- src/qibo/transpiler/router.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/qibo/transpiler/router.py b/src/qibo/transpiler/router.py index 08d3af6f3e..d61d98bdd6 100644 --- a/src/qibo/transpiler/router.py +++ b/src/qibo/transpiler/router.py @@ -533,7 +533,6 @@ def _detach_final_measurements(self, circuit: Circuit): circuit.queue.remove(gate) else: break - print(final_measurements) if len(final_measurements) == 0: return None return final_measurements[::-1] @@ -541,7 +540,6 @@ def _detach_final_measurements(self, circuit: Circuit): def _append_final_measurements(self, routed_circuit: Circuit): """Append the final measurment gates on the correct qubits conserving the measurement register.""" for measurement in self._final_measurements: - print(measurement) original_qubits = measurement.qubits routed_qubits = ( self.circuit.circuit_to_physical(qubit) for qubit in original_qubits From 11f50a3054fae5f42a79f3b80abb728b39b8ab4f Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Thu, 16 Nov 2023 11:15:36 +0400 Subject: [PATCH 07/10] corrections by andrea --- src/qibo/gates/measurements.py | 22 ++++++++++------------ src/qibo/transpiler/router.py | 7 +++---- tests/test_transpiler_router.py | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/qibo/gates/measurements.py b/src/qibo/gates/measurements.py index 20bfa94925..ec5bcb0b43 100644 --- a/src/qibo/gates/measurements.py +++ b/src/qibo/gates/measurements.py @@ -214,13 +214,14 @@ def load(cls, payload): # Overload on_qubits to copy also gate.result, controlled by can be removed for measurements def on_qubits(self, qubit_map) -> "Gate": - """Creates the same gate targeting different qubits. + """Creates the same measurement gate targeting different qubits + and preserving the measurement result register. Args: qubit_map (int): Dictionary mapping original qubit indices to new ones. Returns: - A :class:`qibo.gates.Gate` object of the original gate + A :class:`qibo.gates.Gate.M` object of the original gate type targeting the given qubits. Example: @@ -228,19 +229,16 @@ def on_qubits(self, qubit_map) -> "Gate": .. testcode:: from qibo import models, gates - c = models.Circuit(4) - # Add some CNOT gates - c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(2, 3) - c.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(3, 0) - c.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(1, 3) - c.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) # equivalent to gates.CNOT(2, 1) + measurement = gates.M(0, 1) + c = models.Circuit(3) + c.add(measurement.on_qubits({0: 0, 1: 2})) + assert c.queue[0].result is measurement.result print(c.draw()) .. testoutput:: - q0: ───X───── - q1: ───|─o─X─ - q2: ─o─|─|─o─ - q3: ─X─o─X─── + q0: ─M─ + q1: ─|─ + q2: ─M─ """ qubits = (qubit_map.get(q) for q in self.qubits) diff --git a/src/qibo/transpiler/router.py b/src/qibo/transpiler/router.py index d61d98bdd6..d15194b9e3 100644 --- a/src/qibo/transpiler/router.py +++ b/src/qibo/transpiler/router.py @@ -372,7 +372,7 @@ def execute_block(self, block: Block): self.circuit_blocks.remove_block(block) def routed_circuit(self, circuit_kwargs=None): - """Returns circuit of the routed circuit. + """Return the routed circuit. Args: circuit_kwargs (dict): original circuit init_kwargs. @@ -526,14 +526,13 @@ def _copy_circuit(circuit: Circuit): def _detach_final_measurements(self, circuit: Circuit): """Detach measurement gates at the end of the circuit for separate handling.""" final_measurements = [] - reversed_queue = circuit.queue[::-1] - for gate in reversed_queue: + for gate in circuit.queue[::-1]: if isinstance(gate, gates.M): final_measurements.append(gate) circuit.queue.remove(gate) else: break - if len(final_measurements) == 0: + if not final_measurements: return None return final_measurements[::-1] diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index d5a667fab4..d9d513bf35 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -246,7 +246,7 @@ def test_circuit_map(): circuit_map.execute_block(block_list.search_by_index(1)) circuit_map.execute_block(block_list.search_by_index(2)) circuit_map.execute_block(block_list.search_by_index(3)) - routed_circuit = circuit_map.routed_circuit(circuit_kwargs=None) + routed_circuit = circuit_map.routed_circuit() assert isinstance(routed_circuit.queue[6], gates.CZ) # circuit to logical map: [1,2,0,3]. initial map: {"q0": 2, "q1": 0, "q2": 1, "q3": 3}. assert routed_circuit.queue[6].qubits == (0, 1) # initial circuit qubits (1,2) From 3b4bdbc32890b55a18b5ba076a9220d91a6de297 Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Mon, 20 Nov 2023 12:07:36 +0400 Subject: [PATCH 08/10] corrected issue with intermediate measurements --- src/qibo/transpiler/router.py | 20 +++++++++++++++++--- tests/test_transpiler_router.py | 32 ++++++++++---------------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/qibo/transpiler/router.py b/src/qibo/transpiler/router.py index d15194b9e3..d0a32e23d9 100644 --- a/src/qibo/transpiler/router.py +++ b/src/qibo/transpiler/router.py @@ -351,15 +351,24 @@ class CircuitMap: Args: initial_layout (dict): initial logical-to-physical qubit mapping. circuit (Circuit): circuit to be routed. + blocks (CircuitBlocks): circuit blocks representation, if None the blocks will be computed from the circuit. """ - def __init__(self, initial_layout: dict, circuit: Circuit): - self.circuit_blocks = CircuitBlocks(circuit, index_names=True) + def __init__(self, initial_layout: dict, circuit: Circuit, blocks=None): + if blocks is not None: + self.circuit_blocks = blocks + else: + self.circuit_blocks = CircuitBlocks(circuit, index_names=True) + self.initial_layout = initial_layout self._circuit_logical = list(range(len(initial_layout))) self._physical_logical = list(initial_layout.values()) self._routed_blocks = CircuitBlocks(Circuit(circuit.nqubits)) self._swaps = 0 + def set_circuit_logical(self, circuit_logical_map: list): + """Set the current circuit to logical qubit mapping.""" + self._circuit_logical = circuit_logical_map + def blocks_qubits_pairs(self): """Returns a list containing the qubit pairs of each block.""" return [block.qubits for block in self.circuit_blocks()] @@ -579,7 +588,12 @@ def _find_new_mapping(self): def _compute_cost(self, candidate): """Compute the cost associated to a possible SWAP candidate.""" - temporary_circuit = deepcopy(self.circuit) + temporary_circuit = CircuitMap( + initial_layout=self.circuit.initial_layout, + circuit=Circuit(len(self.circuit.initial_layout)), + blocks=self.circuit.circuit_blocks, + ) + temporary_circuit.set_circuit_logical(deepcopy(self.circuit._circuit_logical)) temporary_circuit.update(candidate) if temporary_circuit._circuit_logical in self._memory_map: return float("inf") diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index d9d513bf35..2a950f05b8 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -298,28 +298,33 @@ def test_sabre_simple(seed): ) -@pytest.mark.parametrize("gates", [10, 40]) +@pytest.mark.parametrize("n_gates", [10, 40]) @pytest.mark.parametrize("look", [0, 5]) @pytest.mark.parametrize("decay", [0.5, 1.0]) @pytest.mark.parametrize("placer", [Trivial, Random]) @pytest.mark.parametrize("connectivity", [star_connectivity(), grid_connectivity()]) -def test_sabre_random_circuits(gates, look, decay, placer, connectivity): +def test_sabre_random_circuits(n_gates, look, decay, placer, connectivity): placer = placer(connectivity=connectivity) layout_circ = Circuit(5) initial_layout = placer(layout_circ) router = Sabre(connectivity=connectivity, lookahead=look, decay_lookahead=decay) - circuit = generate_random_circuit(nqubits=5, ngates=gates) + circuit = generate_random_circuit(nqubits=5, ngates=n_gates) + measurement = gates.M(*range(5)) + circuit.add(measurement) transpiled_circuit, final_qubit_map = router(circuit, initial_layout) assert router.added_swaps >= 0 assert_connectivity(connectivity, transpiled_circuit) assert_placement(transpiled_circuit, final_qubit_map) - assert gates + router.added_swaps == transpiled_circuit.ngates + assert n_gates + router.added_swaps + 1 == transpiled_circuit.ngates assert_circuit_equivalence( original_circuit=circuit, transpiled_circuit=transpiled_circuit, final_map=final_qubit_map, initial_map=initial_layout, ) + circuit_result = transpiled_circuit.execute(nshots=100) + assert circuit_result.frequencies() == measurement.result.frequencies() + assert transpiled_circuit.queue[-1].result is measurement.result def test_sabre_memory_map(): @@ -338,23 +343,7 @@ def test_sabre_intermediate_measurements(): circ = Circuit(3, density_matrix=True) circ.add(gates.H(0)) circ.add(measurement) - circ.add(gates.CNOT(0, 1)) - connectivity = nx.Graph() - connectivity.add_nodes_from([0, 1, 2]) - connectivity.add_edges_from([(0, 1), (1, 2)]) - router = Sabre(connectivity=connectivity) - initial_layout = {"q0": 0, "q1": 1, "q2": 2} - routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) - circuit_result = routed_circ.execute(nshots=100) - assert routed_circ.queue[1].result is measurement.result - - -def test_sabre_final_measurements(): - measurement = gates.M(0, 1, 2) - circ = Circuit(3) - circ.add(gates.H(0)) circ.add(gates.CNOT(0, 2)) - circ.add(measurement) connectivity = nx.Graph() connectivity.add_nodes_from([0, 1, 2]) connectivity.add_edges_from([(0, 1), (1, 2)]) @@ -362,5 +351,4 @@ def test_sabre_final_measurements(): initial_layout = {"q0": 0, "q1": 1, "q2": 2} routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) circuit_result = routed_circ.execute(nshots=100) - assert circuit_result.frequencies() == measurement.result.frequencies() - assert routed_circ.queue[-1].result is measurement.result + assert routed_circ.queue[2].result is measurement.result From 23cd8689ce4221ef7ad5ffa8b4a32689c34f1332 Mon Sep 17 00:00:00 2001 From: Andrea Papaluca Date: Mon, 20 Nov 2023 13:53:34 +0400 Subject: [PATCH 09/10] moved measurement --- tests/test_transpiler_router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index 2a950f05b8..d991c7dd45 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -339,7 +339,7 @@ def test_sabre_memory_map(): def test_sabre_intermediate_measurements(): - measurement = gates.M(0) + measurement = gates.M(1) circ = Circuit(3, density_matrix=True) circ.add(gates.H(0)) circ.add(measurement) From 053c803abcf810a33c00e10e89a4803df2146893 Mon Sep 17 00:00:00 2001 From: simone bordoni Date: Tue, 21 Nov 2023 11:10:01 +0400 Subject: [PATCH 10/10] fixed tests --- tests/test_transpiler_router.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_transpiler_router.py b/tests/test_transpiler_router.py index d991c7dd45..28a0c9b15f 100644 --- a/tests/test_transpiler_router.py +++ b/tests/test_transpiler_router.py @@ -351,4 +351,4 @@ def test_sabre_intermediate_measurements(): initial_layout = {"q0": 0, "q1": 1, "q2": 2} routed_circ, final_layout = router(circuit=circ, initial_layout=initial_layout) circuit_result = routed_circ.execute(nshots=100) - assert routed_circ.queue[2].result is measurement.result + assert routed_circ.queue[3].result is measurement.result