diff --git a/python/mwmatching.py b/python/mwmatching.py index 492fc3d..37caf24 100644 --- a/python/mwmatching.py +++ b/python/mwmatching.py @@ -613,7 +613,24 @@ class _MatchingContext: blossom.vertex_set.clear() del blossom.vertex_set - def edge_slack_2x_info( + 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 ignores pending changes to vertex duals. + + For an edge between two S-vertices (in different top-level blossoms), + the true slack is the pseudo-slack minus 2 times the running sum + of delta steps. + + For an edge between an S-vertex and unlabeled vertex, + the true slack is the pseudo-slack minus the running sum of delta + steps, plus the pending offset of the top-level blossom that contains + the unlabeled vertex. + """ + (x, y, w) = self.graph.edges[e] + return self.vertex_dual_2x[x] + self.vertex_dual_2x[y] - 2 * w + + def edge_slack_2x( self, x: int, y: int, @@ -645,13 +662,6 @@ class _MatchingContext: dual_2x += by.vertex_dual_offset return dual_2x - 2 * w - def edge_slack_2x(self, e: int) -> float: - """Return 2 times the slack of the edge with index "e".""" - (x, y, w) = self.graph.edges[e] - bx = self.vertex_set_node[x].find() - by = self.vertex_set_node[y].find() - return self.edge_slack_2x_info(x, y, bx, by, w) - # # Least-slack edge tracking: # @@ -689,36 +699,26 @@ class _MatchingContext: for blossom in self.trivial_blossom + self.nontrivial_blossom: blossom.delta2_node = None - def lset_add_vertex_edge( - self, - y: int, - by: _Blossom, - e: int, - slack: float - ) -> None: + 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". This function takes time O(log(n)). """ - - # TODO -- Simplify: We don't need to know the true slack of the edge, - # only the pseudo-slack based on raw vertex duals and weight. + prio = self.edge_pseudo_slack_2x(e) best_edge = self.vertex_best_edge[y] if best_edge != -1: - best_slack = self.edge_slack_2x(best_edge) - if slack >= best_slack: + best_prio = self.edge_pseudo_slack_2x(best_edge) + if prio >= best_prio: return self.vertex_best_edge[y] = e - (p, q, w) = self.graph.edges[e] - prio = self.vertex_dual_2x[p] + self.vertex_dual_2x[q] - 2 * w prev_min = by.vertex_set.min_prio() self.vertex_set_node[y].set_prio(prio) if (by.label == _LABEL_NONE) and (prio < prev_min): - prio = slack + self.delta_sum_2x + prio += by.vertex_dual_offset if by.delta2_node is None: by.delta2_node = self.delta2_queue.insert(prio, by) elif prio < by.delta2_node.prio: @@ -1476,7 +1476,7 @@ class _MatchingContext: # Check whether this edge is tight (has zero slack). # Only tight edges may be part of an alternating tree. - slack = self.edge_slack_2x_info(x, y, bx, by, w) + slack = self.edge_slack_2x(x, y, bx, by, w) if slack <= 0: if ylabel == _LABEL_NONE: # Assign label T to the blossom that contains "y". @@ -1509,7 +1509,7 @@ class _MatchingContext: # 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, slack) + self.lset_add_vertex_edge(y, by, e) # No further S vertices to scan, and no augmenting path found. return None