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

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