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
47# ifndef ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
48# define ALEPH_BLOSSOM_WEIGHTED_MWMATCHING_H
49
50# include <algorithm>
51# include <cassert>
52# include <cmath>
53# include <cstddef>
54# include <limits>
55# include <tuple>
56# include <utility>
57
58# include <ah-errors.H>
59# include <ahFunctional.H>
60# include <ahSort.H>
61# include <tpl_array.H>
62# include <tpl_agraph.H>
63# include <tpl_dynDlist.H>
64# include <tpl_dynListQueue.H>
65# include <tpl_dynListStack.H>
66
68{
69 /* **************************************************
70 * ** public definitions **
71 * ************************************************** */
72
77 using VertexId = unsigned int;
78
83 using VertexPair = std::pair<VertexId, VertexId>;
84
85
90 template <typename WeightType>
91 struct Edge
92 {
93 static_assert(std::numeric_limits<WeightType>::is_specialized,
94 "Edge weight must be a numeric type");
95
98
101
104 : vt(0, 0), weight(0)
105 {}
106
109 : vt(std::move(vt)), weight(w)
110 {}
111
114 : vt(x, y), weight(w)
115 {}
116 };
117
118
119 namespace impl
120 {
121 /* **************************************************
122 * ** private definitions **
123 * ************************************************** */
124
126 constexpr VertexId NO_VERTEX = std::numeric_limits<VertexId>::max();
127
128
130 enum BlossomLabel { LABEL_NONE = 0, LABEL_S = 1, LABEL_T = 2 };
131
132
133 /* **************************************************
134 * ** private helper functions **
135 * ************************************************** */
136
139 {
140 return std::make_pair(vt.second, vt.first);
141 }
142
143
155 template <typename WeightType>
157 {
158 constexpr VertexId max_num_vertex = std::numeric_limits<VertexId>::max();
159
160 for (const Edge<WeightType> & edge: edges)
161 {
162 // Check that vertex IDs are valid.
163 ah_invalid_argument_if(edge.vt.first >= max_num_vertex or edge.vt.second >= max_num_vertex)
164 << "Vertex ID out of range";
165
166 // Check that the edge weight is a finite number.
167 ah_invalid_argument_if(not std::numeric_limits<WeightType>::is_integer and
168 not std::isfinite(edge.weight))
169 << "Edge weights must be finite numbers";
170
171 // Check that edge weight will not cause overflow.
172 ah_invalid_argument_if(edge.weight > std::numeric_limits<WeightType>::max() / 4)
173 << "Edge weight exceeds maximum supported value";
174 }
175
176 // Check that the graph has no self-edges.
177 for (const Edge<WeightType> & edge: edges)
178 ah_invalid_argument_if(edge.vt.first == edge.vt.second)
179 << "Self-edges are not supported";
180
181 // Check that the graph does not contain duplicate edges.
183 edge_endpoints.reserve(edges.size());
184 for (const Edge<WeightType> & edge: edges)
185 {
186 VertexPair vt = edge.vt;
187 if (vt.first > vt.second)
188 std::swap(vt.first, vt.second);
189 edge_endpoints.append(vt);
190 }
191
194 << "Duplicate edges are not supported";
195 }
196
197
198 /* **************************************************
199 * ** struct Problem_Data **
200 * ************************************************** */
201
206 template <typename WeightType>
208 {
213
216
219
222
225
235
236 // Prevent copying.
237 Problem_Data(const Problem_Data &) = delete;
238
240
242 static Array<EdgeT>
244 {
247
248 for (const Edge<WeightType> & edge: edges_in)
249 if (edge.weight >= 0)
250 real_edges.append(edge);
251
252 return real_edges;
253 }
254
257 {
259 for (const Edge<WeightType> & edge: edges)
260 {
261 const VertexId m = std::max(edge.vt.first, edge.vt.second);
262 assert(m < std::numeric_limits<VertexId>::max());
263 num_vertex = std::max(num_vertex, m + 1);
264 }
265
266 return num_vertex;
267 }
268
271 {
272 nodes.reserve(num_vertex);
273 for (VertexId i = 0; i < num_vertex; ++i)
275
276 for (const EdgeT & edge: edges)
277 {
278 assert(edge.vt.first < num_vertex);
279 assert(edge.vt.second < num_vertex);
280 adjacent_graph.insert_arc(nodes[edge.vt.first],
281 nodes[edge.vt.second],
282 &edge);
283 }
284 }
285
287 template <class Op>
288 void for_adjacent_edges(const VertexId x, Op op) const
289 {
290 assert(x < num_vertex);
291 for (Node_Arc_Iterator<Internal_Graph> it(nodes[x]); it.has_curr(); it.next_ne())
292 {
293 Internal_Arc *arc = it.get_curr();
294 op(arc->get_info());
295 }
296 }
297 };
298
299
300 /* **************************************************
301 * ** struct Blossom **
302 * ************************************************** */
303
304 // Forward declaration.
305 template <typename WeightType>
306 struct NonTrivialBlossom;
307
308
316 template <typename WeightType>
317 struct Blossom
318 {
321
324
327
330
333
336
337 protected:
346
347 public:
350 : Blossom(0, false)
351 {}
352
357
360 {
361 return is_nontrivial_blossom ?
362 static_cast<NonTrivialBlossom<WeightType> *>(this) :
363 nullptr;
364 }
365
368 {
369 return is_nontrivial_blossom ?
370 static_cast<const NonTrivialBlossom<WeightType> *>(this) :
371 nullptr;
372 }
373 };
374
375
376 /* **************************************************
377 * ** struct NonTrivialBlossom **
378 * ************************************************** */
379
384 template <typename WeightType>
385 struct NonTrivialBlossom : public Blossom<WeightType>
386 {
395
398
401
404
405 // Needed by DynDlist internals (sentinel node).
407 : Blossom<WeightType>(0, true),
408 dual_var(0)
409 {}
410
412 template <class Blossom_Container, class Edge_Container>
414 const Edge_Container & edges)
416 dual_var(0)
417 {
418 assert(subblossoms.size() == edges.size());
419 assert(subblossoms.size() % 2 == 1);
420 assert(subblossoms.size() >= 3);
421
422 auto blossom_it = subblossoms.begin();
423 auto edge_it = edges.begin();
424 while (blossom_it != subblossoms.end())
425 {
426 this->subblossoms.append(SubBlossom{});
427 this->subblossoms.get_last().blossom = *blossom_it;
428 this->subblossoms.get_last().edge = *edge_it;
429 ++blossom_it;
430 ++edge_it;
431 }
432 }
433
436 {
437 VertexId pos = 0;
438 for (auto it = subblossoms.get_it(); it.has_curr(); it.next_ne(), ++pos)
439 if (it.get_curr().blossom == subblossom)
440 return pos;
441 assert(false);
442 return 0;
443 }
444 };
445
446
448 template <typename WeightType, typename Func>
449 inline void for_vertices_in_blossom(const Blossom<WeightType> *blossom, Func func)
450 {
451 const NonTrivialBlossom<WeightType> *ntb = blossom->nontrivial();
452 if (ntb)
453 {
454 // Visit all vertices in the non-trivial blossom.
455 // Use an explicit stack to avoid deep call chains.
457 stack.append(ntb);
458
459 while (not stack.is_empty())
460 {
461 const NonTrivialBlossom<WeightType> *b = stack.get_last();
462 (void) stack.remove_last();
463
464 for (const auto & sub: b->subblossoms)
465 {
466 ntb = sub.blossom->nontrivial();
467 if (ntb)
468 stack.append(ntb);
469 else
470 func(sub.blossom->base_vertex);
471 }
472 }
473 }
474 else
475 {
476 // A trivial blossom contains just one vertex.
477 func(blossom->base_vertex);
478 }
479 }
480
481
487
488
489 /* **************************************************
490 * ** struct MatchingContext **
491 * ************************************************** */
492
497 template <typename WeightType>
499 {
500 public:
504
520
522 static constexpr WeightType weight_factor =
523 std::numeric_limits<WeightType>::is_integer ? 2 : 1;
524
527
530
533
536
539
542
545
548
551
554
557 : graph(edges_in)
558 {
559 // Initially, all vertices are unmatched.
560 vertex_mate.reserve(graph.num_vertex);
561 for (VertexId x = 0; x < graph.num_vertex; ++x)
563
564 // Create a trivial blossom for each vertex.
565 trivial_blossom.reserve(graph.num_vertex);
566 for (VertexId x = 0; x < graph.num_vertex; ++x)
567 trivial_blossom.append(BlossomT(x));
568
569 // Initially, all vertices are trivial top-level blossoms.
570 vertex_top_blossom.reserve(graph.num_vertex);
571 for (VertexId x = 0; x < graph.num_vertex; ++x)
573
574 // Vertex duals are initialized to half the maximum edge weight.
576 for (const EdgeT & edge: graph.edges)
577 max_weight = std::max(max_weight, edge.weight);
578
580 vertex_dual.reserve(graph.num_vertex);
581 for (VertexId x = 0; x < graph.num_vertex; ++x)
583
584 // Initialize "vertex_best_edge".
585 vertex_best_edge.reserve(graph.num_vertex);
586 for (VertexId x = 0; x < graph.num_vertex; ++x)
587 vertex_best_edge.append(nullptr);
588
589 // Allocate temporary arrays for path tracing.
590 vertex_marker.reserve(graph.num_vertex);
591 for (VertexId x = 0; x < graph.num_vertex; ++x)
592 vertex_marker.append(false);
593
594 marked_vertex.reserve(graph.num_vertex);
595 }
596
597 // Prevent copying.
599
601
602 /* ********** General support routines: ********** */
603
605 WeightType edge_slack(const EdgeT & edge) const
606 {
607 VertexId x = edge.vt.first;
608 VertexId y = edge.vt.second;
610 return vertex_dual[x] + vertex_dual[y] - weight_factor * edge.weight;
611 }
612
613 /*
614 * Least-slack edge tracking:
615 *
616 * To calculate delta steps, the matching algorithm needs to find
617 * - the least-slack edge between any S-vertex and an unlabeled vertex;
618 * - the least-slack edge between any pair of top-level S-blossoms.
619 *
620 * For each unlabeled vertex and each T-vertex, we keep track of the
621 * least-slack edge to any S-vertex. Tracking for unlabeled vertices
622 * serves to provide the least-slack edge for the delta step.
623 * Tracking for T-vertices is done because such vertices can turn into
624 * unlabeled vertices if they are part of a T-blossom that gets expanded.
625 *
626 * For each top-level S-blossom, we keep track of the least-slack edge
627 * to any S-vertex not in the same blossom.
628 *
629 * Furthermore, for each top-level S-blossom, we keep a list of
630 * least-slack edges to other top-level S-blossoms. For any pair of
631 * top-level S-blossoms, the least-slack edge between them is contained
632 * in the edge list of at least one of the blossoms. An edge list may
633 * contain multiple edges to the same S-blossom. Such redundant edges are
634 * pruned during blossom merging to limit the number of tracked edges.
635 *
636 * Note: For a given vertex or blossom, the identity of the least-slack
637 * edge to any S-blossom remains unchanged during a delta step.
638 * Although the delta step changes edge slacks, it changes the slack
639 * of every edge to an S-vertex by the same amount. Therefore the edge
640 * that had least slack before the delta step, will still have least slack
641 * after the delta step.
642 */
643
650 {
651 for (VertexId x = 0; x < graph.num_vertex; ++x)
652 vertex_best_edge[x] = nullptr;
653
654 for (BlossomT & blossom: trivial_blossom)
655 blossom.best_edge = nullptr;
656
658 {
659 blossom.best_edge = nullptr;
660 blossom.best_edge_set.empty();
661 }
662 }
663
671 {
673 if (cur_best_edge == nullptr or slack < edge_slack(*cur_best_edge))
674 vertex_best_edge[y] = edge;
675 }
676
687 std::pair<const EdgeT *, WeightType> lset_get_best_vertex_edge()
688 {
689 const EdgeT *best_edge = nullptr;
691
692 for (VertexId x = 0; x < graph.num_vertex; ++x)
693 if (vertex_top_blossom[x]->label == LABEL_NONE)
694 {
695 const EdgeT *edge = vertex_best_edge[x];
696 if (edge != nullptr)
697 {
698 WeightType slack = edge_slack(*edge);
699 if (best_edge == nullptr or slack < best_slack)
700 {
701 best_edge = edge;
703 }
704 }
705 }
706
707 return std::make_pair(best_edge, best_slack);
708 }
709
711 static void lset_new_blossom([[maybe_unused]] BlossomT *blossom)
712 {
713 assert(blossom->best_edge == nullptr);
714 assert((blossom->nontrivial() == nullptr)
715 or blossom->nontrivial()->best_edge_set.is_empty());
716 }
717
725 const EdgeT *edge,
727 {
728 const EdgeT *cur_best_edge = blossom->best_edge;
729 if (cur_best_edge == nullptr or slack < edge_slack(*cur_best_edge))
730 blossom->best_edge = edge;
731
732 if (NonTrivialBlossomT *ntb = blossom->nontrivial())
733 ntb->best_edge_set.append(edge);
734 }
735
743 {
744 assert(blossom->best_edge == nullptr);
745 assert(blossom->best_edge_set.is_empty());
746
747 // Collect edges from the sub-blossoms that used to be S-blossoms.
748 for (auto & subblossom_item: blossom->subblossoms)
749 if (BlossomT *sub = subblossom_item.blossom; sub->label == LABEL_S)
750 {
751 if (NonTrivialBlossomT *ntb = sub->nontrivial())
752 {
753 // Take least-slack edge set from this subblossom.
754 blossom->best_edge_set.append(std::move(ntb->best_edge_set));
755 }
756 else
757 {
758 // Trivial blossoms don't maintain a least-slack edge set.
759 // Just consider all incident edges.
760 graph.for_adjacent_edges(sub->base_vertex,
761 [this,blossom](const EdgeT *edge)
762 {
763 // Only take edges between different S-blossoms.
764 VertexId x = edge->vt.first;
765 VertexId y = edge->vt.second;
768 if (bx != by
769 and (bx->label == LABEL_S)
770 and (by->label == LABEL_S))
771 {
772 blossom->best_edge_set.append(edge);
773 }
774 });
775 }
776 }
777
778 // Build a temporary array holding the least-slack edge index to
779 // each top-level S-blossom. This array is indexed by the base vertex
780 // of the blossoms.
783 for (VertexId i = 0; i < graph.num_vertex; ++i)
784 best_edge_to_blossom.append(std::make_pair(nullptr, WeightType{}));
785
786 for (const EdgeT *edge: blossom->best_edge_set)
787 {
788 BlossomT *bx = vertex_top_blossom[edge->vt.first];
789 BlossomT *by = vertex_top_blossom[edge->vt.second];
790 assert(bx == blossom or by == blossom);
791
792 // Ignore internal edges.
793 if (bx != by)
794 {
795 bx = (bx == blossom) ? by : bx;
796
797 // Only consider edges to S-blossoms.
798 if (bx->label == LABEL_S)
799 {
800 // Keep only the least-slack edge to blossom "bx".
801 WeightType slack = edge_slack(*edge);
802 VertexId bx_base = bx->base_vertex;
803 if (auto & best_edge_item = best_edge_to_blossom[bx_base]; (best_edge_item.first == nullptr)
804 or (slack < best_edge_item.second))
805 {
806 best_edge_item.first = edge;
807 best_edge_item.second = slack;
808 }
809 }
810 }
811 };
812
813 // Rebuild a compact list of least-slack edges.
814 // Also find the overall least-slack edge to any other S-blossom.
816 blossom->best_edge_set.empty();
818 {
819 const EdgeT *edge = best_edge_item.first;
820 if (edge != nullptr)
821 {
822 blossom->best_edge_set.append(edge);
824 if (blossom->best_edge == nullptr or slack < best_slack)
825 {
826 blossom->best_edge = edge;
828 }
829 }
830 }
831 }
832
843 std::pair<const EdgeT *, WeightType> lset_get_best_blossom_edge()
844 {
845 const EdgeT *best_edge = nullptr;
847
848 auto consider_blossom = [this,&best_edge,&best_slack](BlossomT *blossom)
849 {
850 if (blossom->parent == nullptr and (blossom->label == LABEL_S))
851 {
852 const EdgeT *edge = blossom->best_edge;
853 if (edge != nullptr)
854 {
855 WeightType slack = edge_slack(*edge);
856 if (best_edge == nullptr or (slack < best_slack))
857 {
858 best_edge = edge;
860 }
861 }
862 }
863 };
864
865 for (BlossomT & blossom: trivial_blossom)
866 consider_blossom(&blossom);
867 for (BlossomT & blossom: nontrivial_blossom)
868 consider_blossom(&blossom);
869
870 return std::make_pair(best_edge, best_slack);
871 }
872
873 /* ********** Creating and expanding blossoms: ********** */
874
890 {
892
893 // Initialize a path containing only the edge (x, y).
894 AlternatingPath path;
895 path.edges.append(std::make_pair(x, y));
896
897 // "first_common" is the first common ancestor of "x" and "y"
898 // in the alternating tree, or "nullptr" if no common ancestor
899 // has been found.
900 BlossomT *first_common = nullptr;
901
902 // Alternate between tracing the path from "x" and the path from "y".
903 // This ensures that the search time is bounded by the size of any
904 // newly found blossom.
905 while (x != NO_VERTEX or y != NO_VERTEX)
906 {
907 // Trace path from vertex "x".
908 if (x != NO_VERTEX)
909 {
910 // Stop if we found a common ancestor.
912 if (vertex_marker[bx->base_vertex])
913 {
915 break;
916 }
917
918 // Mark blossom as potential common ancestor.
919 vertex_marker[bx->base_vertex] = true;
920 marked_vertex.append(bx->base_vertex);
921
922 // Trace back to the parent in the alternating tree.
923 x = bx->tree_edge.first;
924 if (x != NO_VERTEX)
925 // While tracing from x, prepend so the left half of the path
926 // remains in forward order.
927 path.edges.insert(bx->tree_edge);
928 }
929
930 // Trace path from vertex "y".
931 if (y != NO_VERTEX)
932 {
933 // Stop if we found a common ancestor.
935 if (vertex_marker[by->base_vertex])
936 {
938 break;
939 }
940
941 // Mark blossom as potential common ancestor.
942 vertex_marker[by->base_vertex] = true;
943 marked_vertex.append(by->base_vertex);
944
945 // Trace back to the parent in the alternating tree.
946 y = by->tree_edge.first;
947 if (y != NO_VERTEX)
948 // While tracing from y, append so the right half stays in
949 // forward order from left to right.
950 path.edges.append(std::make_pair(by->tree_edge.second, y));
951 }
952 }
953
954 // Remove all markers we placed.
955 for (const VertexId k: marked_vertex)
956 vertex_marker[k] = false;
958
959 // If we found a common ancestor, trim the paths so they end there.
960 if (first_common)
961 {
962 assert(first_common->label == LABEL_S);
963 while (vertex_top_blossom[path.edges.get_first().first] != first_common)
964 (void) path.edges.remove_first();
965 while (vertex_top_blossom[path.edges.get_last().second] != first_common)
966 (void) path.edges.remove_last();
967 }
968
969 // Any alternating path between S-blossoms must have odd length.
970 assert(path.edges.size() % 2 == 1);
971
972 return path;
973 }
974
983 void make_blossom(const AlternatingPath & path)
984 {
985 assert(path.edges.size() % 2 == 1);
986 assert(path.edges.size() >= 3);
987
988 // First pass:
989 // Build the ordered ring of sub-blossoms from edge.first.
990 // We keep this pass separate from the cycle check below to keep the
991 // first/second roles explicit and easy to audit.
992 Array<BlossomT *> subblossoms;
993 subblossoms.reserve(path.edges.size());
994 for (VertexPair edge: path.edges)
995 subblossoms.append(vertex_top_blossom[edge.first]);
996
997 // Second pass:
998 // Validate that each edge.second lands in the next sub-blossom in the
999 // ring (with wrap-around at the end).
1000 VertexId pos = 0;
1001 for ([[maybe_unused]] VertexPair edge: path.edges)
1002 {
1003 pos = (pos + 1) % subblossoms.size();
1004 assert(vertex_top_blossom[edge.second] == subblossoms[pos]);
1005 }
1006
1007 // Create the new blossom object.
1008 nontrivial_blossom.append(NonTrivialBlossomT(subblossoms, path.edges));
1009 NonTrivialBlossomT *blossom = &nontrivial_blossom.get_last();
1010
1011 // Link the subblossoms to their new parent.
1012 for (BlossomT *sub: subblossoms)
1013 sub->parent = blossom;
1014
1015 // Mark vertices as belonging to the new blossom.
1016 for_vertices_in_blossom(blossom, [this,blossom](VertexId x)
1017 {
1018 vertex_top_blossom[x] = blossom;
1019 });
1020
1021 // Assign label S to the new blossom and link to the alternating tree.
1022 assert(subblossoms.get_first()->label == LABEL_S);
1023 blossom->label = LABEL_S;
1024 blossom->tree_edge = subblossoms.get_first()->tree_edge;
1025
1026 // Consider vertices inside former T-sub-blossoms which now
1027 // became S-vertices; add them to the queue.
1028 for (BlossomT *sub: subblossoms)
1029 if (sub->label == LABEL_T)
1030 for_vertices_in_blossom(sub, [this](VertexId x)
1031 {
1032 queue.put(x);
1033 });
1034
1035 // Merge least-slack edges for the S-sub-blossoms.
1036 lset_merge_blossoms(blossom);
1037 }
1038
1041 {
1042 for (auto it = nontrivial_blossom.get_it(); it.has_curr(); it.next_ne())
1043 if (&it.get_curr() == blossom)
1044 {
1045 (void) it.del();
1046 return;
1047 }
1048 assert(false);
1049 }
1050
1057 {
1058 assert(blossom->parent == nullptr);
1059 assert(blossom->label == LABEL_T);
1060
1061 // Convert sub-blossoms into top-level blossoms.
1062 for (const auto & sub: blossom->subblossoms)
1063 {
1064 BlossomT *sub_blossom = sub.blossom;
1065 assert(sub_blossom->parent == blossom);
1066 assert(sub_blossom->label == LABEL_NONE);
1067 sub_blossom->parent = nullptr;
1069 [this,sub_blossom](VertexId x)
1070 {
1072 });
1073 }
1074
1075 // The expanded blossom was part of an alternating tree.
1076 // We must now reconstruct the part of the alternating tree
1077 // that ran through this blossom by linking some of the sub-blossoms
1078 // into the tree.
1079
1080 // Find the sub-blossom that was attached to the parent node
1081 // in the alternating tree.
1082 BlossomT *entry = vertex_top_blossom[blossom->tree_edge.second];
1083
1084 // Assign label T to that blossom and link to the alternating tree.
1085 entry->label = LABEL_T;
1086 entry->tree_edge = blossom->tree_edge;
1087
1088 // Find the position of this sub-blossom within the expanding blossom.
1089 const VertexId entry_pos = blossom->find_subblossom_pos(entry);
1090 auto sub_it = blossom->subblossoms.get_it();
1091 for (VertexId i = 0; i < entry_pos; ++i)
1092 sub_it.next_ne();
1093
1094 if (entry_pos % 2 == 0)
1095 {
1096 // Walk backward to the base.
1097 while (sub_it.get_pos() != 0)
1098 {
1099 sub_it.prev();
1100 assign_label_s(sub_it.get_curr().edge.first);
1101
1102 assert(sub_it.get_pos() != 0);
1103 sub_it.prev();
1104 auto & cur = sub_it.get_curr();
1105 cur.blossom->label = LABEL_T;
1106 cur.blossom->tree_edge = flip_vertex_pair(cur.edge);
1107 }
1108 }
1109 else
1110 {
1111 // Walk forward to the base.
1112 while (sub_it.has_curr())
1113 {
1114 assign_label_s(sub_it.get_curr().edge.second);
1115 sub_it.next();
1116
1117 assert(sub_it.has_curr());
1118 VertexPair tree_edge = sub_it.get_curr().edge;
1119 sub_it.next();
1120
1121 BlossomT *sub_blossom = sub_it.has_curr() ?
1122 sub_it.get_curr().blossom :
1123 blossom->subblossoms.get_first().blossom;
1124 sub_blossom->label = LABEL_T;
1125 sub_blossom->tree_edge = tree_edge;
1126 }
1127 }
1128
1129 // Delete the expanded blossom.
1130 erase_nontrivial_blossom(blossom);
1131 }
1132
1139 {
1140 assert(blossom->parent == nullptr);
1141 assert(blossom->label == LABEL_NONE);
1142
1143 // Convert sub-blossoms into top-level blossoms.
1144 for (const auto & sub: blossom->subblossoms)
1145 {
1146 BlossomT *sub_blossom = sub.blossom;
1147 assert(sub_blossom->parent == blossom);
1148 assert(sub_blossom->label == LABEL_NONE);
1149 sub_blossom->parent = nullptr;
1151 [this,sub_blossom](VertexId x)
1152 {
1154 });
1155 }
1156
1157 // Delete the expanded blossom.
1158 erase_nontrivial_blossom(blossom);
1159 }
1160
1161 /* ********** Augmenting: ********** */
1162
1171 BlossomT *entry,
1172 DynListStack<std::pair<NonTrivialBlossomT *, BlossomT *>> & rec_stack)
1173 {
1174 const VertexId entry_pos = blossom->find_subblossom_pos(entry);
1175 auto sub_it = blossom->subblossoms.get_it();
1176 for (VertexId i = 0; i < entry_pos; ++i)
1177 sub_it.next_ne();
1178 while ((sub_it.get_pos() != 0) and sub_it.has_curr())
1179 {
1180 VertexId x, y;
1181 BlossomT *bx = nullptr;
1182 BlossomT *by = nullptr;
1183
1184 if (entry_pos % 2 == 0)
1185 {
1186 // Walk backward to the base.
1187 sub_it.prev();
1188 by = sub_it.get_curr().blossom;
1189 assert(sub_it.get_pos() != 0);
1190 sub_it.prev();
1191 bx = sub_it.get_curr().blossom;
1192 std::tie(x, y) = sub_it.get_curr().edge;
1193 }
1194 else
1195 {
1196 // Walk forward to the base.
1197 sub_it.next();
1198 assert(sub_it.has_curr());
1199 std::tie(x, y) = sub_it.get_curr().edge;
1200 bx = sub_it.get_curr().blossom;
1201 sub_it.next();
1202 by = sub_it.has_curr() ?
1203 sub_it.get_curr().blossom :
1204 blossom->subblossoms.get_first().blossom;
1205 }
1206
1207 // Pull this edge into the matching.
1208 vertex_mate[x] = y;
1209 vertex_mate[y] = x;
1210
1211 // Augment through any non-trivial subblossoms touching this edge.
1213 if (bx_ntb != nullptr)
1214 rec_stack.put(std::make_pair(bx_ntb, &trivial_blossom[x]));
1215
1217 if (by_ntb != nullptr)
1218 rec_stack.put(std::make_pair(by_ntb, &trivial_blossom[y]));
1219 }
1220
1221 // Re-orient the blossom (rotate so "entry" is first).
1222 if (entry_pos != 0)
1223 {
1225 ring.reserve(blossom->subblossoms.size());
1226 for (auto it = blossom->subblossoms.get_it(); it.has_curr(); it.next_ne())
1227 ring.append(it.get_curr());
1228 const size_t n = ring.size();
1230 for (size_t k = 0; k < n; ++k)
1231 rotated.append(ring[(entry_pos + k) % n]);
1232 blossom->subblossoms.swap(rotated);
1233 }
1234
1235 // Update the base vertex.
1236 // We can pull the new base vertex from the entry sub-blossom
1237 // since its augmentation has already finished.
1238 blossom->base_vertex = entry->base_vertex;
1239 }
1240
1250 {
1251 // Use an explicit stack to avoid deep recursion.
1253 rec_stack.put(std::make_pair(blossom, entry));
1254
1255 while (not rec_stack.is_empty())
1256 {
1259 std::tie(outer_blossom, inner_entry) = rec_stack.top();
1260
1262 assert(inner_blossom != nullptr);
1263
1265 {
1266 // After augmenting "inner_blossom",
1267 // continue by augmenting its parent.
1268 rec_stack.top() = std::make_pair(outer_blossom, inner_blossom);
1269 }
1270 else
1271 {
1272 // After augmenting "inner_blossom",
1273 // this entire "outer_blossom" will be finished.
1274 (void) rec_stack.get();
1275 }
1276
1277 // Augment "inner_blossom".
1279 }
1280 }
1281
1288 {
1289 // Check that the path starts and ends in an unmatched blossom.
1290 assert(path.edges.size() % 2 == 1);
1291 assert(vertex_mate[vertex_top_blossom[path.edges.get_first().first]->base_vertex] == NO_VERTEX);
1292 assert(vertex_mate[vertex_top_blossom[path.edges.get_last().second]->base_vertex] == NO_VERTEX);
1293
1294 // Process the unmatched edges on the augmenting path.
1295 auto edge_it = path.edges.begin();
1296 auto edge_end = path.edges.end();
1297 while (edge_it != edge_end)
1298 {
1299 VertexId x = edge_it->first;
1300 VertexId y = edge_it->second;
1301
1302 // Augment any non-trivial blossoms that touch this edge.
1305 if (bx_ntb != nullptr)
1307
1310 if (by_ntb != nullptr)
1312
1313 // Pull this edge into the matching.
1314 vertex_mate[x] = y;
1315 vertex_mate[y] = x;
1316
1317 // Move forward through the augmenting path to
1318 // the next edge to be matched.
1319 // Edges along an augmenting path alternate unmatched/matched;
1320 // only unmatched edges are pulled into the new matching.
1321 ++edge_it;
1322 if (edge_it == edge_end)
1323 break;
1324 ++edge_it;
1325 }
1326 }
1327
1328 /* ********** Labels and alternating tree: ********** */
1329
1343 {
1344 // Assign label S to the blossom that contains vertex "x".
1346 assert(bx->label == LABEL_NONE);
1347 bx->label = LABEL_S;
1348
1349 VertexId y = vertex_mate[x];
1350 if (y == NO_VERTEX)
1351 {
1352 // Vertex "x" is unmatched.
1353 // It must be either a top-level vertex or the base vertex of
1354 // a top-level blossom.
1355 assert(bx->base_vertex == x);
1356
1357 // Mark the blossom as root of an alternating tree.
1358 bx->tree_edge = std::make_pair(NO_VERTEX, x);
1359 }
1360 else
1361 {
1362 // Vertex "x" is matched to T-vertex "y".
1363 assert(vertex_top_blossom[y]->label == LABEL_T);
1364
1365 // Attach the blossom to the alternating tree via vertex "y".
1366 bx->tree_edge = std::make_pair(y, x);
1367 }
1368
1369 // Start least-slack edge tracking for the S-blossom.
1371
1372 // Add all vertices inside the newly labeled S-blossom to the queue.
1374 {
1375 queue.put(v);
1376 });
1377 }
1378
1392 {
1393 assert(vertex_top_blossom[x]->label == LABEL_S);
1394
1396 assert(by->label == LABEL_NONE);
1397
1398 // If "y" is part of a zero-dual blossom, expand it.
1399 // This would otherwise likely happen through a zero-delta4 step,
1400 // so we can just do it now and avoid a substage.
1402 while (ntb != nullptr and ntb->dual_var == 0)
1403 {
1406 assert(by->label == LABEL_NONE);
1407 ntb = by->nontrivial();
1408 }
1409
1410 // Assign label T to the top-level blossom that contains vertex "y".
1411 by->label = LABEL_T;
1412 by->tree_edge = std::make_pair(x, y);
1413
1414 // Assign label S to the blossom that is mated to the T-blossom.
1415 const VertexId z = vertex_mate[by->base_vertex];
1416 assert(z != NO_VERTEX);
1417 assign_label_s(z);
1418 }
1419
1432 {
1433 assert(vertex_top_blossom[x]->label == LABEL_S);
1434 assert(vertex_top_blossom[y]->label == LABEL_S);
1435
1436 // Trace back through the alternating trees from "x" and "y".
1438
1439 // If the path is a cycle, create a new blossom.
1440 // Otherwise the path is an augmenting path.
1441 // Note that an alternating starts and ends in the same blossom,
1442 // but not necessarily in the same vertex within that blossom.
1443 VertexId p = path.edges.get_first().first;
1444 VertexId q = path.edges.get_last().second;
1446 {
1447 make_blossom(path);
1448 return false;
1449 }
1450 augment_matching(path);
1451 return true;
1452 }
1453
1466 {
1467 // Process the queue of S-vertices to be scanned.
1468 // This loop runs through O(n) iterations per stage.
1469 while (not queue.is_empty())
1470 {
1471 // Take one vertex from the queue.
1472 VertexId x = queue.get();
1473
1474 assert(vertex_top_blossom[x]->label == LABEL_S);
1475
1476 // Scan the edges that are incident on "x".
1477 // This loop runs through O(m) iterations per stage.
1478 bool found_augmenting = false;
1479 graph.for_adjacent_edges(x, [this,x,&found_augmenting](const EdgeT *edge)
1480 {
1481 if (found_augmenting)
1482 return;
1483
1484 // "x" is one endpoint of this incident edge;
1485 // recover the opposite endpoint.
1486 VertexId y = (edge->vt.first != x) ? edge->vt.first : edge->vt.second;
1487
1488 // Note: The top-level blossom of vertex "x" may change
1489 // during this loop, so we need to refresh it in each pass.
1491
1492 // Ignore edges that are internal to a blossom.
1493 if (bx == vertex_top_blossom[y])
1494 return;
1495
1497
1498 // Check whether this edge is tight (has zero slack).
1499 // Only tight edges may be part of an alternating tree.
1500 WeightType slack = edge_slack(*edge);
1501 if (slack <= 0)
1502 {
1503 if (ylabel == LABEL_NONE)
1504 {
1505 // Found a tight edge to an unlabeled blossom.
1506 // Assign label T to the blossom that contains "y".
1507 assign_label_t(x, y);
1508 }
1509 else if (ylabel == LABEL_S)
1510 {
1511 // Found a tight edge between two S-blossoms.
1512 // Find either a new blossom or an augmenting path.
1513 if (add_s_to_s_edge(x, y))
1514 found_augmenting = true;
1515 }
1516 }
1517 else if (ylabel == LABEL_S)
1518 {
1519 // Found a non-tight edge between two S-blossoms.
1520 // Pass it to the least-slack edge tracker.
1522 }
1523
1524 if (ylabel != LABEL_S)
1525 {
1526 // Found an to a T-vertex or unlabeled vertex "y".
1527 // Pass it to the least-slack edge tracker.
1528 // Tight edges must also be tracked in this way.
1530 }
1531 });
1532
1533 if (found_augmenting)
1534 return true;
1535 }
1536
1537 // No further S vertices to scan, and no augmenting path found.
1538 return false;
1539 }
1540
1541 /* ********** Delta steps: ********** */
1542
1557 {
1558 DeltaStep delta;
1559 delta.blossom = nullptr;
1560
1561 // Compute delta1: minimum dual variable of any S-vertex.
1562 delta.kind = 1;
1563 delta.value = std::numeric_limits<WeightType>::max();
1564 for (VertexId x = 0; x < graph.num_vertex; ++x)
1565 if (vertex_top_blossom[x]->label == LABEL_S)
1566 delta.value = std::min(delta.value, vertex_dual[x]);
1567
1568 // Compute delta2: minimum slack of any edge between an S-vertex and
1569 // an unlabeled vertex.
1570 const EdgeT *edge;
1572 std::tie(edge, slack) = lset_get_best_vertex_edge();
1573 if (edge != nullptr and slack <= delta.value)
1574 {
1575 delta.kind = 2;
1576 delta.value = slack;
1577 delta.edge = edge->vt;
1578 }
1579
1580 // Compute delta3: half minimum slack of any edge between two
1581 // top-level S-blossoms.
1582 std::tie(edge, slack) = lset_get_best_blossom_edge();
1583 if (edge != nullptr and slack / 2 <= delta.value)
1584 {
1585 delta.kind = 3;
1586 delta.value = slack / 2;
1587 delta.edge = edge->vt;
1588 }
1589
1590 // Compute delta4: half minimum dual of a top-level T-blossom.
1591 for (NonTrivialBlossomT & blossom: nontrivial_blossom)
1592 if (not blossom.parent and blossom.label == LABEL_T)
1593 if (blossom.dual_var / 2 <= delta.value)
1594 {
1595 delta.kind = 4;
1596 delta.value = blossom.dual_var / 2;
1597 delta.blossom = &blossom;
1598 }
1599
1600 return delta;
1601 }
1602
1605 {
1606 // Apply delta to dual variables of all vertices.
1607 for (VertexId x = 0; x < graph.num_vertex; ++x)
1608 if (const BlossomLabel xlabel = vertex_top_blossom[x]->label; xlabel == LABEL_S)
1609 {
1610 // S-vertex: subtract delta from dual variable.
1611 vertex_dual[x] -= delta;
1612 }
1613 else if (xlabel == LABEL_T)
1614 {
1615 // T-vertex: add delta to dual variable.
1616 vertex_dual[x] += delta;
1617 }
1618
1619 // Apply delta to dual variables of top-level non-trivial blossoms.
1620 for (NonTrivialBlossomT & blossom: nontrivial_blossom)
1621 if (blossom.parent == nullptr)
1622 {
1623 if (blossom.label == LABEL_S)
1624 {
1625 // S-blossom: add 2*delta to dual variable.
1626 blossom.dual_var += 2 * delta;
1627 }
1628 else if (blossom.label == LABEL_T)
1629 {
1630 // T-blossom: subtract 2*delta from dual variable.
1631 blossom.dual_var -= 2 * delta;
1632 }
1633 }
1634 }
1635
1636 /* ********** Main algorithm: ********** */
1637
1645 {
1646 // Remove blossom labels.
1647 for (BlossomT & blossom: trivial_blossom)
1648 blossom.label = LABEL_NONE;
1649 for (BlossomT & blossom: nontrivial_blossom)
1650 blossom.label = LABEL_NONE;
1651
1652 // Clear the S-vertex queue.
1653 queue.clear();
1654
1655 // Reset least-slack edge tracking.
1656 lset_reset();
1657 }
1658
1673 {
1674 // Assign label S to all unmatched vertices and put them in the queue.
1675 for (VertexId x = 0; x < graph.num_vertex; ++x)
1676 if (vertex_mate[x] == NO_VERTEX)
1677 assign_label_s(x);
1678
1679 // Stop if all vertices are matched.
1680 // No further improvement is possible in that case.
1681 // This avoids messy calculations of delta steps without any S-vertex.
1682 if (queue.is_empty())
1683 return false;
1684
1685 // Each pass through the following loop is a "substage".
1686 // The substage tries to find an augmenting path.
1687 // If an augmenting path is found, we augment the matching and end
1688 // the stage. Otherwise we update the dual LPP problem and enter the
1689 // next substage, or stop if no further improvement is possible.
1690 //
1691 // This loop runs through at most O(n) iterations per stage.
1692 bool augmented = false;
1693 while (true)
1694 {
1695 // Grow alternating trees.
1696 // End the stage if an augmenting path is found.
1698 if (augmented)
1699 break;
1700
1701 // Calculate delta step in the dual LPP problem.
1703
1704 // Apply the delta step to the dual variables.
1705 substage_apply_delta_step(delta.value);
1706
1707 if (delta.kind == 2)
1708 {
1709 // Use the edge from S-vertex to unlabeled vertex that got
1710 // unlocked through the delta update.
1711 VertexId x = delta.edge.first;
1712 VertexId y = delta.edge.second;
1713 if (vertex_top_blossom[x]->label != LABEL_S)
1714 std::swap(x, y);
1715 assign_label_t(x, y);
1716 }
1717 else if (delta.kind == 3)
1718 {
1719 // Use the S-to-S edge that got unlocked by the delta update.
1720 // This may reveal an augmenting path.
1721 VertexId x = delta.edge.first;
1722 VertexId y = delta.edge.second;
1724 if (augmented)
1725 break;
1726 }
1727 else if (delta.kind == 4)
1728 {
1729 // Expand the T-blossom that reached dual value 0 through
1730 // the delta update.
1731 assert(delta.blossom);
1732 expand_t_blossom(delta.blossom);
1733 }
1734 else
1735 {
1736 // No further improvement possible. End the stage.
1737 assert(delta.kind == 1);
1738 break;
1739 }
1740 }
1741
1742 // Remove all labels, clear queue.
1743 reset_stage();
1744
1745 // Return True if the matching was augmented.
1746 return augmented;
1747 }
1748
1750 void run()
1751 {
1752 // Improve the solution until no further improvement is possible.
1753 //
1754 // Each successful pass through this loop increases the number
1755 // of matched edges by 1.
1756 //
1757 // This loop runs through at most (n/2 + 1) iterations.
1758 // Each iteration takes time O(n**2).
1759 while (run_stage());
1760 }
1761 };
1762
1763
1764 /* **************************************************
1765 * ** struct MatchingVerifier **
1766 * ************************************************** */
1767
1769 template <typename WeightType>
1771 {
1772 public:
1774 : ctx(ctx),
1775 graph(ctx.graph),
1776 edge_duals()
1777 {
1778 edge_duals.reserve(ctx.graph.edges.size());
1779 for (size_t i = 0; i < ctx.graph.edges.size(); ++i)
1780 edge_duals.append(0);
1781 }
1782
1798
1799 private:
1803
1804 static bool checked_add(WeightType & result, WeightType a, WeightType b)
1805 {
1806 if (a > std::numeric_limits<WeightType>::max() - b)
1807 return true;
1808 result = a + b;
1809 return false;
1810 }
1811
1813 std::size_t edge_index(const EdgeT *edge)
1814 {
1815 return edge - &graph.edges.base();
1816 }
1817
1820 {
1821 // Count matched vertices and check symmetry of "vertex_mate".
1823 for (VertexId x = 0; x < graph.num_vertex; ++x)
1824 {
1825 VertexId y = ctx.vertex_mate[x];
1826 if (y != NO_VERTEX)
1827 {
1829 if (ctx.vertex_mate[y] != x)
1830 return false;
1831 }
1832 }
1833
1834 // Count matched edges.
1836 for (const EdgeT & edge: graph.edges)
1837 if (ctx.vertex_mate[edge.vt.first] == edge.vt.second)
1839
1840 // Check that all matched vertices correspond to matched edges.
1841 return (num_matched_vertex == 2 * num_matched_edge);
1842 }
1843
1849 {
1850 for (VertexId x = 0; x < graph.num_vertex; ++x)
1851 {
1852 if (ctx.vertex_dual[x] < 0)
1853 return false;
1854 if (ctx.vertex_mate[x] == NO_VERTEX and ctx.vertex_dual[x] != 0)
1855 return false;
1856 }
1857 return true;
1858 }
1859
1862 {
1863 for (const NonTrivialBlossomT & blossom: ctx.nontrivial_blossom)
1864 if (blossom.dual_var < 0)
1865 return false;
1866 return true;
1867 }
1868
1888 {
1889 // For each vertex "x",
1890 // "vertex_depth[x]" is the depth of the smallest blossom on
1891 // the current descent path that contains "x".
1893 vertex_depth.reserve(graph.num_vertex);
1894 for (VertexId i = 0; i < graph.num_vertex; ++i)
1895 vertex_depth.append(0);
1896
1897 // At each depth, keep track of the sum of blossom duals
1898 // along the current descent path.
1901
1902 // At each depth, keep track of the number of matched edges
1903 // along the current ascent path.
1906
1907 // Use an explicit stack to avoid deep recursion.
1908 // Second item tracks the next sub-blossom index to process.
1910 stack.put(std::make_pair(blossom, size_t{0}));
1911
1912 while (! stack.is_empty())
1913 {
1914 VertexId depth = stack.size();
1915 auto & stack_elem = stack.top();
1916 blossom = stack_elem.first;
1917 size_t sub_index = stack_elem.second;
1918
1919 if (sub_index == 0)
1920 {
1921 // We just entered this sub-blossom.
1922 // Update the depth of all vertices in this sub-blossom.
1924 [&vertex_depth,depth](VertexId x)
1925 {
1926 vertex_depth[x] = depth;
1927 });
1928
1929 // Calculate the sum of blossom duals at the new depth.
1930 path_sum_dual.append(path_sum_dual.get_last());
1931 if (checked_add(path_sum_dual.get_last(),
1932 path_sum_dual.get_last(),
1933 blossom->dual_var))
1934 return false;
1935
1936 // Initialize the number of matched edges at the new depth.
1937 path_num_matched.append(0);
1938
1939 if (blossom->subblossoms.size() < 3)
1940 return false;
1941 }
1942
1943 if (sub_index < blossom->subblossoms.size())
1944 {
1945 auto it = blossom->subblossoms.get_it();
1946 for (size_t i = 0; i < sub_index; ++i)
1947 it.next_ne();
1948 auto & sub_item = it.get_curr();
1949 ++stack_elem.second;
1950
1951 // Examine the current sub-blossom.
1952 BlossomT *sub = sub_item.blossom;
1953 NonTrivialBlossomT *ntb = sub->nontrivial();
1954 if (ntb)
1955 {
1956 // Prepare to descend into this sub-blossom.
1957 stack.put(std::make_pair(ntb, size_t{0}));
1958 }
1959 else
1960 {
1961 // Handle single vertex.
1962 // For each incident edge, find the smallest blossom
1963 // that contains it.
1964 VertexId x = sub->base_vertex;
1965 bool failed = false;
1966 graph.for_adjacent_edges(x, [this,x,&failed,&vertex_depth,&path_sum_dual,&path_num_matched](
1967 const EdgeT *edge)
1968 {
1969 if (failed)
1970 return;
1971
1972 // Only consider edges pointing out from "x".
1973 // This avoids processing the same undirected edge
1974 // twice during verification.
1975 if (edge->vt.first != x)
1976 return;
1977
1978 VertexId y = edge->vt.second;
1980 if (edge_depth > 0)
1981 {
1982 // Found the smallest blossom that contains this edge.
1983 // Add the duals of the containing blossoms.
1985 edge_duals[edge_index(edge)],
1987 {
1988 failed = true;
1989 return;
1990 }
1991
1992 // Update the number of matched edges in the blossom.
1993 if (ctx.vertex_mate[x] == y)
1995 }
1996 });
1997
1998 if (failed)
1999 return false;
2000 }
2001 }
2002 else
2003 {
2004 // We are leaving the current sub-blossom.
2005
2006 // Count the number of vertices inside this blossom.
2010 {
2012 });
2013
2014 // Check that the blossom is "full".
2015 // A blossom is full if all except one of its vertices
2016 // are matched to another vertex within the blossom.
2019 return false;
2020
2021 // Update the number of matched edges in the parent blossom.
2022 path_num_matched[depth - 1] += path_num_matched[depth];
2023
2024 // Push vertices in this sub-blossom back to the parent.
2025 for_vertices_in_blossom(blossom, [&vertex_depth,depth](VertexId x)
2026 {
2027 vertex_depth[x] = depth - 1;
2028 });
2029
2030 // Trim the descending path.
2031 (void) path_sum_dual.remove_last();
2032 (void) path_num_matched.remove_last();
2033
2034 // Remove the current blossom from the stack.
2035 (void) stack.get();
2036 }
2037 }
2038
2039 return true;
2040 }
2041
2047 {
2048 // For each edge, calculate the sum of its vertex duals.
2049 for (const EdgeT & edge: graph.edges)
2050 if (checked_add(edge_duals[edge_index(&edge)],
2051 ctx.vertex_dual[edge.vt.first],
2052 ctx.vertex_dual[edge.vt.second]))
2053 return false;
2054
2055 // Descend down each top-level blossom.
2056 // Check that blossoms are full.
2057 // Add blossom duals to the edges contained inside the blossoms.
2058 // This takes total time O(n**2).
2059 for (const NonTrivialBlossomT & blossom: ctx.nontrivial_blossom)
2060 if (blossom.parent == nullptr)
2061 if (not check_blossom(&blossom))
2062 return false;
2063
2064 return true;
2065 }
2066
2074 {
2075 for (const EdgeT & edge: graph.edges)
2076 {
2078 WeightType weight = ctx.weight_factor * edge.weight;
2079
2080 if (weight > duals)
2081 return false;
2082 WeightType slack = duals - weight;
2083
2084 if (ctx.vertex_mate[edge.vt.first] == edge.vt.second)
2085 if (slack != 0)
2086 return false;
2087 }
2088 return true;
2089 }
2090
2091 private:
2094
2097
2103 };
2104 } // namespace impl
2105
2106
2107 /* **************************************************
2108 * ** public functions **
2109 * ************************************************** */
2110
2133 template <typename WeightType>
2135 const Array<Edge<WeightType>> & edges)
2136 {
2137 // Check that the input meets all constraints.
2139
2140 // Run matching algorithm.
2142 matching.run();
2143
2144 // Verify that the solution is optimal (works only for integer weights).
2145 if (std::numeric_limits<WeightType>::is_integer)
2147
2148 // Extract the matched edges.
2149 Array<VertexPair> solution;
2150 solution.reserve(matching.graph.edges.size());
2151 for (const Edge<WeightType> & edge: matching.graph.edges)
2152 if (matching.vertex_mate[edge.vt.first] == edge.vt.second)
2153 solution.append(edge.vt);
2154
2155 return solution;
2156 }
2157
2158
2175 template <typename WeightType>
2178 {
2179 // For integer types, min() is the most-negative representable value.
2180 // For floating-point types, min() is the smallest *positive* normalized
2181 // value — not the most negative — so we must use lowest() instead.
2183 (std::numeric_limits<WeightType>::is_integer
2184 ? std::numeric_limits<WeightType>::min()
2185 : std::numeric_limits<WeightType>::lowest()) / 4;
2186 const WeightType max_safe_weight = std::numeric_limits<WeightType>::max() / 4;
2187
2188 // Copy edges.
2190
2191 // Don't worry about empty graphs.
2192 if (edges.is_empty())
2193 return edges;
2194
2195 // Count number of vertices.
2196 // Determine minimum and maximum edge weight.
2197 VertexId num_vertex = 0;
2198 WeightType min_weight = edges.get_first().weight;
2200
2201 constexpr VertexId max_num_vertex = std::numeric_limits<VertexId>::max();
2202 for (const Edge<WeightType> & edge: edges)
2203 {
2204 const VertexId m = std::max(edge.vt.first, edge.vt.second);
2206 << "Vertex ID out of range";
2207 num_vertex = std::max(num_vertex, m + 1);
2208
2209 if (not std::numeric_limits<WeightType>::is_integer)
2210 ah_invalid_argument_if(! std::isfinite(edge.weight))
2211 << "Edge weights must be finite numbers";
2212
2213 min_weight = std::min(min_weight, edge.weight);
2214 max_weight = std::max(max_weight, edge.weight);
2215 }
2216
2217 // Calculate weight range and required weight adjustment.
2219 << "Edge weight exceeds maximum supported value";
2220
2222
2224 << "Adjusted edge weight exceeds maximum supported value";
2225
2226 // Do nothing if the weights already ensure maximum-cardinality.
2227 if (min_weight > 0 and min_weight >= num_vertex * weight_range)
2228 return edges;
2229
2230 WeightType delta;
2231 if (weight_range > 0)
2232 {
2233 // Increase weights to make minimum edge weight large enough
2234 // to improve any non-maximum-cardinality matching.
2235 delta = num_vertex * weight_range - min_weight;
2236 }
2237 else
2238 {
2239 // All weights are the same. Increase weights to make them positive.
2240 delta = 1 - min_weight;
2241 }
2242
2243 assert(delta >= 0);
2244
2246 << "Adjusted edge weight exceeds maximum supported value";
2247
2248 // Increase all edge weights by "delta".
2249 for (Edge<WeightType> & edge: edges)
2250 edge.weight += delta;
2251
2252 return edges;
2253 }
2254} // namespace Aleph::blossom_weighted_detail::mwmatching
2255
2256
2257#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:644
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:138
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
void empty() noexcept
Empties the container.
Definition tpl_array.H:341
constexpr bool is_empty() const noexcept
Checks if the container is empty.
Definition tpl_array.H:359
T & get_first() noexcept
return a modifiable reference to the first element.
Definition tpl_array.H:378
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
T & get_last() noexcept
return a modifiable reference to the last element.
Definition tpl_array.H:392
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
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:637
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
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.
bool all_unique(const Container &container, Equal &eq)
Check if all elements in a container are unique using a comparator.
and
Check uniqueness with explicit hash + equality functors.
DynArray< T > & in_place_sort(DynArray< T > &c, Cmp cmp=Cmp())
Sorts a DynArray in place.
Definition ahSort.H:328
STL namespace.
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1120
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.