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]