comparison planemo/lib/python3.7/site-packages/networkx/algorithms/tournament.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 # tournament.py - functions for tournament graphs
2 #
3 # Copyright 2015 NetworkX developers.
4 #
5 # This file is part of NetworkX.
6 #
7 # NetworkX is distributed under a BSD license; see LICENSE.txt for more
8 # information.
9 """Functions concerning tournament graphs.
10
11 A `tournament graph`_ is a complete oriented graph. In other words, it
12 is a directed graph in which there is exactly one directed edge joining
13 each pair of distinct nodes. For each function in this module that
14 accepts a graph as input, you must provide a tournament graph. The
15 responsibility is on the caller to ensure that the graph is a tournament
16 graph.
17
18 To access the functions in this module, you must access them through the
19 :mod:`networkx.algorithms.tournament` module::
20
21 >>> import networkx as nx
22 >>> from networkx.algorithms import tournament
23 >>> G = nx.DiGraph([(0, 1), (1, 2), (2, 0)])
24 >>> tournament.is_tournament(G)
25 True
26
27 .. _tournament graph: https://en.wikipedia.org/wiki/Tournament_%28graph_theory%29
28
29 """
30 from itertools import combinations
31
32 import networkx as nx
33 from networkx.algorithms.simple_paths import is_simple_path as is_path
34 from networkx.utils import arbitrary_element
35 from networkx.utils import not_implemented_for
36 from networkx.utils import py_random_state
37
38 __all__ = ['hamiltonian_path', 'is_reachable', 'is_strongly_connected',
39 'is_tournament', 'random_tournament', 'score_sequence']
40
41
42 def index_satisfying(iterable, condition):
43 """Returns the index of the first element in `iterable` that
44 satisfies the given condition.
45
46 If no such element is found (that is, when the iterable is
47 exhausted), this returns the length of the iterable (that is, one
48 greater than the last index of the iterable).
49
50 `iterable` must not be empty. If `iterable` is empty, this
51 function raises :exc:`ValueError`.
52
53 """
54 # Pre-condition: iterable must not be empty.
55 for i, x in enumerate(iterable):
56 if condition(x):
57 return i
58 # If we reach the end of the iterable without finding an element
59 # that satisfies the condition, return the length of the iterable,
60 # which is one greater than the index of its last element. If the
61 # iterable was empty, `i` will not be defined, so we raise an
62 # exception.
63 try:
64 return i + 1
65 except NameError:
66 raise ValueError('iterable must be non-empty')
67
68
69 @not_implemented_for('undirected')
70 @not_implemented_for('multigraph')
71 def is_tournament(G):
72 """Returns True if and only if `G` is a tournament.
73
74 A tournament is a directed graph, with neither self-loops nor
75 multi-edges, in which there is exactly one directed edge joining
76 each pair of distinct nodes.
77
78 Parameters
79 ----------
80 G : NetworkX graph
81 A directed graph representing a tournament.
82
83 Returns
84 -------
85 bool
86 Whether the given graph is a tournament graph.
87
88 Notes
89 -----
90 Some definitions require a self-loop on each node, but that is not
91 the convention used here.
92
93 """
94 # In a tournament, there is exactly one directed edge joining each pair.
95 return (all((v in G[u]) ^ (u in G[v]) for u, v in combinations(G, 2)) and
96 nx.number_of_selfloops(G) == 0)
97
98
99 @not_implemented_for('undirected')
100 @not_implemented_for('multigraph')
101 def hamiltonian_path(G):
102 """Returns a Hamiltonian path in the given tournament graph.
103
104 Each tournament has a Hamiltonian path. If furthermore, the
105 tournament is strongly connected, then the returned Hamiltonian path
106 is a Hamiltonian cycle (by joining the endpoints of the path).
107
108 Parameters
109 ----------
110 G : NetworkX graph
111 A directed graph representing a tournament.
112
113 Returns
114 -------
115 bool
116 Whether the given graph is a tournament graph.
117
118 Notes
119 -----
120 This is a recursive implementation with an asymptotic running time
121 of $O(n^2)$, ignoring multiplicative polylogarithmic factors, where
122 $n$ is the number of nodes in the graph.
123
124 """
125 if len(G) == 0:
126 return []
127 if len(G) == 1:
128 return [arbitrary_element(G)]
129 v = arbitrary_element(G)
130 hampath = hamiltonian_path(G.subgraph(set(G) - {v}))
131 # Get the index of the first node in the path that does *not* have
132 # an edge to `v`, then insert `v` before that node.
133 index = index_satisfying(hampath, lambda u: v not in G[u])
134 hampath.insert(index, v)
135 return hampath
136
137
138 @py_random_state(1)
139 def random_tournament(n, seed=None):
140 r"""Returns a random tournament graph on `n` nodes.
141
142 Parameters
143 ----------
144 n : int
145 The number of nodes in the returned graph.
146 seed : integer, random_state, or None (default)
147 Indicator of random number generation state.
148 See :ref:`Randomness<randomness>`.
149
150 Returns
151 -------
152 bool
153 Whether the given graph is a tournament graph.
154
155 Notes
156 -----
157 This algorithm adds, for each pair of distinct nodes, an edge with
158 uniformly random orientation. In other words, `\binom{n}{2}` flips
159 of an unbiased coin decide the orientations of the edges in the
160 graph.
161
162 """
163 # Flip an unbiased coin for each pair of distinct nodes.
164 coins = (seed.random() for i in range((n * (n - 1)) // 2))
165 pairs = combinations(range(n), 2)
166 edges = ((u, v) if r < 0.5 else (v, u) for (u, v), r in zip(pairs, coins))
167 return nx.DiGraph(edges)
168
169
170 @not_implemented_for('undirected')
171 @not_implemented_for('multigraph')
172 def score_sequence(G):
173 """Returns the score sequence for the given tournament graph.
174
175 The score sequence is the sorted list of the out-degrees of the
176 nodes of the graph.
177
178 Parameters
179 ----------
180 G : NetworkX graph
181 A directed graph representing a tournament.
182
183 Returns
184 -------
185 list
186 A sorted list of the out-degrees of the nodes of `G`.
187
188 """
189 return sorted(d for v, d in G.out_degree())
190
191
192 @not_implemented_for('undirected')
193 @not_implemented_for('multigraph')
194 def tournament_matrix(G):
195 r"""Returns the tournament matrix for the given tournament graph.
196
197 This function requires SciPy.
198
199 The *tournament matrix* of a tournament graph with edge set *E* is
200 the matrix *T* defined by
201
202 .. math::
203
204 T_{i j} =
205 \begin{cases}
206 +1 & \text{if } (i, j) \in E \\
207 -1 & \text{if } (j, i) \in E \\
208 0 & \text{if } i == j.
209 \end{cases}
210
211 An equivalent definition is `T = A - A^T`, where *A* is the
212 adjacency matrix of the graph `G`.
213
214 Parameters
215 ----------
216 G : NetworkX graph
217 A directed graph representing a tournament.
218
219 Returns
220 -------
221 SciPy sparse matrix
222 The tournament matrix of the tournament graph `G`.
223
224 Raises
225 ------
226 ImportError
227 If SciPy is not available.
228
229 """
230 A = nx.adjacency_matrix(G)
231 return A - A.T
232
233
234 @not_implemented_for('undirected')
235 @not_implemented_for('multigraph')
236 def is_reachable(G, s, t):
237 """Decides whether there is a path from `s` to `t` in the
238 tournament.
239
240 This function is more theoretically efficient than the reachability
241 checks than the shortest path algorithms in
242 :mod:`networkx.algorithms.shortest_paths`.
243
244 The given graph **must** be a tournament, otherwise this function's
245 behavior is undefined.
246
247 Parameters
248 ----------
249 G : NetworkX graph
250 A directed graph representing a tournament.
251
252 s : node
253 A node in the graph.
254
255 t : node
256 A node in the graph.
257
258 Returns
259 -------
260 bool
261 Whether there is a path from `s` to `t` in `G`.
262
263 Notes
264 -----
265 Although this function is more theoretically efficient than the
266 generic shortest path functions, a speedup requires the use of
267 parallelism. Though it may in the future, the current implementation
268 does not use parallelism, thus you may not see much of a speedup.
269
270 This algorithm comes from [1].
271
272 References
273 ----------
274 .. [1] Tantau, Till.
275 "A note on the complexity of the reachability problem for
276 tournaments."
277 *Electronic Colloquium on Computational Complexity*. 2001.
278 <http://eccc.hpi-web.de/report/2001/092/>
279
280 """
281
282 def two_neighborhood(G, v):
283 """Returns the set of nodes at distance at most two from `v`.
284
285 `G` must be a graph and `v` a node in that graph.
286
287 The returned set includes the nodes at distance zero (that is,
288 the node `v` itself), the nodes at distance one (that is, the
289 out-neighbors of `v`), and the nodes at distance two.
290
291 """
292 # TODO This is trivially parallelizable.
293 return {x for x in G
294 if x == v or x in G[v] or
295 any(is_path(G, [v, z, x]) for z in G)}
296
297 def is_closed(G, nodes):
298 """Decides whether the given set of nodes is closed.
299
300 A set *S* of nodes is *closed* if for each node *u* in the graph
301 not in *S* and for each node *v* in *S*, there is an edge from
302 *u* to *v*.
303
304 """
305 # TODO This is trivially parallelizable.
306 return all(v in G[u] for u in set(G) - nodes for v in nodes)
307
308 # TODO This is trivially parallelizable.
309 neighborhoods = [two_neighborhood(G, v) for v in G]
310 return all(not (is_closed(G, S) and s in S and t not in S)
311 for S in neighborhoods)
312
313
314 @not_implemented_for('undirected')
315 @not_implemented_for('multigraph')
316 def is_strongly_connected(G):
317 """Decides whether the given tournament is strongly connected.
318
319 This function is more theoretically efficient than the
320 :func:`~networkx.algorithms.components.is_strongly_connected`
321 function.
322
323 The given graph **must** be a tournament, otherwise this function's
324 behavior is undefined.
325
326 Parameters
327 ----------
328 G : NetworkX graph
329 A directed graph representing a tournament.
330
331 Returns
332 -------
333 bool
334 Whether the tournament is strongly connected.
335
336 Notes
337 -----
338 Although this function is more theoretically efficient than the
339 generic strong connectivity function, a speedup requires the use of
340 parallelism. Though it may in the future, the current implementation
341 does not use parallelism, thus you may not see much of a speedup.
342
343 This algorithm comes from [1].
344
345 References
346 ----------
347 .. [1] Tantau, Till.
348 "A note on the complexity of the reachability problem for
349 tournaments."
350 *Electronic Colloquium on Computational Complexity*. 2001.
351 <http://eccc.hpi-web.de/report/2001/092/>
352
353 """
354 # TODO This is trivially parallelizable.
355 return all(is_reachable(G, u, v) for u in G for v in G)