mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	This commit reverts commit ac0333e1e1 as the original links are working again and they provide extended features such as comments and alternative versions.
		
	
			
		
			
				
	
	
		
			128 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# Adapted from mypy (mypy/build.py) under the MIT license.
 | 
						|
 | 
						|
from typing import *
 | 
						|
 | 
						|
 | 
						|
def strongly_connected_components(
 | 
						|
    vertices: AbstractSet[str], edges: Dict[str, AbstractSet[str]]
 | 
						|
) -> Iterator[AbstractSet[str]]:
 | 
						|
    """Compute Strongly Connected Components of a directed graph.
 | 
						|
 | 
						|
    Args:
 | 
						|
      vertices: the labels for the vertices
 | 
						|
      edges: for each vertex, gives the target vertices of its outgoing edges
 | 
						|
 | 
						|
    Returns:
 | 
						|
      An iterator yielding strongly connected components, each
 | 
						|
      represented as a set of vertices.  Each input vertex will occur
 | 
						|
      exactly once; vertices not part of a SCC are returned as
 | 
						|
      singleton sets.
 | 
						|
 | 
						|
    From http://code.activestate.com/recipes/578507/.
 | 
						|
    """
 | 
						|
    identified: Set[str] = set()
 | 
						|
    stack: List[str] = []
 | 
						|
    index: Dict[str, int] = {}
 | 
						|
    boundaries: List[int] = []
 | 
						|
 | 
						|
    def dfs(v: str) -> Iterator[Set[str]]:
 | 
						|
        index[v] = len(stack)
 | 
						|
        stack.append(v)
 | 
						|
        boundaries.append(index[v])
 | 
						|
 | 
						|
        for w in edges[v]:
 | 
						|
            if w not in index:
 | 
						|
                yield from dfs(w)
 | 
						|
            elif w not in identified:
 | 
						|
                while index[w] < boundaries[-1]:
 | 
						|
                    boundaries.pop()
 | 
						|
 | 
						|
        if boundaries[-1] == index[v]:
 | 
						|
            boundaries.pop()
 | 
						|
            scc = set(stack[index[v] :])
 | 
						|
            del stack[index[v] :]
 | 
						|
            identified.update(scc)
 | 
						|
            yield scc
 | 
						|
 | 
						|
    for v in vertices:
 | 
						|
        if v not in index:
 | 
						|
            yield from dfs(v)
 | 
						|
 | 
						|
 | 
						|
def topsort(
 | 
						|
    data: Dict[AbstractSet[str], Set[AbstractSet[str]]]
 | 
						|
) -> Iterable[AbstractSet[AbstractSet[str]]]:
 | 
						|
    """Topological sort.
 | 
						|
 | 
						|
    Args:
 | 
						|
      data: A map from SCCs (represented as frozen sets of strings) to
 | 
						|
            sets of SCCs, its dependencies.  NOTE: This data structure
 | 
						|
            is modified in place -- for normalization purposes,
 | 
						|
            self-dependencies are removed and entries representing
 | 
						|
            orphans are added.
 | 
						|
 | 
						|
    Returns:
 | 
						|
      An iterator yielding sets of SCCs that have an equivalent
 | 
						|
      ordering.  NOTE: The algorithm doesn't care about the internal
 | 
						|
      structure of SCCs.
 | 
						|
 | 
						|
    Example:
 | 
						|
      Suppose the input has the following structure:
 | 
						|
 | 
						|
        {A: {B, C}, B: {D}, C: {D}}
 | 
						|
 | 
						|
      This is normalized to:
 | 
						|
 | 
						|
        {A: {B, C}, B: {D}, C: {D}, D: {}}
 | 
						|
 | 
						|
      The algorithm will yield the following values:
 | 
						|
 | 
						|
        {D}
 | 
						|
        {B, C}
 | 
						|
        {A}
 | 
						|
 | 
						|
    From http://code.activestate.com/recipes/577413/.
 | 
						|
    """
 | 
						|
    # TODO: Use a faster algorithm?
 | 
						|
    for k, v in data.items():
 | 
						|
        v.discard(k)  # Ignore self dependencies.
 | 
						|
    for item in set.union(*data.values()) - set(data.keys()):
 | 
						|
        data[item] = set()
 | 
						|
    while True:
 | 
						|
        ready = {item for item, dep in data.items() if not dep}
 | 
						|
        if not ready:
 | 
						|
            break
 | 
						|
        yield ready
 | 
						|
        data = {item: (dep - ready) for item, dep in data.items() if item not in ready}
 | 
						|
    assert not data, "A cyclic dependency exists amongst %r" % data
 | 
						|
 | 
						|
 | 
						|
def find_cycles_in_scc(
 | 
						|
    graph: Dict[str, AbstractSet[str]], scc: AbstractSet[str], start: str
 | 
						|
) -> Iterable[List[str]]:
 | 
						|
    """Find cycles in SCC emanating from start.
 | 
						|
 | 
						|
    Yields lists of the form ['A', 'B', 'C', 'A'], which means there's
 | 
						|
    a path from A -> B -> C -> A.  The first item is always the start
 | 
						|
    argument, but the last item may be another element, e.g.  ['A',
 | 
						|
    'B', 'C', 'B'] means there's a path from A to B and there's a
 | 
						|
    cycle from B to C and back.
 | 
						|
    """
 | 
						|
    # Basic input checks.
 | 
						|
    assert start in scc, (start, scc)
 | 
						|
    assert scc <= graph.keys(), scc - graph.keys()
 | 
						|
 | 
						|
    # Reduce the graph to nodes in the SCC.
 | 
						|
    graph = {src: {dst for dst in dsts if dst in scc} for src, dsts in graph.items() if src in scc}
 | 
						|
    assert start in graph
 | 
						|
 | 
						|
    # Recursive helper that yields cycles.
 | 
						|
    def dfs(node: str, path: List[str]) -> Iterator[List[str]]:
 | 
						|
        if node in path:
 | 
						|
            yield path + [node]
 | 
						|
            return
 | 
						|
        path = path + [node]  # TODO: Make this not quadratic.
 | 
						|
        for child in graph[node]:
 | 
						|
            yield from dfs(child, path)
 | 
						|
 | 
						|
    yield from dfs(start, [])
 |