From f03e4301f329adcc38c93583c079699c02c5ba04 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 19:51:58 +0000 Subject: [PATCH] Assert properties not specifics of `DenseLayout` results (backport #10901) (#10907) * Assert properties not specifics of `DenseLayout` results (#10901) `DenseLayout` is a deterministic pass, so there's no risk of RNG manipulations changing the output. Internally, however, the use of the sparse-matrix bandwidth-reduction algorithm in Scipy's `reverse_cuthill_mckee` uses `numpy.argsort` internally with the unstable default sorting algorithm, which means the output _can_ be dependent on the way that sort is implemented. The arrays that are sorted are directly related to the input coupling map, and are likely to include degeneracies, which pose problems when the implementation of the unstable sort changes. This was the case moving from Numpy 1.24 to Numpy 1.25. This commit instead changes the tests from asserting that a precise layout was returned to asserting that the returned layout contains only a connected subgraph of qubits. The "most" connected component that `DenseLayout` finds must be _at least_ connected, though this assertion is not quite as strong as finding the _densest_. The extra test accounts for this weakening. (cherry picked from commit 0f66cdfd309db2865995d694b247fa149cee893b) * Avoid use of un-backported kwarg * Update test/python/transpiler/test_dense_layout.py --------- Co-authored-by: Jake Lishman --- test/python/transpiler/test_dense_layout.py | 87 ++++++++++++--------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/test/python/transpiler/test_dense_layout.py b/test/python/transpiler/test_dense_layout.py index 01642b4036d1..0743d9e0f5fa 100644 --- a/test/python/transpiler/test_dense_layout.py +++ b/test/python/transpiler/test_dense_layout.py @@ -12,6 +12,7 @@ """Test the DenseLayout pass""" +import itertools import unittest import numpy as np @@ -43,6 +44,30 @@ def setUp(self): } self.target_19.add_instruction(CXGate(), instruction_props) + def test_finds_densest_component(self): + """Test that `DenseLayout` finds precisely the densest subcomponent of a coupling graph, not + just _any_ connected component.""" + circuit = QuantumCircuit(5) + for a, b in itertools.permutations(circuit.qubits, 2): + circuit.cx(a, b) + + # The map is a big long sparse line, except the middle 5 physical qubits are all completely + # connected, so `DenseLayout` should always choose those. + left_edge_qubits = range(0, 7) + middle_qubits = range(7, 12) + right_edge_qubits = range(12, 20) + cm = CouplingMap( + [(q, q + 1) for q in left_edge_qubits] + + [(q - 1, q) for q in right_edge_qubits] + + list(itertools.permutations(middle_qubits, 2)) + ) + cm.make_symmetric() + pass_ = DenseLayout(cm) + pass_(circuit) + layout = pass_.property_set["layout"] + used_qubits = {layout[q] for q in circuit.qubits} + self.assertEqual(used_qubits, set(middle_qubits)) + def test_5q_circuit_20q_coupling(self): """Test finds dense 5q corner in 20q coupling map.""" qr = QuantumRegister(5, "q") @@ -53,15 +78,14 @@ def test_5q_circuit_20q_coupling(self): circuit.cx(qr[0], qr[2]) dag = circuit_to_dag(circuit) - pass_ = DenseLayout(CouplingMap(self.cmap20)) + cm = CouplingMap(self.cmap20) + pass_ = DenseLayout(cm) pass_.run(dag) layout = pass_.property_set["layout"] - self.assertEqual(layout[qr[0]], 11) - self.assertEqual(layout[qr[1]], 10) - self.assertEqual(layout[qr[2]], 6) - self.assertEqual(layout[qr[3]], 5) - self.assertEqual(layout[qr[4]], 0) + actual = [layout[q] for q in circuit.qubits] + # CouplingMap.reduce raises an error if the set is not connected. + cm.reduce(actual) def test_6q_circuit_20q_coupling(self): """Test finds dense 5q corner in 20q coupling map.""" @@ -72,17 +96,14 @@ def test_6q_circuit_20q_coupling(self): circuit.cx(qr1[1], qr0[2]) dag = circuit_to_dag(circuit) - pass_ = DenseLayout(CouplingMap(self.cmap20)) + cm = CouplingMap(self.cmap20) + pass_ = DenseLayout(cm) pass_.run(dag) layout = pass_.property_set["layout"] - - self.assertEqual(layout[qr0[0]], 11) - self.assertEqual(layout[qr0[1]], 10) - self.assertEqual(layout[qr0[2]], 6) - self.assertEqual(layout[qr1[0]], 5) - self.assertEqual(layout[qr1[1]], 1) - self.assertEqual(layout[qr1[2]], 0) + actual = [layout[q] for q in circuit.qubits] + # CouplingMap.reduce raises an error if the set is not connected. + cm.reduce(actual) def test_5q_circuit_19q_target_with_noise(self): """Test layout works finds a dense 5q subgraph in a 19q heavy hex target.""" @@ -96,11 +117,9 @@ def test_5q_circuit_19q_target_with_noise(self): pass_ = DenseLayout(target=self.target_19) pass_.run(dag) layout = pass_.property_set["layout"] - self.assertEqual(layout[qr[0]], 9) - self.assertEqual(layout[qr[1]], 3) - self.assertEqual(layout[qr[2]], 11) - self.assertEqual(layout[qr[3]], 15) - self.assertEqual(layout[qr[4]], 4) + actual = [layout[q] for q in circuit.qubits] + # CouplingMap.reduce raises an error if the set is not connected. + self.target_19.build_coupling_map().reduce(actual) def test_5q_circuit_19q_target_without_noise(self): """Test layout works finds a dense 5q subgraph in a 19q heavy hex target with no noise.""" @@ -117,11 +136,9 @@ def test_5q_circuit_19q_target_without_noise(self): pass_ = DenseLayout(target=noiseless_target) pass_.run(dag) layout = pass_.property_set["layout"] - self.assertEqual(layout[qr[0]], 1) - self.assertEqual(layout[qr[1]], 13) - self.assertEqual(layout[qr[2]], 0) - self.assertEqual(layout[qr[3]], 9) - self.assertEqual(layout[qr[4]], 3) + actual = [layout[q] for q in circuit.qubits] + # CouplingMap.reduce raises an error if the set is not connected. + noiseless_target.build_coupling_map().reduce(actual) def test_ideal_target_no_coupling(self): """Test pass fails as expected if a target without edge constraints exists.""" @@ -217,14 +234,13 @@ def test_5q_circuit_20q_with_if_else(self): circuit.cx(0, 4) dag = circuit_to_dag(circuit) - pass_ = DenseLayout(CouplingMap(self.cmap20)) + cm = CouplingMap(self.cmap20) + pass_ = DenseLayout(cm) pass_.run(dag) layout = pass_.property_set["layout"] - self.assertEqual(layout[qr[0]], 11) - self.assertEqual(layout[qr[1]], 10) - self.assertEqual(layout[qr[2]], 6) - self.assertEqual(layout[qr[3]], 5) - self.assertEqual(layout[qr[4]], 0) + actual = [layout[q] for q in circuit.qubits] + # CouplingMap.reduce raises an error if the set is not connected. + cm.reduce(actual) def test_loose_bit_circuit(self): """Test dense layout works with loose bits outside a register.""" @@ -237,14 +253,13 @@ def test_loose_bit_circuit(self): circuit.cx(3, 0) circuit.cx(3, 1) dag = circuit_to_dag(circuit) - pass_ = DenseLayout(CouplingMap(self.cmap20)) + cm = CouplingMap(self.cmap20) + pass_ = DenseLayout(cm) pass_.run(dag) layout = pass_.property_set["layout"] - self.assertEqual(layout[bits[0]], 11) - self.assertEqual(layout[bits[1]], 10) - self.assertEqual(layout[bits[2]], 6) - self.assertEqual(layout[bits[3]], 5) - self.assertEqual(layout[bits[4]], 0) + actual = [layout[q] for q in circuit.qubits] + # CouplingMap.reduce raises an error if the set is not connected. + cm.reduce(actual) if __name__ == "__main__":