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}