Commit f7e0ec7b authored by Tiago Peixoto's avatar Tiago Peixoto

Implement smarter merge-split MCMC

parent e7446106
......@@ -71,7 +71,10 @@ struct Multicanonical
int _i;
double _dS;
size_t _null_move = null_group;
typedef decltype(_state._null_move) move_t;
move_t _null_move = _state._null_move;
int get_bin(double S)
{
......@@ -95,12 +98,12 @@ struct Multicanonical
}
template <class RNG>
size_t move_proposal(size_t v, RNG& rng)
move_t move_proposal(size_t v, RNG& rng)
{
return _state.move_proposal(v, rng);
}
auto virtual_move_dS(size_t v, size_t nr)
auto virtual_move_dS(size_t v, move_t nr)
{
auto dS = _state.virtual_move_dS(v, nr);
double nS = _S + get<0>(dS);
......@@ -117,7 +120,7 @@ struct Multicanonical
return dS;
}
void perform_move(size_t v, size_t nr)
void perform_move(size_t v, move_t nr)
{
_state.perform_move(v, nr);
_S += _dS;
......@@ -149,7 +152,7 @@ struct Multicanonical
return _state.get_niter();
}
void step(size_t, size_t)
void step(size_t, move_t)
{
_hist[_i]++;
_dens[_i] += _f;
......@@ -157,7 +160,6 @@ struct Multicanonical
};
};
} // graph_tool namespace
#endif //GRAPH_BLOCKMODEL_MULTICANONICAL_HH
......@@ -119,6 +119,14 @@ python::object do_multiflip_mcmc_sweep_parallel(python::object omcmc_states,
return orets;
}
namespace graph_tool
{
std::ostream& operator<<(std::ostream& os, move_t move)
{
return os << static_cast<int>(move);
}
}
void export_blockmodel_multiflip_mcmc()
{
using namespace boost::python;
......
......@@ -92,17 +92,19 @@ auto mcmc_sweep(MCMCState state, RNG& rng)
nattempts += state.node_weight(v);
bool accept = false;
if (metropolis_accept(dS, mP, beta, rng))
{
state.perform_move(v, s);
nmoves += state.node_weight(v);
S += dS;
accept = true;
}
state.step(v, s);
if (state._verbose)
cout << v << ": " << r << " -> " << s << " " << S << endl;
cout << v << ": " << r << " -> " << s << " " << accept << " " << dS << " " << mP << " " << -dS * beta + mP << " " << S << endl;
}
if (state.is_sequential() && state.is_deterministic())
......
......@@ -32,11 +32,6 @@ using namespace std;
boost::multi_array<double, 2> __q_cache;
double log_sum(double a, double b)
{
return std::max(a, b) + std::log1p(exp(-abs(a-b)));
}
void init_q_cache(size_t n_max)
{
size_t old_n = __q_cache.shape()[0];
......
......@@ -74,6 +74,14 @@ inline auto lbeta(T x, T y)
return (std::lgamma(x) + std::lgamma(y)) - std::lgamma(x + y);
}
template <class T>
T log_sum(T a, T b)
{
if (a < b)
std::swap(a, b);
return a + std::log1p(exp(b-a));
}
template <class Vec, class PosMap, class Val>
void remove_element(Vec& vec, PosMap& pos, Val val)
{
......
......@@ -1523,20 +1523,15 @@ class BlockState(object):
_get_rng())
def multiflip_mcmc_sweep(self, a1=.99, an=.9, beta=1., c=1., d=.01, niter=1,
entropy_args={}, allow_vacate=True,
sequential=True, verbose=False, **kwargs):
def multiflip_mcmc_sweep(self, beta=1., c=1., a1=.95, psplit=.5,
d=0.01, gibbs_sweeps=0, niter=1, entropy_args={},
verbose=False, **kwargs):
r"""Perform ``niter`` sweeps of a Metropolis-Hastings acceptance-rejection
sampling MCMC with multiple simultaneous moves to sample network
partitions.
Parameters
----------
a1 : ``float`` (optional, default: ``.9``)
Probability of single moves.
an : ``float`` (optional, default: ``.9``)
Relative probability of group merges. The complementary probability
will correspond to multiple moves within a group.
beta : ``float`` (optional, default: ``1.``)
Inverse temperature.
c : ``float`` (optional, default: ``1.``)
......@@ -1545,16 +1540,23 @@ class BlockState(object):
node and their block connections; for :math:`c\to\infty` the blocks
are sampled randomly. Note that only for :math:`c > 0` the MCMC is
guaranteed to be ergodic.
a1 : ``float`` (optional, default: ``.95``)
Probability of proposing a single node move.
psplit : ``float`` (optional, default: ``.5``)
Probability of proposing a group split. A group merge will be
proposed with probability ``1-psplit``.
d : ``float`` (optional, default: ``.01``)
Probability of selecting a new (i.e. empty) group for a given move.
Probability of selecting a new (i.e. empty) group for a given
single-node move.
gibbs_sweeps : ``int`` (optional, default: ``0``)
Number of sweeps of Gibbs sampling to be performed (i.e. each node
is attempted once per sweep) to refine a split proposal.
niter : ``int`` (optional, default: ``1``)
Number of sweeps to perform. During each sweep, a move attempt is
made for each node.
made for each node, on average.
entropy_args : ``dict`` (optional, default: ``{}``)
Entropy arguments, with the same meaning and defaults as in
:meth:`graph_tool.inference.blockmodel.BlockState.entropy`.
allow_vacate : ``bool`` (optional, default: ``True``)
Allow groups to be vacated.
verbose : ``bool`` (optional, default: ``False``)
If ``verbose == True``, detailed information will be displayed.
......@@ -1582,16 +1584,6 @@ class BlockState(object):
not isinstance(self.degs, libinference.simple_degs_t)):
entropy_args["multigraph"] = False
mcmc_state.entropy_args = get_entropy_args(entropy_args)
mcmc_state.mproposals = Vector_size_t()
M = self.g.num_vertices() + 1
mcmc_state.mproposals.resize(M)
if "mproposals" in kwargs:
mcmc_state.mproposals.a = kwargs["mproposals"][:M]
mcmc_state.maccept = Vector_size_t()
mcmc_state.maccept.resize(M)
if "maccept" in kwargs:
mcmc_state.maccept.a = kwargs["maccept"][:M]
mcmc_state.E = self.get_E()
mcmc_state.state = self._state
dispatch = kwargs.pop("dispatch", True)
......
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