use super::Adjacency;
pub use crate::community::{louvain, modularity, Communities};
#[must_use]
pub fn label_propagation(g: &Adjacency, max_iters: usize) -> Communities {
let n = g.n;
let mut label: Vec<usize> = (0..n).collect();
for _ in 0..max_iters.max(1) {
let mut changed = false;
for i in 0..n {
if g.und[i].is_empty() {
continue;
}
let mut tally: std::collections::BTreeMap<usize, f32> = std::collections::BTreeMap::new();
for &(j, w) in &g.und[i] {
*tally.entry(label[j]).or_insert(0.0) += w;
}
let mut best = label[i];
let mut best_w = tally.get(&label[i]).copied().unwrap_or(0.0);
for (&lab, &w) in &tally {
if w > best_w + 1e-9 {
best_w = w;
best = lab;
}
}
if best != label[i] {
label[i] = best;
changed = true;
}
}
if !changed {
break;
}
}
finalize(n, label, g)
}
fn finalize(n: usize, label: Vec<usize>, g: &Adjacency) -> Communities {
let mut map = std::collections::HashMap::new();
let mut of = Vec::with_capacity(n);
let mut count = 0;
for &l in &label {
let id = *map.entry(l).or_insert_with(|| {
let id = count;
count += 1;
id
});
of.push(id);
}
let mut edges = Vec::new();
for i in 0..n {
for &(j, _) in &g.und[i] {
if i < j {
edges.push((i, j));
}
}
}
let q = modularity(n, &edges, &of);
Communities { of, count, modularity: q }
}
#[cfg(test)]
mod tests {
use super::*;
fn two_triangles() -> Adjacency {
Adjacency::from_edges(6, &[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3)])
}
#[test]
fn label_propagation_splits_the_two_triangles() {
let g = two_triangles();
let c = label_propagation(&g, 100);
assert_eq!(c.of[0], c.of[1]);
assert_eq!(c.of[1], c.of[2]);
assert_eq!(c.of[3], c.of[4]);
assert_eq!(c.of[4], c.of[5]);
assert_ne!(c.of[0], c.of[3], "the two triangles are distinct communities");
assert!(c.modularity > 0.3, "a clear split has positive modularity ({})", c.modularity);
}
#[test]
fn louvain_reexport_agrees_on_the_split() {
let edges = vec![(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)];
let c = louvain(6, &edges);
assert_eq!(c.of[0], c.of[1]);
assert_ne!(c.of[0], c.of[3]);
assert!(c.modularity > 0.3);
}
#[test]
fn edgeless_graph_is_all_singletons() {
let g = Adjacency::from_edges(4, &[]);
let c = label_propagation(&g, 10);
assert_eq!(c.count, 4);
}
}