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
 | 
					    """Represents a list of edges forming an alternating path or an
 | 
				
			||||||
    alternating cycle."""
 | 
					    alternating cycle."""
 | 
				
			||||||
    edges: list[tuple[int, int]]
 | 
					    edges: list[tuple[int, int]]
 | 
				
			||||||
 | 
					    is_cycle: bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _MatchingContext:
 | 
					class _MatchingContext:
 | 
				
			||||||
| 
						 | 
					@ -1166,7 +1167,8 @@ class _MatchingContext:
 | 
				
			||||||
        # Any S-to-S alternating path must have odd length.
 | 
					        # Any S-to-S alternating path must have odd length.
 | 
				
			||||||
        assert len(path_edges) % 2 == 1
 | 
					        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:
 | 
					    # Merge and expand blossoms:
 | 
				
			||||||
| 
						 | 
					@ -1243,8 +1245,8 @@ class _MatchingContext:
 | 
				
			||||||
            blossom: _NonTrivialBlossom,
 | 
					            blossom: _NonTrivialBlossom,
 | 
				
			||||||
            sub: _Blossom
 | 
					            sub: _Blossom
 | 
				
			||||||
            ) -> tuple[list[_Blossom], list[tuple[int, int]]]:
 | 
					            ) -> tuple[list[_Blossom], list[tuple[int, int]]]:
 | 
				
			||||||
        """Construct a path through the specified blossom,
 | 
					        """Construct a path with an even number of edges through the
 | 
				
			||||||
        from sub-blossom "sub" to the base of the blossom.
 | 
					        specified blossom, from sub-blossom "sub" to the base of "blossom".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Return:
 | 
					        Return:
 | 
				
			||||||
            Tuple (nodes, edges).
 | 
					            Tuple (nodes, edges).
 | 
				
			||||||
| 
						 | 
					@ -1263,6 +1265,9 @@ class _MatchingContext:
 | 
				
			||||||
            nodes = blossom.subblossoms[p:] + blossom.subblossoms[0:1]
 | 
					            nodes = blossom.subblossoms[p:] + blossom.subblossoms[0:1]
 | 
				
			||||||
            edges = blossom.edges[p:]
 | 
					            edges = blossom.edges[p:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert len(edges) % 2 == 0
 | 
				
			||||||
 | 
					        assert len(nodes) % 2 == 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (nodes, edges)
 | 
					        return (nodes, edges)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def expand_unlabeled_blossom(self, blossom: _NonTrivialBlossom) -> None:
 | 
					    def expand_unlabeled_blossom(self, blossom: _NonTrivialBlossom) -> None:
 | 
				
			||||||
| 
						 | 
					@ -1352,7 +1357,7 @@ class _MatchingContext:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Assign label S to path_nodes[p+1].
 | 
					            # Assign label S to path_nodes[p+1].
 | 
				
			||||||
            (y, x) = path_edges[p]
 | 
					            (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
 | 
					            # Assign label T to path_nodes[i+2] and attach it
 | 
				
			||||||
            # to path_nodes[p+1].
 | 
					            # to path_nodes[p+1].
 | 
				
			||||||
| 
						 | 
					@ -1504,59 +1509,49 @@ class _MatchingContext:
 | 
				
			||||||
            self.vertex_mate[y] = x
 | 
					            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".
 | 
					        """Assign label S to the unlabeled blossom that contains vertex "x".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        If vertex "x" is matched, it is attached to the alternating tree
 | 
					        The newly labeled S-blossom is added to the alternating tree
 | 
				
			||||||
        via its matched edge. If vertex "x" is unmatched, it becomes the root
 | 
					        via its matched edge. All vertices in the newly labeled S-blossom
 | 
				
			||||||
        of an alternating tree.
 | 
					        are added to the scan queue.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        All vertices in the newly labeled blossom are added to the scan queue.
 | 
					        Preconditions:
 | 
				
			||||||
 | 
					         - "x" is a vertex in an unlabeled blossom.
 | 
				
			||||||
        Precondition:
 | 
					         - "x" is matched to a T-vertex via a tight edge.
 | 
				
			||||||
            "x" is an unlabeled vertex, either unmatched or matched to
 | 
					 | 
				
			||||||
            a T-vertex via a tight edge.
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # 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".
 | 
					        # Vertex "x" is matched to T-vertex "y".
 | 
				
			||||||
 | 
					        y = self.vertex_mate[x]
 | 
				
			||||||
 | 
					        assert y != -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        by = self.vertex_set_node[y].find()
 | 
					        by = self.vertex_set_node[y].find()
 | 
				
			||||||
        assert by.label == _LABEL_T
 | 
					        assert by.label == _LABEL_T
 | 
				
			||||||
 | 
					        assert by.tree_blossoms is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # 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
 | 
					        bx.tree_blossoms = by.tree_blossoms
 | 
				
			||||||
            assert bx.tree_blossoms is not None
 | 
					 | 
				
			||||||
        bx.tree_blossoms.add(bx)
 | 
					        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".
 | 
					        """Assign label T to the unlabeled blossom that contains vertex "y".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Attach it to the alternating tree via edge (x, y).
 | 
					        The newly labeled T-blossom is added to the alternating tree.
 | 
				
			||||||
        Then immediately assign label S to the mate of vertex "y".
 | 
					        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:
 | 
					        Preconditions:
 | 
				
			||||||
         - "x" is an S-vertex.
 | 
					         - "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".
 | 
					         - 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.
 | 
					        # 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]
 | 
				
			||||||
        assert z != -1
 | 
					        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".
 | 
					        """Add the edge between S-vertices "x" and "y".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        If the edge connects blossoms that are part of the same alternating
 | 
					        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
 | 
					        If the edge connects two different alternating trees, an augmenting
 | 
				
			||||||
        path has been discovered. In this case the function changes nothing
 | 
					        path has been discovered. This function then augments the matching
 | 
				
			||||||
        and returns the augmenting path.
 | 
					        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:
 | 
					        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".
 | 
					        # Trace back through the alternating trees from "x" and "y".
 | 
				
			||||||
        path = self.trace_alternating_paths(x, y)
 | 
					        path = self.trace_alternating_paths(x, y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # If the path is a cycle, create a new blossom.
 | 
					        assert bx.tree_blossoms is not None
 | 
				
			||||||
        # Otherwise the path is an augmenting path.
 | 
					        assert by.tree_blossoms is not None
 | 
				
			||||||
        # Note that an alternating starts and ends in the same blossom,
 | 
					
 | 
				
			||||||
        # but not necessarily in the same vertex within that blossom.
 | 
					        if bx.tree_blossoms is by.tree_blossoms:
 | 
				
			||||||
        p = path.edges[0][0]
 | 
					            # Both blossoms belong to the same alternating tree.
 | 
				
			||||||
        q = path.edges[-1][1]
 | 
					            # This implies that the alternating path is a cycle.
 | 
				
			||||||
        if self.vertex_set_node[p].find() is self.vertex_set_node[q].find():
 | 
					            # The path will be used to create a new blossom.
 | 
				
			||||||
 | 
					            assert path.is_cycle
 | 
				
			||||||
            self.make_blossom(path)
 | 
					            self.make_blossom(path)
 | 
				
			||||||
            return None
 | 
					
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					        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:
 | 
					    def substage_scan(self) -> None:
 | 
				
			||||||
        """Scan queued S-vertices and consider their incident edges.
 | 
					        """Scan queued S-vertices and consider their incident edges.
 | 
				
			||||||
| 
						 | 
					@ -1799,25 +1824,14 @@ class _MatchingContext:
 | 
				
			||||||
                (x, y, _w) = self.graph.edges[delta_edge]
 | 
					                (x, y, _w) = self.graph.edges[delta_edge]
 | 
				
			||||||
                if self.vertex_set_node[x].find().label != _LABEL_S:
 | 
					                if self.vertex_set_node[x].find().label != _LABEL_S:
 | 
				
			||||||
                    (x, y) = (y, x)
 | 
					                    (x, y) = (y, x)
 | 
				
			||||||
                self.extend_tree_t(x, y)
 | 
					                self.extend_tree_s_to_t(x, y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            elif delta_type == 3:
 | 
					            elif delta_type == 3:
 | 
				
			||||||
                # Use the S-to-S edge that got unlocked by the delta update.
 | 
					                # Use the S-to-S edge that got unlocked by the delta update.
 | 
				
			||||||
                # This reveals either a new blossom or an augmenting path.
 | 
					                # This reveals either a new blossom or an augmenting path.
 | 
				
			||||||
                (x, y, _w) = self.graph.edges[delta_edge]
 | 
					                (x, y, _w) = self.graph.edges[delta_edge]
 | 
				
			||||||
                augmenting_path = self.add_s_to_s_edge(x, y)
 | 
					                if self.add_s_to_s_edge(x, y):
 | 
				
			||||||
                if augmenting_path is not None:
 | 
					                    # Matching was augmented. End the stage.
 | 
				
			||||||
                    # 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.
 | 
					 | 
				
			||||||
                    return True
 | 
					                    return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            elif delta_type == 4:
 | 
					            elif delta_type == 4:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue