Keep alternating trees between stages in C++
This commit is contained in:
		
							parent
							
								
									082397ef80
								
							
						
					
					
						commit
						3815335a9f
					
				
							
								
								
									
										238
									
								
								cpp/mwmatching.h
								
								
								
								
							
							
						
						
									
										238
									
								
								cpp/mwmatching.h
								
								
								
								
							|  | @ -319,7 +319,8 @@ struct Blossom | |||
|     /** Optional edge that attaches this blossom to the alternating tree. */ | ||||
|     VertexPair tree_edge; | ||||
| 
 | ||||
|     // TODO -- tree_blossoms
 | ||||
|     /** Base vertex of the blossom at the root of the alternating tree. */ | ||||
|     VertexId tree_root; | ||||
| 
 | ||||
|     /** Concatenable queue containing all vertices in the blossom. */ | ||||
|     ConcatenableQueue<WeightType, Blossom*, EdgeId> vertex_queue; | ||||
|  | @ -340,6 +341,7 @@ protected: | |||
|         base_vertex(base_vertex), | ||||
|         label(LABEL_NONE), | ||||
|         is_nontrivial_blossom(is_nontrivial_blossom), | ||||
|         tree_root(NO_VERTEX), | ||||
|         vertex_queue(this), | ||||
|         vertex_dual_offset(0) | ||||
|     { } | ||||
|  | @ -783,6 +785,46 @@ public: | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Remove edge "e" from delta2 tracking. | ||||
|      * | ||||
|      * Edge "e" connects vertices "x" and "y". | ||||
|      * Vertex "x" was an S-vertex but now becomes unlabeled. | ||||
|      * Vertex "y" is a T-vertex or unlabeled vertex. | ||||
|      * | ||||
|      * This function takes time O(log(n)). | ||||
|      */ | ||||
|     void delta2_remove_edge(int e, int y, BlossomT* by) | ||||
|     { | ||||
|         if (vertex_sedge_node[e].valid()) { | ||||
|             // Delete edge from the S-edge queue.
 | ||||
|             vertex_sedge_queue[y].remove(&vertex_sedge_node[e]); | ||||
| 
 | ||||
|             // If necessary, update priority of "y" in its ConcatenableQueue.
 | ||||
|             WeightType prio = INVALID_SLACK; | ||||
|             if (! vertex_sedge_queue[y].empty()) { | ||||
|                 prio = vertex_sedge_queue[y].min_prio(); | ||||
|             } | ||||
| 
 | ||||
|             if (prio > vertex_queue_node[y].prio()) { | ||||
|                 vertex_queue_node[y].set_prio(prio); | ||||
|                 if (by->label == LABEL_NONE) { | ||||
|                     // Update or delete blossom in the global delta2 queue.
 | ||||
|                     assert(by->delta2_node.valid()); | ||||
|                     prio = by->vertex_queue.min_prio(); | ||||
|                     if (prio < INVALID_SLACK) { | ||||
|                         prio += by->vertex_dual_offset; | ||||
|                         if (prio > by->delta2_node.prio()) { | ||||
|                             delta2_queue.set_prio(&by->delta2_node, prio); | ||||
|                         } | ||||
|                     } else { | ||||
|                         delta2_queue.remove(&by->delta2_node); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Enable delta2 tracking for "blossom". | ||||
|      * | ||||
|  | @ -1047,12 +1089,36 @@ public: | |||
|             ntb->dual_var += 2 * delta_sum; | ||||
|         } | ||||
| 
 | ||||
|         // Adjust modified vertex duals to preserve true vertex dual.
 | ||||
|         // Loop over vertices in the blossom.
 | ||||
|         assert(blossom->vertex_dual_offset == 0); | ||||
|         WeightType dual_fixup = -delta_sum; | ||||
|         for_vertices_in_blossom(blossom, | ||||
|             [this,dual_fixup](VertexId x) { | ||||
|             [this,blossom,dual_fixup](VertexId x) { | ||||
| 
 | ||||
|                 // Adjust modified vertex dual to preserve true vertex dual.
 | ||||
|                 vertex_dual[x] += dual_fixup; | ||||
| 
 | ||||
|                 // Scan incident edges.
 | ||||
|                 for (EdgeId e : graph.adjacent_edges[x]) { | ||||
|                     const EdgeT& edge = graph.edges[e]; | ||||
|                     VertexId y = (edge.vt.first != x) ? edge.vt.first | ||||
|                                                       : edge.vt.second; | ||||
| 
 | ||||
|                     // If this edge is in the delta3 queue, remove it.
 | ||||
|                     delta3_remove_edge(e); | ||||
| 
 | ||||
|                     BlossomT* by = top_level_blossom(y); | ||||
|                     if (by->label == LABEL_S) { | ||||
|                         // Edge "e" connects unlabeled vertex "x" to
 | ||||
|                         // S-vertex "y". It must be tracked for delta2.
 | ||||
|                         delta2_add_edge(e, x, blossom); | ||||
|                     } else { | ||||
|                         // Edge "e" connects former S-vertex "x" to
 | ||||
|                         // T-vertex or unlabeled vertex "y". This edge was
 | ||||
|                         // tracked for delta2 but must now be removed.
 | ||||
|                         delta2_remove_edge(e, y, by); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1117,6 +1183,33 @@ public: | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Remove the alternating trees with specified root vertices. | ||||
|      * | ||||
|      * All blossoms that are part of these trees become unlabeled. | ||||
|      * Delta tracking is updated accordingly. | ||||
|      * | ||||
|      * This function takes time O((n + m) * log(n)). | ||||
|      */ | ||||
|     void remove_alternating_tree(VertexId x, VertexId y) | ||||
|     { | ||||
|         auto do_blossom = [this,x,y](BlossomT* blossom) { | ||||
|             if (blossom->label != LABEL_NONE) { | ||||
|                 assert(! blossom->parent); | ||||
|                 if ((blossom->tree_root == x) || (blossom->tree_root == y)) { | ||||
|                     reset_blossom_label(blossom); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         for (BlossomT& blossom : trivial_blossom) { | ||||
|             do_blossom(&blossom); | ||||
|         } | ||||
|         for (BlossomT& blossom : nontrivial_blossom) { | ||||
|             do_blossom(&blossom); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* **********  Creating and expanding blossoms:  ********** */ | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -1287,6 +1380,7 @@ public: | |||
|         // Assign label S to the new blossom and link to the alternating tree.
 | ||||
|         blossom->label = LABEL_S; | ||||
|         blossom->tree_edge = subblossoms.front()->tree_edge; | ||||
|         blossom->tree_root = subblossoms.front()->tree_root; | ||||
|     } | ||||
| 
 | ||||
|     /** Erase the specified non-trivial blossom. */ | ||||
|  | @ -1359,6 +1453,7 @@ public: | |||
|         // Assign label T to that blossom and link to the alternating tree.
 | ||||
|         assign_blossom_label_t(entry); | ||||
|         entry->tree_edge = blossom->tree_edge; | ||||
|         entry->tree_root = blossom->tree_root; | ||||
| 
 | ||||
|         // Find the position of this sub-blossom within the expanding blossom.
 | ||||
|         auto subblossom_loc = blossom->find_subblossom(entry); | ||||
|  | @ -1380,8 +1475,10 @@ public: | |||
|                 // Assign label T to the next node on the path.
 | ||||
|                 assert(sub_it != sub_begin); | ||||
|                 --sub_it; | ||||
|                 assign_blossom_label_t(sub_it->blossom); | ||||
|                 sub_it->blossom->tree_edge = flip_vertex_pair(sub_it->edge); | ||||
|                 BlossomT* sub_blossom = sub_it->blossom; | ||||
|                 assign_blossom_label_t(sub_blossom); | ||||
|                 sub_blossom->tree_edge = flip_vertex_pair(sub_it->edge); | ||||
|                 sub_blossom->tree_root = blossom->tree_root; | ||||
|             } | ||||
| 
 | ||||
|         } else { | ||||
|  | @ -1404,6 +1501,7 @@ public: | |||
|                     sub_it->blossom; | ||||
|                 assign_blossom_label_t(sub_blossom); | ||||
|                 sub_blossom->tree_edge = tree_edge; | ||||
|                 sub_blossom->tree_root = blossom->tree_root; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -1596,24 +1694,16 @@ public: | |||
|         BlossomT* bx = top_level_blossom(x); | ||||
|         assign_blossom_label_s(bx); | ||||
| 
 | ||||
|         // Vertex "x" is matched to T-vertex "y".
 | ||||
|         VertexId y = vertex_mate[x]; | ||||
|         if (y == NO_VERTEX) { | ||||
|             // Vertex "x" is unmatched.
 | ||||
|             // It must be either a top-level vertex or the base vertex of
 | ||||
|             // a top-level blossom.
 | ||||
|             assert(bx->base_vertex == x); | ||||
|         assert(y != NO_VERTEX); | ||||
| 
 | ||||
|             // Mark the blossom as root of an alternating tree.
 | ||||
|             bx->tree_edge = std::make_pair(NO_VERTEX, x); | ||||
|         BlossomT* by = top_level_blossom(y); | ||||
|         assert(by->label == LABEL_T); | ||||
| 
 | ||||
|         } else { | ||||
|             // Vertex "x" is matched to T-vertex "y".
 | ||||
|             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); | ||||
|         } | ||||
|         // Attach the blossom to the alternating tree via vertex "y".
 | ||||
|         bx->tree_edge = std::make_pair(y, x); | ||||
|         bx->tree_root = by->tree_root; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -1633,12 +1723,13 @@ public: | |||
|      */ | ||||
|     void extend_tree_s_to_t(VertexId x, VertexId y) | ||||
|     { | ||||
|         assert(top_level_blossom(x)->label == LABEL_S); | ||||
|         BlossomT* bx = top_level_blossom(x); | ||||
|         BlossomT* by = top_level_blossom(y); | ||||
|         assert(bx->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); | ||||
|  | @ -1649,6 +1740,7 @@ public: | |||
|         // Assign label T to the top-level blossom that contains vertex "y".
 | ||||
|         assign_blossom_label_t(by); | ||||
|         by->tree_edge = std::make_pair(x, y); | ||||
|         by->tree_root = bx->tree_root; | ||||
| 
 | ||||
|         // Assign label S to the blossom that is mated to the T-blossom.
 | ||||
|         VertexId z = vertex_mate[by->base_vertex]; | ||||
|  | @ -1682,17 +1774,22 @@ public: | |||
|         // Trace back through the alternating trees from "x" and "y".
 | ||||
|         AlternatingPath path = trace_alternating_paths(x, y); | ||||
| 
 | ||||
|         // If the path is a cycle, create a new blossom.
 | ||||
|         // 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 (top_level_blossom(p) == top_level_blossom(q)) { | ||||
|         if (bx->tree_root == by->tree_root) { | ||||
|             // Both blossoms belong to the same alternating tree.
 | ||||
|             // This implies that the alternating path is a cycle.
 | ||||
|             // Use it to create a new blossom.
 | ||||
|             make_blossom(path); | ||||
|             return false; | ||||
| 
 | ||||
|         } else { | ||||
|             // The blossoms belong to different alternating trees.
 | ||||
|             // This implies that the alternating path is an augmenting path.
 | ||||
| 
 | ||||
|             // Remove labels from all blossoms in the two alternating trees
 | ||||
|             // on the augmenting path.
 | ||||
|             remove_alternating_tree(bx->tree_root, by->tree_root); | ||||
| 
 | ||||
|             // Augment the matching along the alternating path.
 | ||||
|             augment_matching(path); | ||||
|             return true; | ||||
|         } | ||||
|  | @ -1824,34 +1921,26 @@ public: | |||
|     /* **********  Main algorithm:  ********** */ | ||||
| 
 | ||||
|     /**
 | ||||
|      * Reset data which are only valid during a stage. | ||||
|      * Assign label S to all vertices and create a separate alternating | ||||
|      * tree rooted in each vertex. | ||||
|      * | ||||
|      * Mark all blossoms as unlabeled, clear the queue, | ||||
|      * reset least-slack edge tracking. | ||||
|      * This function takes time O(n + m). | ||||
|      * It is called once, at the beginning of the algorithm. | ||||
|      */ | ||||
|     void reset_stage() | ||||
|     void start() | ||||
|     { | ||||
|         assert(scan_queue.empty()); | ||||
| 
 | ||||
|         // Remove blossom labels.
 | ||||
|         for (BlossomT& blossom : trivial_blossom) { | ||||
|             reset_blossom_label(&blossom); | ||||
|         } | ||||
|         for (BlossomT& blossom : nontrivial_blossom) { | ||||
|             reset_blossom_label(&blossom); | ||||
|         } | ||||
| 
 | ||||
|         // Reset least-slack edge tracking.
 | ||||
|         for (BlossomT& blossom : trivial_blossom) { | ||||
|             delta2_disable_blossom(&blossom); | ||||
|         } | ||||
|         for (BlossomT& blossom : nontrivial_blossom) { | ||||
|             delta2_disable_blossom(&blossom); | ||||
|         } | ||||
|         for (VertexId x = 0; x < graph.num_vertex; ++x) { | ||||
|             delta2_clear_vertex(x); | ||||
|             assert(vertex_mate[x] == NO_VERTEX); | ||||
| 
 | ||||
|             // 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); | ||||
|             bx->tree_root = x; | ||||
|         } | ||||
|         delta3_queue.clear(); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -1869,27 +1958,6 @@ public: | |||
|      */ | ||||
|     bool run_stage() | ||||
|     { | ||||
|         // 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.
 | ||||
|                 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); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Stop if all vertices are matched.
 | ||||
|         // No further improvement is possible in that case.
 | ||||
|         // This avoids messy calculations of delta steps without any S-vertex.
 | ||||
|         if (scan_queue.empty()) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         // Each pass through the following loop is a "substage".
 | ||||
|         // The substage tries to find an augmenting path.
 | ||||
|         // If an augmenting path is found, we augment the matching and end
 | ||||
|  | @ -1897,7 +1965,6 @@ public: | |||
|         // next substage, or stop if no further improvement is possible.
 | ||||
|         //
 | ||||
|         // This loop runs through at most O(n) iterations per stage.
 | ||||
|         bool augmented = false; | ||||
|         while (true) { | ||||
| 
 | ||||
|             // Consider the incident edges of newly labeled S-vertices.
 | ||||
|  | @ -1925,9 +1992,9 @@ public: | |||
|                 // This may reveal an augmenting path.
 | ||||
|                 VertexId x = delta.edge.first; | ||||
|                 VertexId y = delta.edge.second; | ||||
|                 augmented = add_s_to_s_edge(x, y); | ||||
|                 bool augmented = add_s_to_s_edge(x, y); | ||||
|                 if (augmented) { | ||||
|                     break; | ||||
|                     return true; | ||||
|                 } | ||||
| 
 | ||||
|             } else if (delta.kind == 4) { | ||||
|  | @ -1939,15 +2006,9 @@ public: | |||
|             } else { | ||||
|                 // No further improvement possible. End the stage.
 | ||||
|                 assert(delta.kind == 1); | ||||
|                 break; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Remove all labels, clear queue.
 | ||||
|         reset_stage(); | ||||
| 
 | ||||
|         // Return True if the matching was augmented.
 | ||||
|         return augmented; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -1961,7 +2022,11 @@ public: | |||
|         assert(scan_queue.empty()); | ||||
| 
 | ||||
|         auto cleanup_blossom = [this](BlossomT* blossom) { | ||||
|             assert(blossom->label == LABEL_NONE); | ||||
| 
 | ||||
|             // Remove blossom label.
 | ||||
|             if ((! blossom->parent) && (blossom->label != LABEL_NONE)) { | ||||
|                 reset_blossom_label(blossom); | ||||
|             } | ||||
| 
 | ||||
|             // Unwind lazy delta updates to vertex dual variables.
 | ||||
|             if (blossom->vertex_dual_offset != 0) { | ||||
|  | @ -1989,6 +2054,9 @@ public: | |||
|     /** Run the matching algorithm. */ | ||||
|     void run() | ||||
|     { | ||||
|         // Assign label S to all vertices.
 | ||||
|         start(); | ||||
| 
 | ||||
|         // Improve the solution until no further improvement is possible.
 | ||||
|         //
 | ||||
|         // Each successful pass through this loop increases the number
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue