1
0
Fork 0

Track blossoms in each alternating tree

This commit is contained in:
Joris van Rantwijk 2024-06-22 17:57:42 +02:00
parent aab2acd78e
commit de30ac3c5e
1 changed files with 51 additions and 4 deletions

View File

@ -382,6 +382,12 @@ class _Blossom:
# "tree_edge = None" if the blossom is the root of an alternating tree.
self.tree_edge: Optional[tuple[int, int]] = None
# For a labeled top-level blossom,
# "alternating_tree" is the set of all top-level blossoms that belong
# to the same alternating tree. The same set instance is shared by
# all top-level blossoms in the tree.
self.tree_blossoms: "Optional[set[_Blossom]]" = None
# Each top-level blossom maintains a union-find datastructure
# containing all vertices in the blossom.
self.vertex_set: "UnionFindQueue[_Blossom, int]"
@ -607,10 +613,12 @@ class _MatchingContext:
for blossom in self.trivial_blossom:
blossom.vertex_set.clear()
del blossom.vertex_set
blossom.tree_blossoms = None
for blossom in self.nontrivial_blossom:
blossom.vertex_set.clear()
del blossom.vertex_set
blossom.tree_blossoms = None
def edge_pseudo_slack_2x(self, e: int) -> float:
"""Return 2 times the pseudo-slack of the specified edge.
@ -860,6 +868,19 @@ class _MatchingContext:
assert not self.scan_queue
# Check consistency of alternating tree.
for blossom in self.trivial_blossom + self.nontrivial_blossom:
if (blossom.parent is None) and (blossom.label != _LABEL_NONE):
assert blossom.tree_blossoms is not None
assert blossom in blossom.tree_blossoms
if blossom.tree_edge is not None:
bx = self.vertex_set_node[blossom.tree_edge[0]].find()
by = self.vertex_set_node[blossom.tree_edge[1]].find()
assert bx.tree_blossoms is blossom.tree_blossoms
assert by.tree_blossoms is blossom.tree_blossoms
else:
assert blossom.tree_blossoms is None
# Remove blossom labels and unwind lazy dual updates.
for blossom in self.trivial_blossom + self.nontrivial_blossom:
if blossom.parent is None:
@ -868,6 +889,7 @@ class _MatchingContext:
blossom.delta4_node = None
assert blossom.label == _LABEL_NONE
blossom.tree_edge = None
blossom.tree_blossoms = None
# Reset least-slack edge tracking.
self.lset_reset()
@ -944,7 +966,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() is not first_common:
while (self.vertex_set_node[yedges[-1][0]].find()
is not first_common):
yedges.pop()
# Fuse the two paths.
@ -991,12 +1014,16 @@ class _MatchingContext:
# Remove blossom labels.
# Mark vertices inside former T-blossoms as S-vertices.
tree_blossoms = subblossoms[0].tree_blossoms
assert tree_blossoms is not None
for sub in subblossoms:
if sub.label == _LABEL_S:
self.remove_blossom_label_s(sub)
elif sub.label == _LABEL_T:
self.remove_blossom_label_t(sub)
self.assign_vertex_label_s(sub)
sub.tree_blossoms = None
tree_blossoms.remove(sub)
# Create the new blossom object.
blossom = _NonTrivialBlossom(subblossoms, path.edges)
@ -1004,6 +1031,8 @@ class _MatchingContext:
# Assign label S to the new blossom and link it to the tree.
self.assign_blossom_label_s(blossom)
blossom.tree_edge = subblossoms[0].tree_edge
blossom.tree_blossoms = tree_blossoms
tree_blossoms.add(blossom)
# Insert into the blossom array.
self.nontrivial_blossom.append(blossom)
@ -1057,6 +1086,11 @@ class _MatchingContext:
self.delta4_queue.delete(blossom.delta4_node)
blossom.delta4_node = None
# Remove blossom from its alternating tree.
tree_blossoms = blossom.tree_blossoms
assert tree_blossoms is not None
tree_blossoms.remove(blossom)
# Split union-find structure.
blossom.vertex_set.split()
@ -1094,6 +1128,8 @@ class _MatchingContext:
# Assign label T to that sub-blossom.
self.assign_blossom_label_t(sub)
sub.tree_edge = blossom.tree_edge
sub.tree_blossoms = tree_blossoms
tree_blossoms.add(sub)
# Walk through the expanded blossom from "sub" to the base vertex.
# Assign alternating S and T labels to the sub-blossoms and attach
@ -1118,6 +1154,8 @@ class _MatchingContext:
sub = path_nodes[p+2]
self.assign_blossom_label_t(sub)
sub.tree_edge = path_edges[p+1]
sub.tree_blossoms = tree_blossoms
tree_blossoms.add(sub)
# Delete the expanded blossom.
# TODO -- list manipulation is too slow
@ -1336,6 +1374,7 @@ class _MatchingContext:
# Mark the blossom as root of an alternating tree.
bx.tree_edge = None
bx.tree_blossoms = {bx}
else:
# Vertex "x" is matched to T-vertex "y".
@ -1345,6 +1384,10 @@ class _MatchingContext:
# Attach the blossom that contains "x" to the alternating tree.
bx.tree_edge = (y, x)
bx.tree_blossoms = by.tree_blossoms
assert bx.tree_blossoms is not None
bx.tree_blossoms.add(bx)
def extend_tree_t(self, x: int, y: int) -> None:
"""Assign label T to the unlabeled blossom that contains vertex "y".
@ -1356,9 +1399,10 @@ class _MatchingContext:
- "y" is an unlabeled, matched vertex.
- There is a tight edge between vertices "x" and "y".
"""
assert self.vertex_set_node[x].find().label == _LABEL_S
bx = self.vertex_set_node[x].find()
by = self.vertex_set_node[y].find()
assert bx.label == _LABEL_S
# Expand zero-dual blossoms before assigning label T.
while isinstance(by, _NonTrivialBlossom) and (by.dual_var == 0):
@ -1368,6 +1412,9 @@ class _MatchingContext:
# Assign label T to the unlabeled blossom.
self.assign_blossom_label_t(by)
by.tree_edge = (x, y)
by.tree_blossoms = bx.tree_blossoms
assert by.tree_blossoms is not None
by.tree_blossoms.add(by)
# Assign label S to the blossom that is mated to the T-blossom.
z = self.vertex_mate[by.base_vertex]
@ -1427,7 +1474,7 @@ class _MatchingContext:
# Scan the edges that are incident on "x".
# This loop runs through O(m) iterations per stage.
for e in adjacent_edges[x]:
(p, q, w) = edges[e]
(p, q, _w) = edges[e]
y = p if p != x else q
# Consider the edge between vertices "x" and "y".
@ -1600,7 +1647,7 @@ class _MatchingContext:
# Calculate delta step in the dual LPP problem.
(delta_type, delta_2x, delta_edge, delta_blossom
) = self.substage_calc_dual_delta()
) = self.substage_calc_dual_delta()
# Update the running sum of delta steps.
# This implicitly updates the dual variables as needed, because