use crate::edge::EdgeIndex;
use crate::graph::traits::{GraphBase, GraphQuery};
use crate::graph::Graph;
use std::collections::VecDeque;
pub fn hopcroft_karp<T>(
graph: &Graph<T, impl Clone>,
left_nodes: &[usize],
right_nodes: &[usize],
) -> Vec<(usize, usize)> {
let n_left = left_nodes.len();
let n_right = right_nodes.len();
if n_left == 0 || n_right == 0 {
return vec![];
}
let node_indices: Vec<_> = graph.nodes().map(|n| n.index()).collect();
let _index_to_pos: std::collections::HashMap<usize, usize> = left_nodes
.iter()
.enumerate()
.map(|(i, &idx)| (idx, i))
.collect();
let mut adj: Vec<Vec<usize>> = vec![vec![]; n_left];
for (left_pos, &left_idx) in left_nodes.iter().enumerate() {
if left_idx < node_indices.len() {
for neighbor in graph.neighbors(node_indices[left_idx]) {
if let Some(right_pos) = right_nodes.iter().position(|&r| r == neighbor.index()) {
adj[left_pos].push(right_pos);
}
}
}
}
let mut pair_left = vec![None; n_left];
let mut pair_right = vec![None; n_right];
let mut dist = vec![0; n_left];
fn bfs(
adj: &[Vec<usize>],
pair_left: &[Option<usize>],
pair_right: &[Option<usize>],
dist: &mut [usize],
) -> bool {
let mut queue = VecDeque::new();
for (i, &p) in pair_left.iter().enumerate() {
if p.is_none() {
dist[i] = 0;
queue.push_back(i);
} else {
dist[i] = usize::MAX;
}
}
let mut found = false;
while let Some(u) = queue.pop_front() {
for &v in &adj[u] {
if let Some(next) = pair_right.get(v).and_then(|&x| x) {
if dist[next] == usize::MAX {
dist[next] = dist[u] + 1;
queue.push_back(next);
}
} else {
found = true;
}
}
}
found
}
fn dfs(
u: usize,
adj: &[Vec<usize>],
pair_left: &mut [Option<usize>],
pair_right: &mut [Option<usize>],
dist: &mut [usize],
) -> bool {
for &v in &adj[u] {
if let Some(next) = pair_right.get(v).and_then(|&x| x) {
if dist[next] == dist[u] + 1 && dfs(next, adj, pair_left, pair_right, dist) {
pair_left[u] = Some(v);
pair_right[v] = Some(u);
return true;
}
} else {
pair_left[u] = Some(v);
pair_right[v] = Some(u);
return true;
}
}
dist[u] = usize::MAX;
false
}
while bfs(&adj, &pair_left, &pair_right, &mut dist) {
for i in 0..n_left {
if pair_left[i].is_none() {
dfs(i, &adj, &mut pair_left, &mut pair_right, &mut dist);
}
}
}
left_nodes
.iter()
.enumerate()
.filter_map(|(i, &left_idx)| {
pair_left[i].map(|right_pos| (left_idx, right_nodes[right_pos]))
})
.collect()
}
pub fn blossom<T>(graph: &Graph<T, impl Clone>) -> Vec<EdgeIndex> {
let n = graph.node_count();
if n == 0 {
return vec![];
}
let node_indices: Vec<_> = graph.nodes().map(|n| n.index()).collect();
let mut match_node: Vec<Option<usize>> = vec![None; n]; let mut used_edges = Vec::new();
for start in 0..n {
if match_node[start].is_some() {
continue;
}
let mut parent: Vec<Option<usize>> = vec![None; n];
let mut visited = vec![false; n];
let mut queue = VecDeque::new();
queue.push_back(start);
visited[start] = true;
while let Some(u) = queue.pop_front() {
for neighbor in graph.neighbors(node_indices[u]) {
let v = neighbor.index();
if v >= n {
continue;
}
if !visited[v] {
visited[v] = true;
parent[v] = Some(u);
if let Some(matched) = match_node.get(v).and_then(|&x| x) {
if !visited[matched] {
visited[matched] = true;
parent[matched] = Some(v);
queue.push_back(matched);
}
} else {
let mut curr = v;
let mut prev = u;
while let Some(p) = parent[prev] {
match_node[prev] = Some(curr);
match_node[curr] = Some(prev);
curr = p;
prev = parent[p].unwrap();
}
match_node[start] = Some(curr);
match_node[curr] = Some(start);
break;
}
}
}
}
}
let mut seen = std::collections::HashSet::new();
for (u, &v_opt) in match_node.iter().enumerate() {
if let Some(v) = v_opt {
if u < v && !seen.contains(&(u, v)) {
seen.insert((u, v));
if u < node_indices.len() {
for edge_idx in graph.incident_edges(node_indices[u]) {
if let Ok((src, tgt)) = graph.edge_endpoints(edge_idx) {
if (src.index() == u && tgt.index() == v)
|| (src.index() == v && tgt.index() == u)
{
used_edges.push(edge_idx);
break;
}
}
}
}
}
}
}
used_edges
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::builders::GraphBuilder;
#[test]
fn test_hopcroft_karp_basic() {
let graph = GraphBuilder::undirected()
.with_nodes(vec!["L1", "L2", "R1", "R2"])
.with_edges(vec![(0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0)])
.build()
.unwrap();
let left = vec![0, 1];
let right = vec![2, 3];
let matching = hopcroft_karp(&graph, &left, &right);
assert!(!matching.is_empty());
}
#[test]
fn test_hopcroft_karp_empty() {
let graph: Graph<&str, f64> = GraphBuilder::undirected()
.with_nodes(vec!["L1", "R1"])
.build()
.unwrap();
let left = vec![0];
let right = vec![1];
let matching = hopcroft_karp(&graph, &left, &right);
assert!(matching.is_empty()); }
#[test]
fn test_hopcroft_karp_perfect_matching() {
let graph: Graph<&str, f64> = GraphBuilder::undirected()
.with_nodes(vec!["L1", "L2", "R1", "R2"])
.with_edges(vec![(0, 2, 1.0), (1, 3, 1.0)])
.build()
.unwrap();
let left = vec![0, 1];
let right = vec![2, 3];
let matching = hopcroft_karp(&graph, &left, &right);
assert_eq!(matching.len(), 2); }
#[test]
fn test_hopcroft_karp_single_node() {
let graph: Graph<&str, f64> = GraphBuilder::undirected()
.with_nodes(vec!["L1"])
.build()
.unwrap();
let left = vec![0];
let right = vec![];
let matching = hopcroft_karp(&graph, &left, &right);
assert!(matching.is_empty());
}
#[test]
fn test_blossom_basic() {
let graph: Graph<&str, f64> = GraphBuilder::undirected()
.with_nodes(vec!["A", "B", "C", "D"])
.with_edges(vec![(0, 1, 1.0), (2, 3, 1.0)])
.build()
.unwrap();
let matching = blossom(&graph);
assert!(!matching.is_empty());
}
#[test]
fn test_blossom_odd_cycle() {
let graph: Graph<i32, f64> = GraphBuilder::undirected()
.with_nodes(vec![1, 2, 3, 4, 5])
.with_edges(vec![
(0, 1, 1.0),
(1, 2, 1.0),
(2, 3, 1.0),
(3, 4, 1.0),
(4, 0, 1.0),
])
.build()
.unwrap();
let matching = blossom(&graph);
assert_eq!(matching.len(), 2); }
#[test]
fn test_blossom_empty_graph() {
let graph: Graph<&str, f64> = GraphBuilder::undirected()
.with_nodes(vec!["A", "B", "C"])
.build()
.unwrap();
let matching = blossom(&graph);
assert!(matching.is_empty());
}
#[test]
fn test_blossom_single_edge() {
let graph: Graph<&str, f64> = GraphBuilder::undirected()
.with_nodes(vec!["A", "B"])
.with_edges(vec![(0, 1, 1.0)])
.build()
.unwrap();
let matching = blossom(&graph);
assert_eq!(matching.len(), 1);
}
#[test]
fn test_blossom_path() {
let graph: Graph<i32, f64> = GraphBuilder::undirected()
.with_nodes(vec![1, 2, 3, 4])
.with_edges(vec![(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0)])
.build()
.unwrap();
let matching = blossom(&graph);
assert_eq!(matching.len(), 2); }
#[test]
fn test_blossom_complete_graph() {
let graph: Graph<i32, f64> = GraphBuilder::undirected()
.with_nodes(vec![1, 2, 3, 4])
.with_edges(vec![
(0, 1, 1.0),
(0, 2, 1.0),
(0, 3, 1.0),
(1, 2, 1.0),
(1, 3, 1.0),
(2, 3, 1.0),
])
.build()
.unwrap();
let matching = blossom(&graph);
assert_eq!(matching.len(), 2); }
}