stabilize community IDs so identical groupings get identical labels (#1090)

Follow-up to the file-ordering fix. The from-scratch build writes each node's
community field straight from cluster()'s enumerate() after a STABLE size-sort,
so the hundreds of equal-sized small communities in a sparse graph were ordered
by the partitioner's (not seed-stable) enumeration order. Their integer IDs
permuted run-to-run, which reads as 77-88% "community churn" in a per-node cid
diff even though the actual grouping is reproducible.

Add a tuple(sorted(nodes)) tiebreak to make the sort a total order, so an
identical grouping always yields identical community IDs. Verified: with the
partition returned in shuffled order across 5 runs, the node->cid map is now
identical. (A separate ~0.06% community-count drift remains - likely
non-canonical edge weights upstream - tracked separately.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Safi
2026-06-01 21:54:11 +01:00
co-authored by Claude Opus 4.8
parent 1d4e11226f
commit f5f3a1cc59
+7 -2
View File
@@ -178,8 +178,13 @@ def cluster(
second_pass.append(nodes)
final_communities = second_pass
# Re-index by size descending for deterministic ordering
final_communities.sort(key=len, reverse=True)
# Re-index by size descending. The tuple(sorted(nodes)) tiebreak makes this a
# TOTAL order, so an identical grouping always gets identical community IDs.
# Without it, the hundreds of equal-sized small communities are ordered by the
# partitioner's (not seed-stable) enumeration order, so their integer IDs
# permute run-to-run - which reads as massive "community churn" in a per-node
# cid diff even though the actual grouping is reproducible (#1090 follow-up).
final_communities.sort(key=lambda nodes: (-len(nodes), tuple(sorted(map(str, nodes)))))
return {i: sorted(nodes) for i, nodes in enumerate(final_communities)}