Commit 0cebc67b authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

inference.blockmodel: Implement multiple edge covariates

parent 5053de1f
...@@ -475,6 +475,7 @@ BOOST_PYTHON_MODULE(libgraph_tool_core) ...@@ -475,6 +475,7 @@ BOOST_PYTHON_MODULE(libgraph_tool_core)
boost::mpl::for_each<boost::mpl::push_back<scalar_types,string>::type>(export_vector_types()); boost::mpl::for_each<boost::mpl::push_back<scalar_types,string>::type>(export_vector_types());
export_vector_types()(size_t(), "size_t"); export_vector_types()(size_t(), "size_t");
export_vector_types()(std::vector<double>(), "Vector_double");
class_<GraphInterface>("GraphInterface", init<>()) class_<GraphInterface>("GraphInterface", init<>())
.def(init<GraphInterface,bool,boost::python::object, .def(init<GraphInterface,bool,boost::python::object,
......
...@@ -120,7 +120,8 @@ struct in_degreeS ...@@ -120,7 +120,8 @@ struct in_degreeS
auto get_in_degree(typename boost::graph_traits<Graph>::vertex_descriptor v, auto get_in_degree(typename boost::graph_traits<Graph>::vertex_descriptor v,
const Graph& g, std::true_type, Weight& weight) const const Graph& g, std::true_type, Weight& weight) const
{ {
typename boost::property_traits<Weight>::value_type d = 0; typedef typename boost::property_traits<Weight>::value_type val_t;
val_t d = val_t();
typename boost::graph_traits<Graph>::in_edge_iterator e, e_end; typename boost::graph_traits<Graph>::in_edge_iterator e, e_end;
for (std::tie(e, e_end) = in_edges(v, g); e != e_end; ++e) for (std::tie(e, e_end) = in_edges(v, g); e != e_end; ++e)
d += get(weight, *e); d += get(weight, *e);
...@@ -179,7 +180,8 @@ struct out_degreeS ...@@ -179,7 +180,8 @@ struct out_degreeS
auto get_out_degree(typename boost::graph_traits<Graph>::vertex_descriptor v, auto get_out_degree(typename boost::graph_traits<Graph>::vertex_descriptor v,
const Graph& g, const Weight& weight) const const Graph& g, const Weight& weight) const
{ {
typename boost::property_traits<Weight>::value_type d = 0; typedef typename boost::property_traits<Weight>::value_type val_t;
val_t d = val_t();
typename boost::graph_traits<Graph>::out_edge_iterator e, e_end; typename boost::graph_traits<Graph>::out_edge_iterator e, e_end;
for (std::tie(e, e_end) = out_edges(v, g); e != e_end; ++e) for (std::tie(e, e_end) = out_edges(v, g); e != e_end; ++e)
d += get(weight, *e); d += get(weight, *e);
......
...@@ -67,18 +67,17 @@ void clear_xlogx() ...@@ -67,18 +67,17 @@ void clear_xlogx()
void init_lgamma(size_t x) void init_lgamma(size_t x)
{ {
using namespace boost::math::policies;
#pragma omp critical (_lgamma_) #pragma omp critical (_lgamma_)
{ {
size_t old_size = __lgamma_cache.size(); size_t old_size = __lgamma_cache.size();
if (x >= old_size) if (x >= old_size)
{ {
__lgamma_cache.resize(x + 1); __lgamma_cache.resize(x + 1);
if (old_size == 0) if (old_size == 0)
__lgamma_cache[0] = numeric_limits<double>::infinity(); __lgamma_cache[0] = numeric_limits<double>::infinity();
for (size_t i = std::max(old_size, size_t(1)); i < __lgamma_cache.size(); ++i) for (size_t i = std::max(old_size, size_t(1));
__lgamma_cache[i] = boost::math::lgamma(i); i < __lgamma_cache.size(); ++i)
__lgamma_cache[i] = lgamma(i);
} }
} }
} }
......
...@@ -220,6 +220,7 @@ void export_blockmodel_state() ...@@ -220,6 +220,7 @@ void export_blockmodel_state()
.def_readwrite("dense", &entropy_args_t::dense) .def_readwrite("dense", &entropy_args_t::dense)
.def_readwrite("multigraph", &entropy_args_t::multigraph) .def_readwrite("multigraph", &entropy_args_t::multigraph)
.def_readwrite("adjacency", &entropy_args_t::adjacency) .def_readwrite("adjacency", &entropy_args_t::adjacency)
.def_readwrite("recs", &entropy_args_t::recs)
.def_readwrite("partition_dl", &entropy_args_t::partition_dl) .def_readwrite("partition_dl", &entropy_args_t::partition_dl)
.def_readwrite("degree_dl", &entropy_args_t::degree_dl) .def_readwrite("degree_dl", &entropy_args_t::degree_dl)
.def_readwrite("degree_dl_kind", &entropy_args_t::degree_dl_kind) .def_readwrite("degree_dl_kind", &entropy_args_t::degree_dl_kind)
...@@ -232,10 +233,11 @@ void export_blockmodel_state() ...@@ -232,10 +233,11 @@ void export_blockmodel_state()
enum_<weight_type>("rec_type") enum_<weight_type>("rec_type")
.value("none", weight_type::NONE) .value("none", weight_type::NONE)
.value("positive", weight_type::POSITIVE) .value("real_exponential", weight_type::REAL_EXPONENTIAL)
.value("signed", weight_type::SIGNED) .value("real_normal", weight_type::REAL_NORMAL)
.value("discrete_geometric", weight_type::DISCRETE_GEOMETRIC) .value("discrete_geometric", weight_type::DISCRETE_GEOMETRIC)
.value("discrete_poisson", weight_type::DISCRETE_POISSON) .value("discrete_poisson", weight_type::DISCRETE_POISSON)
.value("discrete_binomial", weight_type::DISCRETE_BINOMIAL)
.value("delta_t", weight_type::DELTA_T); .value("delta_t", weight_type::DELTA_T);
def("make_block_state", &make_block_state); def("make_block_state", &make_block_state);
...@@ -255,4 +257,10 @@ void export_blockmodel_state() ...@@ -255,4 +257,10 @@ void export_blockmodel_state()
def("log_q_approx", log_q_approx); def("log_q_approx", log_q_approx);
def("log_q_approx_big", log_q_approx_big); def("log_q_approx_big", log_q_approx_big);
def("log_q_approx_small", log_q_approx_small); def("log_q_approx_small", log_q_approx_small);
def("positive_w_log_P", positive_w_log_P<size_t>);
def("signed_w_log_P", signed_w_log_P<size_t>);
def("geometric_w_log_P", geometric_w_log_P<size_t>);
def("binomial_w_log_P", binomial_w_log_P<size_t>);
def("poisson_w_log_P", poisson_w_log_P<size_t>);
} }
This diff is collapsed.
...@@ -462,12 +462,13 @@ struct Layers ...@@ -462,12 +462,13 @@ struct Layers
} }
double entropy(bool dense, bool multigraph, bool deg_entropy, double entropy(bool dense, bool multigraph, bool deg_entropy,
bool exact) bool exact, bool recs)
{ {
double S = 0; double S = 0;
if (_master) if (_master)
{ {
S += BaseState::entropy(dense, multigraph, deg_entropy, exact); S += BaseState::entropy(dense, multigraph, deg_entropy, exact,
recs);
S -= covariate_entropy(_bg, _mrs); S -= covariate_entropy(_bg, _mrs);
if (multigraph) if (multigraph)
S -= BaseState::get_parallel_entropy(); S -= BaseState::get_parallel_entropy();
...@@ -481,7 +482,8 @@ struct Layers ...@@ -481,7 +482,8 @@ struct Layers
else else
{ {
for (auto& state : _layers) for (auto& state : _layers)
S += state.entropy(dense, multigraph, deg_entropy, exact); S += state.entropy(dense, multigraph, deg_entropy, exact,
recs);
} }
return S; return S;
} }
......
...@@ -54,18 +54,13 @@ typedef mpl::vector2<std::true_type, std::false_type> use_hash_tr; ...@@ -54,18 +54,13 @@ typedef mpl::vector2<std::true_type, std::false_type> use_hash_tr;
((bclabel,, vmap_t, 0)) \ ((bclabel,, vmap_t, 0)) \
((pclabel,, vmap_t, 0)) \ ((pclabel,, vmap_t, 0)) \
((deg_corr,, bool, 0)) \ ((deg_corr,, bool, 0)) \
((rec_type,, int, 0)) \ ((rec_types,, std::vector<int>, 0)) \
((rec,, eprop_map_t<double>::type, 0)) \ ((rec,, eprop_map_t<std::vector<double>>::type, 0)) \
((drec,, eprop_map_t<double>::type, 0)) \ ((drec,, eprop_map_t<std::vector<double>>::type, 0)) \
((brec,, eprop_map_t<double>::type, 0)) \ ((brec,, eprop_map_t<std::vector<double>>::type, 0)) \
((bdrec,, eprop_map_t<double>::type, 0)) \ ((bdrec,, eprop_map_t<std::vector<double>>::type, 0)) \
((brecsum,, vprop_map_t<double>::type, 0)) \ ((brecsum,, vprop_map_t<double>::type, 0)) \
((m0,, double, 0)) \ ((wparams,, std::vector<std::vector<double>>, 0)) \
((k0,, double, 0)) \
((v0,, double, 0)) \
((nu0,, double, 0)) \
((alpha,, double, 0)) \
((beta,, double, 0)) \
((allow_empty,, bool, 0)) ((allow_empty,, bool, 0))
GEN_STATE_BASE(OverlapBlockStateBase, OVERLAP_BLOCK_STATE_params) GEN_STATE_BASE(OverlapBlockStateBase, OVERLAP_BLOCK_STATE_params)
...@@ -160,26 +155,24 @@ public: ...@@ -160,26 +155,24 @@ public:
void remove_vertex(size_t v) void remove_vertex(size_t v)
{ {
switch (_rec_type) remove_vertex(v,
{ [&](auto& e, auto& me)
case weight_type::POSITIVE: // positive weights {
remove_vertex(v, for (size_t i = 0; i < this->_rec_types.size(); ++i)
[&](auto& e, auto& me)
{
this->_brec[me] -= this->_rec[e];
});
break;
case weight_type::SIGNED: // positive and negative weights
remove_vertex(v,
[&](auto& e, auto& me)
{ {
this->_brec[me] -= this->_rec[e]; switch (this->_rec_types[i])
this->_bdrec[me] -= this->_drec[e]; {
}); case weight_type::REAL_NORMAL: // signed weights
break; this->_bdrec[me][i] -= this->_drec[e][i];
case weight_type::NONE: // no weights case weight_type::REAL_EXPONENTIAL:
remove_vertex(v, [](auto&, auto&){}); case weight_type::DISCRETE_GEOMETRIC:
} case weight_type::DISCRETE_POISSON:
case weight_type::DISCRETE_BINOMIAL:
case weight_type::DELTA_T:
this->_brec[me][i] -= this->_rec[e][i];
}
}
});
} }
template <class EOP> template <class EOP>
...@@ -199,8 +192,8 @@ public: ...@@ -199,8 +192,8 @@ public:
me = add_edge(r, s, _bg).first; me = add_edge(r, s, _bg).first;
_emat.put_me(r, s, me); _emat.put_me(r, s, me);
_c_mrs[me] = 0; _c_mrs[me] = 0;
_c_brec[me] = 0; _c_brec[me].clear();
_c_bdrec[me] = 0; _c_bdrec[me].clear();
} }
assert(me == _emat.get_me(r, s)); assert(me == _emat.get_me(r, s));
...@@ -225,8 +218,8 @@ public: ...@@ -225,8 +218,8 @@ public:
me = add_edge(s, r, _bg).first; me = add_edge(s, r, _bg).first;
_emat.put_me(s, r, me); _emat.put_me(s, r, me);
_c_mrs[me] = 0; _c_mrs[me] = 0;
_c_brec[me] = 0; _c_brec[me].clear();
_c_bdrec[me] = 0; _c_bdrec[me].clear();
} }
assert(me == _emat.get_me(s, r)); assert(me == _emat.get_me(s, r));
...@@ -249,26 +242,24 @@ public: ...@@ -249,26 +242,24 @@ public:
void add_vertex(size_t v, size_t r) void add_vertex(size_t v, size_t r)
{ {
switch (_rec_type) add_vertex(v, r,
{ [&](auto& e, auto& me)
case weight_type::POSITIVE: // positive weights {
add_vertex(v, r, for (size_t i = 0; i < this->_rec_types.size(); ++i)
[&](auto& e, auto& me)
{ {
this->_brec[me] += this->_rec[e]; switch (this->_rec_types[i])
}); {
break; case weight_type::REAL_NORMAL: // signed weights
case weight_type::SIGNED: // positive and negative weights this->_bdrec[me][i] += this->_drec[e][i];
add_vertex(v, r, case weight_type::REAL_EXPONENTIAL:
[&](auto& e, auto& me) case weight_type::DISCRETE_GEOMETRIC:
{ case weight_type::DISCRETE_POISSON:
this->_brec[me] += this->_rec[e]; case weight_type::DISCRETE_BINOMIAL:
this->_bdrec[me] += this->_drec[e]; case weight_type::DELTA_T:
}); this->_brec[me][i] += this->_rec[e][i];
break; }
case weight_type::NONE: // no weights }
add_vertex(v, r, [](auto&, auto&){}); });
}
} }
bool allow_move(size_t r, size_t nr) bool allow_move(size_t r, size_t nr)
...@@ -337,12 +328,20 @@ public: ...@@ -337,12 +328,20 @@ public:
is_loop_overlap(_overlap_stats), args...); is_loop_overlap(_overlap_stats), args...);
}; };
switch (_rec_type) int rec_type = weight_type::NONE;
for (auto rt : _rec_types)
{ {
case weight_type::POSITIVE: // positive weights rec_type = rt;
if (rt == weight_type::REAL_NORMAL)
break;
}
switch (rec_type)
{
case weight_type::REAL_EXPONENTIAL:
mv_entries(_rec); mv_entries(_rec);
break; break;
case weight_type::SIGNED: // positive and negative weights case weight_type::REAL_NORMAL:
mv_entries(_rec, _drec); mv_entries(_rec, _drec);
break; break;
default: // no weights default: // no weights
...@@ -451,59 +450,96 @@ public: ...@@ -451,59 +450,96 @@ public:
} }
} }
switch (_rec_type)
{ auto positive_entries_op = [&](size_t i, auto&& w_log_P)
case weight_type::POSITIVE: // positive weights {
entries_op(m_entries, _emat, entries_op(m_entries, this->_emat,
[&](auto, auto, auto& me, auto& delta) [&](auto, auto, auto& me, auto& delta)
{
size_t ers = 0;
double xrs = 0;
if (me != m_entries.get_null_edge())
{ {
ers = this->_mrs[me]; size_t ers = 0;
xrs = this->_brec[me]; double xrs = 0;
} if (me != _emat.get_null_edge())
auto d = get<0>(delta); {
auto dx = get<1>(delta); ers = this->_mrs[me];
dS -= -positive_w_log_P(ers, xrs, xrs = this->_brec[me][i];
this->_alpha, this->_beta); }
dS += -positive_w_log_P(ers + d, xrs + dx, auto d = get<0>(delta);
this->_alpha, this->_beta); auto dx = get<1>(delta)[i];
}); dS -= -w_log_P(ers, xrs);
break; dS += -w_log_P(ers + d, xrs + dx);
case weight_type::SIGNED: // positive and negative weights });
entries_op(m_entries, _emat, };
[&](auto, auto, auto& me, auto& delta)
{ for (size_t i = 0; i < _rec_types.size(); ++i)
size_t ers = 0; {
double xrs = 0, x2rs = 0; auto& wparams = _wparams[i];
if (me != m_entries.get_null_edge()) switch (_rec_types[i])
{
case weight_type::REAL_EXPONENTIAL:
positive_entries_op(i,
[&](auto N, auto x)
{ return positive_w_log_P(N, x,
wparams[0],
wparams[1]);
});
break;
case weight_type::DISCRETE_GEOMETRIC:
positive_entries_op(i,
[&](auto N, auto x)
{ return geometric_w_log_P(N, x,
wparams[0],
wparams[1]);
});
break;
case weight_type::DISCRETE_POISSON:
positive_entries_op(i,
[&](auto N, auto x)
{ return poisson_w_log_P(N, x,
wparams[0],
wparams[1]);
});
break;
case weight_type::DISCRETE_BINOMIAL:
positive_entries_op(i,
[&](auto N, auto x)
{ return binomial_w_log_P(N, x,
wparams[0],
wparams[1],
wparams[2]);
});
break;
case weight_type::REAL_NORMAL:
entries_op(m_entries, _emat,
[&](auto, auto, auto& me, auto& delta)
{ {
ers = this->_mrs[me]; size_t ers = 0;
xrs = this->_brec[me]; double xrs = 0, x2rs = 0;
x2rs = this->_bdrec[me]; if (me != _emat.get_null_edge())
} {
auto d = get<0>(delta); ers = this->_mrs[me];
auto dx = get<1>(delta); xrs = this->_brec[me][i];
auto dx2 = get<2>(delta); x2rs = this->_bdrec[me][i];
auto sigma1 = x2rs - xrs * (xrs / ers); }
auto sigma2 = x2rs + dx2 - (xrs + dx) * ((xrs + dx) / (ers + d)); auto d = get<0>(delta);
dS -= -signed_w_log_P(ers, xrs, sigma1, auto dx = get<1>(delta)[i];
this->_m0, auto dx2 = get<2>(delta)[i];
this->_k0, auto sigma1 = x2rs - xrs * (xrs / ers);
this->_v0, auto sigma2 = x2rs + dx2 - (xrs + dx) * ((xrs + dx) / (ers + d));
this->_nu0); dS -= -signed_w_log_P(ers, xrs, sigma1,
dS += -signed_w_log_P(ers + d, xrs + dx, wparams[0],
sigma2, wparams[1],
this->_m0, wparams[2],
this->_k0, wparams[3]);
this->_v0, dS += -signed_w_log_P(ers + d, xrs + dx,
this->_nu0); sigma2,
}); wparams[0],
wparams[1],
wparams[2],
wparams[3]);
});
break; break;
}
} }
return dS; return dS;
} }
...@@ -734,7 +770,7 @@ public: ...@@ -734,7 +770,7 @@ public:
if (multigraph) if (multigraph)
{ {
for(const auto& h : _overlap_stats.get_parallel_bundles()) for (const auto& h : _overlap_stats.get_parallel_bundles())
{ {
for (const auto& kc : h) for (const auto& kc : h)
{ {
...@@ -757,7 +793,8 @@ public: ...@@ -757,7 +793,8 @@ public:
throw GraphException("Dense entropy for overlapping model not implemented!"); throw GraphException("Dense entropy for overlapping model not implemented!");
} }
double entropy(bool dense, bool multigraph, bool deg_entropy, bool exact) double entropy(bool dense, bool multigraph, bool deg_entropy, bool exact,
bool recs)
{ {
double S = 0; double S = 0;
if (dense) if (dense)
...@@ -765,26 +802,73 @@ public: ...@@ -765,26 +802,73 @@ public:
else else
S = sparse_entropy(multigraph, deg_entropy, exact); S = sparse_entropy(multigraph, deg_entropy, exact);
switch (_rec_type) if (recs)
{ {
case weight_type::POSITIVE: // positive weights for (size_t i = 0; i < _rec_types.size(); ++i)
for (auto me : edges_range(_bg))
{
auto ers = _mrs[me];
auto xrs = _brec[me];
S += -positive_w_log_P(ers, xrs, _alpha, _beta);
}
break;
case weight_type::SIGNED: // positive and negative weights
for (auto me : edges_range(_bg))
{ {
auto ers = _mrs[me]; switch (_rec_types[i])
auto xrs = _brec[me]; {
auto x2rs = _bdrec[me]; case weight_type::REAL_EXPONENTIAL:
auto sigma = x2rs - xrs * (xrs / ers); for (auto me : edges_range(_bg))
S += -signed_w_log_P(ers, xrs, sigma, _m0, _k0, _v0, _nu0); {
auto ers = _mrs[me];
auto xrs = _brec[me][i];
S += -positive_w_log_P(ers, xrs,
_wparams[i][0],
_wparams[i][1]);
}
break;
case weight_type::DISCRETE_GEOMETRIC:
for (auto me : edges_range(_bg))
{
auto ers = _mrs[me];
auto xrs = _brec[me][i];
S += -geometric_w_log_P(ers, xrs,
_wparams[i][0],
_wparams[i][1]);
}
break;
case weight_type::DISCRETE_POISSON:
for (auto me : edges_range(_bg))
{
auto ers = _mrs[me];