Commit 74ab77ec authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Recover missing functions: BlockState.add/remove_vertex()

parent 80878919
......@@ -464,6 +464,74 @@ public:
remove_vertex(v, r);
}
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;
gt_hash_set<vertex_t> vset(vs.begin(), vs.end());
gt_hash_set<edges_t> eset;
for (auto v : vset)
{
for (auto e : all_edges_range(v, _g))
{
auto u = (source(e, _g) == v) ? target(e, _g) : source(e, _g);
if (vset.find(u) != vset.end())
eset.insert(e);
}
}
for (auto v : vset)
remove_vertex(v, _b[v],
[&](auto& e) { return eset.find(e) != eset.end(); });
for (auto& e : eset)
{
vertex_t v = source(e, _g);
vertex_t u = target(e, _g);
vertex_t r = _b[v];
vertex_t s = _b[u];
auto me = _emat.get_me(r, s);
auto ew = _eweight[e];
_mrs[me] -= ew;
assert(_mrs[me] >= 0);
_mrp[r] -= ew;
_mrm[s] -= ew;
for (size_t i = 0; i < _rec_types.size(); ++i)
{
switch (_rec_types[i])
{
case weight_type::REAL_NORMAL: // signed weights
_bdrec[i][me] -= _drec[i][e];
[[gnu::fallthrough]];
default:
_brec[i][me] -= _rec[i][e];
}
}
if (_mrs[me] == 0)
{
_emat.remove_me(me, _bg);
if (_coupled_state != nullptr)
_coupled_state->remove_edge(me);
else
boost::remove_edge(me, this->_bg);
}
}
}
void remove_vertices(python::object ovs)
{
multi_array_ref<uint64_t, 1> vs = get_array<uint64_t, 1>(ovs);
remove_vertices(vs);
}
template <class EFilt>
void add_vertex(size_t v, size_t r, EFilt&& efilt)
......@@ -476,6 +544,89 @@ public:
add_vertex(v, r, [](auto&){ return false; });
}
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;
for (size_t i = 0; i < vs.size(); ++i)
vset[vs[i]] = rs[i];
typedef typename graph_traits<g_t>::edge_descriptor edges_t;
gt_hash_set<edges_t> eset;
for (auto vr : vset)
{
auto v = vr.first;
for (auto e : all_edges_range(v, _g))
{
auto u = (source(e, _g) == v) ? target(e, _g) : source(e, _g);
if (vset.find(u) != vset.end())
eset.insert(e);
}
}
for (auto vr : vset)
add_vertex(vr.first, vr.second,
[&](auto& e){ return eset.find(e) != eset.end(); });
for (auto e : eset)
{
vertex_t v = source(e, _g);
vertex_t u = target(e, _g);
vertex_t r = vset[v];
vertex_t s = vset[u];
auto me = _emat.get_me(r, s);
if (me == _emat.get_null_edge())
{
me = boost::add_edge(r, s, _bg).first;
_emat.put_me(r, s, me);
_c_mrs[me] = 0;
for (size_t i = 0; i < _rec_types.size(); ++i)
{
_c_brec[i][me] = 0;
_c_bdrec[i][me] = 0;
}
if (_coupled_state != nullptr)
_coupled_state->add_edge(me);
}
assert(me == _emat.get_me(r, s));
auto ew = _eweight[e];
_mrs[me] += ew;
_mrp[r] += ew;
_mrm[s] += ew;
for (size_t i = 0; i < _rec_types.size(); ++i)
{
switch (_rec_types[i])
{
case weight_type::REAL_NORMAL: // signed weights
_bdrec[i][me] += _drec[i][e];
[[gnu::fallthrough]];
default:
_brec[i][me] += _rec[i][e];
}
}
}
}
void add_vertices(python::object ovs, python::object ors)
{
multi_array_ref<uint64_t, 1> vs = get_array<uint64_t, 1>(ovs);
multi_array_ref<uint64_t, 1> rs = get_array<uint64_t, 1>(ors);
add_vertices(vs, rs);
}
template <bool Add, bool Deplete=true>
void modify_edge(size_t u, size_t v, GraphInterface::edge_t& e,
const std::vector<double>& rec)
......@@ -664,6 +815,22 @@ public:
vweight[v] = 0;
}
template <class Vec>
void move_vertices(Vec& v, Vec& nr)
{
for (size_t i = 0; i < std::min(v.size(), nr.size()); ++i)
move_vertex(v[i], nr[i]);
}
void move_vertices(python::object ovs, python::object ors)
{
multi_array_ref<uint64_t, 1> vs = get_array<uint64_t, 1>(ovs);
multi_array_ref<uint64_t, 1> rs = get_array<uint64_t, 1>(ors);
if (vs.size() != rs.size())
throw ValueException("vertex and group lists do not have the same size");
move_vertices(vs, rs);
}
template <class VMap>
void set_partition(VMap&& b)
{
......
......@@ -41,8 +41,14 @@ void export_sbm_state()
([&](auto* s)
{
typedef typename std::remove_reference<decltype(*s)>::type state_t;
void (state_t::*remove_vertices)(python::object) =
&state_t::remove_vertices;
void (state_t::*add_vertices)(python::object, python::object) =
&state_t::add_vertices;
void (state_t::*move_vertex)(size_t, size_t) =
&state_t::move_vertex;
void (state_t::*move_vertices)(python::object, python::object) =
&state_t::move_vertices;
double (state_t::*virtual_move)(size_t, size_t, size_t,
const entropy_args_t&) =
&state_t::virtual_move;
......@@ -59,7 +65,10 @@ void export_sbm_state()
class_<state_t, bases<BlockStateVirtualBase>>
c(name_demangle(typeid(state_t).name()).c_str(),
no_init);
c.def("move_vertex", move_vertex)
c.def("remove_vertices", remove_vertices)
.def("add_vertices", add_vertices)
.def("move_vertex", move_vertex)
.def("move_vertices", move_vertices)
.def("set_partition", set_partition)
.def("virtual_move", virtual_move)
.def("sample_block", sample_block)
......
......@@ -40,8 +40,14 @@ void export_sbm_state_rmap()
{
typedef typename std::remove_reference<decltype(*s)>::type state_t;
// void (state_t::*remove_vertices)(python::object) =
// &state_t::remove_vertices;
// void (state_t::*add_vertices)(python::object, python::object) =
// &state_t::add_vertices;
// void (state_t::*move_vertex)(size_t, size_t) =
// &state_t::move_vertex;
// void (state_t::*move_vertices)(python::object, python::object) =
// &state_t::move_vertices;
// double (state_t::*virtual_move)(size_t, size_t, size_t,
// entropy_args_t) =
// &state_t::virtual_move;
......@@ -62,7 +68,10 @@ void export_sbm_state_rmap()
&state_t::get_B_E)
.def("get_B_E_D",
&state_t::get_B_E_D);
// c.def("move_vertex", move_vertex)
// c.def("remove_vertices", remove_vertices)
// .def("add_vertices", add_vertices)
// .def("move_vertex", move_vertex)
// .def("move_vertices", move_vertices)
// .def("set_partition", set_partition)
// .def("virtual_move", virtual_move)
// .def("sample_block", sample_block)
......
......@@ -334,6 +334,22 @@ struct Layers
// assert(check_edge_counts());
}
template <class Vec>
void move_vertices(Vec& v, Vec& nr)
{
for (size_t i = 0; i < std::min(v.size(), nr.size()); ++i)
move_vertex(v[i], nr[i]);
}
void move_vertices(python::object ovs, python::object ors)
{
multi_array_ref<uint64_t, 1> vs = get_array<uint64_t, 1>(ovs);
multi_array_ref<uint64_t, 1> rs = get_array<uint64_t, 1>(ors);
if (vs.size() != rs.size())
throw ValueException("vertex and group lists do not have the same size");
move_vertices(vs, rs);
}
void remove_vertex(size_t v)
{
size_t r = _b[v];
......@@ -351,6 +367,53 @@ struct Layers
_actual_B--;
}
template <class Vec>
void remove_vertices(Vec& vs)
{
gt_hash_map<size_t, vector<size_t>> lvs;
gt_hash_set<size_t> rs;
for (auto v : vs)
{
for (auto l : _vc[v])
lvs[l].push_back(v);
rs.insert(_b[v]);
}
for (auto& lv : lvs)
{
auto l = lv.first;
auto& state = _layers[l];
vector<size_t> us;
gt_hash_map<size_t, size_t> rus;
for (auto v : lv.second)
{
auto u = _vmap[v][l];
us.push_back(u);
size_t r = _b[v];
size_t r_u = state._b[u];
rus[r] = r_u;
}
state.remove_vertices(us);
// for (auto rr_u : rus)
// {
// if (state._wr[rr_u.second] == 0)
// state.remove_block_map(rr_u.first);
// }
}
BaseState::remove_vertices(vs);
for (auto r : rs)
{
if (_wr[r] == 0)
_actual_B--;
}
}
void remove_vertices(python::object ovs)
{
multi_array_ref<uint64_t, 1> vs = get_array<uint64_t, 1>(ovs);
remove_vertices(vs);
}
void add_vertex(size_t v, size_t r)
{
auto& ls = _vc[v];
......@@ -368,6 +431,50 @@ struct Layers
BaseState::add_vertex(v, r);
}
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");
gt_hash_map<size_t, vector<size_t>> lvs;
gt_hash_map<size_t, size_t> vrs;
for (size_t i = 0; i < vs.size(); ++i)
{
auto v = vs[i];
vrs[v] = rs[i];
for (auto l : _vc[v])
lvs[l].push_back(v);
}
for (auto& lv : lvs)
{
auto l = lv.first;
auto& state = _layers[l];
vector<size_t> us;
vector<size_t> rus;
for (auto v : lv.second)
{
us.emplace_back(_vmap[v][l]);
rus.emplace_back(state.get_block_map(vrs[v]));
}
state.add_vertices(us, rus);
}
for (auto r : rs)
{
if (_wr[r] == 0)
_actual_B++;
}
BaseState::add_vertices(vs, rs);
}
void add_vertices(python::object ovs, python::object ors)
{
multi_array_ref<uint64_t, 1> vs = get_array<uint64_t, 1>(ovs);
multi_array_ref<uint64_t, 1> rs = get_array<uint64_t, 1>(ors);
add_vertices(vs, rs);
}
template <class VMap>
void set_partition(VMap&& b)
{
......
......@@ -65,6 +65,12 @@ void export_lsbm()
= &state_t::get_move_prob;
void (state_t::*set_partition)(boost::any&)
= &state_t::set_partition;
void (state_t::*move_vertices)(python::object, python::object) =
&state_t::move_vertices;
void (state_t::*remove_vertices)(python::object) =
&state_t::remove_vertices;
void (state_t::*add_vertices)(python::object, python::object) =
&state_t::add_vertices;
void (state_t::*couple_state)(LayeredBlockStateVirtualBase&,
const entropy_args_t&) =
&state_t::couple_state;
......@@ -75,6 +81,9 @@ void export_lsbm()
c.def("remove_vertex", &state_t::remove_vertex)
.def("add_vertex", &state_t::add_vertex)
.def("move_vertex", &state_t::move_vertex)
.def("add_vertices", add_vertices)
.def("remove_vertices", remove_vertices)
.def("move_vertices", move_vertices)
.def("set_partition", set_partition)
.def("virtual_move", virtual_move)
.def("sample_block", sample_block)
......
......@@ -84,6 +84,9 @@ void export_layered_overlap_blockmodel_state()
double (state_t::*get_move_prob)(size_t, size_t, size_t,
double, double, bool)
= &state_t::get_move_prob;
void (state_t::*move_vertices)(python::object,
python::object) =
&state_t::move_vertices;
void (state_t::*couple_state)(LayeredBlockStateVirtualBase&,
const entropy_args_t&) =
&state_t::couple_state;
......@@ -93,6 +96,7 @@ void export_layered_overlap_blockmodel_state()
c.def("remove_vertex", &state_t::remove_vertex)
.def("add_vertex", &state_t::add_vertex)
.def("move_vertex", &state_t::move_vertex)
.def("move_vertices", move_vertices)
.def("virtual_move", virtual_move)
.def("sample_block", sample_block)
.def("entropy", &state_t::entropy)
......
......@@ -181,6 +181,8 @@ void export_overlap_blockmodel_state()
= &state_t::get_move_prob;
void (state_t::*set_partition)(boost::any&)
= &state_t::set_partition;
void (state_t::*move_vertices)(python::object, python::object) =
&state_t::move_vertices;
void (state_t::*remove_vertex)(size_t) = &state_t::remove_vertex;
void (state_t::*add_vertex)(size_t, size_t) = &state_t::add_vertex;
......@@ -189,6 +191,7 @@ void export_overlap_blockmodel_state()
c.def("remove_vertex", remove_vertex)
.def("add_vertex", add_vertex)
.def("move_vertex", &state_t::move_vertex)
.def("move_vertices", move_vertices)
.def("set_partition", set_partition)
.def("virtual_move", virtual_move)
.def("sample_block", sample_block)
......
......@@ -275,6 +275,22 @@ public:
get_partition_stats(v).move_vertex(v, r, nr, _g);
}
template <class Vec>
void move_vertices(Vec& v, Vec& nr)
{
for (size_t i = 0; i < std::min(v.size(), nr.size()); ++i)
move_vertex(v[i], nr[i]);
}
void move_vertices(python::object ovs, python::object ors)
{
multi_array_ref<uint64_t, 1> vs = get_array<uint64_t, 1>(ovs);
multi_array_ref<uint64_t, 1> rs = get_array<uint64_t, 1>(ors);
if (vs.size() != rs.size())
throw ValueException("vertex and group lists do not have the same size");
move_vertices(vs, rs);
}
void add_edge(size_t, size_t, GraphInterface::edge_t&,
const std::vector<double>&)
{
......
......@@ -1131,28 +1131,55 @@ class BlockState(MCMCState, MultiflipMCMCState, MultilevelMCMCState,
**kwargs)))
def move_vertex(self, v, s):
r"""Move vertex ``v`` to block ``s``."""
self._state.move_vertex(int(v), s)
r"""Move vertex ``v`` to block ``s``.
This optionally accepts a list of vertices and blocks to move
simultaneously.
"""
if not isinstance(v, collections.abc.Iterable):
self._state.move_vertex(int(v), s)
else:
self._state.move_vertices(numpy.asarray(v, dtype="uint64"),
numpy.asarray(s, dtype="uint64"))
def remove_vertex(self, v):
r"""Remove vertex ``v`` from its current group.
This optionally accepts a list of vertices to remove.
.. warning::
This will leave the state in an inconsistent state before the vertex
is returned to some other group, or if the same vertex is removed
twice.
"""
self._state.remove_vertex(int(v))
if isinstance(v, collections.abc.Iterable):
if not isinstance(v, numpy.ndarray):
v = list(v)
self._state.remove_vertices(numpy.asarray(v, dtype="uint64"))
else:
self._state.remove_vertex(int(v))
def add_vertex(self, v, r):
r"""Add vertex ``v`` to block ``r``.
This optionally accepts a list of vertices and blocks to add.
.. warning::
This can leave the state in an inconsistent state if a vertex is
added twice to the same group.
"""
self._state.add_vertex(int(v), r)
if isinstance(v, collections.abc.Iterable):
if not isinstance(v, numpy.ndarray):
v = list(v)
if not isinstance(r, numpy.ndarray):
r = list(r)
self._state.add_vertices(numpy.asarray(v, dtype="uint64"),
numpy.asarray(r, dtype="uint64"))
else:
self._state.add_vertex(int(v), r)
def sample_vertex_move(self, v, c=1., d=.1):
r"""Sample block membership proposal of vertex ``v`` according to real-valued
......
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