Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/generators/trees.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 # -*- encoding: utf-8 -*- | |
2 # Copyright (C) 2015-2019 by | |
3 # Jeffrey Finkelstein <jeffrey.finkelstein@gmail.com> | |
4 # NetworkX developers | |
5 # All rights reserved. | |
6 # BSD license. | |
7 # | |
8 # Authors: Jeffrey Finkelstein <jeffrey.finkelstein@gmail.com> | |
9 """Functions for generating trees.""" | |
10 from collections import defaultdict | |
11 | |
12 import networkx as nx | |
13 from networkx.utils import generate_unique_node | |
14 from networkx.utils import py_random_state | |
15 | |
16 __all__ = ['prefix_tree', 'random_tree'] | |
17 | |
18 #: The nil node, the only leaf node in a prefix tree. | |
19 #: | |
20 #: Each predecessor of the nil node corresponds to the end of a path | |
21 #: used to generate the prefix tree. | |
22 NIL = 'NIL' | |
23 | |
24 | |
25 def prefix_tree(paths): | |
26 """Creates a directed prefix tree from the given list of iterables. | |
27 | |
28 Parameters | |
29 ---------- | |
30 paths: iterable of lists | |
31 An iterable over "paths", which are themselves lists of | |
32 nodes. Common prefixes among these paths are converted into | |
33 common initial segments in the generated tree. | |
34 | |
35 Most commonly, this may be an iterable over lists of integers, | |
36 or an iterable over Python strings. | |
37 | |
38 Returns | |
39 ------- | |
40 T: DiGraph | |
41 A directed graph representing an arborescence consisting of the | |
42 prefix tree generated by `paths`. Nodes are directed "downward", | |
43 from parent to child. A special "synthetic" root node is added | |
44 to be the parent of the first node in each path. A special | |
45 "synthetic" leaf node, the "nil" node, is added to be the child | |
46 of all nodes representing the last element in a path. (The | |
47 addition of this nil node technically makes this not an | |
48 arborescence but a directed acyclic graph; removing the nil node | |
49 makes it an arborescence.) | |
50 | |
51 Each node has an attribute 'source' whose value is the original | |
52 element of the path to which this node corresponds. The 'source' | |
53 of the root node is None, and the 'source' of the nil node is | |
54 :data:`.NIL`. | |
55 | |
56 The root node is the only node of in-degree zero in the graph, | |
57 and the nil node is the only node of out-degree zero. For | |
58 convenience, the nil node can be accessed via the :data:`.NIL` | |
59 attribute; for example:: | |
60 | |
61 >>> from networkx.generators.trees import NIL | |
62 >>> paths = ['ab', 'abs', 'ad'] | |
63 >>> T, root = nx.prefix_tree(paths) | |
64 >>> T.predecessors(NIL) # doctest: +SKIP | |
65 | |
66 root : string | |
67 The randomly generated uuid of the root node. | |
68 | |
69 Notes | |
70 ----- | |
71 The prefix tree is also known as a *trie*. | |
72 | |
73 Examples | |
74 -------- | |
75 Create a prefix tree from a list of strings with some common | |
76 prefixes:: | |
77 | |
78 >>> strings = ['ab', 'abs', 'ad'] | |
79 >>> T, root = nx.prefix_tree(strings) | |
80 | |
81 Continuing the above example, to recover the original paths that | |
82 generated the prefix tree, traverse up the tree from the | |
83 :data:`.NIL` node to the root:: | |
84 | |
85 >>> from networkx.generators.trees import NIL | |
86 >>> | |
87 >>> strings = ['ab', 'abs', 'ad'] | |
88 >>> T, root = nx.prefix_tree(strings) | |
89 >>> recovered = [] | |
90 >>> for v in T.predecessors(NIL): | |
91 ... s = '' | |
92 ... while v != root: | |
93 ... # Prepend the character `v` to the accumulator `s`. | |
94 ... s = str(T.nodes[v]['source']) + s | |
95 ... # Each non-nil, non-root node has exactly one parent. | |
96 ... v = next(T.predecessors(v)) | |
97 ... recovered.append(s) | |
98 >>> sorted(recovered) | |
99 ['ab', 'abs', 'ad'] | |
100 | |
101 """ | |
102 def _helper(paths, root, B): | |
103 """Recursively create a trie from the given list of paths. | |
104 | |
105 `paths` is a list of paths, each of which is itself a list of | |
106 nodes, relative to the given `root` (but not including it). This | |
107 list of paths will be interpreted as a tree-like structure, in | |
108 which two paths that share a prefix represent two branches of | |
109 the tree with the same initial segment. | |
110 | |
111 `root` is the parent of the node at index 0 in each path. | |
112 | |
113 `B` is the "accumulator", the :class:`networkx.DiGraph` | |
114 representing the branching to which the new nodes and edges will | |
115 be added. | |
116 | |
117 """ | |
118 # Create a mapping from each head node to the list of tail paths | |
119 # remaining beneath that node. | |
120 children = defaultdict(list) | |
121 for path in paths: | |
122 # If the path is the empty list, that represents the empty | |
123 # string, so we add an edge to the NIL node. | |
124 if not path: | |
125 B.add_edge(root, NIL) | |
126 continue | |
127 # TODO In Python 3, this should be `child, *rest = path`. | |
128 child, rest = path[0], path[1:] | |
129 # `child` may exist as the head of more than one path in `paths`. | |
130 children[child].append(rest) | |
131 # Add a node for each child found above and add edges from the | |
132 # root to each child. In this loop, `head` is the child and | |
133 # `tails` is the list of remaining paths under that child. | |
134 for head, tails in children.items(): | |
135 # We need to relabel each child with a unique name. To do | |
136 # this we simply change each key in the dictionary to be a | |
137 # (key, uuid) pair. | |
138 new_head = generate_unique_node() | |
139 # Ensure the new child knows the name of the old child so | |
140 # that the user can recover the mapping to the original | |
141 # nodes. | |
142 B.add_node(new_head, source=head) | |
143 B.add_edge(root, new_head) | |
144 _helper(tails, new_head, B) | |
145 | |
146 # Initialize the prefix tree with a root node and a nil node. | |
147 T = nx.DiGraph() | |
148 root = generate_unique_node() | |
149 T.add_node(root, source=None) | |
150 T.add_node(NIL, source=NIL) | |
151 # Populate the tree. | |
152 _helper(paths, root, T) | |
153 return T, root | |
154 | |
155 | |
156 # From the Wikipedia article on Prüfer sequences: | |
157 # | |
158 # > Generating uniformly distributed random Prüfer sequences and | |
159 # > converting them into the corresponding trees is a straightforward | |
160 # > method of generating uniformly distributed random labelled trees. | |
161 # | |
162 @py_random_state(1) | |
163 def random_tree(n, seed=None): | |
164 """Returns a uniformly random tree on `n` nodes. | |
165 | |
166 Parameters | |
167 ---------- | |
168 n : int | |
169 A positive integer representing the number of nodes in the tree. | |
170 seed : integer, random_state, or None (default) | |
171 Indicator of random number generation state. | |
172 See :ref:`Randomness<randomness>`. | |
173 | |
174 Returns | |
175 ------- | |
176 NetworkX graph | |
177 A tree, given as an undirected graph, whose nodes are numbers in | |
178 the set {0, …, *n* - 1}. | |
179 | |
180 Raises | |
181 ------ | |
182 NetworkXPointlessConcept | |
183 If `n` is zero (because the null graph is not a tree). | |
184 | |
185 Notes | |
186 ----- | |
187 The current implementation of this function generates a uniformly | |
188 random Prüfer sequence then converts that to a tree via the | |
189 :func:`~networkx.from_prufer_sequence` function. Since there is a | |
190 bijection between Prüfer sequences of length *n* - 2 and trees on | |
191 *n* nodes, the tree is chosen uniformly at random from the set of | |
192 all trees on *n* nodes. | |
193 | |
194 """ | |
195 if n == 0: | |
196 raise nx.NetworkXPointlessConcept('the null graph is not a tree') | |
197 # Cannot create a Prüfer sequence unless `n` is at least two. | |
198 if n == 1: | |
199 return nx.empty_graph(1) | |
200 sequence = [seed.choice(range(n)) for i in range(n - 2)] | |
201 return nx.from_prufer_sequence(sequence) |