Clean up least-slack edge tracking
This commit is contained in:
		
							parent
							
								
									1a98624f2b
								
							
						
					
					
						commit
						f8c6b99842
					
				|  | @ -78,7 +78,7 @@ def maximum_weight_matching( | |||
|     # of matched edges by 1. | ||||
|     # | ||||
|     # This loop runs through at most (n/2 + 1) iterations. | ||||
|     # Each iteration takes time O(n**2). | ||||
|     # Each iteration takes time O((n + m) * log(n)). | ||||
|     while ctx.run_stage(): | ||||
|         pass | ||||
| 
 | ||||
|  | @ -554,6 +554,8 @@ class _MatchingContext: | |||
| 
 | ||||
|         # "vertex_set_node[x]" represents the vertex "x" inside the | ||||
|         # union-find datastructure of its top-level blossom. | ||||
|         # | ||||
|         # Initially, each vertex belongs to its owwn trivial top-level blossom. | ||||
|         self.vertex_set_node = [b.vertex_set.insert(i, math.inf) | ||||
|                                 for (i, b) in enumerate(self.trivial_blossom)] | ||||
| 
 | ||||
|  | @ -587,7 +589,7 @@ class _MatchingContext: | |||
|         self.delta_sum_2x: float = 0 | ||||
| 
 | ||||
|         # Queue containing unlabeled top-level blossoms that have an edge to | ||||
|         # an S-blossom. The priority of a blossom is 2 times the least slack | ||||
|         # an S-blossom. The priority of a blossom is 2 times its least slack | ||||
|         # to an S blossom, plus 2 times the running sum of delta steps. | ||||
|         self.delta2_queue: PriorityQueue[_Blossom] = PriorityQueue() | ||||
| 
 | ||||
|  | @ -623,11 +625,15 @@ class _MatchingContext: | |||
|             del blossom.vertex_set | ||||
|             blossom.tree_blossoms = None | ||||
| 
 | ||||
|     # | ||||
|     # Least-slack edge tracking: | ||||
|     # | ||||
| 
 | ||||
|     def edge_pseudo_slack_2x(self, e: int) -> float: | ||||
|         """Return 2 times the pseudo-slack of the specified edge. | ||||
| 
 | ||||
|         The pseudo-slack of an edge is related to its true slack, but | ||||
|         distorted in a way that makes it invariant under delta steps. | ||||
|         adjusted in a way that makes it invariant under delta steps. | ||||
| 
 | ||||
|         If the edge connects two S-vertices in different top-level blossoms, | ||||
|         the true slack is the pseudo-slack minus 2 times the running sum | ||||
|  | @ -641,47 +647,33 @@ class _MatchingContext: | |||
|         (x, y, w) = self.graph.edges[e] | ||||
|         return self.vertex_dual_2x[x] + self.vertex_dual_2x[y] - 2 * w | ||||
| 
 | ||||
|     # | ||||
|     # Least-slack edge tracking: | ||||
|     # | ||||
|     # To calculate delta steps, the matching algorithm needs to find | ||||
|     #  - the least-slack edge between any S-vertex and an unlabeled vertex; | ||||
|     #  - the least-slack edge between any pair of top-level S-blossoms. | ||||
|     # | ||||
|     # For each unlabeled vertex and each T-vertex, we keep track of the | ||||
|     # least-slack edge to any S-vertex. Tracking for unlabeled vertices | ||||
|     # serves to provide the least-slack edge for the delta step. | ||||
|     # Tracking for T-vertices is done because such vertices can turn into | ||||
|     # unlabeled vertices if they are part of a T-blossom that gets expanded. | ||||
|     # | ||||
|     # Note: For a given vertex or blossom, the identity of the least-slack | ||||
|     # edge to any S-blossom remains unchanged during a delta step. | ||||
|     # Although the delta step changes edge slacks, it changes the slack | ||||
|     # of every edge to an S-vertex by the same amount. Therefore the edge | ||||
|     # that had least slack before the delta step, will still have least slack | ||||
|     # after the delta step. | ||||
|     # | ||||
|     def delta2_add_edge(self, e: int, y: int, by: _Blossom) -> None: | ||||
|         """Add edge "e" for delta2 tracking. | ||||
| 
 | ||||
|     # TODO -- rename function, maybe refactor | ||||
|     def lset_add_vertex_edge(self, y: int, by: _Blossom, e: int) -> None: | ||||
|         """Add edge "e" from an S-vertex to unlabeled vertex or T-vertex "y". | ||||
|         Edge "e" connects an S-vertex to a T-vertex or unlabeled vertex "y". | ||||
| 
 | ||||
|         This function takes time O(log(n)). | ||||
|         """ | ||||
| 
 | ||||
|         prio = self.edge_pseudo_slack_2x(e) | ||||
| 
 | ||||
|         improved = (self.vertex_sedge_queue[y].empty() | ||||
|                     or (self.vertex_sedge_queue[y].find_min().prio > prio)) | ||||
| 
 | ||||
|         # Insert edge in the S-edge queue of vertex "y". | ||||
|         assert self.vertex_sedge_node[e] is None | ||||
|         self.vertex_sedge_node[e] = self.vertex_sedge_queue[y].insert(prio, e) | ||||
| 
 | ||||
|         # Continue if the new edge becomes the least-slack S-edge for "y". | ||||
|         if not improved: | ||||
|             return | ||||
| 
 | ||||
|         # Update the priority of "y" in its UnionFindQueue. | ||||
|         prev_min = by.vertex_set.min_prio() | ||||
|         self.vertex_set_node[y].set_prio(prio) | ||||
| 
 | ||||
|         # If the blossom is unlabeled and the new edge becomes its least-slack | ||||
|         # S-edge, insert or update the blossom in the global delta2 queue. | ||||
|         if (by.label == _LABEL_NONE) and (prio < prev_min): | ||||
|             prio += by.vertex_dual_offset | ||||
|             if by.delta2_node is None: | ||||
|  | @ -689,20 +681,99 @@ class _MatchingContext: | |||
|             elif prio < by.delta2_node.prio: | ||||
|                 self.delta2_queue.decrease_prio(by.delta2_node, prio) | ||||
| 
 | ||||
|     # TODO -- rename function, maybe refactor | ||||
|     def lset_get_best_vertex_edge(self) -> tuple[int, float]: | ||||
|         """Return the index and slack of the least-slack edge between | ||||
|         any S-vertex and unlabeled vertex. | ||||
|     def delta2_remove_edge(self, e: int, y: int, by: _Blossom) -> None: | ||||
|         """Remove edge "e" from delta2 tracking. | ||||
| 
 | ||||
|         This function is called if an S-vertex becomes unlabeled, | ||||
|         and edge "e" connects that vertex to vertex "y" which is a T-vertex | ||||
|         or unlabeled vertex. | ||||
| 
 | ||||
|         This function takes time O(log(n)). | ||||
|         """ | ||||
|         vertex_sedge_node = self.vertex_sedge_node[e] | ||||
|         if vertex_sedge_node is not None: | ||||
|             # Delete edge from the S-edge queue of vertex "y". | ||||
|             vertex_sedge_queue = self.vertex_sedge_queue[y] | ||||
|             vertex_sedge_queue.delete(vertex_sedge_node) | ||||
|             self.vertex_sedge_node[e] = None | ||||
| 
 | ||||
|             if vertex_sedge_queue.empty(): | ||||
|                 prio = math.inf | ||||
|             else: | ||||
|                 prio = vertex_sedge_queue.find_min().prio | ||||
| 
 | ||||
|             # If necessary, update the priority of "y" in its UnionFindQueue. | ||||
|             if prio > self.vertex_set_node[y].prio: | ||||
|                 self.vertex_set_node[y].set_prio(prio) | ||||
|                 if by.label == _LABEL_NONE: | ||||
|                     # Update or delete the blossom in the global delta2 queue. | ||||
|                     assert by.delta2_node is not None | ||||
|                     prio = by.vertex_set.min_prio() | ||||
|                     if prio < math.inf: | ||||
|                         prio += by.vertex_dual_offset | ||||
|                         if prio > by.delta2_node.prio: | ||||
|                             self.delta2_queue.increase_prio( | ||||
|                                 by.delta2_node, prio) | ||||
|                     else: | ||||
|                         self.delta2_queue.delete(by.delta2_node) | ||||
|                         by.delta2_node = None | ||||
| 
 | ||||
|     def delta2_enable_blossom(self, blossom: _Blossom) -> None: | ||||
|         """Enable delta2 tracking for "blossom". | ||||
| 
 | ||||
|         This function is called when a blossom becomes an unlabeled top-level | ||||
|         blossom. If the blossom has at least one edge to an S-vertex, | ||||
|         the blossom will be inserted in the global delta2 queue. | ||||
| 
 | ||||
|         This function takes time O(log(n)). | ||||
|         """ | ||||
|         assert blossom.delta2_node is None | ||||
|         prio = blossom.vertex_set.min_prio() | ||||
|         if prio < math.inf: | ||||
|             prio += blossom.vertex_dual_offset | ||||
|             blossom.delta2_node = self.delta2_queue.insert(prio, blossom) | ||||
| 
 | ||||
|     def delta2_disable_blossom(self, blossom: _Blossom) -> None: | ||||
|         """Disable delta2 tracking for "blossom". | ||||
| 
 | ||||
|         The blossom will be removed from the global delta2 queue. | ||||
|         This function is called when a blossom stops being an unlabeled | ||||
|         top-level blossom. | ||||
| 
 | ||||
|         This function takes time O(log(n)). | ||||
|         """ | ||||
|         if blossom.delta2_node is not None: | ||||
|             self.delta2_queue.delete(blossom.delta2_node) | ||||
|             blossom.delta2_node = None | ||||
| 
 | ||||
|     def delta2_clear_vertex(self, x: int) -> None: | ||||
|         """Clear delta2 tracking for vertex "x". | ||||
| 
 | ||||
|         This function is called when "x" becomes an S-vertex. | ||||
|         It is assumed that the blossom containing "x" has already been | ||||
|         disabled for delta2 tracking. | ||||
| 
 | ||||
|         This function takes time O(k * log(n)), | ||||
|         where "k" is the number of edges incident on "x". | ||||
|         """ | ||||
|         self.vertex_sedge_queue[x].clear() | ||||
|         for e in self.graph.adjacent_edges[x]: | ||||
|             self.vertex_sedge_node[e] = None | ||||
|         self.vertex_set_node[x].set_prio(math.inf) | ||||
| 
 | ||||
|     def delta2_get_min_edge(self) -> tuple[int, float]: | ||||
|         """Find the least-slack edge between any S-vertex and any unlabeled | ||||
|         vertex. | ||||
| 
 | ||||
|         This function takes time O(log(n)). | ||||
| 
 | ||||
|         Returns: | ||||
|             Tuple (edge_index, slack_2x) if there is a least-slack edge, | ||||
|             or (-1, 0) if there is no suitable edge. | ||||
|             Tuple (edge_index, slack_2x) if there is an S-to-unlabeled edge, | ||||
|             or (-1, Inf) if there is no such edge. | ||||
|         """ | ||||
| 
 | ||||
|         if self.delta2_queue.empty(): | ||||
|             return (-1, 0) | ||||
|             return (-1, math.inf) | ||||
| 
 | ||||
|         delta2_node = self.delta2_queue.find_min() | ||||
|         blossom = delta2_node.data | ||||
|  | @ -716,6 +787,74 @@ class _MatchingContext: | |||
| 
 | ||||
|         return (e, slack_2x) | ||||
| 
 | ||||
|     def delta3_add_edge(self, e: int) -> None: | ||||
|         """Add edge "e" for delta3 tracking. | ||||
| 
 | ||||
|         This function is called if a vertex becomes an S-vertex and edge "e" | ||||
|         connects it to an S-vertex in a different top-level blossom. | ||||
| 
 | ||||
|         This function takes time O(log(n)). | ||||
|         """ | ||||
|         # The edge may already be in the delta3 queue, if it was previously | ||||
|         # discovered in the opposite direction. | ||||
|         if self.delta3_node[e] is None: | ||||
|             # Priority is edge slack plus 2 times the running sum of | ||||
|             # delta steps. | ||||
|             prio_2x = self.edge_pseudo_slack_2x(e) | ||||
|             if self.graph.integer_weights: | ||||
|                 # If all edge weights are integers, the slack of | ||||
|                 # any edge between S-vertices is also an integer. | ||||
|                 assert prio_2x % 2 == 0 | ||||
|                 prio = prio_2x // 2 | ||||
|             else: | ||||
|                 prio = prio_2x / 2 | ||||
|             self.delta3_node[e] = self.delta3_queue.insert(prio, e) | ||||
| 
 | ||||
|     def delta3_remove_edge(self, e: int) -> None: | ||||
|         """Remove edge "e" from delta3 tracking. | ||||
| 
 | ||||
|         This function is called if a former S-vertex becomes unlabeled, | ||||
|         and edge "e" connects it to another S-vertex. | ||||
| 
 | ||||
|         This function takes time O(log(n)). | ||||
|         """ | ||||
|         delta3_node = self.delta3_node[e] | ||||
|         if delta3_node is not None: | ||||
|             self.delta3_queue.delete(delta3_node) | ||||
|             self.delta3_node[e] = None | ||||
| 
 | ||||
|     def delta3_get_min_edge(self) -> tuple[int, float]: | ||||
|         """Find the least-slack edge between any pair of S-vertices in | ||||
|         different top-level blossoms. | ||||
| 
 | ||||
|         This function takes time O(1 + k * log(n)), | ||||
|         where "k" is the number of intra-blossom edges removed from the queue. | ||||
| 
 | ||||
|         Returns: | ||||
|             Tuple (edge_index, slack) if there is an S-to-S edge, | ||||
|             or (-1, Inf) if there is no suitable edge. | ||||
|         """ | ||||
|         while not self.delta3_queue.empty(): | ||||
|             delta3_node = self.delta3_queue.find_min() | ||||
|             e = delta3_node.data | ||||
|             (x, y, _w) = self.graph.edges[e] | ||||
|             bx = self.vertex_set_node[x].find() | ||||
|             by = self.vertex_set_node[y].find() | ||||
|             assert (bx.label == _LABEL_S) and (by.label == _LABEL_S) | ||||
|             if bx is not by: | ||||
|                 slack = delta3_node.prio - self.delta_sum_2x | ||||
|                 return (e, slack) | ||||
| 
 | ||||
|             # Reject edges between vertices within the same top-level blossom. | ||||
|             # Although intra-blossom edges are never inserted into the queue, | ||||
|             # existing edges in the queue may become intra-blossom when | ||||
|             # a new blossom is formed. | ||||
|             self.delta3_queue.delete(delta3_node) | ||||
|             self.delta3_node[e] = None | ||||
| 
 | ||||
|         # If the queue is empty, no suitable edge exists. | ||||
|         return (-1, math.inf) | ||||
| 
 | ||||
|     # | ||||
|     # General support routines: | ||||
|     # | ||||
|  | @ -734,10 +873,7 @@ class _MatchingContext: | |||
|         assert blossom.label == _LABEL_NONE | ||||
|         blossom.label = _LABEL_S | ||||
| 
 | ||||
|         # Delete blossom from delta2 queue. | ||||
|         if blossom.delta2_node is not None: | ||||
|             self.delta2_queue.delete(blossom.delta2_node) | ||||
|             blossom.delta2_node = None | ||||
|         self.delta2_disable_blossom(blossom) | ||||
| 
 | ||||
|         # Prepare for lazy updating of S-blossom dual variable. | ||||
|         if isinstance(blossom, _NonTrivialBlossom): | ||||
|  | @ -765,14 +901,7 @@ class _MatchingContext: | |||
|         blossom.vertex_dual_offset = 0 | ||||
|         for x in vertices: | ||||
|             self.vertex_dual_2x[x] += vertex_dual_fixup | ||||
| 
 | ||||
|             # Clean up tracking of edges from vertex "x" to S-vertices. | ||||
|             # We maintain that tracking only for unlabeled vertices and | ||||
|             # T-vertices. | ||||
|             self.vertex_sedge_queue[x].clear() | ||||
|             for e in self.graph.adjacent_edges[x]: | ||||
|                 self.vertex_sedge_node[e] = None | ||||
|             self.vertex_set_node[x].set_prio(math.inf) | ||||
|             self.delta2_clear_vertex(x) | ||||
| 
 | ||||
|     def assign_blossom_label_t(self, blossom: _Blossom) -> None: | ||||
|         """Assign label T to an unlabeled top-level blossom.""" | ||||
|  | @ -781,10 +910,7 @@ class _MatchingContext: | |||
|         assert blossom.label == _LABEL_NONE | ||||
|         blossom.label = _LABEL_T | ||||
| 
 | ||||
|         # Delete blossom from delta2 queue. | ||||
|         if blossom.delta2_node is not None: | ||||
|             self.delta2_queue.delete(blossom.delta2_node) | ||||
|             blossom.delta2_node = None | ||||
|         self.delta2_disable_blossom(blossom) | ||||
| 
 | ||||
|         if isinstance(blossom, _NonTrivialBlossom): | ||||
| 
 | ||||
|  | @ -838,47 +964,17 @@ class _MatchingContext: | |||
|             (p, q, _w) = edges[e] | ||||
|             y = p if p != x else q | ||||
| 
 | ||||
|             # If this edge was in the delta3_queue, remove it since | ||||
|             # this is no longer an edge between S-vertices. | ||||
|             delta3_node = self.delta3_node[e] | ||||
|             if delta3_node is not None: | ||||
|                 self.delta3_queue.delete(delta3_node) | ||||
|                 self.delta3_node[e] = None | ||||
|             self.delta3_remove_edge(e) | ||||
| 
 | ||||
|             by = self.vertex_set_node[y].find() | ||||
|             if by.label == _LABEL_S: | ||||
|                 # This is an edge between "x" and an S-vertex. | ||||
|                 # Add this edge to "vertex_sedge_queue[x]". | ||||
|                 # Update delta2 tracking accordingly. | ||||
|                 self.lset_add_vertex_edge(x, bx, e) | ||||
|                 self.delta2_add_edge(e, x, bx) | ||||
| 
 | ||||
|             else: | ||||
|                 # This is no longer an edge between "y" and an S-vertex. | ||||
|                 # Remove this edge from "vertex_sedge_queue[y]". | ||||
|                 # Update delta2 tracking accordingly. | ||||
|                 # TODO -- untangle this mess | ||||
|                 vertex_sedge_node = self.vertex_sedge_node[e] | ||||
|                 if vertex_sedge_node is not None: | ||||
|                     vertex_sedge_queue = self.vertex_sedge_queue[y] | ||||
|                     vertex_sedge_queue.delete(vertex_sedge_node) | ||||
|                     self.vertex_sedge_node[e] = None | ||||
|                     if vertex_sedge_queue.empty(): | ||||
|                         prio = math.inf | ||||
|                     else: | ||||
|                         prio = vertex_sedge_queue.find_min().prio | ||||
|                     if prio > self.vertex_set_node[y].prio: | ||||
|                         self.vertex_set_node[y].set_prio(prio) | ||||
|                         if by.label == _LABEL_NONE: | ||||
|                             assert by.delta2_node is not None | ||||
|                             prio = by.vertex_set.min_prio() | ||||
|                             if prio < math.inf: | ||||
|                                 prio += by.vertex_dual_offset | ||||
|                                 if prio > by.delta2_node.prio: | ||||
|                                     self.delta2_queue.increase_prio( | ||||
|                                         by.delta2_node, prio) | ||||
|                             else: | ||||
|                                 self.delta2_queue.delete(by.delta2_node) | ||||
|                                 by.delta2_node = None | ||||
|                 self.delta2_remove_edge(e, y, by) | ||||
| 
 | ||||
|     def reset_blossom_label(self, blossom: _Blossom) -> None: | ||||
|         """Remove blossom label.""" | ||||
|  | @ -907,14 +1003,7 @@ class _MatchingContext: | |||
|         elif blossom.label == _LABEL_T: | ||||
| 
 | ||||
|             self.remove_blossom_label_t(blossom) | ||||
| 
 | ||||
|             # Since the blossom is now unlabeled, insert it in delta2_queue | ||||
|             # if it has at least one edge to an S-vertex. | ||||
|             assert blossom.delta2_node is None | ||||
|             prio = blossom.vertex_set.min_prio() | ||||
|             if prio < math.inf: | ||||
|                 prio += blossom.vertex_dual_offset | ||||
|                 blossom.delta2_node = self.delta2_queue.insert(prio, blossom) | ||||
|             self.delta2_enable_blossom(blossom) | ||||
| 
 | ||||
|     def _check_alternating_tree_consistency(self) -> None: | ||||
|         """TODO -- remove this function, only for debugging""" | ||||
|  | @ -1161,12 +1250,7 @@ class _MatchingContext: | |||
|             assert sub.vertex_dual_offset == 0 | ||||
|             sub.vertex_dual_offset = vertex_dual_fixup | ||||
| 
 | ||||
|             # Insert blossom in delta2_queue if necessary. | ||||
|             prio = sub.vertex_set.min_prio() | ||||
|             if prio < math.inf: | ||||
|                 assert sub.delta2_node is None | ||||
|                 prio += sub.vertex_dual_offset | ||||
|                 sub.delta2_node = self.delta2_queue.insert(prio, sub) | ||||
|             self.delta2_enable_blossom(sub) | ||||
| 
 | ||||
|         # The expanding blossom was part of an alternating tree, linked to | ||||
|         # a parent node in the tree via one of its subblossoms, and linked to | ||||
|  | @ -1224,10 +1308,7 @@ class _MatchingContext: | |||
|         assert blossom.parent is None | ||||
|         assert blossom.label == _LABEL_NONE | ||||
| 
 | ||||
|         # Remove blossom from delta2 heap. | ||||
|         assert blossom.delta2_node is not None | ||||
|         self.delta2_queue.delete(blossom.delta2_node) | ||||
|         blossom.delta2_node = None | ||||
|         self.delta2_disable_blossom(blossom) | ||||
| 
 | ||||
|         # Split union-find structure. | ||||
|         blossom.vertex_set.split() | ||||
|  | @ -1244,12 +1325,7 @@ class _MatchingContext: | |||
|             assert sub.vertex_dual_offset == 0 | ||||
|             sub.vertex_dual_offset = vertex_dual_offset | ||||
| 
 | ||||
|             # Insert blossom in delta2_queue if necessary. | ||||
|             prio = sub.vertex_set.min_prio() | ||||
|             if prio < math.inf: | ||||
|                 assert sub.delta2_node is None | ||||
|                 prio += sub.vertex_dual_offset | ||||
|                 sub.delta2_node = self.delta2_queue.insert(prio, sub) | ||||
|             self.delta2_enable_blossom(sub) | ||||
| 
 | ||||
|         # Delete the expanded blossom. | ||||
|         self.nontrivial_blossom.remove(blossom) | ||||
|  | @ -1546,25 +1622,9 @@ class _MatchingContext: | |||
|                     continue | ||||
| 
 | ||||
|                 if by.label == _LABEL_S: | ||||
|                     # Update tracking of least-slack edges between S-blossoms. | ||||
|                     # Priority is edge slack plus 2 times the running sum of | ||||
|                     # delta steps. | ||||
|                     if self.delta3_node[e] is None: | ||||
|                         prio_2x = self.edge_pseudo_slack_2x(e) | ||||
|                         if self.graph.integer_weights: | ||||
|                             # If all edge weights are integers, the slack of | ||||
|                             # any edge between S-vertices is also an integer. | ||||
|                             assert prio_2x % 2 == 0 | ||||
|                             prio = prio_2x // 2 | ||||
|                         else: | ||||
|                             prio = prio_2x / 2 | ||||
|                         self.delta3_node[e] = self.delta3_queue.insert(prio, e) | ||||
|                     self.delta3_add_edge(e) | ||||
|                 else: | ||||
|                     # Update tracking of least-slack edges from vertex "y" to | ||||
|                     # any S-vertex. We do this for T-vertices and unlabeled | ||||
|                     # vertices. Edges which already have zero slack are still | ||||
|                     # tracked. | ||||
|                     self.lset_add_vertex_edge(y, by, e) | ||||
|                     self.delta2_add_edge(e, y, by) | ||||
| 
 | ||||
|         self.scan_queue.clear() | ||||
| 
 | ||||
|  | @ -1604,7 +1664,7 @@ class _MatchingContext: | |||
|         # Compute delta2: minimum slack of any edge between an S-vertex and | ||||
|         # an unlabeled vertex. | ||||
|         # This takes time O(log(n)). | ||||
|         (e, slack) = self.lset_get_best_vertex_edge() | ||||
|         (e, slack) = self.delta2_get_min_edge() | ||||
|         if (e != -1) and (slack <= delta_2x): | ||||
|             delta_type = 2 | ||||
|             delta_2x = slack | ||||
|  | @ -1612,31 +1672,12 @@ class _MatchingContext: | |||
| 
 | ||||
|         # Compute delta3: half minimum slack of any edge between two top-level | ||||
|         # S-blossoms. | ||||
|         # | ||||
|         # This loop iterates O(m) times per stage. | ||||
|         # Each iteration takes time O(log(n)). | ||||
|         while not self.delta3_queue.empty(): | ||||
|             delta3_node = self.delta3_queue.find_min() | ||||
|             e = delta3_node.data | ||||
|             (x, y, _w) = self.graph.edges[e] | ||||
|             bx = self.vertex_set_node[x].find() | ||||
|             by = self.vertex_set_node[y].find() | ||||
|             assert (bx.label == _LABEL_S) and (by.label == _LABEL_S) | ||||
|             if bx is not by: | ||||
|                 # Found edge between different top-level S-blossoms. | ||||
|                 slack = delta3_node.prio - self.delta_sum_2x | ||||
|                 if slack <= delta_2x: | ||||
|                     delta_type = 3 | ||||
|                     delta_2x = slack | ||||
|                     delta_edge = e | ||||
|                 break | ||||
| 
 | ||||
|             # Reject edges between vertices within the same top-level blossom. | ||||
|             # Although intra-blossom edges are never inserted into the queue, | ||||
|             # existing edges in the queue may become intra-blossom when | ||||
|             # a new blossom is formed. | ||||
|             self.delta3_queue.delete(delta3_node) | ||||
|             self.delta3_node[e] = None | ||||
|         # This takes total time O(m * log(n)) per stage. | ||||
|         (e, slack) = self.delta3_get_min_edge() | ||||
|         if (e != -1) and (slack <= delta_2x): | ||||
|             delta_type = 3 | ||||
|             delta_2x = slack | ||||
|             delta_edge = e | ||||
| 
 | ||||
|         # Compute delta4: half minimum dual variable of a top-level T-blossom. | ||||
|         # This takes time O(log(n)). | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue