From 123a0a78f1c9e1c0e7e00399081734c8150d716a Mon Sep 17 00:00:00 2001 From: Ashish Panigrahi Date: Wed, 22 Sep 2021 01:15:55 +0530 Subject: [PATCH] Add `from_heavy_hex()` and `from_heavy_square()` generator methods to CouplingMap (#6959) * add class methods for heavy hex and heavy square graphs * added tests for heavy_hex and heavy_square * fix black formatting * changed variable name to 'distance' * fixed variable name * added bidirectional tests * fixed linting * added release note template * Fix release note * Expand docstring * Fix copy paste errors in docstrings Co-authored-by: Jake Lishman Co-authored-by: Matthew Treinish Co-authored-by: Jake Lishman --- qiskit/transpiler/coupling.py | 50 ++++++ ...-square-coupling-map-29f459b93cd18518.yaml | 27 +++ test/python/transpiler/test_coupling.py | 160 ++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 releasenotes/notes/heavy-hex-heavy-square-coupling-map-29f459b93cd18518.yaml diff --git a/qiskit/transpiler/coupling.py b/qiskit/transpiler/coupling.py index fd6c2f5915f6..a285b3f5817f 100644 --- a/qiskit/transpiler/coupling.py +++ b/qiskit/transpiler/coupling.py @@ -320,6 +320,56 @@ def from_grid(cls, num_rows, num_columns, bidirectional=True): ) return cmap + @classmethod + def from_heavy_hex(cls, distance, bidirectional=True): + """Return a heavy hexagon graph coupling map + + A heavy hexagon graph is described in: + + https://journals.aps.org/prx/abstract/10.1103/PhysRevX.10.011022 + + Args: + distance (int): The code distance for the generated heavy hex + graph. The value for distance can be any odd positive integer. + The distance relates to the number of qubits by: + :math:`n = \\frac{5d^2 - 2d - 1}{2}` where :math:`n` is the + number of qubits and :math:`d` is the ``distance`` parameter. + bidirectional (bool): Whether the edges in the output coupling + graph are bidirectional or not. By default this is set to + ``True`` + Returns: + CouplingMap: A heavy hex coupling graph + """ + cmap = cls(description="heavy-hex") + cmap.graph = rx.generators.directed_heavy_hex_graph(distance, bidirectional=bidirectional) + return cmap + + @classmethod + def from_heavy_square(cls, distance, bidirectional=True): + """Return a heavy square graph coupling map. + + A heavy square graph is described in: + + https://journals.aps.org/prx/abstract/10.1103/PhysRevX.10.011022 + + Args: + distance (int): The code distance for the generated heavy square + graph. The value for distance can be any odd positive integer. + The distance relates to the number of qubits by: + :math:`n = 3d^2 - 2d` where :math:`n` is the + number of qubits and :math:`d` is the ``distance`` parameter. + bidirectional (bool): Whether the edges in the output coupling + graph are bidirectional or not. By default this is set to + ``True`` + Returns: + CouplingMap: A heavy square coupling graph + """ + cmap = cls(description="heavy-square") + cmap.graph = rx.generators.directed_heavy_square_graph( + distance, bidirectional=bidirectional + ) + return cmap + def largest_connected_component(self): """Return a set of qubits in the largest connected component.""" return max(rx.weakly_connected_components(self.graph), key=len) diff --git a/releasenotes/notes/heavy-hex-heavy-square-coupling-map-29f459b93cd18518.yaml b/releasenotes/notes/heavy-hex-heavy-square-coupling-map-29f459b93cd18518.yaml new file mode 100644 index 000000000000..d7d74beb4dfd --- /dev/null +++ b/releasenotes/notes/heavy-hex-heavy-square-coupling-map-29f459b93cd18518.yaml @@ -0,0 +1,27 @@ +--- +features: + - | + Added two new constructor methods, + :meth:`~qiskit.transpiler.CouplingMap.from_heavy_hex` and + :meth:`~qiskit.transpiler.CouplingMap.from_heavy_square`, to the + :class:`~qiskit.transpiler.CouplingMap` class. These constructor methods + are used to create a :class:`~qiskit.transpiler.CouplingMap` that are + a heavy hex or heavy square graph as described in: + https://journals.aps.org/prx/abstract/10.1103/PhysRevX.10.011022 + + For example: + + .. jupyter-execute:: + + from qiskit.transpiler import CouplingMap + + cmap = CouplingMap.from_heavy_hex(5) + cmap.draw() + + + .. jupyter-execute:: + + from qiskit.transpiler import CouplingMap + + cmap = CouplingMap.from_heavy_square(5) + cmap.draw() diff --git a/test/python/transpiler/test_coupling.py b/test/python/transpiler/test_coupling.py index 7f30bf85b2fc..384f3d759290 100644 --- a/test/python/transpiler/test_coupling.py +++ b/test/python/transpiler/test_coupling.py @@ -196,6 +196,166 @@ def test_grid_factory_unidirectional(self): expected = [(0, 3), (0, 1), (3, 4), (1, 4), (1, 2), (4, 5), (2, 5)] self.assertEqual(set(edges), set(expected)) + def test_heavy_hex_factory(self): + coupling = CouplingMap.from_heavy_hex(3, bidirectional=False) + edges = coupling.get_edges() + expected = [ + (0, 9), + (0, 13), + (1, 13), + (1, 14), + (2, 14), + (3, 9), + (3, 15), + (4, 15), + (4, 16), + (5, 12), + (5, 16), + (6, 17), + (7, 17), + (7, 18), + (8, 12), + (8, 18), + (10, 14), + (10, 16), + (11, 15), + (11, 17), + ] + self.assertEqual(set(edges), set(expected)) + + def test_heavy_hex_factory_bidirectional(self): + coupling = CouplingMap.from_heavy_hex(3, bidirectional=True) + edges = coupling.get_edges() + expected = [ + (0, 9), + (0, 13), + (1, 13), + (1, 14), + (2, 14), + (3, 9), + (3, 15), + (4, 15), + (4, 16), + (5, 12), + (5, 16), + (6, 17), + (7, 17), + (7, 18), + (8, 12), + (8, 18), + (9, 0), + (9, 3), + (10, 14), + (10, 16), + (11, 15), + (11, 17), + (12, 5), + (12, 8), + (13, 0), + (13, 1), + (14, 1), + (14, 2), + (14, 10), + (15, 3), + (15, 4), + (15, 11), + (16, 4), + (16, 5), + (16, 10), + (17, 6), + (17, 7), + (17, 11), + (18, 7), + (18, 8), + ] + self.assertEqual(set(edges), set(expected)) + + def test_heavy_square_factory(self): + coupling = CouplingMap.from_heavy_square(3, bidirectional=False) + edges = coupling.get_edges() + expected = [ + (0, 15), + (1, 16), + (2, 11), + (3, 12), + (3, 17), + (4, 18), + (5, 11), + (6, 12), + (6, 19), + (7, 20), + (9, 15), + (9, 17), + (10, 16), + (10, 18), + (13, 17), + (13, 19), + (14, 18), + (14, 20), + (15, 1), + (16, 2), + (17, 4), + (18, 5), + (19, 7), + (20, 8), + ] + self.assertEqual(set(edges), set(expected)) + + def test_heavy_square_factory_bidirectional(self): + coupling = CouplingMap.from_heavy_square(3, bidirectional=True) + edges = coupling.get_edges() + expected = [ + (0, 15), + (1, 15), + (1, 16), + (2, 11), + (2, 16), + (3, 12), + (3, 17), + (4, 17), + (4, 18), + (5, 11), + (5, 18), + (6, 12), + (6, 19), + (7, 19), + (7, 20), + (8, 20), + (9, 15), + (9, 17), + (10, 16), + (10, 18), + (11, 2), + (11, 5), + (12, 3), + (12, 6), + (13, 17), + (13, 19), + (14, 18), + (14, 20), + (15, 0), + (15, 1), + (15, 9), + (16, 1), + (16, 2), + (16, 10), + (17, 3), + (17, 4), + (17, 9), + (17, 13), + (18, 4), + (18, 5), + (18, 10), + (18, 14), + (19, 6), + (19, 7), + (19, 13), + (20, 7), + (20, 8), + (20, 14), + ] + self.assertEqual(set(edges), set(expected)) + def test_subgraph(self): coupling = CouplingMap.from_line(6, bidirectional=False) with self.assertWarns(DeprecationWarning):