Commit f7e0ec7b authored by Tiago Peixoto's avatar Tiago Peixoto

Implement smarter merge-split MCMC

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