rustworkx_core/max_weight_matching.rs
1// Licensed under the Apache License, Version 2.0 (the "License"); you may
2// not use this file except in compliance with the License. You may obtain
3// a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10// License for the specific language governing permissions and limitations
11// under the License.
12
13// Needed to pass shared state between functions
14// closures don't work because of recursion
15#![allow(clippy::too_many_arguments)]
16// Allow single character names to match naming convention from
17// paper
18#![allow(clippy::many_single_char_names)]
19
20use std::cmp::max;
21use std::hash::Hash;
22use std::mem;
23
24use hashbrown::{HashMap, HashSet};
25
26use petgraph::visit::{
27 EdgeCount, EdgeRef, GraphBase, GraphProp, IntoEdges, IntoNodeIdentifiers, NodeCount,
28 NodeIndexable,
29};
30use petgraph::Undirected;
31
32/// Return 2 * slack of edge k (does not work inside blossoms).
33fn slack(edge_index: usize, dual_var: &[i128], edges: &[(usize, usize, i128)]) -> i128 {
34 let (source_index, target_index, weight) = edges[edge_index];
35 dual_var[source_index] + dual_var[target_index] - 2 * weight
36}
37
38/// Generate the leaf vertices of a blossom.
39fn blossom_leaves<E>(
40 blossom: usize,
41 num_nodes: usize,
42 blossom_children: &[Vec<usize>],
43) -> Result<Vec<usize>, E> {
44 let mut out_vec: Vec<usize> = Vec::new();
45 if blossom < num_nodes {
46 out_vec.push(blossom);
47 } else {
48 let child_blossom = &blossom_children[blossom];
49 for c in child_blossom {
50 let child = *c;
51 if child < num_nodes {
52 out_vec.push(child);
53 } else {
54 for v in blossom_leaves(child, num_nodes, blossom_children)? {
55 out_vec.push(v);
56 }
57 }
58 }
59 }
60 Ok(out_vec)
61}
62
63/// Assign label t to the top-level blossom containing vertex w
64/// and record the fact that w was reached through the edge with
65/// remote endpoint p.
66fn assign_label<E>(
67 w: usize,
68 t: usize,
69 p: Option<usize>,
70 num_nodes: usize,
71 in_blossoms: &[usize],
72 labels: &mut Vec<Option<usize>>,
73 label_ends: &mut Vec<Option<usize>>,
74 best_edge: &mut Vec<Option<usize>>,
75 queue: &mut Vec<usize>,
76 blossom_children: &[Vec<usize>],
77 blossom_base: &[Option<usize>],
78 endpoints: &[usize],
79 mate: &HashMap<usize, usize>,
80) -> Result<(), E> {
81 let b = in_blossoms[w];
82 assert!(labels[w] == Some(0) && labels[b] == Some(0));
83 labels[w] = Some(t);
84 labels[b] = Some(t);
85 label_ends[b] = p;
86 label_ends[w] = p;
87 best_edge[w] = None;
88 best_edge[b] = None;
89 if t == 1 {
90 // b became an S-vertex/blossom; add it(s vertices) to the queue
91 queue.append(&mut blossom_leaves(b, num_nodes, blossom_children)?);
92 } else if t == 2 {
93 // b became a T-vertex/blossom; assign label S to its mate.
94 // (If b is a non-trivial blossom, its base is the only vertex
95 // with an external mate.)
96 let blossom_index: usize = b;
97 let base: usize = blossom_base[blossom_index].unwrap();
98 assert!(mate.get(&base).is_some());
99 assign_label(
100 endpoints[mate[&base]],
101 1,
102 mate.get(&base).map(|p| (p ^ 1)),
103 num_nodes,
104 in_blossoms,
105 labels,
106 label_ends,
107 best_edge,
108 queue,
109 blossom_children,
110 blossom_base,
111 endpoints,
112 mate,
113 )?;
114 }
115 Ok(())
116}
117
118/// Trace back from vertices v and w to discover either a new blossom
119/// or an augmenting path. Return the base vertex of the new blossom or None.
120fn scan_blossom(
121 node_a: usize,
122 node_b: usize,
123 in_blossoms: &[usize],
124 blossom_base: &[Option<usize>],
125 endpoints: &[usize],
126 labels: &mut [Option<usize>],
127 label_ends: &[Option<usize>],
128 mate: &HashMap<usize, usize>,
129) -> Option<usize> {
130 let mut v: Option<usize> = Some(node_a);
131 let mut w: Option<usize> = Some(node_b);
132 // Trace back from v and w, placing breadcrumbs as we go
133 let mut path: Vec<usize> = Vec::new();
134 let mut base: Option<usize> = None;
135 while v.is_some() || w.is_some() {
136 // Look for a breadcrumb in v's blossom or put a new breadcrump
137 let mut blossom = in_blossoms[v.unwrap()];
138 if labels[blossom].is_none() || labels[blossom].unwrap() & 4 > 0 {
139 base = blossom_base[blossom];
140 break;
141 }
142 assert!(labels[blossom] == Some(1));
143 path.push(blossom);
144 labels[blossom] = Some(5);
145 // Trace one step back.
146 assert!(label_ends[blossom] == mate.get(&blossom_base[blossom].unwrap()).copied());
147 if label_ends[blossom].is_none() {
148 // The base of blossom is single; stop tracing this path
149 v = None;
150 } else {
151 let tmp = endpoints[label_ends[blossom].unwrap()];
152 blossom = in_blossoms[tmp];
153 assert!(labels[blossom] == Some(2));
154 // blossom is a T-blossom; trace one more step back.
155 assert!(label_ends[blossom].is_some());
156 v = Some(endpoints[label_ends[blossom].unwrap()]);
157 }
158 // Swap v and w so that we alternate between both paths.
159 if w.is_some() {
160 mem::swap(&mut v, &mut w);
161 }
162 }
163 // Remove breadcrumbs.
164 for blossom in path {
165 labels[blossom] = Some(1);
166 }
167 // Return base vertex, if we found one.
168 base
169}
170
171/// Construct a new blossom with given base, containing edge k which
172/// connects a pair of S vertices. Label the new blossom as S; set its dual
173/// variable to zero; relabel its T-vertices to S and add them to the queue.
174fn add_blossom<E>(
175 base: usize,
176 edge: usize,
177 blossom_children: &mut [Vec<usize>],
178 num_nodes: usize,
179 edges: &[(usize, usize, i128)],
180 in_blossoms: &mut [usize],
181 dual_var: &mut [i128],
182 labels: &mut [Option<usize>],
183 label_ends: &mut [Option<usize>],
184 best_edge: &mut [Option<usize>],
185 queue: &mut Vec<usize>,
186 blossom_base: &mut [Option<usize>],
187 endpoints: &[usize],
188 blossom_endpoints: &mut [Vec<usize>],
189 unused_blossoms: &mut Vec<usize>,
190 blossom_best_edges: &mut [Vec<usize>],
191 blossom_parents: &mut [Option<usize>],
192 neighbor_endpoints: &[Vec<usize>],
193 mate: &HashMap<usize, usize>,
194) -> Result<(), E> {
195 let (mut v, mut w, _weight) = edges[edge];
196 let blossom_b = in_blossoms[base];
197 let mut blossom_v = in_blossoms[v];
198 let mut blossom_w = in_blossoms[w];
199 // Create blossom
200 let blossom = unused_blossoms.pop().unwrap();
201 blossom_base[blossom] = Some(base);
202 blossom_parents[blossom] = None;
203 blossom_parents[blossom_b] = Some(blossom);
204 // Make list of sub-blossoms and their interconnecting edge endpoints.
205 blossom_children[blossom].clear();
206 blossom_endpoints[blossom].clear();
207 // Trace back from blossom_v to base.
208 while blossom_v != blossom_b {
209 // Add blossom_v to the new blossom
210 blossom_parents[blossom_v] = Some(blossom);
211 blossom_children[blossom].push(blossom_v);
212 let blossom_v_endpoint_label = label_ends[blossom_v].unwrap();
213 blossom_endpoints[blossom].push(blossom_v_endpoint_label);
214 assert!(
215 labels[blossom_v] == Some(2)
216 || (labels[blossom_v] == Some(1)
217 && label_ends[blossom_v]
218 == mate.get(&blossom_base[blossom_v].unwrap()).copied())
219 );
220 // Trace one step back.
221 assert!(label_ends[blossom_v].is_some());
222 v = endpoints[blossom_v_endpoint_label];
223 blossom_v = in_blossoms[v];
224 }
225 // Reverse lists, add endpoint that connects the pair of S vertices.
226 blossom_children[blossom].push(blossom_b);
227 blossom_children[blossom].reverse();
228 blossom_endpoints[blossom].reverse();
229 blossom_endpoints[blossom].push(2 * edge);
230 // Trace back from w to base.
231 while blossom_w != blossom_b {
232 // Add blossom_w to the new blossom
233 blossom_parents[blossom_w] = Some(blossom);
234 blossom_children[blossom].push(blossom_w);
235 let blossom_w_endpoint_label = label_ends[blossom_w].unwrap();
236 blossom_endpoints[blossom].push(blossom_w_endpoint_label ^ 1);
237 assert!(
238 labels[blossom_w] == Some(2)
239 || (labels[blossom_w] == Some(1)
240 && label_ends[blossom_w]
241 == mate.get(&blossom_base[blossom_w].unwrap()).copied())
242 );
243 // Trace one step back
244 assert!(label_ends[blossom_w].is_some());
245 w = endpoints[blossom_w_endpoint_label];
246 blossom_w = in_blossoms[w];
247 }
248 // Set label to S.
249 assert!(labels[blossom_b] == Some(1));
250 labels[blossom] = Some(1);
251 label_ends[blossom] = label_ends[blossom_b];
252 // Set dual variable to 0
253 dual_var[blossom] = 0;
254 // Relabel vertices
255 for node in blossom_leaves(blossom, num_nodes, blossom_children)? {
256 if labels[in_blossoms[node]] == Some(2) {
257 // This T-vertex now turns into an S-vertex because it becomes
258 // part of an S-blossom; add it to the queue
259 queue.push(node);
260 }
261 in_blossoms[node] = blossom;
262 }
263 // Compute blossom_best_edges[blossom]
264 let mut best_edge_to: HashMap<usize, usize> = HashMap::with_capacity(2 * num_nodes);
265 for bv_ref in &blossom_children[blossom] {
266 let bv = *bv_ref;
267 // This sub-blossom does not have a list of least-slack edges;
268 // get the information from the vertices.
269 let nblists: Vec<Vec<usize>> = if blossom_best_edges[bv].is_empty() {
270 let mut tmp: Vec<Vec<usize>> = Vec::new();
271 for node in blossom_leaves(bv, num_nodes, blossom_children)? {
272 tmp.push(neighbor_endpoints[node].iter().map(|p| p / 2).collect());
273 }
274 tmp
275 } else {
276 // Walk this sub-blossom's least-slack edges.
277 vec![blossom_best_edges[bv].clone()]
278 };
279 for nblist in nblists {
280 for edge_index in nblist {
281 let (mut i, mut j, _edge_weight) = edges[edge_index];
282 if in_blossoms[j] == blossom {
283 mem::swap(&mut i, &mut j);
284 }
285 let blossom_j = in_blossoms[j];
286 if blossom_j != blossom
287 && labels[blossom_j] == Some(1)
288 && (best_edge_to.get(&blossom_j).is_none()
289 || slack(edge_index, dual_var, edges)
290 < slack(best_edge_to[&blossom_j], dual_var, edges))
291 {
292 best_edge_to.insert(blossom_j, edge_index);
293 }
294 }
295 }
296 // Forget about least-slack edges of the sub-blossom.
297 blossom_best_edges[bv].clear();
298 best_edge[bv] = None;
299 }
300 blossom_best_edges[blossom] = best_edge_to.values().copied().collect();
301 //select best_edge[blossom]
302 best_edge[blossom] = None;
303 for edge_index in &blossom_best_edges[blossom] {
304 if best_edge[blossom].is_none()
305 || slack(*edge_index, dual_var, edges)
306 < slack(best_edge[blossom].unwrap(), dual_var, edges)
307 {
308 best_edge[blossom] = Some(*edge_index);
309 }
310 }
311 Ok(())
312}
313
314/// Expand the given top level blossom
315fn expand_blossom<E>(
316 blossom: usize,
317 end_stage: bool,
318 num_nodes: usize,
319 blossom_children: &mut Vec<Vec<usize>>,
320 blossom_parents: &mut Vec<Option<usize>>,
321 in_blossoms: &mut Vec<usize>,
322 dual_var: &[i128],
323 labels: &mut Vec<Option<usize>>,
324 label_ends: &mut Vec<Option<usize>>,
325 best_edge: &mut Vec<Option<usize>>,
326 queue: &mut Vec<usize>,
327 blossom_base: &mut Vec<Option<usize>>,
328 endpoints: &[usize],
329 mate: &HashMap<usize, usize>,
330 blossom_endpoints: &mut Vec<Vec<usize>>,
331 allowed_edge: &mut Vec<bool>,
332 unused_blossoms: &mut Vec<usize>,
333) -> Result<(), E> {
334 // Convert sub-blossoms into top-level blossoms.
335 for s in blossom_children[blossom].clone() {
336 blossom_parents[s] = None;
337 if s < num_nodes {
338 in_blossoms[s] = s
339 } else if end_stage && dual_var[s] == 0 {
340 // Recursively expand this sub-blossom
341 expand_blossom(
342 s,
343 end_stage,
344 num_nodes,
345 blossom_children,
346 blossom_parents,
347 in_blossoms,
348 dual_var,
349 labels,
350 label_ends,
351 best_edge,
352 queue,
353 blossom_base,
354 endpoints,
355 mate,
356 blossom_endpoints,
357 allowed_edge,
358 unused_blossoms,
359 )?;
360 } else {
361 for v in blossom_leaves(s, num_nodes, blossom_children)? {
362 in_blossoms[v] = s;
363 }
364 }
365 }
366 // if we expand a T-blossom during a stage, its a sub-blossoms must be
367 // relabeled
368 if !end_stage && labels[blossom] == Some(2) {
369 // start at the sub-blossom through which the expanding blossom
370 // obtained its label, and relabel sub-blossoms until we reach the
371 // base.
372 assert!(label_ends[blossom].is_some());
373 let entry_child = in_blossoms[endpoints[label_ends[blossom].unwrap() ^ 1]];
374 // Decide in which direction we will go around the blossom.
375 let i = blossom_children[blossom]
376 .iter()
377 .position(|x| *x == entry_child)
378 .unwrap();
379 let mut j = i as i128;
380 let j_step: i128;
381 let endpoint_trick: usize = if i & 1 != 0 {
382 // Start index is odd; go forward and wrap
383 j -= blossom_children[blossom].len() as i128;
384 j_step = 1;
385 0
386 } else {
387 // start index is even; go backward
388 j_step = -1;
389 1
390 };
391 // Move along the blossom until we get to the base
392 let mut p = label_ends[blossom].unwrap();
393 while j != 0 {
394 // Relabel the T-sub-blossom.
395 labels[endpoints[p ^ 1]] = Some(0);
396 if j < 0 {
397 let length = blossom_endpoints[blossom].len();
398 let index = length - j.unsigned_abs() as usize;
399 labels[endpoints
400 [blossom_endpoints[blossom][index - endpoint_trick] ^ endpoint_trick ^ 1]] =
401 Some(0);
402 } else {
403 labels[endpoints[blossom_endpoints[blossom][j as usize - endpoint_trick]
404 ^ endpoint_trick
405 ^ 1]] = Some(0);
406 }
407 assign_label(
408 endpoints[p ^ 1],
409 2,
410 Some(p),
411 num_nodes,
412 in_blossoms,
413 labels,
414 label_ends,
415 best_edge,
416 queue,
417 blossom_children,
418 blossom_base,
419 endpoints,
420 mate,
421 )?;
422 // Step to the next S-sub-blossom and note it's forwward endpoint.
423 let endpoint_index = if j < 0 {
424 let tmp = j - endpoint_trick as i128;
425 let length = blossom_endpoints[blossom].len();
426 let index = length - tmp.unsigned_abs() as usize;
427 blossom_endpoints[blossom][index]
428 } else {
429 blossom_endpoints[blossom][j as usize - endpoint_trick]
430 };
431 allowed_edge[endpoint_index / 2] = true;
432 j += j_step;
433 p = if j < 0 {
434 let tmp = j - endpoint_trick as i128;
435 let length = blossom_endpoints[blossom].len();
436 let index = length - tmp.unsigned_abs() as usize;
437 blossom_endpoints[blossom][index] ^ endpoint_trick
438 } else {
439 blossom_endpoints[blossom][j as usize - endpoint_trick] ^ endpoint_trick
440 };
441 // Step to the next T-sub-blossom.
442 allowed_edge[p / 2] = true;
443 j += j_step;
444 }
445 // Relabel the base T-sub-blossom WITHOUT stepping through to
446 // its mate (so don't call assign_label())
447 let blossom_v = if j < 0 {
448 let length = blossom_children[blossom].len();
449 let index = length - j.unsigned_abs() as usize;
450 blossom_children[blossom][index]
451 } else {
452 blossom_children[blossom][j as usize]
453 };
454 labels[endpoints[p ^ 1]] = Some(2);
455 labels[blossom_v] = Some(2);
456 label_ends[endpoints[p ^ 1]] = Some(p);
457 label_ends[blossom_v] = Some(p);
458 best_edge[blossom_v] = None;
459 // Continue along the blossom until we get back to entry_child
460 j += j_step;
461 let mut j_index = if j < 0 {
462 let length = blossom_children[blossom].len();
463 length - j.unsigned_abs() as usize
464 } else {
465 j as usize
466 };
467 while blossom_children[blossom][j_index] != entry_child {
468 // Examine the vertices of the sub-blossom to see whether
469 // it is reachable from a neighboring S-vertex outside the
470 // expanding blososm.
471 let bv = blossom_children[blossom][j_index];
472 if labels[bv] == Some(1) {
473 // This sub-blossom just got label S through one of its
474 // neighbors; leave it
475 j += j_step;
476 if j < 0 {
477 let length = blossom_children[blossom].len();
478 j_index = length - j.unsigned_abs() as usize;
479 } else {
480 j_index = j as usize;
481 }
482 continue;
483 }
484 let mut v: usize = 0;
485 for tmp in blossom_leaves(bv, num_nodes, blossom_children)? {
486 v = tmp;
487 if labels[v].unwrap() != 0 {
488 break;
489 }
490 }
491 // If the sub-blossom contains a reachable vertex, assign label T
492 // to the sub-blossom
493 if labels[v] != Some(0) {
494 assert!(labels[v] == Some(2));
495 assert!(in_blossoms[v] == bv);
496 labels[v] = Some(0);
497 labels[endpoints[mate[&blossom_base[bv].unwrap()]]] = Some(0);
498 assign_label(
499 v,
500 2,
501 label_ends[v],
502 num_nodes,
503 in_blossoms,
504 labels,
505 label_ends,
506 best_edge,
507 queue,
508 blossom_children,
509 blossom_base,
510 endpoints,
511 mate,
512 )?;
513 }
514 j += j_step;
515 if j < 0 {
516 let length = blossom_children[blossom].len();
517 j_index = length - j.unsigned_abs() as usize;
518 } else {
519 j_index = j as usize;
520 }
521 }
522 }
523 // Recycle the blossom number.
524 labels[blossom] = None;
525 label_ends[blossom] = None;
526 blossom_children[blossom].clear();
527 blossom_endpoints[blossom].clear();
528 blossom_base[blossom] = None;
529 best_edge[blossom] = None;
530 unused_blossoms.push(blossom);
531 Ok(())
532}
533
534/// Swap matched/unmatched edges over an alternating path through blossom b
535/// between vertex v and the base vertex. Keep blossom bookkeeping consistent.
536fn augment_blossom(
537 blossom: usize,
538 node: usize,
539 num_nodes: usize,
540 blossom_parents: &[Option<usize>],
541 endpoints: &[usize],
542 blossom_children: &mut Vec<Vec<usize>>,
543 blossom_endpoints: &mut Vec<Vec<usize>>,
544 blossom_base: &mut Vec<Option<usize>>,
545 mate: &mut HashMap<usize, usize>,
546) {
547 // Bubble up through the blossom tree from vertex v to an immediate
548 // sub-blossom of b.
549 let mut tmp = node;
550 while blossom_parents[tmp] != Some(blossom) {
551 tmp = blossom_parents[tmp].unwrap();
552 }
553 // Recursively deal with the first sub-blossom.
554 if tmp >= num_nodes {
555 augment_blossom(
556 tmp,
557 node,
558 num_nodes,
559 blossom_parents,
560 endpoints,
561 blossom_children,
562 blossom_endpoints,
563 blossom_base,
564 mate,
565 );
566 }
567 // Decide in which direction we will go around the blossom.
568 let i = blossom_children[blossom]
569 .iter()
570 .position(|x| *x == tmp)
571 .unwrap();
572 let mut j: i128 = i as i128;
573 let j_step: i128;
574 let endpoint_trick: usize = if i & 1 != 0 {
575 // start index is odd; go forward and wrap.
576 j -= blossom_children[blossom].len() as i128;
577 j_step = 1;
578 0
579 } else {
580 // Start index is even; go backward.
581 j_step = -1;
582 1
583 };
584 // Move along the blossom until we get to the base.
585 while j != 0 {
586 // Step to the next sub-blossom and augment it recursively.
587 j += j_step;
588
589 tmp = if j < 0 {
590 let length = blossom_children[blossom].len();
591 let index = length - j.unsigned_abs() as usize;
592 blossom_children[blossom][index]
593 } else {
594 blossom_children[blossom][j as usize]
595 };
596 let p = if j < 0 {
597 let length = blossom_endpoints[blossom].len();
598 let index = length - j.unsigned_abs() as usize - endpoint_trick;
599 blossom_endpoints[blossom][index] ^ endpoint_trick
600 } else {
601 blossom_endpoints[blossom][j as usize - endpoint_trick] ^ endpoint_trick
602 };
603 if tmp > num_nodes {
604 augment_blossom(
605 tmp,
606 endpoints[p],
607 num_nodes,
608 blossom_parents,
609 endpoints,
610 blossom_children,
611 blossom_endpoints,
612 blossom_base,
613 mate,
614 );
615 }
616 j += j_step;
617 if j < 0 {
618 let length = blossom_children[blossom].len();
619 let index = length - j.unsigned_abs() as usize;
620 tmp = blossom_children[blossom][index];
621 } else {
622 tmp = blossom_children[blossom][j as usize];
623 }
624 if tmp > num_nodes {
625 augment_blossom(
626 tmp,
627 endpoints[p ^ 1],
628 num_nodes,
629 blossom_parents,
630 endpoints,
631 blossom_children,
632 blossom_endpoints,
633 blossom_base,
634 mate,
635 );
636 }
637 // Match the edge connecting those sub-blossoms.
638 mate.insert(endpoints[p], p ^ 1);
639 mate.insert(endpoints[p ^ 1], p);
640 }
641 // Rotate the list of sub-blossoms to put the new base at the front.
642 let mut children: Vec<usize> = blossom_children[blossom][i..].to_vec();
643 children.extend_from_slice(&blossom_children[blossom][..i]);
644 blossom_children[blossom] = children;
645 let mut endpoint: Vec<usize> = blossom_endpoints[blossom][i..].to_vec();
646 endpoint.extend_from_slice(&blossom_endpoints[blossom][..i]);
647 blossom_endpoints[blossom] = endpoint;
648 blossom_base[blossom] = blossom_base[blossom_children[blossom][0]];
649 assert!(blossom_base[blossom] == Some(node));
650}
651
652/// Swap matched/unmatched edges over an alternating path between two
653/// single vertices. The augmenting path runs through edge k, which
654/// connects a pair of S vertices.
655fn augment_matching(
656 edge: usize,
657 num_nodes: usize,
658 edges: &[(usize, usize, i128)],
659 in_blossoms: &[usize],
660 labels: &[Option<usize>],
661 label_ends: &[Option<usize>],
662 blossom_parents: &[Option<usize>],
663 endpoints: &[usize],
664 blossom_children: &mut Vec<Vec<usize>>,
665 blossom_endpoints: &mut Vec<Vec<usize>>,
666 blossom_base: &mut Vec<Option<usize>>,
667 mate: &mut HashMap<usize, usize>,
668) {
669 let (v, w, _weight) = edges[edge];
670 for (s_ref, p_ref) in [(v, 2 * edge + 1), (w, 2 * edge)].iter() {
671 // Match vertex s to remote endpoint p. Then trace back from s
672 // until we find a single vertex, swapping matched and unmatched
673 // edges as we go.
674 let mut s: usize = *s_ref;
675 let mut p: usize = *p_ref;
676 loop {
677 let blossom_s = in_blossoms[s];
678 assert!(labels[blossom_s] == Some(1));
679 assert!(label_ends[blossom_s] == mate.get(&blossom_base[blossom_s].unwrap()).copied());
680 // Augment through the S-blossom from s to base.
681 if blossom_s >= num_nodes {
682 augment_blossom(
683 blossom_s,
684 s,
685 num_nodes,
686 blossom_parents,
687 endpoints,
688 blossom_children,
689 blossom_endpoints,
690 blossom_base,
691 mate,
692 );
693 }
694 // Update mate[s]
695 mate.insert(s, p);
696 // Trace one step back.
697 if label_ends[blossom_s].is_none() {
698 // Reached a single vertex; stop
699 break;
700 }
701 let t = endpoints[label_ends[blossom_s].unwrap()];
702 let blossom_t = in_blossoms[t];
703 assert!(labels[blossom_t] == Some(2));
704 // Trace one step back
705 assert!(label_ends[blossom_t].is_some());
706 s = endpoints[label_ends[blossom_t].unwrap()];
707 let j = endpoints[label_ends[blossom_t].unwrap() ^ 1];
708 // Augment through the T-blossom from j to base.
709 assert!(blossom_base[blossom_t] == Some(t));
710 if blossom_t >= num_nodes {
711 augment_blossom(
712 blossom_t,
713 j,
714 num_nodes,
715 blossom_parents,
716 endpoints,
717 blossom_children,
718 blossom_endpoints,
719 blossom_base,
720 mate,
721 );
722 }
723 // Update mate[j]
724 mate.insert(j, label_ends[blossom_t].unwrap());
725 // Keep the opposite endpoint;
726 // it will be assigned to mate[s] in the next step.
727 p = label_ends[blossom_t].unwrap() ^ 1;
728 }
729 }
730}
731
732/// Swap matched/unmatched edges over an alternating path between two
733/// single vertices. The augmenting path runs through the edge, which
734/// connects a pair of S vertices.
735fn verify_optimum(
736 max_cardinality: bool,
737 num_nodes: usize,
738 num_edges: usize,
739 edges: &[(usize, usize, i128)],
740 endpoints: &[usize],
741 dual_var: &[i128],
742 blossom_parents: &[Option<usize>],
743 blossom_endpoints: &[Vec<usize>],
744 blossom_base: &[Option<usize>],
745 mate: &HashMap<usize, usize>,
746) {
747 let dual_var_node_min: i128 = *dual_var[..num_nodes].iter().min().unwrap();
748 let node_dual_offset: i128 = if max_cardinality {
749 // Vertices may have negative dual;
750 // find a constant non-negative number to add to all vertex duals.
751 max(0, -dual_var_node_min)
752 } else {
753 0
754 };
755 assert!(dual_var_node_min + node_dual_offset >= 0);
756 assert!(*dual_var[num_nodes..].iter().min().unwrap() >= 0);
757 // 0. all edges have non-negative slack and
758 // 1. all matched edges have zero slack;
759 for (edge, (i, j, weight)) in edges.iter().enumerate().take(num_edges) {
760 let mut s = dual_var[*i] + dual_var[*j] - 2 * weight;
761 let mut i_blossoms: Vec<usize> = vec![*i];
762 let mut j_blossoms: Vec<usize> = vec![*j];
763 while blossom_parents[*i_blossoms.last().unwrap()].is_some() {
764 i_blossoms.push(blossom_parents[*i_blossoms.last().unwrap()].unwrap());
765 }
766 while blossom_parents[*j_blossoms.last().unwrap()].is_some() {
767 j_blossoms.push(blossom_parents[*j_blossoms.last().unwrap()].unwrap());
768 }
769 i_blossoms.reverse();
770 j_blossoms.reverse();
771 for (blossom_i, blossom_j) in i_blossoms.iter().zip(j_blossoms.iter()) {
772 if blossom_i != blossom_j {
773 break;
774 }
775 s += 2 * dual_var[*blossom_i];
776 }
777 assert!(s >= 0);
778
779 if (mate.get(i).is_some() && mate.get(i).unwrap() / 2 == edge)
780 || (mate.get(j).is_some() && mate.get(j).unwrap() / 2 == edge)
781 {
782 assert!(mate[i] / 2 == edge && mate[j] / 2 == edge);
783 assert!(s == 0);
784 }
785 }
786 // 2. all single vertices have zero dual value;
787 for (node, dual_var_node) in dual_var.iter().enumerate().take(num_nodes) {
788 assert!(mate.get(&node).is_some() || dual_var_node + node_dual_offset == 0);
789 }
790 // 3. all blossoms with positive dual value are full.
791 for blossom in num_nodes..2 * num_nodes {
792 if blossom_base[blossom].is_some() && dual_var[blossom] > 0 {
793 assert!(blossom_endpoints[blossom].len() % 2 == 1);
794 for p in blossom_endpoints[blossom].iter().skip(1).step_by(2) {
795 assert!(mate.get(&endpoints[*p]).copied() == Some(p ^ 1));
796 assert!(mate.get(&endpoints[*p ^ 1]) == Some(p));
797 }
798 }
799 }
800}
801
802/// Compute a maximum-weighted matching in the general undirected weighted
803/// graph given by "edges". If `max_cardinality` is true, only
804/// maximum-cardinality matchings are considered as solutions.
805///
806/// The algorithm is based on "Efficient Algorithms for Finding Maximum
807/// Matching in Graphs" by Zvi Galil, ACM Computing Surveys, 1986.
808///
809/// Based on networkx implementation
810/// <https://github.com/networkx/networkx/blob/3351206a3ce5b3a39bb2fc451e93ef545b96c95b/networkx/algorithms/matching.py>
811///
812/// With reference to the standalone prototype implementation from:
813/// <http://jorisvr.nl/article/maximum-matching>
814///
815/// <http://jorisvr.nl/files/graphmatching/20130407/mwmatching.py>
816///
817/// The function takes time O(n**3)
818///
819/// Arguments:
820///
821/// * `graph` - The undirected graph to compute the maximum weight matching for
822/// * `max_cardinality` - If set to true compute the maximum-cardinality matching
823/// with maximum weight among all maximum-cardinality matchings
824/// * `weight_fn` - A callback function that will be give a edge reference and
825/// expected to return a `i128` representing the weight of the edge
826/// * `verify_optimum_flag`: If true an prior to returning an additional routine
827/// to verify the optimal solution was found will be run after computing
828/// the maximum weight matching. If it's true and the found matching is not
829/// an optimal solution this function will panic. This option should
830/// normally be only set true during testing.
831///
832/// # Example
833/// ```rust
834/// use rustworkx_core::petgraph;
835/// use rustworkx_core::max_weight_matching::max_weight_matching;
836/// use rustworkx_core::Result;
837///
838/// use hashbrown::HashSet;
839///
840/// // Create a path graph
841/// let g = petgraph::graph::UnGraph::<i32, i128>::from_edges(&[
842/// (1, 2, 5), (2, 3, 11), (3, 4, 5)
843/// ]);
844///
845/// // Run max weight matching with max cardinality set to false
846/// let res: Result<HashSet<(usize, usize)>> = max_weight_matching(
847/// &g, false, |e| Ok(*e.weight()), true
848/// );
849/// // Run max weight matching with max cardinality set to true
850/// let maxc_res: Result<HashSet<(usize, usize)>> = max_weight_matching(
851/// &g, true, |e| Ok(*e.weight()), true
852/// );
853///
854/// let matching = res.unwrap();
855/// let maxc_matching = maxc_res.unwrap();
856/// // Check output
857/// assert_eq!(matching.len(), 1);
858/// assert!(matching.contains(&(2, 3)) || matching.contains(&(3, 2)));
859/// assert_eq!(maxc_matching.len(), 2);
860/// assert!(maxc_matching.contains(&(1, 2)) || maxc_matching.contains(&(2, 1)));
861/// assert!(maxc_matching.contains(&(3, 4)) || maxc_matching.contains(&(4, 3)));
862/// ```
863pub fn max_weight_matching<G, F, E>(
864 graph: G,
865 max_cardinality: bool,
866 mut weight_fn: F,
867 verify_optimum_flag: bool,
868) -> Result<HashSet<(usize, usize)>, E>
869where
870 G: EdgeCount
871 + NodeCount
872 + IntoNodeIdentifiers
873 + GraphProp<EdgeType = Undirected>
874 + GraphBase
875 + NodeIndexable
876 + IntoEdges,
877 F: FnMut(G::EdgeRef) -> Result<i128, E>,
878 <G as GraphBase>::NodeId: std::cmp::Eq + Hash,
879{
880 let num_edges = graph.edge_count();
881 let num_nodes = graph.node_count();
882 let mut out_set: HashSet<(usize, usize)> = HashSet::with_capacity(num_nodes);
883 // Exit fast for graph without edges
884 if num_edges == 0 {
885 return Ok(HashSet::new());
886 }
887 // Node indices in the PyGraph may not be contiguous however the
888 // algorithm operates on contiguous indices 0..num_nodes. node_map maps
889 // the PyGraph's NodeIndex to the contiguous usize used inside the
890 // algorithm
891 let node_map: HashMap<G::NodeId, usize> = graph
892 .node_identifiers()
893 .enumerate()
894 .map(|(index, node_index)| (node_index, index))
895 .collect();
896 let mut edges: Vec<(usize, usize, i128)> = Vec::with_capacity(num_edges);
897 let mut max_weight: i128 = 0;
898 for edge in graph.edge_references() {
899 let edge_weight: i128 = weight_fn(edge)?;
900 if edge_weight > max_weight {
901 max_weight = edge_weight;
902 };
903 edges.push((
904 node_map[&edge.source()],
905 node_map[&edge.target()],
906 edge_weight,
907 ));
908 }
909 // If p is an edge endpoint
910 // endpoints[p] is the node index to which endpoint p is attached
911 let endpoints: Vec<usize> = (0..2 * num_edges)
912 .map(|endpoint| {
913 let edge_tuple = edges[endpoint / 2];
914 let out_value: usize = if endpoint % 2 == 0 {
915 edge_tuple.0
916 } else {
917 edge_tuple.1
918 };
919 out_value
920 })
921 .collect();
922 // If v is a node/vertex
923 // neighbor_endpoints[v] is the list of remote endpoints of the edges
924 // attached to v.
925 // Not modified by algorithm (only mut to initially construct contents).
926 let mut neighbor_endpoints: Vec<Vec<usize>> = (0..num_nodes).map(|_| Vec::new()).collect();
927 for edge in 0..num_edges {
928 neighbor_endpoints[edges[edge].0].push(2 * edge + 1);
929 neighbor_endpoints[edges[edge].1].push(2 * edge);
930 }
931 // If v is a vertex,
932 // mate[v] is the remote endpoint of its matched edge, or None if it is
933 // single (i.e. endpoint[mate[v]] is v's partner vertex).
934 // Initially all vertices are single; updated during augmentation.
935 let mut mate: HashMap<usize, usize> = HashMap::with_capacity(num_nodes);
936 // If b is a top-level blossom
937 // label[b] is 0 if b is unlabeled (free);
938 // 1 if b is a S-vertex/blossom;
939 // 2 if b is a T-vertex/blossom;
940 // The label of a vertex/node is found by looking at the label of its
941 // top-level containing blossom
942 // If v is a node/vertex inside a T-Blossom,
943 // label[v] is 2 if and only if v is reachable from an S_Vertex outside
944 // the blossom.
945 let mut labels: Vec<Option<usize>>;
946 // If b is a labeled top-level blossom,
947 // label_ends[b] is the remote endpoint of the edge through which b
948 // obtained its label, or None if b's base vertex is single.
949 // If v is a vertex inside a T-blossom and label[v] == 2,
950 // label_ends[v] is the remote endpoint of the edge through which v is
951 // reachable from outside the blossom
952 let mut label_ends: Vec<Option<usize>>;
953 // If v is a vertex/node
954 // in_blossoms[v] is the top-level blossom to which v belongs.
955 // If v is a top-level vertex, v is itself a blossom (a trivial blossom)
956 // and in_blossoms[v] == v.
957 // Initially all nodes are top-level trivial blossoms.
958 let mut in_blossoms: Vec<usize> = (0..num_nodes).collect();
959 // if b is a sub-blossom
960 // blossom_parents[b] is its immediate parent
961 // If b is a top-level blossom, blossom_parents[b] is None
962 let mut blossom_parents: Vec<Option<usize>> = vec![None; 2 * num_nodes];
963 // If b is a non-trivial (sub-)blossom,
964 // blossom_children[b] is an ordered list of its sub-blossoms, starting with
965 // the base and going round the blossom.
966 let mut blossom_children: Vec<Vec<usize>> = (0..2 * num_nodes).map(|_| Vec::new()).collect();
967 // If b is a (sub-)blossom,
968 // blossombase[b] is its base VERTEX (i.e. recursive sub-blossom).
969 let mut blossom_base: Vec<Option<usize>> = (0..num_nodes).map(Some).collect();
970 blossom_base.append(&mut vec![None; num_nodes]);
971 // If b is a non-trivial (sub-)blossom,
972 // blossom_endpoints[b] is a list of endpoints on its connecting edges,
973 // such that blossom_endpoints[b][i] is the local endpoint of
974 // blossom_children[b][i] on the edge that connects it to
975 // blossom_children[b][wrap(i+1)].
976 let mut blossom_endpoints: Vec<Vec<usize>> = (0..2 * num_nodes).map(|_| Vec::new()).collect();
977 // If v is a free vertex (or an unreached vertex inside a T-blossom),
978 // best_edge[v] is the edge to an S-vertex with least slack,
979 // or None if there is no such edge. If b is a (possibly trivial)
980 // top-level S-blossom,
981 // best_edge[b] is the least-slack edge to a different S-blossom,
982 // or None if there is no such edge.
983 let mut best_edge: Vec<Option<usize>>;
984 // If b is a non-trivial top-level S-blossom,
985 // blossom_best_edges[b] is a list of least-slack edges to neighboring
986 // S-blossoms, or None if no such list has been computed yet.
987 // This is used for efficient computation of delta3.
988 let mut blossom_best_edges: Vec<Vec<usize>> = (0..2 * num_nodes).map(|_| Vec::new()).collect();
989 let mut unused_blossoms: Vec<usize> = (num_nodes..2 * num_nodes).collect();
990 // If v is a vertex,
991 // dual_var[v] = 2 * u(v) where u(v) is the v's variable in the dual
992 // optimization problem (multiplication by two ensures integer values
993 // throughout the algorithm if all edge weights are integers).
994 // If b is a non-trivial blossom,
995 // dual_var[b] = z(b) where z(b) is b's variable in the dual optimization
996 // problem.
997 // dual_var is for vertex v in 0..num_nodes and blossom b in
998 // num_nodes..2* num nodes
999 let mut dual_var: Vec<i128> = vec![max_weight; num_nodes];
1000 dual_var.append(&mut vec![0; num_nodes]);
1001 // If allowed_edge[k] is true, edge k has zero slack in the optimization
1002 // problem; if allowed_edge[k] is false, the edge's slack may or may not
1003 // be zero.
1004 let mut allowed_edge: Vec<bool>;
1005 // Queue of newly discovered S-vertices
1006 let mut queue: Vec<usize> = Vec::with_capacity(num_nodes);
1007
1008 // Main loop: continue until no further improvement is possible
1009 for _ in 0..num_nodes {
1010 // Each iteration of this loop is a "stage".
1011 // A stage finds an augmenting path and uses that to improve
1012 // the matching.
1013
1014 // Removal labels from top-level blossoms/vertices
1015 labels = vec![Some(0); 2 * num_nodes];
1016 label_ends = vec![None; 2 * num_nodes];
1017 // Forget all about least-slack edges.
1018 best_edge = vec![None; 2 * num_nodes];
1019 blossom_best_edges.splice(num_nodes.., (0..num_nodes).map(|_| Vec::new()));
1020 // Loss of labeling means that we can not be sure that currently
1021 // allowable edges remain allowable throughout this stage.
1022 allowed_edge = vec![false; num_edges];
1023 // Make queue empty
1024 queue.clear();
1025 // Label single blossom/vertices with S and put them in queue.
1026 for v in 0..num_nodes {
1027 if mate.get(&v).is_none() && labels[in_blossoms[v]] == Some(0) {
1028 assign_label(
1029 v,
1030 1,
1031 None,
1032 num_nodes,
1033 &in_blossoms,
1034 &mut labels,
1035 &mut label_ends,
1036 &mut best_edge,
1037 &mut queue,
1038 &blossom_children,
1039 &blossom_base,
1040 &endpoints,
1041 &mate,
1042 )?;
1043 }
1044 }
1045 // Loop until we succeed in augmenting the matching.
1046 let mut augmented = false;
1047 loop {
1048 // Each iteration of this loop is a "substage".
1049 // A substage tries to find an augmenting path;
1050 // if found, the path is used to improve the matching and
1051 // the stage ends. If there is no augmenting path, the
1052 // primal-dual method is used to find some slack out of
1053 // the dual variables.
1054
1055 // Continue labeling until all vertices which are reachable
1056 // through an alternating path have got a label.
1057 while !queue.is_empty() && !augmented {
1058 // Take an S vertex from the queue
1059 let v = queue.pop().unwrap();
1060 assert!(labels[in_blossoms[v]] == Some(1));
1061
1062 // Scan its neighbors
1063 for p in &neighbor_endpoints[v] {
1064 let k = *p / 2;
1065 let mut kslack = 0;
1066 let w = endpoints[*p];
1067 // w is a neighbor of v
1068 if in_blossoms[v] == in_blossoms[w] {
1069 // this edge is internal to a blossom; ignore it
1070 continue;
1071 }
1072 if !allowed_edge[k] {
1073 kslack = slack(k, &dual_var, &edges);
1074 if kslack <= 0 {
1075 // edge k has zero slack -> it is allowable
1076 allowed_edge[k] = true;
1077 }
1078 }
1079 if allowed_edge[k] {
1080 if labels[in_blossoms[w]] == Some(0) {
1081 // (C1) w is a free vertex;
1082 // label w with T and label its mate with S (R12).
1083 assign_label(
1084 w,
1085 2,
1086 Some(*p ^ 1),
1087 num_nodes,
1088 &in_blossoms,
1089 &mut labels,
1090 &mut label_ends,
1091 &mut best_edge,
1092 &mut queue,
1093 &blossom_children,
1094 &blossom_base,
1095 &endpoints,
1096 &mate,
1097 )?;
1098 } else if labels[in_blossoms[w]] == Some(1) {
1099 // (C2) w is an S-vertex (not in the same blossom);
1100 // follow back-links to discover either an
1101 // augmenting path or a new blossom.
1102 let base = scan_blossom(
1103 v,
1104 w,
1105 &in_blossoms,
1106 &blossom_base,
1107 &endpoints,
1108 &mut labels,
1109 &label_ends,
1110 &mate,
1111 );
1112 match base {
1113 // Found a new blossom; add it to the blossom
1114 // bookkeeping and turn it into an S-blossom.
1115 Some(base) => add_blossom(
1116 base,
1117 k,
1118 &mut blossom_children,
1119 num_nodes,
1120 &edges,
1121 &mut in_blossoms,
1122 &mut dual_var,
1123 &mut labels,
1124 &mut label_ends,
1125 &mut best_edge,
1126 &mut queue,
1127 &mut blossom_base,
1128 &endpoints,
1129 &mut blossom_endpoints,
1130 &mut unused_blossoms,
1131 &mut blossom_best_edges,
1132 &mut blossom_parents,
1133 &neighbor_endpoints,
1134 &mate,
1135 )?,
1136 // Found an augmenting path; augment the
1137 // matching and end this stage.
1138 None => {
1139 augment_matching(
1140 k,
1141 num_nodes,
1142 &edges,
1143 &in_blossoms,
1144 &labels,
1145 &label_ends,
1146 &blossom_parents,
1147 &endpoints,
1148 &mut blossom_children,
1149 &mut blossom_endpoints,
1150 &mut blossom_base,
1151 &mut mate,
1152 );
1153 augmented = true;
1154 break;
1155 }
1156 };
1157 } else if labels[w] == Some(0) {
1158 // w is inside a T-blossom, but w itself has not
1159 // yet been reached from outside the blossom;
1160 // mark it as reached (we need this to relabel
1161 // during T-blossom expansion).
1162 assert!(labels[in_blossoms[w]] == Some(2));
1163 labels[w] = Some(2);
1164 label_ends[w] = Some(*p ^ 1);
1165 }
1166 } else if labels[in_blossoms[w]] == Some(1) {
1167 // Keep track of the least-slack non-allowable edge to
1168 // a different S-blossom
1169 let blossom = in_blossoms[v];
1170 if best_edge[blossom].is_none()
1171 || kslack < slack(best_edge[blossom].unwrap(), &dual_var, &edges)
1172 {
1173 best_edge[blossom] = Some(k);
1174 }
1175 } else if labels[w] == Some(0) {
1176 // w is a free vertex (or an unreached vertex inside
1177 // a T-blossom) but we can not reach it yet;
1178 // keep track of the least-slack edge that reaches w.
1179 if best_edge[w].is_none()
1180 || kslack < slack(best_edge[w].unwrap(), &dual_var, &edges)
1181 {
1182 best_edge[w] = Some(k)
1183 }
1184 }
1185 }
1186 }
1187 if augmented {
1188 break;
1189 }
1190 // There is no augmenting path under these constraints;
1191 // compute delta and reduce slack in the optimization problem.
1192 // (Note that our vertex dual variables, edge slacks and delta's
1193 // are pre-multiplied by two.)
1194 let mut delta_type = -1;
1195 let mut delta: Option<i128> = None;
1196 let mut delta_edge: Option<usize> = None;
1197 let mut delta_blossom: Option<usize> = None;
1198
1199 // Compute delta1: the minimum value of any vertex dual.
1200 if !max_cardinality {
1201 delta_type = 1;
1202 delta = Some(*dual_var[..num_nodes].iter().min().unwrap());
1203 }
1204
1205 // Compute delta2: the minimum slack on any edge between
1206 // an S-vertex and a free vertex.
1207 for v in 0..num_nodes {
1208 if labels[in_blossoms[v]] == Some(0) && best_edge[v].is_some() {
1209 let d = slack(best_edge[v].unwrap(), &dual_var, &edges);
1210 if delta_type == -1 || Some(d) < delta {
1211 delta = Some(d);
1212 delta_type = 2;
1213 delta_edge = best_edge[v];
1214 }
1215 }
1216 }
1217
1218 // Compute delta3: half the minimum slack on any edge between a
1219 // pair of S-blossoms
1220 for blossom in 0..2 * num_nodes {
1221 if blossom_parents[blossom].is_none()
1222 && labels[blossom] == Some(1)
1223 && best_edge[blossom].is_some()
1224 {
1225 let kslack = slack(best_edge[blossom].unwrap(), &dual_var, &edges);
1226 assert!(kslack % 2 == 0);
1227 let d = Some(kslack / 2);
1228 if delta_type == -1 || d < delta {
1229 delta = d;
1230 delta_type = 3;
1231 delta_edge = best_edge[blossom];
1232 }
1233 }
1234 }
1235
1236 // Compute delta4: minimum z variable of any T-blossom
1237 for blossom in num_nodes..2 * num_nodes {
1238 if blossom_base[blossom].is_some()
1239 && blossom_parents[blossom].is_none()
1240 && labels[blossom] == Some(2)
1241 && (delta_type == -1 || dual_var[blossom] < delta.unwrap())
1242 {
1243 delta = Some(dual_var[blossom]);
1244 delta_type = 4;
1245 delta_blossom = Some(blossom);
1246 }
1247 }
1248 if delta_type == -1 {
1249 // No further improvement possible; max-cardinality optimum
1250 // reached. Do a final delta update to make the optimum
1251 // verifiable
1252 assert!(max_cardinality);
1253 delta_type = 1;
1254 delta = Some(max(0, *dual_var[..num_nodes].iter().min().unwrap()));
1255 }
1256
1257 // Update dual variables according to delta.
1258 for v in 0..num_nodes {
1259 if labels[in_blossoms[v]] == Some(1) {
1260 // S-vertex: 2*u = 2*u - 2*delta
1261 dual_var[v] -= delta.unwrap();
1262 } else if labels[in_blossoms[v]] == Some(2) {
1263 // T-vertex: 2*u = 2*u + 2*delta
1264 dual_var[v] += delta.unwrap();
1265 }
1266 }
1267 for b in num_nodes..2 * num_nodes {
1268 if blossom_base[b].is_some() && blossom_parents[b].is_none() {
1269 if labels[b] == Some(1) {
1270 // top-level S-blossom: z = z + 2*delta
1271 dual_var[b] += delta.unwrap();
1272 } else if labels[b] == Some(2) {
1273 // top-level T-blossom: z = z - 2*delta
1274 dual_var[b] -= delta.unwrap();
1275 }
1276 }
1277 }
1278 // Take action at the point where minimum delta occurred.
1279 if delta_type == 1 {
1280 // No further improvement possible; optimum reached
1281 break;
1282 } else if delta_type == 2 {
1283 // Use the least-slack edge to continue the search.
1284 allowed_edge[delta_edge.unwrap()] = true;
1285 let (mut i, mut j, _weight) = edges[delta_edge.unwrap()];
1286 if labels[in_blossoms[i]] == Some(0) {
1287 mem::swap(&mut i, &mut j);
1288 }
1289 assert!(labels[in_blossoms[i]] == Some(1));
1290 queue.push(i);
1291 } else if delta_type == 3 {
1292 // Use the least-slack edge to continue the search.
1293 allowed_edge[delta_edge.unwrap()] = true;
1294 let (i, _j, _weight) = edges[delta_edge.unwrap()];
1295 assert!(labels[in_blossoms[i]] == Some(1));
1296 queue.push(i);
1297 } else if delta_type == 4 {
1298 // Expand the least-z blossom
1299 expand_blossom(
1300 delta_blossom.unwrap(),
1301 false,
1302 num_nodes,
1303 &mut blossom_children,
1304 &mut blossom_parents,
1305 &mut in_blossoms,
1306 &dual_var,
1307 &mut labels,
1308 &mut label_ends,
1309 &mut best_edge,
1310 &mut queue,
1311 &mut blossom_base,
1312 &endpoints,
1313 &mate,
1314 &mut blossom_endpoints,
1315 &mut allowed_edge,
1316 &mut unused_blossoms,
1317 )?;
1318 }
1319 // end of this substage
1320 }
1321 // Stop when no more augment paths can be found
1322 if !augmented {
1323 break;
1324 }
1325
1326 // End of a stage; expand all S-blossoms which have a dual_var == 0
1327 for blossom in num_nodes..2 * num_nodes {
1328 if blossom_parents[blossom].is_none()
1329 && blossom_base[blossom].is_some()
1330 && labels[blossom] == Some(1)
1331 && dual_var[blossom] == 0
1332 {
1333 expand_blossom(
1334 blossom,
1335 true,
1336 num_nodes,
1337 &mut blossom_children,
1338 &mut blossom_parents,
1339 &mut in_blossoms,
1340 &dual_var,
1341 &mut labels,
1342 &mut label_ends,
1343 &mut best_edge,
1344 &mut queue,
1345 &mut blossom_base,
1346 &endpoints,
1347 &mate,
1348 &mut blossom_endpoints,
1349 &mut allowed_edge,
1350 &mut unused_blossoms,
1351 )?;
1352 }
1353 }
1354 }
1355 if verify_optimum_flag {
1356 verify_optimum(
1357 max_cardinality,
1358 num_nodes,
1359 num_edges,
1360 &edges,
1361 &endpoints,
1362 &dual_var,
1363 &blossom_parents,
1364 &blossom_endpoints,
1365 &blossom_base,
1366 &mate,
1367 );
1368 }
1369
1370 // Transform mate[] such that mate[v] is the vertex to which v is paired
1371 // Also handle holes in node indices from PyGraph node removals by mapping
1372 // linear index to node index.
1373 let mut seen: HashSet<(usize, usize)> = HashSet::with_capacity(2 * num_nodes);
1374 let node_list: Vec<G::NodeId> = graph.node_identifiers().collect();
1375 for (index, node) in mate.iter() {
1376 let tmp = (
1377 graph.to_index(node_list[*index]),
1378 graph.to_index(node_list[endpoints[*node]]),
1379 );
1380 let rev_tmp = (
1381 graph.to_index(node_list[endpoints[*node]]),
1382 graph.to_index(node_list[*index]),
1383 );
1384 if !seen.contains(&tmp) && !seen.contains(&rev_tmp) {
1385 out_set.insert(tmp);
1386 seen.insert(tmp);
1387 seen.insert(rev_tmp);
1388 }
1389 }
1390 Ok(out_set)
1391}