diff --git a/cpp/mwmatching.h b/cpp/mwmatching.h index b29bfb6..1a74246 100644 --- a/cpp/mwmatching.h +++ b/cpp/mwmatching.h @@ -320,7 +320,8 @@ struct Blossom // TODO -- tree_blossoms - // TOOD -- vertex_queue + /** Concatenable queue containing all vertices in the blossom. */ + ConcatenableQueue vertex_queue; // TODO -- delta2_node @@ -337,13 +338,14 @@ protected: base_vertex(base_vertex), label(LABEL_NONE), is_nontrivial_blossom(is_nontrivial_blossom), + vertex_queue(this), vertex_dual_offset(0) { } public: /** Initialize a trivial (single-vertex) blossom. */ - explicit Blossom(VertexId base_vertex) - : Blossom(base_vertex, false) + explicit Blossom(VertexId x = NO_VERTEX) + : Blossom(x, false) { } /** @@ -584,18 +586,6 @@ public: // NOTE - this MUST be a list, because we delete items from it while keeping pointers to other items std::list nontrivial_blossom; - // TODO -- vertex_queue_node - -// TODO -- remove - /** - * Every vertex is contained in exactly one top-level blossom - * (possibly the trivial blossom that contains just that vertex). - * - * "vertex_top_blossom[x]" is the top-level blossom that contains - * vertex "x". - */ - std::vector vertex_top_blossom; - /** * Modified dual variable of each vertex. * @@ -627,6 +617,10 @@ public: /** Running sum of applied delta steps. */ WeightType delta_sum; + /** For each vertex, a node in its top-level blossom. */ + typedef ConcatenableQueue VertexQueue; + std::vector vertex_queue_node; + // TODO -- delta2_queue /** @@ -673,21 +667,22 @@ public: */ explicit MatchingContext(const std::vector& edges_in) : graph(edges_in), + trivial_blossom(graph.num_vertex), + vertex_queue_node(graph.num_vertex), delta3_node(edges_in.size()) { // Initially all vertices are unmatched. vertex_mate.resize(graph.num_vertex, NO_VERTEX); - // Create a trivial blossom for each vertex. - trivial_blossom.reserve(graph.num_vertex); + // Initialize a trivial blossom for each vertex. for (VertexId x = 0; x < graph.num_vertex; ++x) { - trivial_blossom.emplace_back(x); + trivial_blossom[x].base_vertex = x; } - // Initially all vertices are trivial top-level blossoms. - vertex_top_blossom.reserve(graph.num_vertex); + // Insert each vertex as the only element in its own blossom. for (VertexId x = 0; x < graph.num_vertex; ++x) { - vertex_top_blossom.push_back(&trivial_blossom[x]); + trivial_blossom[x].vertex_queue.insert( + &vertex_queue_node[x], 0, x); } // Vertex duals are initialized to half the maximum edge weight. @@ -721,8 +716,7 @@ public: */ BlossomT* top_level_blossom(VertexId x) const { - // TODO - return vertex_top_blossom[x]; + return vertex_queue_node[x].find(); } /* ********** Least slack edge tracking: ********** */ @@ -818,7 +812,7 @@ public: WeightType best_slack = 0; for (VertexId x = 0; x < graph.num_vertex; ++x) { - if (vertex_top_blossom[x]->label == LABEL_NONE) { + if (top_level_blossom(x)->label == LABEL_NONE) { const EdgeT* edge = vertex_best_edge[x]; if (edge != nullptr) { WeightType slack = edge_slack(*edge); @@ -1095,13 +1089,12 @@ public: * discovers an augmenting path. In this case it returns an alternating * path that starts and ends in an unmatched vertex. * - * This function takes time O(k) to discover a blossom, where "k" is the - * number of sub-blossoms, or time O(n) to discover an augmenting path. + * This function takes time O(k * log(n)) to discover a blossom, + * where "k" is the number of sub-blossoms, + * or time O(n * log(n)) to discover an augmenting path. */ AlternatingPath trace_alternating_paths(VertexId x, VertexId y) { - assert(vertex_top_blossom[x] != vertex_top_blossom[y]); - // Initialize a path containing only the edge (x, y). AlternatingPath path; path.edges.emplace_back(x, y); @@ -1120,7 +1113,7 @@ public: if (x != NO_VERTEX) { // Stop if we found a common ancestor. - BlossomT* bx = vertex_top_blossom[x]; + BlossomT* bx = top_level_blossom(x); if (vertex_marker[bx->base_vertex]) { first_common = bx; break; @@ -1141,7 +1134,7 @@ public: if (y != NO_VERTEX) { // Stop if we found a common ancestor. - BlossomT* by = vertex_top_blossom[y]; + BlossomT* by = top_level_blossom(y); if (vertex_marker[by->base_vertex]) { first_common = by; break; @@ -1168,11 +1161,11 @@ public: // If we found a common ancestor, trim the paths so they end there. if (first_common) { assert(first_common->label == LABEL_S); - while (vertex_top_blossom[path.edges.front().first] + while (top_level_blossom(path.edges.front().first) != first_common) { path.edges.pop_front(); } - while (vertex_top_blossom[path.edges.back().second] + while (top_level_blossom(path.edges.back().second) != first_common) { path.edges.pop_back(); } @@ -1190,7 +1183,7 @@ public: * Assign label S to the new blossom. * Relabel all T-sub-blossoms as S and add their vertices to the queue. * - * This function takes total time O(n**2) per stage. + * This function takes total time O((n + m) * log(n)) per stage. */ void make_blossom(const AlternatingPath& path) { @@ -1201,14 +1194,14 @@ public: std::vector subblossoms; subblossoms.reserve(path.edges.size()); for (VertexPair edge : path.edges) { - subblossoms.push_back(vertex_top_blossom[edge.first]); + subblossoms.push_back(top_level_blossom(edge.first)); } // Check that the path is cyclic. VertexId pos = 0; for (VertexPair edge : path.edges) { pos = (pos + 1) % subblossoms.size(); - assert(vertex_top_blossom[edge.second] == subblossoms[pos]); + assert(top_level_blossom(edge.second) == subblossoms[pos]); } // Blossom must start and end with an S-blossom. @@ -1238,10 +1231,13 @@ public: sub->parent = blossom; } - // Mark vertices as belonging to the new blossom. - for_vertices_in_blossom(blossom, [this,blossom](VertexId x) { - vertex_top_blossom[x] = blossom; - }); + // Merge concatenable queues. +// TODO -- avoid temporary array + std::vector subqueues; + for (BlossomT* sub : subblossoms) { + subqueues.push_back(&sub->vertex_queue); + } + blossom->vertex_queue.merge(subqueues.begin(), subqueues.end()); // Assign label S to the new blossom and link to the alternating tree. blossom->label = LABEL_S; @@ -1271,6 +1267,10 @@ public: assert(blossom->parent == nullptr); assert(blossom->label == LABEL_NONE); + // Split concatenable queue, thus reconstructing the separate + // concatenable queues of the sub-blossoms. + blossom->vertex_queue.split(); + // Prepare to push pending delta updates down to the sub-blossoms. WeightType vertex_dual_offset = blossom->vertex_dual_offset; blossom->vertex_dual_offset = 0; @@ -1281,10 +1281,6 @@ public: assert(sub_blossom->parent == blossom); assert(sub_blossom->label == LABEL_NONE); sub_blossom->parent = nullptr; - for_vertices_in_blossom(sub_blossom, - [this,sub_blossom](VertexId x) { - vertex_top_blossom[x] = sub_blossom; - }); // Push pending delta updates to sub-blossom. assert(sub_blossom->vertex_dual_offset == 0); @@ -1326,7 +1322,7 @@ public: // Find the sub-blossom that was attached to the parent node // in the alternating tree. - BlossomT* entry = vertex_top_blossom[blossom->tree_edge.second]; + BlossomT* entry = top_level_blossom(blossom->tree_edge.second); // Assign label T to that blossom and link to the alternating tree. assign_blossom_label_t(entry); @@ -1469,8 +1465,6 @@ public: * from sub-blossom "entry" to the base vertex of the blossom. * * Recursively handle sub-blossoms as needed. - * - * This function takes time O(n). */ void augment_blossom(NonTrivialBlossomT* blossom, BlossomT* entry) { @@ -1505,14 +1499,18 @@ public: /** * Augment the matching through the specified augmenting path. * - * This function takes time O(n). + * This function takes time O(n * log(n)). */ void augment_matching(const AlternatingPath& path) { // Check that the path starts and ends in an unmatched blossom. assert(path.edges.size() % 2 == 1); - assert(vertex_mate[vertex_top_blossom[path.edges.front().first]->base_vertex] == NO_VERTEX); - assert(vertex_mate[vertex_top_blossom[path.edges.back().second]->base_vertex] == NO_VERTEX); + assert(vertex_mate[ + top_level_blossom(path.edges.front().first)->base_vertex] + == NO_VERTEX); + assert(vertex_mate[ + top_level_blossom(path.edges.back().second)->base_vertex] + == NO_VERTEX); // Process the unmatched edges on the augmenting path. auto edge_it = path.edges.begin(); @@ -1522,13 +1520,13 @@ public: VertexId y = edge_it->second; // Augment any non-trivial blossoms that touch this edge. - BlossomT* bx = vertex_top_blossom[x]; + BlossomT* bx = top_level_blossom(x); NonTrivialBlossomT* bx_ntb = bx->nontrivial(); if (bx_ntb != nullptr) { augment_blossom(bx_ntb, &trivial_blossom[x]); } - BlossomT* by = vertex_top_blossom[y]; + BlossomT* by = top_level_blossom(y); NonTrivialBlossomT* by_ntb = by->nontrivial(); if (by_ntb != nullptr) { augment_blossom(by_ntb, &trivial_blossom[y]); @@ -1557,8 +1555,6 @@ public: * 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. * @pre "x" is matched to a T-vertex via a tight edge. */ @@ -1658,9 +1654,10 @@ public: // Otherwise the path is an augmenting path. // Note that an alternating starts and ends in the same blossom, // but not necessarily in the same vertex within that blossom. +// TODO -- directly check whether both blossoms are in the same tree VertexId p = path.edges.front().first; VertexId q = path.edges.back().second; - if (vertex_top_blossom[p] == vertex_top_blossom[q]) { + if (top_level_blossom(p) == top_level_blossom(q)) { make_blossom(path); return false; } else { @@ -1873,7 +1870,7 @@ public: // unlocked through the delta update. VertexId x = delta.edge.first; VertexId y = delta.edge.second; - if (vertex_top_blossom[x]->label != LABEL_S) { + if (top_level_blossom(x)->label != LABEL_S) { std::swap(x, y); } extend_tree_s_to_t(x, y);