Commit 07911562 authored by Tiago Peixoto's avatar Tiago Peixoto

Allow the disabling of probability caching in random_rewire()

This makes the code more efficient if the input parameter combinations
are much larger than the graph itself.
parent 7b2aa166
......@@ -82,7 +82,7 @@ void generate_graph(GraphInterface& gi, size_t N, python::object deg_sample,
size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
bool no_sweep, bool self_loops, bool parallel_edges,
python::object corr_prob, boost::any block,
size_t seed, bool verbose);
bool cache, size_t seed, bool verbose);
void predecessor_graph(GraphInterface& gi, GraphInterface& gpi,
boost::any pred_map);
void line_graph(GraphInterface& gi, GraphInterface& lgi,
......
......@@ -62,12 +62,13 @@ struct graph_rewire_block
template <class Graph, class EdgeIndexMap, class CorrProb, class BlockProp>
void operator()(Graph& g, EdgeIndexMap edge_index, CorrProb corr_prob,
pair<bool, bool> rest, BlockProp block_prop,
pair<size_t, bool> iter_sweep, bool verbose, size_t& pcount,
rng_t& rng) const
pair<size_t, bool> iter_sweep,
pair<bool, bool> cache_verbose, size_t& pcount, rng_t& rng)
const
{
graph_rewire<ProbabilisticRewireStrategy>()
(g, edge_index, corr_prob, rest.first, rest.second, iter_sweep,
verbose, pcount, rng, PropertyBlock<BlockProp>(block_prop));
cache_verbose, pcount, rng, PropertyBlock<BlockProp>(block_prop));
}
};
......@@ -75,7 +76,7 @@ struct graph_rewire_block
size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
bool no_sweep, bool self_loops, bool parallel_edges,
python::object corr_prob, boost::any block,
size_t seed, bool verbose)
bool cache, size_t seed, bool verbose)
{
rng_t rng(static_cast<rng_t::result_type>(seed));
PythonFuncWrap corr(corr_prob);
......@@ -86,35 +87,40 @@ size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
(gi, boost::bind<void>(graph_rewire<ErdosRewireStrategy>(),
_1, gi.GetEdgeIndex(), boost::ref(corr),
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
make_pair(niter, no_sweep),
make_pair(cache, 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),
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
make_pair(niter, no_sweep),
make_pair(cache, 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),
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
make_pair(niter, no_sweep),
make_pair(cache, 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),
self_loops, parallel_edges,
make_pair(niter, no_sweep), verbose,
make_pair(niter, no_sweep),
make_pair(cache, verbose),
boost::ref(pcount), boost::ref(rng)))();
else if (strat == "blockmodel")
run_action<>()
(gi, boost::bind<void>(graph_rewire_block(),
_1, gi.GetEdgeIndex(), boost::ref(corr),
make_pair(self_loops, parallel_edges), _2,
make_pair(niter, no_sweep), verbose,
make_pair(niter, no_sweep),
make_pair(cache, verbose),
boost::ref(pcount), boost::ref(rng)),
vertex_properties())(block);
else
......
......@@ -177,12 +177,15 @@ struct graph_rewire
class BlockDeg>
void operator()(Graph& g, EdgeIndexMap edge_index, CorrProb corr_prob,
bool self_loops, bool parallel_edges,
pair<size_t, bool> iter_sweep, bool verbose, size_t& pcount,
rng_t& rng, BlockDeg bd)
pair<size_t, bool> iter_sweep,
pair<bool, bool> cache_verbose,
size_t& pcount, rng_t& rng, BlockDeg bd)
const
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
bool cache = cache_verbose.first;
bool verbose = cache_verbose.second;
vector<edge_t> edges;
vector<size_t> edge_pos;
......@@ -198,7 +201,7 @@ struct graph_rewire
}
RewireStrategy<Graph, EdgeIndexMap, CorrProb, BlockDeg>
rewire(g, edge_index, edges, corr_prob, bd, rng);
rewire(g, edge_index, edges, corr_prob, bd, cache, rng);
size_t niter;
bool no_sweep;
......@@ -235,12 +238,13 @@ struct graph_rewire
template <class Graph, class EdgeIndexMap, class CorrProb>
void operator()(Graph& g, EdgeIndexMap edge_index, CorrProb corr_prob,
bool self_loops, bool parallel_edges,
pair<size_t, bool> iter_sweep, bool verbose, size_t& pcount,
rng_t& rng)
pair<size_t, bool> iter_sweep,
pair<bool, bool> cache_verbose,
size_t& pcount, rng_t& rng)
const
{
operator()(g, edge_index, corr_prob, self_loops, parallel_edges,
iter_sweep, verbose, pcount, rng, DegreeBlock());
iter_sweep, cache_verbose, pcount, rng, DegreeBlock());
}
};
......@@ -256,7 +260,7 @@ public:
ErdosRewireStrategy(Graph& g, EdgeIndexMap edge_index,
vector<edge_t>& edges, CorrProb, BlockDeg,
rng_t& rng)
bool, rng_t& rng)
: _g(g), _edge_index(edge_index), _edges(edges),
_vertices(HardNumVertices()(g)), _rng(rng)
{
......@@ -398,7 +402,7 @@ public:
RandomRewireStrategy(Graph& g, EdgeIndexMap edge_index,
vector<edge_t>& edges, CorrProb, BlockDeg,
rng_t& rng)
bool, rng_t& rng)
: base_t(g, edge_index, edges, rng), _g(g) {}
pair<size_t,bool> get_target_edge(size_t e)
......@@ -442,7 +446,7 @@ public:
CorrelatedRewireStrategy(Graph& g, EdgeIndexMap edge_index,
vector<edge_t>& edges, CorrProb, BlockDeg,
rng_t& rng)
bool, rng_t& rng)
: base_t(g, edge_index, edges, rng), _g(g)
{
for (size_t ei = 0; ei < base_t::_edges.size(); ++ei)
......@@ -516,8 +520,9 @@ public:
ProbabilisticRewireStrategy(Graph& g, EdgeIndexMap edge_index,
vector<edge_t>& edges, CorrProb corr_prob,
BlockDeg blockdeg, rng_t& rng)
: base_t(g, edge_index, edges, rng), _g(g), _blockdeg(blockdeg)
BlockDeg blockdeg, bool cache, rng_t& rng)
: base_t(g, edge_index, edges, rng), _g(g), _corr_prob(corr_prob),
_blockdeg(blockdeg)
{
tr1::unordered_set<deg_t, hash<deg_t> > deg_set;
for (size_t ei = 0; ei < base_t::_edges.size(); ++ei)
......@@ -527,17 +532,32 @@ public:
deg_set.insert(get_deg(target(e, g), g));
}
// cache probabilities
for (typeof(deg_set.begin()) s_iter = deg_set.begin();
s_iter != deg_set.end(); ++s_iter)
for (typeof(deg_set.begin()) t_iter = deg_set.begin();
t_iter != deg_set.end(); ++t_iter)
{
double p = corr_prob(*s_iter, *t_iter);
if (isnan(p) || isinf(p))
p = 0;
_probs[make_pair(*s_iter, *t_iter)] = p;
}
if (cache)
{
// cache probabilities
for (typeof(deg_set.begin()) s_iter = deg_set.begin();
s_iter != deg_set.end(); ++s_iter)
for (typeof(deg_set.begin()) t_iter = deg_set.begin();
t_iter != deg_set.end(); ++t_iter)
{
double p = _corr_prob(*s_iter, *t_iter);
if (isnan(p) || isinf(p) || p < 0)
p = 0;
_probs[make_pair(*s_iter, *t_iter)] = p;
}
}
}
double get_prob(const deg_t& s_deg, const deg_t& t_deg)
{
if (_probs.empty())
{
double p = _corr_prob(s_deg, t_deg);
if (isnan(p) || isinf(p) || p < 0)
p = 0;
return p;
}
return _probs[make_pair(s_deg, t_deg)];
}
deg_t get_deg(vertex_t v, Graph& g)
......@@ -563,12 +583,12 @@ public:
deg_t ep_s_deg = get_deg(source(ep, base_t::_edges, _g), _g);
deg_t ep_t_deg = get_deg(target(ep, base_t::_edges, _g), _g);
double pi = (_probs[make_pair(s_deg, t_deg)] *
_probs[make_pair(ep_s_deg, ep_t_deg)]);
double pf = (_probs[make_pair(s_deg, ep_t_deg)] *
_probs[make_pair(ep_s_deg, t_deg)]);
double pi = (get_prob(s_deg, t_deg) *
get_prob(ep_s_deg, ep_t_deg));
double pf = (get_prob(s_deg, ep_t_deg) *
get_prob(ep_s_deg, t_deg));
if (pf >= pi)
if (pf >= pi || pi == 0)
return ep;
if (pf == 0)
......@@ -590,6 +610,7 @@ public:
private:
Graph& _g;
EdgeIndexMap _edge_index;
CorrProb _corr_prob;
BlockDeg _blockdeg;
tr1::unordered_map<pair<deg_t, deg_t>, double, hash<pair<deg_t, deg_t> > >
_probs;
......
......@@ -56,7 +56,7 @@ __all__ = ["random_graph", "random_rewire", "predecessor_tree", "line_graph",
"price_network"]
def random_graph(N, deg_sampler, deg_corr=None, directed=True,
def random_graph(N, deg_sampler, deg_corr=None, cache_probs=True, directed=True,
parallel_edges=False, self_loops=False, blockmodel=None,
block_type="int", degree_block=False,
random=True, mix_time=10, verbose=False):
......@@ -79,7 +79,6 @@ def random_graph(N, deg_sampler, deg_corr=None, directed=True,
be the index of the vertex which will receive the degree.
If ``blockmodel != None``, the first value passed will be the vertex
index, and the second will be the block value of the vertex.
deg_corr : function (optional, default: ``None``)
A function which gives the degree correlation of the graph. It should be
callable with two parameters: the in,out-degree pair of the source
......@@ -90,6 +89,13 @@ def random_graph(N, deg_sampler, deg_corr=None, directed=True,
If ``blockmodel != None``, the value passed to the function will be the
block value of the respective vertices, not the in/out-degree pairs.
cache_probs : bool (optional, default: ``True``)
If ``True``, the probabilities returned by the ``deg_corr`` parameter
will be cached internally. This is crucial for good performance, since
in this case the supplied python function is called only a few times,
and not at every attempted edge rewire move. However, in the case were
the different parameter combinations to the probability function is very
large, the memory requirements to keep the cache may be very large.
directed : bool (optional, default: ``True``)
Whether the generated graph should be directed.
parallel_edges : bool (optional, default: ``False``)
......@@ -405,8 +411,8 @@ def random_graph(N, deg_sampler, deg_corr=None, directed=True,
if deg_corr is not None:
random_rewire(g, strat="probabilistic", n_iter=mix_time,
parallel_edges=parallel_edges, deg_corr=deg_corr,
self_loops=self_loops, blockmodel=bm,
verbose=verbose)
cache_probs=cache_probs, self_loops=self_loops,
blockmodel=bm, verbose=verbose)
else:
random_rewire(g, parallel_edges=parallel_edges, n_iter=mix_time,
self_loops=self_loops, verbose=verbose)
......@@ -421,7 +427,8 @@ def random_graph(N, deg_sampler, deg_corr=None, directed=True,
"probabilistic"]})
def random_rewire(g, strat="uncorrelated", n_iter=1, edge_sweep=True,
parallel_edges=False, self_loops=False, deg_corr=None,
blockmodel=None, ret_fail=False, verbose=False):
cache_probs=True, blockmodel=None, ret_fail=False,
verbose=False):
r"""
Shuffle the graph in-place.
......@@ -467,6 +474,13 @@ def random_rewire(g, strat="uncorrelated", n_iter=1, edge_sweep=True,
If ``blockmodel != None``, the value passed to the function will be the
block value of the respective vertices, not the in/out-degree pairs.
cache_probs : bool (optional, default: ``True``)
If ``True``, the probabilities returned by the ``deg_corr`` parameter
will be cached internally. This is crucial for good performance, since
in this case the supplied python function is called only a few times,
and not at every attempted edge rewire move. However, in the case were
the different parameter combinations to the probability function is very
large, the memory requirements to keep the cache may be very large.
blockmodel : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
If supplied, the graph will be rewired to conform to a blockmodel
ensemble. The value must be a vertex property map which defines the
......@@ -682,6 +696,7 @@ def random_rewire(g, strat="uncorrelated", n_iter=1, edge_sweep=True,
n_iter, not edge_sweep,
self_loops, parallel_edges,
corr, _prop("v", g, blockmodel),
cache_probs,
seed, verbose)
if ret_fail:
return pcount
......
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