scirs2_graph/algorithms/
decomposition.rs1use crate::base::{EdgeWeight, Graph, IndexType, Node};
6use std::collections::{HashMap, HashSet};
7use std::hash::Hash;
8
9pub fn k_core_decomposition<N, E, Ix>(graph: &Graph<N, E, Ix>) -> HashMap<N, usize>
15where
16 N: Node + Clone + Hash + Eq,
17 E: EdgeWeight,
18 Ix: IndexType,
19{
20 let mut core_numbers = HashMap::new();
21 let mut degrees = HashMap::new();
22
23 for node in graph.nodes() {
25 degrees.insert(node.clone(), graph.neighbors(node).unwrap().len());
26 }
27
28 let mut nodes_by_degree: Vec<(N, usize)> =
30 degrees.iter().map(|(n, &d)| (n.clone(), d)).collect();
31 nodes_by_degree.sort_by_key(|&(_, d)| d);
32
33 let mut remaining_nodes: HashSet<N> = graph.nodes().into_iter().cloned().collect();
35 let mut current_core;
36
37 while !remaining_nodes.is_empty() {
38 let min_degree = remaining_nodes
40 .iter()
41 .map(|n| degrees[n])
42 .min()
43 .unwrap_or(0);
44
45 current_core = min_degree;
46
47 let nodes_to_remove: Vec<N> = remaining_nodes
49 .iter()
50 .filter(|n| degrees[*n] == min_degree)
51 .cloned()
52 .collect();
53
54 for node in nodes_to_remove {
56 core_numbers.insert(node.clone(), current_core);
57 remaining_nodes.remove(&node);
58
59 if let Ok(neighbors) = graph.neighbors(&node) {
61 for neighbor in neighbors {
62 if remaining_nodes.contains(&neighbor) {
63 if let Some(deg) = degrees.get_mut(&neighbor) {
64 *deg = deg.saturating_sub(1);
65 }
66 }
67 }
68 }
69 }
70 }
71
72 core_numbers
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::error::Result as GraphResult;
79 use crate::generators::create_graph;
80
81 #[test]
82 fn test_k_core_decomposition() -> GraphResult<()> {
83 let mut graph = create_graph::<&str, ()>();
85
86 graph.add_edge("A", "B", ())?;
88 graph.add_edge("B", "C", ())?;
89 graph.add_edge("C", "A", ())?;
90
91 graph.add_edge("D", "A", ())?;
93 graph.add_edge("D", "B", ())?;
94
95 graph.add_edge("E", "D", ())?;
97
98 let core_numbers = k_core_decomposition(&graph);
99
100 assert_eq!(core_numbers[&"A"], 1);
106 assert_eq!(core_numbers[&"B"], 1);
107 assert_eq!(core_numbers[&"C"], 2);
108 assert_eq!(core_numbers[&"D"], 2);
109 assert_eq!(core_numbers[&"E"], 1);
110
111 Ok(())
112 }
113
114 #[test]
115 fn test_k_core_star_graph() -> GraphResult<()> {
116 let mut star = create_graph::<i32, ()>();
118
119 star.add_edge(0, 1, ())?;
120 star.add_edge(0, 2, ())?;
121 star.add_edge(0, 3, ())?;
122 star.add_edge(0, 4, ())?;
123
124 let core_numbers = k_core_decomposition(&star);
125
126 assert_eq!(core_numbers[&0], 0);
131 assert_eq!(core_numbers[&1], 1);
132 assert_eq!(core_numbers[&2], 1);
133 assert_eq!(core_numbers[&3], 1);
134 assert_eq!(core_numbers[&4], 1);
135
136 Ok(())
137 }
138
139 #[test]
140 fn test_k_core_complete_graph() -> GraphResult<()> {
141 let mut graph = create_graph::<&str, ()>();
143
144 let nodes = vec!["A", "B", "C", "D"];
145 for i in 0..nodes.len() {
146 for j in i + 1..nodes.len() {
147 graph.add_edge(nodes[i], nodes[j], ())?;
148 }
149 }
150
151 let core_numbers = k_core_decomposition(&graph);
152
153 for node in &nodes {
155 assert_eq!(core_numbers[node], 3);
156 }
157
158 Ok(())
159 }
160}