Resolve cycles in a dependency graph by identifying strongly connected components and breaking cycles. Args: graph: A dependency graph represented as adjacency lists (node -> set of dependencies) Returns: A new acyclic graph with the same nod
(graph: Dict[str, Set[str]])
| 76 | # Visit each node |
| 77 | for node in graph: |
| 78 | if node not in index: |
| 79 | strongconnect(node) |
| 80 | |
| 81 | return result |
| 82 | |
| 83 | |
| 84 | def resolve_cycles(graph: dict[str, set[str]]) -> dict[str, set[str]]: |
| 85 | """ |
| 86 | Resolve cycles in a dependency graph by identifying strongly connected |
| 87 | components and breaking cycles. |
| 88 | |
| 89 | Args: |
| 90 | graph: A dependency graph represented as adjacency lists |
| 91 | (node -> set of dependencies) |
| 92 | |
| 93 | Returns: |
| 94 | A new acyclic graph with the same nodes but with cycles broken |
| 95 | """ |
| 96 | # Detect cycles (SCCs) |
| 97 | cycles = detect_cycles(graph) |
| 98 | |
| 99 | if not cycles: |
| 100 | logger.debug("No cycles detected in the dependency graph") |
| 101 | return graph |
| 102 | |
| 103 | logger.debug(f"Detected {len(cycles)} cycles in the dependency graph") |
| 104 | |
| 105 | # Create a copy of the graph to modify |
| 106 | new_graph = {node: deps.copy() for node, deps in graph.items()} |
| 107 | |
| 108 | # Process each cycle |
| 109 | for i, cycle in enumerate(cycles): |
| 110 | logger.debug(f"Cycle {i + 1}: {' -> '.join(cycle)}") |
| 111 | |
| 112 | # Strategy: Break the cycle by removing the "weakest" dependency |
| 113 | # Here, we just arbitrarily remove the last edge to make the graph acyclic |
| 114 | # In a real-world scenario, you might use heuristics to determine which edge to break |
| 115 | # For example, removing edges between different modules before edges within the same module |
| 116 | for j in range(len(cycle) - 1): |
| 117 | current = cycle[j] |
| 118 | next_node = cycle[j + 1] |
| 119 | |
| 120 | if next_node in new_graph[current]: |
| 121 | logger.debug(f"Breaking cycle by removing dependency: {current} -> {next_node}") |
| 122 | new_graph[current].remove(next_node) |
no test coverage detected