Clean up management of the alternating tree
This commit is contained in:
parent
b960a85b6c
commit
f35a640e43
|
@ -508,6 +508,7 @@ class _AlternatingPath(NamedTuple):
|
|||
"""Represents a list of edges forming an alternating path or an
|
||||
alternating cycle."""
|
||||
edges: list[tuple[int, int]]
|
||||
is_cycle: bool
|
||||
|
||||
|
||||
class _MatchingContext:
|
||||
|
@ -1166,7 +1167,8 @@ class _MatchingContext:
|
|||
# Any S-to-S alternating path must have odd length.
|
||||
assert len(path_edges) % 2 == 1
|
||||
|
||||
return _AlternatingPath(path_edges)
|
||||
return _AlternatingPath(edges=path_edges,
|
||||
is_cycle=(first_common is not None))
|
||||
|
||||
#
|
||||
# Merge and expand blossoms:
|
||||
|
@ -1243,8 +1245,8 @@ class _MatchingContext:
|
|||
blossom: _NonTrivialBlossom,
|
||||
sub: _Blossom
|
||||
) -> tuple[list[_Blossom], list[tuple[int, int]]]:
|
||||
"""Construct a path through the specified blossom,
|
||||
from sub-blossom "sub" to the base of the blossom.
|
||||
"""Construct a path with an even number of edges through the
|
||||
specified blossom, from sub-blossom "sub" to the base of "blossom".
|
||||
|
||||
Return:
|
||||
Tuple (nodes, edges).
|
||||
|
@ -1263,6 +1265,9 @@ class _MatchingContext:
|
|||
nodes = blossom.subblossoms[p:] + blossom.subblossoms[0:1]
|
||||
edges = blossom.edges[p:]
|
||||
|
||||
assert len(edges) % 2 == 0
|
||||
assert len(nodes) % 2 == 1
|
||||
|
||||
return (nodes, edges)
|
||||
|
||||
def expand_unlabeled_blossom(self, blossom: _NonTrivialBlossom) -> None:
|
||||
|
@ -1352,7 +1357,7 @@ class _MatchingContext:
|
|||
|
||||
# Assign label S to path_nodes[p+1].
|
||||
(y, x) = path_edges[p]
|
||||
self.extend_tree_s(x)
|
||||
self.extend_tree_t_to_s(x)
|
||||
|
||||
# Assign label T to path_nodes[i+2] and attach it
|
||||
# to path_nodes[p+1].
|
||||
|
@ -1504,59 +1509,49 @@ class _MatchingContext:
|
|||
self.vertex_mate[y] = x
|
||||
|
||||
#
|
||||
# Labeling and alternating tree expansion:
|
||||
# Alternating tree:
|
||||
#
|
||||
|
||||
def extend_tree_s(self, x: int) -> None:
|
||||
def extend_tree_t_to_s(self, x: int) -> None:
|
||||
"""Assign label S to the unlabeled blossom that contains vertex "x".
|
||||
|
||||
If vertex "x" is matched, it is attached to the alternating tree
|
||||
via its matched edge. If vertex "x" is unmatched, it becomes the root
|
||||
of an alternating tree.
|
||||
The newly labeled S-blossom is added to the alternating tree
|
||||
via its matched edge. All vertices in the newly labeled S-blossom
|
||||
are added to the scan queue.
|
||||
|
||||
All vertices in the newly labeled blossom are added to the scan queue.
|
||||
|
||||
Precondition:
|
||||
"x" is an unlabeled vertex, either unmatched or matched to
|
||||
a T-vertex via a tight edge.
|
||||
Preconditions:
|
||||
- "x" is a vertex in an unlabeled blossom.
|
||||
- "x" is matched to a T-vertex via a tight edge.
|
||||
"""
|
||||
|
||||
# Assign label S to the blossom that contains vertex "x".
|
||||
bx = self.vertex_set_node[x].find()
|
||||
self.assign_blossom_label_s(bx)
|
||||
|
||||
# Vertex "x" is matched to T-vertex "y".
|
||||
y = self.vertex_mate[x]
|
||||
if y == -1:
|
||||
# Vertex "x" is unmatched.
|
||||
# It must be either a top-level vertex or the base vertex of
|
||||
# a top-level blossom.
|
||||
assert bx.base_vertex == x
|
||||
assert y != -1
|
||||
|
||||
# Mark the blossom as root of an alternating tree.
|
||||
bx.tree_edge = None
|
||||
bx.tree_blossoms = {bx}
|
||||
by = self.vertex_set_node[y].find()
|
||||
assert by.label == _LABEL_T
|
||||
assert by.tree_blossoms is not None
|
||||
|
||||
else:
|
||||
# Vertex "x" is matched to T-vertex "y".
|
||||
by = self.vertex_set_node[y].find()
|
||||
assert by.label == _LABEL_T
|
||||
# Attach the blossom that contains "x" to the alternating tree.
|
||||
bx.tree_edge = (y, x)
|
||||
bx.tree_blossoms = by.tree_blossoms
|
||||
bx.tree_blossoms.add(bx)
|
||||
|
||||
# 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:
|
||||
def extend_tree_s_to_t(self, x: int, y: int) -> None:
|
||||
"""Assign label T to the unlabeled blossom that contains vertex "y".
|
||||
|
||||
Attach it to the alternating tree via edge (x, y).
|
||||
Then immediately assign label S to the mate of vertex "y".
|
||||
The newly labeled T-blossom is added to the alternating tree.
|
||||
Directly afterwards, label S is assigned to the blossom that has
|
||||
a matched edge to the base of the newly labeled T-blossom, and
|
||||
that newly labeled S-blossom is also added to the alternating tree.
|
||||
|
||||
Preconditions:
|
||||
- "x" is an S-vertex.
|
||||
- "y" is an unlabeled, matched vertex.
|
||||
- "y" is a vertex in an unlabeled blossom with a matched base vertex.
|
||||
- There is a tight edge between vertices "x" and "y".
|
||||
"""
|
||||
|
||||
|
@ -1579,36 +1574,66 @@ class _MatchingContext:
|
|||
# Assign label S to the blossom that is mated to the T-blossom.
|
||||
z = self.vertex_mate[by.base_vertex]
|
||||
assert z != -1
|
||||
self.extend_tree_s(z)
|
||||
self.extend_tree_t_to_s(z)
|
||||
|
||||
def add_s_to_s_edge(self, x: int, y: int) -> Optional[_AlternatingPath]:
|
||||
def add_s_to_s_edge(self, x: int, y: int) -> bool:
|
||||
"""Add the edge between S-vertices "x" and "y".
|
||||
|
||||
If the edge connects blossoms that are part of the same alternating
|
||||
tree, this function creates a new S-blossom and returns None.
|
||||
tree, this function creates a new S-blossom and returns False.
|
||||
|
||||
If the edge connects two different alternating trees, an augmenting
|
||||
path has been discovered. In this case the function changes nothing
|
||||
and returns the augmenting path.
|
||||
path has been discovered. This function then augments the matching
|
||||
and returns True. Labels are removed from blossoms that belonged
|
||||
to the two alternating trees involved in the matching. All other
|
||||
alternating trees and labels are preserved.
|
||||
|
||||
Preconditions:
|
||||
- "x" and "y" are S-vertices in different top-level blossoms.
|
||||
- There is a tight edge between vertices "x" and "y".
|
||||
|
||||
Returns:
|
||||
Augmenting path if found; otherwise None.
|
||||
True if the matching was augmented; otherwise False.
|
||||
"""
|
||||
|
||||
bx = self.vertex_set_node[x].find()
|
||||
by = self.vertex_set_node[y].find()
|
||||
|
||||
assert bx.label == _LABEL_S
|
||||
assert by.label == _LABEL_S
|
||||
assert bx is not by
|
||||
|
||||
# Trace back through the alternating trees from "x" and "y".
|
||||
path = self.trace_alternating_paths(x, y)
|
||||
|
||||
# If the path is a cycle, create a new blossom.
|
||||
# Otherwise the path is an augmenting path.
|
||||
# Note that an alternating starts and ends in the same blossom,
|
||||
# but not necessarily in the same vertex within that blossom.
|
||||
p = path.edges[0][0]
|
||||
q = path.edges[-1][1]
|
||||
if self.vertex_set_node[p].find() is self.vertex_set_node[q].find():
|
||||
assert bx.tree_blossoms is not None
|
||||
assert by.tree_blossoms is not None
|
||||
|
||||
if bx.tree_blossoms is by.tree_blossoms:
|
||||
# Both blossoms belong to the same alternating tree.
|
||||
# This implies that the alternating path is a cycle.
|
||||
# The path will be used to create a new blossom.
|
||||
assert path.is_cycle
|
||||
self.make_blossom(path)
|
||||
return None
|
||||
|
||||
return False
|
||||
|
||||
else:
|
||||
return path
|
||||
# The blossoms belong to different alternating trees.
|
||||
# This implies that the alternating path is an augmenting
|
||||
# path between two unlabeled vertices.
|
||||
# The path will be used to augment the matching.
|
||||
|
||||
# Delete the two alternating trees on the augmenting path.
|
||||
# The blossoms in those trees become unlabeled.
|
||||
self.remove_alternating_tree(bx.tree_blossoms)
|
||||
self.remove_alternating_tree(by.tree_blossoms)
|
||||
|
||||
# Augment the matching.
|
||||
assert not path.is_cycle
|
||||
self.augment_matching(path)
|
||||
|
||||
return True
|
||||
|
||||
def substage_scan(self) -> None:
|
||||
"""Scan queued S-vertices and consider their incident edges.
|
||||
|
@ -1799,25 +1824,14 @@ class _MatchingContext:
|
|||
(x, y, _w) = self.graph.edges[delta_edge]
|
||||
if self.vertex_set_node[x].find().label != _LABEL_S:
|
||||
(x, y) = (y, x)
|
||||
self.extend_tree_t(x, y)
|
||||
self.extend_tree_s_to_t(x, y)
|
||||
|
||||
elif delta_type == 3:
|
||||
# Use the S-to-S edge that got unlocked by the delta update.
|
||||
# This reveals either a new blossom or an augmenting path.
|
||||
(x, y, _w) = self.graph.edges[delta_edge]
|
||||
augmenting_path = self.add_s_to_s_edge(x, y)
|
||||
if augmenting_path is not None:
|
||||
# Found augmenting path.
|
||||
# Delete the two alternating trees on the augmenting path.
|
||||
bx = self.vertex_set_node[x].find()
|
||||
by = self.vertex_set_node[y].find()
|
||||
assert bx.tree_blossoms is not None
|
||||
assert by.tree_blossoms is not None
|
||||
self.remove_alternating_tree(bx.tree_blossoms)
|
||||
self.remove_alternating_tree(by.tree_blossoms)
|
||||
# Augment the matching.
|
||||
self.augment_matching(augmenting_path)
|
||||
# End the stage.
|
||||
if self.add_s_to_s_edge(x, y):
|
||||
# Matching was augmented. End the stage.
|
||||
return True
|
||||
|
||||
elif delta_type == 4:
|
||||
|
|
Loading…
Reference in New Issue