Skip to content

Commit

Permalink
VF2 Layout : The layout allocation as a subgraph isomorphism problem (#…
Browse files Browse the repository at this point in the history
…6620)

* VF2 initial commit

* Update qiskit/transpiler/passes/layout/vf2_layout.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* test

* direction

* test

* some tests

* done with test

* reno

* Update releasenotes/notes/vf2layout-4cea88087c355769.yaml

* induced subraph test

* Update qiskit/transpiler/passes/layout/vf2_layout.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* adapt tests

* dynamic id_order selection

* lint

* black

* remove dynamic id_order

* iter instead of dict

* iter instead of dict

* remove id_order arg

* Update qiskit/transpiler/passes/layout/vf2_layout.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Update releasenotes/notes/vf2layout-4cea88087c355769.yaml

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Update releasenotes/notes/vf2layout-4cea88087c355769.yaml

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Update qiskit/transpiler/passes/layout/vf2_layout.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Update qiskit/transpiler/passes/layout/vf2_layout.py

Co-authored-by: georgios-ts <45130028+georgios-ts@users.noreply.github.com>

* vf2_mapping

* Update test/python/transpiler/test_vf2_layout.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Update test/python/transpiler/test_vf2_layout.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* from_hexagonal_lattice

* dag.two_qubit_ops bites again

* dag.two_qubit_ops bites again

* Update vf2layout docstrings

This commit updates the vf2layout docstrings to include details
on how the pass works and the user expectations when using it.
Previously this information was set in the module docstring which
doesn't get rendered in the compiled documentation. This commit
just moves these details to the class docstring and expands on it slightly.

* Fix lint

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
Co-authored-by: georgios-ts <45130028+georgios-ts@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 2, 2021
1 parent 4cf3491 commit bc77630
Show file tree
Hide file tree
Showing 5 changed files with 447 additions and 0 deletions.
2 changes: 2 additions & 0 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
NoiseAdaptiveLayout
SabreLayout
CSPLayout
VF2Layout
ApplyLayout
Layout2qDistance
EnlargeWithAncilla
Expand Down Expand Up @@ -151,6 +152,7 @@
from .layout import NoiseAdaptiveLayout
from .layout import SabreLayout
from .layout import CSPLayout
from .layout import VF2Layout
from .layout import ApplyLayout
from .layout import Layout2qDistance
from .layout import EnlargeWithAncilla
Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/layout/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .noise_adaptive_layout import NoiseAdaptiveLayout
from .sabre_layout import SabreLayout
from .csp_layout import CSPLayout
from .vf2_layout import VF2Layout
from .apply_layout import ApplyLayout
from .layout_2q_distance import Layout2qDistance
from .enlarge_with_ancilla import EnlargeWithAncilla
Expand Down
97 changes: 97 additions & 0 deletions qiskit/transpiler/passes/layout/vf2_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# 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.

"""VF2Layout pass to find a layout using subgraph isomorphism"""
import random
from retworkx import PyGraph, PyDiGraph, vf2_mapping
from qiskit.transpiler.layout import Layout
from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.transpiler.exceptions import TranspilerError


class VF2Layout(AnalysisPass):
"""A pass for choosing a Layout of a circuit onto a Coupling graph, as a
a subgraph isomorphism problem, solved by VF2++.
If a solution is found that means there is a "perfect layout" and that no
further swap mapping or routing is needed. If a solution is found the layout
will be set in the property set as ``property_set['layout']``. However, if no
solution is found, no ``property_set['layout']`` is set. The stopping reason is
set in ``property_set['VF2Layout_stop_reason']`` in all the cases and will be
one of the following values:
* ``"solution found"``: If a perfect layout was found.
* ``"nonexistent solution"``: If no perfect layout was found.
"""

def __init__(self, coupling_map, strict_direction=False, seed=None):
"""Initialize a ``VF2Layout`` pass instance
Args:
coupling_map (CouplingMap): Directed graph representing a coupling map.
strict_direction (bool): If True, considers the direction of the coupling map.
Default is False.
seed (int): Sets the seed of the PRNG. -1 Means no node shuffling.
"""
super().__init__()
self.coupling_map = coupling_map
self.strict_direction = strict_direction
self.seed = seed

def run(self, dag):
"""run the layout method"""
qubits = dag.qubits
qubit_indices = {qubit: index for index, qubit in enumerate(qubits)}

interactions = []
for node in dag.op_nodes(include_directives=False):
len_args = len(node.qargs)
if len_args == 2:
interactions.append((qubit_indices[node.qargs[0]], qubit_indices[node.qargs[1]]))
if len_args >= 3:
raise TranspilerError(
"VF2Layout only can handle 2-qubit gates or less. Node "
f"{node.name} ({node}) is {len_args}-qubit"
)

if self.strict_direction:
cm_graph = self.coupling_map.graph
im_graph = PyDiGraph(multigraph=False)
else:
cm_graph = self.coupling_map.graph.to_undirected()
im_graph = PyGraph(multigraph=False)

cm_nodes = list(cm_graph.node_indexes())
if self.seed != -1:
random.Random(self.seed).shuffle(cm_nodes)
shuffled_cm_graph = type(cm_graph)()
shuffled_cm_graph.add_nodes_from(cm_nodes)
new_edges = [(cm_nodes[edge[0]], cm_nodes[edge[1]]) for edge in cm_graph.edge_list()]
shuffled_cm_graph.add_edges_from_no_data(new_edges)
cm_nodes = [k for k, v in sorted(enumerate(cm_nodes), key=lambda item: item[1])]
cm_graph = shuffled_cm_graph
im_graph.add_nodes_from(range(len(qubits)))
im_graph.add_edges_from_no_data(interactions)

mappings = vf2_mapping(cm_graph, im_graph, subgraph=True, id_order=False, induced=False)
try:
mapping = next(mappings)
stop_reason = "solution found"
layout = Layout({qubits[im_i]: cm_nodes[cm_i] for cm_i, im_i in mapping.items()})
self.property_set["layout"] = layout
for reg in dag.qregs.values():
self.property_set["layout"].add_register(reg)
except StopIteration:
stop_reason = "nonexistent solution"

self.property_set["VF2Layout_stop_reason"] = stop_reason
7 changes: 7 additions & 0 deletions releasenotes/notes/vf2layout-4cea88087c355769.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
A new pass :class:`qiskit.transpiler.passes.VF2Layout` is
introduced. This pass models the layout allocation problem as a subgraph
isomorphism problem. For this, it uses VF2, a state of the art heuristic to find
subgraphs. The pass works very much like :class:`qiskit.transpiler.passes.CSPLayout`.
Loading

0 comments on commit bc77630

Please sign in to comment.