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) |
