From 5500750c130a0be836b64030fe6285de66975b1d Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Thu, 14 Nov 2024 23:31:11 +0100 Subject: [PATCH] Reorganize code that handles labeling --- cpp/mwmatching.h | 176 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 127 insertions(+), 49 deletions(-) diff --git a/cpp/mwmatching.h b/cpp/mwmatching.h index 1967d28..0b1af14 100644 --- a/cpp/mwmatching.h +++ b/cpp/mwmatching.h @@ -856,6 +856,70 @@ public: return std::make_tuple(NO_EDGE, 0); } + /* ********** Managing blossom labels: ********** */ + + /** + * Change an unlabeled top-level blossom into an S-blossom. + * + * For a blossom with "j" vertices and "k" incident edges, + * this function takes time O(j * log(n) + k). + * + * This function is called at most once per blossom per stage. + * It therefore takes total time O(n * log(n) + m) per stage. + */ + void assign_blossom_label_s(BlossomT* blossom) + { + assert(! blossom->parent); + assert(blossom->label == LABEL_NONE); + + blossom->label = LABEL_S; + + // Add new S-vertices to the scan queue. + for_vertices_in_blossom(blossom, + [this](VertexId x) { + scan_queue.push_back(x); + }); + } + + /** + * Change an unlabeled top-level blossom into a T-blossom. + * + * This function takes time O(log(n)). + */ + void assign_blossom_label_t(BlossomT* blossom) + { + assert(! blossom->parent); + assert(blossom->label == LABEL_NONE); + + blossom->label = LABEL_T; + } + + /** + * Change a top-level T-blossom into an unlabeled blossom. + * + * This function takes time O(log(n)). + */ + void remove_blossom_label_t(BlossomT* blossom) + { + assert(! blossom->parent); + assert(blossom->label == LABEL_T); + + blossom->label = LABEL_NONE; + } + + /** + * Change a top-level S-blossom into an S-subblossom. + * + * This function takes time O(1). + */ + void change_s_blossom_to_subblossom(BlossomT* blossom) + { + assert(! blossom->parent); + assert(blossom->label == LABEL_S); + + blossom->label = LABEL_NONE; + } + /* ********** Creating and expanding blossoms: ********** */ /** @@ -985,6 +1049,21 @@ public: assert(vertex_top_blossom[edge.second] == subblossoms[pos]); } + // Blossom must start and end with an S-blossom. + assert(subblossoms.front()->label == LABEL_S); + + for (BlossomT* sub : subblossoms) { + + // Mark vertices inside former T-blossoms as S-vertices. + if (sub->label == LABEL_T) { + remove_blossom_label_t(sub); + assign_blossom_label_s(sub); + } + + // Remove labels from sub-blossoms. + change_s_blossom_to_subblossom(sub); + } + // Create the new blossom object. nontrivial_blossom.emplace_back(subblossoms, path.edges); NonTrivialBlossomT* blossom = &nontrivial_blossom.back(); @@ -1000,20 +1079,8 @@ public: }); // Assign label S to the new blossom and link to the alternating tree. - assert(subblossoms.front()->label == LABEL_S); blossom->label = LABEL_S; blossom->tree_edge = subblossoms.front()->tree_edge; - - // Consider vertices inside former T-sub-blossoms which now - // became S-vertices; add them to the queue. - for (BlossomT* sub : subblossoms) { - if (sub->label == LABEL_T) { - for_vertices_in_blossom(sub, - [this](VertexId x) { - scan_queue.push_back(x); - }); - } - } } /** Erase the specified non-trivial blossom. */ @@ -1039,6 +1106,9 @@ public: assert(blossom->parent == nullptr); assert(blossom->label == LABEL_T); + // Remove label from blossom. + remove_blossom_label_t(blossom); + // Convert sub-blossoms into top-level blossoms. for (const auto& sub : blossom->subblossoms) { BlossomT* sub_blossom = sub.blossom; @@ -1061,7 +1131,7 @@ public: BlossomT* entry = vertex_top_blossom[blossom->tree_edge.second]; // Assign label T to that blossom and link to the alternating tree. - entry->label = LABEL_T; + assign_blossom_label_t(entry); entry->tree_edge = blossom->tree_edge; // Find the position of this sub-blossom within the expanding blossom. @@ -1079,12 +1149,12 @@ public: while (sub_it != sub_begin) { // Assign label S to the next node on the path. --sub_it; - assign_label_s(sub_it->edge.first); + extend_tree_t_to_s(sub_it->edge.first); // Assign label T to the next node on the path. assert(sub_it != sub_begin); --sub_it; - sub_it->blossom->label = LABEL_T; + assign_blossom_label_t(sub_it->blossom); sub_it->blossom->tree_edge = flip_vertex_pair(sub_it->edge); } @@ -1094,7 +1164,7 @@ public: auto sub_end = blossom->subblossoms.end(); while (sub_it != sub_end) { // Assign label S to the next node on the path. - assign_label_s(sub_it->edge.second); + extend_tree_t_to_s(sub_it->edge.second); ++sub_it; // Assign label T to the next node on the path. @@ -1106,7 +1176,7 @@ public: BlossomT *sub_blossom = (sub_it == sub_end) ? blossom->subblossoms.front().blossom : sub_it->blossom; - sub_blossom->label = LABEL_T; + assign_blossom_label_t(sub_blossom); sub_blossom->tree_edge = tree_edge; } } @@ -1306,26 +1376,25 @@ public: } } - /* ********** Labels and alternating tree: ********** */ + /* ********** Alternating tree: ********** */ /** * Assign label S to the unlabeled blossom that contains vertex "x". * - * If vertex "x" is matched, it becomes attached to the alternating tree - * via its matched edge. If vertex "x" is unmatched, it becomes the root - * of an alternating tree. + * The newly labeled S-blossom is added to the alternating tree + * via its matched edge. All vertices in the newly labeled S-blossom + * are added to the scan queue. * * All vertices in the newly labeled blossom are added to the scan queue. * - * @pre "x" is an unlabeled vertex, either unmatched or matched to - * a T-vertex via a tight edge. + * @pre "x" is an unlabeled vertex. + * @pre "x" is matched to a T-vertex via a tight edge. */ - void assign_label_s(VertexId x) + void extend_tree_t_to_s(VertexId x) { // Assign label S to the blossom that contains vertex "x". - BlossomT* bx = vertex_top_blossom[x]; - assert(bx->label == LABEL_NONE); - bx->label = LABEL_S; + BlossomT* bx = top_level_blossom(x); + assign_blossom_label_s(bx); VertexId y = vertex_mate[x]; if (y == NO_VERTEX) { @@ -1339,57 +1408,52 @@ public: } else { // Vertex "x" is matched to T-vertex "y". - assert(vertex_top_blossom[y]->label == LABEL_T); + BlossomT* by = top_level_blossom(y); + assert(by->label == LABEL_T); // Attach the blossom to the alternating tree via vertex "y". bx->tree_edge = std::make_pair(y, x); } - - // Add all vertices inside the newly labeled S-blossom to the queue. - for_vertices_in_blossom(bx, - [this](VertexId v) { - scan_queue.push_back(v); - }); } /** * Assign label T to the unlabeled blossom that contains vertex "y". * - * Attach it to the alternating tree via edge (x, y). - * Then immediately assign label S to the mate of vertex "y". + * The newly labeled T-blossom is added to the alternating tree. + * Directly afterwards, label S is assigned to the blossom that has + * a matched edge to the base of the newly labeled T-blossom. + * That newly labeled S-blossom is also added to the alternating tree. * * Note that this function may expand blossoms that contain vertex "y". * * @pre "x" is an S-vertex. * @pre "y" is an unlabeled, matched vertex. + * @pre The top-level blossom that contains "y" has a matched base vertex. * @pre There is a tight edge between vertices "x" and "y". */ - void assign_label_t(VertexId x, VertexId y) + void extend_tree_s_to_t(VertexId x, VertexId y) { - assert(vertex_top_blossom[x]->label == LABEL_S); - - BlossomT* by = vertex_top_blossom[y]; - assert(by->label == LABEL_NONE); + assert(top_level_blossom(x)->label == LABEL_S); // If "y" is part of a zero-dual blossom, expand it. // This would otherwise likely happen through a zero-delta4 step, // so we can just do it now and avoid a substage. + BlossomT* by = top_level_blossom(y); NonTrivialBlossomT* ntb = by->nontrivial(); while (ntb != nullptr && ntb->dual_var == 0) { expand_unlabeled_blossom(ntb); - by = vertex_top_blossom[y]; - assert(by->label == LABEL_NONE); + by = top_level_blossom(y); ntb = by->nontrivial(); } // Assign label T to the top-level blossom that contains vertex "y". - by->label = LABEL_T; + assign_blossom_label_t(by); by->tree_edge = std::make_pair(x, y); // Assign label S to the blossom that is mated to the T-blossom. VertexId z = vertex_mate[by->base_vertex]; assert(z != NO_VERTEX); - assign_label_s(z); + extend_tree_t_to_s(z); } /** @@ -1401,12 +1465,19 @@ public: * If the edge connects two different alternating trees, an augmenting * path has been discovered. In this case the matching is augmented. * + * @pre "x" and "y" are S-vertices in different top-level blossoms. + * @pre There is a tight edge between vertices "x" and "y". + * * @return True if the matching was augmented; otherwise false. */ bool add_s_to_s_edge(VertexId x, VertexId y) { - assert(vertex_top_blossom[x]->label == LABEL_S); - assert(vertex_top_blossom[y]->label == LABEL_S); + BlossomT* bx = top_level_blossom(x); + BlossomT* by = top_level_blossom(y); + + assert(bx->label == LABEL_S); + assert(by->label == LABEL_S); + assert(bx != by); // Trace back through the alternating trees from "x" and "y". AlternatingPath path = trace_alternating_paths(x, y); @@ -1615,7 +1686,14 @@ public: // Assign label S to all unmatched vertices and put them in the queue. for (VertexId x = 0; x < graph.num_vertex; ++x) { if (vertex_mate[x] == NO_VERTEX) { - assign_label_s(x); + + // Assign label S. + BlossomT* bx = top_level_blossom(x); + assert(bx->base_vertex == x); + assign_blossom_label_s(bx); + + // Mark blossom as the root of an alternating tree. + bx->tree_edge = std::make_pair(NO_VERTEX, x); } } @@ -1656,7 +1734,7 @@ public: if (vertex_top_blossom[x]->label != LABEL_S) { std::swap(x, y); } - assign_label_t(x, y); + extend_tree_s_to_t(x, y); } else if (delta.kind == 3) { // Use the S-to-S edge that got unlocked by the delta update.