def test_large_partial_random(self) -> None: """Test a random (partial) mapping on a large randomly generated graph""" size = 100 # Note that graph may have "gaps" in the node counts, i.e. the numbering is noncontiguous. graph = rx.undirected_gnm_random_graph(size, size**2 // 10) for i in graph.node_indexes(): try: graph.remove_edge(i, i) # Remove self-loops. except rx.NoEdgeBetweenNodes: continue # Make sure the graph is connected by adding C_n graph.add_edges_from_no_data([(i, i + 1) for i in range(len(graph) - 1)]) swapper = ApproximateTokenSwapper( graph) # type: ApproximateTokenSwapper[int] # Generate a randomized permutation. rand_perm = random.permutation(graph.nodes()) permutation = dict(zip(graph.nodes(), rand_perm)) mapping = dict(itertools.islice(permutation.items(), 0, size, 2)) # Drop every 2nd element. out = list(swapper.map(mapping, trials=40)) util.swap_permutation([out], mapping, allow_missing_keys=True) self.assertEqual({i: i for i in mapping.values()}, mapping)
def __init__(self, coupling_map: CouplingMap, from_layout: Union[Layout, str], to_layout: Union[Layout, str], seed: Union[int, np.random.default_rng] = None, trials=4): """LayoutTransformation initializer. Args: coupling_map (CouplingMap): Directed graph representing a coupling map. from_layout (Union[Layout, str]): The starting layout of qubits onto physical qubits. If the type is str, look up `property_set` when this pass runs. to_layout (Union[Layout, str]): The final layout of qubits on phyiscal qubits. If the type is str, look up `property_set` when this pass runs. seed (Union[int, np.random.default_rng]): Seed to use for random trials. trials (int): How many randomized trials to perform, taking the best circuit as output. """ super().__init__() self.coupling_map = coupling_map self.from_layout = from_layout self.to_layout = to_layout graph = coupling_map.graph.to_undirected() self.token_swapper = ApproximateTokenSwapper(graph, seed) self.trials = trials
def test_partial_simple(self) -> None: """Test a partial mapping on a small graph.""" graph = rx.generators.path_graph(4) mapping = {0: 3} swapper = ApproximateTokenSwapper(graph) # type: ApproximateTokenSwapper[int] out = list(swapper.map(mapping)) self.assertEqual(3, len(out)) util.swap_permutation([out], mapping, allow_missing_keys=True) self.assertEqual({3: 3}, mapping)
def test_small(self) -> None: """Test an inverting permutation on a small path graph of size 8""" graph = rx.generators.path_graph(8) permutation = {i: 7 - i for i in range(8)} swapper = ApproximateTokenSwapper(graph) # type: ApproximateTokenSwapper[int] out = list(swapper.map(permutation)) util.swap_permutation([out], permutation) self.assertEqual({i: i for i in range(8)}, permutation)
def test_partial_small(self) -> None: """Test an partial inverting permutation on a small path graph of size 5""" graph = rx.generators.path_graph(4) permutation = {i: 3 - i for i in range(2)} swapper = ApproximateTokenSwapper(graph) # type: ApproximateTokenSwapper[int] out = list(swapper.map(permutation)) self.assertEqual(5, len(out)) util.swap_permutation([out], permutation, allow_missing_keys=True) self.assertEqual({i: i for i in permutation.values()}, permutation)
def test_simple(self) -> None: """Test a simple permutation on a path graph of size 4.""" graph = rx.generators.path_graph(4) permutation = {0: 0, 1: 3, 3: 1, 2: 2} swapper = ApproximateTokenSwapper(graph) # type: ApproximateTokenSwapper[int] out = list(swapper.map(permutation)) self.assertEqual(3, len(out)) util.swap_permutation([out], permutation) self.assertEqual({i: i for i in range(4)}, permutation)
def test_bug1(self) -> None: """Tests for a bug that occured in happy swap chains of length >2.""" graph = nx.Graph() graph.add_edges_from([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), (3, 6)]) permutation = {0: 4, 1: 0, 2: 3, 3: 6, 4: 2, 6: 1} swapper = ApproximateTokenSwapper( graph) # type: ApproximateTokenSwapper[int] out = list(swapper.map(permutation)) util.swap_permutation([out], permutation) self.assertEqual({i: i for i in permutation}, permutation)
def test_large_partial_random(self) -> None: """Test a random (partial) mapping on a large randomly generated graph""" size = 100 # Note that graph may have "gaps" in the node counts, i.e. the numbering is noncontiguous. graph = nx.dense_gnm_random_graph(size, size**2 // 10) graph.remove_edges_from( (i, i) for i in graph.nodes) # Remove self-loops. # Make sure the graph is connected by adding C_n nodes = list(graph.nodes) graph.add_edges_from((node, nodes[(i + 1) % len(nodes)]) for i, node in enumerate(nodes)) swapper = ApproximateTokenSwapper( graph) # type: ApproximateTokenSwapper[int] # Generate a randomized permutation. rand_perm = random.permutation(graph.nodes()) permutation = dict(zip(graph.nodes(), rand_perm)) mapping = dict(itertools.islice(permutation.items(), 0, size, 2)) # Drop every 2nd element. out = list(swapper.map(mapping, trials=40)) util.swap_permutation([out], mapping, allow_missing_keys=True) self.assertEqual({i: i for i in mapping.values()}, mapping)
class LayoutTransformation(TransformationPass): """ Adds a Swap circuit for a given (partial) permutation to the circuit. This circuit is found by a 4-approximation algorithm for Token Swapping. More details are available in the routing code. """ def __init__(self, coupling_map: CouplingMap, from_layout: Union[Layout, str], to_layout: Union[Layout, str], seed: Union[int, np.random.default_rng] = None, trials=4): """LayoutTransformation initializer. Args: coupling_map (CouplingMap): Directed graph representing a coupling map. from_layout (Union[Layout, str]): The starting layout of qubits onto physical qubits. If the type is str, look up `property_set` when this pass runs. to_layout (Union[Layout, str]): The final layout of qubits on phyiscal qubits. If the type is str, look up `property_set` when this pass runs. seed (Union[int, np.random.default_rng]): Seed to use for random trials. trials (int): How many randomized trials to perform, taking the best circuit as output. """ super().__init__() self.coupling_map = coupling_map self.from_layout = from_layout self.to_layout = to_layout graph = coupling_map.graph.to_undirected() self.token_swapper = ApproximateTokenSwapper(graph, seed) self.trials = trials def run(self, dag): """Apply the specified partial permutation to the circuit. Args: dag (DAGCircuit): DAG to transform the layout of. Returns: DAGCircuit: The DAG with transformed layout. Raises: TranspilerError: if the coupling map or the layout are not compatible with the DAG. Or if either of string from/to_layout is not found in `property_set`. """ if len(dag.qregs) != 1 or dag.qregs.get('q', None) is None: raise TranspilerError( 'LayoutTransform runs on physical circuits only') if len(dag.qubits) > len(self.coupling_map.physical_qubits): raise TranspilerError( 'The layout does not match the amount of qubits in the DAG') from_layout = self.from_layout if isinstance(from_layout, str): try: from_layout = self.property_set[from_layout] except Exception: raise TranspilerError( 'No {} (from_layout) in property_set.'.format(from_layout)) to_layout = self.to_layout if isinstance(to_layout, str): try: to_layout = self.property_set[to_layout] except Exception: raise TranspilerError( 'No {} (to_layout) in property_set.'.format(to_layout)) # Find the permutation between the initial physical qubits and final physical qubits. permutation = { pqubit: to_layout.get_virtual_bits()[vqubit] for vqubit, pqubit in from_layout.get_virtual_bits().items() } perm_circ = self.token_swapper.permutation_circuit( permutation, self.trials) edge_map = { vqubit: dag.qubits[pqubit] for (pqubit, vqubit) in perm_circ.inputmap.items() } dag.compose(perm_circ.circuit, edge_map=edge_map) return dag