Commit b1e1bc5e authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Rewiring: bugfix, improvements and restructuring resulting in cleaner and faster code

Restructure the rewiring code, introducing further abstraction through
class inheritance.

Both uncorrelated and correlated cases draw edges directly.
This has actually proven faster than drawing vertices for the correlated
case, since realizing that indexes could be stored instead of edges.
Doing so avoids changes in the pool of candidate edges, which in turn
removes the need to rebuild it for each edge to rewire.
Consequently, it also makes the uncorrelated case a lot quicker.

In the uncorrelated undirected case, the new code also fixes a serious
bug: when building the edge pool, only one end of each edge was looked
at, because the "edges" vector is not equivalent to drawing all
out_edges from all vertices, as is done now.
parent e4aaa63b
...@@ -363,64 +363,63 @@ make_random_permutation_iterator(RandomAccessIterator first, ...@@ -363,64 +363,63 @@ make_random_permutation_iterator(RandomAccessIterator first,
rng); rng);
} }
// this will rewire the edges so that the combined (in, out) degree distribution // this is the mother class for edge-based rewire strategies
// will be the same, but all the rest is random // it contains the common loop for finding edges to swap, so different
template <class Graph, class EdgeIndexMap> // strategies need only to specify where to sample the edges from.
class RandomRewireStrategy template<class Graph, class EdgeIndexMap>
class EdgeBasedRewireStrategy
{ {
public: public:
RandomRewireStrategy (const Graph& g, EdgeIndexMap edge_index, rng_t& rng)
: _g(g), _rng(rng), _edge_is_new(edge_index)
{
}
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
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;
EdgeBasedRewireStrategy (const Graph& g, EdgeIndexMap edge_index,
rng_t& rng)
: _g(g), _edge_is_new(edge_index), random(rng) {}
template<class EdgesType> template<class EdgesType>
pair<edge_t,edge_t> operator()(const edge_t& e, const EdgesType& edges, pair<edge_t, edge_t> operator()(const edge_t& e, const EdgesType& edges,
bool self_loops, bool parallel_edges) bool self_loops, bool parallel_edges)
{ {
_edges_source = edges; // where should we sample the edges from
_edges_target = edges; vector<index_t> *edges_source, *edges_target;
typedef random_number_generator<rng_t,size_t> random_t; get_edges(e, &edges_source, &edges_target);
random_t random(_rng);
typedef random_permutation_iterator<typename edges_t::iterator,random_t>
random_edge_iter;
//try randomly drawn pairs of edges until one satisfies all the //try randomly drawn pairs of edges until one satisfies all the
//consistency checks //consistency checks
bool found = false; bool found = false;
edge_t se, te; edge_t es, et;
typedef random_permutation_iterator
<typename vector<index_t>::iterator, random_t> random_edge_iter;
random_edge_iter esi(_edges_source.begin(), _edges_source.end(), random_edge_iter esi(edges_source->begin(), edges_source->end(),
random); random);
for (; esi != _edges_source.end() && !found; ++esi) for (; esi != edges_source->end() && !found; ++esi)
{ {
es = edges[*esi];
if(!self_loops) // reject self-loops if not allowed if(!self_loops) // reject self-loops if not allowed
{ {
if((source(e, _g) == target(*esi, _g))) if((source(e, _g) == target(es, _g)))
continue; continue;
} }
random_edge_iter eti(_edges_target.begin(), _edges_target.end(), random_edge_iter eti(edges_target->begin(), edges_target->end(),
random); random);
for (; eti != edges_target->end() && !found; ++eti)
for (; eti != _edges_target.end() && !found; ++eti)
{ {
et = edges[*eti];
if (!self_loops) // reject self-loops if not allowed if (!self_loops) // reject self-loops if not allowed
{ {
if ((source(*esi, _g) == target_in()(*eti, _g)) || if ((source(es, _g) == target_in()(et, _g)) ||
(source_in()(*eti, _g) == target(e, _g))) (source_in()(et, _g) == target(e, _g)))
continue; continue;
} }
if (!parallel_edges) // reject parallel edges if not allowed if (!parallel_edges) // reject parallel edges if not allowed
{ {
if (swap_edge_triad::parallel_check(e, *esi, *eti, if (swap_edge_triad::parallel_check(e, es, et,
_edge_is_new, _g)) _edge_is_new, _g))
continue; continue;
} }
se = *esi;
te = *eti;
found = true; found = true;
} }
} }
...@@ -428,29 +427,35 @@ public: ...@@ -428,29 +427,35 @@ 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(se, te); return make_pair(es, et);
} }
virtual void get_edges(const edge_t& e, vector<index_t>** edges_source,
vector<index_t>** edges_target) = 0;
virtual ~EdgeBasedRewireStrategy() {}
private: private:
const Graph& _g; const Graph& _g;
rng_t& _rng;
typedef vector<edge_t> edges_t;
edges_t _edges_target, _edges_source;
vector_property_map<bool, EdgeIndexMap> _edge_is_new; vector_property_map<bool, EdgeIndexMap> _edge_is_new;
typedef random_number_generator<rng_t, size_t> random_t;
random_t random;
}; };
// this will rewire the edges so that the (in,out) degree distributions and the // this will rewire the edges so that the combined (in, out) degree distribution
// (in,out)->(in,out) correlations will be the same, but all the rest is random // will be the same, but all the rest is random
template <class Graph, class EdgeIndexMap> template <class Graph, class EdgeIndexMap>
class CorrelatedRewireStrategy class RandomRewireStrategy
: public EdgeBasedRewireStrategy<Graph,EdgeIndexMap>
{ {
public: public:
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t; typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
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;
CorrelatedRewireStrategy (const Graph& g, EdgeIndexMap edge_index, RandomRewireStrategy (const Graph& g, EdgeIndexMap edge_index,
rng_t& rng) rng_t& rng)
: _g(g), _rng(rng), _edge_is_new(edge_index) : EdgeBasedRewireStrategy<Graph,EdgeIndexMap>(g, edge_index, rng), _g(g)
{ {
int i, N = num_vertices(_g); int i, N = num_vertices(_g);
for (i = 0; i < N; ++i) for (i = 0; i < N; ++i)
...@@ -458,115 +463,75 @@ public: ...@@ -458,115 +463,75 @@ public:
vertex_t v = vertex(i, _g); vertex_t v = vertex(i, _g);
if (v == graph_traits<Graph>::null_vertex()) if (v == graph_traits<Graph>::null_vertex())
continue; continue;
typename graph_traits<Graph>::out_edge_iterator e_i, e_i_end;
size_t j = in_degreeS()(v, _g); for (tie(e_i, e_i_end) = out_edges(v, _g); e_i != e_i_end; ++e_i)
size_t k = out_degree(v, _g); {
_all_edges.push_back(edge_index[*e_i]);
typedef typename is_convertible }
<typename graph_traits<Graph>::directed_category,
directed_tag>::type is_directed;
if (j > 0 || !is_directed::value)
_deg_target_vertices[make_pair(j,k)].push_back(v);
if (k > 0)
_deg_source_vertices[make_pair(j,k)].push_back(v);
} }
} }
void get_edges(const edge_t& e, vector<index_t>** edges_source,
vector<index_t>** edges_target)
{
*edges_source = &_all_edges;
*edges_target = &_all_edges;
}
private:
const Graph& _g;
vector<index_t> _all_edges;
};
template<class EdgesType>
pair<edge_t, edge_t> operator()(const edge_t& e, const EdgesType& edges, // this will rewire the edges so that the (in,out) degree distributions and the
bool self_loops, bool parallel_edges) // (in,out)->(in,out) correlations will be the same, but all the rest is random
template <class Graph, class EdgeIndexMap>
class CorrelatedRewireStrategy
: public EdgeBasedRewireStrategy<Graph,EdgeIndexMap>
{
public:
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
typedef typename EdgeIndexMap::value_type index_t;
CorrelatedRewireStrategy (const Graph& g, EdgeIndexMap edge_index,
rng_t& rng)
: EdgeBasedRewireStrategy<Graph,EdgeIndexMap>(g, edge_index, rng), _g(g)
{ {
vector<vertex_t>& source_vertices = int i, N = num_vertices(_g);
_deg_source_vertices[make_pair(in_degreeS()(source(e, _g), _g), for (i = 0; i < N; ++i)
out_degree(source(e, _g), _g))];
vector<vertex_t>& target_vertices =
_deg_target_vertices[make_pair(in_degreeS()(target(e, _g), _g),
out_degree(target(e, _g), _g))];
typedef random_number_generator<rng_t, size_t> random_t;
random_t random(_rng);
typedef random_permutation_iterator<typename vector<vertex_t>::iterator,
random_t>
random_vertex_iter;
typedef random_permutation_iterator<typename vector<edge_t>::iterator,
random_t>
random_edge_iter;
edge_t se, te;
//try new combinations until one satisfies all the consistency checks
bool found = false;
random_vertex_iter vs(source_vertices.begin(),
source_vertices.end(), random);
for (; vs != source_vertices.end() && !found; ++vs)
{ {
random_vertex_iter vt(target_vertices.begin(), vertex_t v = vertex(i, _g);
target_vertices.end(), random); if (v == graph_traits<Graph>::null_vertex())
for (; vt != target_vertices.end() && !found; ++vt) continue;
typename graph_traits<Graph>::out_edge_iterator e_i, e_i_end;
for (tie(e_i, e_i_end) = out_edges(v, _g); e_i != e_i_end; ++e_i)
{ {
// set up the edges to draw from _edges_source_by
vector<edge_t> in_edges_vt, out_edges_vs; [make_pair(in_degreeS()(source(*e_i, _g), _g),
typename in_or_out_edge_iteratorS<Graph>::type ie, ie_end; out_degree(source(*e_i, _g), _g))]
typename graph_traits<Graph>::out_edge_iterator oe, oe_end; .push_back(edge_index[*e_i]);
tie(ie, ie_end) = _edges_target_by
in_or_out_edge_iteratorS<Graph>::get_edges(*vt, _g); [make_pair(in_degreeS()(target_in()(*e_i, _g), _g),
for (; ie != ie_end; ++ie) out_degree(target_in()(*e_i, _g), _g))]
in_edges_vt.push_back(*ie); .push_back(edge_index[*e_i]);
for (tie(oe, oe_end) = out_edges(*vs, _g); oe != oe_end ; ++oe)
out_edges_vs.push_back(*oe);
// for combinations of in_vt and out_vs...
random_edge_iter out_vs_i(out_edges_vs.begin(),
out_edges_vs.end(), random);
for (; out_vs_i != out_edges_vs.end() && !found; ++out_vs_i)
{
if(!self_loops) // reject self-loops if not allowed
{
if((*vs == *vt) ||
(source(e, _g) == target(*out_vs_i, _g)))
continue;
}
random_edge_iter in_vt_i(in_edges_vt.begin(),
in_edges_vt.end(), random);
for (; in_vt_i != in_edges_vt.end() && !found; ++in_vt_i)
{
if(!self_loops) // reject self-loops if not allowed
{
if((source_in()(*in_vt_i, _g) == target(e, _g)))
continue;
}
if(!parallel_edges) // reject parallel edges if not
// allowed
{
if (swap_edge_triad::parallel_check
(e, *out_vs_i, *in_vt_i, _edge_is_new, _g))
continue;
}
se = *out_vs_i;
te = *in_vt_i;
found = true;
}
}
} }
} }
if (!found)
throw GraphException("Couldn't find random pair of edges to swap"
"... This is a bug.");
_edge_is_new[e]=true;
return make_pair(se, te);
} }
void get_edges(const edge_t& e, vector<index_t>** edges_source,
vector<index_t>** edges_target)
{
*edges_source =
&_edges_source_by[make_pair(in_degreeS()(source(e, _g), _g),
out_degree(source(e, _g), _g))];
*edges_target =
&_edges_target_by[make_pair(in_degreeS()(target_in()(e, _g), _g),
out_degree(target_in()(e, _g), _g))];
}
private: private:
const Graph& _g; const Graph& _g;
rng_t& _rng; typedef tr1::unordered_map<pair<size_t, size_t>, vector<index_t>,
typedef tr1::unordered_map<pair<size_t, size_t>, vector<vertex_t>, hash<pair<size_t, size_t> > > edges_by_end_deg_t;
hash<pair<size_t, size_t> > > deg_vertices_t; edges_by_end_deg_t _edges_source_by, _edges_target_by;
deg_vertices_t _deg_source_vertices;
deg_vertices_t _deg_target_vertices;
vector_property_map<bool, EdgeIndexMap> _edge_is_new;
}; };
} // graph_tool namespace } // graph_tool namespace
......
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