Conversation
…r composite cartel detection) Simple label-propagation community detector. Each node starts in its own community; each iteration, every node adopts the label with greatest weighted neighbor-vote (ties broken by lowest community id for determinism). Stops when no label changes or maxIterations reached. Surface: Graph.labelPropagation : int -> Graph<'N> -> Map<'N, int> Trade-offs (documented in XML-doc): - Fast: O(iterations × edges), no dense-matrix. - Quality: below Louvain on complex structures; catches obvious dense cliques reliably (exactly the trivial-cartel-detect case). - Determinism: tie-break by lowest id. - NOT a replacement for Louvain; dependency-free first pass. Composes with modularityScore (PR #324): LP produces partition, modularity evaluates it. Full end-to-end pattern verified in test labelPropagation produces partition consumable by modularityScore — two K3 cliques bridged thin → Q > 0.3. Tests (3 new, 31 total in GraphTests, all passing): - Empty graph -> empty map - Two K3 cliques converge to two labels (nodes within a clique share label) - LP partition consumed by modularityScore yields Q > 0.3 (cartel-detection pipeline correctness) Amara Otto-132 17th-ferry observation: her proposed 'CoordinationRiskScore' combines λ₁ + ΔQ + covariance + sync + exclusivity + influence. This graduation ships the ΔQ prerequisite (partition from LP + Q from modularityScore). Remaining primitives queued for future graduations per Otto-105 cadence. Build: 0 Warning / 0 Error. Provenance: - 12th ferry §5 + 13th ferry §2 + 14th ferry alert row - Implementation: Otto (13th graduation) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
Adds a simple label-propagation community detector to Zeta.Core.Graph and verifies it composes with Graph.modularityScore for a toy “two cliques + bridge” scenario.
Changes:
- Introduce
Graph.labelPropagation : int -> Graph<'N> -> Map<'N,int>for deterministic label-propagation community detection. - Add xUnit/FsUnit tests covering empty-graph behavior, clique separation, and modularity scoring of the produced partition.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Core/Graph.fs | Adds Graph.labelPropagation implementation and API documentation. |
| tests/Tests.FSharp/Algebra/Graph.Tests.fs | Adds tests validating label propagation behavior and its composition with modularity scoring. |
| let (s, t) = entry.Key | ||
| let si = idx.[s] | ||
| let ti = idx.[t] | ||
| if entry.Weight <> 0L && si <> ti then |
There was a problem hiding this comment.
P0: Graph<'N> supports signed edge weights (retraction-native), but labelPropagation currently adds neighbors even when entry.Weight is negative. Because negative weights are later treated as 0 votes, nodes connected only via negative edges can still change label arbitrarily (all votes tie at 0, then tie-break picks lowest label). Consider filtering neighbors to entry.Weight > 0L (or otherwise handling signed weights explicitly) so anti-edges don’t influence community assignment unexpectedly.
| if entry.Weight <> 0L && si <> ti then | |
| if entry.Weight > 0L && si <> ti then |
| /// Provenance: 12th ferry §5 + 13th ferry §2 "community | ||
| /// detection" + 14th ferry alert row "Modularity Q jump > | ||
| /// 0.1 or Q > 0.4 (community-detection-based)". |
There was a problem hiding this comment.
The doc comment’s provenance references (“12th ferry §5”, “13th ferry §2”, “14th ferry alert row …”) don’t appear to resolve to any docs in-tree (searching docs/**/*.md only finds generic ferry mentions in the ADR, not these sections). Please either link to a concrete file path/anchor that exists in the repo, or remove/adjust the references so readers can actually follow them.
| /// Provenance: 12th ferry §5 + 13th ferry §2 "community | |
| /// detection" + 14th ferry alert row "Modularity Q jump > | |
| /// 0.1 or Q > 0.4 (community-detection-based)". | |
| /// Intended as a simple first-pass community-detection | |
| /// primitive for obvious dense-clique cases and modularity- | |
| /// oriented alerting heuristics. |
| let labelA = partition.[1] | ||
| let labelB = partition.[4] | ||
| // Both cliques share a label within themselves | ||
| partition.[2] |> should equal labelA | ||
| partition.[3] |> should equal labelA | ||
| partition.[5] |> should equal labelB | ||
| partition.[6] |> should equal labelB |
There was a problem hiding this comment.
This test claims “two labels” but never asserts that labelA and labelB differ. As written, it would still pass if label propagation collapses the whole graph into a single community. Add an explicit assertion that labelA <> labelB (or equivalent) to ensure the intended behavior is actually verified.
… detector) (#328) First full integration of the Graph detection pipeline: combines largestEigenvalue (spectral growth) + labelPropagation (community partition) + modularityScore (partition evaluation) into a single scalar risk score. Surface: Graph.coordinationRiskScore (alpha: double) (beta: double) (eigenTol: double) (eigenIter: int) (lpIter: int) (baseline: Graph<'N>) (attacked: Graph<'N>) : double option Composite formula (MVP): risk = alpha * Δλ₁_rel + beta * ΔQ where: - Δλ₁_rel = (λ₁(attacked) - λ₁(baseline)) / max(λ₁(baseline), eps) - ΔQ = Q(attacked, LP(attacked)) - Q(baseline, LP(baseline)) Both signals fire when a dense subgraph is injected: λ₁ grows because the cartel adjacency has high leading eigenvalue; Q grows because LP finds the cartel as its own community and Newman Q evaluates that partition highly. Weight defaults per Amara 17th-ferry initial priors: - alpha = 0.5 spectral growth - beta = 0.5 modularity shift Tests (3 new, 34 total in GraphTests, all passing): - Empty graphs -> None - Cartel injection -> composite > 1.0 (both signals fire) - attacked == baseline -> composite near 0 (|score| < 0.2) Calibration deferred (Amara Otto-132 Part 2 correction #4 — robust statistics via median + MAD): this MVP uses raw linear weighting over differences. Full CoordinationRiskScore with robust z-scores over baseline null-distribution is a future graduation once baseline-calibration machinery ships. RobustStats.robustAggregate (PR #295) already provides the median-MAD machinery; just needs a calibration harness to use it. 14th graduation under Otto-105 cadence. First full integration ship using 4 Graph primitives composed together (λ₁ + LP + modularity + composer). Build: 0 Warning / 0 Error. Provenance: - Concept: Aaron (firefly network + trivial-cartel-detect) + Amara's composite-score formulations across 12th/13th/14th/ 17th ferries - Implementation: Otto (14th graduation) Composes with: - Graph.largestEigenvalue (PR #321) - Graph.labelPropagation (PR #326) - Graph.modularityScore (PR #324) - RobustStats.robustAggregate (PR #295) — for future robust variant Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
…ns tracked; 3 already shipped) (#330) * ferry: Amara 17th absorb — Cartel-Lab Implementation Closure + 5.5 Verification (8 corrections tracked) Two-part ferry: Amara's deep-research Implementation Closure for Cartel-Lab + her own GPT-5.5 Thinking verification pass with 8 load-bearing corrections. Otto correction-pass status (all 8 tracked): 1. λ₁(K₃) = 2 — ALREADY CORRECT PR #321 Otto-127 (independent convergence before verification arrived) 2. Modularity relational-not-absolute — ALREADY CORRECT PR #324 Otto-128 (caught mid-tick via hand-calc) 3. Cohesion/Exclusivity/Conductance replace entropy-collapse — SHIPPED PR #329 Otto-135 (3 primitives + 6 tests) 4. Windowed stake covariance acceleration — FUTURE GRADUATION 5. Event-stream → phase pipeline for PLV — FUTURE GRADUATION 6. 'ZSet invertible' → 'deltas support retractions' — ADR ALREADY PHRASED CORRECTLY (PR #316 never claimed full invertibility) 7. KSK 'contract' → 'policy layer' — FILED BACKLOG PR #318 Otto-124 (Max coord pending) 8. SOTA humility — DOC PHRASING (applied in new absorb docs) Amara's proposed 3-PR split NOT adopted (Otto-105 small- graduation cadence; content delivered across 7 ticks instead: PRs #317, #321, #323, #324, #326, #328, #329). Amara's proposed /cartel-lab/ folder NOT adopted (Otto-108 Conway's-Law: single-module-tree until interfaces harden). Current Graph.fs + test-support split works. Aaron's SharderInfoTheoreticTests flake flag (trailing Otto-132 note) filed as BACKLOG PR #327 Otto-133 — unrelated hygiene item. Amara's Otto-136 follow-up note: '#323 conceptually accepted, do not canonicalize until sharder test is seed-locked/ recalibrated'. Acknowledged — #323 lives in tests/Simulation/ already (test-scoped); 'canonicalize' = future promotion to src/Core/NetworkIntegrity/ per Amara's PR #3 split suggestion; that's gated on #327 completion. §33 archive header compliance. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * lint: fix line-start PR-number header false-positive in 17th-ferry absorb --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Simple label-propagation community detector. Composes with modularityScore: LP produces partition, Q evaluates it. End-to-end test: two K3 cliques bridged thin → Q > 0.3. 31 GraphTests passing.