weavatrix_graph/algo/
matching.rs1use crate::IndexUndirectedGraphView;
2use crate::Vec;
3use alloc::collections::VecDeque;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct MaximumMatching<Node> {
7 pairs: Vec<(Node, Node)>,
8}
9
10impl<Node> MaximumMatching<Node> {
11 #[must_use]
12 pub fn pairs(&self) -> &[(Node, Node)] {
13 &self.pairs
14 }
15
16 #[must_use]
17 pub const fn len(&self) -> usize {
18 self.pairs.len()
19 }
20
21 #[must_use]
22 pub const fn is_empty(&self) -> bool {
23 self.pairs.is_empty()
24 }
25}
26
27pub fn maximum_matching<G>(graph: &G) -> MaximumMatching<G::Node>
29where
30 G: IndexUndirectedGraphView,
31{
32 let (nodes, adjacency) = indexed(graph);
33 let matching = Blossom::new(adjacency).solve();
34 let pairs = matching
35 .iter()
36 .enumerate()
37 .filter_map(|(left, right)| {
38 let right = (*right)?;
39 (left < right).then(|| Some((nodes[left]?, nodes[right]?)))?
40 })
41 .collect();
42 MaximumMatching { pairs }
43}
44
45fn indexed<G: IndexUndirectedGraphView>(graph: &G) -> (Vec<Option<G::Node>>, Vec<Vec<usize>>) {
46 let mut nodes = vec![None; graph.node_bound()];
47 let mut adjacency = vec![Vec::new(); graph.node_bound()];
48 for node in graph.node_indices() {
49 let slot = G::node_slot(node);
50 nodes[slot] = Some(node);
51 adjacency[slot] = graph
52 .incident_edges(node)
53 .filter_map(|edge| graph.opposite(edge, node))
54 .map(G::node_slot)
55 .filter(|neighbor| *neighbor != slot)
56 .collect();
57 }
58 (nodes, adjacency)
59}
60
61struct Blossom {
62 adjacency: Vec<Vec<usize>>,
63 matching: Vec<Option<usize>>,
64 parent: Vec<Option<usize>>,
65 base: Vec<usize>,
66 used: Vec<bool>,
67 contracted: Vec<bool>,
68}
69
70impl Blossom {
71 fn new(adjacency: Vec<Vec<usize>>) -> Self {
72 let bound = adjacency.len();
73 Self {
74 adjacency,
75 matching: vec![None; bound],
76 parent: vec![None; bound],
77 base: (0..bound).collect(),
78 used: vec![false; bound],
79 contracted: vec![false; bound],
80 }
81 }
82
83 fn solve(mut self) -> Vec<Option<usize>> {
84 self.seed_greedy();
85 for root in 0..self.adjacency.len() {
86 if self.matching[root].is_none() {
87 self.find_augmenting_path(root);
88 }
89 }
90 self.matching
91 }
92
93 fn seed_greedy(&mut self) {
94 for left in 0..self.adjacency.len() {
95 if self.matching[left].is_some() {
96 continue;
97 }
98 let right = self.adjacency[left]
99 .iter()
100 .copied()
101 .find(|right| self.matching[*right].is_none());
102 if let Some(right) = right {
103 self.matching[left] = Some(right);
104 self.matching[right] = Some(left);
105 }
106 }
107 }
108
109 fn find_augmenting_path(&mut self, root: usize) -> bool {
110 self.used.fill(false);
111 self.parent.fill(None);
112 for (slot, base) in self.base.iter_mut().enumerate() {
113 *base = slot;
114 }
115 let mut queue = VecDeque::from([root]);
116 self.used[root] = true;
117 while let Some(node) = queue.pop_front() {
118 for index in 0..self.adjacency[node].len() {
119 let neighbor = self.adjacency[node][index];
120 if self.base[node] == self.base[neighbor] || self.matching[node] == Some(neighbor) {
121 continue;
122 }
123 if neighbor == root
124 || self.matching[neighbor]
125 .and_then(|matched| self.parent[matched])
126 .is_some()
127 {
128 self.contract(node, neighbor, &mut queue);
129 } else if self.parent[neighbor].is_none() {
130 self.parent[neighbor] = Some(node);
131 if self.matching[neighbor].is_none() {
132 self.augment(neighbor);
133 return true;
134 }
135 if let Some(matched) = self.matching[neighbor] {
136 self.used[matched] = true;
137 queue.push_back(matched);
138 }
139 }
140 }
141 }
142 false
143 }
144
145 fn contract(&mut self, left: usize, right: usize, queue: &mut VecDeque<usize>) {
146 let base = self.lowest_common_base(left, right);
147 self.contracted.fill(false);
148 self.mark_path(left, right, base);
149 self.mark_path(right, left, base);
150 for node in 0..self.adjacency.len() {
151 if self.contracted[self.base[node]] {
152 self.base[node] = base;
153 if !self.used[node] {
154 self.used[node] = true;
155 queue.push_back(node);
156 }
157 }
158 }
159 }
160
161 fn lowest_common_base(&self, mut left: usize, mut right: usize) -> usize {
162 let mut path = vec![false; self.adjacency.len()];
163 loop {
164 left = self.base[left];
165 path[left] = true;
166 let Some(matched) = self.matching[left] else {
167 break;
168 };
169 let Some(parent) = self.parent[matched] else {
170 break;
171 };
172 left = parent;
173 }
174 loop {
175 right = self.base[right];
176 if path[right] {
177 return right;
178 }
179 let Some(matched) = self.matching[right] else {
180 return right;
181 };
182 let Some(parent) = self.parent[matched] else {
183 return right;
184 };
185 right = parent;
186 }
187 }
188
189 fn mark_path(&mut self, mut node: usize, mut child: usize, base: usize) {
190 while self.base[node] != base {
191 let Some(matched) = self.matching[node] else {
192 break;
193 };
194 self.contracted[self.base[node]] = true;
195 self.contracted[self.base[matched]] = true;
196 self.parent[node] = Some(child);
197 child = matched;
198 let Some(parent) = self.parent[matched] else {
199 break;
200 };
201 node = parent;
202 }
203 }
204
205 fn augment(&mut self, mut node: usize) {
206 while let Some(parent) = self.parent[node] {
207 let next = self.matching[parent];
208 self.matching[node] = Some(parent);
209 self.matching[parent] = Some(node);
210 let Some(next) = next else {
211 break;
212 };
213 node = next;
214 }
215 }
216}