def find_negative_cycle( graph: Graph, source: str) -> Tuple[bool, Optional[List[Graph.Edge]]]: """ Finds a negative cycle starting at source in graph. :param graph: the graph where source is :param source: the starting node of the cycle to find :return: found: True if a negative cycle exists, False otherwise cycle: the list of edges representing the cycle. It is None if found is False. """ # Step 1: Find all neighbors outgoing from source neighbors = { edge.opposite(source): edge for edge in graph.incident_edges(source, outgoing=True) } # Step 2: Find minimum edge weight in the graph minimum_edge_weight = min(edge.element() for edge in graph.edges()) # Step 3: Make all edges positive for edge in graph.edges(): edge._element += abs(minimum_edge_weight) # Step 4: Call compute_shortest_path from each vertex in neighbors to source paths = { vertex: compute_shortest_path(graph, vertex, source) for vertex in neighbors } # Step 5: Restore all edges weights for edge in graph.edges(): edge._element -= abs(minimum_edge_weight) # Step 6: Close cycles for vertex, path in paths.items(): if len(path) == 0: continue path_weight = sum(edge.element() for edge in path) first_edge = neighbors[vertex] first_weight = first_edge.element() cycle_weight = first_weight + path_weight cycle = [first_edge] + path if cycle_weight < 0: return True, cycle return False, None