def test_reaches_full_connectivity(self): """Test to reach full connectivity on the longest line of Mumbai.""" # The longest line on e.g. Mumbai has 21 qubits ll27 = list(range(21)) ll27_map = [[ll27[idx], ll27[idx + 1]] for idx in range(len(ll27) - 1)] ll27_map += [[ll27[idx + 1], ll27[idx]] for idx in range(len(ll27) - 1)] # Create a line swap strategy on this line layer1 = tuple((ll27[idx], ll27[idx + 1]) for idx in range(0, len(ll27) - 1, 2)) layer2 = tuple( (ll27[idx], ll27[idx + 1]) for idx in range(1, len(ll27), 2)) n = len(ll27) for n_layers, result in [ (n - 4, False), (n - 3, False), (n - 2, True), (n - 1, True), ]: swap_strat_ll = [] for idx in range(n_layers): if idx % 2 == 0: swap_strat_ll.append(layer1) else: swap_strat_ll.append(layer2) strat = SwapStrategy(CouplingMap(ll27_map), tuple(swap_strat_ll)) self.assertEqual(len(strat.missing_couplings) == 0, result)
def test_full_line(self): """Test to reach full connectivity on a line.""" n_nodes = 5 strategy = SwapStrategy.from_line(list(range(n_nodes))) self.assertEqual(len(strategy._swap_layers), n_nodes - 2) # The LineSwapStrategy will apply the following permutations layers = [ [0, 1, 2, 3, 4], # coupling map [1, 0, 3, 2, 4], # layer 1 [1, 3, 0, 4, 2], # layer 2 [3, 1, 4, 0, 2], # layer 3 <-- full connectivity is reached. ] for layer_idx, layer in enumerate(layers): expected = set() for idx in range(len(layer) - 1): expected.add((layer[idx], layer[idx + 1])) expected.add((layer[idx + 1], layer[idx])) strat_edges = strategy.swapped_coupling_map(layer_idx).get_edges() self.assertEqual(len(strat_edges), len(expected)) for edge in strat_edges: self.assertTrue(edge in expected) self.assertEqual(strategy.swap_layer(0), [(0, 1), (2, 3)]) self.assertEqual(strategy.swap_layer(1), [(1, 2), (3, 4)]) self.assertEqual(strategy.swap_layer(2), [(0, 1), (2, 3)]) self.assertEqual(len(strategy.missing_couplings), 0)
def test_check_configuration(self): """Test that tries to initialize an invalid swap strategy.""" with self.assertRaises(QiskitError): SwapStrategy( coupling_map=self.line_coupling_map, swap_layers=(((0, 1), (2, 3)), ((1, 3), (2, 4))), )
def test_invalid_strategy(self): """Test that a raise properly occurs.""" coupling_map = CouplingMap(couplinglist=[(0, 1), (1, 2)]) swap_layers = (((0, 1), (2, 3)), ((1, 2), (3, 4))) with self.assertRaises(QiskitError): SwapStrategy(coupling_map, swap_layers)
def test_only_one_swap_per_qubit_per_layer(self): """Test that tries to initialize an invalid swap strategy.""" message = "The 0th swap layer contains a qubit with multiple swaps." with self.assertRaises(QiskitError, msg=message): SwapStrategy( coupling_map=self.line_coupling_map, swap_layers=(((0, 1), (1, 2)), ), )
def test_line(self): """Test the creation of a line swap strategy.""" n_nodes = 5 strategy = SwapStrategy.from_line(list(range(n_nodes))) self.assertEqual(strategy.swap_layer(0), [(0, 1), (2, 3)]) self.assertEqual(strategy.swap_layer(1), [(1, 2), (3, 4)]) self.assertEqual(strategy.swap_layer(2), [(0, 1), (2, 3)]) self.assertEqual(len(strategy.missing_couplings), 0)
def setUp(self): """Assume a linear coupling map.""" super().setUp() cmap = CouplingMap(couplinglist=[(0, 1), (1, 2), (2, 3)]) swap_strat = SwapStrategy(cmap, swap_layers=[[(0, 1), (2, 3)], [(1, 2)]]) self.pm_ = PassManager([ FindCommutingPauliEvolutions(), Commuting2qGateRouter(swap_strat), ])
def test_ccx(self): """Test that extra multi-qubit operations are properly adjusted. Here, we test that the circuit .. parsed-literal:: ┌──────────────────────────┐ q_0: ┤0 ├──■── │ │┌─┴─┐ q_1: ┤1 exp(-it (IZZ + ZIZ))(1) ├┤ X ├ │ │└─┬─┘ q_2: ┤2 ├──■── └──────────────────────────┘ q_3: ───────────────────────────────── becomes .. parsed-literal:: ┌─────────────────┐ ┌───┐ q_0: ┤0 ├─X────────────────────┤ X ├ │ exp(-it ZZ)(1) │ │ ┌─────────────────┐└─┬─┘ q_1: ┤1 ├─X─┤0 ├──■── └─────────────────┘ │ exp(-it ZZ)(2) │ │ q_2: ──────────────────────┤1 ├──■── └─────────────────┘ q_3: ────────────────────────────────────────────── as expected. I.e. the Toffoli is properly adjusted at the end. """ cmap = CouplingMap(couplinglist=[(0, 1), (1, 2)]) swap_strat = SwapStrategy(cmap, swap_layers=(((0, 1), ), )) pm_ = PassManager([ FindCommutingPauliEvolutions(), Commuting2qGateRouter(swap_strat), ]) op = PauliSumOp.from_list([("IZZ", 1), ("ZIZ", 2)]) circ = QuantumCircuit(4) circ.append(PauliEvolutionGate(op, 1), range(3)) circ.ccx(0, 2, 1) swapped = pm_.run(circ) expected = QuantumCircuit(4) expected.append(PauliEvolutionGate(Pauli("ZZ"), 1), (0, 1)) expected.swap(0, 1) expected.append(PauliEvolutionGate(Pauli("ZZ"), 2), (1, 2)) expected.ccx(1, 2, 0) self.assertEqual(swapped, expected)
def test_possible_edges(self): """Test that possible edges works as expected.""" coupling_map = CouplingMap(couplinglist=[(0, 1), (1, 2), (2, 3)]) strat = SwapStrategy(coupling_map, (((0, 1), (2, 3)), ((1, 2), ))) expected = set() for i in range(4): for j in range(4): if i != j: expected.add((i, j)) self.assertSetEqual(strat.possible_edges, expected)
def setUp(self): super().setUp() self.line_coupling_map = CouplingMap(couplinglist=[ (0, 1), (1, 2), (2, 3), (3, 4), (1, 0), (2, 1), (3, 2), (4, 3), ]) self.line_swap_layers = ( ((0, 1), (2, 3)), ((1, 2), (3, 4)), ((0, 1), (2, 3)), ((1, 2), (3, 4)), ((0, 1), (2, 3)), ) self.line_edge_coloring = {(0, 1): 0, (1, 2): 1, (2, 3): 0, (3, 4): 1} self.line_strategy = SwapStrategy(self.line_coupling_map, self.line_swap_layers)
def setUp(self): """Setup useful variables.""" super().setUp() # A fully connected problem. op = PauliSumOp.from_list([("IIZZ", 1), ("IZIZ", 1), ("ZIIZ", 1), ("IZZI", 1), ("ZIZI", 1), ("ZZII", 1)]) self.circ = QuantumCircuit(4) self.circ.append(PauliEvolutionGate(op, 1), range(4)) self.swap_cmap = CouplingMap(couplinglist=[(0, 1), (1, 2), (2, 3)]) # This swap strategy does not reach full connectivity. self.swap_strat = SwapStrategy(self.swap_cmap, swap_layers=(((0, 1), (2, 3)), ))
def test_idle_qubit(self): """Test to route on an op that has an idle qubit. The op is :code:`[("IIXX", 1), ("IXIX", 2)]`. The expected circuit is: ..parsed-literal:: ┌─────────────────┐ q_0: ┤0 ├─X──────────────────── │ exp(-it XX)(3) │ │ ┌─────────────────┐ q_1: ┤1 ├─X─┤0 ├ └─────────────────┘ │ exp(-it XX)(6) │ q_2: ──────────────────────┤1 ├ └─────────────────┘ q_3: ───────────────────────────────────────── """ op = PauliSumOp.from_list([("IIXX", 1), ("IXIX", 2)]) cmap = CouplingMap(couplinglist=[(0, 1), (1, 2), (2, 3)]) swap_strat = SwapStrategy(cmap, swap_layers=(((0, 1), ), )) pm_ = PassManager([ FindCommutingPauliEvolutions(), Commuting2qGateRouter(swap_strat) ]) circ = QuantumCircuit(4) circ.append(PauliEvolutionGate(op, 3), range(4)) swapped = pm_.run(circ) expected = QuantumCircuit(4) expected.append(PauliEvolutionGate(Pauli("XX"), 3), (0, 1)) expected.swap(0, 1) expected.append(PauliEvolutionGate(Pauli("XX"), 6), (1, 2)) self.assertEqual(swapped, expected)
def test_enlarge_with_ancilla(self): """This pass tests that idle qubits after an embedding are left idle.""" # Create a four qubit problem. op = PauliSumOp.from_list([("IZZI", 1), ("ZIIZ", 2), ("ZIZI", 3)]) circ = QuantumCircuit(4) circ.append(PauliEvolutionGate(op, 1), range(4)) # Create a four qubit quantum circuit. backend_cmap = CouplingMap(couplinglist=[(0, 1), (1, 2), (1, 3), (3, 4)]) swap_cmap = CouplingMap(couplinglist=[(0, 1), (1, 2), (2, 3)]) swap_strat = SwapStrategy(swap_cmap, swap_layers=(((0, 1), (2, 3)), ((1, 2), ))) initial_layout = Layout.from_intlist([0, 1, 3, 4], *circ.qregs) pm_pre = PassManager([ FindCommutingPauliEvolutions(), Commuting2qGateRouter(swap_strat), SetLayout(initial_layout), FullAncillaAllocation(backend_cmap), EnlargeWithAncilla(), ApplyLayout(), ]) embedded = pm_pre.run(circ) expected = QuantumCircuit(5) expected.append(PauliEvolutionGate(Pauli("ZZ"), 1), (1, 3)) expected.swap(0, 1) expected.swap(3, 4) expected.append(PauliEvolutionGate(Pauli("ZZ"), 2), (1, 3)) expected.swap(1, 3) expected.append(PauliEvolutionGate(Pauli("ZZ"), 3), (0, 1)) self.assertEqual(embedded, expected)
def test_repr(self): """The the representation.""" expected = ("SwapStrategy with swap layers:\n((0, 1),),\non " "[[0, 1], [1, 0], [1, 2], [2, 1]] coupling map.") self.assertEqual(repr(SwapStrategy.from_line([0, 1, 2])), expected)
def test_invalid_line_strategy(self): """Test the number of layers.""" message = "Negative number -1 passed for number of swap layers." with self.assertRaises(ValueError, msg=message): SwapStrategy.from_line([0, 1, 2], -1)
def test_t_device(self): """Test the swap strategy to route a complete problem on a T device. The coupling map in this test corresponds to .. parsed-literal:: 0 -- 1 -- 2 | 3 | 4 The problem being routed is a fully connect ZZ graph. It has 10 terms since there are five qubits in the coupling map. This test checks that the circuit produced by the commuting gate router has the instructions we expect. There are several circuits that are valid since some of the Rzz gates commute. """ swaps = ( ((1, 3), ), ((0, 1), (3, 4)), ((1, 3), ), ) cmap = CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]]) cmap.make_symmetric() swap_strat = SwapStrategy(cmap, swaps) # A dense Pauli op. op = PauliSumOp.from_list([ ("IIIZZ", 1), ("IIZIZ", 2), ("IZIIZ", 3), ("ZIIIZ", 4), ("IIZZI", 5), ("IZIZI", 6), ("ZIIZI", 7), ("IZZII", 8), ("ZIZII", 9), ("ZZIII", 10), ]) circ = QuantumCircuit(5) circ.append(PauliEvolutionGate(op, 1), range(5)) pm_ = PassManager([ FindCommutingPauliEvolutions(), Commuting2qGateRouter(swap_strat), ]) swapped = circuit_to_dag(pm_.run(circ)) # The swapped circuit can take on several forms as some of the gates commute. # We test that sets of gates are where we expected them in the circuit data def inst_info(op, qargs, qreg): """Get a tuple we can easily test.""" param = None if len(op.params) > 0: param = op.params[0] return op.name, param, qreg.index(qargs[0]), qreg.index(qargs[1]) qreg = swapped.qregs["q"] inst_list = list( inst_info(node.op, node.qargs, qreg) for node in swapped.op_nodes()) # First block has the Rzz gates ("IIIZZ", 1), ("IIZZI", 5), ("IZIZI", 6), ("ZZIII", 10) expected = { ("PauliEvolution", 1.0, 0, 1), ("PauliEvolution", 5.0, 1, 2), ("PauliEvolution", 6.0, 1, 3), ("PauliEvolution", 10.0, 3, 4), } self.assertSetEqual(set(inst_list[0:4]), expected) # Block 2 is a swap self.assertSetEqual({inst_list[4]}, {("swap", None, 1, 3)}) # Block 3 This combines a swap layer and two layers of Rzz gates. expected = { ("PauliEvolution", 8.0, 2, 1), ("PauliEvolution", 7.0, 3, 4), ("PauliEvolution", 3.0, 0, 1), ("swap", None, 0, 1), ("PauliEvolution", 2.0, 1, 2), ("PauliEvolution", 4.0, 1, 3), ("swap", None, 3, 4), } self.assertSetEqual(set(inst_list[5:12]), expected) # Test the remaining instructions. self.assertSetEqual({inst_list[12]}, {("swap", None, 1, 3)}) self.assertSetEqual({inst_list[13]}, {("PauliEvolution", 9.0, 2, 1)})
def test_invalid_line(self): """Test that lines should be longer than 1.""" message = "The line cannot have less than two elements, but is [1]" with self.assertRaises(ValueError, msg=message): SwapStrategy.from_line([1], 0)
class TestSwapStrategy(QiskitTestCase): """A class to test the swap strategies.""" def setUp(self): super().setUp() self.line_coupling_map = CouplingMap(couplinglist=[ (0, 1), (1, 2), (2, 3), (3, 4), (1, 0), (2, 1), (3, 2), (4, 3), ]) self.line_swap_layers = ( ((0, 1), (2, 3)), ((1, 2), (3, 4)), ((0, 1), (2, 3)), ((1, 2), (3, 4)), ((0, 1), (2, 3)), ) self.line_edge_coloring = {(0, 1): 0, (1, 2): 1, (2, 3): 0, (3, 4): 1} self.line_strategy = SwapStrategy(self.line_coupling_map, self.line_swap_layers) @data( (0, [0, 1, 2, 3, 4]), (1, [1, 0, 3, 2, 4]), (2, [1, 3, 0, 4, 2]), (3, [3, 1, 4, 0, 2]), (4, [3, 4, 1, 2, 0]), (5, [4, 3, 2, 1, 0]), ) @unpack def test_inverse_composed_permutation(self, layer_idx: int, expected: List[int]): """Test the inverse of the permutations.""" self.assertEqual( self.line_strategy.inverse_composed_permutation(layer_idx), expected) def test_apply_swap_layer(self): """Test that swapping a list of elements is correct.""" list_to_swap = [0, 10, 20, 30, 40] swapped_list = self.line_strategy.apply_swap_layer(list_to_swap, 0) self.assertEqual(swapped_list, [10, 0, 30, 20, 40]) self.assertFalse(list_to_swap == swapped_list) swapped_list = self.line_strategy.apply_swap_layer(list_to_swap, 1, inplace=True) self.assertEqual(swapped_list, [0, 20, 10, 40, 30]) self.assertTrue(list_to_swap == swapped_list) def test_length(self): """Test the __len__ operator.""" self.assertEqual(len(self.line_strategy), 5) def test_swapped_coupling_map(self): """Test the edges generated by a swap strategy.""" edge_set = {(2, 0), (0, 4), (4, 1), (1, 3), (3, 1), (1, 4), (4, 0), (0, 2)} swapped_map = self.line_strategy.swapped_coupling_map(3) self.assertEqual(edge_set, set(swapped_map.get_edges())) def test_check_configuration(self): """Test that tries to initialize an invalid swap strategy.""" with self.assertRaises(QiskitError): SwapStrategy( coupling_map=self.line_coupling_map, swap_layers=(((0, 1), (2, 3)), ((1, 3), (2, 4))), ) def test_only_one_swap_per_qubit_per_layer(self): """Test that tries to initialize an invalid swap strategy.""" message = "The 0th swap layer contains a qubit with multiple swaps." with self.assertRaises(QiskitError, msg=message): SwapStrategy( coupling_map=self.line_coupling_map, swap_layers=(((0, 1), (1, 2)), ), ) def test_distance_matrix(self): """Test the computation of the swap strategy distance matrix.""" line_distance_matrix = np.array([ [0, 0, 3, 1, 2], [0, 0, 0, 2, 3], [3, 0, 0, 0, 1], [1, 2, 0, 0, 0], [2, 3, 1, 0, 0], ]) self.assertTrue( np.all(line_distance_matrix == self.line_strategy.distance_matrix)) # Check that the distance matrix cannot be written to. with self.assertRaises(ValueError): self.line_strategy.distance_matrix[1, 2] = 5 def test_reaches_full_connectivity(self): """Test to reach full connectivity on the longest line of Mumbai.""" # The longest line on e.g. Mumbai has 21 qubits ll27 = list(range(21)) ll27_map = [[ll27[idx], ll27[idx + 1]] for idx in range(len(ll27) - 1)] ll27_map += [[ll27[idx + 1], ll27[idx]] for idx in range(len(ll27) - 1)] # Create a line swap strategy on this line layer1 = tuple((ll27[idx], ll27[idx + 1]) for idx in range(0, len(ll27) - 1, 2)) layer2 = tuple( (ll27[idx], ll27[idx + 1]) for idx in range(1, len(ll27), 2)) n = len(ll27) for n_layers, result in [ (n - 4, False), (n - 3, False), (n - 2, True), (n - 1, True), ]: swap_strat_ll = [] for idx in range(n_layers): if idx % 2 == 0: swap_strat_ll.append(layer1) else: swap_strat_ll.append(layer2) strat = SwapStrategy(CouplingMap(ll27_map), tuple(swap_strat_ll)) self.assertEqual(len(strat.missing_couplings) == 0, result) def test_new_connections(self): """Test the new connections method.""" new_cnx = self.line_strategy.new_connections(0) expected = [{1, 0}, {2, 1}, {3, 2}, {4, 3}] self.assertListEqual(new_cnx, expected) # Test after first swap layer (0, 1) first new_cnx = self.line_strategy.new_connections(1) expected = [{3, 0}, {4, 2}] self.assertListEqual(new_cnx, expected) def test_possible_edges(self): """Test that possible edges works as expected.""" coupling_map = CouplingMap(couplinglist=[(0, 1), (1, 2), (2, 3)]) strat = SwapStrategy(coupling_map, (((0, 1), (2, 3)), ((1, 2), ))) expected = set() for i in range(4): for j in range(4): if i != j: expected.add((i, j)) self.assertSetEqual(strat.possible_edges, expected)