-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Token swapper permutation synthesis plugin #10657
Changes from 2 commits
4181428
ace8693
3f948d2
1d6ee58
5858b3d
74ccc4b
a689b02
e725b37
e3e2c3f
816ac1e
a4b97c7
2eb7075
8677dcb
e15d739
ecd793e
9dd1f85
73e80a7
3389c31
fb8956d
e111e63
ef4e877
e9b4766
24ae130
8e65d74
f06dc65
5e9ff47
7886221
c3bd53b
b617f6f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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.exceptions import TranspilerError, CouplingError | ||
from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper | ||
|
||
from qiskit.synthesis.clifford import ( | ||
synth_clifford_full, | ||
|
@@ -404,3 +407,66 @@ 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. | ||
|
||
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 <https://arxiv.org/abs/1809.03452>`__. | ||
|
||
""" | ||
|
||
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) | ||
|
||
pattern_as_dict = {j: i for i, j in enumerate(pattern)} | ||
|
||
if coupling_map is None or qubits is None: | ||
mtreinish marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# The abstract synthesis uses a fully connected coupling map, allowing | ||
# 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 | ||
# 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be able to handle disconnected subgraphs if we work with the graph object directly. Something like: graph = coupling_map.graph.subgraph(qubits).to_undirected() The only issue with that is I don't think there is any guarantees on the index ordering from for i in coupling_map.graph.node_indices():
coupling_map.graph[i] = i
graph = coupling_map.graph.subgraph(qubits).to_undirected() then you you can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, this is a very interesting suggestion. By experimenting with the approximate token swapper, it seems that if there is a way to fulfill the permutation (on a disconnected subgraph), it will do so; but if there is no such way, it will throw a rather strange error
Question: wrapping the second in the
Would this be a problem? Another question: is it safe to modify the original coupling map: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hah, well that's a bug in rustworkx. A As for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I pushed up a fix for this in rustworkx: Qiskit/rustworkx#971 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really want to support disjoint coupling maps in this and other synthesis plugins. First, as explained in #10657 (comment), using Do I understand correctly that disconnected coupling maps are now allowed in Qiskit? So an alternative would be to add an argument (something like There are a few more minor changes required for @mtreinish, if you agree with the The second problem is that there is still one edge case when
This panics as per
I would be happy to look into this. One additional question is whether we need to wait till the new version of rustworkx is released (including PR 971 and possibly additional fixes) before this PR could be merged? |
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I have exposed both |
||
out = list(swapper.map(pattern_as_dict, trials)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the list cast here necessary? The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
decomposition = QuantumCircuit(len(graph.node_indices())) | ||
for swap in out: | ||
decomposition.swap(*swap) | ||
return decomposition |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add this class somewhere in the toctree to render the docstring here. Right now this class (nor any of the HLS plugins) is not being included in the documentation builds. I was talking to @Cryoris offline about this a bit the other day as there isn't a unified place to document this right now things are spread out a bit too much in the organizational structure. I think for right now if you added a HLS Plugins section to the module docstring in
qiskit/transpiler/passes/synthesis/plugin.py
and added an autosummary for this class that'd be enough. We can refactor the organizational structure in a follow up easily enough.