1
0
Fork 0

Separate function top_level_blossom()

This commit is contained in:
Joris van Rantwijk 2024-07-21 14:25:49 +02:00
parent e8490010d6
commit c731c32473
1 changed files with 52 additions and 40 deletions

View File

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