From a4a475c8b65fec4e29a6d0e9d00ececcd02e90c7 Mon Sep 17 00:00:00 2001 From: balloon72 <96562725+balloon72@users.noreply.github.com> Date: Mon, 18 May 2026 19:05:21 +0800 Subject: [PATCH] perf(analyze): reuse degrees for surprise scoring (#914) Co-authored-by: hanmo1 --- graphify/analyze.py | 8 +++++--- tests/test_analyze.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/graphify/analyze.py b/graphify/analyze.py index b72f401f..fd94c0ca 100644 --- a/graphify/analyze.py +++ b/graphify/analyze.py @@ -182,6 +182,7 @@ def _surprise_score( node_community: dict[str, int], u_source: str, v_source: str, + degrees: dict[str, int] | None = None, ) -> tuple[int, list[str]]: """Score how surprising a cross-file edge is. Returns (score, reasons).""" score = 0 @@ -236,8 +237,8 @@ def _surprise_score( reasons.append("semantically similar concepts with no structural link") # 5. Peripheral→hub: a low-degree node connecting to a high-degree one - deg_u = G.degree(u) - deg_v = G.degree(v) + deg_u = degrees[u] if degrees is not None else G.degree(u) + deg_v = degrees[v] if degrees is not None else G.degree(v) if min(deg_u, deg_v) <= 2 and max(deg_u, deg_v) >= 5: score += 1 peripheral = G.nodes[u].get("label", u) if deg_u <= 2 else G.nodes[v].get("label", v) @@ -262,6 +263,7 @@ def _cross_file_surprises(G: nx.Graph, communities: dict[int, list[str]], top_n: Each result includes a 'why' field explaining what makes it non-obvious. """ node_community = _node_community_map(communities) + degrees = dict(G.degree()) candidates = [] for u, v, data in G.edges(data=True): @@ -279,7 +281,7 @@ def _cross_file_surprises(G: nx.Graph, communities: dict[int, list[str]], top_n: if not u_source or not v_source or u_source == v_source: continue - score, reasons = _surprise_score(G, u, v, data, node_community, u_source, v_source) + score, reasons = _surprise_score(G, u, v, data, node_community, u_source, v_source, degrees) src_id = data.get("_src", u) if src_id not in G.nodes: src_id = u diff --git a/tests/test_analyze.py b/tests/test_analyze.py index d24b617d..e0608976 100644 --- a/tests/test_analyze.py +++ b/tests/test_analyze.py @@ -105,6 +105,27 @@ def test_surprising_connections_ambiguous_scores_higher_than_extracted(): assert score_amb > score_ext +def test_surprise_score_accepts_precomputed_degrees(): + G = nx.Graph() + for nid, label, src in [ + ("hub", "Hub", "repo1/hub.py"), + ("leaf", "Leaf", "repo2/leaf.py"), + ("n1", "N1", "repo1/n1.py"), + ("n2", "N2", "repo1/n2.py"), + ("n3", "N3", "repo1/n3.py"), + ("n4", "N4", "repo1/n4.py"), + ]: + G.add_node(nid, label=label, source_file=src, file_type="code") + for node in ("leaf", "n1", "n2", "n3", "n4"): + G.add_edge("hub", node, relation="calls", confidence="EXTRACTED", weight=1.0) + + nc = {"hub": 0, "leaf": 1} + edge = G.edges["hub", "leaf"] + args = (G, "hub", "leaf", edge, nc, "repo1/hub.py", "repo2/leaf.py") + + assert _surprise_score(*args) == _surprise_score(*args, dict(G.degree())) + + def test_surprising_connections_cross_type_scores_higher(): """Code↔paper edge should score higher than code↔code edge.""" G = nx.Graph()