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}