From 5f3117285bdcebebecd0a4f0f5a6070ab9a5378c Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 7 Jun 2023 14:14:09 +0200 Subject: [PATCH 1/4] * Added support for targets without a coupling map. --- .../passes/layout/full_ancilla_allocation.py | 16 +++++++++++--- ...a_allocation_no_cmap-ac3ff65b3639988e.yaml | 7 ++++++ .../test_full_ancilla_allocation.py | 22 +++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml diff --git a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py index e3259b4d0ad9..1cafa7fa07d8 100644 --- a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py +++ b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py @@ -19,11 +19,11 @@ class FullAncillaAllocation(AnalysisPass): - """Allocate all idle nodes from the coupling map as ancilla on the layout. + """Allocate all idle nodes from the coupling map or target as ancilla on the layout. A pass for allocating all idle physical qubits (those that exist in coupling - map but not the dag circuit) as ancilla. It will also choose new virtual - qubits to correspond to those physical ancilla. + map or target but not the dag circuit) as ancilla. It will also choose new + virtual qubits to correspond to those physical ancilla. Note: This is an analysis pass, and only responsible for choosing physical @@ -85,6 +85,16 @@ def run(self, dag): idle_physical_qubits = [ q for q in self.coupling_map.physical_qubits if q not in physical_bits ] + else: + if self.target: + idle_physical_qubits = [ + q for q in range(self.target.num_qubits) if q not in physical_bits + ] + else: + raise TranspilerError( + "Coupling map is None as well as Target. " + "Could not determine number of qubits to allocate to." + ) if idle_physical_qubits: if self.ancilla_name in dag.qregs: diff --git a/releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml b/releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml new file mode 100644 index 000000000000..dd5f637b0ad2 --- /dev/null +++ b/releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + FullAncillaAllocation can now function with target's that do not have a + coupling map. In this case FullAncillaAllocation will add a number of + ancilla qubits so that the number of qubits in the dag matches the number + of qubits in the target. diff --git a/test/python/transpiler/test_full_ancilla_allocation.py b/test/python/transpiler/test_full_ancilla_allocation.py index 741b3b98a2b4..680d01bcd05d 100644 --- a/test/python/transpiler/test_full_ancilla_allocation.py +++ b/test/python/transpiler/test_full_ancilla_allocation.py @@ -214,6 +214,28 @@ def test_bad_layout(self): cm.exception.message, ) + def test_target_without_cmap(self): + """Test that FullAncillaAllocation works when the target does not have a coupling map. + + This situation occurs at the early stages of backend bring-up. + """ + target_data = {"basis_gates": ["h"], "num_qubits": 3} + target = Target.from_configuration(**target_data) + + circ = QuantumCircuit(1) + circ.h(0) + + pass_ = FullAncillaAllocation(target) + pass_.property_set["layout"] = Layout.from_intlist([0], *circ.qregs) + + # Pre pass check + self.assertEqual(len(pass_.property_set["layout"]), 1) + + pass_.run(circuit_to_dag(circ)) + + # Post pass check + self.assertEqual(len(pass_.property_set["layout"]), target.num_qubits) + if __name__ == "__main__": unittest.main() From 1970329e6f974bba234afd3f55e9d67b2f735778 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 7 Jun 2023 14:43:39 +0200 Subject: [PATCH 2/4] * Removed restrictive raise. --- qiskit/transpiler/passes/layout/full_ancilla_allocation.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py index 1cafa7fa07d8..727ebbd51162 100644 --- a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py +++ b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py @@ -90,11 +90,6 @@ def run(self, dag): idle_physical_qubits = [ q for q in range(self.target.num_qubits) if q not in physical_bits ] - else: - raise TranspilerError( - "Coupling map is None as well as Target. " - "Could not determine number of qubits to allocate to." - ) if idle_physical_qubits: if self.ancilla_name in dag.qregs: From f5350b5e30426b9513abd76284623498bf726afa Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 8 Jun 2023 14:44:49 +0200 Subject: [PATCH 3/4] * Changed order of target and coupling map --- .../passes/layout/full_ancilla_allocation.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py index 727ebbd51162..81ae7922806e 100644 --- a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py +++ b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py @@ -81,15 +81,14 @@ def run(self, dag): idle_physical_qubits = [q for q in layout_physical_qubits if q not in physical_bits] - if self.coupling_map: + if self.target: + idle_physical_qubits = [ + q for q in range(self.target.num_qubits) if q not in physical_bits + ] + elif self.coupling_map: idle_physical_qubits = [ q for q in self.coupling_map.physical_qubits if q not in physical_bits ] - else: - if self.target: - idle_physical_qubits = [ - q for q in range(self.target.num_qubits) if q not in physical_bits - ] if idle_physical_qubits: if self.ancilla_name in dag.qregs: From 9898c13fab9969dfc81973c6905404bfc8815cb3 Mon Sep 17 00:00:00 2001 From: "Daniel J. Egger" <38065505+eggerdj@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:06:17 +0200 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Matthew Treinish --- .../ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml b/releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml index dd5f637b0ad2..ac8d2c1d9eb1 100644 --- a/releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml +++ b/releasenotes/notes/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml @@ -1,7 +1,7 @@ --- fixes: - | - FullAncillaAllocation can now function with target's that do not have a - coupling map. In this case FullAncillaAllocation will add a number of - ancilla qubits so that the number of qubits in the dag matches the number - of qubits in the target. + Fixed an issue with :class:`~.FullAncillaAllocation` so it can now function with :class:`~.Target` objects that do not have a + coupling map (typically because there are no 2 qubit gates in the :class:`~.Target`). In this case :class:`~.FullAncillaAllocation` will add + ancilla qubits so that the number of qubits in the :class:`~.DAGCircuit` matches the number + :attr:`.Target.num_qubits`.