// graph-tool -- a general graph modification and manipulation thingy // // Copyright (C) 2006-2020 Tiago de Paula Peixoto // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License as published by the Free // Software Foundation; either version 3 of the License, or (at your option) any // later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more // details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . #define BOOST_PYTHON_MAX_ARITY 20 #include #include "graph_tool.hh" #include "random.hh" #define GRAPH_BLOCKMODEL_RMAP_ENABLE #include "../blockmodel/graph_blockmodel.hh" #include "graph_blockmodel_layers_util.hh" #define BASE_STATE_params BLOCK_STATE_params #include "graph_blockmodel_layers.hh" #include "../support/graph_state.hh" using namespace boost; using namespace graph_tool; GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) template GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, LAYERED_BLOCK_STATE_params) python::object make_layered_block_state(boost::python::object oblock_state, boost::python::object olayered_state) { python::object state; auto dispatch = [&](auto& block_state) { typedef typename std::remove_reference::type state_t; layered_block_state::make_dispatch (olayered_state, [&](auto& s) { state = python::object(s); }, block_state); }; block_state::dispatch(oblock_state, dispatch); return state; } template vector from_list(boost::python::object list) { vector v; for (int i = 0; i < boost::python::len(list); ++i) v.push_back(boost::python::extract(list[i])()); return v; }; template vector> from_rlist(boost::python::object list) { vector> v; for (int i = 0; i < boost::python::len(list); ++i) v.emplace_back(boost::python::extract(list[i])()); return v; }; template vector> from_any_list(boost::python::object list) { vector> v; for (int i = 0; i < boost::python::len(list); ++i) v.emplace_back(any_cast(boost::python::extract(list[i])())); return v; }; void split_layers(GraphInterface& gi, boost::any& aec, boost::any& ab, boost::python::object& arec, boost::python::object& adrec, boost::any& aeweight, boost::any& avweight, boost::any& avc, boost::any& avmap, boost::any& alweight, boost::python::object& ous, boost::python::object& oub, boost::python::object& ourec, boost::python::object& oudrec, boost::python::object& oueweight, boost::python::object& ouvweight, vbmap_t& block_map, boost::python::object& obrmap, boost::python::object& ouvmap) { typedef vprop_map_t::type vmap_t; typedef vprop_map_t>::type vvmap_t; typedef eprop_map_t::type emap_t; typedef eprop_map_t::type remap_t; emap_t& ec = any_cast(aec); vmap_t& b = any_cast(ab); auto rec = from_any_list(arec); auto drec = from_any_list(adrec); vmap_t& vweight = any_cast(avweight); emap_t& eweight = any_cast(aeweight); vvmap_t& vc = any_cast(avc); vvmap_t& vmap = any_cast(avmap); vvmap_t& lweight = any_cast(alweight); auto us = from_rlist(ous); auto ub = from_any_list(oub); vector>> urec, udrec; for (int i = 0; i < boost::python::len(ourec); ++i) urec.push_back(from_any_list(ourec[i])); for (int i = 0; i < boost::python::len(oudrec); ++i) udrec.push_back(from_any_list(oudrec[i])); auto uvweight = from_any_list(ouvweight); auto ueweight = from_any_list(oueweight); auto block_rmap = from_any_list(obrmap); auto uvmap = from_any_list(ouvmap); run_action<>()(gi, [&](auto& g) { std::vector> vhmap(num_vertices(g)); typename vprop_map_t>::type lw(get(vertex_index, g)); for (auto v : vertices_range(g)) { for (size_t i = 0; i < lweight[v].size(); i += 2) { auto l = lweight[v][i]; auto w = lweight[v][i + 1]; lw[v][l] = w; } } auto get_v = [&] (size_t v, size_t l) -> size_t { auto iter = vhmap[v].find(l); if (iter == vhmap[v].end()) { size_t u = add_vertex(us[l].get().get_graph()); vhmap[v][l] = u; size_t pos = lower_bound(vc[v].begin(), vc[v].end(), l) - vc[v].begin(); vc[v].insert(vc[v].begin() + pos, l); vmap[v].insert(vmap[v].begin() + pos, u); uvmap[l].get()[u] = v; if (lw[v].empty()) { uvweight[l].get()[u] = vweight[v]; } else { assert(lw[v].find(l) != lw[v].end()); uvweight[l].get()[u] = lw[v][l]; } size_t r = b[v]; size_t u_r; if (block_map.size() <= l) block_map.resize(l + 1); auto& bmap = block_map[l]; auto riter = bmap.find(r); if (riter == bmap.end()) { u_r = bmap.size(); bmap[r] = u_r; block_rmap[l].get()[u_r] = r; } else { u_r = riter->second; } ub[l].get()[u] = u_r; return u; } else { return iter->second; } }; for (auto e : edges_range(g)) { auto s = source(e, g); auto t = target(e, g); size_t l = ec[e]; auto u_s = get_v(s, l); auto u_t = get_v(t, l); auto ne = add_edge(u_s, u_t, us[l].get().get_graph()).first; ueweight[l].get()[ne] = eweight[e]; for (size_t i = 0; i < rec.size(); ++i) { urec[l][i].get()[ne] = rec[i].get()[e]; udrec[l][i].get()[ne] = drec[i].get()[e]; } assert(uvweight[l].get()[u_s] > 0 || eweight[e] == 0); assert(uvweight[l].get()[u_t] > 0 || eweight[e] == 0); } })(); } void split_groups(boost::any& ab, boost::any& avc, boost::any& avmap, boost::python::object& ous, boost::python::object& oub, boost::python::object& ouvweight, vbmap_t& block_map, boost::python::object& obrmap, boost::python::object& ouvmap) { typedef vprop_map_t::type vmap_t; typedef vprop_map_t>::type vvmap_t; vmap_t& b = any_cast(ab); vvmap_t& vc = any_cast(avc); vvmap_t& vmap = any_cast(avmap); auto us = from_rlist(ous); auto ub = from_any_list(oub); auto block_rmap = from_any_list(obrmap); auto uvmap = from_any_list(ouvmap); auto uvweight = from_any_list(ouvweight); block_map.resize(us.size()); for (size_t l = 0; l < us.size(); ++l) { auto& u = us[l].get().get_graph(); auto& uvmap_l = uvmap[l].get(); auto& ub_l = ub[l].get(); auto& bmap = block_map[l]; auto& brmap = block_rmap[l].get(); auto& vweight_l = uvweight[l].get(); for (auto w : vertices_range(u)) { if (vweight_l[w] == 0) continue; auto v = uvmap_l[w]; vc[v].push_back(l); vmap[v].push_back(w); size_t r = b[v]; size_t u_r; auto riter = bmap.find(r); if (riter == bmap.end()) { u_r = bmap.size(); bmap[r] = u_r; brmap[u_r] = r; } else { u_r = riter->second; } ub_l[w] = u_r; } } } void get_rvmap(GraphInterface& gi, boost::any& avc, boost::any& avmap, boost::python::object& ouvmap) { typedef vprop_map_t::type vmap_t; typedef vprop_map_t>::type vvmap_t; vvmap_t& vc = any_cast(avc); vvmap_t& vmap = any_cast(avmap); auto uvmap = from_any_list(ouvmap); run_action<>()(gi, [&](auto& g) { for (auto v : vertices_range(g)) { auto& ls = vc[v]; auto& vs = vmap[v]; for (size_t i = 0; i < ls.size(); ++i) { auto l = ls[i]; auto w = vs[i]; uvmap[l].get()[w] = v; } } })(); } void get_lweights(GraphInterface& gi, boost::any& avc, boost::any& avmap, boost::any& alweight, boost::python::object& ouvweight) { typedef vprop_map_t::type vmap_t; typedef vprop_map_t>::type vvmap_t; vvmap_t& vc = any_cast(avc); vvmap_t& vmap = any_cast(avmap); vvmap_t& lweight = any_cast(alweight); auto uvweight = from_any_list(ouvweight); run_action<>()(gi, [&](auto& g) { for (auto v : vertices_range(g)) { for (size_t i = 0; i < vc[v].size(); ++i) { auto l = vc[v][i]; auto u = vmap[v][i]; auto w = uvweight[l].get()[u]; lweight[v].push_back(l); lweight[v].push_back(w); } } })(); } void get_blweights(GraphInterface& gi, boost::any& ab, boost::any& avc, boost::any& avmap, boost::any& alweight, boost::python::object& ouvweight) { typedef vprop_map_t::type vmap_t; typedef vprop_map_t>::type vvmap_t; vmap_t& b = any_cast(ab); vvmap_t& vc = any_cast(avc); vvmap_t& vmap = any_cast(avmap); vvmap_t& lweight = any_cast(alweight); auto uvweight = from_any_list(ouvweight); run_action<>()(gi, [&](auto& g) { gt_hash_map> blw; for (auto v : vertices_range(g)) { auto r = b[v]; for (size_t i = 0; i < vc[v].size(); ++i) { auto l = vc[v][i]; auto u = vmap[v][i]; auto w = uvweight[l].get()[u]; blw[r][l] += w; } } for (auto& rlw : blw) { auto r = rlw.first; for (auto& lw : rlw.second) { auto l = lw.first; auto w = lw.second; lweight[r].push_back(l); lweight[r].push_back(w); } } })(); } bool bmap_has(const vbmap_t& bmap, size_t c, size_t r) { if (c > bmap.size()) throw GraphException("invalid covariate value:" + lexical_cast(c)); auto iter = bmap[c].find(r); if (iter == bmap[c].end()) return false; return true; } size_t bmap_get(const vbmap_t& bmap, size_t c, size_t r) { if (c > bmap.size()) throw GraphException("invalid covariate value:" + lexical_cast(c)); auto iter = bmap[c].find(r); if (iter == bmap[c].end()) throw GraphException("no mapping for block " + lexical_cast(r) + " in layer " + lexical_cast(c)); return iter->second; } void bmap_set(vbmap_t& bmap, size_t c, size_t r, size_t r_u) { if (c > bmap.size()) throw GraphException("invalid covariate value:" + lexical_cast(c)); bmap[c][r] = r_u; } void bmap_del_c(vbmap_t& bmap, size_t c) { if (c > bmap.size()) throw GraphException("invalid covariate value:" + lexical_cast(c)); bmap.erase(bmap.begin() + c); } vbmap_t bmap_copy(const vbmap_t& bmap) { return bmap; } size_t bmap_size(const vbmap_t& bmap) { return bmap.size(); } typedef gt_hash_map, gt_hash_map, size_t>> ldegs_map_t; ldegs_map_t get_layered_block_degs(GraphInterface& gi, boost::any aeweight, boost::any avweight, boost::any aec, boost::any ab) { ldegs_map_t degs; vmap_t b = boost::any_cast(ab); emap_t eweight = boost::any_cast(aeweight); vmap_t vweight = boost::any_cast(avweight); emap_t ec = boost::any_cast(aec); run_action<>()(gi, [&](auto& g) { for (auto v : vertices_range(g)) { gt_hash_map kin, kout; gt_hash_set ls; for (auto e : out_edges_range(v, g)) { auto w = eweight[e]; auto l = ec[e]; kout[l] += w; ls.insert(l); } if (graph_tool::is_directed(g)) { 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) { size_t skin = 0, skout = 0; auto iter = kin.find(l); if (iter != kin.end()) skin = iter->second; iter = kout.find(l); if (iter != kout.end()) skout = iter->second; auto& h = degs[std::make_tuple(l + 1, b[v])]; h[std::make_tuple(skin, skout)] += vweight[v]; } size_t skin = in_degreeS()(v, g, eweight); size_t skout = out_degreeS()(v, g, eweight); auto& h = degs[std::make_tuple(0, b[v])]; h[std::make_tuple(skin, skout)] += vweight[v]; } })(); return degs; } degs_map_t get_mapped_block_degs(GraphInterface& gi, ldegs_map_t& ldegs, int l, boost::any avmap) { degs_map_t ndegs; vmap_t vmap = boost::any_cast(avmap); run_action<>()(gi, [&](auto& g) { for (auto u : vertices_range(g)) { int v = vmap[u]; auto& d = ndegs[u]; for (auto& ks : ldegs[std::make_tuple(l, v)]) d.emplace_back(get<0>(ks.first), get<1>(ks.first), ks.second); } })(); return ndegs; } ldegs_map_t get_ldegs(GraphInterface& gi, boost::any& avc, boost::any& avmap, boost::python::object& oudegs) { typedef vprop_map_t>::type vvmap_t; vvmap_t& vc = any_cast(avc); vvmap_t& vmap = any_cast(avmap); auto udegs = from_rlist(oudegs); ldegs_map_t ndegs; run_action<>()(gi, [&](auto& g) { gt_hash_map> blw; for (int v : vertices_range(g)) { auto& d = udegs[0].get()[v]; auto& h = ndegs[std::make_tuple(0, v)]; for (auto& kn : d) h[std::make_tuple(get<0>(kn), get<1>(kn))] = get<2>(kn); for (size_t i = 0; i < vc[v].size(); ++i) { int l = vc[v][i]; auto u = vmap[v][i]; auto& d = udegs[l + 1].get()[u]; auto& h = ndegs[std::make_tuple(l + 1, v)]; for (auto& kn : d) h[std::make_tuple(get<0>(kn), get<1>(kn))] = get<2>(kn); } } })(); return ndegs; } ldegs_map_t ldegs_map_copy(ldegs_map_t& ldegs) { return ldegs; } void export_lsbm(); void export_layered_blockmodel_state() { using namespace boost::python; export_lsbm(); def("make_layered_block_state", &make_layered_block_state); def("split_layers", &split_layers); def("split_groups", &split_groups); def("get_rvmap", &get_rvmap); def("get_layered_block_degs", &get_layered_block_degs); def("get_mapped_block_degs", &get_mapped_block_degs); def("get_ldegs", &get_ldegs); def("get_lweights", &get_lweights); def("get_blweights", &get_blweights); class_("ldegs_map_t") .def("copy", &ldegs_map_copy); class_("bmap_t") .def("has", bmap_has) .def("get", bmap_get) .def("set", bmap_set) .def("del_c", bmap_del_c) .def("copy", bmap_copy) .def("size", bmap_size); }