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