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