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

generation: Remove redundant alias code

parent 62424bf0
Pipeline #373 failed with stage
in 366 minutes and 48 seconds
......@@ -77,8 +77,8 @@ void generate_sbm(GraphInterface& gi, boost::any ab, boost::python::object ors,
size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
bool no_sweep, bool self_loops, bool parallel_edges,
bool configuration, bool alias, bool traditional,
bool micro, bool persist, boost::python::object corr_prob,
bool configuration, bool traditional, bool micro,
bool persist, boost::python::object corr_prob,
boost::any apin, boost::any block, bool cache, rng_t& rng,
bool verbose);
void predecessor_graph(GraphInterface& gi, GraphInterface& gpi,
......
......@@ -75,9 +75,8 @@ private:
struct graph_rewire_block
{
graph_rewire_block(bool alias, bool traditional, bool micro) :
alias(alias), traditional(traditional), micro(micro) {}
bool alias;
graph_rewire_block(bool traditional, bool micro) :
traditional(traditional), micro(micro) {}
bool traditional;
bool micro;
......@@ -104,16 +103,10 @@ struct graph_rewire_block
}
else
{
if (alias)
graph_rewire<AliasProbabilisticRewireStrategy>()
(g, edge_index, corr_prob, pin, rest.first, rest.second,
configuration, iter_sweep,cache_verbose, pcount, rng,
PropertyBlock<BlockProp>(block_prop));
else
graph_rewire<ProbabilisticRewireStrategy>()
(g, edge_index, corr_prob, pin, rest.first, rest.second,
configuration, iter_sweep, cache_verbose, pcount, rng,
PropertyBlock<BlockProp>(block_prop));
graph_rewire<ProbabilisticRewireStrategy>()
(g, edge_index, corr_prob, pin, rest.first, rest.second,
configuration, iter_sweep, cache_verbose, pcount, rng,
PropertyBlock<BlockProp>(block_prop));
}
}
};
......@@ -139,8 +132,8 @@ struct graph_rewire_correlated
size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
bool no_sweep, bool self_loops, bool parallel_edges,
bool configuration, bool alias, bool traditional,
bool micro, bool persist, boost::python::object corr_prob,
bool configuration, bool traditional, bool micro,
bool persist, boost::python::object corr_prob,
boost::any apin, boost::any block, bool cache, rng_t& rng,
bool verbose)
{
......@@ -211,7 +204,7 @@ size_t random_rewire(GraphInterface& gi, string strat, size_t niter,
else if (strat == "blockmodel")
{
run_action<>()
(gi, std::bind(graph_rewire_block(alias, traditional, micro),
(gi, std::bind(graph_rewire_block(traditional, micro),
std::placeholders::_1, gi.get_edge_index(),
std::ref(corr), pin,
make_pair(self_loops, parallel_edges),
......
......@@ -891,355 +891,6 @@ private:
};
// general "degree-corrected" stochastic blockmodel
// this version is based on the alias method
template <class Graph, class EdgeIndexMap, class CorrProb, class BlockDeg>
class AliasProbabilisticRewireStrategy:
public RewireStrategyBase<Graph, EdgeIndexMap,
AliasProbabilisticRewireStrategy<Graph, EdgeIndexMap,
CorrProb, BlockDeg> >
{
public:
typedef RewireStrategyBase<Graph, EdgeIndexMap,
AliasProbabilisticRewireStrategy<Graph, EdgeIndexMap,
CorrProb, BlockDeg> >
base_t;
typedef Graph graph_t;
typedef EdgeIndexMap edge_index_t;
typedef typename BlockDeg::block_t deg_t;
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;
AliasProbabilisticRewireStrategy(Graph& g, EdgeIndexMap edge_index,
vector<edge_t>& edges, CorrProb corr_prob,
BlockDeg blockdeg, bool, rng_t& rng,
bool parallel_edges, bool configuration)
: base_t(g, edge_index, edges, rng, parallel_edges, configuration),
_g(g), _corr_prob(corr_prob), _blockdeg(blockdeg)
{
_in_pos.resize(base_t::_edges.size());
if (!is_directed::apply<Graph>::type::value)
_out_pos.resize(base_t::_edges.size());
std::unordered_set<deg_t> deg_set;
for (size_t ei = 0; ei < base_t::_edges.size(); ++ei)
{
edge_t& e = base_t::_edges[ei];
deg_set.insert(get_deg(source(e, g), g));
deg_set.insert(get_deg(target(e, g), g));
vertex_t v = target(e, g);
deg_t r = get_deg(v, g);
_in_edges[r].push_back(ei);
_in_pos[ei] = _in_edges[r].size() - 1;
if (!is_directed::apply<Graph>::type::value)
{
v = source(e, g);
deg_t s = get_deg(v, g);
_out_edges[s].push_back(ei);
_out_pos[ei] = _out_edges[s].size() - 1;
}
}
_corr_prob.get_probs(_probs);
if (_probs.empty())
{
vector<deg_t> items;
for (auto s_iter = deg_set.begin(); s_iter != deg_set.end(); ++s_iter)
items.push_back(*s_iter);
for (auto s_iter = deg_set.begin(); s_iter != deg_set.end(); ++s_iter)
{
vector<double> probs;
double sum = 0;
for (auto t_iter = deg_set.begin(); t_iter != deg_set.end(); ++t_iter)
{
double p = _corr_prob(*s_iter, *t_iter);
// avoid zero probability to not get stuck in rejection step
if (std::isnan(p) || std::isinf(p) || p <= 0)
continue;
probs.push_back(p);
_probs[make_pair(*s_iter, *t_iter)] = log(p);
sum += p;
}
_sampler[*s_iter] = new Sampler<deg_t, boost::mpl::false_>(items, probs);
auto& ps = _sprob[*s_iter];
for (size_t i = 0; i < items.size(); ++i)
{
double er = 0;
if (!is_directed::apply<Graph>::type::value)
er = max(_in_edges[items[i]].size() + _out_edges[items[i]].size(),
numeric_limits<double>::min());
else
er = max(_in_edges[items[i]].size(),
numeric_limits<double>::min());
ps[items[i]] = exp(log(probs[i]) - log(sum) - log(er));
}
}
}
else
{
std::unordered_map<deg_t, vector<double>> sprobs;
std::unordered_map<deg_t, vector<deg_t>> sitems;
for (auto iter = _probs.begin(); iter != _probs.end(); ++iter)
{
deg_t s = iter->first.first;
deg_t t = iter->first.second;
double& p = iter->second;
if (std::isnan(p) || std::isinf(p) || p <= 0)
p = numeric_limits<double>::min();
sitems[s].push_back(t);
sprobs[s].push_back(p);
p = log(p);
}
for (auto iter = sitems.begin(); iter != sitems.end(); ++iter)
{
deg_t s = iter->first;
_sampler[s] = new Sampler<deg_t, boost::mpl::false_>(iter->second, sprobs[s]);
double sum = 0;
for (size_t i = 0; i < iter->second.size(); ++i)
sum += sprobs[s][i];
auto& pr = _sprob[s];
for (size_t i = 0; i < iter->second.size(); ++i)
{
deg_t t = iter->second[i];
pr[t] = exp(log(sprobs[s][i]) - log(sum));
}
}
}
}
~AliasProbabilisticRewireStrategy()
{
for (decltype(_sampler.begin()) iter = _sampler.begin();
iter != _sampler.end(); ++iter)
delete iter->second;
}
double get_prob(const deg_t& s_deg, const deg_t& t_deg)
{
static const double zero = log(numeric_limits<double>::min());
auto k = make_pair(s_deg, t_deg);
auto iter = _probs.find(k);
if (iter == _probs.end())
return zero;
return iter->second;
}
double get_sprob(const deg_t& s_deg, const deg_t& t_deg)
{
auto& pr = _sprob[s_deg];
auto iter = pr.find(t_deg);
if (iter == pr.end())
return numeric_limits<double>::min();
return iter->second;
}
deg_t get_deg(vertex_t v, Graph& g)
{
return _blockdeg.get_block(v, g);
}
pair<size_t, bool> get_target_edge(pair<size_t, bool>& e, bool)
{
if (!is_directed::apply<Graph>::type::value)
{
std::bernoulli_distribution coin(0.5);
e.second = coin(base_t::_rng);
}
vertex_t s = source(e, base_t::_edges, _g);
vertex_t t = target(e, base_t::_edges, _g);
deg_t s_deg = get_deg(s, _g);
deg_t t_deg = get_deg(t, _g);
auto iter = _sampler.find(s_deg);
if (iter == _sampler.end())
throw GraphException("Block label without defined connection probability!");
deg_t nt = iter->second->sample(base_t::_rng);
if (_in_edges[nt].empty() && _out_edges[nt].empty())
return e; // reject
pair<size_t, bool> ep;
std::bernoulli_distribution coin(_in_edges[nt].size() /
double(_in_edges[nt].size() +
_out_edges[nt].size()));
if (is_directed::apply<Graph>::type::value || coin(base_t::_rng))
{
vector<size_t>& ies = _in_edges[nt];
std::uniform_int_distribution<> sample(0, ies.size() - 1);
size_t epi = ies[sample(base_t::_rng)];
ep = make_pair(epi, false);
}
else
{
vector<size_t>& oes = _out_edges[nt];
std::uniform_int_distribution<> sample(0, oes.size() - 1);
size_t epi = oes[sample(base_t::_rng)];
ep = make_pair(epi, true);
}
if (source(e, base_t::_edges, _g) == source(ep, base_t::_edges, _g) ||
target(e, base_t::_edges, _g) == target(ep, base_t::_edges, _g))
return ep; // rewiring is a no-op
vertex_t ep_s = source(ep, base_t::_edges, _g);
vertex_t ep_t = target(ep, base_t::_edges, _g);
deg_t ep_s_deg = get_deg(ep_s, _g);
deg_t ep_t_deg = get_deg(ep_t, _g);
//assert(ep_t_deg == nt);
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);
double a = pf - pi;
if (is_directed::apply<Graph>::type::value)
{
double e_ep_t = _in_edges[ep_t_deg].size();
double e_t = _in_edges[t_deg].size();
if (t == ep_t)
{
e_ep_t -= in_degreeS()(t, _g);
e_t -= in_degreeS()(ep_t, _g);
}
a -= log(get_sprob(s_deg, ep_t_deg)) - log(e_ep_t) +
log(get_sprob(ep_s_deg, t_deg)) - log(e_t); // forwards
a += log(get_sprob(s_deg, t_deg)) - log(e_t) +
log(get_sprob(ep_s_deg, ep_t_deg)) - log(e_ep_t); // backwards
}
else
{
double e_ep_t = _in_edges[ep_t_deg].size() + _out_edges[ep_t_deg].size();
double e_t = _in_edges[t_deg].size() + _out_edges[t_deg].size();
if (t == ep_t)
{
e_ep_t -= out_degree(t, _g);
e_t -= out_degree(ep_t, _g);
}
double e_ep_s = _in_edges[ep_s_deg].size() + _out_edges[ep_s_deg].size();
double e_s = _in_edges[s_deg].size() + _out_edges[s_deg].size();
if (s == ep_s)
{
e_ep_s -= out_degree(s, _g);
e_s -= out_degree(ep_s, _g);
}
a -= log(get_sprob(s_deg, ep_t_deg)) - log(e_ep_t) +
log(get_sprob(ep_t_deg, s_deg)) - log(e_s) +
log(get_sprob(ep_s_deg, t_deg)) - log(e_t) +
log(get_sprob(t_deg, ep_s_deg)) - log(e_ep_s); // forwards
a += log(get_sprob(s_deg, t_deg)) - log(e_t) +
log(get_sprob(t_deg, s_deg)) - log(e_s) +
log(get_sprob(ep_s_deg, ep_t_deg)) - log(e_ep_t) +
log(get_sprob(ep_t_deg, ep_s_deg)) - log(e_ep_s); // backwards
}
if (a > 0)
return ep;
std::uniform_real_distribution<> rsample(0.0, 1.0);
double r = rsample(base_t::_rng);
if (r > exp(a))
return e; // reject
return ep;
}
void update_edge(size_t ei, bool insert)
{
if (insert)
{
vertex_t v = target(base_t::_edges[ei], _g);
deg_t d = get_deg(v, _g);
auto& in_vec = _in_edges[d];
in_vec.push_back(ei);
_in_pos[ei] = in_vec.size() - 1;
if (!is_directed::apply<Graph>::type::value)
{
v = source(base_t::_edges[ei], _g);
deg_t d = get_deg(v, _g);
auto& out_vec = _out_edges[d];
out_vec.push_back(ei);
_out_pos[ei] = out_vec.size() - 1;
}
}
else
{
vertex_t v = target(base_t::_edges[ei], _g);
deg_t d = get_deg(v, _g);
size_t j = _in_pos[ei];
auto& in_vec = _in_edges[d];
_in_pos[in_vec.back()] = j;
in_vec[j] = in_vec.back();
in_vec.pop_back();
if (!is_directed::apply<Graph>::type::value)
{
v = source(base_t::_edges[ei], _g);
deg_t d = get_deg(v, _g);
size_t j = _out_pos[ei];
auto& out_vec = _out_edges[d];
_out_pos[out_vec.back()] = j;
out_vec[j] = out_vec.back();
out_vec.pop_back();
}
}
}
private:
Graph& _g;
EdgeIndexMap _edge_index;
CorrProb _corr_prob;
BlockDeg _blockdeg;
typedef std::unordered_map<deg_t, Sampler<deg_t, boost::mpl::false_>*> sampler_map_t;
sampler_map_t _sampler;
typedef std::unordered_map<deg_t, std::unordered_map<deg_t, double>> sprob_map_t;
sprob_map_t _sprob;
typedef std::unordered_map<pair<deg_t, deg_t>, double> prob_map_t;
prob_map_t _probs;
typedef std::unordered_map<deg_t, vector<size_t>> edge_map_t;
edge_map_t _in_edges;
edge_map_t _out_edges;
vector<size_t> _in_pos;
vector<size_t> _out_pos;
};
// general "traditional" stochastic blockmodel
// this version is based on the alias method, and does not keep the degrees fixed
template <class Graph, class EdgeIndexMap, class CorrProb, class BlockDeg,
......
......@@ -457,9 +457,8 @@ def random_graph(N, deg_sampler, directed=True,
"blockmodel", "blockmodel-micro"]})
def random_rewire(g, model="configuration", n_iter=1, edge_sweep=True,
parallel_edges=False, self_loops=False, configuration=True,
edge_probs=None, block_membership=None, alias=True,
cache_probs=True, persist=False, pin=None, ret_fail=False,
verbose=False):
edge_probs=None, block_membership=None, cache_probs=True,
persist=False, pin=None, ret_fail=False, verbose=False):
r"""Shuffle the graph in-place, following a variety of possible statistical
models, chosen via the parameter ``model``.
......@@ -554,13 +553,6 @@ def random_rewire(g, model="configuration", n_iter=1, edge_sweep=True,
If supplied, the graph will be rewired to conform to a blockmodel
ensemble. The value must be a vertex property map which defines the
block of each vertex.
alias : bool (optional, default: ``True``)
If ``True``, and ``model`` is any of ``probabilistic-configuration``,
``blockmodel-degree``, or ``blockmodel``, the alias method will be used
to sample the candidate edges. In the case of ``blockmodel``, if
``parallel_edges == True`` and ``self_loops == True`` this makes the
sampling of the edges direct (not rejection based), so that ``n_iter ==
1`` is enough to get an uncorrelated sample.
cache_probs : bool (optional, default: ``True``)
If ``True``, the probabilities returned by the ``edge_probs`` parameter
will be cached internally. This is crucial for good performance, since
......@@ -819,10 +811,6 @@ def random_rewire(g, model="configuration", n_iter=1, edge_sweep=True,
elif edge_probs is None:
raise ValueError("A function must be supplied as the 'edge_probs' parameter")
if model == "blockmodel-degree" and alias and edge_sweep:
edge_sweep = False
n_iter *= g.num_edges()
traditional = True
micro = False
if model == "blockmodel-degree":
......@@ -845,7 +833,7 @@ def random_rewire(g, model="configuration", n_iter=1, edge_sweep=True,
_c_str(model),
n_iter, not edge_sweep,
self_loops, parallel_edges,
configuration, alias,
configuration,
traditional, micro, persist, corr,
_prop("e", g, pin),
_prop("v", g, block_membership),
......
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