Track blossoms in each alternating tree
This commit is contained in:
parent
aab2acd78e
commit
de30ac3c5e
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue