Commit 7ec1fbbc authored by Tiago Peixoto's avatar Tiago Peixoto

Include "erdos" rewire strategy to random_rewire()

This implements a simpler rewire strategy where the edges are randomly
placed, without preserving the degrees of the vertices.
parent 9010eb48
......@@ -33,7 +33,12 @@ void random_rewire(GraphInterface& gi, string strat, bool self_loops,
{
rng_t rng(static_cast<rng_t::result_type>(seed));
if (strat == "uncorrelated")
if (strat == "erdos")
run_action<graph_tool::detail::never_reversed>()
(gi, bind<void>(graph_rewire<ErdosRewireStrategy>(),
_1, gi.GetEdgeIndex(), ref(rng), self_loops,
parallel_edges))();
else if (strat == "uncorrelated")
run_action<graph_tool::detail::never_reversed>()
(gi, bind<void>(graph_rewire<RandomRewireStrategy>(),
_1, gi.GetEdgeIndex(), ref(rng), self_loops,
......
......@@ -248,8 +248,7 @@ struct graph_rewire
continue;
typename graph_traits<Graph>::edge_descriptor e = edges[i];
typename graph_traits<Graph>::edge_descriptor se, te;
tie(se, te) = rewire(e, edges, is_edge, self_loops, parallel_edges);
swap_edge_triad()(e, se, te, edges, edge_index, g);
rewire(e, edges, is_edge, self_loops, parallel_edges);
}
}
};
......@@ -309,6 +308,72 @@ private:
RNG* _rng;
};
// this will rewire the edges so that the resulting graph will be entirely
// random (i.e. Erdos-Renyi)
template <class Graph, class EdgeIndexMap>
class ErdosRewireStrategy
{
public:
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
typedef typename EdgeIndexMap::value_type index_t;
ErdosRewireStrategy(Graph& g, EdgeIndexMap edge_index, rng_t& rng)
: _g(g), _edge_index(edge_index), _vertices(HardNumVertices()(g)),
_rng(rng)
{
typeof(_vertices.begin()) viter = _vertices.begin();
typename graph_traits<Graph>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(_g); v != v_end; ++v)
*(viter++) = *v;
}
template<class EdgesType>
void operator()(const edge_t& e, EdgesType& edges, vector<bool>& is_edge,
bool self_loops, bool parallel_edges)
{
//try randomly drawn pairs of vertices until one satisfies all the
//consistency checks
typedef random_permutation_iterator
<typename graph_traits<Graph>::vertex_iterator, rng_t>
random_vertex_iter;
tr1::uniform_int<size_t> sample(0, _vertices.size());
typename graph_traits<Graph>::vertex_descriptor s, t;
while (true)
{
s = sample(_rng);
t = sample(_rng);
if(s == t && !self_loops) // reject self-loops if not allowed
continue;
if (!parallel_edges &&
swap_edge_triad::is_adjacent_in_new(s, t, _edge_is_new, _g))
continue; // reject parallel edges if not allowed
break;
}
edge_t ne = add_edge(s, t, _g).first;
edges[_edge_index[e]] = ne;
remove_edge(e, _g);
if (_edge_index[ne] >= edges.size())
{
edges.resize(_edge_index[ne] + 1);
is_edge.resize(_edge_index[ne] + 1, false);
}
edges[_edge_index[ne]] = ne;
is_edge[_edge_index[ne]] = true;
_edge_is_new[ne] = true;
}
private:
Graph& _g;
EdgeIndexMap _edge_index;
vector<typename graph_traits<Graph>::vertex_descriptor> _vertices;
vector_property_map<bool, EdgeIndexMap> _edge_is_new;
rng_t& _rng;
};
// this is the mother class for edge-based rewire strategies
// it contains the common loop for finding edges to swap, so different
// strategies need only to specify where to sample the edges from.
......@@ -319,13 +384,12 @@ public:
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
typedef typename EdgeIndexMap::value_type index_t;
RewireStrategyBase(const Graph& g, EdgeIndexMap edge_index, rng_t& rng)
: _g(g), _edge_is_new(edge_index), _rng(rng) {}
RewireStrategyBase(Graph& g, EdgeIndexMap edge_index, rng_t& rng)
: _g(g), _edge_index(edge_index), _edge_is_new(edge_index), _rng(rng) {}
template<class EdgesType>
pair<edge_t, edge_t> operator()(const edge_t& e, const EdgesType& edges,
vector<bool>& is_edge,
bool self_loops, bool parallel_edges)
void operator()(const edge_t& e, EdgesType& edges, vector<bool>& is_edge,
bool self_loops, bool parallel_edges)
{
// where should we sample the edges from
vector<index_t>* edges_source=0, *edges_target=0;
......@@ -386,11 +450,12 @@ public:
throw GraphException("Couldn't find random pair of edges to swap"
"... This is a bug.");
_edge_is_new[e] = true;
return make_pair(es, et);
swap_edge_triad()(e, es, et, edges, _edge_index, _g);
}
private:
const Graph& _g;
Graph& _g;
EdgeIndexMap _edge_index;
vector_property_map<bool, EdgeIndexMap> _edge_is_new;
rng_t& _rng;
};
......@@ -414,7 +479,7 @@ public:
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
typedef typename EdgeIndexMap::value_type index_t;
RandomRewireStrategy(const Graph& g, EdgeIndexMap edge_index,
RandomRewireStrategy(Graph& g, EdgeIndexMap edge_index,
rng_t& rng)
: base_t(g, edge_index, rng)
{
......@@ -459,7 +524,7 @@ public:
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
typedef typename EdgeIndexMap::value_type index_t;
CorrelatedRewireStrategy (const Graph& g, EdgeIndexMap edge_index,
CorrelatedRewireStrategy (Graph& g, EdgeIndexMap edge_index,
rng_t& rng) : base_t(g, edge_index, rng), _g(g)
{
int i, N = num_vertices(_g);
......
......@@ -236,22 +236,25 @@ def random_graph(N, deg_sampler, deg_corr=None, directed=True,
g.set_directed(directed)
return g
@_limit_args({"strat":["erdos", "correlated", "uncorrelated"]})
def random_rewire(g, strat= "uncorrelated", parallel_edges = False,
self_loops = False):
r"""
Shuffle the graph in-place. The degrees (either in or out) of each vertex
are always the same, but otherwise the edges are randomly placed. If
strat == "correlated", the degree correlations are also maintained: The new
source and target of each edge both have the same in and out-degree.
Shuffle the graph in-place. If `strat` != "erdos", the degrees (either in or
out) of each vertex are always the same, but otherwise the edges are
randomly placed. If `strat` == "correlated", the degree correlations are
also maintained: The new source and target of each edge both have the same
in and out-degree.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be shuffled. The graph will be modified.
strat : string (optional, default: "uncorrelated")
If strat == "uncorrelated" only the degrees of the vertices will be
maintained, nothing else. If strat == "correlated", additionally the new
source and target of each edge both have the same in and out-degree.
If `strat` == "erdos", the resulting graph will be entirely random. If
`strat` == "uncorrelated" only the degrees of the vertices will be
maintained, nothing else. If `strat` == "correlated", additionally the
new source and target of each edge both have the same in and out-degree.
parallel : bool (optional, default: False)
If True, parallel edges are allowed.
self_loops : bool (optional, default: False)
......@@ -284,33 +287,42 @@ def random_rewire(g, strat= "uncorrelated", parallel_edges = False,
>>> gt.random_rewire(g)
>>> gt.graph_draw(g, layout="arf", output="rewire_uncorr.png", size=(6,6))
<...>
>>> gt.random_rewire(g, "erdos")
>>> gt.graph_draw(g, layout="arf", output="rewire_erdos.png", size=(6,6))
<...>
Some `ridiculograms <http://www.youtube.com/watch?v=YS-asmU3p_4>`_ :
.. image:: rewire_orig.png
.. image:: rewire_corr.png
.. image:: rewire_uncorr.png
.. image:: rewire_erdos.png
*Left:* Original graph; *Middle:* Shuffled graph, with degree
correlations; *Right:* Shuffled graph, without degree correlations.
*From left to right:* Original graph; Shuffled graph, with degree
correlations; Shuffled graph, without degree correlations; Shuffled graph,
with random degrees.
We can try some larger graphs to get better statistics.
>>> figure()
<...>
>>> g = gt.random_graph(20000, lambda: sample_k(20),
>>> g = gt.random_graph(30000, lambda: sample_k(20),
... lambda i,j: exp(abs(i-j)), directed=False)
>>> corr = gt.avg_neighbour_corr(g, "out", "out")
>>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="*-", label="original")
>>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="original")
(...)
>>> gt.random_rewire(g, "correlated")
>>> corr = gt.avg_neighbour_corr(g, "out", "out")
>>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="correlated")
>>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="*", label="correlated")
(...)
>>> gt.random_rewire(g)
>>> corr = gt.avg_neighbour_corr(g, "out", "out")
>>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="uncorrelated")
(...)
>>> gt.random_rewire(g, "erdos")
>>> corr = gt.avg_neighbour_corr(g, "out", "out")
>>> errorbar(corr[2], corr[0], yerr=corr[1], fmt="o-", label="Erdos")
(...)
>>> xlabel("$k$")
<...>
>>> ylabel(r"$\left<k_{nn}\right>$")
......@@ -381,9 +393,11 @@ def random_rewire(g, strat= "uncorrelated", parallel_edges = False,
seed = numpy.random.randint(0, sys.maxint)
g.stash_filter(reversed=True)
libgraph_tool_generation.random_rewire(g._Graph__graph, strat, self_loops,
parallel_edges, seed)
g.pop_filter(reversed=True)
try:
libgraph_tool_generation.random_rewire(g._Graph__graph, strat,
self_loops, parallel_edges, seed)
finally:
g.pop_filter(reversed=True)
def predecessor_tree(g, pred_map):
"""Return a graph from a list of predecessors given by
......
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