178 lines
5.4 KiB
Python
178 lines
5.4 KiB
Python
|
"""
|
||
|
Generators for the small graph atlas.
|
||
|
"""
|
||
|
import gzip
|
||
|
import os
|
||
|
import os.path
|
||
|
from itertools import islice
|
||
|
|
||
|
import networkx as nx
|
||
|
|
||
|
__all__ = ["graph_atlas", "graph_atlas_g"]
|
||
|
|
||
|
#: The total number of graphs in the atlas.
|
||
|
#:
|
||
|
#: The graphs are labeled starting from 0 and extending to (but not
|
||
|
#: including) this number.
|
||
|
NUM_GRAPHS = 1253
|
||
|
|
||
|
#: The absolute path representing the directory containing this file.
|
||
|
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
|
|
||
|
#: The path to the data file containing the graph edge lists.
|
||
|
#:
|
||
|
#: This is the absolute path of the gzipped text file containing the
|
||
|
#: edge list for each graph in the atlas. The file contains one entry
|
||
|
#: per graph in the atlas, in sequential order, starting from graph
|
||
|
#: number 0 and extending through graph number 1252 (see
|
||
|
#: :data:`NUM_GRAPHS`). Each entry looks like
|
||
|
#:
|
||
|
#: .. sourcecode:: text
|
||
|
#:
|
||
|
#: GRAPH 6
|
||
|
#: NODES 3
|
||
|
#: 0 1
|
||
|
#: 0 2
|
||
|
#:
|
||
|
#: where the first two lines are the graph's index in the atlas and the
|
||
|
#: number of nodes in the graph, and the remaining lines are the edge
|
||
|
#: list.
|
||
|
#:
|
||
|
#: This file was generated from a Python list of graphs via code like
|
||
|
#: the following::
|
||
|
#:
|
||
|
#: import gzip
|
||
|
#: from networkx.generators.atlas import graph_atlas_g
|
||
|
#: from networkx.readwrite.edgelist import write_edgelist
|
||
|
#:
|
||
|
#: with gzip.open('atlas.dat.gz', 'wb') as f:
|
||
|
#: for i, G in enumerate(graph_atlas_g()):
|
||
|
#: f.write(bytes(f'GRAPH {i}\n', encoding='utf-8'))
|
||
|
#: f.write(bytes(f'NODES {len(G)}\n', encoding='utf-8'))
|
||
|
#: write_edgelist(G, f, data=False)
|
||
|
#:
|
||
|
ATLAS_FILE = os.path.join(THIS_DIR, "atlas.dat.gz")
|
||
|
|
||
|
|
||
|
def _generate_graphs():
|
||
|
"""Sequentially read the file containing the edge list data for the
|
||
|
graphs in the atlas and generate the graphs one at a time.
|
||
|
|
||
|
This function reads the file given in :data:`.ATLAS_FILE`.
|
||
|
|
||
|
"""
|
||
|
with gzip.open(ATLAS_FILE, "rb") as f:
|
||
|
line = f.readline()
|
||
|
while line and line.startswith(b"GRAPH"):
|
||
|
# The first two lines of each entry tell us the index of the
|
||
|
# graph in the list and the number of nodes in the graph.
|
||
|
# They look like this:
|
||
|
#
|
||
|
# GRAPH 3
|
||
|
# NODES 2
|
||
|
#
|
||
|
graph_index = int(line[6:].rstrip())
|
||
|
line = f.readline()
|
||
|
num_nodes = int(line[6:].rstrip())
|
||
|
# The remaining lines contain the edge list, until the next
|
||
|
# GRAPH line (or until the end of the file).
|
||
|
edgelist = []
|
||
|
line = f.readline()
|
||
|
while line and not line.startswith(b"GRAPH"):
|
||
|
edgelist.append(line.rstrip())
|
||
|
line = f.readline()
|
||
|
G = nx.Graph()
|
||
|
G.name = f"G{graph_index}"
|
||
|
G.add_nodes_from(range(num_nodes))
|
||
|
G.add_edges_from(tuple(map(int, e.split())) for e in edgelist)
|
||
|
yield G
|
||
|
|
||
|
|
||
|
def graph_atlas(i):
|
||
|
"""Returns graph number `i` from the Graph Atlas.
|
||
|
|
||
|
For more information, see :func:`.graph_atlas_g`.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
i : int
|
||
|
The index of the graph from the atlas to get. The graph at index
|
||
|
0 is assumed to be the null graph.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
list
|
||
|
A list of :class:`~networkx.Graph` objects, the one at index *i*
|
||
|
corresponding to the graph *i* in the Graph Atlas.
|
||
|
|
||
|
See also
|
||
|
--------
|
||
|
graph_atlas_g
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
The time required by this function increases linearly with the
|
||
|
argument `i`, since it reads a large file sequentially in order to
|
||
|
generate the graph [1]_.
|
||
|
|
||
|
References
|
||
|
----------
|
||
|
.. [1] Ronald C. Read and Robin J. Wilson, *An Atlas of Graphs*.
|
||
|
Oxford University Press, 1998.
|
||
|
|
||
|
"""
|
||
|
if not (0 <= i < NUM_GRAPHS):
|
||
|
raise ValueError(f"index must be between 0 and {NUM_GRAPHS}")
|
||
|
return next(islice(_generate_graphs(), i, None))
|
||
|
|
||
|
|
||
|
def graph_atlas_g():
|
||
|
"""Returns the list of all graphs with up to seven nodes named in the
|
||
|
Graph Atlas.
|
||
|
|
||
|
The graphs are listed in increasing order by
|
||
|
|
||
|
1. number of nodes,
|
||
|
2. number of edges,
|
||
|
3. degree sequence (for example 111223 < 112222),
|
||
|
4. number of automorphisms,
|
||
|
|
||
|
in that order, with three exceptions as described in the *Notes*
|
||
|
section below. This causes the list to correspond with the index of
|
||
|
the graphs in the Graph Atlas [atlas]_, with the first graph,
|
||
|
``G[0]``, being the null graph.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
list
|
||
|
A list of :class:`~networkx.Graph` objects, the one at index *i*
|
||
|
corresponding to the graph *i* in the Graph Atlas.
|
||
|
|
||
|
See also
|
||
|
--------
|
||
|
graph_atlas
|
||
|
|
||
|
Notes
|
||
|
-----
|
||
|
This function may be expensive in both time and space, since it
|
||
|
reads a large file sequentially in order to populate the list.
|
||
|
|
||
|
Although the NetworkX atlas functions match the order of graphs
|
||
|
given in the "Atlas of Graphs" book, there are (at least) three
|
||
|
errors in the ordering described in the book. The following three
|
||
|
pairs of nodes violate the lexicographically nondecreasing sorted
|
||
|
degree sequence rule:
|
||
|
|
||
|
- graphs 55 and 56 with degree sequences 001111 and 000112,
|
||
|
- graphs 1007 and 1008 with degree sequences 3333444 and 3333336,
|
||
|
- graphs 1012 and 1213 with degree sequences 1244555 and 1244456.
|
||
|
|
||
|
References
|
||
|
----------
|
||
|
.. [atlas] Ronald C. Read and Robin J. Wilson,
|
||
|
*An Atlas of Graphs*.
|
||
|
Oxford University Press, 1998.
|
||
|
|
||
|
"""
|
||
|
return list(_generate_graphs())
|