Separate function top_level_blossom()
This commit is contained in:
parent
e8490010d6
commit
c731c32473
|
@ -393,8 +393,8 @@ class Blossom:
|
|||
|
||||
# Each top-level blossom maintains a concatenable queue containing
|
||||
# all vertices in the blossom.
|
||||
self.vertex_set: ConcatenableQueue[Blossom, int]
|
||||
self.vertex_set = ConcatenableQueue(self)
|
||||
self.vertex_queue: ConcatenableQueue[Blossom, int]
|
||||
self.vertex_queue = ConcatenableQueue(self)
|
||||
|
||||
# If this is a top-level unlabeled blossom with an edge to an
|
||||
# S-blossom, "delta2_node" is the corresponding node in the delta2
|
||||
|
@ -554,11 +554,12 @@ class MatchingContext:
|
|||
# Initially there are no non-trivial blossoms.
|
||||
self.nontrivial_blossom: set[NonTrivialBlossom] = set()
|
||||
|
||||
# "vertex_set_node[x]" represents the vertex "x" inside the
|
||||
# "vertex_queue_node[x]" represents the vertex "x" inside the
|
||||
# concatenable queue of its top-level blossom.
|
||||
#
|
||||
# Initially, each vertex belongs to its own trivial top-level blossom.
|
||||
self.vertex_set_node = [b.vertex_set.insert(i, math.inf)
|
||||
self.vertex_queue_node = [
|
||||
b.vertex_queue.insert(i, math.inf)
|
||||
for (i, b) in enumerate(self.trivial_blossom)]
|
||||
|
||||
# All vertex duals are initialized to half the maximum edge weight.
|
||||
|
@ -623,8 +624,19 @@ class MatchingContext:
|
|||
for blossom in itertools.chain(self.trivial_blossom,
|
||||
self.nontrivial_blossom):
|
||||
blossom.parent = None
|
||||
blossom.vertex_set.clear()
|
||||
del blossom.vertex_set
|
||||
blossom.vertex_queue.clear()
|
||||
del blossom.vertex_queue
|
||||
|
||||
#
|
||||
# Find top-level blossom:
|
||||
#
|
||||
|
||||
def top_level_blossom(self, x: int) -> Blossom:
|
||||
"""Find the top-level blossom that contains vertex "x".
|
||||
|
||||
This function takes time O(log(n)).
|
||||
"""
|
||||
return self.vertex_queue_node[x].find()
|
||||
|
||||
#
|
||||
# Least-slack edge tracking:
|
||||
|
@ -670,7 +682,7 @@ class MatchingContext:
|
|||
return
|
||||
|
||||
# Update the priority of "y" in its ConcatenableQueue.
|
||||
self.vertex_set_node[y].set_prio(prio)
|
||||
self.vertex_queue_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.
|
||||
|
@ -703,12 +715,12 @@ class MatchingContext:
|
|||
prio = vertex_sedge_queue.find_min().prio
|
||||
|
||||
# If necessary, update priority of "y" in its ConcatenableQueue.
|
||||
if prio > self.vertex_set_node[y].prio:
|
||||
self.vertex_set_node[y].set_prio(prio)
|
||||
if prio > self.vertex_queue_node[y].prio:
|
||||
self.vertex_queue_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()
|
||||
prio = by.vertex_queue.min_prio()
|
||||
if prio < math.inf:
|
||||
prio += by.vertex_dual_offset
|
||||
if prio > by.delta2_node.prio:
|
||||
|
@ -728,7 +740,7 @@ class MatchingContext:
|
|||
This function takes time O(log(n)).
|
||||
"""
|
||||
assert blossom.delta2_node is None
|
||||
prio = blossom.vertex_set.min_prio()
|
||||
prio = blossom.vertex_queue.min_prio()
|
||||
if prio < math.inf:
|
||||
prio += blossom.vertex_dual_offset
|
||||
blossom.delta2_node = self.delta2_queue.insert(prio, blossom)
|
||||
|
@ -759,7 +771,7 @@ class MatchingContext:
|
|||
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.vertex_queue_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
|
||||
|
@ -782,7 +794,7 @@ class MatchingContext:
|
|||
assert blossom.parent is None
|
||||
assert blossom.label == LABEL_NONE
|
||||
|
||||
x = blossom.vertex_set.min_elem()
|
||||
x = blossom.vertex_queue.min_elem()
|
||||
e = self.vertex_sedge_queue[x].find_min().data
|
||||
|
||||
return (e, slack_2x)
|
||||
|
@ -838,8 +850,8 @@ class MatchingContext:
|
|||
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()
|
||||
bx = self.top_level_blossom(x)
|
||||
by = self.top_level_blossom(y)
|
||||
assert (bx.label == LABEL_S) and (by.label == LABEL_S)
|
||||
if bx is not by:
|
||||
slack = delta3_node.prio - self.delta_sum_2x
|
||||
|
@ -1001,7 +1013,7 @@ class MatchingContext:
|
|||
# and vertex "x" is no longer an S-vertex.
|
||||
self.delta3_remove_edge(e)
|
||||
|
||||
by = self.vertex_set_node[y].find()
|
||||
by = self.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 via vertex "x".
|
||||
|
@ -1121,7 +1133,7 @@ class MatchingContext:
|
|||
while x != -1 or y != -1:
|
||||
|
||||
# Check if we found a common ancestor.
|
||||
bx = self.vertex_set_node[x].find()
|
||||
bx = self.top_level_blossom(x)
|
||||
if bx.marker:
|
||||
first_common = bx
|
||||
break
|
||||
|
@ -1149,8 +1161,8 @@ class MatchingContext:
|
|||
|
||||
# If we found a common ancestor, trim the paths so they end there.
|
||||
if first_common is not None:
|
||||
assert self.vertex_set_node[xedges[-1][0]].find() is first_common
|
||||
while (self.vertex_set_node[yedges[-1][0]].find()
|
||||
assert self.top_level_blossom(xedges[-1][0]) is first_common
|
||||
while (self.top_level_blossom(yedges[-1][0])
|
||||
is not first_common):
|
||||
yedges.pop()
|
||||
|
||||
|
@ -1187,12 +1199,12 @@ class MatchingContext:
|
|||
assert len(path.edges) >= 3
|
||||
|
||||
# Construct the list of sub-blossoms (current top-level blossoms).
|
||||
subblossoms = [self.vertex_set_node[x].find() for (x, y) in path.edges]
|
||||
subblossoms = [self.top_level_blossom(x) for (x, y) in path.edges]
|
||||
|
||||
# Check that the path is cyclic.
|
||||
# Note the path will not always start and end with the same _vertex_,
|
||||
# but it must start and end in the same _blossom_.
|
||||
subblossoms_next = [self.vertex_set_node[y].find()
|
||||
subblossoms_next = [self.top_level_blossom(y)
|
||||
for (x, y) in path.edges]
|
||||
assert subblossoms[0] == subblossoms_next[-1]
|
||||
assert subblossoms[1:] == subblossoms_next[:-1]
|
||||
|
@ -1237,7 +1249,7 @@ class MatchingContext:
|
|||
tree_blossoms.remove(sub)
|
||||
|
||||
# Merge concatenable queues.
|
||||
blossom.vertex_set.merge([sub.vertex_set for sub in subblossoms])
|
||||
blossom.vertex_queue.merge([sub.vertex_queue for sub in subblossoms])
|
||||
|
||||
@staticmethod
|
||||
def find_path_through_blossom(
|
||||
|
@ -1282,7 +1294,7 @@ class MatchingContext:
|
|||
self.delta2_disable_blossom(blossom)
|
||||
|
||||
# Split concatenable queue.
|
||||
blossom.vertex_set.split()
|
||||
blossom.vertex_queue.split()
|
||||
|
||||
# Prepare to push lazy delta updates down to the sub-blossoms.
|
||||
vertex_dual_offset = blossom.vertex_dual_offset
|
||||
|
@ -1299,7 +1311,7 @@ class MatchingContext:
|
|||
self.delta2_enable_blossom(sub)
|
||||
|
||||
# Avoid leaking a reference cycle.
|
||||
del blossom.vertex_set
|
||||
del blossom.vertex_queue
|
||||
|
||||
# Delete the expanded blossom.
|
||||
self.nontrivial_blossom.remove(blossom)
|
||||
|
@ -1335,7 +1347,7 @@ class MatchingContext:
|
|||
# the alternating tree.
|
||||
assert blossom.tree_edge is not None
|
||||
(x, y) = blossom.tree_edge
|
||||
sub = self.vertex_set_node[y].find()
|
||||
sub = self.top_level_blossom(y)
|
||||
|
||||
# Assign label T to that sub-blossom.
|
||||
self.assign_blossom_label_t(sub)
|
||||
|
@ -1480,7 +1492,7 @@ class MatchingContext:
|
|||
# an unmatched vertex or a blossom with unmatched base.
|
||||
assert len(path.edges) % 2 == 1
|
||||
for x in (path.edges[0][0], path.edges[-1][1]):
|
||||
b = self.vertex_set_node[x].find()
|
||||
b = self.top_level_blossom(x)
|
||||
assert self.vertex_mate[b.base_vertex] == -1
|
||||
|
||||
# The augmenting path looks like this:
|
||||
|
@ -1498,11 +1510,11 @@ class MatchingContext:
|
|||
|
||||
# Augment the non-trivial blossoms on either side of this edge.
|
||||
# No action is necessary for trivial blossoms.
|
||||
bx = self.vertex_set_node[x].find()
|
||||
bx = self.top_level_blossom(x)
|
||||
if isinstance(bx, NonTrivialBlossom):
|
||||
self.augment_blossom(bx, self.trivial_blossom[x])
|
||||
|
||||
by = self.vertex_set_node[y].find()
|
||||
by = self.top_level_blossom(y)
|
||||
if isinstance(by, NonTrivialBlossom):
|
||||
self.augment_blossom(by, self.trivial_blossom[y])
|
||||
|
||||
|
@ -1527,14 +1539,14 @@ class MatchingContext:
|
|||
"""
|
||||
|
||||
# Assign label S to the blossom that contains vertex "x".
|
||||
bx = self.vertex_set_node[x].find()
|
||||
bx = self.top_level_blossom(x)
|
||||
self.assign_blossom_label_s(bx)
|
||||
|
||||
# Vertex "x" is matched to T-vertex "y".
|
||||
y = self.vertex_mate[x]
|
||||
assert y != -1
|
||||
|
||||
by = self.vertex_set_node[y].find()
|
||||
by = self.top_level_blossom(y)
|
||||
assert by.label == LABEL_T
|
||||
assert by.tree_blossoms is not None
|
||||
|
||||
|
@ -1557,14 +1569,14 @@ class MatchingContext:
|
|||
- There is a tight edge between vertices "x" and "y".
|
||||
"""
|
||||
|
||||
bx = self.vertex_set_node[x].find()
|
||||
by = self.vertex_set_node[y].find()
|
||||
bx = self.top_level_blossom(x)
|
||||
by = self.top_level_blossom(y)
|
||||
assert bx.label == LABEL_S
|
||||
|
||||
# Expand zero-dual blossoms before assigning label T.
|
||||
while isinstance(by, NonTrivialBlossom) and (by.dual_var == 0):
|
||||
self.expand_unlabeled_blossom(by)
|
||||
by = self.vertex_set_node[y].find()
|
||||
by = self.top_level_blossom(y)
|
||||
|
||||
# Assign label T to the unlabeled blossom.
|
||||
self.assign_blossom_label_t(by)
|
||||
|
@ -1598,8 +1610,8 @@ class MatchingContext:
|
|||
True if the matching was augmented; otherwise False.
|
||||
"""
|
||||
|
||||
bx = self.vertex_set_node[x].find()
|
||||
by = self.vertex_set_node[y].find()
|
||||
bx = self.top_level_blossom(x)
|
||||
by = self.top_level_blossom(y)
|
||||
|
||||
assert bx.label == LABEL_S
|
||||
assert by.label == LABEL_S
|
||||
|
@ -1663,7 +1675,7 @@ class MatchingContext:
|
|||
for x in self.scan_queue:
|
||||
|
||||
# Double-check that "x" is an S-vertex.
|
||||
bx = self.vertex_set_node[x].find()
|
||||
bx = self.top_level_blossom(x)
|
||||
assert bx.label == LABEL_S
|
||||
|
||||
# Scan the edges that are incident on "x".
|
||||
|
@ -1683,7 +1695,7 @@ class MatchingContext:
|
|||
# Try to pull this edge into an alternating tree.
|
||||
|
||||
# Ignore edges that are internal to a blossom.
|
||||
by = self.vertex_set_node[y].find()
|
||||
by = self.top_level_blossom(y)
|
||||
if bx is by:
|
||||
continue
|
||||
|
||||
|
@ -1778,7 +1790,7 @@ class MatchingContext:
|
|||
"""
|
||||
for x in range(self.graph.num_vertex):
|
||||
assert self.vertex_mate[x] == -1
|
||||
bx = self.vertex_set_node[x].find()
|
||||
bx = self.top_level_blossom(x)
|
||||
assert bx.base_vertex == x
|
||||
|
||||
# Assign label S.
|
||||
|
@ -1829,7 +1841,7 @@ class MatchingContext:
|
|||
# Use the edge from S-vertex to unlabeled vertex that got
|
||||
# unlocked through the delta update.
|
||||
(x, y, _w) = self.graph.edges[delta_edge]
|
||||
if self.vertex_set_node[x].find().label != LABEL_S:
|
||||
if self.top_level_blossom(x).label != LABEL_S:
|
||||
(x, y) = (y, x)
|
||||
self.extend_tree_s_to_t(x, y)
|
||||
|
||||
|
|
Loading…
Reference in New Issue