1use crate::edge::EdgeIndex;
6use crate::graph::traits::{GraphBase, GraphQuery};
7use crate::graph::Graph;
8use std::collections::VecDeque;
9
10pub fn hopcroft_karp<T>(
41 graph: &Graph<T, impl Clone>,
42 left_nodes: &[usize],
43 right_nodes: &[usize],
44) -> Vec<(usize, usize)> {
45 let n_left = left_nodes.len();
46 let n_right = right_nodes.len();
47
48 if n_left == 0 || n_right == 0 {
49 return vec![];
50 }
51
52 let node_indices: Vec<_> = graph.nodes().map(|n| n.index()).collect();
54 let _index_to_pos: std::collections::HashMap<usize, usize> = left_nodes
55 .iter()
56 .enumerate()
57 .map(|(i, &idx)| (idx, i))
58 .collect();
59
60 let mut adj: Vec<Vec<usize>> = vec![vec![]; n_left];
61
62 for (left_pos, &left_idx) in left_nodes.iter().enumerate() {
63 if left_idx < node_indices.len() {
64 for neighbor in graph.neighbors(node_indices[left_idx]) {
65 if let Some(right_pos) = right_nodes.iter().position(|&r| r == neighbor.index()) {
66 adj[left_pos].push(right_pos);
67 }
68 }
69 }
70 }
71
72 let mut pair_left = vec![None; n_left];
74 let mut pair_right = vec![None; n_right];
75 let mut dist = vec![0; n_left];
76
77 fn bfs(
79 adj: &[Vec<usize>],
80 pair_left: &[Option<usize>],
81 pair_right: &[Option<usize>],
82 dist: &mut [usize],
83 ) -> bool {
84 let mut queue = VecDeque::new();
85 for (i, &p) in pair_left.iter().enumerate() {
86 if p.is_none() {
87 dist[i] = 0;
88 queue.push_back(i);
89 } else {
90 dist[i] = usize::MAX;
91 }
92 }
93
94 let mut found = false;
95 while let Some(u) = queue.pop_front() {
96 for &v in &adj[u] {
97 if let Some(next) = pair_right.get(v).and_then(|&x| x) {
98 if dist[next] == usize::MAX {
99 dist[next] = dist[u] + 1;
100 queue.push_back(next);
101 }
102 } else {
103 found = true;
104 }
105 }
106 }
107 found
108 }
109
110 fn dfs(
112 u: usize,
113 adj: &[Vec<usize>],
114 pair_left: &mut [Option<usize>],
115 pair_right: &mut [Option<usize>],
116 dist: &mut [usize],
117 ) -> bool {
118 for &v in &adj[u] {
119 if let Some(next) = pair_right.get(v).and_then(|&x| x) {
120 if dist[next] == dist[u] + 1 && dfs(next, adj, pair_left, pair_right, dist) {
121 pair_left[u] = Some(v);
122 pair_right[v] = Some(u);
123 return true;
124 }
125 } else {
126 pair_left[u] = Some(v);
127 pair_right[v] = Some(u);
128 return true;
129 }
130 }
131 dist[u] = usize::MAX;
132 false
133 }
134
135 while bfs(&adj, &pair_left, &pair_right, &mut dist) {
137 for i in 0..n_left {
138 if pair_left[i].is_none() {
139 dfs(i, &adj, &mut pair_left, &mut pair_right, &mut dist);
140 }
141 }
142 }
143
144 left_nodes
146 .iter()
147 .enumerate()
148 .filter_map(|(i, &left_idx)| {
149 pair_left[i].map(|right_pos| (left_idx, right_nodes[right_pos]))
150 })
151 .collect()
152}
153
154pub fn blossom<T>(graph: &Graph<T, impl Clone>) -> Vec<EdgeIndex> {
183 let n = graph.node_count();
184 if n == 0 {
185 return vec![];
186 }
187
188 let node_indices: Vec<_> = graph.nodes().map(|n| n.index()).collect();
189 let mut match_node: Vec<Option<usize>> = vec![None; n]; let mut used_edges = Vec::new();
191
192 for start in 0..n {
194 if match_node[start].is_some() {
195 continue;
196 }
197
198 let mut parent: Vec<Option<usize>> = vec![None; n];
200 let mut visited = vec![false; n];
201 let mut queue = VecDeque::new();
202
203 queue.push_back(start);
204 visited[start] = true;
205
206 while let Some(u) = queue.pop_front() {
207 for neighbor in graph.neighbors(node_indices[u]) {
208 let v = neighbor.index();
209 if v >= n {
210 continue;
211 }
212
213 if !visited[v] {
214 visited[v] = true;
215 parent[v] = Some(u);
216
217 if let Some(matched) = match_node.get(v).and_then(|&x| x) {
218 if !visited[matched] {
220 visited[matched] = true;
221 parent[matched] = Some(v);
222 queue.push_back(matched);
223 }
224 } else {
225 let mut curr = v;
227 let mut prev = u;
228 while let Some(p) = parent[prev] {
229 match_node[prev] = Some(curr);
230 match_node[curr] = Some(prev);
231 curr = p;
232 prev = parent[p].unwrap();
233 }
234 match_node[start] = Some(curr);
235 match_node[curr] = Some(start);
236 break;
237 }
238 }
239 }
240 }
241 }
242
243 let mut seen = std::collections::HashSet::new();
245 for (u, &v_opt) in match_node.iter().enumerate() {
246 if let Some(v) = v_opt {
247 if u < v && !seen.contains(&(u, v)) {
248 seen.insert((u, v));
249 if u < node_indices.len() {
251 for edge_idx in graph.incident_edges(node_indices[u]) {
252 if let Ok((src, tgt)) = graph.edge_endpoints(edge_idx) {
253 if (src.index() == u && tgt.index() == v)
254 || (src.index() == v && tgt.index() == u)
255 {
256 used_edges.push(edge_idx);
257 break;
258 }
259 }
260 }
261 }
262 }
263 }
264 }
265
266 used_edges
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272 use crate::graph::builders::GraphBuilder;
273
274 #[test]
275 fn test_hopcroft_karp_basic() {
276 let graph = GraphBuilder::undirected()
277 .with_nodes(vec!["L1", "L2", "R1", "R2"])
278 .with_edges(vec![(0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0)])
279 .build()
280 .unwrap();
281
282 let left = vec![0, 1];
283 let right = vec![2, 3];
284 let matching = hopcroft_karp(&graph, &left, &right);
285 assert!(!matching.is_empty());
286 }
287
288 #[test]
289 fn test_hopcroft_karp_empty() {
290 let graph: Graph<&str, f64> = GraphBuilder::undirected()
291 .with_nodes(vec!["L1", "R1"])
292 .build()
293 .unwrap();
294
295 let left = vec![0];
296 let right = vec![1];
297 let matching = hopcroft_karp(&graph, &left, &right);
298 assert!(matching.is_empty()); }
300
301 #[test]
302 fn test_hopcroft_karp_perfect_matching() {
303 let graph: Graph<&str, f64> = GraphBuilder::undirected()
305 .with_nodes(vec!["L1", "L2", "R1", "R2"])
306 .with_edges(vec![(0, 2, 1.0), (1, 3, 1.0)])
307 .build()
308 .unwrap();
309
310 let left = vec![0, 1];
311 let right = vec![2, 3];
312 let matching = hopcroft_karp(&graph, &left, &right);
313 assert_eq!(matching.len(), 2); }
315
316 #[test]
317 fn test_hopcroft_karp_single_node() {
318 let graph: Graph<&str, f64> = GraphBuilder::undirected()
319 .with_nodes(vec!["L1"])
320 .build()
321 .unwrap();
322
323 let left = vec![0];
324 let right = vec![];
325 let matching = hopcroft_karp(&graph, &left, &right);
326 assert!(matching.is_empty());
327 }
328
329 #[test]
330 fn test_blossom_basic() {
331 let graph: Graph<&str, f64> = GraphBuilder::undirected()
332 .with_nodes(vec!["A", "B", "C", "D"])
333 .with_edges(vec![(0, 1, 1.0), (2, 3, 1.0)])
334 .build()
335 .unwrap();
336
337 let matching = blossom(&graph);
338 assert!(!matching.is_empty());
339 }
340
341 #[test]
342 fn test_blossom_odd_cycle() {
343 let graph: Graph<i32, f64> = GraphBuilder::undirected()
346 .with_nodes(vec![1, 2, 3, 4, 5])
347 .with_edges(vec![
348 (0, 1, 1.0),
349 (1, 2, 1.0),
350 (2, 3, 1.0),
351 (3, 4, 1.0),
352 (4, 0, 1.0),
353 ])
354 .build()
355 .unwrap();
356
357 let matching = blossom(&graph);
358 assert_eq!(matching.len(), 2); }
360
361 #[test]
362 fn test_blossom_empty_graph() {
363 let graph: Graph<&str, f64> = GraphBuilder::undirected()
364 .with_nodes(vec!["A", "B", "C"])
365 .build()
366 .unwrap();
367
368 let matching = blossom(&graph);
369 assert!(matching.is_empty());
370 }
371
372 #[test]
373 fn test_blossom_single_edge() {
374 let graph: Graph<&str, f64> = GraphBuilder::undirected()
375 .with_nodes(vec!["A", "B"])
376 .with_edges(vec![(0, 1, 1.0)])
377 .build()
378 .unwrap();
379
380 let matching = blossom(&graph);
381 assert_eq!(matching.len(), 1);
382 }
383
384 #[test]
385 fn test_blossom_path() {
386 let graph: Graph<i32, f64> = GraphBuilder::undirected()
388 .with_nodes(vec![1, 2, 3, 4])
389 .with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0)])
390 .build()
391 .unwrap();
392
393 let matching = blossom(&graph);
394 assert_eq!(matching.len(), 2); }
396
397 #[test]
398 fn test_blossom_complete_graph() {
399 let graph: Graph<i32, f64> = GraphBuilder::undirected()
401 .with_nodes(vec![1, 2, 3, 4])
402 .with_edges(vec![
403 (0, 1, 1.0),
404 (0, 2, 1.0),
405 (0, 3, 1.0),
406 (1, 2, 1.0),
407 (1, 3, 1.0),
408 (2, 3, 1.0),
409 ])
410 .build()
411 .unwrap();
412
413 let matching = blossom(&graph);
414 assert_eq!(matching.len(), 2); }
416}