diff --git a/src/graphs/__init__.py b/src/graphs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/graphs/algorithms.py b/src/graphs/algorithms.py new file mode 100644 index 00000000..2272565d --- /dev/null +++ b/src/graphs/algorithms.py @@ -0,0 +1,40 @@ +""" +Module that contains useful methods for traversing graphs +""" +from typing import Tuple, List +import networkx as nx + + +def condense_to_dag(g: nx.Graph) -> Tuple[nx.DiGraph, List[List]]: + """ + Given a graph g, returns another graph such that all the nodes that form a cycle in + the original graph are condense into a single node. + + Returns + ------- + dag: tuple + a tuple with the directed graph that subsumes the new information and + a list with the corresponding nodes in the original graph for each connected component + """ + + # Get the strongly connected components (SCCs) + sccs = list(nx.strongly_connected_components(g)) + + # Create a new directed graph for the DAG + dag = nx.DiGraph() + + # Map each node to its SCC + scc_map = {} + for i, scc in enumerate(sccs): + for node in scc: + scc_map[node] = i + + # Add nodes to the DAG, each node represents an SCC + dag.add_nodes_from(range(len(sccs))) + + # Add edges between SCCs to form the DAG + for u, v in g.edges(): + if scc_map[u] != scc_map[v]: + dag.add_edge(scc_map[u], scc_map[v]) + + return dag, sccs diff --git a/src/liveness/liveness_analysis.py b/src/liveness/liveness_analysis.py index 2fc0a2e2..13fca206 100644 --- a/src/liveness/liveness_analysis.py +++ b/src/liveness/liveness_analysis.py @@ -7,6 +7,7 @@ from analysis.fixpoint_analysis import BlockAnalysisInfo, BackwardsAnalysis from analysis.abstract_state import digraph_from_block_info from liveness.liveness_state import LivenessState, LivenessBlockInfo +from graphs.algorithms import condense_to_dag from parser.cfg_block_list import CFGBlockList from parser.cfg import CFG @@ -137,4 +138,15 @@ def dot_from_analysis(cfg: CFG, final_dir: Path = Path(".")) -> Dict[str, Dict[s renamed_digraph = nx.relabel_nodes(digraph, renaming_dict) nx.nx_agraph.write_dot(renamed_digraph, final_dir.joinpath(f"{component_name}.dot")) + + condensed_digraph, sccs = condense_to_dag(renamed_digraph) + + # We just consider the first part of the dot representation ("Block {i}:") + renamed_condensed_dict = {i: '\n'.join(sorted([dot_repr.splitlines()[0] for dot_repr in dot_repr_from_variables])) + for i, dot_repr_from_variables in enumerate(sccs)} + + renamed_condensed_digraph = nx.relabel_nodes(condensed_digraph, renamed_condensed_dict) + + nx.nx_agraph.write_dot(renamed_condensed_digraph, final_dir.joinpath(f"{component_name}_condensed.dot")) + return results