Commit 7da1f0a9 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Ongoing documentation improvement

This modified several docstrings and introduces a quick-start guide.
parent 9d542a89
{% extends "!layout.html" %}
{% block rootrellink %}
<li><img src="/gfx/graph-icon.png" alt="logo" style="margin-right: 5px; margin-bottom:-2px;" /><a href="/graph-tool/">Project Homepage</a> &raquo;</li>
{{ super() }}
{% endblock %}
sphinx-build -E -b html . build
OMP_NUM_THREADS=1 sphinx-build -b doctest . build
rsync -rEvpLz build/*
.. automodule:: graph_tool.centrality
.. automodule:: graph_tool.clustering
.. automodule::
......@@ -16,7 +16,7 @@ import sys, os
# If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
# General configuration
# ---------------------
......@@ -43,7 +43,7 @@ master_doc = 'index'
# General information about the project.
project = u'graph-tool'
copyright = u'2008, Tiago de Paula Peixoto'
copyright = u'2009, Tiago de Paula Peixoto <>'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
......@@ -52,7 +52,7 @@ copyright = u'2008, Tiago de Paula Peixoto'
# The short X.Y version.
version = '2.0'
# The full version, including alpha/beta/rc tags.
release = '2.0'
release = '2.0beta1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......@@ -88,6 +88,13 @@ exclude_trees = ['.build']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# doctest
doctest_global_setup = \
import graph_tool.all as gt
from numpy import array
# Options for HTML output
# -----------------------
......@@ -111,7 +118,7 @@ html_style = 'default.css'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
html_favicon = "graph-icon.ico"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
......@@ -143,12 +150,12 @@ html_static_path = ['.static']
#html_split_index = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
#html_copy_source = True
html_copy_source = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
......@@ -192,4 +199,7 @@ latex_documents = [
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'': None}
intersphinx_mapping = {'': None,
'': None,
'': None,
'' : None}
.. automodule:: graph_tool.correlations
.. automodule:: graph_tool.draw
.. automodule:: graph_tool.generation
......@@ -6,4 +6,13 @@ Available subpackages
.. toctree::
\ No newline at end of file
.. automodule:: graph_tool.misc
#! /usr/bin/env python
# We probably will need some things from several places
import sys, os
from pylab import * # for plotting
from numpy.random import * # for random sampling
# We need to import the graph_tool module itself
from graph_tool.all import *
# let's construct a Price network (the one that existed before Barabasi). It is
# a directed network, with preferential attachment. The algorithm below is a
# very naive, and a bit slow, but quite simple.
# We start with an empty, directed graph
g = Graph()
# We want also to keep the age information for each vertex and edge. For that
# let's create some property maps
v_age = g.new_vertex_property("int")
e_age = g.new_edge_property("int")
# The final size of the network
N = 100000
# We have to start with one vertex
v = g.add_vertex()
v_age[v] = 0
# we will keep a list of the vertices. The number of times a vertex is in this
# list will give the probability of it being selected.
vlist = [v]
# let's now add the new edges and vertices
for i in xrange(1, N):
# create our new vertex
v = g.add_vertex()
v_age[v] = i
# we need to sample a new vertex to be the target, based on its in-degree +
# 1. For that, we simply randomly sample it from vlist.
i = randint(0, len(vlist))
target = vlist[i]
# add edge
e = g.add_edge(v, target)
e_age[e] = i
# put v and target in the list
# now we have a graph!
# let's calculate its in-degree distribution and plot it to a file
in_hist = vertex_hist(g, "in")
errorbar(in_hist[1], in_hist[0], fmt=".", yerr=sqrt(in_hist[0]))
# let's do a random walk on the graph and print the age of the vertices we find,
# just for fun.
v = g.vertex(randint(0, g.num_vertices()))
for i in xrange(0, 100):
print "vertex:", v, "in-degree:", v.in_degree(), "out-degree:",\
v.out_degree(), "age:", v_age[v]
if v.out_degree() == 0:
print "Nowhere else to go... We found the main hub!"
n_list = []
for w in v.out_neighbours():
v = n_list[randint(0, len(n_list))]
# let's save our graph for posterity We want to save the age properties as
# well... To do this, they must become "internal" properties, as such:
g.vertex_properties["age"] = v_age
g.edge_properties["age"] = e_age
# now we can save it"price.xml.gz")
Quick start using `graph-tool`
Testy test test
The :mod:`graph_tool` module provides a :class:`~graph_tool.Graph` class and
several algorithms that operate on it. The internals of this class, and of most
algorithms, are written in C++ for performance.
.. math::
The module must be of course imported before it can be used. The package is
subdivided into several sub-modules. To import everything from all of them, one
can do:
(a + b)^2 = a^2 + 2ab + b^2
.. doctest::
>>> from graph_tool.all import *
In the following, it will always be assumed that the previous line was run.
Creating and manipulating graphs
An empty graph can be created by instantiating a :class:`~graph_tool.Graph`
.. doctest::
>>> g = Graph()
By default, newly created graphs are always directed. To construct undirected
graphs, one must pass the ``directed`` parameter:
.. doctest::
>>> ug = Graph(directed=False)
A graph can always be switched on-the-fly from directed to undirected (and vice
versa), with the :meth:`~graph_tool.Graph.set_directed` method. The "directedness" of the
graph can be queried with the :meth:`~graph_tool.Graph.is_directed` method,
.. doctest::
>>> ug = Graph()
>>> ug.set_directed(False)
>>> assert(ug.is_directed() == False)
A graph can also be created from another graph, in which case the entire graph
(and its internal property maps, see :ref:`sec_property_maps`) is copied:
.. doctest::
>>> g1 = Graph()
>>> # ... populate g1 ...
>>> g2 = Graph(g1) # g1 and g2 are copies
Once a graph is created, it can be populated with vertices and edges. A vertex
can be added with the :meth:`~graph_tool.Graph.add_vertex` method,
.. doctest::
>>> v = g.add_vertex()
which returns an instance of a :class:`~graph_tool.Vertex` class, also called a
*vertex descriptor*. The :meth:`~graph_tool.Graph.add_vertex` method also
accepts an optional parameter which specifies the number of vertices to
create. If this value is greater than 1, it returns a list of vertices:
.. doctest::
>>> vlist = g.add_vertex(10)
>>> print len(vlist)
Each vertex has an unique index, which is numbered from 0 to N-1, where N is the
number of vertices. This index can be obtained by using the
:attr:`~graph_tool.Graph.vertex_index` attribute of the graph (which is a
*property map*, see :ref:`sec_property_maps`), or by converting the vertex
descriptor to an int.
.. doctest::
>>> v = g.add_vertex()
>>> print g.vertex_index[v], int(v)
11 11
There is no need to keep the vertex descriptor lying around to access them at a
later point: One can obtain the descriptor of a vertex with a given index using
the :meth:`~graph_tool.Graph.vertex` method,
.. doctest::
>>> print g.vertex(8)
Another option is to iterate through the vertices, as described in section
Once we have some vertices in the graph, we can create some edges between them
with the :meth:`~graph_tool.Graph.add_edge` method, which returns an edge
descriptor (an instance of the :class:`~graph_tool.Edge` class).
.. doctest::
>>> v1 = g.add_vertex()
>>> v2 = g.add_vertex()
>>> e = g.add_edge(v1, v2)
Edges also have an unique index, which is given by the :attr:`~graph_tool.Graph.edge_index`
.. doctest::
>>> print g.edge_index[e]
Unlike the vertices, edge indexes are not guaranteed to be continuous in any
range, but they are always unique.
Both vertex and edge descriptors have methods which query associate information,
such as :meth:`~graph_tool.Vertex.in_degree`,
:meth:`~graph_tool.Vertex.out_degree`, :meth:`~graph_tool.Edge.source` and
.. doctest::
>>> v1 = g.add_vertex()
>>> v2 = g.add_vertex()
>>> e = g.add_edge(v1, v2)
>>> print v1.out_degree(), v2.in_degree()
1 1
>>> assert(e.source() == v1 and == v2)
Edges and vertices can also be removed at any time with the
:meth:`~graph_tool.Graph.remove_vertex` and :meth:`~graph_tool.Graph.remove_edge` methods,
.. doctest::
>>> e = g.add_edge(g.vertex(0), g.vertex(1))
>>> g.remove_edge(e) # e no longer exists
>>> g.remove_vertex(g.vertex(1)) # the second vertex is also gone
.. _sec_iteration:
Iterating over vertices and edges
Algorithms must often iterate through the vertices, edges, out edge, etc. of the
graph. The :class:`~graph_tool.Graph` and :class:`~graph_tool.Edge` classes
provide the necessary iterators for doing so. The iterators always give back
edge or vertex descriptors.
In order to iterate through the vertices or edges of the graph, the
:meth:`~graph_tool.Graph.vertices` and :meth:`~graph_tool.Graph.edges` methods should be used, as such:
.. doctest::
for v in g.vertices():
print v
for e in e.vertices():
print e
The code above will print the vertices and edges of the graph in the order they
are found.
The out- and in-edges of a vertex, as well as the out- and in-neighbours can be
iterated through with the :meth:`~graph_tool.Vertex.out_edges`,
:meth:`~graph_tool.Vertex.in_edges`, :meth:`~graph_tool.Vertex.out_neighbours`
and :meth:`~graph_tool.Vertex.in_neighbours` respectively.
.. doctest::
from itertools import izip
for v in g.vertices():
for e in v.out_edges():
print e
for e in v.out_neighbours():
print e
# the edge and neighbours order always match
for e,w in izip(v.out_edges(), v.out_neighbours()):
assert( == w)
.. warning:
You should never remove vertex or edge descriptors when iterating over them,
since this invalidates the iterators. If you plan to remove vertices or edges
during iteration, you must first store them somewhere (such as in a list) and
remove them only later. Removal during iteration will cause bad things to
.. _sec_property_maps:
Property maps
Property maps are a way of associating additional to the vertices, edges or to
the graph itself. There are thus three types of property maps: vertex, edge and
graph. All of them are instances of the same class,
:class:`~graph_tool.PropertyMap`. Each property map has an associated *value
type*, which must be chosen from the predefined set:
.. tabularcolumns:: |l|l|
.. table::
======================= ================
Type name Aliases
======================= ================
``bool`` ``uint8_t``
``int32_t`` ``int``
``int64_t`` ``long``
``double`` ``float``
``long double``
``vector<bool>`` ``vector<uint8_t>``
``vector<int32_t>`` ``vector<int>``
``vector<int64_t>`` ``vector<long>``
``vector<double>`` ``vector<float>``
``vector<long double>``
``python::object`` ``object``
======================= ================
New property maps can be created for a given graph by calling the
:meth:`~graph_tool.Graph.new_vertex_property`, :meth:`~graph_tool.Graph.new_edge_property`, or
:meth:`~graph_tool.Graph.new_graph_property`, for each map type. The values are then
accessed by vertex or edge descriptors, or the graph itself, as such:
.. doctest::
from itertools import izip
from numpy.random import randint
g = Graph()
# insert some random links
for s,t in izip(randint(0, 100, 100), randint(0, 100, 100)):
g.add_edge(g.vertex(s), g.vertex(t))
vprop_double = g.new_vertex_property("double")
vprop_vint = g.new_vertex_property("vector<int>")
eprop_dict = g.new_edge_property("object")
gprop_bool = g.new_edge_property("bool")
vprop_double[g.vertex(10)] = 3.1416
vprop_vint[g.vertex(40)] = [1, 3, 42, 54]
eprop_dict[g.edges().next()] = {"foo":"bar", "gnu":42}
gprop_bool[g] = True
Property maps with scalar value types can also be accessed as a numpy
:class:`~numpy.ndarray`, with the :meth:`~graph_tool.PropertyMap.get_array`
method, i.e.,
.. doctest::
from numpy.random import random
# this assigns random values to the properties
vprop_double.get_array()[:] = random(g.num_vertices())
Internal property maps
Any created property map can be made "internal" to the respective graph. This
means that it will be copied and saved to a file together with the
graph. Properties are internalized by including them in the graph's
dictionary-like attributes :attr:`~graph_tool.Graph.vertex_properties`,
:attr:`~graph_tool.Graph.edge_properties` or
:attr:`~graph_tool.Graph.graph_properties`. When inserted in the graph, the
property maps must have an unique name (between those of the same type):
.. doctest::
>>> eprop = g.new_edge_property("string")
>>> g.edge_properties["some name"] = eprop
>>> g.list_properties()
some name (edge) (type: string)
Graph I/O
Graphs can be saved and loaded in two formats: `graphml
<>`_ and `dot
<>`_. Graphml is the default and
preferred format. The dot format is also supported, but since it contains no
type information, all properties are read later as strings, and must be
converted per hand. Therefore you should always use graphml, except when
interfacing with another software which expects dot format.
A graph can be saved or loaded to a file with the :attr:``
and :attr:`~graph_tool.Graph.load` methods, which take either a file name or a
file-like object. A graph can also be loaded from disk with the
:func:`~graph_tool.load_graph` function, as such:
.. doctest::
g = Graph()
# ... fill the graph ..."my_graph.xml.gz")
g2 = load_graph("my_graph.xml.gz")
# g and g2 should be a copy of each other
Graph classes can also be pickled with the :mod:`pickle` module.
An Example: Building a Price Network
.. literalinclude::
.. testcode::
from price import *
.. testoutput::
vertex: 36063 in-degree: 0 out-degree: 1 age: 36063
vertex: 9075 in-degree: 4 out-degree: 1 age: 9075
vertex: 5967 in-degree: 3 out-degree: 1 age: 5967
vertex: 1113 in-degree: 7 out-degree: 1 age: 1113
vertex: 25 in-degree: 84 out-degree: 1 age: 25
vertex: 10 in-degree: 541 out-degree: 1 age: 10
vertex: 5 in-degree: 140 out-degree: 1 age: 5
vertex: 2 in-degree: 459 out-degree: 1 age: 2
vertex: 1 in-degree: 520 out-degree: 1 age: 1
vertex: 0 in-degree: 210 out-degree: 0 age: 0
Nowhere else to go... We found the main hub!
.. image:: deg-hist.png
(a - b)^2 = a^2 - 2ab + b^2
.. automodule:: graph_tool.run_action
.. automodule:: graph_tool.stats
.. automodule:: graph_tool.util
......@@ -66,7 +66,7 @@ misc
vertex and edge statistics
assorted untilities
assorted utilities
......@@ -77,6 +77,9 @@ show_config
``graph_tool`` version string
__author__="Tiago de Paula Peixoto <>"
......@@ -17,8 +17,8 @@
# along with this program. If not, see <>.
``graph_tool.centrality`` - Centrality measures
This module includes centrality-related algorithms.
......@@ -55,7 +55,7 @@ def pagerank(g, damping=0.8, prop=None, epslon=1e-6, max_iter=None,
A vertex property map containing the PageRank values.
pagerank : A vertex property map containing the PageRank values.
See Also
......@@ -65,12 +65,13 @@ def pagerank(g, damping=0.8, prop=None, epslon=1e-6, max_iter=None,
The value of PageRank of vertex v :math:`PR(v)` is given interactively by
the relation:
The value of PageRank [pagerank_wikipedia]_ of vertex v :math:`PR(v)` is
given interactively by the relation:
.. math::
.. math:
PR(v) = \frac{1-d}{N} + d \sum_{w \in \Gamma^{-}(v)}
\frac{PR (w)}{d^{+}(w)}</math>
\frac{PR (w)}{d^{+}(w)}
where :math:`\Gamma^{-}(v)` are the in-neighbours of v, :math:`d^{+}(w)` is
the out-degree of w, and d is a damping factor.
......@@ -83,33 +84,34 @@ def pagerank(g, damping=0.8, prop=None, epslon=1e-6, max_iter=None,
>>> from numpy.random import poisson
>>> from numpy.random import poisson, seed
>>> seed(42)
>>> g = gt.random_graph(100, lambda: (poisson(3), poisson(3)), seed=42)
>>> pr = gt.pagerank(g)