From 4181428c47996a6405438d8640ce09f4ab81d28b Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Mon, 14 Aug 2023 15:36:16 +0300 Subject: [PATCH 01/20] trying to implement permutation synthesis plugin based on token swapper --- .../passes/synthesis/high_level_synthesis.py | 60 ++++++++++++++++++ setup.py | 1 + .../transpiler/test_high_level_synthesis.py | 63 ++++++++++++++++++- 3 files changed, 123 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 5750c7fa6260..bec1bda488e0 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -14,13 +14,16 @@ """Synthesize higher-level objects.""" from typing import Optional +import rustworkx as rx +from qiskit.circuit import QuantumCircuit from qiskit.converters import circuit_to_dag from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.target import Target from qiskit.transpiler.coupling import CouplingMap from qiskit.dagcircuit.dagcircuit import DAGCircuit from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper from qiskit.synthesis.clifford import ( synth_clifford_full, @@ -404,3 +407,60 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** """Run synthesis for the given Permutation.""" decomposition = synth_permutation_acg(high_level_object.pattern) return decomposition + + +class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin): + """The permutation synthesis plugin based on the token swapper algorithm. + For more details you can refer to the paper describing the algorithm: + `arXiv:1809.03452 `__. + + This plugin name is :``permutation.token_swapper`` which can be used as the key on + an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`. + + The plugin supports the following plugin-specific options: + + * trials: The number of trials for the token swapper to perform the mapping. The + circuit with the smallest number of SWAPs is returned. + + """ + + def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options): + """Run synthesis for the given Permutation.""" + + print(f"Running token_swapper plugin with {coupling_map = }, {qubits = }, {options = }") + + pattern = high_level_object.pattern + trials = options.get("trials", 5) + + # The plugin considers two different cases: if either the coupling map + # or the set of qubits over which the permutation is defined is None, then it + # performs the abstract synthesis; otherwise it performs the concrete synthesis. + + if coupling_map is None or qubits is None: + pattern_as_dict = {j: i for i, j in enumerate(pattern)} + used_coupling_map = CouplingMap.from_full(len(pattern)) + graph = rx.PyGraph() + graph.extend_from_edge_list(list(used_coupling_map.get_edges())) + swapper = ApproximateTokenSwapper(graph, seed=1) + out = list(swapper.map(pattern_as_dict, trials)) + decomposition = QuantumCircuit(len(graph.node_indices())) + for swap in out: + decomposition.swap(*swap) + else: + if len(pattern) != len(qubits): + raise TranspilerError( + "The permutation pattern and the set of qubits over which the " + "permutation is defined have different lengths" + ) + + pattern_as_dict = {qubits[j]: qubits[i] for i, j in enumerate(pattern)} + graph = rx.PyGraph() + graph.extend_from_edge_list(list(coupling_map.get_edges())) + swapper = ApproximateTokenSwapper(graph, seed=1) + out = list(swapper.map(pattern_as_dict, trials)) + decomposition = QuantumCircuit(len(graph.node_indices())) + for swap in out: + decomposition.swap(*swap) + print(f"GOT DECOMPOSITION") + print(decomposition) + return decomposition diff --git a/setup.py b/setup.py index 06d07404ab4d..97f7b5a8fcc9 100644 --- a/setup.py +++ b/setup.py @@ -138,6 +138,7 @@ "permutation.kms = qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation", "permutation.basic = qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation", "permutation.acg = qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation", + "permutation.token_swapper = qiskit.transpiler.passes.synthesis.high_level_synthesis:TokenSwapperSynthesisPermutation", ], "qiskit.transpiler.routing": [ "basic = qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager", diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index a387d7b6a0b9..14f1d6a48436 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -18,9 +18,10 @@ import unittest.mock from qiskit.circuit import QuantumCircuit, Operation +from qiskit.circuit.library import PermutationGate, LinearFunction from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager, TranspilerError, CouplingMap -from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin +from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin, HighLevelSynthesisPluginManager from qiskit.transpiler.passes.synthesis.high_level_synthesis import HighLevelSynthesis, HLSConfig from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackend5QV2 @@ -462,5 +463,65 @@ def test_qubits_get_passed_to_plugins(self): pm_use_qubits_true.run(qc) +class TestTokenSwapperPermutationPlugin(QiskitTestCase): + """Tests for the token swapper plugin for synthesizing permutation gates.""" + + def test_token_swapper_in_known_plugin_names(self): + """Test that "token_swapper" is an available synthesis plugin for permutation gates.""" + self.assertIn("token_swapper", HighLevelSynthesisPluginManager().method_names("permutation")) + + def test_abstract_synthesis(self): + """Test abstract synthesis of a permutation gate (either the coupling map or the set + of qubits over which the permutation is defined is not specified). + """ + + # Permutation gate + # 4->0, 6->1, 3->2, 7->3, 1->4, 2->5, 0->6, 5->7 + perm = PermutationGate([4, 6, 3, 7, 1, 2, 0, 5]) + + # Circuit with permutation gate + qc = QuantumCircuit(8) + qc.append(perm, range(8)) + + # Synthesize circuit using the token swapper plugin + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + qc_transpiled = PassManager(HighLevelSynthesis(synthesis_config)).run(qc) + print(qc_transpiled) + + # Construct the expected quantum circuit + # From the description below we can see that + # 0->6, 1->4, 2->5, 3->2, 4->0, 5->2->3->7, 6->0->4->1, 7->3 + qc_expected = QuantumCircuit(8) + qc_expected.swap(2, 5) + qc_expected.swap(0, 6) + qc_expected.swap(2, 3) + qc_expected.swap(0, 4) + qc_expected.swap(1, 4) + qc_expected.swap(3, 7) + print(qc_expected) + + self.assertEqual(qc_transpiled, qc_expected) + + def test_concrete_synthesis(self): + """Test concrete synthesis of a permutation gate (we have both the coupling map and the + set of qubits over which the permutation gate is defined; moreover, the coupling map may + have more qubits than the permutation gate). + """ + + # Permutation gate + perm = PermutationGate([4, 3, 2, 1, 0]) + + # Circuit with permutation gate + qc = QuantumCircuit(10) + qc.append(perm, [0, 2, 4, 6, 8]) + + coupling_map = CouplingMap.from_ring(10) + + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + qc_transpiled = PassManager(HighLevelSynthesis(synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True)).run(qc) + + print(qc_transpiled) + + if __name__ == "__main__": unittest.main() From ace8693bd06c10275010b0d7e80e32411607a3dc Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Thu, 17 Aug 2023 16:19:14 +0300 Subject: [PATCH 02/20] improving plugin and tests --- .../passes/synthesis/high_level_synthesis.py | 70 ++++++----- .../transpiler/test_high_level_synthesis.py | 110 ++++++++++++++++-- 2 files changed, 140 insertions(+), 40 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index bec1bda488e0..f388b9f8e0f6 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -22,7 +22,7 @@ from qiskit.transpiler.target import Target from qiskit.transpiler.coupling import CouplingMap from qiskit.dagcircuit.dagcircuit import DAGCircuit -from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.exceptions import TranspilerError, CouplingError from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper from qiskit.synthesis.clifford import ( @@ -411,56 +411,62 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin): """The permutation synthesis plugin based on the token swapper algorithm. - For more details you can refer to the paper describing the algorithm: - `arXiv:1809.03452 `__. This plugin name is :``permutation.token_swapper`` which can be used as the key on an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`. + In more detail, this plugin is used to synthesize objects of type `PermutationGate`. + When synthesis succeeds, the plugin outputs a quantum circuit consisting only of swap + gates. When synthesis does not succeed, the plugin outputs `None`. + + If either `coupling_map` or `qubits` is None, then the synthesized circuit + is not required to adhere to connectivity constraints, as is the case + when the synthesis is done before layout/routing. + + On the other hand, if both `coupling_map` and `qubits` are specified, the synthesized + circuit is supposed to adhere to connectivity constraint. At the moment, the plugin + only works when `qubits` represents a connected subset of `coupling_map` (if this is + not the case, the plugin outputs `None`). + The plugin supports the following plugin-specific options: * trials: The number of trials for the token swapper to perform the mapping. The circuit with the smallest number of SWAPs is returned. + For more details on the token swapper algorithm, see to the paper: + `arXiv:1809.03452 `__. + """ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options): """Run synthesis for the given Permutation.""" - print(f"Running token_swapper plugin with {coupling_map = }, {qubits = }, {options = }") - pattern = high_level_object.pattern trials = options.get("trials", 5) - # The plugin considers two different cases: if either the coupling map - # or the set of qubits over which the permutation is defined is None, then it - # performs the abstract synthesis; otherwise it performs the concrete synthesis. + pattern_as_dict = {j: i for i, j in enumerate(pattern)} if coupling_map is None or qubits is None: - pattern_as_dict = {j: i for i, j in enumerate(pattern)} + # The abstract synthesis uses a fully connected coupling map, allowing + # arbitrary connections between qubits used_coupling_map = CouplingMap.from_full(len(pattern)) - graph = rx.PyGraph() - graph.extend_from_edge_list(list(used_coupling_map.get_edges())) - swapper = ApproximateTokenSwapper(graph, seed=1) - out = list(swapper.map(pattern_as_dict, trials)) - decomposition = QuantumCircuit(len(graph.node_indices())) - for swap in out: - decomposition.swap(*swap) else: - if len(pattern) != len(qubits): - raise TranspilerError( - "The permutation pattern and the set of qubits over which the " - "permutation is defined have different lengths" - ) - - pattern_as_dict = {qubits[j]: qubits[i] for i, j in enumerate(pattern)} - graph = rx.PyGraph() - graph.extend_from_edge_list(list(coupling_map.get_edges())) - swapper = ApproximateTokenSwapper(graph, seed=1) - out = list(swapper.map(pattern_as_dict, trials)) - decomposition = QuantumCircuit(len(graph.node_indices())) - for swap in out: - decomposition.swap(*swap) - print(f"GOT DECOMPOSITION") - print(decomposition) + # The concrete synthesis uses the coupling map restricted to the set of + # qubits over which the permutation gate is defined. Currently, we require + # this set to be connected (otherwise, replacing the node in the DAGCircuit + # that defines this PermutationGate by the DAG corresponding to the constructed + # decomposition becomes problematic); note that the method `reduce` raises an + # error if the reduced coupling map is not connected. + try: + used_coupling_map = coupling_map.reduce(qubits) + except CouplingError: + return None + + graph = rx.PyGraph() + graph.extend_from_edge_list(list(used_coupling_map.get_edges())) + swapper = ApproximateTokenSwapper(graph, seed=1) + out = list(swapper.map(pattern_as_dict, trials)) + decomposition = QuantumCircuit(len(graph.node_indices())) + for swap in out: + decomposition.swap(*swap) return decomposition diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index 14f1d6a48436..fb972f80d8e0 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -13,17 +13,20 @@ """ Tests the interface for HighLevelSynthesis transpiler pass. """ - - +import itertools import unittest.mock from qiskit.circuit import QuantumCircuit, Operation from qiskit.circuit.library import PermutationGate, LinearFunction from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager, TranspilerError, CouplingMap -from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin, HighLevelSynthesisPluginManager +from qiskit.transpiler.passes.synthesis.plugin import ( + HighLevelSynthesisPlugin, + HighLevelSynthesisPluginManager, +) from qiskit.transpiler.passes.synthesis.high_level_synthesis import HighLevelSynthesis, HLSConfig from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackend5QV2 +from qiskit.quantum_info import Operator # In what follows, we create two simple operations OpA and OpB, that potentially mimic @@ -468,7 +471,9 @@ class TestTokenSwapperPermutationPlugin(QiskitTestCase): def test_token_swapper_in_known_plugin_names(self): """Test that "token_swapper" is an available synthesis plugin for permutation gates.""" - self.assertIn("token_swapper", HighLevelSynthesisPluginManager().method_names("permutation")) + self.assertIn( + "token_swapper", HighLevelSynthesisPluginManager().method_names("permutation") + ) def test_abstract_synthesis(self): """Test abstract synthesis of a permutation gate (either the coupling map or the set @@ -486,7 +491,6 @@ def test_abstract_synthesis(self): # Synthesize circuit using the token swapper plugin synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) qc_transpiled = PassManager(HighLevelSynthesis(synthesis_config)).run(qc) - print(qc_transpiled) # Construct the expected quantum circuit # From the description below we can see that @@ -498,7 +502,6 @@ def test_abstract_synthesis(self): qc_expected.swap(0, 4) qc_expected.swap(1, 4) qc_expected.swap(3, 7) - print(qc_expected) self.assertEqual(qc_transpiled, qc_expected) @@ -508,6 +511,34 @@ def test_concrete_synthesis(self): have more qubits than the permutation gate). """ + # Permutation gate + perm = PermutationGate([0, 1, 4, 3, 2]) + + # Circuit with permutation gate + qc = QuantumCircuit(8) + qc.append(perm, [3, 4, 5, 6, 7]) + + coupling_map = CouplingMap.from_ring(8) + + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + qc_transpiled = PassManager( + HighLevelSynthesis( + synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True + ) + ).run(qc) + + qc_expected = QuantumCircuit(8) + qc_expected.swap(6, 7) + qc_expected.swap(5, 6) + qc_expected.swap(6, 7) + self.assertEqual(qc_transpiled, qc_expected) + + def test_concrete_synthesis_over_disconnected_qubits(self): + """Test concrete synthesis of a permutation gate over a disconnected set of qubits. + In this case the plugin should return `None` and `HighLevelSynthesis` + should not change the original circuit. + """ + # Permutation gate perm = PermutationGate([4, 3, 2, 1, 0]) @@ -518,9 +549,72 @@ def test_concrete_synthesis(self): coupling_map = CouplingMap.from_ring(10) synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) - qc_transpiled = PassManager(HighLevelSynthesis(synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True)).run(qc) + qc_transpiled = PassManager( + HighLevelSynthesis( + synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True + ) + ).run(qc) + self.assertEqual(qc_transpiled, qc) + + def test_abstract_synthesis_all_permutations(self): + """Test abstract synthesis of permutation gates, varying permutation gate patterns.""" + + edges = [(0, 1), (1, 0), (1, 2), (2, 1), (1, 3), (3, 1), (3, 4), (4, 3)] + + coupling_map = CouplingMap() + for i in range(5): + coupling_map.add_physical_qubit(i) + for edge in edges: + coupling_map.add_edge(*edge) + + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + pm = PassManager( + HighLevelSynthesis( + synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=False + ) + ) + + for pattern in itertools.permutations(range(4)): + qc = QuantumCircuit(5) + qc.append(PermutationGate(pattern), [2, 0, 3, 1]) + self.assertIn("permutation", qc.count_ops()) + + qc_transpiled = pm.run(qc) + self.assertNotIn("permutation", qc_transpiled.count_ops()) + + self.assertEqual(Operator(qc), Operator(qc_transpiled)) + + def test_concrete_synthesis_all_permutations(self): + """Test concrete synthesis of permutation gates, varying permutation gate patterns.""" + + edges = [(0, 1), (1, 0), (1, 2), (2, 1), (1, 3), (3, 1), (3, 4), (4, 3)] + + coupling_map = CouplingMap() + for i in range(5): + coupling_map.add_physical_qubit(i) + for edge in edges: + coupling_map.add_edge(*edge) + + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + pm = PassManager( + HighLevelSynthesis( + synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True + ) + ) + + for pattern in itertools.permutations(range(4)): + + qc = QuantumCircuit(5) + qc.append(PermutationGate(pattern), [2, 0, 3, 1]) + self.assertIn("permutation", qc.count_ops()) + + qc_transpiled = pm.run(qc) + self.assertNotIn("permutation", qc_transpiled.count_ops()) + self.assertEqual(Operator(qc), Operator(qc_transpiled)) - print(qc_transpiled) + for inst in qc_transpiled: + qubits = tuple([q.index for q in inst.qubits]) + self.assertIn(qubits, edges) if __name__ == "__main__": From 3f948d29bcd91d99a79a4f14c0e3e37f74f44337 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Fri, 18 Aug 2023 09:46:35 +0300 Subject: [PATCH 03/20] pylint fixes --- test/python/transpiler/test_high_level_synthesis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index fb972f80d8e0..5c44d5a977bb 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -17,7 +17,7 @@ import unittest.mock from qiskit.circuit import QuantumCircuit, Operation -from qiskit.circuit.library import PermutationGate, LinearFunction +from qiskit.circuit.library import PermutationGate from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager, TranspilerError, CouplingMap from qiskit.transpiler.passes.synthesis.plugin import ( @@ -613,7 +613,7 @@ def test_concrete_synthesis_all_permutations(self): self.assertEqual(Operator(qc), Operator(qc_transpiled)) for inst in qc_transpiled: - qubits = tuple([q.index for q in inst.qubits]) + qubits = tuple(q.index for q in inst.qubits) self.assertIn(qubits, edges) From 74ccc4b652e24f3bafde2fad1f0b3b5a23167da0 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Fri, 18 Aug 2023 10:00:28 +0300 Subject: [PATCH 04/20] exposing seed and parallel_threshold --- .../passes/synthesis/high_level_synthesis.py | 11 ++++++++--- test/python/transpiler/test_high_level_synthesis.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index f388b9f8e0f6..9dd1b6890b9b 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -432,6 +432,9 @@ class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin): * trials: The number of trials for the token swapper to perform the mapping. The circuit with the smallest number of SWAPs is returned. + * seed: The argument to the token swapper specifying the seed for random trials. + * parallel_threshold: The argument to the token swapper specifying the number of nodes + in the graph beyond which the algorithm will use parallel processing. For more details on the token swapper algorithm, see to the paper: `arXiv:1809.03452 `__. @@ -441,9 +444,11 @@ class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin): def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options): """Run synthesis for the given Permutation.""" - pattern = high_level_object.pattern trials = options.get("trials", 5) + seed = options.get("seed", 0) + parallel_threshold = options.get("parallel_threshold", 50) + pattern = high_level_object.pattern pattern_as_dict = {j: i for i, j in enumerate(pattern)} if coupling_map is None or qubits is None: @@ -464,8 +469,8 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** graph = rx.PyGraph() graph.extend_from_edge_list(list(used_coupling_map.get_edges())) - swapper = ApproximateTokenSwapper(graph, seed=1) - out = list(swapper.map(pattern_as_dict, trials)) + swapper = ApproximateTokenSwapper(graph, seed=seed) + out = swapper.map(pattern_as_dict, trials, parallel_threshold=parallel_threshold) decomposition = QuantumCircuit(len(graph.node_indices())) for swap in out: decomposition.swap(*swap) diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index 5c44d5a977bb..30a1d7d3c5b8 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -489,7 +489,7 @@ def test_abstract_synthesis(self): qc.append(perm, range(8)) # Synthesize circuit using the token swapper plugin - synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10, "seed": 1})]) qc_transpiled = PassManager(HighLevelSynthesis(synthesis_config)).run(qc) # Construct the expected quantum circuit From a689b02ebc9288ee9a15654f2728405c0bf1d807 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Fri, 18 Aug 2023 11:06:40 +0300 Subject: [PATCH 05/20] release notes --- ...per-synthesis-plugin-4ed5009f5f21519d.yaml | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml diff --git a/releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml b/releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml new file mode 100644 index 000000000000..95a5f5816b95 --- /dev/null +++ b/releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml @@ -0,0 +1,41 @@ +--- +features: + - | + Added a new :class:`.HighLevelSynthesisPlugin` for :class:`.PermutationGate` + objects based on Qiskit's token swapper algorithm. To use this plugin, + specify ``token_swapper`` when defining high-level-synthesis config. + + This synthesis plugin is able to run before or after the layout is set. + When synthesis succeeds, the plugin outputs a quantum circuit consisting only of + swap gates. When synthesis does not succeed, the plugin outputs `None`. + + The following code illustrates how the new plugin can be run:: + + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import PermutationGate + from qiskit.transpiler import PassManager, CouplingMap + from qiskit.transpiler.passes.synthesis.high_level_synthesis import HighLevelSynthesis, HLSConfig + + # This creates a circuit with a permutation gate. + qc = QuantumCircuit(8) + perm_gate = PermutationGate([0, 1, 4, 3, 2]) + qc.append(perm_gate, [3, 4, 5, 6, 7]) + + # This defines the coupling map. + coupling_map = CouplingMap.from_ring(8) + + # This high-level-synthesis config specifies that we want to use + # the "token_swapper" plugin for synthesizing permutation gates, + # with the option to use 10 trials. + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + + # This creates the pass manager that runs high-level-synthesis on our circuit. + # The option use_qubit_indices=True indicates that synthesis run after the layout is set, + # and hence should preserve the specified coupling map. + pm = PassManager( + HighLevelSynthesis( + synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True + ) + ) + + qc_transpiled = pm.run(qc) From e725b37dbad840ad268abcfeadad8de94b490e09 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Fri, 18 Aug 2023 11:28:40 +0300 Subject: [PATCH 06/20] clarification comment --- qiskit/transpiler/passes/synthesis/high_level_synthesis.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 9dd1b6890b9b..13a83c4895dd 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -451,9 +451,11 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** pattern = high_level_object.pattern pattern_as_dict = {j: i for i, j in enumerate(pattern)} + # When the plugin is called from the HighLevelSynthesis transpiler pass, + # the coupling map already takes target into account. if coupling_map is None or qubits is None: # The abstract synthesis uses a fully connected coupling map, allowing - # arbitrary connections between qubits + # arbitrary connections between qubits. used_coupling_map = CouplingMap.from_full(len(pattern)) else: # The concrete synthesis uses the coupling map restricted to the set of From a4b97c79dcd77bdbc3573b61f03a8ddb245f8ec4 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 20 Sep 2023 14:25:46 +0300 Subject: [PATCH 07/20] improved support for disconnected coupling maps --- .../passes/synthesis/high_level_synthesis.py | 42 +++++++++++-------- .../transpiler/test_high_level_synthesis.py | 41 +++++++++++++++--- 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 167ef0e83ed5..6cc5a8f6a3a3 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -428,9 +428,10 @@ class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin): when the synthesis is done before layout/routing. On the other hand, if both `coupling_map` and `qubits` are specified, the synthesized - circuit is supposed to adhere to connectivity constraint. At the moment, the plugin - only works when `qubits` represents a connected subset of `coupling_map` (if this is - not the case, the plugin outputs `None`). + circuit is supposed to adhere to connectivity constraints. At the moment, the + plugin only creates swap gates between qubits in `qubits`, i.e. it does not use + any other qubits in the coupling map (if such synthesis is not possible, the + plugin outputs `None`). The plugin supports the following plugin-specific options: @@ -463,21 +464,28 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** used_coupling_map = CouplingMap.from_full(len(pattern)) else: # The concrete synthesis uses the coupling map restricted to the set of - # qubits over which the permutation gate is defined. Currently, we require - # this set to be connected (otherwise, replacing the node in the DAGCircuit - # that defines this PermutationGate by the DAG corresponding to the constructed - # decomposition becomes problematic); note that the method `reduce` raises an - # error if the reduced coupling map is not connected. - try: - used_coupling_map = coupling_map.reduce(qubits) - except CouplingError: - return None + # qubits over which the permutation gate is defined. If we allow using other + # qubits in the coupling map, replacing the node in the DAGCircuit that + # defines this PermutationGate by the DAG corresponding to the constructed + # decomposition becomes problematic. Note that we allow the reduced + # coupling map to be disconnected. + used_coupling_map = coupling_map.reduce(qubits, check_if_connected=False) graph = rx.PyGraph() graph.extend_from_edge_list(list(used_coupling_map.get_edges())) swapper = ApproximateTokenSwapper(graph, seed=seed) - out = swapper.map(pattern_as_dict, trials, parallel_threshold=parallel_threshold) - decomposition = QuantumCircuit(len(graph.node_indices())) - for swap in out: - decomposition.swap(*swap) - return decomposition + + try: + swapper_result = swapper.map( + pattern_as_dict, trials, parallel_threshold=parallel_threshold + ) + except rx.InvalidMapping: + swapper_result = None + + if swapper_result is not None: + decomposition = QuantumCircuit(len(graph.node_indices())) + for swap in swapper_result: + decomposition.swap(*swap) + return decomposition + + return None diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index 30a1d7d3c5b8..fc8044f64554 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -534,17 +534,16 @@ def test_concrete_synthesis(self): self.assertEqual(qc_transpiled, qc_expected) def test_concrete_synthesis_over_disconnected_qubits(self): - """Test concrete synthesis of a permutation gate over a disconnected set of qubits. - In this case the plugin should return `None` and `HighLevelSynthesis` - should not change the original circuit. + """Test concrete synthesis of a permutation gate over a disconnected set of qubits, + when synthesis is possible. """ # Permutation gate - perm = PermutationGate([4, 3, 2, 1, 0]) + perm = PermutationGate([1, 0, 3, 2]) # Circuit with permutation gate qc = QuantumCircuit(10) - qc.append(perm, [0, 2, 4, 6, 8]) + qc.append(perm, [3, 2, 7, 8]) coupling_map = CouplingMap.from_ring(10) @@ -554,6 +553,38 @@ def test_concrete_synthesis_over_disconnected_qubits(self): synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True ) ).run(qc) + + qc_expected = QuantumCircuit(10) + qc_expected.swap(2, 3) + qc_expected.swap(7, 8) + + # Even though the permutation is over a disconnected set of qubits, the synthesis + # is possible. + self.assertEqual(qc_transpiled, qc_expected) + + def test_concrete_synthesis_is_not_possible(self): + """Test concrete synthesis of a permutation gate over a disconnected set of qubits, + when synthesis is not possible. + """ + + # Permutation gate + perm = PermutationGate([0, 2, 1, 3]) + + # Circuit with permutation gate + qc = QuantumCircuit(10) + qc.append(perm, [3, 2, 7, 8]) + + coupling_map = CouplingMap.from_ring(10) + + synthesis_config = HLSConfig(permutation=[("token_swapper", {"trials": 10})]) + qc_transpiled = PassManager( + HighLevelSynthesis( + synthesis_config, coupling_map=coupling_map, target=None, use_qubit_indices=True + ) + ).run(qc) + + # The synthesis is not possible. In this case the plugin should return `None` + # and `HighLevelSynthesis` should not change the original circuit. self.assertEqual(qc_transpiled, qc) def test_abstract_synthesis_all_permutations(self): From 2eb7075140b7429707518e2b251b9b31f18d2d2f Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 20 Sep 2023 18:59:56 +0300 Subject: [PATCH 08/20] unused import --- qiskit/transpiler/passes/synthesis/high_level_synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 6cc5a8f6a3a3..6ad5dd9a8f14 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -22,7 +22,7 @@ from qiskit.transpiler.target import Target from qiskit.transpiler.coupling import CouplingMap from qiskit.dagcircuit.dagcircuit import DAGCircuit -from qiskit.transpiler.exceptions import TranspilerError, CouplingError +from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper from qiskit.synthesis.clifford import ( From 8677dcb1d58582b7a7370e9558cec1b7d45bd9d4 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Thu, 5 Oct 2023 08:37:05 +0300 Subject: [PATCH 09/20] fix arxiv reference --- qiskit/transpiler/passes/synthesis/high_level_synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 6ad5dd9a8f14..d6730c2a375d 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -442,7 +442,7 @@ class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin): in the graph beyond which the algorithm will use parallel processing. For more details on the token swapper algorithm, see to the paper: - `arXiv:1809.03452 `__. + `arXiv:1902.09102 `__. """ From 9dd1f85a6cc428d16a6dbcc60e8720b23e4b3eee Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 18 Oct 2023 13:32:21 +0300 Subject: [PATCH 10/20] minor fix --- qiskit/transpiler/passes/synthesis/high_level_synthesis.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index d4f5c71cfcac..600220ceca80 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -610,8 +610,7 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** # coupling map to be disconnected. used_coupling_map = coupling_map.reduce(qubits, check_if_connected=False) - graph = rx.PyGraph() - graph.extend_from_edge_list(list(used_coupling_map.get_edges())) + graph = used_coupling_map.graph.to_undirected() swapper = ApproximateTokenSwapper(graph, seed=seed) try: From 73e80a7303e20d8ac111ef1b0e5d3d4ccd9fdd12 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 18 Oct 2023 13:36:03 +0300 Subject: [PATCH 11/20] fix merge --- test/python/transpiler/test_high_level_synthesis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index c8c14a9be7a5..cd149aa4f3e8 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -654,7 +654,8 @@ def test_concrete_synthesis_all_permutations(self): for inst in qc_transpiled: qubits = tuple(q.index for q in inst.qubits) self.assertIn(qubits, edges) -======= + + class TestHighLevelSynthesisModifiers(QiskitTestCase): """Tests for high-level-synthesis pass.""" From 3389c31bc942472497402839b7f5620274c0748d Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Wed, 18 Oct 2023 13:57:34 +0300 Subject: [PATCH 12/20] more merge fixes --- qiskit/transpiler/passes/synthesis/high_level_synthesis.py | 4 ++-- test/python/transpiler/test_high_level_synthesis.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 600220ceca80..e905818b6e77 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -13,10 +13,10 @@ """Synthesize higher-level objects.""" -import rustworkx as rx - from typing import Optional, Union, List, Tuple +import rustworkx as rx + from qiskit.circuit.operation import Operation from qiskit.converters import circuit_to_dag diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index cd149aa4f3e8..a30d75039557 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -34,7 +34,6 @@ ) from qiskit.quantum_info import Operator from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackend5QV2 -from qiskit.quantum_info import Operator # In what follows, we create two simple operations OpA and OpB, that potentially mimic From e111e63043cb66c418480d534bd7314710628d89 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Mon, 23 Oct 2023 09:51:59 +0300 Subject: [PATCH 13/20] fixing imports --- test/python/transpiler/test_high_level_synthesis.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index 1b3cf198232d..167dfda3c921 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -41,7 +41,6 @@ from qiskit.circuit.library.generalized_gates import LinearFunction from qiskit.quantum_info import Clifford from qiskit.test import QiskitTestCase -from qiskit.transpiler import PassManager, TranspilerError, CouplingMap from qiskit.transpiler.passes.synthesis.plugin import ( HighLevelSynthesisPlugin, HighLevelSynthesisPluginManager, @@ -51,7 +50,6 @@ from qiskit.converters import dag_to_circuit, circuit_to_dag, circuit_to_instruction from qiskit.transpiler import PassManager, TranspilerError, CouplingMap, Target from qiskit.transpiler.passes.basis import BasisTranslator -from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin from qiskit.transpiler.passes.synthesis.high_level_synthesis import HighLevelSynthesis, HLSConfig from qiskit.circuit.annotated_operation import ( AnnotatedOperation, From e9b476620202525463b19072dc7885cd46441f20 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Thu, 25 Jan 2024 13:53:06 +0200 Subject: [PATCH 14/20] updating toml file --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 137f85530a2b..06db15d3cc24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,6 +88,7 @@ sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevS "permutation.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation" "permutation.basic" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation" "permutation.acg" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation" +"permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:TokenSwapperSynthesisPermutation" [project.entry-points."qiskit.transpiler.init"] default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager" From 24ae130f8faa06a99144d345f98497e5c7109dd4 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Thu, 25 Jan 2024 14:37:01 +0200 Subject: [PATCH 15/20] additional fixes --- qiskit/transpiler/passes/synthesis/high_level_synthesis.py | 2 +- test/python/transpiler/test_high_level_synthesis.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 03f734233915..130cb97b7c91 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -300,7 +300,7 @@ def _recursively_handle_op( # Try to apply plugin mechanism decomposition = self._synthesize_op_using_plugins(op, qubits) - if decomposition: + if decomposition is not None: return decomposition, True # Handle annotated operations diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index 167dfda3c921..18fcaeeb8f22 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -681,7 +681,7 @@ def test_concrete_synthesis_all_permutations(self): self.assertEqual(Operator(qc), Operator(qc_transpiled)) for inst in qc_transpiled: - qubits = tuple(q.index for q in inst.qubits) + qubits = tuple(q._index for q in inst.qubits) self.assertIn(qubits, edges) From 8e65d74f48d3a880146ce2f8b3c1bb0b7a8fcd93 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Thu, 25 Jan 2024 17:47:11 +0200 Subject: [PATCH 16/20] better way to find the position in the circuit --- test/python/transpiler/test_high_level_synthesis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/transpiler/test_high_level_synthesis.py b/test/python/transpiler/test_high_level_synthesis.py index 18fcaeeb8f22..ea5f5ee7e4d6 100644 --- a/test/python/transpiler/test_high_level_synthesis.py +++ b/test/python/transpiler/test_high_level_synthesis.py @@ -681,7 +681,7 @@ def test_concrete_synthesis_all_permutations(self): self.assertEqual(Operator(qc), Operator(qc_transpiled)) for inst in qc_transpiled: - qubits = tuple(q._index for q in inst.qubits) + qubits = tuple(qc_transpiled.find_bit(q).index for q in inst.qubits) self.assertIn(qubits, edges) From 5e9ff47df419ab3d32489ed393c00cae85055bab Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Tue, 30 Jan 2024 15:17:13 +0200 Subject: [PATCH 17/20] bump rustworkx version to 0.14.0 --- .../add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml | 3 +++ requirements.txt | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml b/releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml index 95a5f5816b95..00a69a46c1af 100644 --- a/releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml +++ b/releasenotes/notes/add-token-swapper-synthesis-plugin-4ed5009f5f21519d.yaml @@ -1,4 +1,7 @@ --- +upgrade: + - | + Qiskit 1.0 now requires version 0.14.0 of ``rustworkx``. features: - | Added a new :class:`.HighLevelSynthesisPlugin` for :class:`.PermutationGate` diff --git a/requirements.txt b/requirements.txt index 5c3dd9c879b6..e34fdfe36b2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -rustworkx>=0.13.0 +rustworkx>=0.14.0 numpy>=1.17,<2 scipy>=1.5 sympy>=1.3 From 7886221c62874c469e200edfbf674d9a93cbd3f2 Mon Sep 17 00:00:00 2001 From: AlexanderIvrii Date: Tue, 30 Jan 2024 17:13:59 +0200 Subject: [PATCH 18/20] doc and autosummary improvements --- qiskit/transpiler/passes/synthesis/plugin.py | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/qiskit/transpiler/passes/synthesis/plugin.py b/qiskit/transpiler/passes/synthesis/plugin.py index 69505c127438..44f566fc253d 100644 --- a/qiskit/transpiler/passes/synthesis/plugin.py +++ b/qiskit/transpiler/passes/synthesis/plugin.py @@ -284,6 +284,36 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** will return a list of all the installed Clifford synthesis plugins. +Available Plugins +----------------- + +High-level synthesis plugins that are directly available in Qiskit include plugins +for synthesizing :class:`.Clifford` objects, :class:`.LinearFunction` objects, and +:class:`.PermutationGate` objects. +Some of these plugins implicitly target all-to-all connectivity. This is not a +practical limitation since +:class:`~qiskit.transpiler.passes.synthesis.high_level_synthesis.HighLevelSynthesis` +typically runs before layout and routing, which will ensure that the final circuit +adheres to the device connectivity by inserting additional SWAP gates. A good example +is the permutation synthesis plugin ``ACGSynthesisPermutation`` which can synthesize +any permutation with at most 2 layers of SWAP gates. +On the other hand, some plugins implicitly target linear connectivity. +Typically, the synthesizing circuits have larger depth and the number of gates, +however no additional SWAP gates would be inserted if the following layout pass chose a +consecutive line of qubits inside the topology of the device. A good example of this is +the permutation synthesis plugin ``KMSSynthesisPermutation`` which can synthesize any +permutation of ``n`` qubits in depth ``n``. Typically, it is difficult to know in advance +which of the two approaches: synthesizing circuits for all-to-all connectivity and +inserting SWAP gates vs. synthesizing circuits for linear connectivity and inserting less +or no SWAP gates lead a better final circuit, so it likely makes sense to try both and +see which gives better results. +Finally, some plugins can target a given connectivity, and hence should be run after the +layout is set. In this case the synthesized circuit automatically adheres to +the topology of the device. A good example of this is the permutation synthesis plugin +``TokenSwapperSynthesisPermutation`` which is able to synthesize arbitrary permutations +with respect to arbitrary coupling maps. +For more detail, please refer to description of each individual plugin. + Plugin API ========== @@ -307,6 +337,25 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** HighLevelSynthesisPluginManager high_level_synthesis_plugin_names +Available Plugins +----------------- + +.. autosummary:: + :toctree: ../stubs/ + + DefaultSynthesisClifford + AGSynthesisClifford + BMSynthesisClifford + GreedySynthesisClifford + LayerSynthesisClifford + LayerLnnSynthesisClifford + DefaultSynthesisLinearFunction + KMSSynthesisLinearFunction + PMHSynthesisLinearFunction + KMSSynthesisPermutation + BasicSynthesisPermutation + ACGSynthesisPermutation + TokenSwapperSynthesisPermutation """ import abc From c3bd53b42ccee78abe7e78f8b39178ad69981f44 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 30 Jan 2024 12:10:43 -0500 Subject: [PATCH 19/20] Update plugin docs configuration --- qiskit/transpiler/passes/synthesis/plugin.py | 65 +++++++++++++++----- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/plugin.py b/qiskit/transpiler/passes/synthesis/plugin.py index 44f566fc253d..87eabe4e9e77 100644 --- a/qiskit/transpiler/passes/synthesis/plugin.py +++ b/qiskit/transpiler/passes/synthesis/plugin.py @@ -338,24 +338,61 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** high_level_synthesis_plugin_names Available Plugins ------------------ +================= + +The below are the plugin classes which includes documentation on the various +methods that are implemented by each plugin and the corresponding plugin name +to use the method (this is not the class name). These classes should not be +used directly, but instead should be used through the plugin interface documented +above. The classes are listed here to find the documentation for each of the included +plugins. + +Unitary Synthesis Plugins +------------------------- + +.. autosummary:: + :toctree: ../stubs/ + + ~qiskit.transpiler.passes.synthesis.unitary_synthesis.DefaultUnitarySynthesis + ~qiskit.transpiler.passes.synthesis.aqc_plugin.AGSynthesisClifford + ~qiskit.transpiler.passes.synthesis.aqc_plugin.ACGSynthesisPermutation + ~qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis.SolovayKitaevSynthesis + + +High Level Synthesis +-------------------- + +Clifford Synthesis +'''''''''''''''''' + +.. autosummary:: + :toctree: ../stubs/ + + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.DefaultSynthesisClifford + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.BMSynthesisClifford + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.GreedySynthesisClifford + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.LayerSynthesisClifford + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.LayerLnnSynthesisClifford + +Linear Function Synthesis +''''''''''''''''''''''''' + +.. autosummary:: + :toctree: ../stubs/ + + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.DefaultSynthesisLinearFunction + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.KMSSynthesisLinearFunction + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.PMHSynthesisLinearFunction + +Permutation Synthesis +''''''''''''''''''''' .. autosummary:: :toctree: ../stubs/ - DefaultSynthesisClifford - AGSynthesisClifford - BMSynthesisClifford - GreedySynthesisClifford - LayerSynthesisClifford - LayerLnnSynthesisClifford - DefaultSynthesisLinearFunction - KMSSynthesisLinearFunction - PMHSynthesisLinearFunction - KMSSynthesisPermutation - BasicSynthesisPermutation - ACGSynthesisPermutation - TokenSwapperSynthesisPermutation + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.KMSSynthesisPermutation + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.BasicSynthesisPermutation + ~qiskit.transpiler.passes.synthesis.high_level_synthesis.TokenSwapperSynthesisPermutation """ import abc From b617f6f839b3eb754cd6c684ce45e285978a89d8 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 30 Jan 2024 13:03:44 -0500 Subject: [PATCH 20/20] Remove autosummary for available plugins list This commit removes the autosummary directives for building the documentation for the plugin classes. In the interest of time and combining it with the existing aqc docs we'll do this in a follow up for 1.0.0 after 1.0.0rc1 has been tagged. --- qiskit/transpiler/passes/synthesis/plugin.py | 57 -------------------- 1 file changed, 57 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/plugin.py b/qiskit/transpiler/passes/synthesis/plugin.py index 87eabe4e9e77..f5024e4ef419 100644 --- a/qiskit/transpiler/passes/synthesis/plugin.py +++ b/qiskit/transpiler/passes/synthesis/plugin.py @@ -336,63 +336,6 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** HighLevelSynthesisPlugin HighLevelSynthesisPluginManager high_level_synthesis_plugin_names - -Available Plugins -================= - -The below are the plugin classes which includes documentation on the various -methods that are implemented by each plugin and the corresponding plugin name -to use the method (this is not the class name). These classes should not be -used directly, but instead should be used through the plugin interface documented -above. The classes are listed here to find the documentation for each of the included -plugins. - -Unitary Synthesis Plugins -------------------------- - -.. autosummary:: - :toctree: ../stubs/ - - ~qiskit.transpiler.passes.synthesis.unitary_synthesis.DefaultUnitarySynthesis - ~qiskit.transpiler.passes.synthesis.aqc_plugin.AGSynthesisClifford - ~qiskit.transpiler.passes.synthesis.aqc_plugin.ACGSynthesisPermutation - ~qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis.SolovayKitaevSynthesis - - -High Level Synthesis --------------------- - -Clifford Synthesis -'''''''''''''''''' - -.. autosummary:: - :toctree: ../stubs/ - - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.DefaultSynthesisClifford - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.BMSynthesisClifford - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.GreedySynthesisClifford - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.LayerSynthesisClifford - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.LayerLnnSynthesisClifford - -Linear Function Synthesis -''''''''''''''''''''''''' - -.. autosummary:: - :toctree: ../stubs/ - - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.DefaultSynthesisLinearFunction - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.KMSSynthesisLinearFunction - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.PMHSynthesisLinearFunction - -Permutation Synthesis -''''''''''''''''''''' - -.. autosummary:: - :toctree: ../stubs/ - - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.KMSSynthesisPermutation - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.BasicSynthesisPermutation - ~qiskit.transpiler.passes.synthesis.high_level_synthesis.TokenSwapperSynthesisPermutation """ import abc