1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
|
#-*- coding: utf-8 -*-
"""
Recognition Tests
=================
A *forest* is an acyclic, undirected graph, and a *tree* is a connected forest.
Depending on the subfield, there are various conventions for generalizing these
definitions to directed graphs.
In one convention, directed variants of forest and tree are defined in an
identical manner, except that the direction of the edges is ignored. In effect,
each directed edge is treated as a single undirected edge. Then, additional
restrictions are imposed to define *branchings* and *arborescences*.
In another convention, directed variants of forest and tree correspond to
the previous convention's branchings and arborescences, respectively. Then two
new terms, *polyforest* and *polytree*, are defined to correspond to the other
convention's forest and tree.
Summarizing::
+-----------------------------+
| Convention 1 | Convention 2 |
+=============================+
| forest | polyforest |
| tree | polytree |
| branching | forest |
| arborescence | tree |
+-----------------------------+
Each convention has its reasons. The first convention emphasizes definitional
similarity in that directed forests and trees are only concerned with
acyclicity and do not have an in-degree constraint, just as their undirected
counterparts do not. The second convention emphasizes functional similarity
in the sense that the directed analog of a spanning tree is a spanning
arborescence. That is, take any spanning tree and choose one node as the root.
Then every edge is assigned a direction such there is a directed path from the
root to every other node. The result is a spanning arborescence.
NetworkX follows the first convention. Explicitly, these are:
undirected forest
An undirected graph with no undirected cycles.
undirected tree
A connected, undirected forest.
directed forest
A directed graph with no undirected cycles. Equivalently, the underlying
graph structure (which ignores edge orientations) is an undirected forest.
In another convention, this is known as a polyforest.
directed tree
A weakly connected, directed forest. Equivalently, the underlying graph
structure (which ignores edge orientations) is an undirected tree. In
another convention, this is known as a polytree.
branching
A directed forest with each node having, at most, one parent. So the maximum
in-degree is equal to 1. In another convention, this is known as a forest.
arborescence
A directed tree with each node having, at most, one parent. So the maximum
in-degree is equal to 1. In another convention, this is known as a tree.
"""
import networkx as nx
__author__ = """\n""".join([
'Ferdinando Papale <ferdinando.papale@gmail.com>',
'chebee7i <chebee7i@gmail.com>',
])
__all__ = ['is_arborescence', 'is_branching', 'is_forest', 'is_tree']
@nx.utils.not_implemented_for('undirected')
def is_arborescence(G):
"""
Returns `True` if `G` is an arborescence.
An arborescence is a directed tree with maximum in-degree equal to 1.
Parameters
----------
G : graph
The graph to test.
Returns
-------
b : bool
A boolean that is `True` if `G` is an arborescence.
Notes
-----
In another convention, an arborescence is known as a *tree*.
See Also
--------
is_tree
"""
if not is_tree(G):
return False
if max(G.in_degree().values()) > 1:
return False
return True
@nx.utils.not_implemented_for('undirected')
def is_branching(G):
"""
Returns `True` if `G` is a branching.
A branching is a directed forest with maximum in-degree equal to 1.
Parameters
----------
G : directed graph
The directed graph to test.
Returns
-------
b : bool
A boolean that is `True` if `G` is a branching.
Notes
-----
In another convention, a branching is also known as a *forest*.
See Also
--------
is_forest
"""
if not is_forest(G):
return False
if max(G.in_degree().values()) > 1:
return False
return True
def is_forest(G):
"""
Returns `True` if G is a forest.
A forest is a graph with no undirected cycles.
For directed graphs, `G` is a forest if the underlying graph is a forest.
The underlying graph is obtained by treating each directed edge as a single
undirected edge in a multigraph.
Parameters
----------
G : graph
The graph to test.
Returns
-------
b : bool
A boolean that is `True` if `G` is a forest.
Notes
-----
In another convention, a directed forest is known as a *polyforest* and
then *forest* corresponds to a *branching*.
See Also
--------
is_branching
"""
if len(G) == 0:
raise nx.exception.NetworkXPointlessConcept('G has no nodes.')
if G.is_directed():
components = nx.weakly_connected_component_subgraphs
else:
components = nx.connected_component_subgraphs
for component in components(G):
# Make sure the component is a tree.
if component.number_of_edges() != component.number_of_nodes() - 1:
return False
return True
def is_tree(G):
"""
Returns `True` if `G` is a tree.
A tree is a connected graph with no undirected cycles.
For directed graphs, `G` is a tree if the underlying graph is a tree. The
underlying graph is obtained by treating each directed edge as a single
undirected edge in a multigraph.
Parameters
----------
G : graph
The graph to test.
Returns
-------
b : bool
A boolean that is `True` if `G` is a tree.
Notes
-----
In another convention, a directed tree is known as a *polytree* and then
*tree* corresponds to an *arborescence*.
See Also
--------
is_arborescence
"""
if len(G) == 0:
raise nx.exception.NetworkXPointlessConcept('G has no nodes.')
# A connected graph with no cycles has n-1 edges.
if G.number_of_edges() != len(G) - 1:
return False
if G.is_directed():
is_connected = nx.is_weakly_connected
else:
is_connected = nx.is_connected
return is_connected(G)
|