source: src/graph_tool/generation/__init__.py @ 41686e

Revision 41686e, 7.9 KB checked in by Tiago de Paula Peixoto <tiago@…>, 4 years ago (diff)
Move random_rewire() to 'generation' module
  • Property mode set to 100644
Line 
1#! /usr/bin/env python
2# graph_tool.py -- a general graph manipulation python module
3#
4# Copyright (C) 2007 Tiago de Paula Peixoto <tiago@forked.de>
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19"""
20``graph_tool.generation`` - Random Graph Generation
21---------------------------------------------------
22"""
23
24from .. dl_import import dl_import
25dl_import("import libgraph_tool_generation")
26
27from .. core import Graph
28import sys, numpy
29
30__all__ = ["random_graph", "random_rewire"]
31
32def _corr_wrap(i, j, corr):
33    return corr(i[1], j[1])
34
35def random_graph(N, deg_sampler, deg_corr=None, directed=True,
36                 parallel=False, self_loops=False,
37                 seed=0, verbose=False):
38    r"""
39    Generate a random graph, with a given degree distribution and correlation.
40
41    Parameters
42    ----------
43    N : int
44        Number of vertices in the graph.
45    deg_sampler : function
46        A degree sampler function which is called without arguments, and returns
47        a tuple of ints representing the in and out-degree of a given vertex (or
48        a single int for undirected graphs, representing the out-degree). This
49        function is called once per vertex, but may be called more times, if the
50        degree sequence cannot be used to build a graph.
51    deg_corr : function (optional, default: None)
52        A function which give the degree correlation of the graph. It should be
53        callable with two parameters: the in,out-degree pair of the source
54        vertex an edge, and the in,out-degree pair of the target of the same
55        edge (for undirected graphs, both parameters are single values). The
56        function should return a number proportional to the probability of such
57        an edge existing in the generated graph.
58    directed : bool (optional, default: True)
59        Whether the generated graph should be directed.
60    parallel : bool (optional, default: False)
61        If True, parallel edges are allowed.
62    self_loops : bool (optional, default: False)
63        If True, self-loops are allowed.
64    seed : int (optional, default: 0)
65        Seed for the random number generator. If seed=0, a random value is
66        chosen.
67
68    Returns
69    -------
70    random_graph : Graph
71        The generated graph.
72
73    See Also
74    --------
75    random_rewire: in place graph shuffling
76
77    Notes
78    -----
79    The algorithm maintains a list of all available source and target degree
80    pairs, such that the deg_corr function is called only once with the same
81    parameters.
82
83    The uncorrelated case, the complexity is :math:`O(V+E)`. For the correlated
84    case the worst-case complexity is :math:`O(V^2)`, but the typical case has
85    complexity :math:`O(V+N_k^2)`, where :math:`N_k < V` is the number of
86    different degrees sampled (or in,out-degree pairs).
87
88    Examples
89    --------
90
91    >>> from numpy.random import randint, random, seed, poisson
92    >>> from pylab import *
93    >>> seed(42)
94
95    This is a degree sampler which uses rejection sampling to sample from the
96    distribution :math:`P(k)\propto 1/k`, up to a maximum.
97
98    >>> def sample_k(max):
99    ...     accept = False
100    ...     while not accept:
101    ...         k = randint(1,max+1)
102    ...         accept = random() < 1.0/k
103    ...     return k
104    ...
105
106    The following generates a random undirected graph with degree distribution
107    :math:`P(k)\propto 1/k` (with k_max=40) and an *assortative* degree
108    correlation of the form:
109
110    .. math::
111
112        P(i,k) \propto \frac{1}{1+|i-k|}
113
114    >>> g = gt.random_graph(1000, lambda: sample_k(40),
115    ...                     lambda i,k: 1.0/(1+abs(i-k)), directed=False)
116    >>> gt.scalar_assortativity(g, "out")
117    (0.52810631736984548, 0.012618649197538264)
118
119    The following samples an in,out-degree pair from the joint distribution:
120
121    .. math::
122
123        p(j,k) = \frac{1}{2}\frac{e^{-m_1}m_1^j}{j!}\frac{e^{-m_1}m_1^k}{k!} +
124                 \frac{1}{2}\frac{e^{-m_2}m_2^j}{j!}\frac{e^{-m_2}m_2^k}{k!}
125
126    with :math:`m_1 = 4` and :math:`m_2 = 20`.
127
128    >>> def deg_sample():
129    ...    if random() > 0.5:
130    ...        return poisson(4), poisson(4)
131    ...    else:
132    ...        return poisson(20), poisson(20)
133    ...
134
135    The following generates a random directed graph with this distribution, and
136    plots the combined degree correlation.
137
138    >>> g = gt.random_graph(20000, deg_sample)
139    >>>
140    >>> hist = gt.combined_corr_hist(g, "in", "out")
141    >>> imshow(hist[0], interpolation="nearest")
142    <...>
143    >>> colorbar()
144    <...>
145    >>> xlabel("in degree")
146    <...>
147    >>> ylabel("out degree")
148    <...>
149    >>> savefig("combined-deg-hist.png")
150
151    .. figure:: combined-deg-hist.png
152        :align: center
153
154        Combined degree histogram.
155
156    A correlated directed graph can be build as follows. Consider the following
157    degree correlation:
158
159    .. math::
160
161         P(j',k'|j,k)=\frac{e^{-k}k^{j'}}{j'!}
162         \frac{e^{-(20-j)}(20-j)^{k'}}{k'!}
163
164    i.e., the in->out correlation is "disassortative", the out->in correlation
165    is "assortative", and everything else is uncorrelated.
166    We will use a flat degree distribution in the range [1,20).
167
168    >>> p = scipy.stats.poisson
169    >>> g = gt.random_graph(20000, lambda: (sample_k(19), sample_k(19)),
170    ...                                     lambda a,b: (p.pmf(a[0],b[1])*
171    ...                                                  p.pmf(a[1],20-b[0])))
172
173    Lets plot the average degree correlations to check.
174
175    >>> clf()
176    >>> corr = gt.avg_neighbour_corr(g, "in", "in")
177    >>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="<in> vs in")
178    (...)
179    >>> corr = gt.avg_neighbour_corr(g, "in", "out")
180    >>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="<out> vs in")
181    (...)
182    >>> corr = gt.avg_neighbour_corr(g, "out", "in")
183    >>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="<in> vs out")
184    (...)
185    >>> corr = gt.avg_neighbour_corr(g, "out", "out")
186    >>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="<out> vs out")
187    (...)
188    >>> legend(loc="best")
189    <...>
190    >>> xlabel("source degree")
191    <...>
192    >>> ylabel("average target degree")
193    <...>
194    >>> savefig("deg-corr-dir.png")
195
196    .. figure:: deg-corr-dir.png
197        :align: center
198
199        Average nearest neighbour correlations.
200
201    """
202    if seed == 0:
203        seed = numpy.random.randint(0, sys.maxint)
204    g = Graph()
205    if deg_corr == None:
206        uncorrelated = True
207    else:
208        uncorrelated = False
209    if not directed and deg_corr != None:
210        corr = lambda i,j: _corr_wrap(i, j, deg_corr)
211    else:
212        corr = deg_corr
213    libgraph_tool_generation.gen_random_graph(g._Graph__graph, N,
214                                              deg_sampler, corr,
215                                              uncorrelated, not parallel,
216                                              not self_loops, not directed,
217                                              seed, verbose)
218    g.set_directed(directed)
219    return g
220
221def random_rewire(g, strat="uncorrelated", self_loops = False,
222                  parallel_edges = False, seed = 0):
223    if seed != 0:
224        seed = random.randint(0, sys.maxint)
225    if g.is_reversed():
226        was_reversed = True
227    else:
228        was_reversed = False
229    g.set_reversed(False)
230    libgraph_tool_generation.random_rewire(g._Graph__graph, strat, self_loops,
231                                           parallel_edges, seed)
232    if was_reversed:
233        g.set_reversed(True)
234
Note: See TracBrowser for help on using the repository browser.