1use std::collections::HashMap;
21
22use crate::error::{GraphalgError, GraphalgResult};
23use crate::repr::weighted_graph::WeightedGraph;
24
25#[derive(Debug, Clone, PartialEq)]
27pub struct WeightedMatchingResult {
28 pub mate: Vec<Option<usize>>,
30 pub matched_edges: Vec<(usize, usize, f64)>,
32 pub total_weight: f64,
34}
35
36#[derive(Debug, Clone)]
38pub struct WeightedGeneralMatching {
39 n: usize,
40 pair_weight: HashMap<(usize, usize), f64>,
43}
44
45impl WeightedGeneralMatching {
46 pub fn new(n: usize) -> Self {
48 Self {
49 n,
50 pair_weight: HashMap::new(),
51 }
52 }
53
54 pub fn add_edge(&mut self, u: usize, v: usize, w: f64) -> GraphalgResult<()> {
60 if u >= self.n || v >= self.n {
61 return Err(GraphalgError::IndexOutOfBounds {
62 index: u.max(v),
63 len: self.n,
64 });
65 }
66 if !w.is_finite() {
67 return Err(GraphalgError::InvalidEdgeWeight(format!(
68 "edge ({u},{v}) weight not finite: {w}"
69 )));
70 }
71 if u == v {
72 return Ok(());
73 }
74 let key = if u < v { (u, v) } else { (v, u) };
75 self.pair_weight
76 .entry(key)
77 .and_modify(|old| {
78 if w > *old {
79 *old = w;
80 }
81 })
82 .or_insert(w);
83 Ok(())
84 }
85
86 pub fn from_weighted_graph(g: &WeightedGraph) -> GraphalgResult<Self> {
89 let mut m = Self::new(g.n);
90 for (u, v, w) in g.all_edges() {
91 m.add_edge(u, v, w)?;
92 }
93 Ok(m)
94 }
95
96 pub fn num_nodes(&self) -> usize {
98 self.n
99 }
100
101 pub fn edges(&self) -> Vec<(usize, usize, f64)> {
103 let mut e: Vec<(usize, usize, f64)> = self
104 .pair_weight
105 .iter()
106 .map(|(&(u, v), &w)| (u, v, w))
107 .collect();
108 e.sort_by_key(|a| (a.0, a.1));
109 e
110 }
111
112 pub fn solve(&self) -> GraphalgResult<WeightedMatchingResult> {
115 self.solve_inner(false)
116 }
117
118 pub fn solve_max_cardinality(&self) -> GraphalgResult<WeightedMatchingResult> {
121 self.solve_inner(true)
122 }
123
124 fn solve_inner(&self, maxcardinality: bool) -> GraphalgResult<WeightedMatchingResult> {
125 let edges = self.edges();
126 let mate_vertices = if edges.is_empty() || self.n == 0 {
127 vec![-1i64; self.n]
128 } else {
129 let mut matcher = Matcher::new(self.n, &edges, maxcardinality);
130 matcher.run()?;
131 matcher.mate
132 };
133 let mut mate = vec![None; self.n];
134 let mut matched_edges = Vec::new();
135 let mut total_weight = 0.0;
136 for v in 0..self.n {
137 if mate_vertices[v] >= 0 {
138 let u = mate_vertices[v] as usize;
139 mate[v] = Some(u);
140 if v < u {
141 let key = (v, u);
142 let w = *self.pair_weight.get(&key).unwrap_or(&0.0);
143 matched_edges.push((v, u, w));
144 total_weight += w;
145 }
146 }
147 }
148 matched_edges.sort_by_key(|a| (a.0, a.1));
149 Ok(WeightedMatchingResult {
150 mate,
151 matched_edges,
152 total_weight,
153 })
154 }
155}
156
157fn ring_index(idx: isize, len: isize) -> usize {
159 (((idx % len) + len) % len) as usize
160}
161
162struct Matcher {
167 nvertex: usize,
168 edges: Vec<(usize, usize, f64)>,
169 endpoint: Vec<usize>,
170 neighbend: Vec<Vec<usize>>,
171 mate: Vec<i64>,
173 label: Vec<i64>,
175 labelend: Vec<i64>,
176 inblossom: Vec<usize>,
177 blossomparent: Vec<i64>,
178 blossomchilds: Vec<Vec<usize>>,
179 blossombase: Vec<i64>,
180 blossomendps: Vec<Vec<usize>>,
181 bestedge: Vec<i64>,
182 blossombestedges: Vec<Option<Vec<usize>>>,
183 unusedblossoms: Vec<usize>,
184 dualvar: Vec<f64>,
185 allowedge: Vec<bool>,
186 queue: Vec<usize>,
187 maxcardinality: bool,
188}
189
190impl Matcher {
191 fn new(nvertex: usize, edges: &[(usize, usize, f64)], maxcardinality: bool) -> Self {
192 let nedge = edges.len();
193 let mut endpoint = vec![0usize; 2 * nedge];
194 for (k, &(i, j, _)) in edges.iter().enumerate() {
195 endpoint[2 * k] = i;
196 endpoint[2 * k + 1] = j;
197 }
198 let mut neighbend = vec![Vec::new(); nvertex];
199 for (k, &(i, j, _)) in edges.iter().enumerate() {
200 neighbend[i].push(2 * k + 1);
201 neighbend[j].push(2 * k);
202 }
203 let mut maxweight = 0.0f64;
204 for &(_, _, w) in edges {
205 if w > maxweight {
206 maxweight = w;
207 }
208 }
209 let mut dualvar = vec![0.0f64; 2 * nvertex];
210 for d in dualvar.iter_mut().take(nvertex) {
211 *d = maxweight;
212 }
213 Self {
214 nvertex,
215 edges: edges.to_vec(),
216 endpoint,
217 neighbend,
218 mate: vec![-1; nvertex],
219 label: vec![0; 2 * nvertex],
220 labelend: vec![-1; 2 * nvertex],
221 inblossom: (0..nvertex).collect(),
222 blossomparent: vec![-1; 2 * nvertex],
223 blossomchilds: vec![Vec::new(); 2 * nvertex],
224 blossombase: (0..nvertex as i64)
225 .chain(std::iter::repeat_n(-1, nvertex))
226 .collect(),
227 blossomendps: vec![Vec::new(); 2 * nvertex],
228 bestedge: vec![-1; 2 * nvertex],
229 blossombestedges: vec![None; 2 * nvertex],
230 unusedblossoms: (nvertex..2 * nvertex).collect(),
231 dualvar,
232 allowedge: vec![false; nedge],
233 queue: Vec::new(),
234 maxcardinality,
235 }
236 }
237
238 fn slack(&self, k: usize) -> f64 {
239 let (i, j, w) = self.edges[k];
240 self.dualvar[i] + self.dualvar[j] - 2.0 * w
241 }
242
243 fn blossom_leaves(&self, b: usize) -> Vec<usize> {
244 if b < self.nvertex {
245 return vec![b];
246 }
247 let mut out = Vec::new();
248 for &t in &self.blossomchilds[b] {
249 if t < self.nvertex {
250 out.push(t);
251 } else {
252 out.extend(self.blossom_leaves(t));
253 }
254 }
255 out
256 }
257
258 fn assign_label(&mut self, w: usize, t: i64, p: i64) {
259 let b = self.inblossom[w];
260 self.label[w] = t;
261 self.label[b] = t;
262 self.labelend[w] = p;
263 self.labelend[b] = p;
264 self.bestedge[w] = -1;
265 self.bestedge[b] = -1;
266 if t == 1 {
267 let leaves = self.blossom_leaves(b);
268 self.queue.extend(leaves);
269 } else if t == 2 {
270 let base = self.blossombase[b] as usize;
271 let mb = self.mate[base];
272 let endp = self.endpoint[mb as usize];
273 self.assign_label(endp, 1, mb ^ 1);
274 }
275 }
276
277 fn scan_blossom(&mut self, v: usize, w: usize) -> i64 {
278 let mut path: Vec<usize> = Vec::new();
279 let mut base: i64 = -1;
280 let mut v: i64 = v as i64;
281 let mut w: i64 = w as i64;
282 while v != -1 || w != -1 {
283 let b = self.inblossom[v as usize];
284 if self.label[b] & 4 != 0 {
285 base = self.blossombase[b];
286 break;
287 }
288 path.push(b);
289 self.label[b] = 5;
290 if self.labelend[b] == -1 {
291 v = -1;
292 } else {
293 v = self.endpoint[self.labelend[b] as usize] as i64;
294 let b2 = self.inblossom[v as usize];
295 v = self.endpoint[self.labelend[b2] as usize] as i64;
296 }
297 if w != -1 {
298 std::mem::swap(&mut v, &mut w);
299 }
300 }
301 for b in path {
302 self.label[b] = 1;
303 }
304 base
305 }
306
307 fn add_blossom(&mut self, base: usize, k: usize) -> GraphalgResult<()> {
308 let (v0, w0, _) = self.edges[k];
309 let bb = self.inblossom[base];
310 let b = self
311 .unusedblossoms
312 .pop()
313 .ok_or_else(|| GraphalgError::NumericalInstability("blossom pool exhausted".into()))?;
314 self.blossombase[b] = base as i64;
315 self.blossomparent[b] = -1;
316 self.blossomparent[bb] = b as i64;
317 let mut path: Vec<usize> = Vec::new();
318 let mut endps: Vec<usize> = Vec::new();
319 let mut v = v0;
320 let mut bv = self.inblossom[v0];
321 while bv != bb {
322 self.blossomparent[bv] = b as i64;
323 path.push(bv);
324 endps.push(self.labelend[bv] as usize);
325 v = self.endpoint[self.labelend[bv] as usize];
326 bv = self.inblossom[v];
327 }
328 path.push(bb);
329 path.reverse();
330 endps.reverse();
331 endps.push(2 * k);
332 let mut w = w0;
333 let mut bw = self.inblossom[w0];
334 while bw != bb {
335 self.blossomparent[bw] = b as i64;
336 path.push(bw);
337 endps.push((self.labelend[bw] as usize) ^ 1);
338 w = self.endpoint[self.labelend[bw] as usize];
339 bw = self.inblossom[w];
340 }
341 let _ = (v, w);
342 self.blossomchilds[b] = path;
343 self.blossomendps[b] = endps;
344 self.label[b] = 1;
345 self.labelend[b] = self.labelend[bb];
346 self.dualvar[b] = 0.0;
347 for leaf in self.blossom_leaves(b) {
348 if self.label[self.inblossom[leaf]] == 2 {
349 self.queue.push(leaf);
350 }
351 self.inblossom[leaf] = b;
352 }
353 let mut bestedgeto = vec![-1i64; 2 * self.nvertex];
355 let childs = self.blossomchilds[b].clone();
356 for &bv in &childs {
357 let nblists: Vec<Vec<usize>> = match &self.blossombestedges[bv] {
358 Some(list) => vec![list.clone()],
359 None => self
360 .blossom_leaves(bv)
361 .into_iter()
362 .map(|leaf| {
363 self.neighbend[leaf]
364 .iter()
365 .map(|&p| p / 2)
366 .collect::<Vec<usize>>()
367 })
368 .collect(),
369 };
370 for nblist in nblists {
371 for kk in nblist {
372 let (mut i, mut j, _) = self.edges[kk];
373 if self.inblossom[j] == b {
374 std::mem::swap(&mut i, &mut j);
375 }
376 let bj = self.inblossom[j];
377 if bj != b
378 && self.label[bj] == 1
379 && (bestedgeto[bj] == -1
380 || self.slack(kk) < self.slack(bestedgeto[bj] as usize))
381 {
382 bestedgeto[bj] = kk as i64;
383 }
384 }
385 }
386 self.blossombestedges[bv] = None;
387 self.bestedge[bv] = -1;
388 }
389 let collected: Vec<usize> = bestedgeto
390 .iter()
391 .filter(|&&kk| kk != -1)
392 .map(|&kk| kk as usize)
393 .collect();
394 self.blossombestedges[b] = Some(collected.clone());
395 self.bestedge[b] = -1;
396 for kk in collected {
397 if self.bestedge[b] == -1 || self.slack(kk) < self.slack(self.bestedge[b] as usize) {
398 self.bestedge[b] = kk as i64;
399 }
400 }
401 Ok(())
402 }
403
404 fn expand_blossom(&mut self, b: usize, endstage: bool) -> GraphalgResult<()> {
405 let childs = self.blossomchilds[b].clone();
406 for &s in &childs {
407 self.blossomparent[s] = -1;
408 if s < self.nvertex {
409 self.inblossom[s] = s;
410 } else if endstage && self.dualvar[s] == 0.0 {
411 self.expand_blossom(s, endstage)?;
412 } else {
413 for leaf in self.blossom_leaves(s) {
414 self.inblossom[leaf] = s;
415 }
416 }
417 }
418 if !endstage && self.label[b] == 2 {
419 let entry_ep = (self.labelend[b] as usize) ^ 1;
420 let entrychild = self.inblossom[self.endpoint[entry_ep]];
421 let childs_b = self.blossomchilds[b].clone();
422 let endps_b = self.blossomendps[b].clone();
423 let len = childs_b.len() as isize;
424 let pos = childs_b
425 .iter()
426 .position(|&c| c == entrychild)
427 .ok_or_else(|| GraphalgError::NumericalInstability("entrychild missing".into()))?;
428 let mut j = pos as isize;
429 let jstep: isize;
430 let endptrick: isize;
431 if j & 1 != 0 {
432 j -= len;
433 jstep = 1;
434 endptrick = 0;
435 } else {
436 jstep = -1;
437 endptrick = 1;
438 }
439 let mut p = self.labelend[b];
440 while j != 0 {
441 self.label[self.endpoint[(p as usize) ^ 1]] = 0;
442 let e1 = endps_b[ring_index(j - endptrick, len)];
443 self.label[self.endpoint[e1 ^ (endptrick as usize) ^ 1]] = 0;
444 let endp = self.endpoint[(p as usize) ^ 1];
445 self.assign_label(endp, 2, p);
446 self.allowedge[e1 / 2] = true;
447 j += jstep;
448 let e2 = endps_b[ring_index(j - endptrick, len)];
449 p = (e2 ^ (endptrick as usize)) as i64;
450 self.allowedge[(p as usize) / 2] = true;
451 j += jstep;
452 }
453 let bv = childs_b[ring_index(j, len)];
454 let endp = self.endpoint[(p as usize) ^ 1];
455 self.label[endp] = 2;
456 self.label[bv] = 2;
457 self.labelend[endp] = p;
458 self.labelend[bv] = p;
459 self.bestedge[bv] = -1;
460 j += jstep;
461 while childs_b[ring_index(j, len)] != entrychild {
462 let bv = childs_b[ring_index(j, len)];
463 if self.label[bv] == 1 {
464 j += jstep;
465 continue;
466 }
467 let mut vv = usize::MAX;
468 for leaf in self.blossom_leaves(bv) {
469 if self.label[leaf] != 0 {
470 vv = leaf;
471 break;
472 }
473 }
474 if vv != usize::MAX {
475 self.label[vv] = 0;
476 let base_bv = self.blossombase[bv] as usize;
477 let mep = self.mate[base_bv];
478 self.label[self.endpoint[mep as usize]] = 0;
479 let le = self.labelend[vv];
480 self.assign_label(vv, 2, le);
481 }
482 j += jstep;
483 }
484 }
485 self.label[b] = -1;
486 self.labelend[b] = -1;
487 self.blossomchilds[b] = Vec::new();
488 self.blossomendps[b] = Vec::new();
489 self.blossombase[b] = -1;
490 self.blossombestedges[b] = None;
491 self.bestedge[b] = -1;
492 self.unusedblossoms.push(b);
493 Ok(())
494 }
495
496 fn augment_blossom(&mut self, b: usize, v: usize) -> GraphalgResult<()> {
497 let mut t = v;
498 while self.blossomparent[t] != b as i64 {
499 t = self.blossomparent[t] as usize;
500 }
501 if t >= self.nvertex {
502 self.augment_blossom(t, v)?;
503 }
504 let childs_b = self.blossomchilds[b].clone();
505 let endps_b = self.blossomendps[b].clone();
506 let len = childs_b.len() as isize;
507 let i =
508 childs_b.iter().position(|&c| c == t).ok_or_else(|| {
509 GraphalgError::NumericalInstability("augment child missing".into())
510 })? as isize;
511 let mut j = i;
512 let jstep: isize;
513 let endptrick: isize;
514 if i & 1 != 0 {
515 j -= len;
516 jstep = 1;
517 endptrick = 0;
518 } else {
519 jstep = -1;
520 endptrick = 1;
521 }
522 while j != 0 {
523 j += jstep;
524 let t1 = childs_b[ring_index(j, len)];
525 let p = endps_b[ring_index(j - endptrick, len)] ^ (endptrick as usize);
526 if t1 >= self.nvertex {
527 let ep = self.endpoint[p];
528 self.augment_blossom(t1, ep)?;
529 }
530 j += jstep;
531 let t2 = childs_b[ring_index(j, len)];
532 if t2 >= self.nvertex {
533 let ep = self.endpoint[p ^ 1];
534 self.augment_blossom(t2, ep)?;
535 }
536 self.mate[self.endpoint[p]] = (p ^ 1) as i64;
537 self.mate[self.endpoint[p ^ 1]] = p as i64;
538 }
539 let iu = i as usize;
540 let new_childs: Vec<usize> = childs_b[iu..]
541 .iter()
542 .chain(childs_b[..iu].iter())
543 .copied()
544 .collect();
545 let new_endps: Vec<usize> = endps_b[iu..]
546 .iter()
547 .chain(endps_b[..iu].iter())
548 .copied()
549 .collect();
550 self.blossomchilds[b] = new_childs;
551 self.blossomendps[b] = new_endps;
552 self.blossombase[b] = self.blossombase[self.blossomchilds[b][0]];
553 Ok(())
554 }
555
556 fn augment_matching(&mut self, k: usize) -> GraphalgResult<()> {
557 let (v, w, _) = self.edges[k];
558 let starts = [(v, (2 * k + 1) as i64), (w, (2 * k) as i64)];
559 for &(s0, p0) in &starts {
560 let mut s = s0;
561 let mut p = p0;
562 loop {
563 let bs = self.inblossom[s];
564 if bs >= self.nvertex {
565 self.augment_blossom(bs, s)?;
566 }
567 self.mate[s] = p;
568 if self.labelend[bs] == -1 {
569 break;
570 }
571 let t = self.endpoint[self.labelend[bs] as usize];
572 let bt = self.inblossom[t];
573 s = self.endpoint[self.labelend[bt] as usize];
574 let j = self.endpoint[(self.labelend[bt] as usize) ^ 1];
575 if bt >= self.nvertex {
576 self.augment_blossom(bt, j)?;
577 }
578 self.mate[j] = self.labelend[bt];
579 p = self.labelend[bt] ^ 1;
580 }
581 }
582 Ok(())
583 }
584
585 fn min_vertex_dual(&self) -> f64 {
586 let mut m = f64::INFINITY;
587 for v in 0..self.nvertex {
588 if self.dualvar[v] < m {
589 m = self.dualvar[v];
590 }
591 }
592 m
593 }
594
595 fn run(&mut self) -> GraphalgResult<()> {
596 let guard_cap = 16 * self.nvertex * self.nvertex + 1000;
597 for _phase in 0..self.nvertex {
598 for x in self.label.iter_mut() {
599 *x = 0;
600 }
601 for x in self.bestedge.iter_mut() {
602 *x = -1;
603 }
604 for b in self.nvertex..2 * self.nvertex {
605 self.blossombestedges[b] = None;
606 }
607 self.queue.clear();
608 for x in self.allowedge.iter_mut() {
609 *x = false;
610 }
611 for v in 0..self.nvertex {
612 if self.mate[v] == -1 && self.label[self.inblossom[v]] == 0 {
613 self.assign_label(v, 1, -1);
614 }
615 }
616 let mut augmented = false;
617 let mut guard = 0usize;
618 loop {
619 guard += 1;
620 if guard > guard_cap {
621 return Err(GraphalgError::NumericalInstability(
622 "weighted matching did not converge".into(),
623 ));
624 }
625 while !augmented {
626 let v = match self.queue.pop() {
627 Some(v) => v,
628 None => break,
629 };
630 let neigh = self.neighbend[v].clone();
631 for p in neigh {
632 let k = p / 2;
633 let w = self.endpoint[p];
634 if self.inblossom[v] == self.inblossom[w] {
635 continue;
636 }
637 if !self.allowedge[k] {
638 let kslack = self.slack(k);
639 if kslack <= 0.0 {
640 self.allowedge[k] = true;
641 }
642 }
643 if self.allowedge[k] {
644 if self.label[self.inblossom[w]] == 0 {
645 self.assign_label(w, 2, (p ^ 1) as i64);
646 } else if self.label[self.inblossom[w]] == 1 {
647 let base = self.scan_blossom(v, w);
648 if base >= 0 {
649 self.add_blossom(base as usize, k)?;
650 } else {
651 self.augment_matching(k)?;
652 augmented = true;
653 break;
654 }
655 } else if self.label[w] == 0 {
656 self.label[w] = 2;
657 self.labelend[w] = (p ^ 1) as i64;
658 }
659 } else if self.label[self.inblossom[w]] == 1 {
660 let bvv = self.inblossom[v];
661 if self.bestedge[bvv] == -1
662 || self.slack(k) < self.slack(self.bestedge[bvv] as usize)
663 {
664 self.bestedge[bvv] = k as i64;
665 }
666 } else if self.label[w] == 0
667 && (self.bestedge[w] == -1
668 || self.slack(k) < self.slack(self.bestedge[w] as usize))
669 {
670 self.bestedge[w] = k as i64;
671 }
672 }
673 }
674 if augmented {
675 break;
676 }
677 let mut deltatype: i32 = -1;
678 let mut delta = 0.0f64;
679 let mut deltaedge: i64 = -1;
680 let mut deltablossom: i64 = -1;
681 if !self.maxcardinality {
682 deltatype = 1;
683 delta = self.min_vertex_dual().max(0.0);
684 }
685 for v in 0..self.nvertex {
686 if self.label[self.inblossom[v]] == 0 && self.bestedge[v] != -1 {
687 let d = self.slack(self.bestedge[v] as usize);
688 if deltatype == -1 || d < delta {
689 delta = d;
690 deltatype = 2;
691 deltaedge = self.bestedge[v];
692 }
693 }
694 }
695 for b in 0..2 * self.nvertex {
696 if self.blossomparent[b] == -1 && self.label[b] == 1 && self.bestedge[b] != -1 {
697 let d = self.slack(self.bestedge[b] as usize) / 2.0;
698 if deltatype == -1 || d < delta {
699 delta = d;
700 deltatype = 3;
701 deltaedge = self.bestedge[b];
702 }
703 }
704 }
705 for b in self.nvertex..2 * self.nvertex {
706 if self.blossombase[b] >= 0
707 && self.blossomparent[b] == -1
708 && self.label[b] == 2
709 && (deltatype == -1 || self.dualvar[b] < delta)
710 {
711 delta = self.dualvar[b];
712 deltatype = 4;
713 deltablossom = b as i64;
714 }
715 }
716 if deltatype == -1 {
717 deltatype = 1;
718 delta = self.min_vertex_dual().max(0.0);
719 }
720 for v in 0..self.nvertex {
721 let lbl = self.label[self.inblossom[v]];
722 if lbl == 1 {
723 self.dualvar[v] -= delta;
724 } else if lbl == 2 {
725 self.dualvar[v] += delta;
726 }
727 }
728 for b in self.nvertex..2 * self.nvertex {
729 if self.blossombase[b] >= 0 && self.blossomparent[b] == -1 {
730 if self.label[b] == 1 {
731 self.dualvar[b] += delta;
732 } else if self.label[b] == 2 {
733 self.dualvar[b] -= delta;
734 }
735 }
736 }
737 if deltatype == 1 {
738 break;
739 } else if deltatype == 2 {
740 let de = deltaedge as usize;
741 self.allowedge[de] = true;
742 let (mut i, j, _) = self.edges[de];
743 if self.label[self.inblossom[i]] == 0 {
744 i = j;
745 }
746 self.queue.push(i);
747 } else if deltatype == 3 {
748 let de = deltaedge as usize;
749 self.allowedge[de] = true;
750 let (i, _, _) = self.edges[de];
751 self.queue.push(i);
752 } else if deltatype == 4 {
753 self.expand_blossom(deltablossom as usize, false)?;
754 }
755 }
756 if !augmented {
757 break;
758 }
759 for b in self.nvertex..2 * self.nvertex {
760 if self.blossomparent[b] == -1
761 && self.blossombase[b] >= 0
762 && self.label[b] == 1
763 && self.dualvar[b] == 0.0
764 {
765 self.expand_blossom(b, true)?;
766 }
767 }
768 }
769 for v in 0..self.nvertex {
770 if self.mate[v] >= 0 {
771 self.mate[v] = self.endpoint[self.mate[v] as usize] as i64;
772 }
773 }
774 Ok(())
775 }
776}
777
778#[cfg(test)]
779mod tests {
780 use super::*;
781 use crate::handle::LcgRng;
782
783 fn approx(a: f64, b: f64) -> bool {
784 (a - b).abs() < 1e-7
785 }
786
787 fn validate(res: &WeightedMatchingResult, edges: &[(usize, usize, f64)], n: usize) {
789 let mut used = vec![false; n];
791 for v in 0..n {
792 if let Some(u) = res.mate[v] {
793 assert_eq!(res.mate[u], Some(v), "mate not symmetric at {v}");
794 assert!(!used[v], "vertex {v} shared by two matched edges");
795 used[v] = true;
796 }
797 }
798 let mut sum = 0.0;
800 for &(u, v, w) in &res.matched_edges {
801 assert_eq!(res.mate[u], Some(v));
802 let found = edges
804 .iter()
805 .any(|&(a, b, ww)| ((a, b) == (u, v) || (a, b) == (v, u)) && approx(ww, w));
806 assert!(found, "matched edge ({u},{v},{w}) not in graph");
807 sum += w;
808 }
809 assert!(approx(sum, res.total_weight), "weight mismatch");
810 }
811
812 fn edge_weight(edges: &[(usize, usize, f64)], i: usize, j: usize) -> Option<f64> {
813 let mut best: Option<f64> = None;
814 for &(a, b, w) in edges {
815 if (a, b) == (i, j) || (a, b) == (j, i) {
816 best = Some(best.map_or(w, |x: f64| x.max(w)));
817 }
818 }
819 best
820 }
821
822 fn brute_max_weight(n: usize, edges: &[(usize, usize, f64)]) -> f64 {
824 let full = 1usize << n;
825 let mut dp = vec![0.0f64; full];
826 for mask in 1..full {
827 let i = mask.trailing_zeros() as usize;
828 let without_i = mask & !(1 << i);
829 let mut best = dp[without_i];
830 let mut rest = without_i;
831 while rest != 0 {
832 let jb = rest.trailing_zeros() as usize;
833 if let Some(w) = edge_weight(edges, i, jb) {
834 let cand = w + dp[without_i & !(1 << jb)];
835 if cand > best {
836 best = cand;
837 }
838 }
839 rest &= rest - 1;
840 }
841 dp[mask] = best;
842 }
843 dp[full - 1]
844 }
845
846 #[test]
847 fn triangle_picks_heaviest_edge() {
848 let mut m = WeightedGeneralMatching::new(3);
849 m.add_edge(0, 1, 3.0).expect("e");
850 m.add_edge(1, 2, 5.0).expect("e");
851 m.add_edge(0, 2, 4.0).expect("e");
852 let res = m.solve().expect("solve");
853 validate(&res, &m.edges(), 3);
854 assert!(approx(res.total_weight, 5.0), "got {}", res.total_weight);
855 assert_eq!(res.matched_edges, vec![(1, 2, 5.0)]);
856 }
857
858 #[test]
859 fn empty_and_single_vertex() {
860 let m0 = WeightedGeneralMatching::new(0);
861 let r0 = m0.solve().expect("solve");
862 assert!(r0.matched_edges.is_empty());
863 assert!(approx(r0.total_weight, 0.0));
864
865 let m1 = WeightedGeneralMatching::new(1);
866 let r1 = m1.solve().expect("solve");
867 assert_eq!(r1.mate, vec![None]);
868 assert!(approx(r1.total_weight, 0.0));
869 }
870
871 #[test]
872 fn path_optimal_alternating_set() {
873 let mut m = WeightedGeneralMatching::new(5);
878 let edges = [(0, 1, 1.0), (1, 2, 2.0), (2, 3, 2.0), (3, 4, 1.0)];
879 for &(u, v, w) in &edges {
880 m.add_edge(u, v, w).expect("e");
881 }
882 let res = m.solve().expect("solve");
883 validate(&res, &m.edges(), 5);
884 let brute = brute_max_weight(5, &m.edges());
885 assert!(
886 approx(res.total_weight, brute),
887 "got {} brute {brute}",
888 res.total_weight
889 );
890 assert!(approx(res.total_weight, 3.0));
891 }
892
893 #[test]
894 fn blossom_needed_odd_cycle() {
895 let mut m = WeightedGeneralMatching::new(5);
897 let edges = [
898 (0, 1, 6.0),
899 (1, 2, 6.0),
900 (2, 3, 6.0),
901 (3, 4, 6.0),
902 (4, 0, 6.0),
903 (0, 2, 10.0),
904 ];
905 for &(u, v, w) in &edges {
906 m.add_edge(u, v, w).expect("e");
907 }
908 let res = m.solve().expect("solve");
909 validate(&res, &m.edges(), 5);
910 let brute = brute_max_weight(5, &m.edges());
911 assert!(
912 approx(res.total_weight, brute),
913 "got {} brute {brute}",
914 res.total_weight
915 );
916 }
917
918 #[test]
919 fn matches_brute_force_on_random_graphs() {
920 let mut rng = LcgRng::new(0xC0FFEE);
921 for iter in 0..200 {
922 let n = 2 + (rng.next_u64() as usize % 8); let prob = if iter % 2 == 0 { 50 } else { 80 };
925 let mut m = WeightedGeneralMatching::new(n);
926 let mut edges = Vec::new();
927 for u in 0..n {
928 for v in (u + 1)..n {
929 if rng.next_u64() % 100 < prob {
930 let w = 1.0 + (rng.next_u64() as usize % 9) as f64; m.add_edge(u, v, w).expect("e");
932 edges.push((u, v, w));
933 }
934 }
935 }
936 let res = m.solve().expect("solve");
937 validate(&res, &m.edges(), n);
938 let brute = brute_max_weight(n, &m.edges());
939 assert!(
940 approx(res.total_weight, brute),
941 "n={n} edges={:?} got {} brute {brute}",
942 edges,
943 res.total_weight
944 );
945 }
946 }
947
948 #[test]
949 fn complete_graph_even_perfect() {
950 let mut m = WeightedGeneralMatching::new(4);
952 m.add_edge(0, 1, 1.0).expect("e");
953 m.add_edge(2, 3, 1.0).expect("e");
954 m.add_edge(0, 2, 5.0).expect("e");
955 m.add_edge(1, 3, 5.0).expect("e");
956 m.add_edge(0, 3, 2.0).expect("e");
957 m.add_edge(1, 2, 2.0).expect("e");
958 let res = m.solve().expect("solve");
959 validate(&res, &m.edges(), 4);
960 assert!(approx(res.total_weight, 10.0), "got {}", res.total_weight);
962 assert_eq!(res.matched_edges.len(), 2);
963 }
964
965 #[test]
966 fn rejects_bad_edges() {
967 let mut m = WeightedGeneralMatching::new(3);
968 assert!(m.add_edge(0, 5, 1.0).is_err());
969 assert!(m.add_edge(0, 1, f64::NAN).is_err());
970 assert!(m.add_edge(1, 1, 3.0).is_ok());
972 assert_eq!(m.edges().len(), 0);
973 }
974
975 #[test]
976 fn parallel_edges_keep_max() {
977 let mut m = WeightedGeneralMatching::new(2);
978 m.add_edge(0, 1, 2.0).expect("e");
979 m.add_edge(1, 0, 7.0).expect("e");
980 let res = m.solve().expect("solve");
981 assert!(approx(res.total_weight, 7.0));
982 }
983}