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,27 +155,25 @@ public: ...@@ -160,27 +155,25 @@ public:
void remove_vertex(size_t v) void remove_vertex(size_t v)
{ {
switch (_rec_type)
{
case weight_type::POSITIVE: // positive weights
remove_vertex(v, remove_vertex(v,
[&](auto& e, auto& me) [&](auto& e, auto& me)
{ {
this->_brec[me] -= this->_rec[e]; for (size_t i = 0; i < this->_rec_types.size(); ++i)
});
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>
void add_vertex(size_t v, size_t r, EOP&& eop) void add_vertex(size_t v, size_t r, EOP&& 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)
{
case weight_type::POSITIVE: // positive weights
add_vertex(v, r, add_vertex(v, r,
[&](auto& e, auto& me) [&](auto& e, auto& me)
{ {
this->_brec[me] += this->_rec[e]; for (size_t i = 0; i < this->_rec_types.size(); ++i)
});
break;
case weight_type::SIGNED: // positive and negative weights
add_vertex(v, r,
[&](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:
add_vertex(v, r, [](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];
}
} }
});
} }
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, this->_emat,
entries_op(m_entries, _emat,
[&](auto, auto, auto& me, auto& delta) [&](auto, auto, auto& me, auto& delta)
{ {
size_t ers = 0; size_t ers = 0;
double xrs = 0; double xrs = 0;
if (me != m_entries.get_null_edge()) if (me != _emat.get_null_edge())
{ {
ers = this->_mrs[me]; ers = this->_mrs[me];
xrs = this->_brec[me]; xrs = this->_brec[me][i];
} }
auto d = get<0>(delta); auto d = get<0>(delta);
auto dx = get<1>(delta); auto dx = get<1>(delta)[i];
dS -= -positive_w_log_P(ers, xrs, dS -= -w_log_P(ers, xrs);
this->_alpha, this->_beta); dS += -w_log_P(ers + d, xrs + dx);
dS += -positive_w_log_P(ers + d, xrs + dx, });
this->_alpha, this->_beta); };
for (size_t i = 0; i < _rec_types.size(); ++i)
{
auto& wparams = _wparams[i];
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; break;
case weight_type::SIGNED: // positive and negative weights case weight_type::REAL_NORMAL:
entries_op(m_entries, _emat, entries_op(m_entries, _emat,
[&](auto, auto, auto& me, auto& delta) [&](auto, auto, auto& me, auto& delta)
{ {
size_t ers = 0; size_t ers = 0;
double xrs = 0, x2rs = 0; double xrs = 0, x2rs = 0;
if (me != m_entries.get_null_edge()) if (me != _emat.get_null_edge())
{ {
ers = this->_mrs[me]; ers = this->_mrs[me];
xrs = this->_brec[me]; xrs = this->_brec[me][i];
x2rs = this->_bdrec[me]; x2rs = this->_bdrec[me][i];
} }
auto d = get<0>(delta); auto d = get<0>(delta);
auto dx = get<1>(delta); auto dx = get<1>(delta)[i];
auto dx2 = get<2>(delta); auto dx2 = get<2>(delta)[i];
auto sigma1 = x2rs - xrs * (xrs / ers); auto sigma1 = x2rs - xrs * (xrs / ers);
auto sigma2 = x2rs + dx2 - (xrs + dx) * ((xrs + dx) / (ers + d)); auto sigma2 = x2rs + dx2 - (xrs + dx) * ((xrs + dx) / (ers + d));
dS -= -signed_w_log_P(ers, xrs, sigma1, dS -= -signed_w_log_P(ers, xrs, sigma1,
this->_m0, wparams[0],
this->_k0, wparams[1],
this->_v0, wparams[2],
this->_nu0); wparams[3]);
dS += -signed_w_log_P(ers + d, xrs + dx, dS += -signed_w_log_P(ers + d, xrs + dx,
sigma2, sigma2,
this->_m0, wparams[0],
this->_k0, wparams[1],
this->_v0, wparams[2],
this->_nu0); 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,27 +802,74 @@ public: ...@@ -765,27 +802,74 @@ public:
else else
S = sparse_entropy(multigraph, deg_entropy, exact); S = sparse_entropy(multigraph, deg_entropy, exact);
switch (_rec_type) if (recs)
{
for (size_t i = 0; i < _rec_types.size(); ++i)
{ {
case weight_type::POSITIVE: // positive weights switch (_rec_types[i])
{
case weight_type::REAL_EXPONENTIAL:
for (auto me : edges_range(_bg)) for (auto me : edges_range(_bg))
{ {
auto ers = _mrs[me]; auto ers = _mrs[me];
auto xrs = _brec[me]; auto xrs = _brec[me][i];
S += -positive_w_log_P(ers, xrs, _alpha, _beta); S += -positive_w_log_P(ers, xrs,
_wparams[i][0],
_wparams[i][1]);
} }
break; break;
case weight_type::SIGNED: // positive and negative weights case weight_type::DISCRETE_GEOMETRIC:
for (auto me : edges_range(_bg)) for (auto me : edges_range(_bg))
{ {
auto ers = _mrs[me]; auto ers = _mrs[me];
auto xrs = _brec[me]; auto xrs = _brec[me][i];
auto x2rs = _bdrec[me]; 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];
auto xrs = _brec[me][i];
S += -poisson_w_log_P(ers, xrs,
_wparams[i][0],
_wparams[i][1]);
}