facett_graphview/analysis/
community.rs1use super::Adjacency;
7
8pub use crate::community::{louvain, modularity, Communities};
10
11#[must_use]
19pub fn label_propagation(g: &Adjacency, max_iters: usize) -> Communities {
20 let n = g.n;
21 let mut label: Vec<usize> = (0..n).collect();
22 for _ in 0..max_iters.max(1) {
23 let mut changed = false;
24 for i in 0..n {
26 if g.und[i].is_empty() {
27 continue;
28 }
29 let mut tally: std::collections::BTreeMap<usize, f32> = std::collections::BTreeMap::new();
31 for &(j, w) in &g.und[i] {
32 *tally.entry(label[j]).or_insert(0.0) += w;
33 }
34 let mut best = label[i];
35 let mut best_w = tally.get(&label[i]).copied().unwrap_or(0.0);
36 for (&lab, &w) in &tally {
37 if w > best_w + 1e-9 {
38 best_w = w;
39 best = lab;
40 }
41 }
42 if best != label[i] {
43 label[i] = best;
44 changed = true;
45 }
46 }
47 if !changed {
48 break;
49 }
50 }
51 finalize(n, label, g)
52}
53
54fn finalize(n: usize, label: Vec<usize>, g: &Adjacency) -> Communities {
56 let mut map = std::collections::HashMap::new();
57 let mut of = Vec::with_capacity(n);
58 let mut count = 0;
59 for &l in &label {
60 let id = *map.entry(l).or_insert_with(|| {
61 let id = count;
62 count += 1;
63 id
64 });
65 of.push(id);
66 }
67 let mut edges = Vec::new();
69 for i in 0..n {
70 for &(j, _) in &g.und[i] {
71 if i < j {
72 edges.push((i, j));
73 }
74 }
75 }
76 let q = modularity(n, &edges, &of);
77 Communities { of, count, modularity: q }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 fn two_triangles() -> Adjacency {
88 Adjacency::from_edges(6, &[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3)])
89 }
90
91 #[test]
92 fn label_propagation_splits_the_two_triangles() {
93 let g = two_triangles();
94 let c = label_propagation(&g, 100);
95 assert_eq!(c.of[0], c.of[1]);
97 assert_eq!(c.of[1], c.of[2]);
98 assert_eq!(c.of[3], c.of[4]);
99 assert_eq!(c.of[4], c.of[5]);
100 assert_ne!(c.of[0], c.of[3], "the two triangles are distinct communities");
101 assert!(c.modularity > 0.3, "a clear split has positive modularity ({})", c.modularity);
102 }
103
104 #[test]
105 fn louvain_reexport_agrees_on_the_split() {
106 let edges = vec![(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)];
108 let c = louvain(6, &edges);
109 assert_eq!(c.of[0], c.of[1]);
110 assert_ne!(c.of[0], c.of[3]);
111 assert!(c.modularity > 0.3);
112 }
113
114 #[test]
115 fn edgeless_graph_is_all_singletons() {
116 let g = Adjacency::from_edges(4, &[]);
117 let c = label_propagation(&g, 10);
118 assert_eq!(c.count, 4);
119 }
120}