mirror of https://github.com/Qiskit/qiskit.git
533 lines
16 KiB
Python
533 lines
16 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2017, 2024.
|
|
#
|
|
# 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.
|
|
|
|
# pylint: disable=missing-docstring
|
|
|
|
import unittest
|
|
|
|
import numpy as np
|
|
import rustworkx as rx
|
|
|
|
from qiskit.transpiler import CouplingMap
|
|
from qiskit.transpiler.exceptions import CouplingError
|
|
from qiskit.utils import optionals
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
from ..visualization.visualization import QiskitVisualizationTestCase, path_to_diagram_reference
|
|
from ..legacy_cmaps import RUESCHLIKON_CMAP
|
|
|
|
|
|
class CouplingTest(QiskitTestCase):
|
|
def test_empty_coupling_class(self):
|
|
coupling = CouplingMap()
|
|
self.assertEqual(0, coupling.size())
|
|
self.assertEqual([], coupling.physical_qubits)
|
|
self.assertEqual([], coupling.get_edges())
|
|
self.assertFalse(coupling.is_connected())
|
|
self.assertEqual("", str(coupling))
|
|
|
|
def test_coupling_str(self):
|
|
coupling_list = [[0, 1], [0, 2], [1, 2]]
|
|
coupling = CouplingMap(coupling_list)
|
|
expected = "[[0, 1], [0, 2], [1, 2]]"
|
|
self.assertEqual(expected, str(coupling))
|
|
|
|
def test_coupling_distance(self):
|
|
coupling_list = [(0, 1), (0, 2), (1, 2)]
|
|
coupling = CouplingMap(coupling_list)
|
|
self.assertTrue(coupling.is_connected())
|
|
physical_qubits = coupling.physical_qubits
|
|
result = coupling.distance(physical_qubits[0], physical_qubits[1])
|
|
self.assertIsInstance(result, int)
|
|
self.assertEqual(1, result)
|
|
|
|
def test_add_physical_qubits(self):
|
|
coupling = CouplingMap()
|
|
self.assertEqual("", str(coupling))
|
|
coupling.add_physical_qubit(0)
|
|
self.assertEqual([0], coupling.physical_qubits)
|
|
self.assertEqual("", str(coupling))
|
|
|
|
def test_add_physical_qubits_not_int(self):
|
|
coupling = CouplingMap()
|
|
self.assertRaises(CouplingError, coupling.add_physical_qubit, "q")
|
|
|
|
def test_add_edge(self):
|
|
coupling = CouplingMap()
|
|
self.assertEqual("", str(coupling))
|
|
coupling.add_edge(0, 1)
|
|
expected = "[[0, 1]]"
|
|
self.assertEqual(expected, str(coupling))
|
|
|
|
def test_neighbors(self):
|
|
"""Test neighboring qubits are found correctly."""
|
|
coupling = CouplingMap([[0, 1], [0, 2], [1, 0]])
|
|
|
|
physical_qubits = coupling.physical_qubits
|
|
self.assertEqual(set(coupling.neighbors(physical_qubits[0])), {1, 2})
|
|
self.assertEqual(set(coupling.neighbors(physical_qubits[1])), {0})
|
|
self.assertEqual(set(coupling.neighbors(physical_qubits[2])), set())
|
|
|
|
def test_distance_error(self):
|
|
"""Test distance between unconnected physical_qubits."""
|
|
graph = CouplingMap()
|
|
graph.add_physical_qubit(0)
|
|
graph.add_physical_qubit(1)
|
|
self.assertRaises(CouplingError, graph.distance, 0, 1)
|
|
|
|
def test_distance_self_loop(self):
|
|
"""Test distance between the same physical qubit."""
|
|
graph = CouplingMap()
|
|
graph.add_physical_qubit(0)
|
|
graph.add_physical_qubit(1)
|
|
self.assertEqual(0.0, graph.distance(0, 0))
|
|
|
|
def test_init_with_couplinglist(self):
|
|
coupling_list = [[0, 1], [1, 2]]
|
|
coupling = CouplingMap(coupling_list)
|
|
|
|
qubits_expected = [0, 1, 2]
|
|
edges_expected = [(0, 1), (1, 2)]
|
|
|
|
self.assertEqual(coupling.physical_qubits, qubits_expected)
|
|
self.assertEqual(coupling.get_edges(), edges_expected)
|
|
self.assertEqual(2, coupling.distance(0, 2))
|
|
|
|
def test_successful_reduced_map(self):
|
|
"""Generate a reduced map"""
|
|
cmap = RUESCHLIKON_CMAP
|
|
coupling_map = CouplingMap(cmap)
|
|
out = coupling_map.reduce([12, 11, 10, 9]).get_edges()
|
|
ans = [(1, 2), (3, 2), (0, 1)]
|
|
self.assertEqual(set(out), set(ans))
|
|
|
|
def test_bad_reduced_map(self):
|
|
"""Generate disconnected reduced map"""
|
|
cmap = RUESCHLIKON_CMAP
|
|
coupling_map = CouplingMap(cmap)
|
|
with self.assertRaises(CouplingError):
|
|
coupling_map.reduce([12, 11, 10, 3])
|
|
|
|
def test_disconnected_reduced_map_allowed(self):
|
|
"""Generate disconnected reduced map but do not error"""
|
|
cmap = RUESCHLIKON_CMAP
|
|
coupling_map = CouplingMap(cmap)
|
|
reduced_map = coupling_map.reduce([12, 11, 10, 3], check_if_connected=False)
|
|
reduced_edges = reduced_map.get_edges()
|
|
qubits_expected = [0, 1, 2, 3]
|
|
edges_expected = [(0, 1), (1, 2)]
|
|
self.assertEqual(qubits_expected, reduced_map.physical_qubits)
|
|
self.assertEqual(set(reduced_edges), set(edges_expected))
|
|
|
|
def test_symmetric_small_true(self):
|
|
coupling_list = [[0, 1], [1, 0]]
|
|
coupling = CouplingMap(coupling_list)
|
|
|
|
self.assertTrue(coupling.is_symmetric)
|
|
|
|
def test_symmetric_big_false(self):
|
|
coupling_list = [
|
|
[1, 0],
|
|
[1, 2],
|
|
[2, 3],
|
|
[4, 3],
|
|
[4, 10],
|
|
[5, 4],
|
|
[5, 6],
|
|
[5, 9],
|
|
[6, 8],
|
|
[9, 8],
|
|
[9, 10],
|
|
[7, 8],
|
|
[11, 3],
|
|
[11, 10],
|
|
[11, 12],
|
|
[12, 2],
|
|
[13, 1],
|
|
[13, 12],
|
|
]
|
|
coupling = CouplingMap(coupling_list)
|
|
|
|
self.assertFalse(coupling.is_symmetric)
|
|
|
|
def test_make_symmetric(self):
|
|
coupling_list = [[0, 1], [0, 2]]
|
|
coupling = CouplingMap(coupling_list)
|
|
|
|
coupling.make_symmetric()
|
|
edges = coupling.get_edges()
|
|
|
|
self.assertEqual(set(edges), {(0, 1), (0, 2), (2, 0), (1, 0)})
|
|
|
|
def test_full_factory(self):
|
|
coupling = CouplingMap.from_full(4)
|
|
edges = coupling.get_edges()
|
|
expected = [
|
|
(0, 1),
|
|
(0, 2),
|
|
(0, 3),
|
|
(1, 0),
|
|
(1, 2),
|
|
(1, 3),
|
|
(2, 0),
|
|
(2, 1),
|
|
(2, 3),
|
|
(3, 0),
|
|
(3, 1),
|
|
(3, 2),
|
|
]
|
|
self.assertEqual(set(edges), set(expected))
|
|
|
|
def test_line_factory(self):
|
|
coupling = CouplingMap.from_line(4)
|
|
edges = coupling.get_edges()
|
|
expected = [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)]
|
|
self.assertEqual(set(edges), set(expected))
|
|
|
|
def test_grid_factory(self):
|
|
coupling = CouplingMap.from_grid(2, 3)
|
|
edges = coupling.get_edges()
|
|
expected = [
|
|
(0, 3),
|
|
(0, 1),
|
|
(3, 0),
|
|
(3, 4),
|
|
(1, 0),
|
|
(1, 4),
|
|
(1, 2),
|
|
(4, 1),
|
|
(4, 3),
|
|
(4, 5),
|
|
(2, 1),
|
|
(2, 5),
|
|
(5, 2),
|
|
(5, 4),
|
|
]
|
|
self.assertEqual(set(edges), set(expected))
|
|
|
|
def test_grid_factory_unidirectional(self):
|
|
coupling = CouplingMap.from_grid(2, 3, bidirectional=False)
|
|
edges = coupling.get_edges()
|
|
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_hexagonal_lattice_2_2_factory(self):
|
|
coupling = CouplingMap.from_hexagonal_lattice(2, 2, bidirectional=False)
|
|
edges = coupling.get_edges()
|
|
expected = [
|
|
(0, 1),
|
|
(1, 2),
|
|
(2, 3),
|
|
(3, 4),
|
|
(5, 6),
|
|
(6, 7),
|
|
(7, 8),
|
|
(8, 9),
|
|
(9, 10),
|
|
(11, 12),
|
|
(12, 13),
|
|
(13, 14),
|
|
(14, 15),
|
|
(0, 5),
|
|
(2, 7),
|
|
(4, 9),
|
|
(6, 11),
|
|
(8, 13),
|
|
(10, 15),
|
|
]
|
|
self.assertEqual(set(edges), set(expected))
|
|
|
|
def test_hexagonal_lattice_2_2_factory_bidirectional(self):
|
|
coupling = CouplingMap.from_hexagonal_lattice(2, 2, bidirectional=True)
|
|
edges = coupling.get_edges()
|
|
expected = [
|
|
(0, 1),
|
|
(1, 0),
|
|
(1, 2),
|
|
(2, 1),
|
|
(2, 3),
|
|
(3, 2),
|
|
(3, 4),
|
|
(4, 3),
|
|
(5, 6),
|
|
(6, 5),
|
|
(6, 7),
|
|
(7, 6),
|
|
(7, 8),
|
|
(8, 7),
|
|
(8, 9),
|
|
(9, 8),
|
|
(9, 10),
|
|
(10, 9),
|
|
(11, 12),
|
|
(12, 11),
|
|
(12, 13),
|
|
(13, 12),
|
|
(13, 14),
|
|
(14, 13),
|
|
(14, 15),
|
|
(15, 14),
|
|
(0, 5),
|
|
(5, 0),
|
|
(2, 7),
|
|
(7, 2),
|
|
(4, 9),
|
|
(9, 4),
|
|
(6, 11),
|
|
(11, 6),
|
|
(8, 13),
|
|
(13, 8),
|
|
(10, 15),
|
|
(15, 10),
|
|
]
|
|
self.assertEqual(set(edges), set(expected))
|
|
|
|
def test_implements_iter(self):
|
|
"""Test that the object is implicitly iterable."""
|
|
coupling = CouplingMap.from_line(3)
|
|
expected = [(0, 1), (1, 0), (1, 2), (2, 1)]
|
|
self.assertEqual(sorted(coupling), expected)
|
|
|
|
def test_disjoint_coupling_map(self):
|
|
cmap = CouplingMap([[0, 1], [1, 0], [2, 3], [3, 2]])
|
|
self.assertFalse(cmap.is_connected())
|
|
distance_matrix = cmap.distance_matrix
|
|
expected = np.array(
|
|
[
|
|
[0, 1, np.inf, np.inf],
|
|
[1, 0, np.inf, np.inf],
|
|
[np.inf, np.inf, 0, 1],
|
|
[np.inf, np.inf, 1, 0],
|
|
]
|
|
)
|
|
np.testing.assert_array_equal(expected, distance_matrix)
|
|
|
|
def test_disjoint_coupling_map_distance_no_path_qubits(self):
|
|
cmap = CouplingMap([[0, 1], [1, 0], [2, 3], [3, 2]])
|
|
self.assertFalse(cmap.is_connected())
|
|
with self.assertRaises(CouplingError):
|
|
cmap.distance(0, 3)
|
|
|
|
def test_component_mapping(self):
|
|
cmap = CouplingMap([[0, 1], [1, 0], [2, 3], [3, 2]])
|
|
components = cmap.connected_components()
|
|
self.assertEqual(components[1].graph[0], 2)
|
|
self.assertEqual(components[1].graph[1], 3)
|
|
self.assertEqual(components[0].graph[0], 0)
|
|
self.assertEqual(components[0].graph[1], 1)
|
|
|
|
def test_components_connected_graph(self):
|
|
cmap = CouplingMap.from_line(5)
|
|
self.assertTrue(cmap.is_connected())
|
|
subgraphs = cmap.connected_components()
|
|
self.assertEqual(len(subgraphs), 1)
|
|
self.assertTrue(rx.is_isomorphic(cmap.graph, subgraphs[0].graph))
|
|
|
|
def test_components_disconnected_graph(self):
|
|
cmap = CouplingMap([[0, 1], [1, 2], [3, 4], [4, 5]])
|
|
self.assertFalse(cmap.is_connected())
|
|
subgraphs = cmap.connected_components()
|
|
self.assertEqual(len(subgraphs), 2)
|
|
expected_subgraph = CouplingMap([[0, 1], [1, 2]])
|
|
self.assertTrue(rx.is_isomorphic(expected_subgraph.graph, subgraphs[0].graph))
|
|
self.assertTrue(rx.is_isomorphic(expected_subgraph.graph, subgraphs[1].graph))
|
|
|
|
def test_equality(self):
|
|
"""Test that equality checks that the graphs have the same nodes, node labels, and edges."""
|
|
|
|
# two coupling maps with 4 nodes and the same edges
|
|
coupling0 = CouplingMap([(0, 1), (0, 2), (2, 3)])
|
|
coupling1 = CouplingMap([(0, 1), (0, 2), (2, 3)])
|
|
self.assertEqual(coupling0, coupling1)
|
|
|
|
# coupling map with 5 nodes not equal to the previous 2
|
|
coupling2 = CouplingMap([(0, 1), (0, 2), (2, 4)])
|
|
self.assertNotEqual(coupling0, coupling2)
|
|
|
|
# coupling map isomorphic to coupling0, but with cyclically shifted labels
|
|
coupling3 = CouplingMap([(1, 2), (1, 3), (3, 0)])
|
|
self.assertNotEqual(coupling0, coupling3)
|
|
|
|
# additional test for comparison to a non-CouplingMap object
|
|
self.assertNotEqual(coupling0, 1)
|
|
|
|
|
|
class CouplingVisualizationTest(QiskitVisualizationTestCase):
|
|
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
|
|
@unittest.skipUnless(optionals.HAS_PIL, "Pillow not installed")
|
|
def test_coupling_draw(self):
|
|
"""Test that the coupling map drawing with respect to the reference file is correct."""
|
|
cmap = CouplingMap([[0, 1], [1, 2], [2, 3], [2, 4], [2, 5], [2, 6]])
|
|
image_ref = path_to_diagram_reference("coupling_map.png")
|
|
image = cmap.draw()
|
|
self.assertImagesAreEqual(image, image_ref, diff_tolerance=0.01)
|