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)
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()(std::vector<double>(), "Vector_double");
class_<GraphInterface>("GraphInterface", init<>())
.def(init<GraphInterface,bool,boost::python::object,
......
......@@ -120,7 +120,8 @@ struct in_degreeS
auto get_in_degree(typename boost::graph_traits<Graph>::vertex_descriptor v,
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;
for (std::tie(e, e_end) = in_edges(v, g); e != e_end; ++e)
d += get(weight, *e);
......@@ -179,7 +180,8 @@ struct out_degreeS
auto get_out_degree(typename boost::graph_traits<Graph>::vertex_descriptor v,
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;
for (std::tie(e, e_end) = out_edges(v, g); e != e_end; ++e)
d += get(weight, *e);
......
......@@ -67,18 +67,17 @@ void clear_xlogx()
void init_lgamma(size_t x)
{
using namespace boost::math::policies;
#pragma omp critical (_lgamma_)
{
size_t old_size = __lgamma_cache.size();
if (x >= old_size)
{
__lgamma_cache.resize(x + 1);
if (old_size == 0)
__lgamma_cache[0] = numeric_limits<double>::infinity();
for (size_t i = std::max(old_size, size_t(1)); i < __lgamma_cache.size(); ++i)
__lgamma_cache[i] = boost::math::lgamma(i);
for (size_t i = std::max(old_size, size_t(1));
i < __lgamma_cache.size(); ++i)
__lgamma_cache[i] = lgamma(i);
}
}
}
......
......@@ -220,6 +220,7 @@ void export_blockmodel_state()
.def_readwrite("dense", &entropy_args_t::dense)
.def_readwrite("multigraph", &entropy_args_t::multigraph)
.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("degree_dl", &entropy_args_t::degree_dl)
.def_readwrite("degree_dl_kind", &entropy_args_t::degree_dl_kind)
......@@ -232,10 +233,11 @@ void export_blockmodel_state()
enum_<weight_type>("rec_type")
.value("none", weight_type::NONE)
.value("positive", weight_type::POSITIVE)
.value("signed", weight_type::SIGNED)
.value("real_exponential", weight_type::REAL_EXPONENTIAL)
.value("real_normal", weight_type::REAL_NORMAL)
.value("discrete_geometric", weight_type::DISCRETE_GEOMETRIC)
.value("discrete_poisson", weight_type::DISCRETE_POISSON)
.value("discrete_binomial", weight_type::DISCRETE_BINOMIAL)
.value("delta_t", weight_type::DELTA_T);
def("make_block_state", &make_block_state);
......@@ -255,4 +257,10 @@ void export_blockmodel_state()
def("log_q_approx", log_q_approx);
def("log_q_approx_big", log_q_approx_big);
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
}
double entropy(bool dense, bool multigraph, bool deg_entropy,
bool exact)
bool exact, bool recs)
{
double S = 0;
if (_master)
{
S += BaseState::entropy(dense, multigraph, deg_entropy, exact);
S += BaseState::entropy(dense, multigraph, deg_entropy, exact,
recs);
S -= covariate_entropy(_bg, _mrs);
if (multigraph)
S -= BaseState::get_parallel_entropy();
......@@ -481,7 +482,8 @@ struct Layers
else
{
for (auto& state : _layers)
S += state.entropy(dense, multigraph, deg_entropy, exact);
S += state.entropy(dense, multigraph, deg_entropy, exact,
recs);
}
return S;
}
......
......@@ -54,18 +54,13 @@ typedef mpl::vector2<std::true_type, std::false_type> use_hash_tr;
((bclabel,, vmap_t, 0)) \
((pclabel,, vmap_t, 0)) \
((deg_corr,, bool, 0)) \
((rec_type,, int, 0)) \
((rec,, eprop_map_t<double>::type, 0)) \
((drec,, eprop_map_t<double>::type, 0)) \
((brec,, eprop_map_t<double>::type, 0)) \
((bdrec,, eprop_map_t<double>::type, 0)) \
((rec_types,, std::vector<int>, 0)) \
((rec,, eprop_map_t<std::vector<double>>::type, 0)) \
((drec,, eprop_map_t<std::vector<double>>::type, 0)) \
((brec,, eprop_map_t<std::vector<double>>::type, 0)) \
((bdrec,, eprop_map_t<std::vector<double>>::type, 0)) \
((brecsum,, vprop_map_t<double>::type, 0)) \
((m0,, double, 0)) \
((k0,, double, 0)) \
((v0,, double, 0)) \
((nu0,, double, 0)) \
((alpha,, double, 0)) \
((beta,, double, 0)) \
((wparams,, std::vector<std::vector<double>>, 0)) \
((allow_empty,, bool, 0))
GEN_STATE_BASE(OverlapBlockStateBase, OVERLAP_BLOCK_STATE_params)
......@@ -160,27 +155,25 @@ public:
void remove_vertex(size_t v)
{
switch (_rec_type)
{
case weight_type::POSITIVE: // positive weights
remove_vertex(v,
[&](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)
for (size_t i = 0; i < this->_rec_types.size(); ++i)
{
this->_brec[me] -= this->_rec[e];
this->_bdrec[me] -= this->_drec[e];
});
break;
case weight_type::NONE: // no weights
remove_vertex(v, [](auto&, auto&){});
switch (this->_rec_types[i])
{
case weight_type::REAL_NORMAL: // signed weights
this->_bdrec[me][i] -= this->_drec[e][i];
case weight_type::REAL_EXPONENTIAL:
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>
void add_vertex(size_t v, size_t r, EOP&& eop)
......@@ -199,8 +192,8 @@ public:
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;
_c_brec[me].clear();
_c_bdrec[me].clear();
}
assert(me == _emat.get_me(r, s));
......@@ -225,8 +218,8 @@ public:
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;
_c_brec[me].clear();
_c_bdrec[me].clear();
}
assert(me == _emat.get_me(s, r));
......@@ -249,26 +242,24 @@ public:
void add_vertex(size_t v, size_t r)
{
switch (_rec_type)
{
case weight_type::POSITIVE: // positive weights
add_vertex(v, r,
[&](auto& e, auto& me)
{
this->_brec[me] += this->_rec[e];
});
break;
case weight_type::SIGNED: // positive and negative weights
add_vertex(v, r,
[&](auto& e, auto& me)
for (size_t i = 0; i < this->_rec_types.size(); ++i)
{
this->_brec[me] += this->_rec[e];
this->_bdrec[me] += this->_drec[e];
});
break;
case weight_type::NONE: // no weights
add_vertex(v, r, [](auto&, auto&){});
switch (this->_rec_types[i])
{
case weight_type::REAL_NORMAL: // signed weights
this->_bdrec[me][i] += this->_drec[e][i];
case weight_type::REAL_EXPONENTIAL:
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)
......@@ -337,12 +328,20 @@ public:
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);
break;
case weight_type::SIGNED: // positive and negative weights
case weight_type::REAL_NORMAL:
mv_entries(_rec, _drec);
break;
default: // no weights
......@@ -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)
{
size_t ers = 0;
double xrs = 0;
if (me != m_entries.get_null_edge())
if (me != _emat.get_null_edge())
{
ers = this->_mrs[me];
xrs = this->_brec[me];
xrs = this->_brec[me][i];
}
auto d = get<0>(delta);
auto dx = get<1>(delta);
dS -= -positive_w_log_P(ers, xrs,
this->_alpha, this->_beta);
dS += -positive_w_log_P(ers + d, xrs + dx,
this->_alpha, this->_beta);
auto dx = get<1>(delta)[i];
dS -= -w_log_P(ers, xrs);
dS += -w_log_P(ers + d, xrs + dx);
});
};
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;
case weight_type::SIGNED: // positive and negative weights
case weight_type::REAL_NORMAL:
entries_op(m_entries, _emat,
[&](auto, auto, auto& me, auto& delta)
{
size_t ers = 0;
double xrs = 0, x2rs = 0;
if (me != m_entries.get_null_edge())
if (me != _emat.get_null_edge())
{
ers = this->_mrs[me];
xrs = this->_brec[me];
x2rs = this->_bdrec[me];
xrs = this->_brec[me][i];
x2rs = this->_bdrec[me][i];
}
auto d = get<0>(delta);
auto dx = get<1>(delta);
auto dx2 = get<2>(delta);
auto dx = get<1>(delta)[i];
auto dx2 = get<2>(delta)[i];
auto sigma1 = x2rs - xrs * (xrs / ers);
auto sigma2 = x2rs + dx2 - (xrs + dx) * ((xrs + dx) / (ers + d));
dS -= -signed_w_log_P(ers, xrs, sigma1,
this->_m0,
this->_k0,
this->_v0,
this->_nu0);
wparams[0],
wparams[1],
wparams[2],
wparams[3]);
dS += -signed_w_log_P(ers + d, xrs + dx,
sigma2,
this->_m0,
this->_k0,
this->_v0,
this->_nu0);
wparams[0],
wparams[1],
wparams[2],
wparams[3]);
});
break;
}
}
return dS;
}
......@@ -734,7 +770,7 @@ public:
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)
{
......@@ -757,7 +793,8 @@ public:
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;
if (dense)
......@@ -765,27 +802,74 @@ public:
else
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))
{
auto ers = _mrs[me];
auto xrs = _brec[me];
S += -positive_w_log_P(ers, xrs, _alpha, _beta);
auto xrs = _brec[me][i];
S += -positive_w_log_P(ers, xrs,
_wparams[i][0],
_wparams[i][1]);
}
break;
case weight_type::SIGNED: // positive and negative weights
case weight_type::DISCRETE_GEOMETRIC:
for (auto me : edges_range(_bg))
{
auto ers = _mrs[me];
auto xrs = _brec[me];
auto x2rs = _bdrec[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];
auto xrs = _brec[me][i];
S += -poisson_w_log_P(ers, xrs,
_wparams[i][0],
_wparams[i][1]);
}
for (auto e : edges_range(_g))
S += boost::math::lgamma(_rec[e][i] + 1);
break;
case weight_type::DISCRETE_BINOMIAL:
for (auto me : edges_range(_bg))
{
auto ers = _mrs[me];
auto xrs = _brec[me][i];
S += -binomial_w_log_P(ers, xrs,
_wparams[i][0],
_wparams[i][1],
_wparams[i][2]);
}
for (auto e : edges_range(_g))
S -= lbinom(_wparams[i][0], _rec[e][i]);
break;
case weight_type::REAL_NORMAL:
for (auto me : edges_range(_bg))
{
auto ers = _mrs[me];
auto xrs = _brec[me][i];
auto x2rs = _bdrec[me][i];
auto sigma = x2rs - xrs * (xrs / ers);
S += -signed_w_log_P(ers, xrs, sigma, _m0, _k0, _v0, _nu0);
S += -signed_w_log_P(ers, xrs, sigma,
_wparams[i][0],
_wparams[i][1],
_wparams[i][2],
_wparams[i][3]);
}
break;
}
}
}
return S;
}
......@@ -812,7 +896,7 @@ public:
double get_parallel_entropy()
{
double S = 0;
for(auto& h : _overlap_stats.get_parallel_bundles())
for (auto& h : _overlap_stats.get_parallel_bundles())
{
for (auto& kc : h)
S += lgamma_fast(kc.second + 1);
......@@ -1021,7 +1105,8 @@ public:
std::vector<size_t> _bmap;
std::vector<size_t> _vmap;
typedef SingleEntrySet<g_t, bg_t, int, double, double> m_entries_t;
typedef SingleEntrySet<g_t, bg_t, int, std::vector<double>,
std::vector<double>> m_entries_t;
m_entries_t _m_entries;
UnityPropertyMap<int,GraphInterface::edge_t> _eweight;
......
......@@ -1248,12 +1248,17 @@ public:
void set_move(size_t, size_t) {}
template <class... DVals>
void insert_delta(size_t t, size_t s, DVals... delta)
void insert_delta(bool add, size_t t, size_t s, DVals... delta)
{
if (!is_directed::apply<Graph>::type::value && (t > s))
std::swap(t, s);
_entries[_pos] = make_pair(t, s);
add_to_tuple(_delta[_pos], delta...);
if (add)
tuple_op(_delta[_pos], [&](auto& r, auto& v){ r += v; },
delta...);
else
tuple_op(_delta[_pos], [&](auto& r, auto& v){ r -= v; },
delta...);
++_pos;
}
......@@ -1314,6 +1319,8 @@ public:
return emat.get_me(t, s);
}
std::tuple<EVals...> _self_weight;
private:
size_t _pos;
std::array<pair<size_t, size_t>, 2> _entries;
......
......@@ -39,26 +39,61 @@
#include <omp.h>
#endif
namespace std
{
template <class T>
void operator+=(vector<T>& ret, const vector<T>& v)
{
ret.resize(max(ret.size(), v.size()));
for (size_t i = 0; i < v.size(); ++i)
ret[i] += v[i];
}
template <class T>
void operator-=(vector<T>& ret, const vector<T>& v)
{
ret.resize(max(ret.size(), v.size()));
for (size_t i = 0; i < v.size(); ++i)
ret[i] -= v[i];
}
template <class T1, class T2>
void operator*=(vector<T1>& ret, const T2& v)
{
for (auto& x : ret)
x *= v;
}
template <class T1, class T2>
void operator/=(vector<T1>& ret, const T2& v)
{
for (auto& x : ret)
x /= v;
}
}
namespace graph_tool
{
// tuple utils
template <size_t i, class T>
void add_to_tuple_imp(T&)
template <size_t i, class T, class OP>
void tuple_op_imp(T&, OP&&)