about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/components/strongly_connected.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/algorithms/components/strongly_connected.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/components/strongly_connected.py351
1 files changed, 351 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/components/strongly_connected.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/components/strongly_connected.py
new file mode 100644
index 00000000..393728ff
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/components/strongly_connected.py
@@ -0,0 +1,351 @@
+"""Strongly connected components."""
+
+import networkx as nx
+from networkx.utils.decorators import not_implemented_for
+
+__all__ = [
+    "number_strongly_connected_components",
+    "strongly_connected_components",
+    "is_strongly_connected",
+    "kosaraju_strongly_connected_components",
+    "condensation",
+]
+
+
+@not_implemented_for("undirected")
+@nx._dispatchable
+def strongly_connected_components(G):
+    """Generate nodes in strongly connected components of graph.
+
+    Parameters
+    ----------
+    G : NetworkX Graph
+        A directed graph.
+
+    Returns
+    -------
+    comp : generator of sets
+        A generator of sets of nodes, one for each strongly connected
+        component of G.
+
+    Raises
+    ------
+    NetworkXNotImplemented
+        If G is undirected.
+
+    Examples
+    --------
+    Generate a sorted list of strongly connected components, largest first.
+
+    >>> G = nx.cycle_graph(4, create_using=nx.DiGraph())
+    >>> nx.add_cycle(G, [10, 11, 12])
+    >>> [
+    ...     len(c)
+    ...     for c in sorted(nx.strongly_connected_components(G), key=len, reverse=True)
+    ... ]
+    [4, 3]
+
+    If you only want the largest component, it's more efficient to
+    use max instead of sort.
+
+    >>> largest = max(nx.strongly_connected_components(G), key=len)
+
+    See Also
+    --------
+    connected_components
+    weakly_connected_components
+    kosaraju_strongly_connected_components
+
+    Notes
+    -----
+    Uses Tarjan's algorithm[1]_ with Nuutila's modifications[2]_.
+    Nonrecursive version of algorithm.
+
+    References
+    ----------
+    .. [1] Depth-first search and linear graph algorithms, R. Tarjan
+       SIAM Journal of Computing 1(2):146-160, (1972).
+
+    .. [2] On finding the strongly connected components in a directed graph.
+       E. Nuutila and E. Soisalon-Soinen
+       Information Processing Letters 49(1): 9-14, (1994)..
+
+    """
+    preorder = {}
+    lowlink = {}
+    scc_found = set()
+    scc_queue = []
+    i = 0  # Preorder counter
+    neighbors = {v: iter(G[v]) for v in G}
+    for source in G:
+        if source not in scc_found:
+            queue = [source]
+            while queue:
+                v = queue[-1]
+                if v not in preorder:
+                    i = i + 1
+                    preorder[v] = i
+                done = True
+                for w in neighbors[v]:
+                    if w not in preorder:
+                        queue.append(w)
+                        done = False
+                        break
+                if done:
+                    lowlink[v] = preorder[v]
+                    for w in G[v]:
+                        if w not in scc_found:
+                            if preorder[w] > preorder[v]:
+                                lowlink[v] = min([lowlink[v], lowlink[w]])
+                            else:
+                                lowlink[v] = min([lowlink[v], preorder[w]])
+                    queue.pop()
+                    if lowlink[v] == preorder[v]:
+                        scc = {v}
+                        while scc_queue and preorder[scc_queue[-1]] > preorder[v]:
+                            k = scc_queue.pop()
+                            scc.add(k)
+                        scc_found.update(scc)
+                        yield scc
+                    else:
+                        scc_queue.append(v)
+
+
+@not_implemented_for("undirected")
+@nx._dispatchable
+def kosaraju_strongly_connected_components(G, source=None):
+    """Generate nodes in strongly connected components of graph.
+
+    Parameters
+    ----------
+    G : NetworkX Graph
+        A directed graph.
+
+    Returns
+    -------
+    comp : generator of sets
+        A generator of sets of nodes, one for each strongly connected
+        component of G.
+
+    Raises
+    ------
+    NetworkXNotImplemented
+        If G is undirected.
+
+    Examples
+    --------
+    Generate a sorted list of strongly connected components, largest first.
+
+    >>> G = nx.cycle_graph(4, create_using=nx.DiGraph())
+    >>> nx.add_cycle(G, [10, 11, 12])
+    >>> [
+    ...     len(c)
+    ...     for c in sorted(
+    ...         nx.kosaraju_strongly_connected_components(G), key=len, reverse=True
+    ...     )
+    ... ]
+    [4, 3]
+
+    If you only want the largest component, it's more efficient to
+    use max instead of sort.
+
+    >>> largest = max(nx.kosaraju_strongly_connected_components(G), key=len)
+
+    See Also
+    --------
+    strongly_connected_components
+
+    Notes
+    -----
+    Uses Kosaraju's algorithm.
+
+    """
+    post = list(nx.dfs_postorder_nodes(G.reverse(copy=False), source=source))
+
+    seen = set()
+    while post:
+        r = post.pop()
+        if r in seen:
+            continue
+        c = nx.dfs_preorder_nodes(G, r)
+        new = {v for v in c if v not in seen}
+        seen.update(new)
+        yield new
+
+
+@not_implemented_for("undirected")
+@nx._dispatchable
+def number_strongly_connected_components(G):
+    """Returns number of strongly connected components in graph.
+
+    Parameters
+    ----------
+    G : NetworkX graph
+       A directed graph.
+
+    Returns
+    -------
+    n : integer
+       Number of strongly connected components
+
+    Raises
+    ------
+    NetworkXNotImplemented
+        If G is undirected.
+
+    Examples
+    --------
+    >>> G = nx.DiGraph(
+    ...     [(0, 1), (1, 2), (2, 0), (2, 3), (4, 5), (3, 4), (5, 6), (6, 3), (6, 7)]
+    ... )
+    >>> nx.number_strongly_connected_components(G)
+    3
+
+    See Also
+    --------
+    strongly_connected_components
+    number_connected_components
+    number_weakly_connected_components
+
+    Notes
+    -----
+    For directed graphs only.
+    """
+    return sum(1 for scc in strongly_connected_components(G))
+
+
+@not_implemented_for("undirected")
+@nx._dispatchable
+def is_strongly_connected(G):
+    """Test directed graph for strong connectivity.
+
+    A directed graph is strongly connected if and only if every vertex in
+    the graph is reachable from every other vertex.
+
+    Parameters
+    ----------
+    G : NetworkX Graph
+       A directed graph.
+
+    Returns
+    -------
+    connected : bool
+      True if the graph is strongly connected, False otherwise.
+
+    Examples
+    --------
+    >>> G = nx.DiGraph([(0, 1), (1, 2), (2, 3), (3, 0), (2, 4), (4, 2)])
+    >>> nx.is_strongly_connected(G)
+    True
+    >>> G.remove_edge(2, 3)
+    >>> nx.is_strongly_connected(G)
+    False
+
+    Raises
+    ------
+    NetworkXNotImplemented
+        If G is undirected.
+
+    See Also
+    --------
+    is_weakly_connected
+    is_semiconnected
+    is_connected
+    is_biconnected
+    strongly_connected_components
+
+    Notes
+    -----
+    For directed graphs only.
+    """
+    if len(G) == 0:
+        raise nx.NetworkXPointlessConcept(
+            """Connectivity is undefined for the null graph."""
+        )
+
+    return len(next(strongly_connected_components(G))) == len(G)
+
+
+@not_implemented_for("undirected")
+@nx._dispatchable(returns_graph=True)
+def condensation(G, scc=None):
+    """Returns the condensation of G.
+
+    The condensation of G is the graph with each of the strongly connected
+    components contracted into a single node.
+
+    Parameters
+    ----------
+    G : NetworkX DiGraph
+       A directed graph.
+
+    scc:  list or generator (optional, default=None)
+       Strongly connected components. If provided, the elements in
+       `scc` must partition the nodes in `G`. If not provided, it will be
+       calculated as scc=nx.strongly_connected_components(G).
+
+    Returns
+    -------
+    C : NetworkX DiGraph
+       The condensation graph C of G.  The node labels are integers
+       corresponding to the index of the component in the list of
+       strongly connected components of G.  C has a graph attribute named
+       'mapping' with a dictionary mapping the original nodes to the
+       nodes in C to which they belong.  Each node in C also has a node
+       attribute 'members' with the set of original nodes in G that
+       form the SCC that the node in C represents.
+
+    Raises
+    ------
+    NetworkXNotImplemented
+        If G is undirected.
+
+    Examples
+    --------
+    Contracting two sets of strongly connected nodes into two distinct SCC
+    using the barbell graph.
+
+    >>> G = nx.barbell_graph(4, 0)
+    >>> G.remove_edge(3, 4)
+    >>> G = nx.DiGraph(G)
+    >>> H = nx.condensation(G)
+    >>> H.nodes.data()
+    NodeDataView({0: {'members': {0, 1, 2, 3}}, 1: {'members': {4, 5, 6, 7}}})
+    >>> H.graph["mapping"]
+    {0: 0, 1: 0, 2: 0, 3: 0, 4: 1, 5: 1, 6: 1, 7: 1}
+
+    Contracting a complete graph into one single SCC.
+
+    >>> G = nx.complete_graph(7, create_using=nx.DiGraph)
+    >>> H = nx.condensation(G)
+    >>> H.nodes
+    NodeView((0,))
+    >>> H.nodes.data()
+    NodeDataView({0: {'members': {0, 1, 2, 3, 4, 5, 6}}})
+
+    Notes
+    -----
+    After contracting all strongly connected components to a single node,
+    the resulting graph is a directed acyclic graph.
+
+    """
+    if scc is None:
+        scc = nx.strongly_connected_components(G)
+    mapping = {}
+    members = {}
+    C = nx.DiGraph()
+    # Add mapping dict as graph attribute
+    C.graph["mapping"] = mapping
+    if len(G) == 0:
+        return C
+    for i, component in enumerate(scc):
+        members[i] = component
+        mapping.update((n, i) for n in component)
+    number_of_components = i + 1
+    C.add_nodes_from(range(number_of_components))
+    C.add_edges_from(
+        (mapping[u], mapping[v]) for u, v in G.edges() if mapping[u] != mapping[v]
+    )
+    # Add a list of members (ie original nodes) to each node (ie scc) in C.
+    nx.set_node_attributes(C, members, "members")
+    return C