def test_find_cycle_disconnected_graphs(self):
     self.graph.add_nodes_from(["A", "B", "C"])
     self.graph.add_edges_from_no_data([(10, 11), (12, 10), (11, 12)])
     res = retworkx.digraph_find_cycle(self.graph, 0)
     self.assertEqual(res, [(0, 1), (1, 2), (2, 3), (3, 0)])
     res = retworkx.digraph_find_cycle(self.graph, 10)
     self.assertEqual(res, [(10, 11), (11, 12), (12, 10)])
 def test_find_cycle_multiple_roots_same_cycles(self):
     res = retworkx.digraph_find_cycle(self.graph, 0)
     self.assertEqual(res, [(0, 1), (1, 2), (2, 3), (3, 0)])
     res = retworkx.digraph_find_cycle(self.graph, 1)
     self.assertEqual(res, [(1, 2), (2, 3), (3, 0), (0, 1)])
     res = retworkx.digraph_find_cycle(self.graph, 5)
     self.assertEqual(res, [])
 def test_find_cycle(self):
     graph = retworkx.PyDiGraph()
     graph.add_nodes_from(list(range(6)))
     graph.add_edges_from_no_data([(0, 1), (0, 3), (0, 5), (1, 2), (2, 3),
                                   (3, 4), (4, 5), (4, 0)])
     res = retworkx.digraph_find_cycle(graph, 0)
     self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], res)
 def test_self_loop(self):
     self.graph.add_edge(1, 1, None)
     res = retworkx.digraph_find_cycle(self.graph, 0)
     self.assertEqual([(1, 1)], res)
 def test_invalid_types(self):
     graph = retworkx.PyGraph()
     with self.assertRaises(TypeError):
         retworkx.digraph_find_cycle(graph)
    def _trial_map(self, digraph: rx.PyDiGraph, sub_digraph: rx.PyDiGraph,
                   todo_nodes: MutableSet[int],
                   tokens: MutableMapping[int, int]) -> Iterator[Swap[int]]:
        """Try to map the tokens to their destinations and minimize the number of swaps."""
        def swap(node0: int, node1: int) -> None:
            """Swap two nodes, maintaining data structures.

            Args:
              node0: The first node
              node1: The second node
            """
            self._swap(node0, node1, tokens, digraph, sub_digraph, todo_nodes)

        # Can't just iterate over todo_nodes, since it may change during iteration.
        steps = 0
        while todo_nodes and steps <= 4 * len(self.graph)**2:
            todo_node_id = self.seed.integers(0, len(todo_nodes))
            todo_node = tuple(todo_nodes)[todo_node_id]

            # Try to find a happy swap chain first by searching for a cycle,
            # excluding self-loops.
            # Note that if there are only unhappy swaps involving this todo_node,
            # then an unhappy swap must be performed at some point.
            # So it is not useful to globally search for all happy swap chains first.
            cycle = rx.digraph_find_cycle(sub_digraph, source=todo_node)
            if len(cycle) > 0:
                assert len(cycle) > 1, "The cycle was not happy."
                # We iterate over the cycle in reversed order, starting at the last edge.
                # The first edge is excluded.
                for edge in list(cycle)[-1:0:-1]:
                    yield edge
                    swap(edge[0], edge[1])
                steps += len(cycle) - 1
            else:
                # Try to find a node without a token to swap with.
                try:
                    edge = next(edge for edge in rx.digraph_dfs_edges(
                        sub_digraph, todo_node) if edge[1] not in tokens)
                    # Swap predecessor and successor, because successor does not have a token
                    yield edge
                    swap(edge[0], edge[1])
                    steps += 1
                except StopIteration:
                    # Unhappy swap case
                    cycle = rx.digraph_find_cycle(digraph, source=todo_node)
                    assert len(cycle) == 1, "The cycle was not unhappy."
                    unhappy_node = cycle[0][0]
                    # Find a node that wants to swap with this node.
                    try:
                        predecessor = next(
                            predecessor
                            for predecessor in digraph.predecessor_indices(
                                unhappy_node) if predecessor != unhappy_node)
                    except StopIteration:
                        logger.error(
                            "Unexpected StopIteration raised when getting predecessors"
                            "in unhappy swap case.")
                        return
                    yield unhappy_node, predecessor
                    swap(unhappy_node, predecessor)
                    steps += 1
        if todo_nodes:
            raise RuntimeError(
                "Too many iterations while approximating the Token Swaps.")