From bad832f22503656f3e95526fb53df25d566f38f5 Mon Sep 17 00:00:00 2001 From: safishamsi Date: Fri, 28 Aug 2026 01:19:45 +0100 Subject: [PATCH] test(cluster): pin partition invariance to edge-endpoint orientation (#3146) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build the same logical undirected graph with forward vs flipped edge endpoints and shuffled node insertion, and assert _partition yields identical communities — guarding the canonical edge-ordering the determinism fix introduced. --- tests/test_cluster.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_cluster.py b/tests/test_cluster.py index f5875bffc..fa4d0a265 100644 --- a/tests/test_cluster.py +++ b/tests/test_cluster.py @@ -145,3 +145,35 @@ def test_native_leiden_returns_none_when_binding_absent(monkeypatch): stable = nx.Graph() stable.add_edge("x", "y") assert cl._native_leiden(stable, 1.0) is None + + +def test_partition_is_invariant_to_edge_endpoint_orientation(): + """#3146: for an undirected graph, (a,b) and (b,a) are the same edge, but the + orientation networkx yields can vary across builds/machines. _partition must + canonicalise endpoints so the ordering fed to the clusterer — and thus the + resulting communities — is identical regardless of how edges were inserted.""" + import random + edges = [ + ("a1", "a2"), ("a1", "a3"), ("a2", "a3"), ("a3", "a4"), + ("b1", "b2"), ("b1", "b3"), ("b2", "b3"), ("b3", "b4"), + ("a1", "b1"), + ] + + def build(order, flip): + G = nx.Graph() + for n in order: + G.add_node(n) + for (u, v) in edges: + G.add_edge(v, u) if flip else G.add_edge(u, v) + return G + + nodes = sorted({n for e in edges for n in e}) + forward = build(nodes, flip=False) + shuffled = list(nodes) + random.Random(0).shuffle(shuffled) + flipped = build(shuffled, flip=True) + + from graphify.cluster import _partition + assert _grouping(_partition(forward, 1.0)) == _grouping(_partition(flipped, 1.0)), ( + "partition drifted with edge-endpoint orientation / insertion order" + )