-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcalculate_statistics.py
More file actions
121 lines (95 loc) · 4.26 KB
/
Copy pathcalculate_statistics.py
File metadata and controls
121 lines (95 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/env python3
import argparse
import networkx as nx
def load_graph(path: str) -> nx.DiGraph:
"""
Load a directed include graph from a GraphML file.
"""
try:
G = nx.read_graphml(path)
except Exception as e:
print(f"Error loading GraphML file: {e}")
raise
return G
def compute_statistics(G: nx.DiGraph) -> dict:
"""
Compute various statistics on the include graph.
"""
stats = {}
# Node and edge count
print("Calculating basic metrics (nodes, edges)...")
stats['num_nodes'] = G.number_of_nodes()
stats['num_edges'] = G.number_of_edges()
# Degree metrics
print("Calculating degree metrics (in/out degrees)...")
in_degrees = dict(G.in_degree())
out_degrees = dict(G.out_degree())
stats['max_in_degree'] = max(in_degrees.values()) if in_degrees else 0
stats['max_out_degree'] = max(out_degrees.values()) if out_degrees else 0
stats['top_included_files'] = sorted(in_degrees.items(), key=lambda x: x[1], reverse=True)[:10]
stats['most_including_files'] = sorted(out_degrees.items(), key=lambda x: x[1], reverse=True)[:10]
# Degree centrality
print("Calculating degree centrality...")
stats['degree_centrality'] = nx.degree_centrality(G)
stats['top_degree_centrality'] = sorted(stats['degree_centrality'].items(), key=lambda x: x[1], reverse=True)[:10]
# Connected components and cycles
print("Finding strongly connected components...")
scc = list(nx.strongly_connected_components(G))
stats['num_strongly_connected_components'] = len(scc)
stats['largest_strongly_connected_component_size'] = max((len(c) for c in scc), default=0)
for c in scc:
if len(c) == stats['largest_strongly_connected_component_size']:
stats['largest_strongly_connected_component_nodes'] = []
for n in c:
stats['largest_strongly_connected_component_nodes'].append(G.nodes[n]['file'])
break
# Detect simple cycles
print("Finding simple cycles...")
try:
cycles = list(nx.simple_cycles(G))
stats['num_cycles'] = len(cycles)
except nx.NetworkXNoCycle:
stats['num_cycles'] = 0
# Average clustering coefficient
print("Calculating average directed clustering coefficient...")
stats['average_clustering'] = nx.average_clustering(G)
print("Statistics calculation complete.")
return stats
def print_statistics(stats: dict, G: nx.DiGraph):
"""
Print the computed statistics.
"""
def fname(node_id):
return G.nodes[node_id].get('file', node_id)
print("Graph Statistics:")
print(f"- Number of nodes: {stats['num_nodes']}")
print(f"- Number of edges: {stats['num_edges']}")
print(f"- Maximum in-degree (most included): {stats['max_in_degree']}")
print(f"- Maximum out-degree (most including): {stats['max_out_degree']}")
print("\nTop 10 most included files (in-degree):")
for node, deg in stats['top_included_files']:
print(f" {fname(node)}: {deg}")
print("\nTop 10 most including files (out-degree):")
for node, deg in stats['most_including_files']:
print(f" {fname(node)}: {deg}")
print("\nTop 10 nodes by degree centrality:")
for node, cent in stats['top_degree_centrality']:
print(f" {fname(node)}: {cent:.4f}")
print(f"\nNumber of strongly connected components: {stats['num_strongly_connected_components']}")
print(f"Size of largest strongly connected component: {stats['largest_strongly_connected_component_size']}")
print(f"Largest strongly connected component nodes: ")
for f in stats['largest_strongly_connected_component_nodes']:
print(f" {f}")
print(f"Number of simple cycles: {stats['num_cycles']}")
print(f"\nAverage clustering coefficient: {stats['average_clustering']:.4f}")
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="Analyze a C/C++ include graph stored in GraphML format.")
parser.add_argument("graphml",
help="Path to the GraphML file representing the include graph")
args = parser.parse_args()
print(f"Loading graph from {args.graphml}...")
G = load_graph(args.graphml)
print("Calculating statistics...")
stats = compute_statistics(G)
print_statistics(stats, G)