Commit 62055e30 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

inference.blockmodel: simplify and speedup vertex movements

parent 74cfb8a4
......@@ -153,6 +153,8 @@ void export_blockmodel_state()
&state_t::remove_vertex;
void (state_t::*add_vertex)(size_t, size_t) =
&state_t::add_vertex;
void (state_t::*move_vertex)(size_t, size_t) =
&state_t::move_vertex;
void (state_t::*remove_vertices)(python::object) =
&state_t::remove_vertices;
void (state_t::*add_vertices)(python::object, python::object) =
......@@ -180,7 +182,7 @@ void export_blockmodel_state()
.def("add_vertex", add_vertex)
.def("remove_vertices", remove_vertices)
.def("add_vertices", add_vertices)
.def("move_vertex", &state_t::move_vertex)
.def("move_vertex", move_vertex)
.def("move_vertices", move_vertices)
.def("set_partition", set_partition)
.def("virtual_move", virtual_move)
......
......@@ -110,10 +110,11 @@ public:
_c_bdrec(_bdrec.get_checked()),
_vweight(uncheck(__avweight, typename std::add_pointer<vweight_t>::type())),
_eweight(uncheck(__aeweight, typename std::add_pointer<eweight_t>::type())),
_emat(_g, _b, _bg, rng),
_emat(_bg, rng),
_neighbour_sampler(get(vertex_index_t(), _g), num_vertices(_g)),
_m_entries(num_vertices(_bg)),
_coupled_state(nullptr)
_coupled_state(nullptr),
_gstate(this)
{
rebuild_neighbour_sampler();
_empty_blocks.clear();
......@@ -139,158 +140,125 @@ public:
_emat(other._emat),
_neighbour_sampler(other._neighbour_sampler),
_m_entries(num_vertices(_bg)),
_coupled_state(nullptr)
_coupled_state(nullptr),
_gstate(other._gstate == &other ? this : other._gstate)
{
if (other.is_partition_stats_enabled())
enable_partition_stats();
}
template <bool Add, class BEdge, class EFilt, class EOP>
void modify_vertex(size_t v, size_t r, BEdge&& bedge, EFilt&& efilt,
EOP&& eop)
{
typedef typename graph_traits<g_t>::vertex_descriptor vertex_t;
for (auto e : out_edges_range(v, _g))
{
if (efilt(e) || _eweight[e] == 0)
continue;
vertex_t u = target(e, _g);
vertex_t s = _b[u];
auto& me = bedge[e];
// =========================================================================
// State modification
// =========================================================================
if (Add)
template <class MEntries, class Efilt, class GetB>
void get_move_entries(size_t v, size_t r, size_t nr, MEntries& m_entries,
Efilt&& efilt, GetB&& get_b)
{
if (me != _emat.get_null_edge())
auto& gs = *_gstate;
auto mv_entries = [&](auto&&... args)
{
assert(u == v);
continue;
}
if (u == v)
s = r;
move_entries(v, r, nr, get_b, gs._g, gs._eweight, m_entries,
efilt, is_loop_nop(), args...);
};
me = _emat.get_me(r, s);
if (me == _emat.get_null_edge())
switch (_rec_type)
{
me = add_edge(r, s, _bg).first;
_emat.put_me(r, s, me);
_c_mrs[me] = 0;
_c_brec[me] = 0;
_c_bdrec[me] = 0;
case weight_type::POSITIVE: // positive weights
mv_entries(gs._rec);
break;
case weight_type::SIGNED: // positive and negative weights
mv_entries(gs._rec, gs._drec);
break;
default: // no weights
mv_entries();
}
assert(_emat.get_bedge(e) != _emat.get_null_edge());
}
else
{
if (me == _emat.get_null_edge())
template <class MEntries>
void get_move_entries(size_t v, size_t r, size_t nr, MEntries& m_entries)
{
assert(u == v);
continue;
get_move_entries(v, r, nr, m_entries,
[](auto) { return false; },
[&](auto u) -> auto& { return this->_b[u]; });
}
}
assert(me != _emat.get_null_edge());
assert(me == _emat.get_me(r, s));
assert(std::min(source(me, _bg), target(me, _bg)) == std::min(r, s));
assert(std::max(source(me, _bg), target(me, _bg)) == std::max(r, s));
auto ew = _eweight[e];
if (Add)
template <bool Add, class EFilt, class GetB>
void modify_vertex(size_t v, size_t r, EFilt&& efilt, GetB&& get_b)
{
_mrs[me] += ew;
_mrp[r] += ew;
_mrm[s] += ew;
eop(e, me);
}
_m_entries.clear();
if (Add)
get_move_entries(v, null_group, r, _m_entries, efilt, get_b);
else
{
_mrs[me] -= ew;
_mrp[r] -= ew;
_mrm[s] -= ew;
eop(e, me);
assert(_mrs[me] >= 0);
if (_mrs[me] == 0)
_emat.remove_me(r, s, me, _bg);
me = _emat.get_null_edge();
}
}
for (auto e : in_edges_range(v, _g))
{
if (efilt(e))
continue;
vertex_t u = source(e, _g);
if (u == v)
continue;
vertex_t s = _b[u];
get_move_entries(v, r, null_group, _m_entries, efilt, get_b);
auto& me = _emat.get_bedge(e);
if (Add)
entries_op(_m_entries, _emat,
[&](auto r, auto s, auto& me, auto& delta)
{
me = _emat.get_me(s, r);
if (me == _emat.get_null_edge())
if (Add && me == this->_emat.get_null_edge())
{
me = add_edge(s, r, _bg).first;
_emat.put_me(s, r, me);
_c_mrs[me] = 0;
_c_brec[me] = 0;
_c_bdrec[me] = 0;
}
assert(_emat.get_bedge(e) != _emat.get_null_edge());
me = add_edge(r, s, this->_bg).first;
_emat.put_me(r, s, me);
this->_c_mrs[me] = 0;
this->_c_brec[me] = 0;
this->_c_bdrec[me] = 0;
}
assert(me != _emat.get_null_edge());
assert(me == _emat.get_me(s, r));
assert(std::min(source(me, _bg), target(me, _bg)) == std::min(r, s));
assert(std::max(source(me, _bg), target(me, _bg)) == std::max(r, s));
auto ew = _eweight[e];
this->_mrs[me] += get<0>(delta);
this->_mrp[r] += get<0>(delta);
this->_mrm[s] += get<0>(delta);
if (Add)
switch (this->_rec_type)
{
_mrs[me] += ew;
_mrp[s] += ew;
_mrm[r] += ew;
eop(e, me);
case weight_type::SIGNED: // signed weights
this->_bdrec[me] += get<2>(delta);
case weight_type::POSITIVE: // positive weights
this->_brec[me] += get<1>(delta);
}
else
});
if (_rec_type == weight_type::DELTA_T) // waiting times
{
_mrs[me] -= ew;
_mrp[s] -= ew;
_mrm[r] -= ew;
eop(e, me);
if (_mrs[me] == 0)
_emat.remove_me(s, r, me, _bg);
me = _emat.get_null_edge();
if (_ignore_degrees[v] > 0)
{
double dt = out_degreeS()(v, _g, _rec);
auto r = _b[v];
if (Add)
_brecsum[r] += dt;
else
_brecsum[r] -= dt;
}
}
if (Add)
{
auto& b_v = get_b(v);
b_v = r;
add_partition_node(v, r);
}
else
{
remove_partition_node(v, r);
}
}
void remove_partition_node(size_t v, size_t r)
{
_wr[r] -= _vweight[v];
auto& gs = *_gstate;
_wr[r] -= gs._vweight[v];
if (!_egroups.empty())
_egroups.remove_vertex(v, r, _g);
_egroups.remove_vertex(v, r, gs._g);
if (is_partition_stats_enabled())
get_partition_stats(v).remove_vertex(v, r, _deg_corr, _g,
_vweight, _eweight, _degs);
get_partition_stats(v).remove_vertex(v, r, _deg_corr, gs._g,
gs._vweight, gs._eweight,
gs._degs);
if (_vweight[v] > 0 && _wr[r] == 0)
if (gs._vweight[v] > 0 && _wr[r] == 0)
{
remove_element(_candidate_blocks, _candidate_pos, r);
add_element(_empty_blocks, _empty_pos, r);
......@@ -299,64 +267,50 @@ public:
void add_partition_node(size_t v, size_t r)
{
_b[v] = r;
_wr[r] += _vweight[v];
auto& gs = *_gstate;
_wr[r] += gs._vweight[v];
if (!_egroups.empty())
_egroups.add_vertex(v, r, _eweight, _g);
_egroups.add_vertex(v, r, gs._eweight, gs._g);
if (is_partition_stats_enabled())
get_partition_stats(v).add_vertex(v, r, _deg_corr, _g, _vweight,
_eweight, _degs);
get_partition_stats(v).add_vertex(v, r, _deg_corr, gs._g, gs._vweight,
gs._eweight, gs._degs);
if (_vweight[v] > 0 && _wr[r] == _vweight[v])
if (gs._vweight[v] > 0 && _wr[r] == gs._vweight[v])
{
remove_element(_empty_blocks, _empty_pos, r);
add_element(_candidate_blocks, _candidate_pos, r);
}
}
template <class BEdge, class EFilt>
void remove_vertex(size_t v, BEdge&& bedge, EFilt&& efilt)
{
size_t r = _b[v];
switch (_rec_type)
{
case weight_type::POSITIVE: // positive weights
modify_vertex<false>(v, r, bedge, efilt,
[&](auto& e, auto& me)
{
this->_brec[me] -= this->_rec[e];
});
break;
case weight_type::SIGNED: // positive and negative weights
modify_vertex<false>(v, r, bedge, efilt,
[&](auto& e, auto& me)
{
this->_brec[me] -= this->_rec[e];
this->_bdrec[me] -= this->_drec[e];
});
break;
case weight_type::DELTA_T: // waiting times
if (_ignore_degrees[v] > 0)
template <class EFilt, class GetB>
void remove_vertex(size_t v, size_t r, EFilt && efilt, GetB && get_b)
{
double dt = out_degreeS()(v, _g, _rec);
auto r = _b[v];
_brecsum[r] -= dt;
modify_vertex<false>(v, r, efilt, get_b);
}
case weight_type::NONE: // no weights
modify_vertex<false>(v, r, bedge, efilt, [](auto&, auto&){});
template <class EFilt>
void remove_vertex(size_t v, size_t r, EFilt&& efilt)
{
remove_vertex(v, r, efilt,
[&](auto u) -> auto& { return this->_b[u]; });
}
void remove_vertex(size_t v, size_t r)
{
remove_vertex(v, r, [](auto&) { return false; });
}
void remove_vertex(size_t v)
{
remove_vertex(v, _emat.get_bedge_map(),
[](auto&){ return false; });
size_t r = _b[v];
remove_vertex(v, r);
}
template <class Vlist, class EOP>
void remove_vertices(Vlist& vs, EOP&& eop)
template <class Vlist>
void remove_vertices(Vlist& vs)
{
typedef typename graph_traits<g_t>::vertex_descriptor vertex_t;
typedef typename graph_traits<g_t>::edge_descriptor edges_t;
......@@ -374,9 +328,8 @@ public:
}
}
auto& bedge = _emat.get_bedge_map();
for (auto v : vset)
remove_vertex(v, bedge,
remove_vertex(v, _b[v],
[&](auto& e) { return eset.find(e) != eset.end(); });
for (auto& e : eset)
......@@ -386,7 +339,7 @@ public:
vertex_t r = _b[v];
vertex_t s = _b[u];
auto& me = _emat.get_bedge(e);
auto me = _emat.get_me(r, s);
auto ew = _eweight[e];
_mrs[me] -= ew;
......@@ -396,39 +349,16 @@ public:
_mrp[r] -= ew;
_mrm[s] -= ew;
eop(e, me);
if (_mrs[me] == 0)
_emat.remove_me(r, s, me, _bg);
}
}
template <class Vec>
void remove_vertices(Vec& vs)
{
switch (_rec_type)
{
case weight_type::SIGNED: // signed weights
_bdrec[me] -= _drec[e];
case weight_type::POSITIVE: // positive weights
remove_vertices(vs, [&](auto& e, auto& me)
{ this->_brec[me] -= this->_rec[e]; });
break;
case weight_type::SIGNED: // positive and negative weights
remove_vertices(vs, [&](auto& e, auto& me)
{ this->_brec[me] -= this->_rec[e];
this->_bdrec[me] -= this->_drec[e];});
break;
case weight_type::DELTA_T: // waiting times
for (auto v : vs)
{
if (_ignore_degrees[v] > 0)
{
double dt = out_degreeS()(v, _g, _rec);
auto r = _b[v];
_brecsum[r] -= dt;
}
_brec[me] -= _rec[e];
}
case weight_type::NONE: // no weights
remove_vertices(vs, [&](auto&, auto&) {});
if (_mrs[me] == 0)
_emat.remove_me(r, s, me, _bg);
}
}
......@@ -438,46 +368,30 @@ public:
remove_vertices(vs);
}
template <class BEdge, class Efilt>
void add_vertex(size_t v, size_t r, BEdge&& bedge, Efilt&& efilt)
{
switch (_rec_type)
{
case weight_type::POSITIVE: // positive weights
modify_vertex<true>(v, r, bedge, efilt,
[&](auto& e, auto& me)
{
this->_brec[me] += this->_rec[e];
});
break;
case weight_type::SIGNED: // positive and negative weights
modify_vertex<true>(v, r, bedge, efilt,
[&](auto& e, auto& me)
template <class Efilt, class GetB>
void add_vertex(size_t v, size_t r, Efilt&& efilt, GetB&& get_b)
{
this->_brec[me] += this->_rec[e];
this->_bdrec[me] += this->_drec[e];
});
break;
case weight_type::DELTA_T: // waiting times
if (_ignore_degrees[v] > 0)
{
double dt = out_degreeS()(v, _g, _rec);
_brecsum[r] += dt;
}
case weight_type::NONE: // no weights
modify_vertex<true>(v, r, bedge, efilt, [](auto&, auto&){});
modify_vertex<true>(v, r, efilt, get_b);
}
template <class Efilt>
void add_vertex(size_t v, size_t r, Efilt&& efilt)
{
add_vertex(v, r, efilt,
[&](auto u) -> auto& { return this->_b[u]; });
}
void add_vertex(size_t v, size_t r)
{
add_vertex(v, r, _emat.get_bedge_map(),
[](auto&){ return false; });
add_vertex(v, r, [](auto&){ return false; });
}
template <class Vlist, class Blist, class EOP>
void add_vertices(Vlist& vs, Blist& rs, EOP&& eop)
template <class Vlist, class Blist>
void add_vertices(Vlist& vs, Blist& rs)
{
if (vs.size() != rs.size())
throw ValueException("vertex and group lists do not have the same size");
typedef typename graph_traits<g_t>::vertex_descriptor vertex_t;
gt_hash_map<vertex_t, size_t> vset;
......@@ -498,10 +412,8 @@ public:
}
}
auto bedge = _emat.get_bedge_map().get_checked();
for (auto vr : vset)
add_vertex(vr.first, vr.second, bedge,
add_vertex(vr.first, vr.second,
[&](auto& e){ return eset.find(e) != eset.end(); });
for (auto e : eset)
......@@ -522,9 +434,6 @@ public:
_c_bdrec[me] = 0;
}
bedge[e] = me;
assert(_emat.get_bedge(e) != _emat.get_null_edge());
assert(me == _emat.get_me(r, s));
auto ew = _eweight[e];
......@@ -533,40 +442,14 @@ public:
_mrp[r] += ew;
_mrm[s] += ew;
eop(e, me);
}
}
template <class Vs, class Rs>
void add_vertices(Vs& vs, Rs& rs)
{
if (vs.size() != rs.size())
throw ValueException("vertex and group lists do not have the same size");
switch (_rec_type)
{
case weight_type::SIGNED: // signed weights
_bdrec[me] += _drec[e];
case weight_type::POSITIVE: // positive weights
add_vertices(vs, rs, [&](auto& e, auto& me)
{ this->_brec[me] += this->_rec[e]; });
break;
case weight_type::SIGNED: // positive and negative weights
add_vertices(vs, rs, [&](auto& e, auto& me)
{ this->_brec[me] += this->_rec[e];
this->_bdrec[me] += this->_drec[e];});
break;
case weight_type::DELTA_T: // waiting times
for (size_t i = 0; i < rs.size(); ++i)
{
auto v = vs[i];
if (_ignore_degrees[v] > 0)
{
double dt = out_degreeS()(v, _g, _rec);
auto r = rs[i];
_brecsum[r] += dt;
_brec[me] += _rec[e];
}
}
case weight_type::NONE: // no weights
add_vertices(vs, rs, [&](auto&, auto&) {});
}
}
void add_vertices(python::object ovs, python::object ors)
......@@ -585,9 +468,10 @@ public:
}
// move a vertex from its current block to block nr
void move_vertex(size_t v, size_t nr)
template <class GetB>
void move_vertex(size_t v, size_t nr, GetB&& get_b)
{
size_t r = _b[v];
size_t r = get_b(v);
if (r == nr)
return;
......@@ -595,8 +479,8 @@ public:
if (!allow_move(r, nr))
throw ValueException("cannot move vertex across clabel barriers");
remove_vertex(v);
add_vertex(v, nr);
remove_vertex(v, r, [](auto&) {return false;}, get_b);
add_vertex(v, nr, [](auto&) {return false;}, get_b);
if (_coupled_state != nullptr && _vweight[v] > 0)
{
......@@ -615,6 +499,11 @@ public:
}
}
void move_vertex(size_t v, size_t nr)
{
move_vertex(v, nr, [&](auto u) -> auto& { return this->_b[u]; });
}
void set_vertex_weight(size_t v, int w)
{
set_vertex_weight(v, w, _vweight);
......@@ -691,7 +580,6 @@ public:
void merge_vertices(size_t u, size_t v, Emap& ec, std::true_type)
{
auto eweight_c = _eweight.get_checked();
auto bedge_c = _emat.get_bedge_map().get_checked();
auto rec_c = _rec.get_checked();