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)
Exemplo n.º 2
0
    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)
Exemplo n.º 9
0
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