Statistics
| Branch: | Revision:

iof-tools / networkxMiCe / networkx-master / networkx / algorithms / tree / recognition.py @ 5cef0f13

History | View | Annotate | Download (6.4 KB)

1
#-*- coding: utf-8 -*-
2
"""
3
Recognition Tests
4
=================
5

6
A *forest* is an acyclic, undirected graph, and a *tree* is a connected forest.
7
Depending on the subfield, there are various conventions for generalizing these
8
definitions to directed graphs.
9

10
In one convention, directed variants of forest and tree are defined in an
11
identical manner, except that the direction of the edges is ignored. In effect,
12
each directed edge is treated as a single undirected edge. Then, additional
13
restrictions are imposed to define *branchings* and *arborescences*.
14

15
In another convention, directed variants of forest and tree correspond to
16
the previous convention's branchings and arborescences, respectively. Then two
17
new terms, *polyforest* and *polytree*, are defined to correspond to the other
18
convention's forest and tree.
19

20
Summarizing::
21

22
   +-----------------------------+
23
   | Convention A | Convention B |
24
   +=============================+
25
   | forest       | polyforest   |
26
   | tree         | polytree     |
27
   | branching    | forest       |
28
   | arborescence | tree         |
29
   +-----------------------------+
30

31
Each convention has its reasons. The first convention emphasizes definitional
32
similarity in that directed forests and trees are only concerned with
33
acyclicity and do not have an in-degree constraint, just as their undirected
34
counterparts do not. The second convention emphasizes functional similarity
35
in the sense that the directed analog of a spanning tree is a spanning
36
arborescence. That is, take any spanning tree and choose one node as the root.
37
Then every edge is assigned a direction such there is a directed path from the
38
root to every other node. The result is a spanning arborescence.
39

40
NetworkX follows convention "A". Explicitly, these are:
41

42
undirected forest
43
   An undirected graph with no undirected cycles.
44

45
undirected tree
46
   A connected, undirected forest.
47

48
directed forest
49
   A directed graph with no undirected cycles. Equivalently, the underlying
50
   graph structure (which ignores edge orientations) is an undirected forest.
51
   In convention B, this is known as a polyforest.
52

53
directed tree
54
   A weakly connected, directed forest. Equivalently, the underlying graph
55
   structure (which ignores edge orientations) is an undirected tree. In
56
   convention B, this is known as a polytree.
57

58
branching
59
   A directed forest with each node having, at most, one parent. So the maximum
60
   in-degree is equal to 1. In convention B, this is known as a forest.
61

62
arborescence
63
   A directed tree with each node having, at most, one parent. So the maximum
64
   in-degree is equal to 1. In convention B, this is known as a tree.
65

66
For trees and arborescences, the adjective "spanning" may be added to designate
67
that the graph, when considered as a forest/branching, consists of a single
68
tree/arborescence that includes all nodes in the graph. It is true, by
69
definition, that every tree/arborescence is spanning with respect to the nodes
70
that define the tree/arborescence and so, it might seem redundant to introduce
71
the notion of "spanning". However, the nodes may represent a subset of
72
nodes from a larger graph, and it is in this context that the term "spanning"
73
becomes a useful notion.
74

75
"""
76

    
77
import networkx as nx
78

    
79
__author__ = """\n""".join([
80
    'Ferdinando Papale <ferdinando.papale@gmail.com>',
81
    'chebee7i <chebee7i@gmail.com>',
82
])
83

    
84

    
85
__all__ = ['is_arborescence', 'is_branching', 'is_forest', 'is_tree']
86

    
87

    
88
@nx.utils.not_implemented_for('undirected')
89
def is_arborescence(G):
90
    """
91
    Returns True if `G` is an arborescence.
92

93
    An arborescence is a directed tree with maximum in-degree equal to 1.
94

95
    Parameters
96
    ----------
97
    G : graph
98
        The graph to test.
99

100
    Returns
101
    -------
102
    b : bool
103
        A boolean that is True if `G` is an arborescence.
104

105
    Notes
106
    -----
107
    In another convention, an arborescence is known as a *tree*.
108

109
    See Also
110
    --------
111
    is_tree
112

113
    """
114
    return is_tree(G) and max(d for n, d in G.in_degree()) <= 1
115

    
116

    
117
@nx.utils.not_implemented_for('undirected')
118
def is_branching(G):
119
    """
120
    Returns True if `G` is a branching.
121

122
    A branching is a directed forest with maximum in-degree equal to 1.
123

124
    Parameters
125
    ----------
126
    G : directed graph
127
        The directed graph to test.
128

129
    Returns
130
    -------
131
    b : bool
132
        A boolean that is True if `G` is a branching.
133

134
    Notes
135
    -----
136
    In another convention, a branching is also known as a *forest*.
137

138
    See Also
139
    --------
140
    is_forest
141

142
    """
143
    return is_forest(G) and max(d for n, d in G.in_degree()) <= 1
144

    
145

    
146
def is_forest(G):
147
    """
148
    Returns True if `G` is a forest.
149

150
    A forest is a graph with no undirected cycles.
151

152
    For directed graphs, `G` is a forest if the underlying graph is a forest.
153
    The underlying graph is obtained by treating each directed edge as a single
154
    undirected edge in a multigraph.
155

156
    Parameters
157
    ----------
158
    G : graph
159
        The graph to test.
160

161
    Returns
162
    -------
163
    b : bool
164
        A boolean that is True if `G` is a forest.
165

166
    Notes
167
    -----
168
    In another convention, a directed forest is known as a *polyforest* and
169
    then *forest* corresponds to a *branching*.
170

171
    See Also
172
    --------
173
    is_branching
174

175
    """
176
    if len(G) == 0:
177
        raise nx.exception.NetworkXPointlessConcept('G has no nodes.')
178

    
179
    if G.is_directed():
180
        components = nx.weakly_connected_component_subgraphs
181
    else:
182
        components = nx.connected_component_subgraphs
183

    
184
    return all(len(c) - 1 == c.number_of_edges() for c in components(G))
185

    
186

    
187
def is_tree(G):
188
    """
189
    Returns True if `G` is a tree.
190

191
    A tree is a connected graph with no undirected cycles.
192

193
    For directed graphs, `G` is a tree if the underlying graph is a tree. The
194
    underlying graph is obtained by treating each directed edge as a single
195
    undirected edge in a multigraph.
196

197
    Parameters
198
    ----------
199
    G : graph
200
        The graph to test.
201

202
    Returns
203
    -------
204
    b : bool
205
        A boolean that is True if `G` is a tree.
206

207
    Notes
208
    -----
209
    In another convention, a directed tree is known as a *polytree* and then
210
    *tree* corresponds to an *arborescence*.
211

212
    See Also
213
    --------
214
    is_arborescence
215

216
    """
217
    if len(G) == 0:
218
        raise nx.exception.NetworkXPointlessConcept('G has no nodes.')
219

    
220
    if G.is_directed():
221
        is_connected = nx.is_weakly_connected
222
    else:
223
        is_connected = nx.is_connected
224

    
225
    # A connected graph with no cycles has n-1 edges.
226
    return len(G) - 1 == G.number_of_edges() and is_connected(G)