Skip to content

feat(graph): connected-components split + O(E) modularity + deterministic detection (mache-e1fa1f, mache-ff7e31)#492

Merged
jamestexas merged 3 commits into
mainfrom
feat/community-connected-split
Jul 8, 2026
Merged

feat(graph): connected-components split + O(E) modularity + deterministic detection (mache-e1fa1f, mache-ff7e31)#492
jamestexas merged 3 commits into
mainfrom
feat/community-connected-split

Conversation

@jamestexas

Copy link
Copy Markdown
Contributor

What

DetectCommunities (Phase-1-only local-moving Louvain) can, per Traag et al. 2019, emit internally-disconnected communities — a hazard because the community is the sheaf cache-invalidation unit. Defensive fix + regression gate: 0 such communities on today's dense token projection (it masks the pathology), but it becomes load-bearing when the graph sparsifies under the code-fact IR.

  • splitDisconnectedCommunities — member-gated connected-components pass between local-moving and result collection. Relabels any community whose induced subgraph (in-community edges only) has >1 component into one community per component. Split-only (never merges) → monotonically finer + deterministic. Not full Leiden (its aggregation coarsens + its randomization would thrash the deterministic sheaf cache key).
  • computeModularity O(N²) → O(E)Q = Σ_c[inW_c/m2 − (commDegree_c/m2)²], one adjacency scan; recomputed on the post-split partition.
  • cmd/pack.go — dropped the 5000-ref guardrail (detection is near-linear now; the O(N²) modularity was its only reason).

Correctness — reviewed by the theoretical-foundations-analyst (clean)

  • Modularity identity provably exact. Verified m2 = totalWeight = 2m (no factor-of-2 slip), inW_c = ordered double-sum pairing correctly with 1/2m, and the algebra from the canonical Q = (1/2m)Σ_ij[A_ij − k_ik_j/2m]δ to the O(E) form. New ≡ old ≡ canonical. The parity test provably catches a factor-2 error (all-one partition → −2 vs 0).
  • Split complete + correct — in-community edges only, all components found, fresh non-colliding IDs, monotonically finer.
  • Determinism where it matters — split labels deterministic (sorted seeds); the only nondeterminism is the modularity float's last ULP, which is displayed, never hashed. The sheaf key is the partition (Membership), which is deterministic.

Tests

Invariant (0 communities span >1 connected component), planted-disconnection barbell split, O(E)==O(N²) modularity parity across weighted/asymmetric/edgeless fixtures. Local gate green: gofumpt / go vet / golangci-lint (pre-commit) / go test -race ./internal/graph/ all pass.

Follow-up (not this PR)

Pre-existing pipeline nondeterminism (buildProjection index order, Louvain tie-breaking, non-stable sort.Slice by size) can swap equal-sized community IDs across independent runs — harmless today, but relevant if the partition is ever content-addressed (the CAS/merkle direction). Worth a separate bead.

Bead mache-e1fa1f. Draft — no auto-merge.

🤖 Generated with Claude Code

…E) modularity

DetectCommunities (Phase-1 Louvain) can emit internally-disconnected
communities (Traag et al. 2019) — a hazard for the sheaf cache-invalidation
unit. Defensive fix (0 such communities on today's dense token projection;
becomes load-bearing when the graph sparsifies under the code-fact IR).

- splitDisconnectedCommunities: member-gated connected-components pass
  between local-moving and result collection; relabels any community whose
  induced subgraph has >1 component into one community per component.
  Split-only (never merges) → monotonically finer + deterministic (no Leiden
  aggregation/randomization that would thrash the sheaf cache key).
- computeModularity rewritten O(N^2) -> O(E): Q = sum_c[inW_c/m2 -
  (commDegree_c/m2)^2], one adj scan; recomputed AFTER the split.
- cmd/pack.go: dropped the 5000-ref guardrail (detection is near-linear now).
- Tests: invariant (0 communities span >1 component), planted-disconnection
  barbell split, O(E)==O(N^2) modularity parity on small graphs.

Local gate green: gofumpt clean, go vet clean, internal/graph + cmd Pack pass.
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

find_smells (advisory)

Scoped to files changed in this PR. Rules below run on the standalone (no-LLO) cross-ref tables; _ast rules (cyclomatic_complexity, long_function, long_file, magic_int_in_comparison) are not exercised here.

duplicate_definitions — 2 finding(s) in changed files
Source Node Metric
cmd/pack.go cmd/pack.go/function_declaration_4/block/statement_list/type_declaration_0/type_spec 2
cmd/pack.go cmd/pack.go/function_declaration_4/block/statement_list/type_declaration_1/type_spec 2

Rules: the registry is cmd/smell_rules.go (source of truth); see the authoring guide to add one. Advisory only — these are heuristics, not gates.

Three pre-existing map-iteration-order dependencies made DetectCommunities
non-deterministic run-to-run on identical input (flagged reviewing #492).
Harmless while the partition is only compared as a set, but load-bearing once
it is content-addressed (the CAS/merkle direction).

- buildProjection: assign node indices iterating tokens in SORTED order, so
  node→index (which the whole partition is keyed on) is a pure function of refs.
- phase-1: iterate candidate communities sorted with strict `>`, so ΔQ ties
  break deterministically (lowest community index) not by map order.
- final sort: total order (size desc, then first-member lexicographic) so
  equal-sized communities get stable, input-order-independent IDs.

Test: 40× DetectCommunities on an equal-sized-communities graph → byte-identical
IDs + Membership every run. Existing split/modularity/community tests green.
@jamestexas jamestexas changed the title feat(graph): connected-components community split + O(E) modularity (mache-e1fa1f) feat(graph): connected-components split + O(E) modularity + deterministic detection (mache-e1fa1f, mache-ff7e31) Jul 7, 2026
@jamestexas

Copy link
Copy Markdown
Contributor Author

Added ad475ea (mache-ff7e31): makes DetectCommunities deterministic — the follow-up the math-friend flagged. Three map-iteration-order dependencies fixed: (1) buildProjection node indexing now iterates tokens sorted; (2) phase-1 ΔQ ties break deterministically (sorted candidates, lowest index wins); (3) final sort is a total order (size desc, then first-member) so equal-sized communities get stable IDs. New test runs detection 40× on an equal-sized-communities graph and asserts byte-identical IDs + Membership. This closes the 'partition safe to content-address' gap ahead of the CAS/merkle direction. Full local gate (gofumpt/vet/golangci-lint/-race) green.

@jamestexas jamestexas marked this pull request as ready for review July 8, 2026 23:08
@jamestexas jamestexas merged commit 1624242 into main Jul 8, 2026
13 of 15 checks passed
@jamestexas jamestexas deleted the feat/community-connected-split branch July 8, 2026 23:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant