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
|
# 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,12 +554,13 @@ 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 = [
|
||||||
for (i, b) in enumerate(self.trivial_blossom)]
|
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.
|
# 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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue