Commit e7363032 authored by Tiago Peixoto's avatar Tiago Peixoto

inference: Fix issues with overlapping SBM and self-loops

parent 54fa4532
......@@ -24,13 +24,13 @@ graph_tool.inference.set_test(True)
g = collection.data["football"]
# add self-loops
for i in range(10):
v = numpy.random.randint(g.num_vertices())
g.add_edge(v, v)
# for i in range(10):
# v = numpy.random.randint(g.num_vertices())
# g.add_edge(v, v)
# add parallel edges
for e in list(g.edges())[:10]:
g.add_edge(e.source(), e.target())
# for e in list(g.edges())[:10]:
# g.add_edge(e.source(), e.target())
ec = g.new_ep("int", randint(0, 10, g.num_edges()))
......@@ -88,7 +88,7 @@ for pvals in iter_ranges(pranges):
if not deg_corr and degree_dl_kind != "uniform":
continue
if overlap and degree_dl_kind != "distributed":
if overlap and deg_corr and degree_dl_kind != "distributed": # FIXME
continue
if (rec is not None or layered != False) and not exact:
......@@ -213,7 +213,7 @@ for pvals in iter_ranges(pranges):
if not deg_corr and degree_dl_kind != "uniform":
continue
if overlap and degree_dl_kind != "distributed":
if overlap and deg_corr and degree_dl_kind != "distributed": # FIXME
continue
print(params, file=out)
......
......@@ -345,6 +345,19 @@ public:
{
eops([](auto&, auto&){}, [](auto&, auto&){},
[](auto delta, auto&) { return delta == 0; });
if (_coupled_state != nullptr)
{
_p_entries.clear();
std::vector<double> dummy;
entries_op(m_entries, _emat,
[&](auto r, auto s, auto&, auto delta, auto&)
{
if (delta == 0)
return;
_p_entries.emplace_back(r, s, delta, dummy);
});
}
}
else
{
......@@ -352,6 +365,8 @@ public:
{
if (delta != 0)
return false;
if (get<0>(edelta).empty())
return true;
for (size_t i = 0; i < this->_rec_types.size(); ++i)
{
if (get<0>(edelta)[i] != 0)
......@@ -479,23 +494,26 @@ public:
_Lrecdx[i+1] += _recdx[i] * _B_E_D;
}
}
}
if (_coupled_state != nullptr)
{
_p_entries.clear();
entries_op(m_entries, _emat,
[&](auto r, auto s, auto&, auto delta, auto& edelta)
{
if (delta == 0)
return;
_p_entries.emplace_back(r, s, delta, get<0>(edelta));
});
if (!_p_entries.empty())
_coupled_state->propagate_delta(m_entries.get_move().first,
m_entries.get_move().second,
_p_entries);
if (_coupled_state != nullptr)
{
_p_entries.clear();
entries_op(m_entries, _emat,
[&](auto r, auto s, auto&, auto delta, auto& edelta)
{
if (skip(delta, edelta))
return;
_p_entries.emplace_back(r, s, delta, get<0>(edelta));
});
}
}
// if (_coupled_state != nullptr && !_p_entries.empty())
// {
// _coupled_state->propagate_delta(m_entries.get_move().first,
// m_entries.get_move().second,
// _p_entries);
// }
}
void propagate_delta(size_t u, size_t v,
......@@ -1254,6 +1272,7 @@ public:
void coupled_resize_vertex(size_t v)
{
_b.resize(num_vertices(_g));
_bfield.resize(num_vertices(_g));
init_vertex_weight(v);
_pclabel.resize(num_vertices(_g));
_ignore_degrees.resize(num_vertices(_g));
......@@ -1354,13 +1373,16 @@ public:
deltal /= 2;
vector<int> deltam(num_vertices(_bg), 0);
for (auto e : in_edges_range(v, _g))
if (graph_tool::is_directed(_g))
{
vertex_t u = source(e, _g);
if (u == v)
continue;
vertex_t s = _b[u];
deltam[s] += _eweight[e];
for (auto e : in_edges_range(v, _g))
{
vertex_t u = source(e, _g);
if (u == v)
continue;
vertex_t s = _b[u];
deltam[s] += _eweight[e];
}
}
double dS = 0;
......@@ -1478,17 +1500,14 @@ public:
{
assert(size_t(_b[v]) == r || r == null_group);
if (r == nr)
{
m_entries.set_move(r, nr, num_vertices(_bg));
return 0;
}
if (r != null_group && nr != null_group && !allow_move(r, nr))
return std::numeric_limits<double>::infinity();
get_move_entries(v, r, nr, m_entries, [](auto) { return false; });
if (r == nr)
return 0;
double dS = 0;
if (ea.adjacency)
{
......@@ -2111,7 +2130,7 @@ public:
size_t w = 0;
size_t kout = 0, kin = 0;
degs_op(v, _vweight, _eweight, _degs, _g,
degs_op(v, _vweight, _eweight, _degs, _g, /// <- look here
[&] (size_t din, size_t dout, auto c)
{
kout += dout * c;
......@@ -2139,7 +2158,7 @@ public:
int mst = mts;
int mtm = mtp;
if (is_directed_::apply<g_t>::type::value)
if (graph_tool::is_directed(_g))
{
mst = 0;
const auto& me = m_entries.get_me(s, t, _emat);
......@@ -2152,7 +2171,7 @@ public:
{
int dts = m_entries.get_delta(t, s);
int dst = dts;
if (is_directed_::apply<g_t>::type::value)
if (graph_tool::is_directed(_g))
dst = m_entries.get_delta(s, t);
mts += dts;
......@@ -2171,7 +2190,7 @@ public:
}
}
if (is_directed_::apply<g_t>::type::value)
if (graph_tool::is_directed(_g))
{
p += ew * ((mts + mst + c) / (mtp + mtm + c * B));
}
......@@ -2191,11 +2210,14 @@ public:
sum_prob(e, target(e, _g));
}
for (auto e : in_edges_range(v, _g))
if (graph_tool::is_directed(_g))
{
if (source(e, _g) == v)
continue;
sum_prob(e, source(e, _g));
for (auto e : in_edges_range(v, _g))
{
if (source(e, _g) == v)
continue;
sum_prob(e, source(e, _g));
}
}
if (w > 0)
......
......@@ -201,8 +201,11 @@ public:
// update the half-edge lists
for (auto e : out_edges_range(v, g))
remove_edge(e, b, g);
for (auto e : in_edges_range(v, g))
remove_edge(e, b, g);
if (graph_tool::is_directed(g))
{
for (auto e : in_edges_range(v, g))
remove_edge(e, b, g);
}
}
template <class Vertex, class Vprop, class Eprop>
......@@ -213,8 +216,11 @@ public:
//update the half-edge lists
for (auto e : out_edges_range(v, g))
insert_edge(e, eweight[e], b, g);
for (auto e : in_edges_range(v, g))
insert_edge(e, eweight[e], b, g);
if (graph_tool::is_directed(g))
{
for (auto e : in_edges_range(v, g))
insert_edge(e, eweight[e], b, g);
}
}
template <class Edge, class RNG>
......
......@@ -24,7 +24,7 @@ namespace std
template <class T, class V>
void operator+=(vector<T>& ret, const V& v)
{
ret.resize(max(ret.size(), v.size()));
ret.resize(std::max(ret.size(), v.size()));
for (size_t i = 0; i < v.size(); ++i)
ret[i] += v[i];
}
......@@ -32,7 +32,7 @@ void operator+=(vector<T>& ret, const V& v)
template <class T, class V>
void operator-=(vector<T>& ret, const V& v)
{
ret.resize(max(ret.size(), v.size()));
ret.resize(std::max(ret.size(), v.size()));
for (size_t i = 0; i < v.size(); ++i)
ret[i] -= v[i];
}
......@@ -161,7 +161,7 @@ public:
auto& out_field = First ? _r_out_field : _nr_out_field;
if (is_directed_::apply<Graph>::type::value)
{
auto& in_field = (First ? _r_in_field : _nr_in_field);
auto& in_field = (First ? _r_in_field : _nr_in_field);
return (Source || s == t) ? out_field[t] : in_field[s];
}
else
......@@ -191,7 +191,7 @@ public:
f = _entries.size();
_entries.emplace_back(s, t);
_delta.emplace_back();
if (sizeof...(delta) > 0)
// if (sizeof...(delta) > 0)
_edelta.emplace_back();
}
......@@ -251,7 +251,8 @@ public:
const vector<pair<size_t, size_t>>& get_entries() { return _entries; }
const vector<int>& get_delta() { return _delta; }
const vector<std::tuple<EVals...>>& get_edelta() { return _edelta; }
const vector<std::tuple<EVals...>>& get_edelta() { //_edelta.resize(_delta.size());
return _edelta; }
template <class Emat>
vector<bedge_t>& get_mes(Emat& emat)
......@@ -261,7 +262,7 @@ public:
{
auto& rs = _entries[i];
_mes.push_back(emat.get_me(rs.first, rs.second));
assert(_mes.back() != emat.get_null_edge() || get<0>(_delta[i]) >= 0);
assert(_mes.back() != emat.get_null_edge() || _delta[i] >= 0);
}
return _mes;
}
......@@ -425,6 +426,9 @@ void move_entries(Vertex v, size_t r, size_t nr, VProp& _b, Graph& g,
{
m_entries.set_move(r, nr, B);
if (r == nr)
return;
if (r != null_group)
{
if (nr != null_group)
......@@ -459,9 +463,7 @@ void entries_op(MEntries& m_entries, EMat& emat, OP&& op)
auto& entry = entries[i];
auto er = entry.first;
auto es = entry.second;
op(er, es, mes[i], delta[i], edelta[i]); // warning: edelta may be
// empty, but should be
// eliminated after compilation
op(er, es, mes[i], delta[i], edelta[i]);
}
}
......
......@@ -445,12 +445,15 @@ ldegs_map_t get_layered_block_degs(GraphInterface& gi, boost::any aeweight,
ls.insert(l);
}
for (auto e : in_edges_range(v, g))
if (graph_tool::is_directed(g))
{
auto w = eweight[e];
auto l = ec[e];
kin[l] += w;
ls.insert(l);
for (auto e : in_edges_range(v, g))
{
auto w = eweight[e];
auto l = ec[e];
kin[l] += w;
ls.insert(l);
}
}
for (auto l : ls)
......
......@@ -34,22 +34,16 @@ double virtual_move_covariate(size_t v, size_t r, size_t s, State& state,
if (reset)
state.get_move_entries(v, r, s, m_entries);
auto& entries = m_entries.get_entries();
auto& delta = m_entries.get_delta();
double dS = 0;
for (size_t i = 0; i < entries.size(); ++i)
{
auto& entry = entries[i];
auto er = entry.first;
auto es = entry.second;
int d = delta[i];
int ers = get_beprop(er, es, state._mrs, state._emat);
assert(ers + d >= 0);
dS -= -lgamma_fast(ers + 1);
dS += -lgamma_fast(ers + d + 1);
}
entries_op(m_entries, state._emat,
[&](auto, auto, auto& me, auto d, auto&)
{
int ers = (me != state._emat.get_null_edge()) ?
state._mrs[me] : 0;
assert(ers + d >= 0);
dS -= -lgamma_fast(ers + 1);
dS += -lgamma_fast(ers + d + 1);
});
return dS;
}
......
......@@ -483,7 +483,7 @@ public:
}
if (is_partition_stats_enabled())
get_partition_stats(v).move_vertex(v, r, nr, _deg_corr, _g);
get_partition_stats(v).move_vertex(v, r, nr, _g);
remove_vertex(v);
add_vertex(v, nr);
......@@ -567,9 +567,6 @@ public:
int dwr = _wr[r] - _overlap_stats.virtual_remove_size(v, r, kin, kout);
int dwnr = _overlap_stats.virtual_add_size(v, nr) - _wr[nr];
if (_deg_corr)
dS += _overlap_stats.virtual_move_dS(v, r, nr, _g, kin, kout);
if (multigraph)
dS += _overlap_stats.virtual_move_parallel_dS(v, r, nr, _b, _g);
......@@ -631,6 +628,9 @@ public:
dS = virtual_move_sparse<true>(v, nr, ea.multigraph, m_entries);
else
dS = virtual_move_sparse<false>(v, nr, ea.multigraph, m_entries);
if (_deg_corr && ea.deg_entropy)
dS += _overlap_stats.virtual_move_deg_dS(v, r, nr, _g);
}
double dS_dl = 0;
......@@ -1425,12 +1425,15 @@ public:
break;
}
for (auto e : in_edges_range(t, g))
if (graph_tool::is_directed(g))
{
if (!be[e].empty() || source(e, g) != s)
continue;
be[e] = {_b[u], _b[v]};
break;
for (auto e : in_edges_range(t, g))
{
if (!be[e].empty() || source(e, g) != s)
continue;
be[e] = {_b[u], _b[v]};
break;
}
}
}
}
......
......@@ -868,9 +868,41 @@ struct overlap_partition_stats_t
return S_a - S_b;
}
// template <class Graph, class EWeight>
// double get_delta_deg(size_t v, size_t r, size_t nr, const EWeight&,
// const Graph& g, size_t in_deg = 0,
// size_t out_deg = 0)
// {
// if (r == nr)
// return 0;
// r = get_r(r);
// nr = get_r(nr);
// double S_b = 0, S_a = 0;
// size_t u = get_v(_overlap_stats.get_node(v));
// auto& bv = _bvs[u];
// bv_t n_bv;
// const cdeg_t& deg = _degs[u];
// cdeg_t n_deg;
// get_n_bv(v, r, nr, bv, deg, n_bv, n_deg, g, in_deg,
// out_deg);
// for (auto& k : deg)
// S_b -= lgamma_fast(get<0>(k) + 1) + lgamma_fast(get<1>(k) + 1);
// for (auto& k : n_deg)
// S_a -= lgamma_fast(get<0>(k) + 1) + lgamma_fast(get<1>(k) + 1);
// return S_a - S_b;
// }
template <class Graph>
void move_vertex(size_t v, size_t r, size_t nr, bool, Graph& g,
size_t in_deg = 0, size_t out_deg = 0)
void move_vertex(size_t v, size_t r, size_t nr, Graph& g, size_t in_deg = 0,
size_t out_deg = 0)
{
if (r == nr)
return;
......
......@@ -70,8 +70,11 @@ public:
for (auto e : out_edges_range(v, g))
_out_neighbors[v] = target(e, g);
for (auto e : in_edges_range(v, g))
_in_neighbors[v] = source(e, g);
if (graph_tool::is_directed(g))
{
for (auto e : in_edges_range(v, g))
_in_neighbors[v] = source(e, g);
}
}
// parallel edges
......@@ -87,8 +90,6 @@ public:
auto w = _out_neighbors[u];
if (w == _null)
continue;
if (!graph_tool::is_directed(g) && size_t(node_index[w]) < i)
continue;
out_us[node_index[w]].push_back(u);
}
......@@ -96,21 +97,34 @@ public:
{
if (uc.second.size() > 1)
{
_parallel_bundles.resize(_parallel_bundles.size() + 1);
auto& h = _parallel_bundles.back();
_parallel_bundles.emplace_back();
for (auto u : uc.second)
{
auto w = _out_neighbors[u];
assert(w != _null);
_mi[u] = _mi[w] = _parallel_bundles.size() - 1;
size_t r = b[u];
size_t s = b[w];
if (!graph_tool::is_directed(g) && r > s)
std::swap(r, s);
h[std::make_tuple(r, s,
!graph_tool::is_directed(g) &&
node_index[u] == node_index[w])]++;
bool is_loop = (!graph_tool::is_directed(g) &&
(node_index[u] == node_index[w]));
auto k = std::make_tuple(r, s, is_loop);
if (_mi[w] == -1)
{
_mi[w] = _parallel_bundles.size() - 1;
auto& h = _parallel_bundles.back();
h[k]++;
}
else if (is_loop)
{
auto& h = _parallel_bundles[_mi[w]];
h[k]++;
}
_mi[u] = _mi[w];
}
if (_parallel_bundles.back().empty())
_parallel_bundles.pop_back();
}
}
}
......@@ -131,24 +145,27 @@ public:
if (m != -1)
{
size_t r, s;
auto u = _out_neighbors[v];
if (u == _null)
auto w = _out_neighbors[v];
if (w == _null)
{
u = _in_neighbors[v];
r = b[u];
w = _in_neighbors[v];
r = b[w];
s = v_r;
}
else
{
r = v_r;
s = b[u];
s = b[w];
}
auto& h = _parallel_bundles[m];
if (!graph_tool::is_directed(g) && r > s)
std::swap(r, s);
h[std::make_tuple(r, s,
!graph_tool::is_directed(g) &&
_node_index[u] == _node_index[v])]++;
bool is_loop = (!graph_tool::is_directed(g) &&
(_node_index[w] == _node_index[v]));
if (is_loop)
h[std::make_tuple(r, s, is_loop)] += 2;
else
h[std::make_tuple(r, s, is_loop)]++;
}
}
......@@ -170,26 +187,29 @@ public:
if (m != -1)
{
size_t r, s;
auto u = _out_neighbors[v];
if (u == _null)
auto w = _out_neighbors[v];
if (w == _null)
{
u = _in_neighbors[v];
r = b[u];
w = _in_neighbors[v];
r = b[w];
s = v_r;
}
else
{
r = v_r;
s = b[u];
s = b[w];
}
auto& h = _parallel_bundles[m];
if (!graph_tool::is_directed(g) && r > s)
std::swap(r, s);
auto iter = h.find(std::make_tuple(r, s,
!graph_tool::is_directed(g) &&
_node_index[u] == _node_index[v]));
bool is_loop = (!graph_tool::is_directed(g) &&
(_node_index[w] == _node_index[v]));
auto iter = h.find(std::make_tuple(r, s, is_loop));
assert(iter->second > 0);
iter->second--;
if (is_loop)
iter->second -= 2;
else
iter->second--;
if (iter->second == 0)
h.erase(iter);
}
......@@ -227,8 +247,8 @@ public:
}
template <class Graph>
double virtual_move_dS(size_t v, size_t r, size_t nr, Graph& g,
size_t in_deg = 0, size_t out_deg = 0) const
double virtual_move_deg_dS(size_t v, size_t r, size_t nr, Graph& g,
size_t in_deg = 0, size_t out_deg = 0) const
{