Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/networkx/readwrite/graph6.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
1 # Original author: D. Eppstein, UC Irvine, August 12, 2003. | |
2 # The original code at http://www.ics.uci.edu/~eppstein/PADS/ is public domain. | |
3 # Copyright (C) 2004-2019 by | |
4 # Aric Hagberg <hagberg@lanl.gov> | |
5 # Dan Schult <dschult@colgate.edu> | |
6 # Pieter Swart <swart@lanl.gov> | |
7 # Tomas Gavenciak <gavento@ucw.cz> | |
8 # All rights reserved. | |
9 # BSD license. | |
10 # | |
11 # Authors: Tomas Gavenciak <gavento@ucw.cz> | |
12 # Aric Hagberg <aric.hagberg@lanl.gov> | |
13 """Functions for reading and writing graphs in the *graph6* format. | |
14 | |
15 The *graph6* file format is suitable for small graphs or large dense | |
16 graphs. For large sparse graphs, use the *sparse6* format. | |
17 | |
18 For more information, see the `graph6`_ homepage. | |
19 | |
20 .. _graph6: http://users.cecs.anu.edu.au/~bdm/data/formats.html | |
21 | |
22 """ | |
23 from itertools import islice | |
24 import sys | |
25 | |
26 import networkx as nx | |
27 from networkx.exception import NetworkXError | |
28 from networkx.utils import open_file, not_implemented_for | |
29 | |
30 __all__ = ['from_graph6_bytes', 'read_graph6', 'to_graph6_bytes', | |
31 'write_graph6'] | |
32 | |
33 | |
34 def _generate_graph6_bytes(G, nodes, header): | |
35 """Yield bytes in the graph6 encoding of a graph. | |
36 | |
37 `G` is an undirected simple graph. `nodes` is the list of nodes for | |
38 which the node-induced subgraph will be encoded; if `nodes` is the | |
39 list of all nodes in the graph, the entire graph will be | |
40 encoded. `header` is a Boolean that specifies whether to generate | |
41 the header ``b'>>graph6<<'`` before the remaining data. | |
42 | |
43 This function generates `bytes` objects in the following order: | |
44 | |
45 1. the header (if requested), | |
46 2. the encoding of the number of nodes, | |
47 3. each character, one-at-a-time, in the encoding of the requested | |
48 node-induced subgraph, | |
49 4. a newline character. | |
50 | |
51 This function raises :exc:`ValueError` if the graph is too large for | |
52 the graph6 format (that is, greater than ``2 ** 36`` nodes). | |
53 | |
54 """ | |
55 n = len(G) | |
56 if n >= 2 ** 36: | |
57 raise ValueError('graph6 is only defined if number of nodes is less ' | |
58 'than 2 ** 36') | |
59 if header: | |
60 yield b'>>graph6<<' | |
61 for d in n_to_data(n): | |
62 yield str.encode(chr(d + 63)) | |
63 # This generates the same as `(v in G[u] for u, v in combinations(G, 2))`, | |
64 # but in "column-major" order instead of "row-major" order. | |
65 bits = (nodes[j] in G[nodes[i]] for j in range(1, n) for i in range(j)) | |
66 chunk = list(islice(bits, 6)) | |
67 while chunk: | |
68 d = sum(b << 5 - i for i, b in enumerate(chunk)) | |
69 yield str.encode(chr(d + 63)) | |
70 chunk = list(islice(bits, 6)) | |
71 yield b'\n' | |
72 | |
73 | |
74 def from_graph6_bytes(string): | |
75 """Read a simple undirected graph in graph6 format from string. | |
76 | |
77 Parameters | |
78 ---------- | |
79 string : string | |
80 Data in graph6 format, without a trailing newline. | |
81 | |
82 Returns | |
83 ------- | |
84 G : Graph | |
85 | |
86 Raises | |
87 ------ | |
88 NetworkXError | |
89 If the string is unable to be parsed in graph6 format | |
90 | |
91 ValueError | |
92 If any character ``c`` in the input string does not satisfy | |
93 ``63 <= ord(c) < 127``. | |
94 | |
95 Examples | |
96 -------- | |
97 >>> G = nx.from_graph6_bytes(b'A_') | |
98 >>> sorted(G.edges()) | |
99 [(0, 1)] | |
100 | |
101 See Also | |
102 -------- | |
103 read_graph6, write_graph6 | |
104 | |
105 References | |
106 ---------- | |
107 .. [1] Graph6 specification | |
108 <http://users.cecs.anu.edu.au/~bdm/data/formats.html> | |
109 | |
110 """ | |
111 def bits(): | |
112 """Returns sequence of individual bits from 6-bit-per-value | |
113 list of data values.""" | |
114 for d in data: | |
115 for i in [5, 4, 3, 2, 1, 0]: | |
116 yield (d >> i) & 1 | |
117 | |
118 if string.startswith(b'>>graph6<<'): | |
119 string = string[10:] | |
120 | |
121 if sys.version_info < (3, ): | |
122 data = [ord(c) - 63 for c in string] | |
123 else: | |
124 data = [c - 63 for c in string] | |
125 if any(c > 63 for c in data): | |
126 raise ValueError('each input character must be in range(63, 127)') | |
127 | |
128 n, data = data_to_n(data) | |
129 nd = (n * (n - 1) // 2 + 5) // 6 | |
130 if len(data) != nd: | |
131 raise NetworkXError( | |
132 'Expected %d bits but got %d in graph6' % (n * (n - 1) // 2, len(data) * 6)) | |
133 | |
134 G = nx.Graph() | |
135 G.add_nodes_from(range(n)) | |
136 for (i, j), b in zip([(i, j) for j in range(1, n) for i in range(j)], bits()): | |
137 if b: | |
138 G.add_edge(i, j) | |
139 | |
140 return G | |
141 | |
142 | |
143 def to_graph6_bytes(G, nodes=None, header=True): | |
144 """Convert a simple undirected graph to bytes in graph6 format. | |
145 | |
146 Parameters | |
147 ---------- | |
148 G : Graph (undirected) | |
149 | |
150 nodes: list or iterable | |
151 Nodes are labeled 0...n-1 in the order provided. If None the ordering | |
152 given by ``G.nodes()`` is used. | |
153 | |
154 header: bool | |
155 If True add '>>graph6<<' bytes to head of data. | |
156 | |
157 Raises | |
158 ------ | |
159 NetworkXNotImplemented | |
160 If the graph is directed or is a multigraph. | |
161 | |
162 ValueError | |
163 If the graph has at least ``2 ** 36`` nodes; the graph6 format | |
164 is only defined for graphs of order less than ``2 ** 36``. | |
165 | |
166 Examples | |
167 -------- | |
168 >>> nx.to_graph6_bytes(nx.path_graph(2)) # doctest: +SKIP | |
169 b'>>graph6<<A_\\n' | |
170 | |
171 See Also | |
172 -------- | |
173 from_graph6_bytes, read_graph6, write_graph6_bytes | |
174 | |
175 Notes | |
176 ----- | |
177 The returned bytes end with a newline character. | |
178 | |
179 The format does not support edge or node labels, parallel edges or | |
180 self loops. If self loops are present they are silently ignored. | |
181 | |
182 References | |
183 ---------- | |
184 .. [1] Graph6 specification | |
185 <http://users.cecs.anu.edu.au/~bdm/data/formats.html> | |
186 | |
187 """ | |
188 if nodes is not None: | |
189 G = G.subgraph(nodes) | |
190 H = nx.convert_node_labels_to_integers(G) | |
191 nodes = sorted(H.nodes()) | |
192 return b''.join(_generate_graph6_bytes(H, nodes, header)) | |
193 | |
194 | |
195 @open_file(0, mode='rb') | |
196 def read_graph6(path): | |
197 """Read simple undirected graphs in graph6 format from path. | |
198 | |
199 Parameters | |
200 ---------- | |
201 path : file or string | |
202 File or filename to write. | |
203 | |
204 Returns | |
205 ------- | |
206 G : Graph or list of Graphs | |
207 If the file contains multiple lines then a list of graphs is returned | |
208 | |
209 Raises | |
210 ------ | |
211 NetworkXError | |
212 If the string is unable to be parsed in graph6 format | |
213 | |
214 Examples | |
215 -------- | |
216 You can read a graph6 file by giving the path to the file:: | |
217 | |
218 >>> import tempfile | |
219 >>> with tempfile.NamedTemporaryFile() as f: | |
220 ... _ = f.write(b'>>graph6<<A_\\n') | |
221 ... _ = f.seek(0) | |
222 ... G = nx.read_graph6(f.name) | |
223 >>> list(G.edges()) | |
224 [(0, 1)] | |
225 | |
226 You can also read a graph6 file by giving an open file-like object:: | |
227 | |
228 >>> import tempfile | |
229 >>> with tempfile.NamedTemporaryFile() as f: | |
230 ... _ = f.write(b'>>graph6<<A_\\n') | |
231 ... _ = f.seek(0) | |
232 ... G = nx.read_graph6(f) | |
233 >>> list(G.edges()) | |
234 [(0, 1)] | |
235 | |
236 See Also | |
237 -------- | |
238 from_graph6_bytes, write_graph6 | |
239 | |
240 References | |
241 ---------- | |
242 .. [1] Graph6 specification | |
243 <http://users.cecs.anu.edu.au/~bdm/data/formats.html> | |
244 | |
245 """ | |
246 glist = [] | |
247 for line in path: | |
248 line = line.strip() | |
249 if not len(line): | |
250 continue | |
251 glist.append(from_graph6_bytes(line)) | |
252 if len(glist) == 1: | |
253 return glist[0] | |
254 else: | |
255 return glist | |
256 | |
257 | |
258 @not_implemented_for('directed') | |
259 @not_implemented_for('multigraph') | |
260 @open_file(1, mode='wb') | |
261 def write_graph6(G, path, nodes=None, header=True): | |
262 """Write a simple undirected graph to a path in graph6 format. | |
263 | |
264 Parameters | |
265 ---------- | |
266 G : Graph (undirected) | |
267 | |
268 path : str | |
269 The path naming the file to which to write the graph. | |
270 | |
271 nodes: list or iterable | |
272 Nodes are labeled 0...n-1 in the order provided. If None the ordering | |
273 given by ``G.nodes()`` is used. | |
274 | |
275 header: bool | |
276 If True add '>>graph6<<' string to head of data | |
277 | |
278 Raises | |
279 ------ | |
280 NetworkXNotImplemented | |
281 If the graph is directed or is a multigraph. | |
282 | |
283 ValueError | |
284 If the graph has at least ``2 ** 36`` nodes; the graph6 format | |
285 is only defined for graphs of order less than ``2 ** 36``. | |
286 | |
287 Examples | |
288 -------- | |
289 You can write a graph6 file by giving the path to a file:: | |
290 | |
291 >>> import tempfile | |
292 >>> with tempfile.NamedTemporaryFile() as f: | |
293 ... nx.write_graph6(nx.path_graph(2), f.name) | |
294 ... _ = f.seek(0) | |
295 ... print(f.read()) # doctest: +SKIP | |
296 b'>>graph6<<A_\\n' | |
297 | |
298 See Also | |
299 -------- | |
300 from_graph6_bytes, read_graph6 | |
301 | |
302 Notes | |
303 ----- | |
304 The function writes a newline character after writing the encoding | |
305 of the graph. | |
306 | |
307 The format does not support edge or node labels, parallel edges or | |
308 self loops. If self loops are present they are silently ignored. | |
309 | |
310 References | |
311 ---------- | |
312 .. [1] Graph6 specification | |
313 <http://users.cecs.anu.edu.au/~bdm/data/formats.html> | |
314 | |
315 """ | |
316 return write_graph6_file(G, path, nodes=nodes, header=header) | |
317 | |
318 | |
319 @not_implemented_for('directed') | |
320 @not_implemented_for('multigraph') | |
321 def write_graph6_file(G, f, nodes=None, header=True): | |
322 """Write a simple undirected graph to a file-like object in graph6 format. | |
323 | |
324 Parameters | |
325 ---------- | |
326 G : Graph (undirected) | |
327 | |
328 f : file-like object | |
329 The file to write. | |
330 | |
331 nodes: list or iterable | |
332 Nodes are labeled 0...n-1 in the order provided. If None the ordering | |
333 given by ``G.nodes()`` is used. | |
334 | |
335 header: bool | |
336 If True add '>>graph6<<' string to head of data | |
337 | |
338 Raises | |
339 ------ | |
340 NetworkXNotImplemented | |
341 If the graph is directed or is a multigraph. | |
342 | |
343 ValueError | |
344 If the graph has at least ``2 ** 36`` nodes; the graph6 format | |
345 is only defined for graphs of order less than ``2 ** 36``. | |
346 | |
347 Examples | |
348 -------- | |
349 You can write a graph6 file by giving an open file-like object:: | |
350 | |
351 >>> import tempfile | |
352 >>> with tempfile.NamedTemporaryFile() as f: | |
353 ... nx.write_graph6(nx.path_graph(2), f) | |
354 ... _ = f.seek(0) | |
355 ... print(f.read()) # doctest: +SKIP | |
356 b'>>graph6<<A_\\n' | |
357 | |
358 See Also | |
359 -------- | |
360 from_graph6_bytes, read_graph6 | |
361 | |
362 Notes | |
363 ----- | |
364 The function writes a newline character after writing the encoding | |
365 of the graph. | |
366 | |
367 The format does not support edge or node labels, parallel edges or | |
368 self loops. If self loops are present they are silently ignored. | |
369 | |
370 References | |
371 ---------- | |
372 .. [1] Graph6 specification | |
373 <http://users.cecs.anu.edu.au/~bdm/data/formats.html> | |
374 | |
375 """ | |
376 if nodes is not None: | |
377 G = G.subgraph(nodes) | |
378 H = nx.convert_node_labels_to_integers(G) | |
379 nodes = sorted(H.nodes()) | |
380 for b in _generate_graph6_bytes(H, nodes, header): | |
381 f.write(b) | |
382 | |
383 | |
384 def data_to_n(data): | |
385 """Read initial one-, four- or eight-unit value from graph6 | |
386 integer sequence. | |
387 | |
388 Return (value, rest of seq.)""" | |
389 if data[0] <= 62: | |
390 return data[0], data[1:] | |
391 if data[1] <= 62: | |
392 return (data[1] << 12) + (data[2] << 6) + data[3], data[4:] | |
393 return ((data[2] << 30) + (data[3] << 24) + (data[4] << 18) + | |
394 (data[5] << 12) + (data[6] << 6) + data[7], data[8:]) | |
395 | |
396 | |
397 def n_to_data(n): | |
398 """Convert an integer to one-, four- or eight-unit graph6 sequence. | |
399 | |
400 This function is undefined if `n` is not in ``range(2 ** 36)``. | |
401 | |
402 """ | |
403 if n <= 62: | |
404 return [n] | |
405 elif n <= 258047: | |
406 return [63, (n >> 12) & 0x3f, (n >> 6) & 0x3f, n & 0x3f] | |
407 else: # if n <= 68719476735: | |
408 return [63, 63, | |
409 (n >> 30) & 0x3f, (n >> 24) & 0x3f, (n >> 18) & 0x3f, | |
410 (n >> 12) & 0x3f, (n >> 6) & 0x3f, n & 0x3f] |