From b5aeccc070c22341274eac203689e28019961f59 Mon Sep 17 00:00:00 2001 From: Henry Zou <87874865+henryzou50@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:11:06 -0400 Subject: [PATCH 1/2] Fix input normalisation of `transpile(initial_layout=...)` (#11031) * Fixed issue10554 by modifying _parse_initial_layout in transpile to normalize input of range to a list * Added test and bugfix note * Handle consumable iterables * Fix release-note cross references --------- Co-authored-by: Jake Lishman (cherry picked from commit 6148f903d14612dc74270a836d179af8182a8605) # Conflicts: # test/python/transpiler/test_transpile_layout.py --- qiskit/compiler/transpiler.py | 4 +- ...alization-of-transpile-initial-layout.yaml | 6 + .../transpiler/test_transpile_layout.py | 306 ++++++++++++++++++ 3 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/fix-input-normalization-of-transpile-initial-layout.yaml create mode 100644 test/python/transpiler/test_transpile_layout.py diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index d93d0ce4e380..b69c8695c280 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -503,10 +503,10 @@ def _parse_initial_layout(initial_layout): return initial_layout if isinstance(initial_layout, dict): return Layout(initial_layout) + initial_layout = list(initial_layout) if all(phys is None or isinstance(phys, Qubit) for phys in initial_layout): return Layout.from_qubit_list(initial_layout) - else: - return initial_layout + return initial_layout def _parse_instruction_durations(backend, inst_durations, dt, circuit): diff --git a/releasenotes/notes/fix-input-normalization-of-transpile-initial-layout.yaml b/releasenotes/notes/fix-input-normalization-of-transpile-initial-layout.yaml new file mode 100644 index 000000000000..a996861ce992 --- /dev/null +++ b/releasenotes/notes/fix-input-normalization-of-transpile-initial-layout.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed a regression in :func:`.transpile`, where an ``initial_layout`` given + as a :class:`range` object would no longer be treated as a valid input. + Fixed `#10544 `__. diff --git a/test/python/transpiler/test_transpile_layout.py b/test/python/transpiler/test_transpile_layout.py new file mode 100644 index 000000000000..0ece5126f374 --- /dev/null +++ b/test/python/transpiler/test_transpile_layout.py @@ -0,0 +1,306 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023 +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=missing-function-docstring + +"""Tests the layout object""" + +from qiskit.circuit import QuantumCircuit, QuantumRegister +from qiskit.transpiler.layout import Layout, TranspileLayout +from qiskit.transpiler.coupling import CouplingMap +from qiskit.compiler import transpile +from qiskit.test import QiskitTestCase + + +class TranspileLayoutTest(QiskitTestCase): + """Test the methods in the TranspileLayout object.""" + + def test_final_index_layout_full_path(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(3, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) + res = tqc.layout.final_index_layout() + self.assertEqual(res, [2, 0, 1]) + + def test_final_virtual_layout_full_path(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(3, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) + res = tqc.layout.final_virtual_layout() + self.assertEqual(res, Layout({qc.qubits[0]: 2, qc.qubits[1]: 0, qc.qubits[2]: 1})) + + def test_final_index_layout_full_path_with_ancilla(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(10, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) + # tqc: + # q_2 -> 0 ──X───────────────────────────────────────────────── + # │ + # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── + # │ + # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── + # │ ┌───┐ ┌───┐ + # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ + # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ + # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ + # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ + # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── + # │ └───┘└───┘└───┘ + # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── + # │ + # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── + # │ + # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── + # ┌───┐ │ + # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── + # └───┘ + res = tqc.layout.final_index_layout() + self.assertEqual(res, [4, 5, 3]) + + def test_final_index_layout_full_path_with_ancilla_no_filter(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(10, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) + # tqc: + # q_2 -> 0 ──X───────────────────────────────────────────────── + # │ + # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── + # │ + # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── + # │ ┌───┐ ┌───┐ + # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ + # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ + # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ + # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ + # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── + # │ └───┘└───┘└───┘ + # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── + # │ + # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── + # │ + # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── + # ┌───┐ │ + # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── + # └───┘ + res = tqc.layout.final_index_layout(filter_ancillas=False) + self.assertEqual(res, [4, 5, 3, 0, 1, 2, 6, 7, 8, 9]) + + def test_final_virtual_layout_full_path_with_ancilla(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(10, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) + # tqc: + # q_2 -> 0 ──X───────────────────────────────────────────────── + # │ + # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── + # │ + # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── + # │ ┌───┐ ┌───┐ + # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ + # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ + # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ + # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ + # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── + # │ └───┘└───┘└───┘ + # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── + # │ + # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── + # │ + # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── + # ┌───┐ │ + # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── + # └───┘ + res = tqc.layout.final_virtual_layout() + self.assertEqual(res, Layout({qc.qubits[0]: 4, qc.qubits[1]: 5, qc.qubits[2]: 3})) + + def test_final_virtual_layout_full_path_with_ancilla_no_filter(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(10, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) + # tqc: + # q_2 -> 0 ──X───────────────────────────────────────────────── + # │ + # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── + # │ + # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── + # │ ┌───┐ ┌───┐ + # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ + # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ + # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ + # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ + # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── + # │ └───┘└───┘└───┘ + # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── + # │ + # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── + # │ + # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── + # ┌───┐ │ + # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── + # └───┘ + res = tqc.layout.final_virtual_layout(filter_ancillas=False) + pos_to_virt = {v: k for k, v in tqc.layout.input_qubit_mapping.items()} + expected = Layout( + { + pos_to_virt[0]: 4, + pos_to_virt[1]: 5, + pos_to_virt[2]: 3, + pos_to_virt[3]: 0, + pos_to_virt[4]: 1, + pos_to_virt[5]: 2, + pos_to_virt[6]: 6, + pos_to_virt[7]: 7, + pos_to_virt[8]: 8, + pos_to_virt[9]: 9, + } + ) + self.assertEqual(res, expected) + + def test_routing_permutation(self): + qr = QuantumRegister(5) + final_layout = Layout( + { + qr[0]: 2, + qr[1]: 4, + qr[2]: 1, + qr[3]: 0, + qr[4]: 3, + } + ) + layout_obj = TranspileLayout( + initial_layout=Layout.generate_trivial_layout(qr), + input_qubit_mapping={v: k for k, v in enumerate(qr)}, + final_layout=final_layout, + _input_qubit_count=5, + _output_qubit_list=list(qr), + ) + res = layout_obj.routing_permutation() + self.assertEqual(res, [2, 4, 1, 0, 3]) + + def test_routing_permutation_no_final_layout(self): + qr = QuantumRegister(5) + layout_obj = TranspileLayout( + initial_layout=Layout.generate_trivial_layout(qr), + input_qubit_mapping={v: k for k, v in enumerate(qr)}, + final_layout=None, + _input_qubit_count=5, + _output_qubit_list=list(qr), + ) + res = layout_obj.routing_permutation() + self.assertEqual(res, list(range(5))) + + def test_initial_index_layout(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(3, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) + self.assertEqual(tqc.layout.initial_index_layout(), [2, 1, 0]) + + def test_initial_index_layout_with_ancillas(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(6, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) + self.assertEqual(tqc.layout.initial_index_layout(), [2, 1, 0, 3, 4, 5]) + + def test_initial_index_layout_filter_ancillas(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(6, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[5, 2, 1], seed_transpiler=42) + self.assertEqual(tqc.layout.initial_index_layout(True), [5, 2, 1]) + + def test_initial_virtual_layout(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(3, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) + self.assertEqual( + tqc.layout.initial_virtual_layout(), + Layout.from_qubit_list([qc.qubits[2], qc.qubits[1], qc.qubits[0]]), + ) + + def test_initial_virtual_layout_with_ancillas(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(6, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) + reverse_pos_map = {v: k for k, v in tqc.layout.input_qubit_mapping.items()} + self.assertEqual( + tqc.layout.initial_virtual_layout(), + Layout.from_qubit_list( + [ + reverse_pos_map[2], + reverse_pos_map[1], + reverse_pos_map[0], + reverse_pos_map[3], + reverse_pos_map[4], + reverse_pos_map[5], + ] + ), + ) + + def test_initial_virtual_layout_filter_ancillas(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(6, bidirectional=False) + tqc = transpile(qc, coupling_map=cmap, initial_layout=[5, 2, 1], seed_transpiler=42) + self.assertEqual( + tqc.layout.initial_virtual_layout(True), + Layout( + { + qc.qubits[0]: 5, + qc.qubits[1]: 2, + qc.qubits[2]: 1, + } + ), + ) + + def test_initial_layout_consistency_for_range_and_list(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(3, bidirectional=False) + tqc_1 = transpile(qc, coupling_map=cmap, initial_layout=range(3), seed_transpiler=42) + tqc_2 = transpile(qc, coupling_map=cmap, initial_layout=list(range(3)), seed_transpiler=42) + self.assertEqual(tqc_1.layout.initial_index_layout(), tqc_2.layout.initial_index_layout()) From 95668b7cdd331dddb2b31f7a9d85a127e7f51217 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 24 Oct 2023 15:57:30 -0400 Subject: [PATCH 2/2] Fix backport merge conflicts The backport of this commit from main failed because the test for this bug fix was added to an existing file on the main branch but that didn't exist on the stable/0.25 branch. To fix this the test is moved to a separate standalone file. Arguably, this should be done on main and stable/0.45 too as the file the test was added to was for a different component. --- test/python/transpiler/test_set_layout.py | 34 ++ .../transpiler/test_transpile_layout.py | 306 ------------------ 2 files changed, 34 insertions(+), 306 deletions(-) create mode 100644 test/python/transpiler/test_set_layout.py delete mode 100644 test/python/transpiler/test_transpile_layout.py diff --git a/test/python/transpiler/test_set_layout.py b/test/python/transpiler/test_set_layout.py new file mode 100644 index 000000000000..d12c1f448d2f --- /dev/null +++ b/test/python/transpiler/test_set_layout.py @@ -0,0 +1,34 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023 +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=missing-function-docstring + +"""Tests the SetLayout pass""" + +from qiskit.compiler import transpile +from qiskit.circuit import QuantumCircuit +from qiskit.transpiler import CouplingMap +from qiskit.test import QiskitTestCase + + +class TestSetLayout(QiskitTestCase): + """Test the SetLayout pass.""" + + def test_initial_layout_consistency_for_range_and_list(self): + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + cmap = CouplingMap.from_line(3, bidirectional=False) + tqc_1 = transpile(qc, coupling_map=cmap, initial_layout=range(3), seed_transpiler=42) + tqc_2 = transpile(qc, coupling_map=cmap, initial_layout=list(range(3)), seed_transpiler=42) + self.assertEqual(tqc_1.layout.initial_layout, tqc_2.layout.initial_layout) diff --git a/test/python/transpiler/test_transpile_layout.py b/test/python/transpiler/test_transpile_layout.py deleted file mode 100644 index 0ece5126f374..000000000000 --- a/test/python/transpiler/test_transpile_layout.py +++ /dev/null @@ -1,306 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=missing-function-docstring - -"""Tests the layout object""" - -from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.transpiler.layout import Layout, TranspileLayout -from qiskit.transpiler.coupling import CouplingMap -from qiskit.compiler import transpile -from qiskit.test import QiskitTestCase - - -class TranspileLayoutTest(QiskitTestCase): - """Test the methods in the TranspileLayout object.""" - - def test_final_index_layout_full_path(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(3, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) - res = tqc.layout.final_index_layout() - self.assertEqual(res, [2, 0, 1]) - - def test_final_virtual_layout_full_path(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(3, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) - res = tqc.layout.final_virtual_layout() - self.assertEqual(res, Layout({qc.qubits[0]: 2, qc.qubits[1]: 0, qc.qubits[2]: 1})) - - def test_final_index_layout_full_path_with_ancilla(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(10, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) - # tqc: - # q_2 -> 0 ──X───────────────────────────────────────────────── - # │ - # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── - # │ - # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── - # │ ┌───┐ ┌───┐ - # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ - # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ - # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ - # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ - # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── - # │ └───┘└───┘└───┘ - # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── - # │ - # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── - # │ - # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── - # ┌───┐ │ - # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── - # └───┘ - res = tqc.layout.final_index_layout() - self.assertEqual(res, [4, 5, 3]) - - def test_final_index_layout_full_path_with_ancilla_no_filter(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(10, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) - # tqc: - # q_2 -> 0 ──X───────────────────────────────────────────────── - # │ - # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── - # │ - # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── - # │ ┌───┐ ┌───┐ - # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ - # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ - # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ - # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ - # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── - # │ └───┘└───┘└───┘ - # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── - # │ - # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── - # │ - # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── - # ┌───┐ │ - # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── - # └───┘ - res = tqc.layout.final_index_layout(filter_ancillas=False) - self.assertEqual(res, [4, 5, 3, 0, 1, 2, 6, 7, 8, 9]) - - def test_final_virtual_layout_full_path_with_ancilla(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(10, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) - # tqc: - # q_2 -> 0 ──X───────────────────────────────────────────────── - # │ - # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── - # │ - # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── - # │ ┌───┐ ┌───┐ - # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ - # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ - # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ - # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ - # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── - # │ └───┘└───┘└───┘ - # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── - # │ - # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── - # │ - # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── - # ┌───┐ │ - # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── - # └───┘ - res = tqc.layout.final_virtual_layout() - self.assertEqual(res, Layout({qc.qubits[0]: 4, qc.qubits[1]: 5, qc.qubits[2]: 3})) - - def test_final_virtual_layout_full_path_with_ancilla_no_filter(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(10, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[9, 4, 0], seed_transpiler=42) - # tqc: - # q_2 -> 0 ──X───────────────────────────────────────────────── - # │ - # ancilla_0 -> 1 ──X───X───────────────────────────────────────────── - # │ - # ancilla_1 -> 2 ──────X──X────────────────────────────────────────── - # │ ┌───┐ ┌───┐ - # ancilla_2 -> 3 ─────────X─┤ H ├────────────────────────────■──┤ H ├ - # ┌───┐ └───┘ ┌───┐ ┌───┐┌─┴─┐├───┤ - # q_1 -> 4 ┤ H ├─────────────────────■──┤ H ├─X─┤ H ├┤ X ├┤ H ├ - # └───┘ ┌───┐┌─┴─┐├───┤ │ └───┘└───┘└───┘ - # ancilla_3 -> 5 ─────────────────X─┤ H ├┤ X ├┤ H ├─X──────────────── - # │ └───┘└───┘└───┘ - # ancilla_4 -> 6 ─────────────X───X────────────────────────────────── - # │ - # ancilla_5 -> 7 ─────────X───X────────────────────────────────────── - # │ - # ancilla_6 -> 8 ──────X──X────────────────────────────────────────── - # ┌───┐ │ - # q_0 -> 9 ┤ H ├─X───────────────────────────────────────────── - # └───┘ - res = tqc.layout.final_virtual_layout(filter_ancillas=False) - pos_to_virt = {v: k for k, v in tqc.layout.input_qubit_mapping.items()} - expected = Layout( - { - pos_to_virt[0]: 4, - pos_to_virt[1]: 5, - pos_to_virt[2]: 3, - pos_to_virt[3]: 0, - pos_to_virt[4]: 1, - pos_to_virt[5]: 2, - pos_to_virt[6]: 6, - pos_to_virt[7]: 7, - pos_to_virt[8]: 8, - pos_to_virt[9]: 9, - } - ) - self.assertEqual(res, expected) - - def test_routing_permutation(self): - qr = QuantumRegister(5) - final_layout = Layout( - { - qr[0]: 2, - qr[1]: 4, - qr[2]: 1, - qr[3]: 0, - qr[4]: 3, - } - ) - layout_obj = TranspileLayout( - initial_layout=Layout.generate_trivial_layout(qr), - input_qubit_mapping={v: k for k, v in enumerate(qr)}, - final_layout=final_layout, - _input_qubit_count=5, - _output_qubit_list=list(qr), - ) - res = layout_obj.routing_permutation() - self.assertEqual(res, [2, 4, 1, 0, 3]) - - def test_routing_permutation_no_final_layout(self): - qr = QuantumRegister(5) - layout_obj = TranspileLayout( - initial_layout=Layout.generate_trivial_layout(qr), - input_qubit_mapping={v: k for k, v in enumerate(qr)}, - final_layout=None, - _input_qubit_count=5, - _output_qubit_list=list(qr), - ) - res = layout_obj.routing_permutation() - self.assertEqual(res, list(range(5))) - - def test_initial_index_layout(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(3, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) - self.assertEqual(tqc.layout.initial_index_layout(), [2, 1, 0]) - - def test_initial_index_layout_with_ancillas(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(6, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) - self.assertEqual(tqc.layout.initial_index_layout(), [2, 1, 0, 3, 4, 5]) - - def test_initial_index_layout_filter_ancillas(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(6, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[5, 2, 1], seed_transpiler=42) - self.assertEqual(tqc.layout.initial_index_layout(True), [5, 2, 1]) - - def test_initial_virtual_layout(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(3, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) - self.assertEqual( - tqc.layout.initial_virtual_layout(), - Layout.from_qubit_list([qc.qubits[2], qc.qubits[1], qc.qubits[0]]), - ) - - def test_initial_virtual_layout_with_ancillas(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(6, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[2, 1, 0], seed_transpiler=42) - reverse_pos_map = {v: k for k, v in tqc.layout.input_qubit_mapping.items()} - self.assertEqual( - tqc.layout.initial_virtual_layout(), - Layout.from_qubit_list( - [ - reverse_pos_map[2], - reverse_pos_map[1], - reverse_pos_map[0], - reverse_pos_map[3], - reverse_pos_map[4], - reverse_pos_map[5], - ] - ), - ) - - def test_initial_virtual_layout_filter_ancillas(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(6, bidirectional=False) - tqc = transpile(qc, coupling_map=cmap, initial_layout=[5, 2, 1], seed_transpiler=42) - self.assertEqual( - tqc.layout.initial_virtual_layout(True), - Layout( - { - qc.qubits[0]: 5, - qc.qubits[1]: 2, - qc.qubits[2]: 1, - } - ), - ) - - def test_initial_layout_consistency_for_range_and_list(self): - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - cmap = CouplingMap.from_line(3, bidirectional=False) - tqc_1 = transpile(qc, coupling_map=cmap, initial_layout=range(3), seed_transpiler=42) - tqc_2 = transpile(qc, coupling_map=cmap, initial_layout=list(range(3)), seed_transpiler=42) - self.assertEqual(tqc_1.layout.initial_index_layout(), tqc_2.layout.initial_index_layout())