Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
blossom_weighted_mwmatching.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
45# ifndef ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
46# define ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
47
48# include <algorithm>
49# include <cassert>
50# include <cmath>
51# include <cstddef>
52# include <limits>
53# include <tuple>
54# include <utility>
55
56# include <ah-errors.H>
57# include <ahFunctional.H>
58# include <ahSort.H>
59# include <tpl_array.H>
60# include <tpl_agraph.H>
61# include <tpl_dynDlist.H>
62# include <tpl_dynListQueue.H>
63# include <tpl_dynListStack.H>
64
66{
67 /* **************************************************
68 * ** public definitions **
69 * ************************************************** */
70
75 using VertexId = unsigned int;
76
81 using VertexPair = std::pair<VertexId, VertexId>;
82
83
88 template <typename WeightType>
89 struct Edge
90 {
91 static_assert(std::numeric_limits<WeightType>::is_specialized,
92 "Edge weight must be a numeric type");
93
96
99
102 : vt(0, 0), weight(0)
103 {}
104
107 : vt(std::move(vt)), weight(w)
108 {}
109
112 : vt(x, y), weight(w)
113 {}
114 };
115
116
117 namespace impl
118 {
119 /* **************************************************
120 * ** private definitions **
121 * ************************************************** */
122
124 constexpr VertexId NO_VERTEX = std::numeric_limits<VertexId>::max();
125
126
128 enum BlossomLabel { LABEL_NONE = 0, LABEL_S = 1, LABEL_T = 2 };
129
130
131 /* **************************************************
132 * ** private helper functions **
133 * ************************************************** */
134
137 {
138 return std::make_pair(vt.second, vt.first);
139 }
140
141
153 template <typename WeightType>
155 {
156 constexpr VertexId max_num_vertex = std::numeric_limits<VertexId>::max();
157
158 for (const Edge<WeightType> & edge: edges)
159 {
160 // Check that vertex IDs are valid.
161 ah_invalid_argument_if(edge.vt.first >= max_num_vertex or edge.vt.second >= max_num_vertex)
162 << "Vertex ID out of range";
163
164 // Check that the edge weight is a finite number.
165 ah_invalid_argument_if(not std::numeric_limits<WeightType>::is_integer and
166 not std::isfinite(edge.weight))
167 << "Edge weights must be finite numbers";
168
169 // Check that edge weight will not cause overflow.
170 ah_invalid_argument_if(edge.weight > std::numeric_limits<WeightType>::max() / 4)
171 << "Edge weight exceeds maximum supported value";
172 }
173
174 // Check that the graph has no self-edges.
175 for (const Edge<WeightType> & edge: edges)
176 ah_invalid_argument_if(edge.vt.first == edge.vt.second)
177 << "Self-edges are not supported";
178
179 // Check that the graph does not contain duplicate edges.
181 edge_endpoints.reserve(edges.size());
182 for (const Edge<WeightType> & edge: edges)
183 {
184 VertexPair vt = edge.vt;
185 if (vt.first > vt.second)
186 std::swap(vt.first, vt.second);
187 edge_endpoints.append(vt);
188 }
189
192 << "Duplicate edges are not supported";
193 }
194
195
196 /* **************************************************
197 * ** struct Problem_Data **
198 * ************************************************** */
199
204 template <typename WeightType>
206 {
211
214
217
220
223
233
234 // Prevent copying.
235 Problem_Data(const Problem_Data &) = delete;
236
238
240 static Array<EdgeT>
242 {
245
246 for (const Edge<WeightType> & edge: edges_in)
247 if (edge.weight >= 0)
248 real_edges.append(edge);
249
250 return real_edges;
251 }
252
255 {
257 for (const Edge<WeightType> & edge: edges)
258 {
259 const VertexId m = std::max(edge.vt.first, edge.vt.second);
260 assert(m < std::numeric_limits<VertexId>::max());
261 num_vertex = std::max(num_vertex, m + 1);
262 }
263
264 return num_vertex;
265 }
266
269 {
270 nodes.reserve(num_vertex);
271 for (VertexId i = 0; i < num_vertex; ++i)
273
274 for (const EdgeT & edge: edges)
275 {
276 assert(edge.vt.first < num_vertex);
277 assert(edge.vt.second < num_vertex);
278 adjacent_graph.insert_arc(nodes[edge.vt.first],
279 nodes[edge.vt.second],
280 &edge);
281 }
282 }
283
285 template <class Op>
286 void for_adjacent_edges(const VertexId x, Op op) const
287 {
288 assert(x < num_vertex);
289 for (Node_Arc_Iterator<Internal_Graph> it(nodes[x]); it.has_curr(); it.next_ne())
290 {
291 Internal_Arc *arc = it.get_curr();
292 op(arc->get_info());
293 }
294 }
295 };
296
297
298 /* **************************************************
299 * ** struct Blossom **
300 * ************************************************** */
301
302 // Forward declaration.
303 template <typename WeightType>
304 struct NonTrivialBlossom;
305
306
314 template <typename WeightType>
315 struct Blossom
316 {
319
322
325
328
331
334
335 protected:
344
345 public:
348 : Blossom(0, false)
349 {}
350
355
358 {
359 return is_nontrivial_blossom ?
360 static_cast<NonTrivialBlossom<WeightType> *>(this) :
361 nullptr;
362 }
363
366 {
367 return is_nontrivial_blossom ?
368 static_cast<const NonTrivialBlossom<WeightType> *>(this) :
369 nullptr;
370 }
371 };
372
373
374 /* **************************************************
375 * ** struct NonTrivialBlossom **
376 * ************************************************** */
377
382 template <typename WeightType>
383 struct NonTrivialBlossom : public Blossom<WeightType>
384 {
393
396
399
402
403 // Needed by DynDlist internals (sentinel node).
405 : Blossom<WeightType>(0, true),
406 dual_var(0)
407 {}
408
410 template <class Blossom_Container, class Edge_Container>
412 const Edge_Container & edges)
414 dual_var(0)
415 {
416 assert(subblossoms.size() == edges.size());
417 assert(subblossoms.size() % 2 == 1);
418 assert(subblossoms.size() >= 3);
419
420 auto blossom_it = subblossoms.begin();
421 auto edge_it = edges.begin();
422 while (blossom_it != subblossoms.end())
423 {
424 this->subblossoms.append(SubBlossom{});
425 this->subblossoms.get_last().blossom = *blossom_it;
426 this->subblossoms.get_last().edge = *edge_it;
427 ++blossom_it;
428 ++edge_it;
429 }
430 }
431
434 {
435 VertexId pos = 0;
436 for (auto it = subblossoms.get_it(); it.has_curr(); it.next_ne(), ++pos)
437 if (it.get_curr().blossom == subblossom)
438 return pos;
439 assert(false);
440 return 0;
441 }
442 };
443
444
446 template <typename WeightType, typename Func>
447 inline void for_vertices_in_blossom(const Blossom<WeightType> *blossom, Func func)
448 {
449 const NonTrivialBlossom<WeightType> *ntb = blossom->nontrivial();
450 if (ntb)
451 {
452 // Visit all vertices in the non-trivial blossom.
453 // Use an explicit stack to avoid deep call chains.
455 stack.append(ntb);
456
457 while (not stack.is_empty())
458 {
459 const NonTrivialBlossom<WeightType> *b = stack.get_last();
460 (void) stack.remove_last();
461
462 for (const auto & sub: b->subblossoms)
463 {
464 ntb = sub.blossom->nontrivial();
465 if (ntb)
466 stack.append(ntb);
467 else
468 func(sub.blossom->base_vertex);
469 }
470 }
471 }
472 else
473 {
474 // A trivial blossom contains just one vertex.
475 func(blossom->base_vertex);
476 }
477 }
478
479
485
486
487 /* **************************************************
488 * ** struct MatchingContext **
489 * ************************************************** */
490
495 template <typename WeightType>
497 {
498 public:
502
518
520 static constexpr WeightType weight_factor =
521 std::numeric_limits<WeightType>::is_integer ? 2 : 1;
522
525
528
531
534
537
540
543
546
549
552
555 : graph(edges_in)
556 {
557 // Initially, all vertices are unmatched.
558 vertex_mate.reserve(graph.num_vertex);
559 for (VertexId x = 0; x < graph.num_vertex; ++x)
561
562 // Create a trivial blossom for each vertex.
563 trivial_blossom.reserve(graph.num_vertex);
564 for (VertexId x = 0; x < graph.num_vertex; ++x)
565 trivial_blossom.append(BlossomT(x));
566
567 // Initially, all vertices are trivial top-level blossoms.
568 vertex_top_blossom.reserve(graph.num_vertex);
569 for (VertexId x = 0; x < graph.num_vertex; ++x)
571
572 // Vertex duals are initialized to half the maximum edge weight.
574 for (const EdgeT & edge: graph.edges)
575 max_weight = std::max(max_weight, edge.weight);
576
578 vertex_dual.reserve(graph.num_vertex);
579 for (VertexId x = 0; x < graph.num_vertex; ++x)
581
582 // Initialize "vertex_best_edge".
583 vertex_best_edge.reserve(graph.num_vertex);
584 for (VertexId x = 0; x < graph.num_vertex; ++x)
585 vertex_best_edge.append(nullptr);
586
587 // Allocate temporary arrays for path tracing.
588 vertex_marker.reserve(graph.num_vertex);
589 for (VertexId x = 0; x < graph.num_vertex; ++x)
590 vertex_marker.append(false);
591
592 marked_vertex.reserve(graph.num_vertex);
593 }
594
595 // Prevent copying.
597
599
600 /* ********** General support routines: ********** */
601
603 WeightType edge_slack(const EdgeT & edge) const
604 {
605 VertexId x = edge.vt.first;
606 VertexId y = edge.vt.second;
608 return vertex_dual[x] + vertex_dual[y] - weight_factor * edge.weight;
609 }
610
611 /*
612 * Least-slack edge tracking:
613 *
614 * To calculate delta steps, the matching algorithm needs to find
615 * - the least-slack edge between any S-vertex and an unlabeled vertex;
616 * - the least-slack edge between any pair of top-level S-blossoms.
617 *
618 * For each unlabeled vertex and each T-vertex, we keep track of the
619 * least-slack edge to any S-vertex. Tracking for unlabeled vertices
620 * serves to provide the least-slack edge for the delta step.
621 * Tracking for T-vertices is done because such vertices can turn into
622 * unlabeled vertices if they are part of a T-blossom that gets expanded.
623 *
624 * For each top-level S-blossom, we keep track of the least-slack edge
625 * to any S-vertex not in the same blossom.
626 *
627 * Furthermore, for each top-level S-blossom, we keep a list of
628 * least-slack edges to other top-level S-blossoms. For any pair of
629 * top-level S-blossoms, the least-slack edge between them is contained
630 * in the edge list of at least one of the blossoms. An edge list may
631 * contain multiple edges to the same S-blossom. Such redundant edges are
632 * pruned during blossom merging to limit the number of tracked edges.
633 *
634 * Note: For a given vertex or blossom, the identity of the least-slack
635 * edge to any S-blossom remains unchanged during a delta step.
636 * Although the delta step changes edge slacks, it changes the slack
637 * of every edge to an S-vertex by the same amount. Therefore the edge
638 * that had least slack before the delta step, will still have least slack
639 * after the delta step.
640 */
641
648 {
649 for (VertexId x = 0; x < graph.num_vertex; ++x)
650 vertex_best_edge[x] = nullptr;
651
652 for (BlossomT & blossom: trivial_blossom)
653 blossom.best_edge = nullptr;
654
656 {
657 blossom.best_edge = nullptr;
658 blossom.best_edge_set.empty();
659 }
660 }
661
669 {
671 if (cur_best_edge == nullptr or slack < edge_slack(*cur_best_edge))
672 vertex_best_edge[y] = edge;
673 }
674
685 std::pair<const EdgeT *, WeightType> lset_get_best_vertex_edge()
686 {
687 const EdgeT *best_edge = nullptr;
689
690 for (VertexId x = 0; x < graph.num_vertex; ++x)
691 if (vertex_top_blossom[x]->label == LABEL_NONE)
692 {
693 const EdgeT *edge = vertex_best_edge[x];
694 if (edge != nullptr)
695 {
696 WeightType slack = edge_slack(*edge);
697 if (best_edge == nullptr or slack < best_slack)
698 {
699 best_edge = edge;
701 }
702 }
703 }
704
705 return std::make_pair(best_edge, best_slack);
706 }
707
709 static void lset_new_blossom(BlossomT *blossom)
710 {
711 assert(blossom->best_edge == nullptr);
712 assert((blossom->nontrivial() == nullptr)
713 or blossom->nontrivial()->best_edge_set.is_empty());
714 }
715
723 const EdgeT *edge,
725 {
726 const EdgeT *cur_best_edge = blossom->best_edge;
727 if (cur_best_edge == nullptr or slack < edge_slack(*cur_best_edge))
728 blossom->best_edge = edge;
729
730 if (NonTrivialBlossomT *ntb = blossom->nontrivial())
731 ntb->best_edge_set.append(edge);
732 }
733
741 {
742 assert(blossom->best_edge == nullptr);
743 assert(blossom->best_edge_set.is_empty());
744
745 // Collect edges from the sub-blossoms that used to be S-blossoms.
746 for (auto & subblossom_item: blossom->subblossoms)
747 if (BlossomT *sub = subblossom_item.blossom; sub->label == LABEL_S)
748 {
749 if (NonTrivialBlossomT *ntb = sub->nontrivial())
750 {
751 // Take least-slack edge set from this subblossom.
752 blossom->best_edge_set.append(std::move(ntb->best_edge_set));
753 }
754 else
755 {
756 // Trivial blossoms don't maintain a least-slack edge set.
757 // Just consider all incident edges.
758 graph.for_adjacent_edges(sub->base_vertex,
759 [this,blossom](const EdgeT *edge)
760 {
761 // Only take edges between different S-blossoms.
762 VertexId x = edge->vt.first;
763 VertexId y = edge->vt.second;
766 if (bx != by
767 and (bx->label == LABEL_S)
768 and (by->label == LABEL_S))
769 {
770 blossom->best_edge_set.append(edge);
771 }
772 });
773 }
774 }
775
776 // Build a temporary array holding the least-slack edge index to
777 // each top-level S-blossom. This array is indexed by the base vertex
778 // of the blossoms.
781 for (VertexId i = 0; i < graph.num_vertex; ++i)
782 best_edge_to_blossom.append(std::make_pair(nullptr, WeightType{}));
783
784 for (const EdgeT *edge: blossom->best_edge_set)
785 {
786 BlossomT *bx = vertex_top_blossom[edge->vt.first];
787 BlossomT *by = vertex_top_blossom[edge->vt.second];
788 assert(bx == blossom or by == blossom);
789
790 // Ignore internal edges.
791 if (bx != by)
792 {
793 bx = (bx == blossom) ? by : bx;
794
795 // Only consider edges to S-blossoms.
796 if (bx->label == LABEL_S)
797 {
798 // Keep only the least-slack edge to blossom "bx".
799 WeightType slack = edge_slack(*edge);
800 VertexId bx_base = bx->base_vertex;
801 if (auto & best_edge_item = best_edge_to_blossom[bx_base]; (best_edge_item.first == nullptr)
802 or (slack < best_edge_item.second))
803 {
804 best_edge_item.first = edge;
805 best_edge_item.second = slack;
806 }
807 }
808 }
809 };
810
811 // Rebuild a compact list of least-slack edges.
812 // Also find the overall least-slack edge to any other S-blossom.
814 blossom->best_edge_set.empty();
816 {
817 const EdgeT *edge = best_edge_item.first;
818 if (edge != nullptr)
819 {
820 blossom->best_edge_set.append(edge);
822 if (blossom->best_edge == nullptr or slack < best_slack)
823 {
824 blossom->best_edge = edge;
826 }
827 }
828 }
829 }
830
841 std::pair<const EdgeT *, WeightType> lset_get_best_blossom_edge()
842 {
843 const EdgeT *best_edge = nullptr;
845
846 auto consider_blossom = [this,&best_edge,&best_slack](BlossomT *blossom)
847 {
848 if (blossom->parent == nullptr and (blossom->label == LABEL_S))
849 {
850 const EdgeT *edge = blossom->best_edge;
851 if (edge != nullptr)
852 {
853 WeightType slack = edge_slack(*edge);
854 if (best_edge == nullptr or (slack < best_slack))
855 {
856 best_edge = edge;
858 }
859 }
860 }
861 };
862
863 for (BlossomT & blossom: trivial_blossom)
864 consider_blossom(&blossom);
865 for (BlossomT & blossom: nontrivial_blossom)
866 consider_blossom(&blossom);
867
868 return std::make_pair(best_edge, best_slack);
869 }
870
871 /* ********** Creating and expanding blossoms: ********** */
872
888 {
890
891 // Initialize a path containing only the edge (x, y).
892 AlternatingPath path;
893 path.edges.append(std::make_pair(x, y));
894
895 // "first_common" is the first common ancestor of "x" and "y"
896 // in the alternating tree, or "nullptr" if no common ancestor
897 // has been found.
898 BlossomT *first_common = nullptr;
899
900 // Alternate between tracing the path from "x" and the path from "y".
901 // This ensures that the search time is bounded by the size of any
902 // newly found blossom.
903 while (x != NO_VERTEX or y != NO_VERTEX)
904 {
905 // Trace path from vertex "x".
906 if (x != NO_VERTEX)
907 {
908 // Stop if we found a common ancestor.
910 if (vertex_marker[bx->base_vertex])
911 {
913 break;
914 }
915
916 // Mark blossom as potential common ancestor.
917 vertex_marker[bx->base_vertex] = true;
918 marked_vertex.append(bx->base_vertex);
919
920 // Trace back to the parent in the alternating tree.
921 x = bx->tree_edge.first;
922 if (x != NO_VERTEX)
923 // While tracing from x, prepend so the left half of the path
924 // remains in forward order.
925 path.edges.insert(bx->tree_edge);
926 }
927
928 // Trace path from vertex "y".
929 if (y != NO_VERTEX)
930 {
931 // Stop if we found a common ancestor.
933 if (vertex_marker[by->base_vertex])
934 {
936 break;
937 }
938
939 // Mark blossom as potential common ancestor.
940 vertex_marker[by->base_vertex] = true;
941 marked_vertex.append(by->base_vertex);
942
943 // Trace back to the parent in the alternating tree.
944 y = by->tree_edge.first;
945 if (y != NO_VERTEX)
946 // While tracing from y, append so the right half stays in
947 // forward order from left to right.
948 path.edges.append(std::make_pair(by->tree_edge.second, y));
949 }
950 }
951
952 // Remove all markers we placed.
953 for (const VertexId k: marked_vertex)
954 vertex_marker[k] = false;
956
957 // If we found a common ancestor, trim the paths so they end there.
958 if (first_common)
959 {
960 assert(first_common->label == LABEL_S);
961 while (vertex_top_blossom[path.edges.get_first().first] != first_common)
962 (void) path.edges.remove_first();
963 while (vertex_top_blossom[path.edges.get_last().second] != first_common)
964 (void) path.edges.remove_last();
965 }
966
967 // Any alternating path between S-blossoms must have odd length.
968 assert(path.edges.size() % 2 == 1);
969
970 return path;
971 }
972
981 void make_blossom(const AlternatingPath & path)
982 {
983 assert(path.edges.size() % 2 == 1);
984 assert(path.edges.size() >= 3);
985
986 // First pass:
987 // Build the ordered ring of sub-blossoms from edge.first.
988 // We keep this pass separate from the cycle check below to keep the
989 // first/second roles explicit and easy to audit.
990 Array<BlossomT *> subblossoms;
991 subblossoms.reserve(path.edges.size());
992 for (VertexPair edge: path.edges)
993 subblossoms.append(vertex_top_blossom[edge.first]);
994
995 // Second pass:
996 // Validate that each edge.second lands in the next sub-blossom in the
997 // ring (with wrap-around at the end).
998 VertexId pos = 0;
999 for (VertexPair edge: path.edges)
1000 {
1001 pos = (pos + 1) % subblossoms.size();
1002 assert(vertex_top_blossom[edge.second] == subblossoms[pos]);
1003 }
1004
1005 // Create the new blossom object.
1006 nontrivial_blossom.append(NonTrivialBlossomT(subblossoms, path.edges));
1007 NonTrivialBlossomT *blossom = &nontrivial_blossom.get_last();
1008
1009 // Link the subblossoms to their new parent.
1010 for (BlossomT *sub: subblossoms)
1011 sub->parent = blossom;
1012
1013 // Mark vertices as belonging to the new blossom.
1014 for_vertices_in_blossom(blossom, [this,blossom](VertexId x)
1015 {
1016 vertex_top_blossom[x] = blossom;
1017 });
1018
1019 // Assign label S to the new blossom and link to the alternating tree.
1020 assert(subblossoms.get_first()->label == LABEL_S);
1021 blossom->label = LABEL_S;
1022 blossom->tree_edge = subblossoms.get_first()->tree_edge;
1023
1024 // Consider vertices inside former T-sub-blossoms which now
1025 // became S-vertices; add them to the queue.
1026 for (BlossomT *sub: subblossoms)
1027 if (sub->label == LABEL_T)
1028 for_vertices_in_blossom(sub, [this](VertexId x)
1029 {
1030 queue.put(x);
1031 });
1032
1033 // Merge least-slack edges for the S-sub-blossoms.
1034 lset_merge_blossoms(blossom);
1035 }
1036
1039 {
1040 for (auto it = nontrivial_blossom.get_it(); it.has_curr(); it.next_ne())
1041 if (&it.get_curr() == blossom)
1042 {
1043 (void) it.del();
1044 return;
1045 }
1046 assert(false);
1047 }
1048
1055 {
1056 assert(blossom->parent == nullptr);
1057 assert(blossom->label == LABEL_T);
1058
1059 // Convert sub-blossoms into top-level blossoms.
1060 for (const auto & sub: blossom->subblossoms)
1061 {
1062 BlossomT *sub_blossom = sub.blossom;
1063 assert(sub_blossom->parent == blossom);
1064 assert(sub_blossom->label == LABEL_NONE);
1065 sub_blossom->parent = nullptr;
1067 [this,sub_blossom](VertexId x)
1068 {
1070 });
1071 }
1072
1073 // The expanded blossom was part of an alternating tree.
1074 // We must now reconstruct the part of the alternating tree
1075 // that ran through this blossom by linking some of the sub-blossoms
1076 // into the tree.
1077
1078 // Find the sub-blossom that was attached to the parent node
1079 // in the alternating tree.
1080 BlossomT *entry = vertex_top_blossom[blossom->tree_edge.second];
1081
1082 // Assign label T to that blossom and link to the alternating tree.
1083 entry->label = LABEL_T;
1084 entry->tree_edge = blossom->tree_edge;
1085
1086 // Find the position of this sub-blossom within the expanding blossom.
1087 const VertexId entry_pos = blossom->find_subblossom_pos(entry);
1088 auto sub_it = blossom->subblossoms.get_it();
1089 for (VertexId i = 0; i < entry_pos; ++i)
1090 sub_it.next_ne();
1091
1092 if (entry_pos % 2 == 0)
1093 {
1094 // Walk backward to the base.
1095 while (sub_it.get_pos() != 0)
1096 {
1097 sub_it.prev();
1098 assign_label_s(sub_it.get_curr().edge.first);
1099
1100 assert(sub_it.get_pos() != 0);
1101 sub_it.prev();
1102 auto & cur = sub_it.get_curr();
1103 cur.blossom->label = LABEL_T;
1104 cur.blossom->tree_edge = flip_vertex_pair(cur.edge);
1105 }
1106 }
1107 else
1108 {
1109 // Walk forward to the base.
1110 while (sub_it.has_curr())
1111 {
1112 assign_label_s(sub_it.get_curr().edge.second);
1113 sub_it.next();
1114
1115 assert(sub_it.has_curr());
1116 VertexPair tree_edge = sub_it.get_curr().edge;
1117 sub_it.next();
1118
1119 BlossomT *sub_blossom = sub_it.has_curr() ?
1120 sub_it.get_curr().blossom :
1121 blossom->subblossoms.get_first().blossom;
1122 sub_blossom->label = LABEL_T;
1123 sub_blossom->tree_edge = tree_edge;
1124 }
1125 }
1126
1127 // Delete the expanded blossom.
1128 erase_nontrivial_blossom(blossom);
1129 }
1130
1137 {
1138 assert(blossom->parent == nullptr);
1139 assert(blossom->label == LABEL_NONE);
1140
1141 // Convert sub-blossoms into top-level blossoms.
1142 for (const auto & sub: blossom->subblossoms)
1143 {
1144 BlossomT *sub_blossom = sub.blossom;
1145 assert(sub_blossom->parent == blossom);
1146 assert(sub_blossom->label == LABEL_NONE);
1147 sub_blossom->parent = nullptr;
1149 [this,sub_blossom](VertexId x)
1150 {
1152 });
1153 }
1154
1155 // Delete the expanded blossom.
1156 erase_nontrivial_blossom(blossom);
1157 }
1158
1159 /* ********** Augmenting: ********** */
1160
1169 BlossomT *entry,
1170 DynListStack<std::pair<NonTrivialBlossomT *, BlossomT *>> & rec_stack)
1171 {
1172 const VertexId entry_pos = blossom->find_subblossom_pos(entry);
1173 auto sub_it = blossom->subblossoms.get_it();
1174 for (VertexId i = 0; i < entry_pos; ++i)
1175 sub_it.next_ne();
1176 while ((sub_it.get_pos() != 0) and sub_it.has_curr())
1177 {
1178 VertexId x, y;
1179 BlossomT *bx = nullptr;
1180 BlossomT *by = nullptr;
1181
1182 if (entry_pos % 2 == 0)
1183 {
1184 // Walk backward to the base.
1185 sub_it.prev();
1186 by = sub_it.get_curr().blossom;
1187 assert(sub_it.get_pos() != 0);
1188 sub_it.prev();
1189 bx = sub_it.get_curr().blossom;
1190 std::tie(x, y) = sub_it.get_curr().edge;
1191 }
1192 else
1193 {
1194 // Walk forward to the base.
1195 sub_it.next();
1196 assert(sub_it.has_curr());
1197 std::tie(x, y) = sub_it.get_curr().edge;
1198 bx = sub_it.get_curr().blossom;
1199 sub_it.next();
1200 by = sub_it.has_curr() ?
1201 sub_it.get_curr().blossom :
1202 blossom->subblossoms.get_first().blossom;
1203 }
1204
1205 // Pull this edge into the matching.
1206 vertex_mate[x] = y;
1207 vertex_mate[y] = x;
1208
1209 // Augment through any non-trivial subblossoms touching this edge.
1211 if (bx_ntb != nullptr)
1212 rec_stack.put(std::make_pair(bx_ntb, &trivial_blossom[x]));
1213
1215 if (by_ntb != nullptr)
1216 rec_stack.put(std::make_pair(by_ntb, &trivial_blossom[y]));
1217 }
1218
1219 // Re-orient the blossom (rotate so "entry" is first).
1220 if (entry_pos != 0)
1221 {
1223 ring.reserve(blossom->subblossoms.size());
1224 for (auto it = blossom->subblossoms.get_it(); it.has_curr(); it.next_ne())
1225 ring.append(it.get_curr());
1226 const size_t n = ring.size();
1228 for (size_t k = 0; k < n; ++k)
1229 rotated.append(ring[(entry_pos + k) % n]);
1230 blossom->subblossoms.swap(rotated);
1231 }
1232
1233 // Update the base vertex.
1234 // We can pull the new base vertex from the entry sub-blossom
1235 // since its augmentation has already finished.
1236 blossom->base_vertex = entry->base_vertex;
1237 }
1238
1248 {
1249 // Use an explicit stack to avoid deep recursion.
1251 rec_stack.put(std::make_pair(blossom, entry));
1252
1253 while (not rec_stack.is_empty())
1254 {
1257 std::tie(outer_blossom, inner_entry) = rec_stack.top();
1258
1260 assert(inner_blossom != nullptr);
1261
1263 {
1264 // After augmenting "inner_blossom",
1265 // continue by augmenting its parent.
1266 rec_stack.top() = std::make_pair(outer_blossom, inner_blossom);
1267 }
1268 else
1269 {
1270 // After augmenting "inner_blossom",
1271 // this entire "outer_blossom" will be finished.
1272 (void) rec_stack.get();
1273 }
1274
1275 // Augment "inner_blossom".
1277 }
1278 }
1279
1286 {
1287 // Check that the path starts and ends in an unmatched blossom.
1288 assert(path.edges.size() % 2 == 1);
1289 assert(vertex_mate[vertex_top_blossom[path.edges.get_first().first]->base_vertex] == NO_VERTEX);
1290 assert(vertex_mate[vertex_top_blossom[path.edges.get_last().second]->base_vertex] == NO_VERTEX);
1291
1292 // Process the unmatched edges on the augmenting path.
1293 auto edge_it = path.edges.begin();
1294 auto edge_end = path.edges.end();
1295 while (edge_it != edge_end)
1296 {
1297 VertexId x = edge_it->first;
1298 VertexId y = edge_it->second;
1299
1300 // Augment any non-trivial blossoms that touch this edge.
1303 if (bx_ntb != nullptr)
1305
1308 if (by_ntb != nullptr)
1310
1311 // Pull this edge into the matching.
1312 vertex_mate[x] = y;
1313 vertex_mate[y] = x;
1314
1315 // Move forward through the augmenting path to
1316 // the next edge to be matched.
1317 // Edges along an augmenting path alternate unmatched/matched;
1318 // only unmatched edges are pulled into the new matching.
1319 ++edge_it;
1320 if (edge_it == edge_end)
1321 break;
1322 ++edge_it;
1323 }
1324 }
1325
1326 /* ********** Labels and alternating tree: ********** */
1327
1341 {
1342 // Assign label S to the blossom that contains vertex "x".
1344 assert(bx->label == LABEL_NONE);
1345 bx->label = LABEL_S;
1346
1347 VertexId y = vertex_mate[x];
1348 if (y == NO_VERTEX)
1349 {
1350 // Vertex "x" is unmatched.
1351 // It must be either a top-level vertex or the base vertex of
1352 // a top-level blossom.
1353 assert(bx->base_vertex == x);
1354
1355 // Mark the blossom as root of an alternating tree.
1356 bx->tree_edge = std::make_pair(NO_VERTEX, x);
1357 }
1358 else
1359 {
1360 // Vertex "x" is matched to T-vertex "y".
1361 assert(vertex_top_blossom[y]->label == LABEL_T);
1362
1363 // Attach the blossom to the alternating tree via vertex "y".
1364 bx->tree_edge = std::make_pair(y, x);
1365 }
1366
1367 // Start least-slack edge tracking for the S-blossom.
1369
1370 // Add all vertices inside the newly labeled S-blossom to the queue.
1372 {
1373 queue.put(v);
1374 });
1375 }
1376
1390 {
1391 assert(vertex_top_blossom[x]->label == LABEL_S);
1392
1394 assert(by->label == LABEL_NONE);
1395
1396 // If "y" is part of a zero-dual blossom, expand it.
1397 // This would otherwise likely happen through a zero-delta4 step,
1398 // so we can just do it now and avoid a substage.
1400 while (ntb != nullptr and ntb->dual_var == 0)
1401 {
1404 assert(by->label == LABEL_NONE);
1405 ntb = by->nontrivial();
1406 }
1407
1408 // Assign label T to the top-level blossom that contains vertex "y".
1409 by->label = LABEL_T;
1410 by->tree_edge = std::make_pair(x, y);
1411
1412 // Assign label S to the blossom that is mated to the T-blossom.
1413 const VertexId z = vertex_mate[by->base_vertex];
1414 assert(z != NO_VERTEX);
1415 assign_label_s(z);
1416 }
1417
1430 {
1431 assert(vertex_top_blossom[x]->label == LABEL_S);
1432 assert(vertex_top_blossom[y]->label == LABEL_S);
1433
1434 // Trace back through the alternating trees from "x" and "y".
1436
1437 // If the path is a cycle, create a new blossom.
1438 // Otherwise the path is an augmenting path.
1439 // Note that an alternating starts and ends in the same blossom,
1440 // but not necessarily in the same vertex within that blossom.
1441 VertexId p = path.edges.get_first().first;
1442 VertexId q = path.edges.get_last().second;
1444 {
1445 make_blossom(path);
1446 return false;
1447 }
1448 augment_matching(path);
1449 return true;
1450 }
1451
1464 {
1465 // Process the queue of S-vertices to be scanned.
1466 // This loop runs through O(n) iterations per stage.
1467 while (not queue.is_empty())
1468 {
1469 // Take one vertex from the queue.
1470 VertexId x = queue.get();
1471
1472 assert(vertex_top_blossom[x]->label == LABEL_S);
1473
1474 // Scan the edges that are incident on "x".
1475 // This loop runs through O(m) iterations per stage.
1476 bool found_augmenting = false;
1477 graph.for_adjacent_edges(x, [this,x,&found_augmenting](const EdgeT *edge)
1478 {
1479 if (found_augmenting)
1480 return;
1481
1482 // "x" is one endpoint of this incident edge;
1483 // recover the opposite endpoint.
1484 VertexId y = (edge->vt.first != x) ? edge->vt.first : edge->vt.second;
1485
1486 // Note: The top-level blossom of vertex "x" may change
1487 // during this loop, so we need to refresh it in each pass.
1489
1490 // Ignore edges that are internal to a blossom.
1491 if (bx == vertex_top_blossom[y])
1492 return;
1493
1495
1496 // Check whether this edge is tight (has zero slack).
1497 // Only tight edges may be part of an alternating tree.
1498 WeightType slack = edge_slack(*edge);
1499 if (slack <= 0)
1500 {
1501 if (ylabel == LABEL_NONE)
1502 {
1503 // Found a tight edge to an unlabeled blossom.
1504 // Assign label T to the blossom that contains "y".
1505 assign_label_t(x, y);
1506 }
1507 else if (ylabel == LABEL_S)
1508 {
1509 // Found a tight edge between two S-blossoms.
1510 // Find either a new blossom or an augmenting path.
1511 if (add_s_to_s_edge(x, y))
1512 found_augmenting = true;
1513 }
1514 }
1515 else if (ylabel == LABEL_S)
1516 {
1517 // Found a non-tight edge between two S-blossoms.
1518 // Pass it to the least-slack edge tracker.
1520 }
1521
1522 if (ylabel != LABEL_S)
1523 {
1524 // Found an to a T-vertex or unlabeled vertex "y".
1525 // Pass it to the least-slack edge tracker.
1526 // Tight edges must also be tracked in this way.
1528 }
1529 });
1530
1531 if (found_augmenting)
1532 return true;
1533 }
1534
1535 // No further S vertices to scan, and no augmenting path found.
1536 return false;
1537 }
1538
1539 /* ********** Delta steps: ********** */
1540
1555 {
1556 DeltaStep delta;
1557 delta.blossom = nullptr;
1558
1559 // Compute delta1: minimum dual variable of any S-vertex.
1560 delta.kind = 1;
1561 delta.value = std::numeric_limits<WeightType>::max();
1562 for (VertexId x = 0; x < graph.num_vertex; ++x)
1563 if (vertex_top_blossom[x]->label == LABEL_S)
1564 delta.value = std::min(delta.value, vertex_dual[x]);
1565
1566 // Compute delta2: minimum slack of any edge between an S-vertex and
1567 // an unlabeled vertex.
1568 const EdgeT *edge;
1570 std::tie(edge, slack) = lset_get_best_vertex_edge();
1571 if (edge != nullptr and slack <= delta.value)
1572 {
1573 delta.kind = 2;
1574 delta.value = slack;
1575 delta.edge = edge->vt;
1576 }
1577
1578 // Compute delta3: half minimum slack of any edge between two
1579 // top-level S-blossoms.
1580 std::tie(edge, slack) = lset_get_best_blossom_edge();
1581 if (edge != nullptr and slack / 2 <= delta.value)
1582 {
1583 delta.kind = 3;
1584 delta.value = slack / 2;
1585 delta.edge = edge->vt;
1586 }
1587
1588 // Compute delta4: half minimum dual of a top-level T-blossom.
1589 for (NonTrivialBlossomT & blossom: nontrivial_blossom)
1590 if (not blossom.parent and blossom.label == LABEL_T)
1591 if (blossom.dual_var / 2 <= delta.value)
1592 {
1593 delta.kind = 4;
1594 delta.value = blossom.dual_var / 2;
1595 delta.blossom = &blossom;
1596 }
1597
1598 return delta;
1599 }
1600
1603 {
1604 // Apply delta to dual variables of all vertices.
1605 for (VertexId x = 0; x < graph.num_vertex; ++x)
1606 if (const BlossomLabel xlabel = vertex_top_blossom[x]->label; xlabel == LABEL_S)
1607 {
1608 // S-vertex: subtract delta from dual variable.
1609 vertex_dual[x] -= delta;
1610 }
1611 else if (xlabel == LABEL_T)
1612 {
1613 // T-vertex: add delta to dual variable.
1614 vertex_dual[x] += delta;
1615 }
1616
1617 // Apply delta to dual variables of top-level non-trivial blossoms.
1618 for (NonTrivialBlossomT & blossom: nontrivial_blossom)
1619 if (blossom.parent == nullptr)
1620 {
1621 if (blossom.label == LABEL_S)
1622 {
1623 // S-blossom: add 2*delta to dual variable.
1624 blossom.dual_var += 2 * delta;
1625 }
1626 else if (blossom.label == LABEL_T)
1627 {
1628 // T-blossom: subtract 2*delta from dual variable.
1629 blossom.dual_var -= 2 * delta;
1630 }
1631 }
1632 }
1633
1634 /* ********** Main algorithm: ********** */
1635
1643 {
1644 // Remove blossom labels.
1645 for (BlossomT & blossom: trivial_blossom)
1646 blossom.label = LABEL_NONE;
1647 for (BlossomT & blossom: nontrivial_blossom)
1648 blossom.label = LABEL_NONE;
1649
1650 // Clear the S-vertex queue.
1651 queue.clear();
1652
1653 // Reset least-slack edge tracking.
1654 lset_reset();
1655 }
1656
1671 {
1672 // Assign label S to all unmatched vertices and put them in the queue.
1673 for (VertexId x = 0; x < graph.num_vertex; ++x)
1674 if (vertex_mate[x] == NO_VERTEX)
1675 assign_label_s(x);
1676
1677 // Stop if all vertices are matched.
1678 // No further improvement is possible in that case.
1679 // This avoids messy calculations of delta steps without any S-vertex.
1680 if (queue.is_empty())
1681 return false;
1682
1683 // Each pass through the following loop is a "substage".
1684 // The substage tries to find an augmenting path.
1685 // If an augmenting path is found, we augment the matching and end
1686 // the stage. Otherwise we update the dual LPP problem and enter the
1687 // next substage, or stop if no further improvement is possible.
1688 //
1689 // This loop runs through at most O(n) iterations per stage.
1690 bool augmented = false;
1691 while (true)
1692 {
1693 // Grow alternating trees.
1694 // End the stage if an augmenting path is found.
1696 if (augmented)
1697 break;
1698
1699 // Calculate delta step in the dual LPP problem.
1701
1702 // Apply the delta step to the dual variables.
1704
1705 if (delta.kind == 2)
1706 {
1707 // Use the edge from S-vertex to unlabeled vertex that got
1708 // unlocked through the delta update.
1709 VertexId x = delta.edge.first;
1710 VertexId y = delta.edge.second;
1711 if (vertex_top_blossom[x]->label != LABEL_S)
1712 std::swap(x, y);
1713 assign_label_t(x, y);
1714 }
1715 else if (delta.kind == 3)
1716 {
1717 // Use the S-to-S edge that got unlocked by the delta update.
1718 // This may reveal an augmenting path.
1719 VertexId x = delta.edge.first;
1720 VertexId y = delta.edge.second;
1722 if (augmented)
1723 break;
1724 }
1725 else if (delta.kind == 4)
1726 {
1727 // Expand the T-blossom that reached dual value 0 through
1728 // the delta update.
1729 assert(delta.blossom);
1731 }
1732 else
1733 {
1734 // No further improvement possible. End the stage.
1735 assert(delta.kind == 1);
1736 break;
1737 }
1738 }
1739
1740 // Remove all labels, clear queue.
1741 reset_stage();
1742
1743 // Return True if the matching was augmented.
1744 return augmented;
1745 }
1746
1748 void run()
1749 {
1750 // Improve the solution until no further improvement is possible.
1751 //
1752 // Each successful pass through this loop increases the number
1753 // of matched edges by 1.
1754 //
1755 // This loop runs through at most (n/2 + 1) iterations.
1756 // Each iteration takes time O(n**2).
1757 while (run_stage());
1758 }
1759 };
1760
1761
1762 /* **************************************************
1763 * ** struct MatchingVerifier **
1764 * ************************************************** */
1765
1767 template <typename WeightType>
1769 {
1770 public:
1772 : ctx(ctx),
1773 graph(ctx.graph),
1774 edge_duals()
1775 {
1776 edge_duals.reserve(ctx.graph.edges.size());
1777 for (size_t i = 0; i < ctx.graph.edges.size(); ++i)
1778 edge_duals.append(0);
1779 }
1780
1796
1797 private:
1801
1802 static bool checked_add(WeightType & result, WeightType a, WeightType b)
1803 {
1804 if (a > std::numeric_limits<WeightType>::max() - b)
1805 return true;
1806 result = a + b;
1807 return false;
1808 }
1809
1811 std::size_t edge_index(const EdgeT *edge)
1812 {
1813 return edge - &graph.edges.base();
1814 }
1815
1818 {
1819 // Count matched vertices and check symmetry of "vertex_mate".
1821 for (VertexId x = 0; x < graph.num_vertex; ++x)
1822 {
1823 VertexId y = ctx.vertex_mate[x];
1824 if (y != NO_VERTEX)
1825 {
1827 if (ctx.vertex_mate[y] != x)
1828 return false;
1829 }
1830 }
1831
1832 // Count matched edges.
1834 for (const EdgeT & edge: graph.edges)
1835 if (ctx.vertex_mate[edge.vt.first] == edge.vt.second)
1837
1838 // Check that all matched vertices correspond to matched edges.
1839 return (num_matched_vertex == 2 * num_matched_edge);
1840 }
1841
1847 {
1848 for (VertexId x = 0; x < graph.num_vertex; ++x)
1849 {
1850 if (ctx.vertex_dual[x] < 0)
1851 return false;
1852 if (ctx.vertex_mate[x] == NO_VERTEX and ctx.vertex_dual[x] != 0)
1853 return false;
1854 }
1855 return true;
1856 }
1857
1860 {
1861 for (const NonTrivialBlossomT & blossom: ctx.nontrivial_blossom)
1862 if (blossom.dual_var < 0)
1863 return false;
1864 return true;
1865 }
1866
1886 {
1887 // For each vertex "x",
1888 // "vertex_depth[x]" is the depth of the smallest blossom on
1889 // the current descent path that contains "x".
1891 vertex_depth.reserve(graph.num_vertex);
1892 for (VertexId i = 0; i < graph.num_vertex; ++i)
1893 vertex_depth.append(0);
1894
1895 // At each depth, keep track of the sum of blossom duals
1896 // along the current descent path.
1899
1900 // At each depth, keep track of the number of matched edges
1901 // along the current ascent path.
1904
1905 // Use an explicit stack to avoid deep recursion.
1906 // Second item tracks the next sub-blossom index to process.
1908 stack.put(std::make_pair(blossom, size_t{0}));
1909
1910 while (! stack.is_empty())
1911 {
1912 VertexId depth = stack.size();
1913 auto & stack_elem = stack.top();
1914 blossom = stack_elem.first;
1915 size_t sub_index = stack_elem.second;
1916
1917 if (sub_index == 0)
1918 {
1919 // We just entered this sub-blossom.
1920 // Update the depth of all vertices in this sub-blossom.
1922 [&vertex_depth,depth](VertexId x)
1923 {
1924 vertex_depth[x] = depth;
1925 });
1926
1927 // Calculate the sum of blossom duals at the new depth.
1928 path_sum_dual.append(path_sum_dual.get_last());
1929 if (checked_add(path_sum_dual.get_last(),
1930 path_sum_dual.get_last(),
1931 blossom->dual_var))
1932 return false;
1933
1934 // Initialize the number of matched edges at the new depth.
1935 path_num_matched.append(0);
1936
1937 if (blossom->subblossoms.size() < 3)
1938 return false;
1939 }
1940
1941 if (sub_index < blossom->subblossoms.size())
1942 {
1943 auto it = blossom->subblossoms.get_it();
1944 for (size_t i = 0; i < sub_index; ++i)
1945 it.next_ne();
1946 auto & sub_item = it.get_curr();
1947 ++stack_elem.second;
1948
1949 // Examine the current sub-blossom.
1950 BlossomT *sub = sub_item.blossom;
1951 NonTrivialBlossomT *ntb = sub->nontrivial();
1952 if (ntb)
1953 {
1954 // Prepare to descend into this sub-blossom.
1955 stack.put(std::make_pair(ntb, size_t{0}));
1956 }
1957 else
1958 {
1959 // Handle single vertex.
1960 // For each incident edge, find the smallest blossom
1961 // that contains it.
1962 VertexId x = sub->base_vertex;
1963 bool failed = false;
1964 graph.for_adjacent_edges(x, [this,x,&failed,&vertex_depth,&path_sum_dual,&path_num_matched](
1965 const EdgeT *edge)
1966 {
1967 if (failed)
1968 return;
1969
1970 // Only consider edges pointing out from "x".
1971 // This avoids processing the same undirected edge
1972 // twice during verification.
1973 if (edge->vt.first != x)
1974 return;
1975
1976 VertexId y = edge->vt.second;
1978 if (edge_depth > 0)
1979 {
1980 // Found the smallest blossom that contains this edge.
1981 // Add the duals of the containing blossoms.
1983 edge_duals[edge_index(edge)],
1985 {
1986 failed = true;
1987 return;
1988 }
1989
1990 // Update the number of matched edges in the blossom.
1991 if (ctx.vertex_mate[x] == y)
1993 }
1994 });
1995
1996 if (failed)
1997 return false;
1998 }
1999 }
2000 else
2001 {
2002 // We are leaving the current sub-blossom.
2003
2004 // Count the number of vertices inside this blossom.
2008 {
2010 });
2011
2012 // Check that the blossom is "full".
2013 // A blossom is full if all except one of its vertices
2014 // are matched to another vertex within the blossom.
2017 return false;
2018
2019 // Update the number of matched edges in the parent blossom.
2020 path_num_matched[depth - 1] += path_num_matched[depth];
2021
2022 // Push vertices in this sub-blossom back to the parent.
2023 for_vertices_in_blossom(blossom, [&vertex_depth,depth](VertexId x)
2024 {
2025 vertex_depth[x] = depth - 1;
2026 });
2027
2028 // Trim the descending path.
2029 (void) path_sum_dual.remove_last();
2030 (void) path_num_matched.remove_last();
2031
2032 // Remove the current blossom from the stack.
2033 (void) stack.get();
2034 }
2035 }
2036
2037 return true;
2038 }
2039
2045 {
2046 // For each edge, calculate the sum of its vertex duals.
2047 for (const EdgeT & edge: graph.edges)
2048 if (checked_add(edge_duals[edge_index(&edge)],
2049 ctx.vertex_dual[edge.vt.first],
2050 ctx.vertex_dual[edge.vt.second]))
2051 return false;
2052
2053 // Descend down each top-level blossom.
2054 // Check that blossoms are full.
2055 // Add blossom duals to the edges contained inside the blossoms.
2056 // This takes total time O(n**2).
2057 for (const NonTrivialBlossomT & blossom: ctx.nontrivial_blossom)
2058 if (blossom.parent == nullptr)
2059 if (not check_blossom(&blossom))
2060 return false;
2061
2062 return true;
2063 }
2064
2072 {
2073 for (const EdgeT & edge: graph.edges)
2074 {
2076 WeightType weight = ctx.weight_factor * edge.weight;
2077
2078 if (weight > duals)
2079 return false;
2080 WeightType slack = duals - weight;
2081
2082 if (ctx.vertex_mate[edge.vt.first] == edge.vt.second)
2083 if (slack != 0)
2084 return false;
2085 }
2086 return true;
2087 }
2088
2089 private:
2092
2095
2101 };
2102 } // namespace impl
2103
2104
2105 /* **************************************************
2106 * ** public functions **
2107 * ************************************************** */
2108
2131 template <typename WeightType>
2133 const Array<Edge<WeightType>> & edges)
2134 {
2135 // Check that the input meets all constraints.
2137
2138 // Run matching algorithm.
2140 matching.run();
2141
2142 // Verify that the solution is optimal (works only for integer weights).
2143 if (std::numeric_limits<WeightType>::is_integer)
2145
2146 // Extract the matched edges.
2147 Array<VertexPair> solution;
2148 solution.reserve(matching.graph.edges.size());
2149 for (const Edge<WeightType> & edge: matching.graph.edges)
2150 if (matching.vertex_mate[edge.vt.first] == edge.vt.second)
2151 solution.append(edge.vt);
2152
2153 return solution;
2154 }
2155
2156
2173 template <typename WeightType>
2176 {
2177 // For integer types, min() is the most-negative representable value.
2178 // For floating-point types, min() is the smallest *positive* normalized
2179 // value — not the most negative — so we must use lowest() instead.
2181 (std::numeric_limits<WeightType>::is_integer
2182 ? std::numeric_limits<WeightType>::min()
2183 : std::numeric_limits<WeightType>::lowest()) / 4;
2184 const WeightType max_safe_weight = std::numeric_limits<WeightType>::max() / 4;
2185
2186 // Copy edges.
2188
2189 // Don't worry about empty graphs.
2190 if (edges.is_empty())
2191 return edges;
2192
2193 // Count number of vertices.
2194 // Determine minimum and maximum edge weight.
2195 VertexId num_vertex = 0;
2196 WeightType min_weight = edges.get_first().weight;
2198
2199 constexpr VertexId max_num_vertex = std::numeric_limits<VertexId>::max();
2200 for (const Edge<WeightType> & edge: edges)
2201 {
2202 const VertexId m = std::max(edge.vt.first, edge.vt.second);
2204 << "Vertex ID out of range";
2205 num_vertex = std::max(num_vertex, m + 1);
2206
2207 if (not std::numeric_limits<WeightType>::is_integer)
2208 ah_invalid_argument_if(! std::isfinite(edge.weight))
2209 << "Edge weights must be finite numbers";
2210
2211 min_weight = std::min(min_weight, edge.weight);
2212 max_weight = std::max(max_weight, edge.weight);
2213 }
2214
2215 // Calculate weight range and required weight adjustment.
2217 << "Edge weight exceeds maximum supported value";
2218
2220
2222 << "Adjusted edge weight exceeds maximum supported value";
2223
2224 // Do nothing if the weights already ensure maximum-cardinality.
2225 if (min_weight > 0 and min_weight >= num_vertex * weight_range)
2226 return edges;
2227
2228 WeightType delta;
2229 if (weight_range > 0)
2230 {
2231 // Increase weights to make minimum edge weight large enough
2232 // to improve any non-maximum-cardinality matching.
2233 delta = num_vertex * weight_range - min_weight;
2234 }
2235 else
2236 {
2237 // All weights are the same. Increase weights to make them positive.
2238 delta = 1 - min_weight;
2239 }
2240
2241 assert(delta >= 0);
2242
2244 << "Adjusted edge weight exceeds maximum supported value";
2245
2246 // Increase all edge weights by "delta".
2247 for (Edge<WeightType> & edge: edges)
2248 edge.weight += delta;
2249
2250 return edges;
2251 }
2252} // namespace Aleph::blossom_weighted_detail::mwmatching
2253
2254
2255#endif // ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
Exception handling system with formatted messages for Aleph-w.
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
Functional programming utilities for Aleph-w containers.
High-level sorting functions for Aleph containers.
long double w
Definition btreepic.C:153
virtual Node * insert_node(Node *p)
Definition tpl_agraph.H:393
Arc * insert_arc(Node *src, Node *tgt, void *a)
Definition tpl_agraph.H:456
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
void empty() noexcept
Empties the container.
Definition tpl_array.H:336
constexpr bool is_empty() const noexcept
Checks if the container is empty.
Definition tpl_array.H:348
T & get_first() noexcept
return a modifiable reference to the first element.
Definition tpl_array.H:358
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
T & get_last() noexcept
return a modifiable reference to the last element.
Definition tpl_array.H:366
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:315
Dynamic doubly linked list with O(1) size and bidirectional access.
T & get_last() const
Return a modifiable reference to last item in the list.
const size_t & size() const noexcept
Return the number of elements (constant time)
T remove_last()
Remove the last item of the list; return a copy of removed item.
T & append(const T &item)
Append a copied item at the end of the list.
T & get_first() const
Return a modifiable reference to first item in the list.
T & insert(const T &item)
Insert a copy of item at the beginning of the list.
T remove_first()
Remove the first item of the list; return a copy of removed item.
Dynamic queue of elements of generic type T based on single linked list.
T & put(const T &data)
The type of element.
T get()
Remove the oldest item of the queue.
void clear() noexcept
Empties the container.
bool is_empty() const noexcept
Return true if this is empty.
Dynamic stack of elements of generic type T based on a singly linked list.
T & top()
Return a modifiable reference to the top item of the stack.
bool is_empty() const noexcept
Check if the stack is empty.
constexpr size_t size() const noexcept
Return the number of elements in the stack.
T get()
Alias for pop() - removes and returns the top item.
T & put(const T &data)
Alias for push() - for compatibility with queue-like interfaces.
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Node of Array_Graph
Definition tpl_agraph.H:64
Encapsulates the complete state of the weighted matching algorithm.
static void lset_new_blossom(BlossomT *blossom)
Start tracking edges for a new S-blossom.
void make_blossom(const AlternatingPath &path)
Create a new blossom from an alternating cycle.
MatchingContext & operator=(const MatchingContext &)=delete
void lset_merge_blossoms(NonTrivialBlossomT *blossom)
Update least-slack edge tracking after merging sub-blossoms into a new S-blossom.
void expand_unlabeled_blossom(NonTrivialBlossomT *blossom)
Expand the specified unlabeled blossom.
void expand_t_blossom(NonTrivialBlossomT *blossom)
Expand the specified T-blossom.
bool add_s_to_s_edge(VertexId x, VertexId y)
Add the edge between S-vertices "x" and "y".
Array< BlossomT > trivial_blossom
All trivial (single vertex) blossoms.
std::pair< const EdgeT *, WeightType > lset_get_best_vertex_edge()
Return the index and slack of the least-slack edge between any S-vertex and unlabeled vertex.
const Problem_Data< WeightType > graph
Internal graph representation.
void augment_matching(const AlternatingPath &path)
Augment the matching through the specified augmenting path.
void substage_apply_delta_step(WeightType delta)
Apply a delta step to the dual LPP variables.
DynDlist< NonTrivialBlossomT > nontrivial_blossom
Currently active non-trivial blossoms.
void augment_blossom_rec(NonTrivialBlossomT *blossom, BlossomT *entry, DynListStack< std::pair< NonTrivialBlossomT *, BlossomT * > > &rec_stack)
Augment along an alternating path through the specified blossom, from sub-blossom "entry" to the base...
void reset_stage()
Reset data which are only valid during a stage.
MatchingContext(const Array< EdgeT > &edges_in)
Initialize context from an edge array.
void lset_add_vertex_edge(VertexId y, const EdgeT *edge, WeightType slack)
Add edge "e" from an S-vertex to unlabeled vertex or T-vertex "y".
WeightType edge_slack(const EdgeT &edge) const
Calculate edge slack.
AlternatingPath trace_alternating_paths(VertexId x, VertexId y)
Trace back through the alternating trees from vertices "x" and "y".
void assign_label_t(VertexId x, VertexId y)
Assign label T to the unlabeled blossom that contains vertex "y".
void assign_label_s(VertexId x)
Assign label S to the unlabeled blossom that contains vertex "x".
bool substage_scan()
Scan queued S-vertices to expand the alternating trees.
std::pair< const EdgeT *, WeightType > lset_get_best_blossom_edge()
Return the index and slack of the least-slack edge between any pair of top-level S-blossoms.
void lset_add_blossom_edge(BlossomT *blossom, const EdgeT *edge, WeightType slack)
Add edge "e" between the specified S-blossom and another S-blossom.
Array< BlossomT * > vertex_top_blossom
Maps each vertex to its highest-level containing blossom.
DeltaStep substage_calc_dual_delta()
Calculate a delta step in the dual LPP problem.
void erase_nontrivial_blossom(NonTrivialBlossomT *blossom)
Erase the specified non-trivial blossom.
void augment_blossom(NonTrivialBlossomT *blossom, BlossomT *entry)
Augment along an alternating path through the specified blossom, from sub-blossom "entry" to the base...
static constexpr WeightType weight_factor
Integer scaling factor for weight calculations.
Array< VertexId > vertex_mate
The current mate of each vertex, or NO_VERTEX.
Helper class to verify that an optimal solution has been found.
bool verify()
Verify that the optimum solution has been found.
Array< WeightType > edge_duals
For each edge, the sum of duals of its incident vertices and duals of all blossoms that contain the e...
std::size_t edge_index(const EdgeT *edge)
Convert edge pointer to its index in the array "edges".
bool verify_vertex_mate()
Check that the array "vertex_mate" is consistent.
bool check_blossom(const NonTrivialBlossomT *blossom)
Helper function for verifying the solution.
const Problem_Data< WeightType > & graph
Reference to the input graph.
bool verify_blossom_duals()
Check that blossom dual variables are non-negative.
const MatchingContext< WeightType > & ctx
Reference to the MatchingContext instance.
bool verify_vertex_duals()
Check that vertex dual variables are non-negative, and all unmatched vertices have zero dual.
bool verify_edge_slack()
Check that all edges have non-negative slack, and check that all matched edges have zero slack.
static bool checked_add(WeightType &result, WeightType a, WeightType b)
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
Definition graph-dry.H:595
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
void verify()
static mpfr_t y
Definition mpfr_mul_d.c:3
void for_vertices_in_blossom(const Blossom< WeightType > *blossom, Func func)
Iterate over all base vertices contained within a blossom.
BlossomLabel
Top-level blossoms may be labeled "S" or "T" or unlabeled.
constexpr VertexId NO_VERTEX
Value used to mark an invalid or undefined vertex.
void check_input_graph(const Array< Edge< WeightType > > &edges)
Check that the input is a valid graph.
VertexPair flip_vertex_pair(const VertexPair &vt)
Return a pair of vertices in flipped order.
Array< Edge< WeightType > > adjust_weights_for_maximum_cardinality_matching(const Array< Edge< WeightType > > &edges_in)
Adjust edge weights to prioritize maximum cardinality matching.
unsigned int VertexId
Type representing the unique ID of a vertex.
std::pair< VertexId, VertexId > VertexPair
Type representing a pair of vertices.
Array< VertexPair > maximum_weight_matching(const Array< Edge< WeightType > > &edges)
Compute a maximum-weighted matching in a general undirected graph.
and
Check uniqueness with explicit hash + equality functors.
bool all_unique(const Container &container, Equal &eq)
Check if all elements in a container are unique using a comparator.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
DynArray< T > & in_place_sort(DynArray< T > &c, Cmp cmp=Cmp())
Sorts a DynArray in place.
Definition ahSort.H:321
STL namespace.
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
Edge(VertexId x, VertexId y, WeightType w)
Construct from vertex IDs and weight.
Edge(VertexPair vt, WeightType w)
Construct from a VertexPair and weight.
Represents a sequence of edges along an alternating path.
const NonTrivialBlossom< WeightType > * nontrivial() const
Safe downcast to NonTrivialBlossom (const version).
BlossomLabel label
Label S or T if this is part of the current alternating forest.
VertexPair tree_edge
Tree edge attaching this blossom to its parent in the alternating forest.
NonTrivialBlossom< WeightType > * nontrivial()
Safe downcast to NonTrivialBlossom.
bool is_nontrivial_blossom
Flag to distinguish between Blossom and NonTrivialBlossom.
NonTrivialBlossom< WeightType > * parent
Parent in the blossom hierarchy, or null if top-level.
VertexId base_vertex
Index of the base vertex (the unmatched vertex in the blossom).
const Edge< WeightType > * best_edge
Best edge for dual variable calculation (least slack).
Blossom(const VertexId base_vertex, const bool is_nontrivial_blossom)
VertexPair edge
Edge connecting this sub-blossom to the next in the cycle.
Represents a non-trivial blossom (an odd cycle of sub-blossoms).
DynDlist< SubBlossom > subblossoms
Ring of sub-blossoms forming this blossom.
NonTrivialBlossom(const Blossom_Container &subblossoms, const Edge_Container &edges)
Construct a new non-trivial blossom.
Representation of the input graph for the internal solver.
Problem_Data(const Array< EdgeT > &edges_in)
Initialize data and build the internal graph.
static Array< EdgeT > remove_negative_weight_edges(const Array< EdgeT > &edges_in)
const Array< EdgeT > edges
Pre-filtered edges (non-negative only).
Internal_Graph adjacent_graph
Adjacency graph used to iterate over incident edges.
void for_adjacent_edges(const VertexId x, Op op) const
Iterates over edges incident to vertex x.
Problem_Data & operator=(const Problem_Data &)=delete
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
static int * k
Array-based graph implementation.
Dynamic array container with automatic resizing.
Dynamic doubly linked list implementation.
Dynamic queue implementation based on linked lists.
Dynamic stack implementation based on linked lists.