def _generate_sparse6_bytes(G, nodes, header): """Yield bytes in the sparse6 encoding of a graph. `G` is an undirected simple graph. `nodes` is the list of nodes for which the node-induced subgraph will be encoded; if `nodes` is the list of all nodes in the graph, the entire graph will be encoded. `header` is a Boolean that specifies whether to generate the header ``b'>>sparse6<<'`` before the remaining data. This function generates `bytes` objects in the following order: 1. the header (if requested), 2. the encoding of the number of nodes, 3. each character, one-at-a-time, in the encoding of the requested node-induced subgraph, 4. a newline character. This function raises :exc:`ValueError` if the graph is too large for the graph6 format (that is, greater than ``2 ** 36`` nodes). """ n = len(G) if n >= 2 ** 36: raise ValueError('sparse6 is only defined if number of nodes is less ' 'than 2 ** 36') if header: yield b'>>sparse6<<' yield b':' for d in n_to_data(n): yield str.encode(chr(d + 63)) k = 1 while 1 << k < n: k += 1 def enc(x): """Big endian k-bit encoding of x""" return [1 if (x & 1 << (k - 1 - i)) else 0 for i in range(k)] edges = sorted((max(u, v), min(u, v)) for u, v in G.edges()) bits = [] curv = 0 for (v, u) in edges: if v == curv: # current vertex edge bits.append(0) bits.extend(enc(u)) elif v == curv + 1: # next vertex edge curv += 1 bits.append(1) bits.extend(enc(u)) else: # skip to vertex v and then add edge to u curv = v bits.append(1) bits.extend(enc(v)) bits.append(0) bits.extend(enc(u)) if k < 6 and n == (1 << k) and ((-len(bits)) % 6) >= k and curv < (n - 1): # Padding special case: small k, n=2^k, # more than k bits of padding needed, # current vertex is not (n-1) -- # appending 1111... would add a loop on (n-1) bits.append(0) bits.extend([1] * ((-len(bits)) % 6)) else: bits.extend([1] * ((-len(bits)) % 6)) data = [(bits[i + 0] << 5) + (bits[i + 1] << 4) + (bits[i + 2] << 3) + (bits[i + 3] << 2) + (bits[i + 4] << 1) + (bits[i + 5] << 0) for i in range(0, len(bits), 6)] for d in data: yield str.encode(chr(d + 63)) yield b'\n'
def generate_sparse6(G, nodes=None, header=True): """Generate sparse6 format string from an undirected graph. Parameters ---------- G : Graph (undirected) nodes: list or iterable Nodes are labeled 0...n-1 in the order provided. If None the ordering given by G.nodes() is used. header: bool If True add '>>sparse6<<' string to head of data Returns ------- s : string String in sparse6 format Raises ------ NetworkXError If the graph is directed Examples -------- >>> G = nx.MultiGraph([(0, 1), (0, 1), (0, 1)]) >>> nx.generate_sparse6(G) '>>sparse6<<:A_' See Also -------- read_sparse6, parse_sparse6, write_sparse6 Notes ----- The format does not support edge or node labels. References ---------- Sparse6 specification: http://cs.anu.edu.au/~bdm/data/formats.txt for details. """ n = G.order() k = 1 while 1 << k < n: k += 1 def enc(x): """Big endian k-bit encoding of x""" return [1 if (x & 1 << (k - 1 - i)) else 0 for i in range(k)] if nodes is None: ns = list(G.nodes()) # number -> node else: ns = list(nodes) ndict = dict(((ns[i], i) for i in range(len(ns)))) # node -> number edges = [(ndict[u], ndict[v]) for (u, v) in G.edges()] edges = [(max(u, v), min(u, v)) for (u, v) in edges] edges.sort() bits = [] curv = 0 for (v, u) in edges: if v == curv: # current vertex edge bits.append(0) bits.extend(enc(u)) elif v == curv + 1: # next vertex edge curv += 1 bits.append(1) bits.extend(enc(u)) else: # skip to vertex v and then add edge to u curv = v bits.append(1) bits.extend(enc(v)) bits.append(0) bits.extend(enc(u)) if k < 6 and n == (1 << k) and ((-len(bits)) % 6) >= k and curv < (n - 1): # Padding special case: small k, n=2^k, # more than k bits of padding needed, # current vertex is not (n-1) -- # appending 1111... would add a loop on (n-1) bits.append(0) bits.extend([1] * ((-len(bits)) % 6)) else: bits.extend([1] * ((-len(bits)) % 6)) data = [(bits[i + 0] << 5) + (bits[i + 1] << 4) + (bits[i + 2] << 3) + (bits[i + 3] << 2) + (bits[i + 4] << 1) + (bits[i + 5] << 0) for i in range(0, len(bits), 6)] res = (':' + data_to_graph6(n_to_data(n)) + data_to_graph6(data)) if header: return '>>sparse6<<' + res else: return res
def test_n_data_n_conversion(self): for i in [0, 1, 42, 62, 63, 64, 258047, 258048, 7744773, 68719476735]: assert_equal(g6.data_to_n(g6.n_to_data(i))[0], i) assert_equal(g6.data_to_n(g6.n_to_data(i))[1], []) assert_equal(g6.data_to_n(g6.n_to_data(i) + [42, 43])[1], [42, 43])
def test_n_data_n_conversion(self): for i in [0, 1, 42, 62, 63, 64, 258047, 258048, 7744773, 68719476735]: assert g6.data_to_n(g6.n_to_data(i))[0] == i assert g6.data_to_n(g6.n_to_data(i))[1] == [] assert g6.data_to_n(g6.n_to_data(i) + [42, 43])[1] == [42, 43]
def generate_sparse6(G, nodes=None, header=True): """Generate sparse6 format string from an undirected graph. Parameters ---------- G : Graph (undirected) nodes: list or iterable Nodes are labeled 0...n-1 in the order provided. If None the ordering given by G.nodes() is used. header: bool If True add '>>sparse6<<' string to head of data Returns ------- s : string String in sparse6 format Raises ------ NetworkXError If the graph is directed Examples -------- >>> G = nx.MultiGraph([(0, 1), (0, 1), (0, 1)]) >>> nx.generate_sparse6(G) '>>sparse6<<:A_' See Also -------- read_sparse6, parse_sparse6, write_sparse6 Notes ----- The format does not support edge or node labels. References ---------- Sparse6 specification: http://cs.anu.edu.au/~bdm/data/formats.txt for details. """ n = G.order() k = 1 while 1<<k < n: k += 1 def enc(x): """Big endian k-bit encoding of x""" return [1 if (x & 1 << (k-1-i)) else 0 for i in range(k)] if nodes is None: ns = list(G.nodes()) # number -> node else: ns = list(nodes) ndict = dict(((ns[i], i) for i in range(len(ns)))) # node -> number edges = [(ndict[u], ndict[v]) for (u, v) in G.edges()] edges = [(max(u,v), min(u,v)) for (u, v) in edges] edges.sort() bits = [] curv = 0 for (v, u) in edges: if v == curv: # current vertex edge bits.append(0) bits.extend(enc(u)) elif v == curv + 1: # next vertex edge curv += 1 bits.append(1) bits.extend(enc(u)) else: # skip to vertex v and then add edge to u curv = v bits.append(1) bits.extend(enc(v)) bits.append(0) bits.extend(enc(u)) if k < 6 and n == (1 << k) and ((-len(bits)) % 6) >= k and curv < (n - 1): # Padding special case: small k, n=2^k, # more than k bits of padding needed, # current vertex is not (n-1) -- # appending 1111... would add a loop on (n-1) bits.append(0) bits.extend([1] * ((-len(bits)) % 6)) else: bits.extend([1] * ((-len(bits)) % 6)) data = [(bits[i+0]<<5) + (bits[i+1]<<4) + (bits[i+2]<<3) + (bits[i+3]<<2) + (bits[i+4]<<1) + (bits[i+5]<<0) for i in range(0, len(bits), 6)] res = (':' + data_to_graph6(n_to_data(n)) + data_to_graph6(data)) if header: return '>>sparse6<<' + res else: return res
def _generate_sparse6_bytes(G, nodes, header): """Yield bytes in the sparse6 encoding of a graph. `G` is an undirected simple graph. `nodes` is the list of nodes for which the node-induced subgraph will be encoded; if `nodes` is the list of all nodes in the graph, the entire graph will be encoded. `header` is a Boolean that specifies whether to generate the header ``b'>>sparse6<<'`` before the remaining data. This function generates `bytes` objects in the following order: 1. the header (if requested), 2. the encoding of the number of nodes, 3. each character, one-at-a-time, in the encoding of the requested node-induced subgraph, 4. a newline character. This function raises :exc:`ValueError` if the graph is too large for the graph6 format (that is, greater than ``2 ** 36`` nodes). """ n = len(G) if n >= 2 ** 36: raise ValueError('sparse6 is only defined if number of nodes is less ' 'than 2 ** 36') if header: yield b'>>sparse6<<' yield b':' for d in n_to_data(n): yield str.encode(chr(d + 63)) k = 1 while 1<<k < n: k += 1 def enc(x): """Big endian k-bit encoding of x""" return [1 if (x & 1 << (k-1-i)) else 0 for i in range(k)] edges = sorted((max(u, v), min(u, v)) for u, v in G.edges()) bits = [] curv = 0 for (v, u) in edges: if v == curv: # current vertex edge bits.append(0) bits.extend(enc(u)) elif v == curv + 1: # next vertex edge curv += 1 bits.append(1) bits.extend(enc(u)) else: # skip to vertex v and then add edge to u curv = v bits.append(1) bits.extend(enc(v)) bits.append(0) bits.extend(enc(u)) if k < 6 and n == (1 << k) and ((-len(bits)) % 6) >= k and curv < (n - 1): # Padding special case: small k, n=2^k, # more than k bits of padding needed, # current vertex is not (n-1) -- # appending 1111... would add a loop on (n-1) bits.append(0) bits.extend([1] * ((-len(bits)) % 6)) else: bits.extend([1] * ((-len(bits)) % 6)) data = [(bits[i+0]<<5) + (bits[i+1]<<4) + (bits[i+2]<<3) + (bits[i+3]<<2) + (bits[i+4]<<1) + (bits[i+5]<<0) for i in range(0, len(bits), 6)] for d in data: yield str.encode(chr(d + 63)) yield b'\n'
def test_n_data_n_conversion(self): for i in [0, 1, 42, 62, 63, 64, 258047, 258048, 7744773, 68719476735]: assert_equal(g6.data_to_n(g6.n_to_data(i))[0], i) assert_equal(g6.data_to_n(g6.n_to_data(i))[1], []) assert_equal(g6.data_to_n(g6.n_to_data(i) + [42, 43])[1], [42, 43])