Chapter 18: Graph Algorithms
Contract:
apr-book-ch18
Run: cargo run -p aprender-core --example ch18_graphs
#![allow(clippy::disallowed_methods)]
//! Chapter 18: Graph Algorithms
//!
//! Demonstrates graph construction, Dijkstra, community detection, metrics.
//! Citation: Blondel et al., "Fast Unfolding of Communities," arXiv:0803.0476
//! Contract: contracts/apr-book-ch18-v1.yaml (v2 — api_calls enforced)
use aprender::graph::Graph;
fn main() {
// Build a social-network-like undirected graph with two communities
let edges = vec![
// Community 1: tightly connected (nodes 0-3)
(0, 1, 1.0), (0, 2, 1.0), (1, 2, 1.0), (1, 3, 1.0), (2, 3, 1.0),
// Community 2: tightly connected (nodes 4-7)
(4, 5, 1.0), (4, 6, 1.0), (5, 6, 1.0), (5, 7, 1.0), (6, 7, 1.0),
// Bridge: weak connection between communities
(3, 4, 1.0),
];
let g = Graph::from_weighted_edges(&edges, false);
println!("Graph: {} nodes, {} edges", g.num_nodes(), g.num_edges());
assert!(g.num_nodes() >= 8, "Graph must have 8 nodes");
// Dijkstra shortest path
if let Some((path, dist)) = g.dijkstra(0, 7) {
println!("\nDijkstra 0 -> 7: path={:?}, distance={dist:.0}", path);
assert!(dist > 0.0, "Distance must be positive");
assert!(path.len() >= 2, "Path must have start and end");
}
// Graph density (Cormen et al.)
let density = g.density();
println!("\nGraph metrics:");
println!(" Density: {density:.3}");
assert!(density > 0.0 && density <= 1.0, "Density in (0,1]");
// Clustering coefficient
let cc = g.clustering_coefficient();
println!(" Clustering coefficient: {cc:.3}");
assert!(cc >= 0.0 && cc <= 1.0, "CC in [0,1]");
// Our two tightly-connected communities should have high CC
assert!(cc > 0.3, "Two cliques should yield CC > 0.3");
// Label propagation community detection (Blondel et al., 2008)
let labels = g.label_propagation(100, Some(42));
println!("\nLabel propagation communities:");
for (node, &label) in labels.iter().enumerate() {
println!(" node {node}: community {label}");
}
assert_eq!(labels.len(), g.num_nodes(), "One label per node");
// Community 1 (0-3) should share a label, community 2 (4-7) another
let c1_label = labels[0];
let c2_label = labels[4];
let c1_coherent = labels[0..4].iter().all(|&l| l == c1_label);
let c2_coherent = labels[4..8].iter().all(|&l| l == c2_label);
println!(" Community 1 coherent: {c1_coherent}");
println!(" Community 2 coherent: {c2_coherent}");
println!("\nChapter 18 contracts: PASSED");
}