Commit 8240714e authored by Tiago Peixoto's avatar Tiago Peixoto

Several improvements to random_rewire() / random_graph()

This introduces several simplifications and corrections to the graph
rewire algorithm, to guarantee unbiased sampling.

Now a move is outright rejected if it produces a
self-loop/parallel-edge, instead of retried. This also adds a
"non-sweep" mode, where edges are rewired randomly, possibly with
repetition.

The edge moves are now simplified to the target of the edges only,
since swaping sources is redundant.

The number of iterations can now be explicitly modified, so it is not
necessary to call the function more than once, and it is emphasized in
the documentation that only after sufficiently many iterations can the
graph be guaranteed to be fully mixed.
parent aeaadabc
......@@ -48,11 +48,11 @@ private:
python::object _o;
};
void generate_random_graph(GraphInterface& gi, size_t N,
python::object deg_sample,
bool uncorrelated, bool no_parallel,
bool no_self_loops, bool undirected,
size_t seed, bool verbose, bool verify)
void generate_graph(GraphInterface& gi, size_t N,
python::object deg_sample,
bool uncorrelated, bool no_parallel,
bool no_self_loops, bool undirected,
size_t seed, bool verbose, bool verify)
{
typedef graph_tool::detail::get_all_graph_views::apply<
graph_tool::detail::scalar_pairs, mpl::bool_<false>,
......@@ -65,7 +65,7 @@ void generate_random_graph(GraphInterface& gi, size_t N,
if (uncorrelated)
{
run_action<graph_views>()
(gi, bind<void>(gen_random_graph(), _1, N,
(gi, bind<void>(gen_graph(), _1, N,
PythonFuncWrap(deg_sample),
no_parallel, no_self_loops,
seed, verbose, verify))();
......@@ -73,7 +73,7 @@ void generate_random_graph(GraphInterface& gi, size_t N,
else
{
run_action<graph_views>()
(gi, bind<void>(gen_random_graph(), _1, N,
(gi, bind<void>(gen_graph(), _1, N,
PythonFuncWrap(deg_sample),
no_parallel, no_self_loops,
seed, verbose, verify))();
......@@ -81,9 +81,9 @@ void generate_random_graph(GraphInterface& gi, size_t N,
gi.ReIndexEdges();
}
void random_rewire(GraphInterface& gi, string strat, bool self_loops,
bool parallel_edges, python::object corr_prob, size_t seed,
bool verbose);
size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
bool no_sweep, bool self_loops, bool parallel_edges,
python::object corr_prob, size_t seed, bool verbose);
void predecessor_graph(GraphInterface& gi, GraphInterface& gpi,
boost::any pred_map);
void line_graph(GraphInterface& gi, GraphInterface& lgi,
......@@ -107,7 +107,7 @@ using namespace boost::python;
BOOST_PYTHON_MODULE(libgraph_tool_generation)
{
def("gen_random_graph", &generate_random_graph);
def("gen_graph", &generate_graph);
def("random_rewire", &random_rewire);
def("predecessor_graph", &predecessor_graph);
def("line_graph", &line_graph);
......
......@@ -448,7 +448,7 @@ bool update_deg(size_t t_i, const pair<size_t, size_t>& deg, Vset& vset,
return true;
}
struct gen_random_graph
struct gen_graph
{
template <class Graph, class DegSample>
void operator()(Graph& g, size_t N, DegSample& deg_sample, bool no_parallel,
......
......@@ -48,37 +48,43 @@ private:
python::object _o;
};
void random_rewire(GraphInterface& gi, string strat, bool self_loops,
bool parallel_edges, python::object corr_prob, size_t seed,
bool verbose)
size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
bool no_sweep, bool self_loops, bool parallel_edges,
python::object corr_prob, size_t seed, bool verbose)
{
rng_t rng(static_cast<rng_t::result_type>(seed));
PythonFuncWrap corr(corr_prob);
size_t pcount = 0;
if (strat == "erdos")
run_action<graph_tool::detail::never_reversed>()
(gi, boost::bind<void>(graph_rewire<ErdosRewireStrategy>(),
_1, gi.GetEdgeIndex(), boost::ref(corr),
boost::ref(rng), self_loops, parallel_edges,
verbose))();
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
boost::ref(pcount), boost::ref(rng)))();
else if (strat == "uncorrelated")
run_action<graph_tool::detail::never_reversed>()
(gi, boost::bind<void>(graph_rewire<RandomRewireStrategy>(),
_1, gi.GetEdgeIndex(), boost::ref(corr),
boost::ref(rng), self_loops, parallel_edges,
verbose))();
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
boost::ref(pcount), boost::ref(rng)))();
else if (strat == "correlated")
run_action<graph_tool::detail::never_reversed>()
(gi, boost::bind<void>(graph_rewire<CorrelatedRewireStrategy>(),
_1, gi.GetEdgeIndex(), boost::ref(corr),
boost::ref(rng), self_loops, parallel_edges,
verbose))();
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
boost::ref(pcount), boost::ref(rng)))();
else if (strat == "probabilistic")
run_action<>()
(gi, boost::bind<void>(graph_rewire<ProbabilisticRewireStrategy>(),
_1, gi.GetEdgeIndex(), boost::ref(corr),
boost::ref(rng), self_loops, parallel_edges,
verbose))();
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
boost::ref(pcount), boost::ref(rng)))();
else
throw ValueException("invalid random rewire strategy: " + strat);
return pcount;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -115,7 +115,7 @@ def local_clustering(g, prop=None, undirected=True):
>>> g = gt.random_graph(1000, lambda: (5,5))
>>> clust = gt.local_clustering(g)
>>> print gt.vertex_average(g, clust)
(0.009394444444444445, 0.00045574247520250265)
(0.00908888888888889, 0.0004449824521439575)
References
----------
......@@ -172,7 +172,7 @@ def global_clustering(g):
>>> seed(42)
>>> g = gt.random_graph(1000, lambda: (5,5))
>>> print gt.global_clustering(g)
(0.009389461447318415, 0.0004561857327075321)
(0.009114059777509717, 0.0004464454368899158)
References
----------
......@@ -247,11 +247,11 @@ def extended_clustering(g, props=None, max_depth=3, undirected=False):
>>> for i in xrange(0, 5):
... print gt.vertex_average(g, clusts[i])
...
(0.005768333333333334, 0.00048270786829210805)
(0.025800144927536232, 0.0009764383082280506)
(0.11379500000000001, 0.001958443451513926)
(0.3973463043478261, 0.0029727349290168477)
(0.4375050724637681, 0.0029440016153056154)
(0.0058850000000000005, 0.0004726257592782405)
(0.026346666666666668, 0.0009562588213100747)
(0.11638833333333333, 0.002086419787711849)
(0.3862533333333333, 0.003020064612995335)
(0.44685499999999995, 0.003124572962377774)
References
----------
......@@ -322,9 +322,9 @@ def motifs(g, k, p=1.0, motif_list=None):
>>> g = gt.random_graph(1000, lambda: (5,5))
>>> motifs, counts = gt.motifs(gt.GraphView(g, directed=False), 4)
>>> print len(motifs)
14
11
>>> print counts
[114808, 388149, 791, 901, 2064, 3266, 780, 6, 14, 16, 8, 9, 12, 13]
[115104, 389090, 724, 820, 1828, 3208, 791, 4, 12, 12, 3]
References
......@@ -489,7 +489,7 @@ def motif_significance(g, k, n_shuffles=100, p=1.0, motif_list=None,
>>> print len(motifs)
11
>>> print zscores
[-0.77247260114237382, -0.99569269406173944, -0.89282671051270046, 0.3239871430063806, 0.30808421357288784, 0.78512106107239443, 0.53748384988656916, 1.91, -0.12, -0.3, -0.12]
[0.014875553792545083, 0.016154998074953769, 0.002455801898331304, -1.9579019397305546, 0.83542298414538518, 0.84715258999068244, -0.93385230436820643, -0.11, -0.1, -0.31, -0.14]
"""
s_ms, counts = motifs(g, k, p, motif_list)
......
......@@ -106,9 +106,10 @@ def assortativity(g, deg):
... return k
...
>>> g = gt.random_graph(1000, lambda: sample_k(40),
... lambda i,k: 1.0/(1+abs(i-k)), directed=False)
... lambda i,k: 1.0 / (1 + abs(i - k)), directed=False,
... mix_time=100)
>>> gt.assortativity(g, "out")
(0.15254237288135594, 0.005229351128632439)
(0.12973425402803934, 0.004896761863480971)
References
----------
......@@ -176,14 +177,14 @@ def scalar_assortativity(g, deg):
... return k
...
>>> g = gt.random_graph(1000, lambda: sample_k(40), lambda i,k: abs(i-k),
... directed=False)
... directed=False, mix_time=100)
>>> gt.scalar_assortativity(g, "out")
(-0.45180067377519767, 0.010346379229268765)
(-0.3786743856734846, 0.01193890427539548)
>>> g = gt.random_graph(1000, lambda: sample_k(40),
... lambda i,k: 1.0/(1+abs(i-k)),
... directed=False)
... lambda i, k: 1.0 / (1 + abs(i - k)),
... directed=False, mix_time=100)
>>> gt.scalar_assortativity(g, "out")
(0.6245182572712221, 0.011132275391353953)
(0.551341394873777, 0.012430151900034204)
References
----------
......@@ -263,8 +264,8 @@ def corr_hist(g, deg_source, deg_target, bins=[[0, 1], [0, 1]], weight=None,
... return k
...
>>> g = gt.random_graph(10000, lambda: sample_k(40),
... lambda i,j: (sin(i/pi)*sin(j/pi)+1)/2,
... directed=False)
... lambda i, j: (sin(i / pi) * sin(j / pi) + 1) / 2,
... directed=False, mix_time=100)
>>> h = gt.corr_hist(g, "out", "out")
>>> clf()
>>> xlabel("source out-degree")
......@@ -345,9 +346,9 @@ def combined_corr_hist(g, deg1, deg2, bins=[[0, 1], [0, 1]], float_count=True):
>>> def sample_k(max):
... accept = False
... while not accept:
... i = randint(1,max+1)
... j = randint(1,max+1)
... accept = random() < (sin(i/pi)*sin(j/pi)+1)/2
... i = randint(1, max + 1)
... j = randint(1, max + 1)
... accept = random() < (sin(i / pi) * sin(j / pi) + 1) / 2
... return i,j
...
>>> g = gt.random_graph(10000, lambda: sample_k(40))
......@@ -437,12 +438,12 @@ def avg_neighbour_corr(g, deg_source, deg_target, bins=[0, 1], weight=None):
... accept = False
... while not accept:
... k = randint(1,max+1)
... accept = random() < 1.0/k
... accept = random() < 1.0 / k
... return k
...
>>> g = gt.random_graph(10000, lambda: sample_k(40),
... lambda i,j: (sin(i/pi)*sin(j/pi)+1)/2,
... directed=False)
... lambda i, j: (sin(i / pi) * sin(j / pi) + 1) / 2,
... directed=False, mix_time=100)
>>> h = gt.avg_neighbour_corr(g, "out", "out")
>>> clf()
>>> xlabel("source out-degree")
......
This diff is collapsed.
......@@ -43,6 +43,7 @@ import scipy.sparse
__all__ = ["adjacency", "laplacian", "incidence"]
def adjacency(g, sparse=True, weight=None):
r"""Return the adjacency matrix of the graph.
......@@ -86,8 +87,8 @@ def adjacency(g, sparse=True, weight=None):
[ 0. 0. 0. ..., 0. 0. 0.]
[ 0. 0. 0. ..., 0. 0. 0.]
...,
[ 0. 0. 0. ..., 0. 1. 0.]
[ 0. 0. 0. ..., 0. 0. 0.]
[ 0. 0. 0. ..., 0. 0. 1.]
[ 0. 0. 1. ..., 0. 0. 0.]
[ 0. 1. 0. ..., 0. 0. 0.]]
References
......@@ -194,8 +195,8 @@ def laplacian(g, deg="total", normalized=True, sparse=True, weight=None):
[ 0. 1. 0. ..., 0. 0. 0. ]
[ 0. 0. 1. ..., 0. 0. 0. ]
...,
[ 0. 0. 0. ..., 1. 0.05 0. ]
[ 0. 0. 0. ..., 0. 1. 0. ]
[ 0. 0. 0. ..., 1. 0. 0.05]
[ 0. 0. 0.05 ..., 0. 1. 0. ]
[ 0. 0.05 0. ..., 0. 0. 1. ]]
References
......@@ -279,7 +280,7 @@ def incidence(g, sparse=True):
>>> print m.todense()
[[ 0. 0. 0. ..., 0. 0. 0.]
[ 0. 0. 0. ..., 0. 0. 0.]
[ 0. 0. 0. ..., -1. 0. 0.]
[ 0. 0. 0. ..., 0. 0. 0.]
...,
[ 0. 0. 0. ..., 0. 0. 0.]
[ 0. 0. 0. ..., 0. 0. 0.]
......
......@@ -371,10 +371,10 @@ def distance_histogram(g, weight=None, bins=[0, 1], samples=None,
>>> g = gt.random_graph(100, lambda: (3, 3))
>>> hist = gt.distance_histogram(g)
>>> print hist
[array([ 0., 300., 868., 2222., 3906., 2463., 141.]), array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint64)]
[array([ 0., 300., 861., 2165., 3801., 2576., 197.]), array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint64)]
>>> hist = gt.distance_histogram(g, samples=10)
>>> print hist
[array([ 0., 30., 87., 230., 408., 223., 12.]), array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint64)]
[array([ 0., 30., 87., 221., 395., 234., 23.]), array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint64)]
"""
if samples != None:
......
......@@ -30,6 +30,7 @@ Summary
shortest_distance
shortest_path
similarity
isomorphism
subgraph_isomorphism
mark_subgraph
......@@ -172,7 +173,7 @@ def subgraph_isomorphism(sub, g, max_n=0, random=True):
>>> sub = gt.random_graph(10, lambda: (poisson(1.8), poisson(1.9)))
>>> vm, em = gt.subgraph_isomorphism(sub, g)
>>> print len(vm)
93
79
>>> for i in xrange(len(vm)):
... g.set_vertex_filter(None)
... g.set_edge_filter(None)
......@@ -385,10 +386,9 @@ def dominator_tree(g, root, dom_map=None):
>>> root = [v for v in g.vertices() if v.in_degree() == 0]
>>> dom = gt.dominator_tree(g, root[0])
>>> print dom.a
[ 0 0 72 0 0 0 0 0 0 0 0 0 0 0 21 0 0 0 0 0 0 3 0 0 0
0 0 0 0 0 0 41 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 3 0 0
0 0 0 0 0 2 0 0 0 0 0 0 0 80 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
References
----------
......@@ -430,8 +430,8 @@ def topological_sort(g):
>>> g.set_edge_filter(tree)
>>> sort = gt.topological_sort(g)
>>> print sort
[19 27 1 7 0 23 8 16 2 15 24 12 3 4 22 5 6 9 10 11 18 13 21 14 20
17 25 26 28 29]
[ 3 20 9 29 15 0 10 23 1 2 21 7 4 12 11 5 26 27 6 8 13 14 22 16 17
28 18 19 24 25]
References
----------
......@@ -520,11 +520,11 @@ def label_components(g, vprop=None, directed=None):
>>> g = gt.random_graph(100, lambda: (1, 1))
>>> comp, hist = gt.label_components(g)
>>> print comp.get_array()
[0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 2 0 0 0 1 0 0 0 0 1 1 0 2 0 1 1 0 0 0 0 1 0
0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 2 0 0 0 0 1 0 0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0]
[0 0 0 1 0 2 0 0 0 0 2 0 0 0 2 1 0 2 0 1 2 0 1 0 0 1 0 2 0 2 1 0 2 0 0 0 0
0 0 1 0 0 2 2 2 0 0 0 0 0 0 2 0 0 1 1 0 0 2 0 1 0 0 0 2 0 0 2 2 1 2 1 0 0
2 0 0 1 2 1 2 2 0 0 0 0 0 2 0 0 0 1 1 0 0 0 1 1 2 2]
>>> print hist
[81 15 4]
[58 18 24]
"""
if vprop is None:
......@@ -572,12 +572,12 @@ def label_largest_component(g, directed=None):
>>> g = gt.random_graph(100, lambda: poisson(1), directed=False)
>>> l = gt.label_largest_component(g)
>>> print l.a
[1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0
0 0 1 1 0 0 1 0 0 1 0 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0]
[1 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 0 1 0 0 1 1 1 1 1 1 0 1 0 0 1 1 0 0 0 0
1 1 1 0 0 1 1 0 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 0 0 1 1 1 0 0 0 0 1 0 0 1 1
0 1 0 0 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 1 0 0]
>>> u = gt.GraphView(g, vfilt=l) # extract the largest component as a graph
>>> print u.num_vertices()
26
46
"""
label = g.new_vertex_property("bool")
......@@ -647,15 +647,15 @@ def label_biconnected_components(g, eprop=None, vprop=None):
>>> g = gt.random_graph(100, lambda: 2, directed=False)
>>> comp, art, hist = gt.label_biconnected_components(g)
>>> print comp.a
[1 0 0 0 2 0 1 0 0 0 0 0 1 0 0 3 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1 1 0 0 0 0 0
1 0 1 3 0 2 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 3 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 1 3 1 0 2 1 0 0 0 0 0 2 0 0 0 2]
[0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 0 1 0 0
0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1]
>>> print art.a
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
>>> print hist
[77 13 6 4]
[81 19]
"""
......@@ -735,43 +735,43 @@ def shortest_distance(g, source=None, weights=None, max_dist=None,
>>> seed(42)
>>> g = gt.random_graph(100, lambda: (poisson(3), poisson(3)))
>>> dist = gt.shortest_distance(g, source=g.vertex(0))
>>> print dist.get_array()
[ 0 3 5 4 2147483647 1
6 3 4 4 5 4
4 4 4 4 1 3
3 1 5 3 2147483647 4
2 5 5 2147483647 5 5
4 3 3 2 4 4
4 4 5 5 2147483647 2147483647
4 4 3 5 3 4
2147483647 3 2 4 5 5
3 3 3 5 4 2147483647
3 4 5 4 2 2147483647
4 3 2 4 2 2147483647
3 3 4 3 4 5
2 3 6 4 4 2147483647
6 4 5 1 4 5
3 4 4 2 4 6
3 4 2 4]
>>> print dist.a
[ 0 3 6 4 2147483647 3
4 3 4 2 3 4
3 4 2 4 2 5
4 4 2147483647 4 2147483647 6
4 7 5 2147483647 3 4
2 3 5 5 4 5
1 5 6 1 2147483647 8
4 2 1 5 5 6
7 4 5 3 4 4
5 3 3 5 4 5
4 3 5 4 2 2147483647
6 5 4 5 1 2147483647
5 5 4 2 5 4
6 3 5 3 4 2147483647
4 4 7 4 3 5
5 2 7 3 4 4
4 3 4 4]
>>> dist = gt.shortest_distance(g)
>>> print array(dist[g.vertex(0)])
[ 0 3 5 4 2147483647 1
6 3 4 4 5 4
4 4 4 4 1 3
3 1 5 3 2147483647 4
2 5 5 2147483647 5 5
4 3 3 2 4 4
4 4 5 5 2147483647 2147483647
4 4 3 5 3 4
2147483647 3 2 4 5 5
3 3 3 5 4 2147483647
3 4 5 4 2 2147483647
4 3 2 4 2 2147483647
3 3 4 3 4 5
2 3 6 4 4 2147483647
6 4 5 1 4 5
3 4 4 2 4 6
3 4 2 4]
>>> print dist[g.vertex(0)].a
[ 0 3 6 4 2147483647 3
4 3 4 2 3 4
3 4 2 4 2 5
4 4 2147483647 4 2147483647 6
4 7 5 2147483647 3 4
2 3 5 5 4 5
1 5 6 1 2147483647 8
4 2 1 5 5 6
7 4 5 3 4 4
5 3 3 5 4 5
4 3 5 4 2 2147483647
6 5 4 5 1 2147483647
5 5 4 2 5 4
6 3 5 3 4 2147483647
4 4 7 4 3 5
5 2 7 3 4 4
4 3 4 4]
References
----------
......@@ -875,9 +875,9 @@ def shortest_path(g, source, target, weights=None, pred_map=None):
>>> g = gt.random_graph(300, lambda: (poisson(3), poisson(3)))
>>> vlist, elist = gt.shortest_path(g, g.vertex(10), g.vertex(11))
>>> print [str(v) for v in vlist]
['10', '66', '46', '266', '101', '143', '91', '275', '82', '11']
['10', '222', '246', '0', '50', '257', '12', '242', '11']
>>> print [str(e) for e in elist]
['(10,66)', '(66,46)', '(46,266)', '(266,101)', '(101,143)', '(143,91)', '(91,275)', '(275,82)', '(82,11)']
['(10,222)', '(222,246)', '(246,0)', '(0,50)', '(50,257)', '(257,12)', '(12,242)', '(242,11)']
References
----------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment