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.")