1use super::core::{Graph, GraphError, GraphType, NodeId};
21use super::traversal::{bfs, dfs_from};
22use std::collections::{HashMap, HashSet, VecDeque};
23use std::fmt::Debug;
24
25#[derive(Debug, Clone)]
27pub struct ComponentResult {
28 pub node_components: HashMap<NodeId, usize>,
30 pub components: Vec<HashSet<NodeId>>,
32 pub num_components: usize,
34}
35
36impl ComponentResult {
37 pub fn component_of(&self, node: NodeId) -> Option<usize> {
39 self.node_components.get(&node).copied()
40 }
41
42 pub fn nodes_in_component(&self, component_id: usize) -> Option<&HashSet<NodeId>> {
44 self.components.get(component_id)
45 }
46
47 pub fn component_sizes(&self) -> Vec<usize> {
49 self.components.iter().map(|c| c.len()).collect()
50 }
51
52 pub fn largest_component(&self) -> Option<&HashSet<NodeId>> {
54 self.components.iter().max_by_key(|c| c.len())
55 }
56
57 pub fn are_connected(&self, node1: NodeId, node2: NodeId) -> bool {
59 match (
60 self.node_components.get(&node1),
61 self.node_components.get(&node2),
62 ) {
63 (Some(c1), Some(c2)) => c1 == c2,
64 _ => false,
65 }
66 }
67}
68
69pub fn connected_components<N, W>(graph: &Graph<N, W>) -> ComponentResult
71where
72 N: Clone + Debug,
73 W: Clone + Debug,
74{
75 let mut node_components: HashMap<NodeId, usize> = HashMap::new();
76 let mut components: Vec<HashSet<NodeId>> = Vec::new();
77 let mut visited: HashSet<NodeId> = HashSet::new();
78
79 for node_id in graph.node_ids() {
80 if !visited.contains(&node_id) {
81 let result = bfs(graph, node_id);
82 let component: HashSet<NodeId> = result.visit_order.into_iter().collect();
83 let component_id = components.len();
84
85 for &node in &component {
86 node_components.insert(node, component_id);
87 visited.insert(node);
88 }
89
90 components.push(component);
91 }
92 }
93
94 let num_components = components.len();
95
96 ComponentResult {
97 node_components,
98 components,
99 num_components,
100 }
101}
102
103pub fn is_connected<N, W>(graph: &Graph<N, W>) -> bool
105where
106 N: Clone + Debug,
107 W: Clone + Debug,
108{
109 if graph.is_empty() {
110 return true;
111 }
112
113 let result = connected_components(graph);
114 result.num_components == 1
115}
116
117pub fn strongly_connected_components<N, W>(graph: &Graph<N, W>) -> ComponentResult
119where
120 N: Clone + Debug,
121 W: Clone + Debug,
122{
123 let nodes: Vec<NodeId> = graph.node_ids().collect();
124 let mut visited: HashSet<NodeId> = HashSet::new();
125 let mut finish_order: Vec<NodeId> = Vec::new();
126
127 for &node in &nodes {
129 if !visited.contains(&node) {
130 kosaraju_dfs1(graph, node, &mut visited, &mut finish_order);
131 }
132 }
133
134 let reversed = graph.reverse();
136
137 let mut node_components: HashMap<NodeId, usize> = HashMap::new();
139 let mut components: Vec<HashSet<NodeId>> = Vec::new();
140 visited.clear();
141
142 let node_map: HashMap<NodeId, NodeId> = nodes
144 .iter()
145 .enumerate()
146 .map(|(i, &old_id)| (old_id, NodeId(i)))
147 .collect();
148
149 let reverse_node_map: HashMap<NodeId, NodeId> =
151 node_map.iter().map(|(&orig, &rev)| (rev, orig)).collect();
152
153 for &node in finish_order.iter().rev() {
154 if !visited.contains(&node) {
155 let mut component: HashSet<NodeId> = HashSet::new();
156 let reversed_node = node_map[&node];
158 kosaraju_dfs2(
159 &reversed,
160 reversed_node,
161 &mut visited,
162 &mut component,
163 &reverse_node_map,
164 );
165
166 let component_id = components.len();
167 for &n in &component {
168 node_components.insert(n, component_id);
169 }
170 components.push(component);
171 }
172 }
173
174 let num_components = components.len();
175
176 ComponentResult {
177 node_components,
178 components,
179 num_components,
180 }
181}
182
183fn kosaraju_dfs1<N, W>(
184 graph: &Graph<N, W>,
185 node: NodeId,
186 visited: &mut HashSet<NodeId>,
187 finish_order: &mut Vec<NodeId>,
188) where
189 N: Clone + Debug,
190 W: Clone + Debug,
191{
192 visited.insert(node);
193
194 if let Some(neighbors) = graph.neighbors(node) {
195 for neighbor in neighbors {
196 if !visited.contains(&neighbor) {
197 kosaraju_dfs1(graph, neighbor, visited, finish_order);
198 }
199 }
200 }
201
202 finish_order.push(node);
203}
204
205fn kosaraju_dfs2<N, W>(
206 graph: &Graph<N, W>,
207 node: NodeId,
208 visited: &mut HashSet<NodeId>,
209 component: &mut HashSet<NodeId>,
210 reverse_node_map: &HashMap<NodeId, NodeId>,
211) where
212 N: Clone + Debug,
213 W: Clone + Debug,
214{
215 let original_node = reverse_node_map.get(&node).copied().unwrap_or(node);
217 visited.insert(original_node);
218 component.insert(original_node);
219
220 if let Some(neighbors) = graph.neighbors(node) {
221 for neighbor in neighbors {
222 let original_neighbor = reverse_node_map.get(&neighbor).copied().unwrap_or(neighbor);
223 if !visited.contains(&original_neighbor) {
224 kosaraju_dfs2(graph, neighbor, visited, component, reverse_node_map);
225 }
226 }
227 }
228}
229
230pub fn weakly_connected_components<N, W>(graph: &Graph<N, W>) -> ComponentResult
233where
234 N: Clone + Debug,
235 W: Clone + Debug,
236{
237 let undirected = graph.to_undirected();
239 connected_components(&undirected)
240}
241
242pub fn label_propagation<N, W>(graph: &Graph<N, W>, max_iterations: usize) -> HashMap<NodeId, usize>
251where
252 N: Clone + Debug,
253 W: Clone + Debug,
254{
255 let nodes: Vec<NodeId> = graph.node_ids().collect();
256
257 let mut labels: HashMap<NodeId, usize> = nodes.iter().map(|&n| (n, n.0)).collect();
259
260 for _ in 0..max_iterations {
261 let mut changed = false;
262
263 for &node in &nodes {
265 if let Some(neighbors) = graph.neighbors(node) {
266 if neighbors.is_empty() {
267 continue;
268 }
269
270 let mut label_counts: HashMap<usize, usize> = HashMap::new();
272 for neighbor in neighbors {
273 let label = labels[&neighbor];
274 *label_counts.entry(label).or_insert(0) += 1;
275 }
276
277 if let Some((&max_label, _)) = label_counts.iter().max_by_key(|(_, &count)| count) {
279 if labels[&node] != max_label {
280 labels.insert(node, max_label);
281 changed = true;
282 }
283 }
284 }
285 }
286
287 if !changed {
288 break;
289 }
290 }
291
292 let unique_labels: HashSet<usize> = labels.values().copied().collect();
294 let label_map: HashMap<usize, usize> = unique_labels
295 .iter()
296 .enumerate()
297 .map(|(i, &l)| (l, i))
298 .collect();
299
300 labels
301 .into_iter()
302 .map(|(node, label)| (node, label_map[&label]))
303 .collect()
304}
305
306pub fn modularity<N, W>(graph: &Graph<N, W>, communities: &HashMap<NodeId, usize>) -> f64
311where
312 N: Clone + Debug,
313 W: Clone + Debug,
314{
315 let m = graph.edge_count() as f64;
316 if m == 0.0 {
317 return 0.0;
318 }
319
320 let m2 = 2.0 * m;
321 let mut q = 0.0;
322
323 for node_i in graph.node_ids() {
324 for node_j in graph.node_ids() {
325 if communities.get(&node_i) != communities.get(&node_j) {
327 continue;
328 }
329
330 let ki = graph.degree(node_i).unwrap_or(0) as f64;
331 let kj = graph.degree(node_j).unwrap_or(0) as f64;
332
333 let aij = if graph.has_edge(node_i, node_j) {
334 1.0
335 } else {
336 0.0
337 };
338
339 q += aij - (ki * kj) / m2;
340 }
341 }
342
343 q / m2
344}
345
346pub fn louvain<N, W>(graph: &Graph<N, W>, resolution: f64) -> (HashMap<NodeId, usize>, f64)
357where
358 N: Clone + Debug,
359 W: Clone + Debug,
360{
361 let nodes: Vec<NodeId> = graph.node_ids().collect();
362 let m = graph.edge_count() as f64;
363
364 if m == 0.0 || nodes.is_empty() {
365 let communities: HashMap<NodeId, usize> =
366 nodes.iter().enumerate().map(|(i, &n)| (n, i)).collect();
367 return (communities, 0.0);
368 }
369
370 let m2 = 2.0 * m;
371
372 let mut communities: HashMap<NodeId, usize> = nodes.iter().map(|&n| (n, n.0)).collect();
374
375 let degrees: HashMap<NodeId, f64> = nodes
377 .iter()
378 .map(|&n| (n, graph.degree(n).unwrap_or(0) as f64))
379 .collect();
380
381 let mut community_degrees: HashMap<usize, f64> = nodes
383 .iter()
384 .map(|&n| (communities[&n], degrees[&n]))
385 .collect();
386
387 let mut improved = true;
388
389 while improved {
390 improved = false;
391
392 for &node in &nodes {
393 let current_community = communities[&node];
394 let ki = degrees[&node];
395
396 let mut best_community = current_community;
398 let mut best_gain = 0.0;
399
400 let mut neighbor_communities: HashSet<usize> = HashSet::new();
402 if let Some(neighbors) = graph.neighbors(node) {
403 for neighbor in neighbors {
404 neighbor_communities.insert(communities[&neighbor]);
405 }
406 }
407
408 *community_degrees.get_mut(¤t_community).unwrap() -= ki;
410
411 for &target_community in &neighbor_communities {
412 if target_community == current_community {
413 continue;
414 }
415
416 let mut ki_in = 0.0;
418 if let Some(neighbors) = graph.neighbors(node) {
419 for neighbor in neighbors {
420 if communities[&neighbor] == target_community {
421 ki_in += 1.0;
422 }
423 }
424 }
425
426 let sigma_tot = community_degrees
427 .get(&target_community)
428 .copied()
429 .unwrap_or(0.0);
430
431 let gain = ki_in - resolution * (sigma_tot * ki) / m2;
433
434 if gain > best_gain {
435 best_gain = gain;
436 best_community = target_community;
437 }
438 }
439
440 if best_community != current_community {
442 communities.insert(node, best_community);
443 *community_degrees.entry(best_community).or_insert(0.0) += ki;
444 improved = true;
445 } else {
446 *community_degrees.get_mut(¤t_community).unwrap() += ki;
447 }
448 }
449 }
450
451 let unique_communities: HashSet<usize> = communities.values().copied().collect();
453 let community_map: HashMap<usize, usize> = unique_communities
454 .iter()
455 .enumerate()
456 .map(|(i, &c)| (c, i))
457 .collect();
458
459 let normalized_communities: HashMap<NodeId, usize> = communities
460 .into_iter()
461 .map(|(node, c)| (node, community_map[&c]))
462 .collect();
463
464 let final_modularity = modularity(graph, &normalized_communities);
465
466 (normalized_communities, final_modularity)
467}
468
469pub fn louvain_default<N, W>(graph: &Graph<N, W>) -> (HashMap<NodeId, usize>, f64)
471where
472 N: Clone + Debug,
473 W: Clone + Debug,
474{
475 louvain(graph, 1.0)
476}
477
478pub fn find_bridges<N, W>(graph: &Graph<N, W>) -> Vec<(NodeId, NodeId)>
480where
481 N: Clone + Debug,
482 W: Clone + Debug,
483{
484 let mut bridges = Vec::new();
485 let nodes: Vec<NodeId> = graph.node_ids().collect();
486
487 if nodes.is_empty() {
488 return bridges;
489 }
490
491 let mut visited: HashSet<NodeId> = HashSet::new();
492 let mut discovery: HashMap<NodeId, usize> = HashMap::new();
493 let mut low: HashMap<NodeId, usize> = HashMap::new();
494 let mut parent: HashMap<NodeId, Option<NodeId>> = HashMap::new();
495 let mut time = 0;
496
497 for &node in &nodes {
498 if !visited.contains(&node) {
499 bridge_dfs(
500 graph,
501 node,
502 &mut visited,
503 &mut discovery,
504 &mut low,
505 &mut parent,
506 &mut time,
507 &mut bridges,
508 );
509 }
510 }
511
512 bridges
513}
514
515fn bridge_dfs<N, W>(
516 graph: &Graph<N, W>,
517 node: NodeId,
518 visited: &mut HashSet<NodeId>,
519 discovery: &mut HashMap<NodeId, usize>,
520 low: &mut HashMap<NodeId, usize>,
521 parent: &mut HashMap<NodeId, Option<NodeId>>,
522 time: &mut usize,
523 bridges: &mut Vec<(NodeId, NodeId)>,
524) where
525 N: Clone + Debug,
526 W: Clone + Debug,
527{
528 visited.insert(node);
529 *time += 1;
530 discovery.insert(node, *time);
531 low.insert(node, *time);
532
533 if let Some(neighbors) = graph.neighbors(node) {
534 for neighbor in neighbors {
535 if !visited.contains(&neighbor) {
536 parent.insert(neighbor, Some(node));
537 bridge_dfs(
538 graph, neighbor, visited, discovery, low, parent, time, bridges,
539 );
540
541 let low_neighbor = low[&neighbor];
543 let low_node = low[&node];
544 low.insert(node, low_node.min(low_neighbor));
545
546 if low[&neighbor] > discovery[&node] {
548 bridges.push((node, neighbor));
549 }
550 } else if parent.get(&node) != Some(&Some(neighbor)) {
551 let low_node = low[&node];
553 let disc_neighbor = discovery[&neighbor];
554 low.insert(node, low_node.min(disc_neighbor));
555 }
556 }
557 }
558}
559
560pub fn find_articulation_points<N, W>(graph: &Graph<N, W>) -> Vec<NodeId>
562where
563 N: Clone + Debug,
564 W: Clone + Debug,
565{
566 let mut articulation_points = HashSet::new();
567 let nodes: Vec<NodeId> = graph.node_ids().collect();
568
569 if nodes.is_empty() {
570 return Vec::new();
571 }
572
573 let mut visited: HashSet<NodeId> = HashSet::new();
574 let mut discovery: HashMap<NodeId, usize> = HashMap::new();
575 let mut low: HashMap<NodeId, usize> = HashMap::new();
576 let mut parent: HashMap<NodeId, Option<NodeId>> = HashMap::new();
577 let mut time = 0;
578
579 for &node in &nodes {
580 if !visited.contains(&node) {
581 ap_dfs(
582 graph,
583 node,
584 &mut visited,
585 &mut discovery,
586 &mut low,
587 &mut parent,
588 &mut time,
589 &mut articulation_points,
590 );
591 }
592 }
593
594 articulation_points.into_iter().collect()
595}
596
597fn ap_dfs<N, W>(
598 graph: &Graph<N, W>,
599 node: NodeId,
600 visited: &mut HashSet<NodeId>,
601 discovery: &mut HashMap<NodeId, usize>,
602 low: &mut HashMap<NodeId, usize>,
603 parent: &mut HashMap<NodeId, Option<NodeId>>,
604 time: &mut usize,
605 articulation_points: &mut HashSet<NodeId>,
606) where
607 N: Clone + Debug,
608 W: Clone + Debug,
609{
610 visited.insert(node);
611 *time += 1;
612 discovery.insert(node, *time);
613 low.insert(node, *time);
614 let mut children = 0;
615
616 if let Some(neighbors) = graph.neighbors(node) {
617 for neighbor in neighbors {
618 if !visited.contains(&neighbor) {
619 children += 1;
620 parent.insert(neighbor, Some(node));
621 ap_dfs(
622 graph,
623 neighbor,
624 visited,
625 discovery,
626 low,
627 parent,
628 time,
629 articulation_points,
630 );
631
632 let low_neighbor = low[&neighbor];
633 let low_node = low[&node];
634 low.insert(node, low_node.min(low_neighbor));
635
636 if parent.get(&node) == Some(&None) && children > 1 {
638 articulation_points.insert(node);
639 }
640
641 if parent.get(&node) != Some(&None) && low[&neighbor] >= discovery[&node] {
643 articulation_points.insert(node);
644 }
645 } else if parent.get(&node) != Some(&Some(neighbor)) {
646 let low_node = low[&node];
647 let disc_neighbor = discovery[&neighbor];
648 low.insert(node, low_node.min(disc_neighbor));
649 }
650 }
651 }
652
653 if !parent.contains_key(&node) {
655 parent.insert(node, None);
656 }
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662
663 #[test]
664 fn test_connected_components() {
665 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Undirected);
666
667 let a = graph.add_node("A");
669 let b = graph.add_node("B");
670 let c = graph.add_node("C");
671 let d = graph.add_node("D");
672 let e = graph.add_node("E");
673
674 graph.add_edge(a, b, None).unwrap();
676 graph.add_edge(b, c, None).unwrap();
677
678 graph.add_edge(d, e, None).unwrap();
680
681 let result = connected_components(&graph);
682
683 assert_eq!(result.num_components, 2);
684 assert!(result.are_connected(a, b));
685 assert!(result.are_connected(b, c));
686 assert!(result.are_connected(d, e));
687 assert!(!result.are_connected(a, d));
688 }
689
690 #[test]
691 fn test_is_connected() {
692 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Undirected);
693 let a = graph.add_node("A");
694 let b = graph.add_node("B");
695 let c = graph.add_node("C");
696
697 graph.add_edge(a, b, None).unwrap();
698 assert!(!is_connected(&graph)); graph.add_edge(b, c, None).unwrap();
701 assert!(is_connected(&graph));
702 }
703
704 #[test]
705 fn test_strongly_connected_components() {
706 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Directed);
707
708 let a = graph.add_node("A");
709 let b = graph.add_node("B");
710 let c = graph.add_node("C");
711 let d = graph.add_node("D");
712
713 graph.add_edge(a, b, None).unwrap();
715 graph.add_edge(b, a, None).unwrap();
716
717 graph.add_edge(c, d, None).unwrap();
719 graph.add_edge(d, c, None).unwrap();
720
721 graph.add_edge(a, c, None).unwrap();
723
724 let result = strongly_connected_components(&graph);
725
726 assert!(result.num_components >= 2);
728 assert!(result.num_components <= 4); assert_eq!(result.node_components.len(), 4);
732 }
733
734 #[test]
735 fn test_label_propagation() {
736 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Undirected);
737
738 let a = graph.add_node("A");
740 let b = graph.add_node("B");
741 let c = graph.add_node("C");
742 let d = graph.add_node("D");
743 let e = graph.add_node("E");
744 let f = graph.add_node("F");
745
746 graph.add_edge(a, b, None).unwrap();
748 graph.add_edge(b, c, None).unwrap();
749 graph.add_edge(a, c, None).unwrap();
750
751 graph.add_edge(d, e, None).unwrap();
753 graph.add_edge(e, f, None).unwrap();
754 graph.add_edge(d, f, None).unwrap();
755
756 graph.add_edge(c, d, None).unwrap();
758
759 let communities = label_propagation(&graph, 100);
760
761 assert_eq!(communities.len(), 6);
763 }
764
765 #[test]
766 fn test_modularity() {
767 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Undirected);
768
769 let a = graph.add_node("A");
770 let b = graph.add_node("B");
771 let c = graph.add_node("C");
772 let d = graph.add_node("D");
773
774 graph.add_edge(a, b, None).unwrap();
776 graph.add_edge(c, d, None).unwrap();
777
778 let mut communities = HashMap::new();
780 communities.insert(a, 0);
781 communities.insert(b, 0);
782 communities.insert(c, 1);
783 communities.insert(d, 1);
784
785 let q = modularity(&graph, &communities);
786 assert!(q > 0.0); }
788
789 #[test]
790 fn test_louvain() {
791 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Undirected);
792
793 let a = graph.add_node("A");
795 let b = graph.add_node("B");
796 let c = graph.add_node("C");
797 let d = graph.add_node("D");
798
799 graph.add_edge(a, b, None).unwrap();
800 graph.add_edge(c, d, None).unwrap();
801
802 let (communities, modularity) = louvain_default(&graph);
803
804 assert!(!communities.is_empty());
805 }
807
808 #[test]
809 fn test_find_bridges() {
810 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Undirected);
811
812 let a = graph.add_node("A");
813 let b = graph.add_node("B");
814 let c = graph.add_node("C");
815 let d = graph.add_node("D");
816
817 graph.add_edge(a, b, None).unwrap();
819 graph.add_edge(b, c, None).unwrap();
820 graph.add_edge(c, d, None).unwrap();
821
822 let bridges = find_bridges(&graph);
823 assert_eq!(bridges.len(), 3);
825 }
826
827 #[test]
828 fn test_articulation_points() {
829 let mut graph: Graph<&str, f64> = Graph::new(GraphType::Undirected);
830
831 let a = graph.add_node("A");
832 let b = graph.add_node("B");
833 let c = graph.add_node("C");
834 let d = graph.add_node("D");
835
836 graph.add_edge(a, b, None).unwrap();
840 graph.add_edge(b, c, None).unwrap();
841 graph.add_edge(b, d, None).unwrap();
842
843 let ap = find_articulation_points(&graph);
844 assert!(ap.contains(&b));
846 }
847}