From bdf0ae3b1d87d3a97b350dade1d42047de860b7b Mon Sep 17 00:00:00 2001 From: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:24:35 +0200 Subject: [PATCH 1/5] Improves Returned Information of VF2PostLayout --- .../passes/layout/vf2_post_layout.py | 11 +++-- .../python/transpiler/test_vf2_post_layout.py | 48 +++++++++++++++++-- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/qiskit/transpiler/passes/layout/vf2_post_layout.py b/qiskit/transpiler/passes/layout/vf2_post_layout.py index 96ffc745b451..c068edec47ee 100644 --- a/qiskit/transpiler/passes/layout/vf2_post_layout.py +++ b/qiskit/transpiler/passes/layout/vf2_post_layout.py @@ -35,6 +35,7 @@ class VF2PostLayoutStopReason(Enum): """Stop reasons for VF2PostLayout pass.""" SOLUTION_FOUND = "solution found" + NO_BETTER_SOLUTION_FOUND = "no better solution found" NO_SOLUTION_FOUND = "nonexistent solution" MORE_THAN_2Q = ">2q gates in basis" @@ -66,15 +67,16 @@ class VF2PostLayout(AnalysisPass): If a solution is found that means there is a lower error layout available for the circuit. If a solution is found the layout will be set in the property set as - ``property_set['post_layout']``. However, if no solution is found, no + ``property_set['post_layout']``. However, if no solution or no better solution is found, no ``property_set['post_layout']`` is set. The stopping reason is set in ``property_set['VF2PostLayout_stop_reason']`` in all the cases and will be one of the values enumerated in ``VF2PostLayoutStopReason`` which has the following values: * ``"solution found"``: If a solution was found. + * ``"no better solution found"``: If the initial layout of the circuit is the best solution. * ``"nonexistent solution"``: If no solution was found. - * ``">2q gates in basis"``: If VF2PostLayout can't work with basis + * ``">2q gates in basis"``: If VF2PostLayout can't work with the basis of the circuit. By default this pass will construct a heuristic scoring map based on the the error rates in the provided ``target`` (or ``properties`` if ``target`` @@ -339,7 +341,10 @@ def run(self, dag): ) break if chosen_layout is None: - stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND + if initial_layout is not None: + stop_reason = VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND + else: + stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND else: chosen_layout = vf2_utils.map_free_qubits( free_nodes, diff --git a/test/python/transpiler/test_vf2_post_layout.py b/test/python/transpiler/test_vf2_post_layout.py index d06e46a229bd..da0dbb274c2b 100644 --- a/test/python/transpiler/test_vf2_post_layout.py +++ b/test/python/transpiler/test_vf2_post_layout.py @@ -108,7 +108,7 @@ def test_empty_circuit(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_empty_circuit_v2(self): @@ -119,7 +119,7 @@ def test_empty_circuit_v2(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_skip_3q_circuit(self): @@ -389,6 +389,46 @@ def test_target_some_error(self): # No layout selected because nothing will beat initial layout self.assertNotIn("post_layout", vf2_pass.property_set) + def test_trivial_layout_is_best(self): + """Test that vf2postlayout reports no better solution if the trivial layout is the best layout""" + n_qubits = 4 + trivial_target = Target() + trivial_target.add_instruction( + CXGate(), {(i, i + 1): InstructionProperties(error=0.001) for i in range(n_qubits - 1)} + ) + + circuit = QuantumCircuit(n_qubits) + circuit.cx(0, 1) + circuit.cx(1, 2) + + vf2_pass = VF2PostLayout(target=trivial_target, seed=self.seed, strict_direction=False) + dag = circuit_to_dag(circuit) + vf2_pass.run(dag) + self.assertEqual( + vf2_pass.property_set["VF2PostLayout_stop_reason"], + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, + ) + + def test_last_qubits_best(self): + """Test that vf2postlayout determines the best layout when the last qubits have least error""" + n_qubits = 4 + target_last_qubits_best = Target() + target_last_qubits_best.add_instruction( + CXGate(), + {(i, i + 1): InstructionProperties(error=10**-i) for i in range(n_qubits - 1)}, + ) + + circuit = QuantumCircuit(n_qubits) + circuit.cx(0, 1) + circuit.cx(1, 2) + + vf2_pass = VF2PostLayout( + target=target_last_qubits_best, seed=self.seed, strict_direction=False + ) + dag = circuit_to_dag(circuit) + vf2_pass.run(dag) + self.assertLayout(dag, target_last_qubits_best.build_coupling_map(), vf2_pass.property_set) + class TestVF2PostLayoutScoring(QiskitTestCase): """Test scoring heuristic function for VF2PostLayout.""" @@ -480,7 +520,7 @@ def test_empty_circuit(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_empty_circuit_v2(self): @@ -491,7 +531,7 @@ def test_empty_circuit_v2(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_skip_3q_circuit(self): From a1a9cc321341f62ec871338863a7e57ffaae1fa6 Mon Sep 17 00:00:00 2001 From: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:26:34 +0200 Subject: [PATCH 2/5] small change to class doc --- qiskit/transpiler/passes/layout/vf2_post_layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/layout/vf2_post_layout.py b/qiskit/transpiler/passes/layout/vf2_post_layout.py index c068edec47ee..aabc79049869 100644 --- a/qiskit/transpiler/passes/layout/vf2_post_layout.py +++ b/qiskit/transpiler/passes/layout/vf2_post_layout.py @@ -52,7 +52,7 @@ def _target_match(node_a, node_b): class VF2PostLayout(AnalysisPass): - """A pass for choosing a Layout after transpilation of a circuit onto a + """A pass for improving an existing Layout after transpilation of a circuit onto a Coupling graph, as a subgraph isomorphism problem, solved by VF2++. Unlike the :class:`~.VF2Layout` transpiler pass which is designed to find an From 0abcdef97daeb329f1357d2712fe2c9f05ffe564 Mon Sep 17 00:00:00 2001 From: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:32:05 +0200 Subject: [PATCH 3/5] Set 'no better solution' as default --- .../transpiler/passes/layout/vf2_post_layout.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qiskit/transpiler/passes/layout/vf2_post_layout.py b/qiskit/transpiler/passes/layout/vf2_post_layout.py index aabc79049869..c1b1da8d4190 100644 --- a/qiskit/transpiler/passes/layout/vf2_post_layout.py +++ b/qiskit/transpiler/passes/layout/vf2_post_layout.py @@ -280,6 +280,8 @@ def run(self, dag): self.strict_direction, run_in_parallel, ) + chosen_layout = initial_layout + stop_reason = VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND # Circuit not in basis so we have nothing to compare against return here except KeyError: self.property_set[ @@ -294,7 +296,6 @@ def run(self, dag): for mapping in mappings: trials += 1 logger.debug("Running trial: %s", trials) - stop_reason = VF2PostLayoutStopReason.SOLUTION_FOUND layout_mapping = {im_i: cm_nodes[cm_i] for cm_i, im_i in mapping.items()} if self.strict_direction: layout = Layout( @@ -327,6 +328,7 @@ def run(self, dag): ) chosen_layout = layout chosen_layout_score = layout_score + stop_reason = VF2PostLayoutStopReason.SOLUTION_FOUND if self.max_trials and trials >= self.max_trials: logger.debug("Trial %s is >= configured max trials %s", trials, self.max_trials) @@ -340,12 +342,7 @@ def run(self, dag): self.time_limit, ) break - if chosen_layout is None: - if initial_layout is not None: - stop_reason = VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND - else: - stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND - else: + if stop_reason == VF2PostLayoutStopReason.SOLUTION_FOUND: chosen_layout = vf2_utils.map_free_qubits( free_nodes, chosen_layout, @@ -369,7 +366,10 @@ def run(self, dag): chosen_layout.add(bit, i) break self.property_set["post_layout"] = chosen_layout - + else: + if chosen_layout is None: + stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND + # else the initial layout is optimal -> don't set post_layout, return 'no better solution' self.property_set["VF2PostLayout_stop_reason"] = stop_reason def _score_layout(self, layout, bit_map, reverse_bit_map, im_graph): From 6551d6d0e73f48358e39e71634e99849fcbc478d Mon Sep 17 00:00:00 2001 From: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:42:59 +0200 Subject: [PATCH 4/5] added release note --- .../vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml diff --git a/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml b/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml new file mode 100644 index 000000000000..04255c79719b --- /dev/null +++ b/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + VF2PostLayout no distinguishes between 'no solution' and 'no better solution' when determining a + Layout for a given quantum circuit. 'no better solution' is set when the initial layout of a quantum + circuit is also the optimal one, i.e. incurs the least cost in terms of error rates. From 6ae3b3197be5763d62509a801b11d984c3ad618f Mon Sep 17 00:00:00 2001 From: Sebastian Brandhofer <148463728+sbrandhsn@users.noreply.github.com> Date: Fri, 3 Nov 2023 10:59:27 +0100 Subject: [PATCH 5/5] update reno --- .../vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml b/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml index 04255c79719b..25d361e51896 100644 --- a/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml +++ b/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml @@ -1,6 +1,7 @@ --- features: - | - VF2PostLayout no distinguishes between 'no solution' and 'no better solution' when determining a - Layout for a given quantum circuit. 'no better solution' is set when the initial layout of a quantum - circuit is also the optimal one, i.e. incurs the least cost in terms of error rates. + :class:`.VF2PostLayout` now distinguishes between 'no solution' and 'no better solution' when + determining a :class:`.Layout` for a given quantum circuit. 'no better solution' is set when the + initial layout of a quantum circuit is also the optimal one, i.e. incurs the least cost in terms of + error rates.