Clean up magagement of blossom labels
This commit is contained in:
parent
f8c6b99842
commit
b960a85b6c
|
@ -386,7 +386,7 @@ class _Blossom:
|
||||||
self.tree_edge: Optional[tuple[int, int]] = None
|
self.tree_edge: Optional[tuple[int, int]] = None
|
||||||
|
|
||||||
# For a labeled top-level blossom,
|
# For a labeled top-level blossom,
|
||||||
# "alternating_tree" is the set of all top-level blossoms that belong
|
# "tree_blossoms" is the set of all top-level blossoms that belong
|
||||||
# to the same alternating tree. The same set instance is shared by
|
# to the same alternating tree. The same set instance is shared by
|
||||||
# all top-level blossoms in the tree.
|
# all top-level blossoms in the tree.
|
||||||
self.tree_blossoms: "Optional[set[_Blossom]]" = None
|
self.tree_blossoms: "Optional[set[_Blossom]]" = None
|
||||||
|
@ -856,77 +856,155 @@ class _MatchingContext:
|
||||||
return (-1, math.inf)
|
return (-1, math.inf)
|
||||||
|
|
||||||
#
|
#
|
||||||
# General support routines:
|
# Managing blossom labels:
|
||||||
#
|
#
|
||||||
|
|
||||||
# TODO -- Although this code is correct, there is a conceptual problem
|
|
||||||
# that the division of responsibilities is asymmetric.
|
|
||||||
# For example, assign_blossom_label_s is not the exact
|
|
||||||
# opposite of remove_blossom_label_s, and remove_blossom_label_s
|
|
||||||
# has different responsibilities from remove_blossom_label_t.
|
|
||||||
# This must be fixed by shifting responsibilities or renaming
|
|
||||||
# functions, otherwise this stuff is impossible to understand.
|
|
||||||
|
|
||||||
def assign_blossom_label_s(self, blossom: _Blossom) -> None:
|
def assign_blossom_label_s(self, blossom: _Blossom) -> None:
|
||||||
"""Assign label S to an unlabeled top-level blossom."""
|
"""Change an unlabeled top-level blossom into an S-blossom."""
|
||||||
|
|
||||||
assert blossom.parent is None
|
assert blossom.parent is None
|
||||||
assert blossom.label == _LABEL_NONE
|
assert blossom.label == _LABEL_NONE
|
||||||
blossom.label = _LABEL_S
|
blossom.label = _LABEL_S
|
||||||
|
|
||||||
|
# Labeled blossoms must not be in the delta2 queue.
|
||||||
self.delta2_disable_blossom(blossom)
|
self.delta2_disable_blossom(blossom)
|
||||||
|
|
||||||
# Prepare for lazy updating of S-blossom dual variable.
|
# Adjust for lazy updating of S-blossom dual variables.
|
||||||
|
#
|
||||||
|
# The true dual value of an unlabeled top-level blossom is
|
||||||
|
# blossom.dual_var
|
||||||
|
#
|
||||||
|
# while the true dual value of a top-level S-blossom is
|
||||||
|
# blossom.dual_var + ctx.delta_sum_2x
|
||||||
|
#
|
||||||
|
# The value of blossom.dual_var must be adjusted accordingly
|
||||||
|
# when the blossom changes from unlabeled to S-blossom.
|
||||||
|
#
|
||||||
if isinstance(blossom, _NonTrivialBlossom):
|
if isinstance(blossom, _NonTrivialBlossom):
|
||||||
blossom.dual_var -= self.delta_sum_2x
|
blossom.dual_var -= self.delta_sum_2x
|
||||||
|
|
||||||
def remove_blossom_label_s(self, blossom: _Blossom) -> None:
|
# Apply pending updates to vertex dual variables and prepare
|
||||||
"""Remove label S from a top-level S-blossom."""
|
# for lazy updating of S-vertex dual variables.
|
||||||
assert blossom.parent is None
|
#
|
||||||
assert blossom.label == _LABEL_S
|
# For S-blossoms, blossom.vertex_dual_offset is always 0.
|
||||||
blossom.label = _LABEL_NONE
|
#
|
||||||
|
# Furthermore, the true dual value of an unlabeled vertex is
|
||||||
# Catch up with lazy updates to S-blossom dual variable.
|
# (vertex_dual_2x[x] + blossom.vertex_dual_offset) / 2
|
||||||
if isinstance(blossom, _NonTrivialBlossom):
|
#
|
||||||
blossom.dual_var += self.delta_sum_2x
|
# while the true dual value of an S-vertex is
|
||||||
|
# (vertex_dual_2x[x] - delta_sum_2x) / 2
|
||||||
def assign_vertex_label_s(self, blossom: _Blossom) -> None:
|
#
|
||||||
"""Adjust after assigning label S to previously unlabeled vertices."""
|
# The value of vertex_dual_2x must be adjusted accordingly
|
||||||
|
# when vertices change from unlabeled to S-vertex.
|
||||||
# Add the new S-vertices to the scan queue.
|
#
|
||||||
vertices = blossom.vertices()
|
|
||||||
self.scan_queue.extend(vertices)
|
|
||||||
|
|
||||||
# Prepare for lazy updating of S-vertex dual variables.
|
|
||||||
vertex_dual_fixup = self.delta_sum_2x + blossom.vertex_dual_offset
|
vertex_dual_fixup = self.delta_sum_2x + blossom.vertex_dual_offset
|
||||||
blossom.vertex_dual_offset = 0
|
blossom.vertex_dual_offset = 0
|
||||||
|
vertices = blossom.vertices()
|
||||||
for x in vertices:
|
for x in vertices:
|
||||||
self.vertex_dual_2x[x] += vertex_dual_fixup
|
self.vertex_dual_2x[x] += vertex_dual_fixup
|
||||||
|
|
||||||
|
# S-vertices do not keep track of potential delta2 edges.
|
||||||
self.delta2_clear_vertex(x)
|
self.delta2_clear_vertex(x)
|
||||||
|
|
||||||
|
# Add the new S-vertices to the scan queue.
|
||||||
|
self.scan_queue.extend(vertices)
|
||||||
|
|
||||||
def assign_blossom_label_t(self, blossom: _Blossom) -> None:
|
def assign_blossom_label_t(self, blossom: _Blossom) -> None:
|
||||||
"""Assign label T to an unlabeled top-level blossom."""
|
"""Change an unlabeled top-level blossom into a T-blossom."""
|
||||||
|
|
||||||
assert blossom.parent is None
|
assert blossom.parent is None
|
||||||
assert blossom.label == _LABEL_NONE
|
assert blossom.label == _LABEL_NONE
|
||||||
blossom.label = _LABEL_T
|
blossom.label = _LABEL_T
|
||||||
|
|
||||||
|
# Labeled blossoms must not be in the delta2 queue.
|
||||||
self.delta2_disable_blossom(blossom)
|
self.delta2_disable_blossom(blossom)
|
||||||
|
|
||||||
if isinstance(blossom, _NonTrivialBlossom):
|
if isinstance(blossom, _NonTrivialBlossom):
|
||||||
|
|
||||||
# Prepare for lazy updating of T-blossom dual variables.
|
# Adjust for lazy updating of T-blossom dual variables.
|
||||||
|
#
|
||||||
|
# The true dual value of an unlabeled top-level blossom is
|
||||||
|
# blossom.dual_var
|
||||||
|
#
|
||||||
|
# while the true dual value of a top-level T-blossom is
|
||||||
|
# blossom.dual_var - ctx.delta_sum_2x
|
||||||
|
#
|
||||||
|
# The value of blossom.dual_var must be adjusted accordingly
|
||||||
|
# when the blossom changes from unlabeled to S-blossom.
|
||||||
|
#
|
||||||
blossom.dual_var += self.delta_sum_2x
|
blossom.dual_var += self.delta_sum_2x
|
||||||
|
|
||||||
# Insert blossom into the delta4 queue.
|
# Top-level T-blossoms are tracked in the delta4 queue.
|
||||||
assert blossom.delta4_node is None
|
assert blossom.delta4_node is None
|
||||||
blossom.delta4_node = self.delta4_queue.insert(blossom.dual_var,
|
blossom.delta4_node = self.delta4_queue.insert(blossom.dual_var,
|
||||||
blossom)
|
blossom)
|
||||||
|
|
||||||
# Prepare for lazy updating of T-vertex dual variables.
|
# Prepare for lazy updating of T-vertex dual variables.
|
||||||
|
#
|
||||||
|
# The true dual value of an unlabeled vertex is
|
||||||
|
# (vertex_dual_2x[x] + blossom.vertex_dual_offset) / 2
|
||||||
|
#
|
||||||
|
# while the true dual value of a T-vertex is
|
||||||
|
# (vertex_dual_2x[x] + blossom.vertex_dual_offset + delta_sum_2x) / 2
|
||||||
|
#
|
||||||
|
# The value of blossom.vertex_dual_offset must be adjusted accordingly
|
||||||
|
# when the blossom changes from unlabeled to T-blossom.
|
||||||
|
#
|
||||||
blossom.vertex_dual_offset -= self.delta_sum_2x
|
blossom.vertex_dual_offset -= self.delta_sum_2x
|
||||||
|
|
||||||
|
def remove_blossom_label_s(self, blossom: _Blossom) -> None:
|
||||||
|
"""Change a top-level S-blossom into an unlabeled blossom.
|
||||||
|
|
||||||
|
For a blossom with "j" vertices and "k" incident edges,
|
||||||
|
this function takes time O((j + k) * log(n)).
|
||||||
|
|
||||||
|
This function is called at most once per blossom per stage.
|
||||||
|
It therefore takes total time O((n + m) * log(n)) per stage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert blossom.parent is None
|
||||||
|
assert blossom.label == _LABEL_S
|
||||||
|
blossom.label = _LABEL_NONE
|
||||||
|
|
||||||
|
# Unwind lazy delta updates to the S-blossom dual variable.
|
||||||
|
if isinstance(blossom, _NonTrivialBlossom):
|
||||||
|
blossom.dual_var += self.delta_sum_2x
|
||||||
|
|
||||||
|
assert blossom.vertex_dual_offset == 0
|
||||||
|
vertex_dual_fixup = -self.delta_sum_2x
|
||||||
|
|
||||||
|
edges = self.graph.edges
|
||||||
|
adjacent_edges = self.graph.adjacent_edges
|
||||||
|
|
||||||
|
for x in blossom.vertices():
|
||||||
|
|
||||||
|
# Unwind lazy delta updates to S-vertex dual variables.
|
||||||
|
self.vertex_dual_2x[x] += vertex_dual_fixup
|
||||||
|
|
||||||
|
# Scan the incident edges of all vertices in the blossom.
|
||||||
|
for e in adjacent_edges[x]:
|
||||||
|
(p, q, _w) = edges[e]
|
||||||
|
y = p if p != x else q
|
||||||
|
|
||||||
|
# If this edge is in the delta3 queue, remove it.
|
||||||
|
# Only edges between S-vertices are tracked for delta3,
|
||||||
|
# and vertex "x" is no longer an S-vertex.
|
||||||
|
self.delta3_remove_edge(e)
|
||||||
|
|
||||||
|
by = self.vertex_set_node[y].find()
|
||||||
|
if by.label == _LABEL_S:
|
||||||
|
# Edge "e" connects unlabeled vertex "x" to S-vertex "y".
|
||||||
|
# It must be tracked for delta2 via vertex "x".
|
||||||
|
self.delta2_add_edge(e, x, blossom)
|
||||||
|
else:
|
||||||
|
# Edge "e" connects former S-vertex "x" to T-vertex
|
||||||
|
# or unlabeled vertex "y". That implies this edge was
|
||||||
|
# tracked for delta2 via vertex "y", but it must be
|
||||||
|
# removed now.
|
||||||
|
self.delta2_remove_edge(e, y, by)
|
||||||
|
|
||||||
def remove_blossom_label_t(self, blossom: _Blossom) -> None:
|
def remove_blossom_label_t(self, blossom: _Blossom) -> None:
|
||||||
"""Remove label T from a top-level T-blossom."""
|
"""Change a top-level T-blossom into an unlabeled blossom."""
|
||||||
|
|
||||||
assert blossom.parent is None
|
assert blossom.parent is None
|
||||||
assert blossom.label == _LABEL_T
|
assert blossom.label == _LABEL_T
|
||||||
|
@ -934,47 +1012,34 @@ class _MatchingContext:
|
||||||
|
|
||||||
if isinstance(blossom, _NonTrivialBlossom):
|
if isinstance(blossom, _NonTrivialBlossom):
|
||||||
|
|
||||||
# Remove blossom from delta4 queue.
|
# Unlabeled blossoms are not tracked in the delta4 queue.
|
||||||
assert blossom.delta4_node is not None
|
assert blossom.delta4_node is not None
|
||||||
self.delta4_queue.delete(blossom.delta4_node)
|
self.delta4_queue.delete(blossom.delta4_node)
|
||||||
blossom.delta4_node = None
|
blossom.delta4_node = None
|
||||||
|
|
||||||
# Unwind lazy updates to T-blossom dual variable.
|
# Unwind lazy updates to the T-blossom dual variable.
|
||||||
blossom.dual_var -= self.delta_sum_2x
|
blossom.dual_var -= self.delta_sum_2x
|
||||||
|
|
||||||
# Unwind lazy updates of T-vertex dual variables.
|
# Unwind lazy updates of T-vertex dual variables.
|
||||||
blossom.vertex_dual_offset += self.delta_sum_2x
|
blossom.vertex_dual_offset += self.delta_sum_2x
|
||||||
|
|
||||||
def remove_vertex_label_s(self, x: int, bx: _Blossom) -> None:
|
# Enable unlabeled top-level blossom for delta2 tracking.
|
||||||
"""Adjust delta tracking for S-vertex losings its label.
|
self.delta2_enable_blossom(blossom)
|
||||||
|
|
||||||
This function is called when vertex "x" was an S-vertex but
|
def change_s_blossom_to_subblossom(self, blossom: _Blossom) -> None:
|
||||||
has just lost its label. This requires adjustments in the tracking
|
"""Change a top-level S-blossom into an S-subblossom."""
|
||||||
of delta2 and delta3.
|
|
||||||
|
|
||||||
This function is takes time O(q * log(n)),
|
assert blossom.parent is None
|
||||||
where q is the number of incident edges of vertex "x".
|
assert blossom.label == _LABEL_S
|
||||||
This function is called at most once per vertex per stage.
|
blossom.label = _LABEL_NONE
|
||||||
This function therefore takes ammortized time O(m * log(n)) per stage.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Scan the edges that are incident on "x".
|
# Unwind lazy delta updates to the S-blossom dual variable.
|
||||||
edges = self.graph.edges
|
if isinstance(blossom, _NonTrivialBlossom):
|
||||||
for e in self.graph.adjacent_edges[x]:
|
blossom.dual_var += self.delta_sum_2x
|
||||||
(p, q, _w) = edges[e]
|
|
||||||
y = p if p != x else q
|
|
||||||
|
|
||||||
self.delta3_remove_edge(e)
|
#
|
||||||
|
# General support routines:
|
||||||
by = self.vertex_set_node[y].find()
|
#
|
||||||
if by.label == _LABEL_S:
|
|
||||||
# This is an edge between "x" and an S-vertex.
|
|
||||||
# Add this edge to "vertex_sedge_queue[x]".
|
|
||||||
# Update delta2 tracking accordingly.
|
|
||||||
self.delta2_add_edge(e, x, bx)
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.delta2_remove_edge(e, y, by)
|
|
||||||
|
|
||||||
def reset_blossom_label(self, blossom: _Blossom) -> None:
|
def reset_blossom_label(self, blossom: _Blossom) -> None:
|
||||||
"""Remove blossom label."""
|
"""Remove blossom label."""
|
||||||
|
@ -983,27 +1048,9 @@ class _MatchingContext:
|
||||||
assert blossom.label != _LABEL_NONE
|
assert blossom.label != _LABEL_NONE
|
||||||
|
|
||||||
if blossom.label == _LABEL_S:
|
if blossom.label == _LABEL_S:
|
||||||
|
self.remove_blossom_label_s(blossom)
|
||||||
# Remove label.
|
|
||||||
blossom.label = _LABEL_NONE
|
|
||||||
|
|
||||||
# Unwind lazy delta updates to S-blossom dual variable.
|
|
||||||
if isinstance(blossom, _NonTrivialBlossom):
|
|
||||||
blossom.dual_var += self.delta_sum_2x
|
|
||||||
|
|
||||||
# Unwind lazy delta updates to S-vertex dual variables.
|
|
||||||
assert blossom.vertex_dual_offset == 0
|
|
||||||
vertex_dual_fixup = -self.delta_sum_2x
|
|
||||||
for x in blossom.vertices():
|
|
||||||
self.vertex_dual_2x[x] += vertex_dual_fixup
|
|
||||||
|
|
||||||
# Adjust delta tracking for S-vertex losing its label.
|
|
||||||
self.remove_vertex_label_s(x, blossom)
|
|
||||||
|
|
||||||
elif blossom.label == _LABEL_T:
|
elif blossom.label == _LABEL_T:
|
||||||
|
|
||||||
self.remove_blossom_label_t(blossom)
|
self.remove_blossom_label_t(blossom)
|
||||||
self.delta2_enable_blossom(blossom)
|
|
||||||
|
|
||||||
def _check_alternating_tree_consistency(self) -> None:
|
def _check_alternating_tree_consistency(self) -> None:
|
||||||
"""TODO -- remove this function, only for debugging"""
|
"""TODO -- remove this function, only for debugging"""
|
||||||
|
@ -1155,25 +1202,28 @@ 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.
|
||||||
for sub in subblossoms:
|
for sub in subblossoms:
|
||||||
if sub.label == _LABEL_S:
|
if sub.label == _LABEL_T:
|
||||||
self.remove_blossom_label_s(sub)
|
|
||||||
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_blossom_label_s(sub)
|
||||||
|
self.change_s_blossom_to_subblossom(sub)
|
||||||
|
|
||||||
# Create the new blossom object.
|
# Create the new blossom object.
|
||||||
blossom = _NonTrivialBlossom(subblossoms, path.edges)
|
blossom = _NonTrivialBlossom(subblossoms, path.edges)
|
||||||
|
|
||||||
# Assign label S to the new blossom and link it to the tree.
|
# Assign label S to the new blossom.
|
||||||
self.assign_blossom_label_s(blossom)
|
blossom.label = _LABEL_S
|
||||||
|
|
||||||
|
# Prepare for lazy updating of S-blossom dual variable.
|
||||||
|
blossom.dual_var = -self.delta_sum_2x
|
||||||
|
|
||||||
|
# Link the new blossom to the alternating tree.
|
||||||
tree_blossoms = subblossoms[0].tree_blossoms
|
tree_blossoms = subblossoms[0].tree_blossoms
|
||||||
assert tree_blossoms is not None
|
assert tree_blossoms is not None
|
||||||
blossom.tree_edge = subblossoms[0].tree_edge
|
blossom.tree_edge = subblossoms[0].tree_edge
|
||||||
blossom.tree_blossoms = tree_blossoms
|
blossom.tree_blossoms = tree_blossoms
|
||||||
tree_blossoms.add(blossom)
|
tree_blossoms.add(blossom)
|
||||||
|
|
||||||
# Insert into the blossom array.
|
# Add to the list of blossoms.
|
||||||
self.nontrivial_blossom.add(blossom)
|
self.nontrivial_blossom.add(blossom)
|
||||||
|
|
||||||
# Link the subblossoms to the their new parent.
|
# Link the subblossoms to the their new parent.
|
||||||
|
@ -1215,6 +1265,38 @@ class _MatchingContext:
|
||||||
|
|
||||||
return (nodes, edges)
|
return (nodes, edges)
|
||||||
|
|
||||||
|
def expand_unlabeled_blossom(self, blossom: _NonTrivialBlossom) -> None:
|
||||||
|
"""Expand the specified unlabeled blossom.
|
||||||
|
|
||||||
|
This function takes total time O(n*log(n)) per stage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert blossom.parent is None
|
||||||
|
assert blossom.label == _LABEL_NONE
|
||||||
|
|
||||||
|
# Remove blossom from the delta2 queue.
|
||||||
|
self.delta2_disable_blossom(blossom)
|
||||||
|
|
||||||
|
# Split union-find structure.
|
||||||
|
blossom.vertex_set.split()
|
||||||
|
|
||||||
|
# Prepare to push lazy delta updates down to the sub-blossoms.
|
||||||
|
vertex_dual_offset = blossom.vertex_dual_offset
|
||||||
|
blossom.vertex_dual_offset = 0
|
||||||
|
|
||||||
|
# Convert sub-blossoms into top-level blossoms.
|
||||||
|
for sub in blossom.subblossoms:
|
||||||
|
assert sub.label == _LABEL_NONE
|
||||||
|
sub.parent = None
|
||||||
|
|
||||||
|
assert sub.vertex_dual_offset == 0
|
||||||
|
sub.vertex_dual_offset = vertex_dual_offset
|
||||||
|
|
||||||
|
self.delta2_enable_blossom(sub)
|
||||||
|
|
||||||
|
# Delete the expanded blossom.
|
||||||
|
self.nontrivial_blossom.remove(blossom)
|
||||||
|
|
||||||
def expand_t_blossom(self, blossom: _NonTrivialBlossom) -> None:
|
def expand_t_blossom(self, blossom: _NonTrivialBlossom) -> None:
|
||||||
"""Expand the specified T-blossom.
|
"""Expand the specified T-blossom.
|
||||||
|
|
||||||
|
@ -1225,34 +1307,18 @@ class _MatchingContext:
|
||||||
assert blossom.label == _LABEL_T
|
assert blossom.label == _LABEL_T
|
||||||
assert blossom.delta2_node is None
|
assert blossom.delta2_node is None
|
||||||
|
|
||||||
# Remove expanded blossom from the delta4 queue.
|
|
||||||
assert blossom.delta4_node is not None
|
|
||||||
self.delta4_queue.delete(blossom.delta4_node)
|
|
||||||
blossom.delta4_node = None
|
|
||||||
|
|
||||||
# Remove blossom from its alternating tree.
|
# Remove blossom from its alternating tree.
|
||||||
tree_blossoms = blossom.tree_blossoms
|
tree_blossoms = blossom.tree_blossoms
|
||||||
assert tree_blossoms is not None
|
assert tree_blossoms is not None
|
||||||
tree_blossoms.remove(blossom)
|
tree_blossoms.remove(blossom)
|
||||||
|
|
||||||
# Split union-find structure.
|
# Remove label T.
|
||||||
blossom.vertex_set.split()
|
self.remove_blossom_label_t(blossom)
|
||||||
|
|
||||||
# Prepare to push lazy delta updates down to the sub-blossoms.
|
# Expand the now-unlabeled blossom.
|
||||||
vertex_dual_fixup = self.delta_sum_2x + blossom.vertex_dual_offset
|
self.expand_unlabeled_blossom(blossom)
|
||||||
blossom.vertex_dual_offset = 0
|
|
||||||
|
|
||||||
# Convert sub-blossoms into top-level blossoms.
|
# The expanded blossom was part of an alternating tree, linked to
|
||||||
for sub in blossom.subblossoms:
|
|
||||||
assert sub.label == _LABEL_NONE
|
|
||||||
sub.parent = None
|
|
||||||
|
|
||||||
assert sub.vertex_dual_offset == 0
|
|
||||||
sub.vertex_dual_offset = vertex_dual_fixup
|
|
||||||
|
|
||||||
self.delta2_enable_blossom(sub)
|
|
||||||
|
|
||||||
# The expanding blossom was part of an alternating tree, linked to
|
|
||||||
# a parent node in the tree via one of its subblossoms, and linked to
|
# a parent node in the tree via one of its subblossoms, and linked to
|
||||||
# a child node of the tree via the base vertex.
|
# a child node of the tree via the base vertex.
|
||||||
# We must reconstruct this part of the alternating tree, which will
|
# We must reconstruct this part of the alternating tree, which will
|
||||||
|
@ -1296,40 +1362,6 @@ class _MatchingContext:
|
||||||
sub.tree_blossoms = tree_blossoms
|
sub.tree_blossoms = tree_blossoms
|
||||||
tree_blossoms.add(sub)
|
tree_blossoms.add(sub)
|
||||||
|
|
||||||
# Delete the expanded blossom.
|
|
||||||
self.nontrivial_blossom.remove(blossom)
|
|
||||||
|
|
||||||
def expand_unlabeled_blossom(self, blossom: _NonTrivialBlossom) -> None:
|
|
||||||
"""Expand the specified unlabeled blossom.
|
|
||||||
|
|
||||||
This function takes total time O(n*log(n)) per stage.
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert blossom.parent is None
|
|
||||||
assert blossom.label == _LABEL_NONE
|
|
||||||
|
|
||||||
self.delta2_disable_blossom(blossom)
|
|
||||||
|
|
||||||
# Split union-find structure.
|
|
||||||
blossom.vertex_set.split()
|
|
||||||
|
|
||||||
# Prepare to push lazy delta updates down to the sub-blossoms.
|
|
||||||
vertex_dual_offset = blossom.vertex_dual_offset
|
|
||||||
blossom.vertex_dual_offset = 0
|
|
||||||
|
|
||||||
# Convert sub-blossoms into top-level blossoms.
|
|
||||||
for sub in blossom.subblossoms:
|
|
||||||
assert sub.label == _LABEL_NONE
|
|
||||||
sub.parent = None
|
|
||||||
|
|
||||||
assert sub.vertex_dual_offset == 0
|
|
||||||
sub.vertex_dual_offset = vertex_dual_offset
|
|
||||||
|
|
||||||
self.delta2_enable_blossom(sub)
|
|
||||||
|
|
||||||
# Delete the expanded blossom.
|
|
||||||
self.nontrivial_blossom.remove(blossom)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Augmenting:
|
# Augmenting:
|
||||||
#
|
#
|
||||||
|
@ -1492,7 +1524,6 @@ 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.vertex_set_node[x].find()
|
||||||
self.assign_blossom_label_s(bx)
|
self.assign_blossom_label_s(bx)
|
||||||
self.assign_vertex_label_s(bx)
|
|
||||||
|
|
||||||
y = self.vertex_mate[x]
|
y = self.vertex_mate[x]
|
||||||
if y == -1:
|
if y == -1:
|
||||||
|
@ -1712,7 +1743,6 @@ class _MatchingContext:
|
||||||
|
|
||||||
# Assign label S.
|
# Assign label S.
|
||||||
self.assign_blossom_label_s(bx)
|
self.assign_blossom_label_s(bx)
|
||||||
self.assign_vertex_label_s(bx)
|
|
||||||
|
|
||||||
# Mark blossom as the root of an alternating tree.
|
# Mark blossom as the root of an alternating tree.
|
||||||
bx.tree_edge = None
|
bx.tree_edge = None
|
||||||
|
|
Loading…
Reference in New Issue