Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
|
@ -0,0 +1,6 @@
|
|||
from .connected import *
|
||||
from .strongly_connected import *
|
||||
from .weakly_connected import *
|
||||
from .attracting import *
|
||||
from .biconnected import *
|
||||
from .semiconnected import *
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,111 @@
|
|||
"""Attracting components."""
|
||||
import networkx as nx
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = [
|
||||
"number_attracting_components",
|
||||
"attracting_components",
|
||||
"is_attracting_component",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def attracting_components(G):
|
||||
"""Generates the attracting components in `G`.
|
||||
|
||||
An attracting component in a directed graph `G` is a strongly connected
|
||||
component with the property that a random walker on the graph will never
|
||||
leave the component, once it enters the component.
|
||||
|
||||
The nodes in attracting components can also be thought of as recurrent
|
||||
nodes. If a random walker enters the attractor containing the node, then
|
||||
the node will be visited infinitely often.
|
||||
|
||||
To obtain induced subgraphs on each component use:
|
||||
``(G.subgraph(c).copy() for c in attracting_components(G))``
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph, MultiDiGraph
|
||||
The graph to be analyzed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
attractors : generator of sets
|
||||
A generator of sets of nodes, one for each attracting component of G.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is undirected.
|
||||
|
||||
See Also
|
||||
--------
|
||||
number_attracting_components
|
||||
is_attracting_component
|
||||
|
||||
"""
|
||||
scc = list(nx.strongly_connected_components(G))
|
||||
cG = nx.condensation(G, scc)
|
||||
for n in cG:
|
||||
if cG.out_degree(n) == 0:
|
||||
yield scc[n]
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def number_attracting_components(G):
|
||||
"""Returns the number of attracting components in `G`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph, MultiDiGraph
|
||||
The graph to be analyzed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
n : int
|
||||
The number of attracting components in G.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is undirected.
|
||||
|
||||
See Also
|
||||
--------
|
||||
attracting_components
|
||||
is_attracting_component
|
||||
|
||||
"""
|
||||
return sum(1 for ac in attracting_components(G))
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def is_attracting_component(G):
|
||||
"""Returns True if `G` consists of a single attracting component.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : DiGraph, MultiDiGraph
|
||||
The graph to be analyzed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
attracting : bool
|
||||
True if `G` has a single attracting component. Otherwise, False.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is undirected.
|
||||
|
||||
See Also
|
||||
--------
|
||||
attracting_components
|
||||
number_attracting_components
|
||||
|
||||
"""
|
||||
ac = list(attracting_components(G))
|
||||
if len(ac) == 1:
|
||||
return len(ac[0]) == len(G)
|
||||
return False
|
|
@ -0,0 +1,380 @@
|
|||
"""Biconnected components and articulation points."""
|
||||
from itertools import chain
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = [
|
||||
"biconnected_components",
|
||||
"biconnected_component_edges",
|
||||
"is_biconnected",
|
||||
"articulation_points",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def is_biconnected(G):
|
||||
"""Returns True if the graph is biconnected, False otherwise.
|
||||
|
||||
A graph is biconnected if, and only if, it cannot be disconnected by
|
||||
removing only one node (and all edges incident on that node). If
|
||||
removing a node increases the number of disconnected components
|
||||
in the graph, that node is called an articulation point, or cut
|
||||
vertex. A biconnected graph has no articulation points.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
An undirected graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
biconnected : bool
|
||||
True if the graph is biconnected, False otherwise.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is not undirected.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
False
|
||||
>>> G.add_edge(0, 3)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
True
|
||||
|
||||
See Also
|
||||
--------
|
||||
biconnected_components
|
||||
articulation_points
|
||||
biconnected_component_edges
|
||||
is_strongly_connected
|
||||
is_weakly_connected
|
||||
is_connected
|
||||
is_semiconnected
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm to find articulation points and biconnected
|
||||
components is implemented using a non-recursive depth-first-search
|
||||
(DFS) that keeps track of the highest level that back edges reach
|
||||
in the DFS tree. A node `n` is an articulation point if, and only
|
||||
if, there exists a subtree rooted at `n` such that there is no
|
||||
back edge from any successor of `n` that links to a predecessor of
|
||||
`n` in the DFS tree. By keeping track of all the edges traversed
|
||||
by the DFS we can obtain the biconnected components because all
|
||||
edges of a bicomponent will be traversed consecutively between
|
||||
articulation points.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Hopcroft, J.; Tarjan, R. (1973).
|
||||
"Efficient algorithms for graph manipulation".
|
||||
Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
|
||||
|
||||
"""
|
||||
bcc = list(biconnected_components(G))
|
||||
if len(bcc) == 1:
|
||||
return len(bcc[0]) == len(G)
|
||||
return False # Multiple bicomponents or No bicomponents (empty graph?)
|
||||
|
||||
|
||||
# if len(bcc) == 0: # No bicomponents (it could be an empty graph)
|
||||
# return False
|
||||
# return len(bcc[0]) == len(G)
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def biconnected_component_edges(G):
|
||||
"""Returns a generator of lists of edges, one list for each biconnected
|
||||
component of the input graph.
|
||||
|
||||
Biconnected components are maximal subgraphs such that the removal of a
|
||||
node (and all edges incident on that node) will not disconnect the
|
||||
subgraph. Note that nodes may be part of more than one biconnected
|
||||
component. Those nodes are articulation points, or cut vertices.
|
||||
However, each edge belongs to one, and only one, biconnected component.
|
||||
|
||||
Notice that by convention a dyad is considered a biconnected component.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
An undirected graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
edges : generator of lists
|
||||
Generator of lists of edges, one list for each bicomponent.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is not undirected.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.barbell_graph(4, 2)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
False
|
||||
>>> bicomponents_edges = list(nx.biconnected_component_edges(G))
|
||||
>>> len(bicomponents_edges)
|
||||
5
|
||||
>>> G.add_edge(2, 8)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
True
|
||||
>>> bicomponents_edges = list(nx.biconnected_component_edges(G))
|
||||
>>> len(bicomponents_edges)
|
||||
1
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_biconnected,
|
||||
biconnected_components,
|
||||
articulation_points,
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm to find articulation points and biconnected
|
||||
components is implemented using a non-recursive depth-first-search
|
||||
(DFS) that keeps track of the highest level that back edges reach
|
||||
in the DFS tree. A node `n` is an articulation point if, and only
|
||||
if, there exists a subtree rooted at `n` such that there is no
|
||||
back edge from any successor of `n` that links to a predecessor of
|
||||
`n` in the DFS tree. By keeping track of all the edges traversed
|
||||
by the DFS we can obtain the biconnected components because all
|
||||
edges of a bicomponent will be traversed consecutively between
|
||||
articulation points.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Hopcroft, J.; Tarjan, R. (1973).
|
||||
"Efficient algorithms for graph manipulation".
|
||||
Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
|
||||
|
||||
"""
|
||||
yield from _biconnected_dfs(G, components=True)
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def biconnected_components(G):
|
||||
"""Returns a generator of sets of nodes, one set for each biconnected
|
||||
component of the graph
|
||||
|
||||
Biconnected components are maximal subgraphs such that the removal of a
|
||||
node (and all edges incident on that node) will not disconnect the
|
||||
subgraph. Note that nodes may be part of more than one biconnected
|
||||
component. Those nodes are articulation points, or cut vertices. The
|
||||
removal of articulation points will increase the number of connected
|
||||
components of the graph.
|
||||
|
||||
Notice that by convention a dyad is considered a biconnected component.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
An undirected graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
nodes : generator
|
||||
Generator of sets of nodes, one set for each biconnected component.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is not undirected.
|
||||
|
||||
See Also
|
||||
--------
|
||||
k_components : this function is a special case where k=2
|
||||
bridge_components : similar to this function, but is defined using
|
||||
2-edge-connectivity instead of 2-node-connectivity.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.lollipop_graph(5, 1)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
False
|
||||
>>> bicomponents = list(nx.biconnected_components(G))
|
||||
>>> len(bicomponents)
|
||||
2
|
||||
>>> G.add_edge(0, 5)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
True
|
||||
>>> bicomponents = list(nx.biconnected_components(G))
|
||||
>>> len(bicomponents)
|
||||
1
|
||||
|
||||
You can generate a sorted list of biconnected components, largest
|
||||
first, using sort.
|
||||
|
||||
>>> G.remove_edge(0, 5)
|
||||
>>> [len(c) for c in sorted(nx.biconnected_components(G), key=len, reverse=True)]
|
||||
[5, 2]
|
||||
|
||||
If you only want the largest connected component, it's more
|
||||
efficient to use max instead of sort.
|
||||
|
||||
>>> Gc = max(nx.biconnected_components(G), key=len)
|
||||
|
||||
To create the components as subgraphs use:
|
||||
``(G.subgraph(c).copy() for c in biconnected_components(G))``
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_biconnected
|
||||
articulation_points
|
||||
biconnected_component_edges
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm to find articulation points and biconnected
|
||||
components is implemented using a non-recursive depth-first-search
|
||||
(DFS) that keeps track of the highest level that back edges reach
|
||||
in the DFS tree. A node `n` is an articulation point if, and only
|
||||
if, there exists a subtree rooted at `n` such that there is no
|
||||
back edge from any successor of `n` that links to a predecessor of
|
||||
`n` in the DFS tree. By keeping track of all the edges traversed
|
||||
by the DFS we can obtain the biconnected components because all
|
||||
edges of a bicomponent will be traversed consecutively between
|
||||
articulation points.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Hopcroft, J.; Tarjan, R. (1973).
|
||||
"Efficient algorithms for graph manipulation".
|
||||
Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
|
||||
|
||||
"""
|
||||
for comp in _biconnected_dfs(G, components=True):
|
||||
yield set(chain.from_iterable(comp))
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def articulation_points(G):
|
||||
"""Yield the articulation points, or cut vertices, of a graph.
|
||||
|
||||
An articulation point or cut vertex is any node whose removal (along with
|
||||
all its incident edges) increases the number of connected components of
|
||||
a graph. An undirected connected graph without articulation points is
|
||||
biconnected. Articulation points belong to more than one biconnected
|
||||
component of a graph.
|
||||
|
||||
Notice that by convention a dyad is considered a biconnected component.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
An undirected graph.
|
||||
|
||||
Yields
|
||||
------
|
||||
node
|
||||
An articulation point in the graph.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is not undirected.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
>>> G = nx.barbell_graph(4, 2)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
False
|
||||
>>> len(list(nx.articulation_points(G)))
|
||||
4
|
||||
>>> G.add_edge(2, 8)
|
||||
>>> print(nx.is_biconnected(G))
|
||||
True
|
||||
>>> len(list(nx.articulation_points(G)))
|
||||
0
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_biconnected
|
||||
biconnected_components
|
||||
biconnected_component_edges
|
||||
|
||||
Notes
|
||||
-----
|
||||
The algorithm to find articulation points and biconnected
|
||||
components is implemented using a non-recursive depth-first-search
|
||||
(DFS) that keeps track of the highest level that back edges reach
|
||||
in the DFS tree. A node `n` is an articulation point if, and only
|
||||
if, there exists a subtree rooted at `n` such that there is no
|
||||
back edge from any successor of `n` that links to a predecessor of
|
||||
`n` in the DFS tree. By keeping track of all the edges traversed
|
||||
by the DFS we can obtain the biconnected components because all
|
||||
edges of a bicomponent will be traversed consecutively between
|
||||
articulation points.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Hopcroft, J.; Tarjan, R. (1973).
|
||||
"Efficient algorithms for graph manipulation".
|
||||
Communications of the ACM 16: 372–378. doi:10.1145/362248.362272
|
||||
|
||||
"""
|
||||
seen = set()
|
||||
for articulation in _biconnected_dfs(G, components=False):
|
||||
if articulation not in seen:
|
||||
seen.add(articulation)
|
||||
yield articulation
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def _biconnected_dfs(G, components=True):
|
||||
# depth-first search algorithm to generate articulation points
|
||||
# and biconnected components
|
||||
visited = set()
|
||||
for start in G:
|
||||
if start in visited:
|
||||
continue
|
||||
discovery = {start: 0} # time of first discovery of node during search
|
||||
low = {start: 0}
|
||||
root_children = 0
|
||||
visited.add(start)
|
||||
edge_stack = []
|
||||
stack = [(start, start, iter(G[start]))]
|
||||
while stack:
|
||||
grandparent, parent, children = stack[-1]
|
||||
try:
|
||||
child = next(children)
|
||||
if grandparent == child:
|
||||
continue
|
||||
if child in visited:
|
||||
if discovery[child] <= discovery[parent]: # back edge
|
||||
low[parent] = min(low[parent], discovery[child])
|
||||
if components:
|
||||
edge_stack.append((parent, child))
|
||||
else:
|
||||
low[child] = discovery[child] = len(discovery)
|
||||
visited.add(child)
|
||||
stack.append((parent, child, iter(G[child])))
|
||||
if components:
|
||||
edge_stack.append((parent, child))
|
||||
except StopIteration:
|
||||
stack.pop()
|
||||
if len(stack) > 1:
|
||||
if low[parent] >= discovery[grandparent]:
|
||||
if components:
|
||||
ind = edge_stack.index((grandparent, parent))
|
||||
yield edge_stack[ind:]
|
||||
edge_stack = edge_stack[:ind]
|
||||
else:
|
||||
yield grandparent
|
||||
low[grandparent] = min(low[parent], low[grandparent])
|
||||
elif stack: # length 1 so grandparent is root
|
||||
root_children += 1
|
||||
if components:
|
||||
ind = edge_stack.index((grandparent, parent))
|
||||
yield edge_stack[ind:]
|
||||
if not components:
|
||||
# root node is articulation point if it has more than 1 child
|
||||
if root_children > 1:
|
||||
yield start
|
|
@ -0,0 +1,187 @@
|
|||
"""Connected components."""
|
||||
import networkx as nx
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
from ...utils import arbitrary_element
|
||||
|
||||
__all__ = [
|
||||
"number_connected_components",
|
||||
"connected_components",
|
||||
"is_connected",
|
||||
"node_connected_component",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def connected_components(G):
|
||||
"""Generate connected components.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
An undirected graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
comp : generator of sets
|
||||
A generator of sets of nodes, one for each component of G.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is directed.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Generate a sorted list of connected components, largest first.
|
||||
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> nx.add_path(G, [10, 11, 12])
|
||||
>>> [len(c) for c in sorted(nx.connected_components(G), key=len, reverse=True)]
|
||||
[4, 3]
|
||||
|
||||
If you only want the largest connected component, it's more
|
||||
efficient to use max instead of sort.
|
||||
|
||||
>>> largest_cc = max(nx.connected_components(G), key=len)
|
||||
|
||||
To create the induced subgraph of each component use:
|
||||
|
||||
>>> S = [G.subgraph(c).copy() for c in nx.connected_components(G)]
|
||||
|
||||
See Also
|
||||
--------
|
||||
strongly_connected_components
|
||||
weakly_connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
For undirected graphs only.
|
||||
|
||||
"""
|
||||
seen = set()
|
||||
for v in G:
|
||||
if v not in seen:
|
||||
c = _plain_bfs(G, v)
|
||||
seen.update(c)
|
||||
yield c
|
||||
|
||||
|
||||
def number_connected_components(G):
|
||||
"""Returns the number of connected components.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
An undirected graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
n : integer
|
||||
Number of connected components
|
||||
|
||||
See Also
|
||||
--------
|
||||
connected_components
|
||||
number_weakly_connected_components
|
||||
number_strongly_connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
For undirected graphs only.
|
||||
|
||||
"""
|
||||
return sum(1 for cc in connected_components(G))
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def is_connected(G):
|
||||
"""Returns True if the graph is connected, False otherwise.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
An undirected graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
connected : bool
|
||||
True if the graph is connected, false otherwise.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is directed.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4)
|
||||
>>> print(nx.is_connected(G))
|
||||
True
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_strongly_connected
|
||||
is_weakly_connected
|
||||
is_semiconnected
|
||||
is_biconnected
|
||||
connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
For undirected graphs only.
|
||||
|
||||
"""
|
||||
if len(G) == 0:
|
||||
raise nx.NetworkXPointlessConcept(
|
||||
"Connectivity is undefined ", "for the null graph."
|
||||
)
|
||||
return sum(1 for node in _plain_bfs(G, arbitrary_element(G))) == len(G)
|
||||
|
||||
|
||||
@not_implemented_for("directed")
|
||||
def node_connected_component(G, n):
|
||||
"""Returns the set of nodes in the component of graph containing node n.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
An undirected graph.
|
||||
|
||||
n : node label
|
||||
A node in G
|
||||
|
||||
Returns
|
||||
-------
|
||||
comp : set
|
||||
A set of nodes in the component of G containing node n.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is directed.
|
||||
|
||||
See Also
|
||||
--------
|
||||
connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
For undirected graphs only.
|
||||
|
||||
"""
|
||||
return _plain_bfs(G, n)
|
||||
|
||||
|
||||
def _plain_bfs(G, source):
|
||||
"""A fast BFS node generator"""
|
||||
G_adj = G.adj
|
||||
seen = set()
|
||||
nextlevel = {source}
|
||||
while nextlevel:
|
||||
thislevel = nextlevel
|
||||
nextlevel = set()
|
||||
for v in thislevel:
|
||||
if v not in seen:
|
||||
seen.add(v)
|
||||
nextlevel.update(G_adj[v])
|
||||
return seen
|
|
@ -0,0 +1,64 @@
|
|||
"""Semiconnectedness."""
|
||||
import networkx as nx
|
||||
from networkx.utils import not_implemented_for, pairwise
|
||||
|
||||
__all__ = ["is_semiconnected"]
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def is_semiconnected(G, topo_order=None):
|
||||
"""Returns True if the graph is semiconnected, False otherwise.
|
||||
|
||||
A graph is semiconnected if, and only if, for any pair of nodes, either one
|
||||
is reachable from the other, or they are mutually reachable.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
A directed graph.
|
||||
|
||||
topo_order: list or tuple, optional
|
||||
A topological order for G (if None, the function will compute one)
|
||||
|
||||
Returns
|
||||
-------
|
||||
semiconnected : bool
|
||||
True if the graph is semiconnected, False otherwise.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If the input graph is undirected.
|
||||
|
||||
NetworkXPointlessConcept
|
||||
If the graph is empty.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> G = nx.path_graph(4, create_using=nx.DiGraph())
|
||||
>>> print(nx.is_semiconnected(G))
|
||||
True
|
||||
>>> G = nx.DiGraph([(1, 2), (3, 2)])
|
||||
>>> print(nx.is_semiconnected(G))
|
||||
False
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_strongly_connected
|
||||
is_weakly_connected
|
||||
is_connected
|
||||
is_biconnected
|
||||
"""
|
||||
if len(G) == 0:
|
||||
raise nx.NetworkXPointlessConcept(
|
||||
"Connectivity is undefined for the null graph."
|
||||
)
|
||||
|
||||
if not nx.is_weakly_connected(G):
|
||||
return False
|
||||
|
||||
G = nx.condensation(G)
|
||||
if topo_order is None:
|
||||
topo_order = nx.topological_sort(G)
|
||||
|
||||
return all(G.has_edge(u, v) for u, v in pairwise(topo_order))
|
|
@ -0,0 +1,398 @@
|
|||
"""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",
|
||||
"strongly_connected_components_recursive",
|
||||
"kosaraju_strongly_connected_components",
|
||||
"condensation",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
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
|
||||
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 G[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")
|
||||
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 genrator 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}
|
||||
yield new
|
||||
seen.update(new)
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def strongly_connected_components_recursive(G):
|
||||
"""Generate nodes in strongly connected components of graph.
|
||||
|
||||
Recursive version of algorithm.
|
||||
|
||||
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_recursive(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_recursive(G), key=len)
|
||||
|
||||
To create the induced subgraph of the components use:
|
||||
>>> S = [G.subgraph(c).copy() for c in nx.weakly_connected_components(G)]
|
||||
|
||||
See Also
|
||||
--------
|
||||
connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
Uses Tarjan's algorithm[1]_ with Nuutila's modifications[2]_.
|
||||
|
||||
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)..
|
||||
|
||||
"""
|
||||
|
||||
def visit(v, cnt):
|
||||
root[v] = cnt
|
||||
visited[v] = cnt
|
||||
cnt += 1
|
||||
stack.append(v)
|
||||
for w in G[v]:
|
||||
if w not in visited:
|
||||
yield from visit(w, cnt)
|
||||
if w not in component:
|
||||
root[v] = min(root[v], root[w])
|
||||
if root[v] == visited[v]:
|
||||
component[v] = root[v]
|
||||
tmpc = {v} # hold nodes in this component
|
||||
while stack[-1] != v:
|
||||
w = stack.pop()
|
||||
component[w] = root[v]
|
||||
tmpc.add(w)
|
||||
stack.remove(v)
|
||||
yield tmpc
|
||||
|
||||
visited = {}
|
||||
component = {}
|
||||
root = {}
|
||||
cnt = 0
|
||||
stack = []
|
||||
for source in G:
|
||||
if source not in visited:
|
||||
yield from visit(source, cnt)
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
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.
|
||||
|
||||
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")
|
||||
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.
|
||||
|
||||
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(list(strongly_connected_components(G))[0]) == len(G)
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
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.
|
||||
|
||||
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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,68 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx import NetworkXNotImplemented
|
||||
|
||||
|
||||
class TestAttractingComponents:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.G1 = nx.DiGraph()
|
||||
cls.G1.add_edges_from(
|
||||
[
|
||||
(5, 11),
|
||||
(11, 2),
|
||||
(11, 9),
|
||||
(11, 10),
|
||||
(7, 11),
|
||||
(7, 8),
|
||||
(8, 9),
|
||||
(3, 8),
|
||||
(3, 10),
|
||||
]
|
||||
)
|
||||
cls.G2 = nx.DiGraph()
|
||||
cls.G2.add_edges_from([(0, 1), (0, 2), (1, 1), (1, 2), (2, 1)])
|
||||
|
||||
cls.G3 = nx.DiGraph()
|
||||
cls.G3.add_edges_from([(0, 1), (1, 2), (2, 1), (0, 3), (3, 4), (4, 3)])
|
||||
|
||||
cls.G4 = nx.DiGraph()
|
||||
|
||||
def test_attracting_components(self):
|
||||
ac = list(nx.attracting_components(self.G1))
|
||||
assert {2} in ac
|
||||
assert {9} in ac
|
||||
assert {10} in ac
|
||||
|
||||
ac = list(nx.attracting_components(self.G2))
|
||||
ac = [tuple(sorted(x)) for x in ac]
|
||||
assert ac == [(1, 2)]
|
||||
|
||||
ac = list(nx.attracting_components(self.G3))
|
||||
ac = [tuple(sorted(x)) for x in ac]
|
||||
assert (1, 2) in ac
|
||||
assert (3, 4) in ac
|
||||
assert len(ac) == 2
|
||||
|
||||
ac = list(nx.attracting_components(self.G4))
|
||||
assert ac == []
|
||||
|
||||
def test_number_attacting_components(self):
|
||||
assert nx.number_attracting_components(self.G1) == 3
|
||||
assert nx.number_attracting_components(self.G2) == 1
|
||||
assert nx.number_attracting_components(self.G3) == 2
|
||||
assert nx.number_attracting_components(self.G4) == 0
|
||||
|
||||
def test_is_attracting_component(self):
|
||||
assert not nx.is_attracting_component(self.G1)
|
||||
assert not nx.is_attracting_component(self.G2)
|
||||
assert not nx.is_attracting_component(self.G3)
|
||||
g2 = self.G3.subgraph([1, 2])
|
||||
assert nx.is_attracting_component(g2)
|
||||
assert not nx.is_attracting_component(self.G4)
|
||||
|
||||
def test_connected_raise(self):
|
||||
G = nx.Graph()
|
||||
pytest.raises(NetworkXNotImplemented, nx.attracting_components, G)
|
||||
pytest.raises(NetworkXNotImplemented, nx.number_attracting_components, G)
|
||||
pytest.raises(NetworkXNotImplemented, nx.is_attracting_component, G)
|
|
@ -0,0 +1,244 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx import NetworkXNotImplemented
|
||||
|
||||
|
||||
def assert_components_edges_equal(x, y):
|
||||
sx = {frozenset([frozenset(e) for e in c]) for c in x}
|
||||
sy = {frozenset([frozenset(e) for e in c]) for c in y}
|
||||
assert sx == sy
|
||||
|
||||
|
||||
def assert_components_equal(x, y):
|
||||
sx = {frozenset(c) for c in x}
|
||||
sy = {frozenset(c) for c in y}
|
||||
assert sx == sy
|
||||
|
||||
|
||||
def test_barbell():
|
||||
G = nx.barbell_graph(8, 4)
|
||||
nx.add_path(G, [7, 20, 21, 22])
|
||||
nx.add_cycle(G, [22, 23, 24, 25])
|
||||
pts = set(nx.articulation_points(G))
|
||||
assert pts == {7, 8, 9, 10, 11, 12, 20, 21, 22}
|
||||
|
||||
answer = [
|
||||
{12, 13, 14, 15, 16, 17, 18, 19},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7},
|
||||
{22, 23, 24, 25},
|
||||
{11, 12},
|
||||
{10, 11},
|
||||
{9, 10},
|
||||
{8, 9},
|
||||
{7, 8},
|
||||
{21, 22},
|
||||
{20, 21},
|
||||
{7, 20},
|
||||
]
|
||||
assert_components_equal(list(nx.biconnected_components(G)), answer)
|
||||
|
||||
G.add_edge(2, 17)
|
||||
pts = set(nx.articulation_points(G))
|
||||
assert pts == {7, 20, 21, 22}
|
||||
|
||||
|
||||
def test_articulation_points_repetitions():
|
||||
G = nx.Graph()
|
||||
G.add_edges_from([(0, 1), (1, 2), (1, 3)])
|
||||
assert list(nx.articulation_points(G)) == [1]
|
||||
|
||||
|
||||
def test_articulation_points_cycle():
|
||||
G = nx.cycle_graph(3)
|
||||
nx.add_cycle(G, [1, 3, 4])
|
||||
pts = set(nx.articulation_points(G))
|
||||
assert pts == {1}
|
||||
|
||||
|
||||
def test_is_biconnected():
|
||||
G = nx.cycle_graph(3)
|
||||
assert nx.is_biconnected(G)
|
||||
nx.add_cycle(G, [1, 3, 4])
|
||||
assert not nx.is_biconnected(G)
|
||||
|
||||
|
||||
def test_empty_is_biconnected():
|
||||
G = nx.empty_graph(5)
|
||||
assert not nx.is_biconnected(G)
|
||||
G.add_edge(0, 1)
|
||||
assert not nx.is_biconnected(G)
|
||||
|
||||
|
||||
def test_biconnected_components_cycle():
|
||||
G = nx.cycle_graph(3)
|
||||
nx.add_cycle(G, [1, 3, 4])
|
||||
answer = [{0, 1, 2}, {1, 3, 4}]
|
||||
assert_components_equal(list(nx.biconnected_components(G)), answer)
|
||||
|
||||
|
||||
def test_biconnected_components1():
|
||||
# graph example from
|
||||
# http://www.ibluemojo.com/school/articul_algorithm.html
|
||||
edges = [
|
||||
(0, 1),
|
||||
(0, 5),
|
||||
(0, 6),
|
||||
(0, 14),
|
||||
(1, 5),
|
||||
(1, 6),
|
||||
(1, 14),
|
||||
(2, 4),
|
||||
(2, 10),
|
||||
(3, 4),
|
||||
(3, 15),
|
||||
(4, 6),
|
||||
(4, 7),
|
||||
(4, 10),
|
||||
(5, 14),
|
||||
(6, 14),
|
||||
(7, 9),
|
||||
(8, 9),
|
||||
(8, 12),
|
||||
(8, 13),
|
||||
(10, 15),
|
||||
(11, 12),
|
||||
(11, 13),
|
||||
(12, 13),
|
||||
]
|
||||
G = nx.Graph(edges)
|
||||
pts = set(nx.articulation_points(G))
|
||||
assert pts == {4, 6, 7, 8, 9}
|
||||
comps = list(nx.biconnected_component_edges(G))
|
||||
answer = [
|
||||
[(3, 4), (15, 3), (10, 15), (10, 4), (2, 10), (4, 2)],
|
||||
[(13, 12), (13, 8), (11, 13), (12, 11), (8, 12)],
|
||||
[(9, 8)],
|
||||
[(7, 9)],
|
||||
[(4, 7)],
|
||||
[(6, 4)],
|
||||
[(14, 0), (5, 1), (5, 0), (14, 5), (14, 1), (6, 14), (6, 0), (1, 6), (0, 1)],
|
||||
]
|
||||
assert_components_edges_equal(comps, answer)
|
||||
|
||||
|
||||
def test_biconnected_components2():
|
||||
G = nx.Graph()
|
||||
nx.add_cycle(G, "ABC")
|
||||
nx.add_cycle(G, "CDE")
|
||||
nx.add_cycle(G, "FIJHG")
|
||||
nx.add_cycle(G, "GIJ")
|
||||
G.add_edge("E", "G")
|
||||
comps = list(nx.biconnected_component_edges(G))
|
||||
answer = [
|
||||
[
|
||||
tuple("GF"),
|
||||
tuple("FI"),
|
||||
tuple("IG"),
|
||||
tuple("IJ"),
|
||||
tuple("JG"),
|
||||
tuple("JH"),
|
||||
tuple("HG"),
|
||||
],
|
||||
[tuple("EG")],
|
||||
[tuple("CD"), tuple("DE"), tuple("CE")],
|
||||
[tuple("AB"), tuple("BC"), tuple("AC")],
|
||||
]
|
||||
assert_components_edges_equal(comps, answer)
|
||||
|
||||
|
||||
def test_biconnected_davis():
|
||||
D = nx.davis_southern_women_graph()
|
||||
bcc = list(nx.biconnected_components(D))[0]
|
||||
assert set(D) == bcc # All nodes in a giant bicomponent
|
||||
# So no articulation points
|
||||
assert len(list(nx.articulation_points(D))) == 0
|
||||
|
||||
|
||||
def test_biconnected_karate():
|
||||
K = nx.karate_club_graph()
|
||||
answer = [
|
||||
{
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
32,
|
||||
33,
|
||||
},
|
||||
{0, 4, 5, 6, 10, 16},
|
||||
{0, 11},
|
||||
]
|
||||
bcc = list(nx.biconnected_components(K))
|
||||
assert_components_equal(bcc, answer)
|
||||
assert set(nx.articulation_points(K)) == {0}
|
||||
|
||||
|
||||
def test_biconnected_eppstein():
|
||||
# tests from http://www.ics.uci.edu/~eppstein/PADS/Biconnectivity.py
|
||||
G1 = nx.Graph(
|
||||
{
|
||||
0: [1, 2, 5],
|
||||
1: [0, 5],
|
||||
2: [0, 3, 4],
|
||||
3: [2, 4, 5, 6],
|
||||
4: [2, 3, 5, 6],
|
||||
5: [0, 1, 3, 4],
|
||||
6: [3, 4],
|
||||
}
|
||||
)
|
||||
G2 = nx.Graph(
|
||||
{
|
||||
0: [2, 5],
|
||||
1: [3, 8],
|
||||
2: [0, 3, 5],
|
||||
3: [1, 2, 6, 8],
|
||||
4: [7],
|
||||
5: [0, 2],
|
||||
6: [3, 8],
|
||||
7: [4],
|
||||
8: [1, 3, 6],
|
||||
}
|
||||
)
|
||||
assert nx.is_biconnected(G1)
|
||||
assert not nx.is_biconnected(G2)
|
||||
answer_G2 = [{1, 3, 6, 8}, {0, 2, 5}, {2, 3}, {4, 7}]
|
||||
bcc = list(nx.biconnected_components(G2))
|
||||
assert_components_equal(bcc, answer_G2)
|
||||
|
||||
|
||||
def test_null_graph():
|
||||
G = nx.Graph()
|
||||
assert not nx.is_biconnected(G)
|
||||
assert list(nx.biconnected_components(G)) == []
|
||||
assert list(nx.biconnected_component_edges(G)) == []
|
||||
assert list(nx.articulation_points(G)) == []
|
||||
|
||||
|
||||
def test_connected_raise():
|
||||
DG = nx.DiGraph()
|
||||
pytest.raises(NetworkXNotImplemented, nx.biconnected_components, DG)
|
||||
pytest.raises(NetworkXNotImplemented, nx.biconnected_component_edges, DG)
|
||||
pytest.raises(NetworkXNotImplemented, nx.articulation_points, DG)
|
||||
pytest.raises(NetworkXNotImplemented, nx.is_biconnected, DG)
|
|
@ -0,0 +1,111 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx import convert_node_labels_to_integers as cnlti
|
||||
from networkx import NetworkXNotImplemented
|
||||
|
||||
|
||||
class TestConnected:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
G1 = cnlti(nx.grid_2d_graph(2, 2), first_label=0, ordering="sorted")
|
||||
G2 = cnlti(nx.lollipop_graph(3, 3), first_label=4, ordering="sorted")
|
||||
G3 = cnlti(nx.house_graph(), first_label=10, ordering="sorted")
|
||||
cls.G = nx.union(G1, G2)
|
||||
cls.G = nx.union(cls.G, G3)
|
||||
cls.DG = nx.DiGraph([(1, 2), (1, 3), (2, 3)])
|
||||
cls.grid = cnlti(nx.grid_2d_graph(4, 4), first_label=1)
|
||||
|
||||
cls.gc = []
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2),
|
||||
(2, 3),
|
||||
(2, 8),
|
||||
(3, 4),
|
||||
(3, 7),
|
||||
(4, 5),
|
||||
(5, 3),
|
||||
(5, 6),
|
||||
(7, 4),
|
||||
(7, 6),
|
||||
(8, 1),
|
||||
(8, 7),
|
||||
]
|
||||
)
|
||||
C = [[3, 4, 5, 7], [1, 2, 8], [6]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from([(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)])
|
||||
C = [[2, 3, 4], [1]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from([(1, 2), (2, 3), (3, 2), (2, 1)])
|
||||
C = [[1, 2, 3]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
# Eppstein's tests
|
||||
G = nx.DiGraph({0: [1], 1: [2, 3], 2: [4, 5], 3: [4, 5], 4: [6], 5: [], 6: []})
|
||||
C = [[0], [1], [2], [3], [4], [5], [6]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph({0: [1], 1: [2, 3, 4], 2: [0, 3], 3: [4], 4: [3]})
|
||||
C = [[0, 1, 2], [3, 4]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph()
|
||||
C = []
|
||||
cls.gc.append((G, C))
|
||||
|
||||
def test_connected_components(self):
|
||||
cc = nx.connected_components
|
||||
G = self.G
|
||||
C = {
|
||||
frozenset([0, 1, 2, 3]),
|
||||
frozenset([4, 5, 6, 7, 8, 9]),
|
||||
frozenset([10, 11, 12, 13, 14]),
|
||||
}
|
||||
assert {frozenset(g) for g in cc(G)} == C
|
||||
|
||||
def test_number_connected_components(self):
|
||||
ncc = nx.number_connected_components
|
||||
assert ncc(self.G) == 3
|
||||
|
||||
def test_number_connected_components2(self):
|
||||
ncc = nx.number_connected_components
|
||||
assert ncc(self.grid) == 1
|
||||
|
||||
def test_connected_components2(self):
|
||||
cc = nx.connected_components
|
||||
G = self.grid
|
||||
C = {frozenset([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])}
|
||||
assert {frozenset(g) for g in cc(G)} == C
|
||||
|
||||
def test_node_connected_components(self):
|
||||
ncc = nx.node_connected_component
|
||||
G = self.grid
|
||||
C = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
||||
assert ncc(G, 1) == C
|
||||
|
||||
def test_is_connected(self):
|
||||
assert nx.is_connected(self.grid)
|
||||
G = nx.Graph()
|
||||
G.add_nodes_from([1, 2])
|
||||
assert not nx.is_connected(G)
|
||||
|
||||
def test_connected_raise(self):
|
||||
pytest.raises(NetworkXNotImplemented, nx.connected_components, self.DG)
|
||||
pytest.raises(NetworkXNotImplemented, nx.number_connected_components, self.DG)
|
||||
pytest.raises(NetworkXNotImplemented, nx.node_connected_component, self.DG, 1)
|
||||
pytest.raises(NetworkXNotImplemented, nx.is_connected, self.DG)
|
||||
pytest.raises(nx.NetworkXPointlessConcept, nx.is_connected, nx.Graph())
|
||||
|
||||
def test_connected_mutability(self):
|
||||
G = self.grid
|
||||
seen = set()
|
||||
for component in nx.connected_components(G):
|
||||
assert len(seen & component) == 0
|
||||
seen.update(component)
|
||||
component.clear()
|
|
@ -0,0 +1,53 @@
|
|||
from itertools import chain
|
||||
import networkx as nx
|
||||
import pytest
|
||||
|
||||
|
||||
class TestIsSemiconnected:
|
||||
def test_undirected(self):
|
||||
pytest.raises(nx.NetworkXNotImplemented, nx.is_semiconnected, nx.Graph())
|
||||
pytest.raises(nx.NetworkXNotImplemented, nx.is_semiconnected, nx.MultiGraph())
|
||||
|
||||
def test_empty(self):
|
||||
pytest.raises(nx.NetworkXPointlessConcept, nx.is_semiconnected, nx.DiGraph())
|
||||
pytest.raises(
|
||||
nx.NetworkXPointlessConcept, nx.is_semiconnected, nx.MultiDiGraph()
|
||||
)
|
||||
|
||||
def test_single_node_graph(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node(0)
|
||||
assert nx.is_semiconnected(G)
|
||||
|
||||
def test_path(self):
|
||||
G = nx.path_graph(100, create_using=nx.DiGraph())
|
||||
assert nx.is_semiconnected(G)
|
||||
G.add_edge(100, 99)
|
||||
assert not nx.is_semiconnected(G)
|
||||
|
||||
def test_cycle(self):
|
||||
G = nx.cycle_graph(100, create_using=nx.DiGraph())
|
||||
assert nx.is_semiconnected(G)
|
||||
G = nx.path_graph(100, create_using=nx.DiGraph())
|
||||
G.add_edge(0, 99)
|
||||
assert nx.is_semiconnected(G)
|
||||
|
||||
def test_tree(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from(
|
||||
chain.from_iterable([(i, 2 * i + 1), (i, 2 * i + 2)] for i in range(100))
|
||||
)
|
||||
assert not nx.is_semiconnected(G)
|
||||
|
||||
def test_dumbbell(self):
|
||||
G = nx.cycle_graph(100, create_using=nx.DiGraph())
|
||||
G.add_edges_from((i + 100, (i + 1) % 100 + 100) for i in range(100))
|
||||
assert not nx.is_semiconnected(G) # G is disconnected.
|
||||
G.add_edge(100, 99)
|
||||
assert nx.is_semiconnected(G)
|
||||
|
||||
def test_alternating_path(self):
|
||||
G = nx.DiGraph(
|
||||
chain.from_iterable([(i, i - 1), (i, i + 1)] for i in range(0, 100, 2))
|
||||
)
|
||||
assert not nx.is_semiconnected(G)
|
|
@ -0,0 +1,216 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx import NetworkXNotImplemented
|
||||
|
||||
|
||||
class TestStronglyConnected:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.gc = []
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2),
|
||||
(2, 3),
|
||||
(2, 8),
|
||||
(3, 4),
|
||||
(3, 7),
|
||||
(4, 5),
|
||||
(5, 3),
|
||||
(5, 6),
|
||||
(7, 4),
|
||||
(7, 6),
|
||||
(8, 1),
|
||||
(8, 7),
|
||||
]
|
||||
)
|
||||
C = {frozenset([3, 4, 5, 7]), frozenset([1, 2, 8]), frozenset([6])}
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from([(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)])
|
||||
C = {frozenset([2, 3, 4]), frozenset([1])}
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from([(1, 2), (2, 3), (3, 2), (2, 1)])
|
||||
C = {frozenset([1, 2, 3])}
|
||||
cls.gc.append((G, C))
|
||||
|
||||
# Eppstein's tests
|
||||
G = nx.DiGraph({0: [1], 1: [2, 3], 2: [4, 5], 3: [4, 5], 4: [6], 5: [], 6: []})
|
||||
C = {
|
||||
frozenset([0]),
|
||||
frozenset([1]),
|
||||
frozenset([2]),
|
||||
frozenset([3]),
|
||||
frozenset([4]),
|
||||
frozenset([5]),
|
||||
frozenset([6]),
|
||||
}
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph({0: [1], 1: [2, 3, 4], 2: [0, 3], 3: [4], 4: [3]})
|
||||
C = {frozenset([0, 1, 2]), frozenset([3, 4])}
|
||||
cls.gc.append((G, C))
|
||||
|
||||
def test_tarjan(self):
|
||||
scc = nx.strongly_connected_components
|
||||
for G, C in self.gc:
|
||||
assert {frozenset(g) for g in scc(G)} == C
|
||||
|
||||
def test_tarjan_recursive(self):
|
||||
scc = nx.strongly_connected_components_recursive
|
||||
for G, C in self.gc:
|
||||
assert {frozenset(g) for g in scc(G)} == C
|
||||
|
||||
def test_kosaraju(self):
|
||||
scc = nx.kosaraju_strongly_connected_components
|
||||
for G, C in self.gc:
|
||||
assert {frozenset(g) for g in scc(G)} == C
|
||||
|
||||
def test_number_strongly_connected_components(self):
|
||||
ncc = nx.number_strongly_connected_components
|
||||
for G, C in self.gc:
|
||||
assert ncc(G) == len(C)
|
||||
|
||||
def test_is_strongly_connected(self):
|
||||
for G, C in self.gc:
|
||||
if len(C) == 1:
|
||||
assert nx.is_strongly_connected(G)
|
||||
else:
|
||||
assert not nx.is_strongly_connected(G)
|
||||
|
||||
def test_contract_scc1(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2),
|
||||
(2, 3),
|
||||
(2, 11),
|
||||
(2, 12),
|
||||
(3, 4),
|
||||
(4, 3),
|
||||
(4, 5),
|
||||
(5, 6),
|
||||
(6, 5),
|
||||
(6, 7),
|
||||
(7, 8),
|
||||
(7, 9),
|
||||
(7, 10),
|
||||
(8, 9),
|
||||
(9, 7),
|
||||
(10, 6),
|
||||
(11, 2),
|
||||
(11, 4),
|
||||
(11, 6),
|
||||
(12, 6),
|
||||
(12, 11),
|
||||
]
|
||||
)
|
||||
scc = list(nx.strongly_connected_components(G))
|
||||
cG = nx.condensation(G, scc)
|
||||
# DAG
|
||||
assert nx.is_directed_acyclic_graph(cG)
|
||||
# nodes
|
||||
assert sorted(cG.nodes()) == [0, 1, 2, 3]
|
||||
# edges
|
||||
mapping = {}
|
||||
for i, component in enumerate(scc):
|
||||
for n in component:
|
||||
mapping[n] = i
|
||||
edge = (mapping[2], mapping[3])
|
||||
assert cG.has_edge(*edge)
|
||||
edge = (mapping[2], mapping[5])
|
||||
assert cG.has_edge(*edge)
|
||||
edge = (mapping[3], mapping[5])
|
||||
assert cG.has_edge(*edge)
|
||||
|
||||
def test_contract_scc_isolate(self):
|
||||
# Bug found and fixed in [1687].
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(1, 2)
|
||||
G.add_edge(2, 1)
|
||||
scc = list(nx.strongly_connected_components(G))
|
||||
cG = nx.condensation(G, scc)
|
||||
assert list(cG.nodes()) == [0]
|
||||
assert list(cG.edges()) == []
|
||||
|
||||
def test_contract_scc_edge(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(1, 2)
|
||||
G.add_edge(2, 1)
|
||||
G.add_edge(2, 3)
|
||||
G.add_edge(3, 4)
|
||||
G.add_edge(4, 3)
|
||||
scc = list(nx.strongly_connected_components(G))
|
||||
cG = nx.condensation(G, scc)
|
||||
assert sorted(cG.nodes()) == [0, 1]
|
||||
if 1 in scc[0]:
|
||||
edge = (0, 1)
|
||||
else:
|
||||
edge = (1, 0)
|
||||
assert list(cG.edges()) == [edge]
|
||||
|
||||
def test_condensation_mapping_and_members(self):
|
||||
G, C = self.gc[1]
|
||||
C = sorted(C, key=len, reverse=True)
|
||||
cG = nx.condensation(G)
|
||||
mapping = cG.graph["mapping"]
|
||||
assert all(n in G for n in mapping)
|
||||
assert all(0 == cN for n, cN in mapping.items() if n in C[0])
|
||||
assert all(1 == cN for n, cN in mapping.items() if n in C[1])
|
||||
for n, d in cG.nodes(data=True):
|
||||
assert set(C[n]) == cG.nodes[n]["members"]
|
||||
|
||||
def test_null_graph(self):
|
||||
G = nx.DiGraph()
|
||||
assert list(nx.strongly_connected_components(G)) == []
|
||||
assert list(nx.kosaraju_strongly_connected_components(G)) == []
|
||||
assert list(nx.strongly_connected_components_recursive(G)) == []
|
||||
assert len(nx.condensation(G)) == 0
|
||||
pytest.raises(
|
||||
nx.NetworkXPointlessConcept, nx.is_strongly_connected, nx.DiGraph()
|
||||
)
|
||||
|
||||
def test_connected_raise(self):
|
||||
G = nx.Graph()
|
||||
pytest.raises(NetworkXNotImplemented, nx.strongly_connected_components, G)
|
||||
pytest.raises(
|
||||
NetworkXNotImplemented, nx.kosaraju_strongly_connected_components, G
|
||||
)
|
||||
pytest.raises(
|
||||
NetworkXNotImplemented, nx.strongly_connected_components_recursive, G
|
||||
)
|
||||
pytest.raises(NetworkXNotImplemented, nx.is_strongly_connected, G)
|
||||
pytest.raises(
|
||||
nx.NetworkXPointlessConcept, nx.is_strongly_connected, nx.DiGraph()
|
||||
)
|
||||
pytest.raises(NetworkXNotImplemented, nx.condensation, G)
|
||||
|
||||
|
||||
# Commented out due to variability on Travis-CI hardware/operating systems
|
||||
# def test_linear_time(self):
|
||||
# # See Issue #2831
|
||||
# count = 100 # base case
|
||||
# dg = nx.DiGraph()
|
||||
# dg.add_nodes_from([0, 1])
|
||||
# for i in range(2, count):
|
||||
# dg.add_node(i)
|
||||
# dg.add_edge(i, 1)
|
||||
# dg.add_edge(0, i)
|
||||
# t = time.time()
|
||||
# ret = tuple(nx.strongly_connected_components(dg))
|
||||
# dt = time.time() - t
|
||||
#
|
||||
# count = 200
|
||||
# dg = nx.DiGraph()
|
||||
# dg.add_nodes_from([0, 1])
|
||||
# for i in range(2, count):
|
||||
# dg.add_node(i)
|
||||
# dg.add_edge(i, 1)
|
||||
# dg.add_edge(0, i)
|
||||
# t = time.time()
|
||||
# ret = tuple(nx.strongly_connected_components(dg))
|
||||
# dt2 = time.time() - t
|
||||
# assert_less(dt2, dt * 2.3) # should be 2 times longer for this graph
|
|
@ -0,0 +1,78 @@
|
|||
import pytest
|
||||
import networkx as nx
|
||||
from networkx import NetworkXNotImplemented
|
||||
|
||||
|
||||
class TestWeaklyConnected:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.gc = []
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2),
|
||||
(2, 3),
|
||||
(2, 8),
|
||||
(3, 4),
|
||||
(3, 7),
|
||||
(4, 5),
|
||||
(5, 3),
|
||||
(5, 6),
|
||||
(7, 4),
|
||||
(7, 6),
|
||||
(8, 1),
|
||||
(8, 7),
|
||||
]
|
||||
)
|
||||
C = [[3, 4, 5, 7], [1, 2, 8], [6]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from([(1, 2), (1, 3), (1, 4), (4, 2), (3, 4), (2, 3)])
|
||||
C = [[2, 3, 4], [1]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from([(1, 2), (2, 3), (3, 2), (2, 1)])
|
||||
C = [[1, 2, 3]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
# Eppstein's tests
|
||||
G = nx.DiGraph({0: [1], 1: [2, 3], 2: [4, 5], 3: [4, 5], 4: [6], 5: [], 6: []})
|
||||
C = [[0], [1], [2], [3], [4], [5], [6]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
G = nx.DiGraph({0: [1], 1: [2, 3, 4], 2: [0, 3], 3: [4], 4: [3]})
|
||||
C = [[0, 1, 2], [3, 4]]
|
||||
cls.gc.append((G, C))
|
||||
|
||||
def test_weakly_connected_components(self):
|
||||
for G, C in self.gc:
|
||||
U = G.to_undirected()
|
||||
w = {frozenset(g) for g in nx.weakly_connected_components(G)}
|
||||
c = {frozenset(g) for g in nx.connected_components(U)}
|
||||
assert w == c
|
||||
|
||||
def test_number_weakly_connected_components(self):
|
||||
for G, C in self.gc:
|
||||
U = G.to_undirected()
|
||||
w = nx.number_weakly_connected_components(G)
|
||||
c = nx.number_connected_components(U)
|
||||
assert w == c
|
||||
|
||||
def test_is_weakly_connected(self):
|
||||
for G, C in self.gc:
|
||||
U = G.to_undirected()
|
||||
assert nx.is_weakly_connected(G) == nx.is_connected(U)
|
||||
|
||||
def test_null_graph(self):
|
||||
G = nx.DiGraph()
|
||||
assert list(nx.weakly_connected_components(G)) == []
|
||||
assert nx.number_weakly_connected_components(G) == 0
|
||||
pytest.raises(nx.NetworkXPointlessConcept, nx.is_weakly_connected, G)
|
||||
|
||||
def test_connected_raise(self):
|
||||
G = nx.Graph()
|
||||
pytest.raises(NetworkXNotImplemented, nx.weakly_connected_components, G)
|
||||
pytest.raises(NetworkXNotImplemented, nx.number_weakly_connected_components, G)
|
||||
pytest.raises(NetworkXNotImplemented, nx.is_weakly_connected, G)
|
|
@ -0,0 +1,168 @@
|
|||
"""Weakly connected components."""
|
||||
import networkx as nx
|
||||
from networkx.utils.decorators import not_implemented_for
|
||||
|
||||
__all__ = [
|
||||
"number_weakly_connected_components",
|
||||
"weakly_connected_components",
|
||||
"is_weakly_connected",
|
||||
]
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def weakly_connected_components(G):
|
||||
"""Generate weakly connected components of G.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
A directed graph
|
||||
|
||||
Returns
|
||||
-------
|
||||
comp : generator of sets
|
||||
A generator of sets of nodes, one for each weakly connected
|
||||
component of G.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is undirected.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Generate a sorted list of weakly connected components, largest first.
|
||||
|
||||
>>> G = nx.path_graph(4, create_using=nx.DiGraph())
|
||||
>>> nx.add_path(G, [10, 11, 12])
|
||||
>>> [
|
||||
... len(c)
|
||||
... for c in sorted(nx.weakly_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_cc = max(nx.weakly_connected_components(G), key=len)
|
||||
|
||||
See Also
|
||||
--------
|
||||
connected_components
|
||||
strongly_connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
For directed graphs only.
|
||||
|
||||
"""
|
||||
seen = set()
|
||||
for v in G:
|
||||
if v not in seen:
|
||||
c = set(_plain_bfs(G, v))
|
||||
yield c
|
||||
seen.update(c)
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def number_weakly_connected_components(G):
|
||||
"""Returns the number of weakly connected components in G.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX graph
|
||||
A directed graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
n : integer
|
||||
Number of weakly connected components
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is undirected.
|
||||
|
||||
See Also
|
||||
--------
|
||||
weakly_connected_components
|
||||
number_connected_components
|
||||
number_strongly_connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
For directed graphs only.
|
||||
|
||||
"""
|
||||
return sum(1 for wcc in weakly_connected_components(G))
|
||||
|
||||
|
||||
@not_implemented_for("undirected")
|
||||
def is_weakly_connected(G):
|
||||
"""Test directed graph for weak connectivity.
|
||||
|
||||
A directed graph is weakly connected if and only if the graph
|
||||
is connected when the direction of the edge between nodes is ignored.
|
||||
|
||||
Note that if a graph is strongly connected (i.e. the graph is connected
|
||||
even when we account for directionality), it is by definition weakly
|
||||
connected as well.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
G : NetworkX Graph
|
||||
A directed graph.
|
||||
|
||||
Returns
|
||||
-------
|
||||
connected : bool
|
||||
True if the graph is weakly connected, False otherwise.
|
||||
|
||||
Raises
|
||||
------
|
||||
NetworkXNotImplemented
|
||||
If G is undirected.
|
||||
|
||||
See Also
|
||||
--------
|
||||
is_strongly_connected
|
||||
is_semiconnected
|
||||
is_connected
|
||||
is_biconnected
|
||||
weakly_connected_components
|
||||
|
||||
Notes
|
||||
-----
|
||||
For directed graphs only.
|
||||
|
||||
"""
|
||||
if len(G) == 0:
|
||||
raise nx.NetworkXPointlessConcept(
|
||||
"""Connectivity is undefined for the null graph."""
|
||||
)
|
||||
|
||||
return len(list(weakly_connected_components(G))[0]) == len(G)
|
||||
|
||||
|
||||
def _plain_bfs(G, source):
|
||||
"""A fast BFS node generator
|
||||
|
||||
The direction of the edge between nodes is ignored.
|
||||
|
||||
For directed graphs only.
|
||||
|
||||
"""
|
||||
Gsucc = G.succ
|
||||
Gpred = G.pred
|
||||
|
||||
seen = set()
|
||||
nextlevel = {source}
|
||||
while nextlevel:
|
||||
thislevel = nextlevel
|
||||
nextlevel = set()
|
||||
for v in thislevel:
|
||||
if v not in seen:
|
||||
yield v
|
||||
seen.add(v)
|
||||
nextlevel.update(Gsucc[v])
|
||||
nextlevel.update(Gpred[v])
|
Loading…
Add table
Add a link
Reference in a new issue