Commit 918ae899 authored by Tiago Peixoto's avatar Tiago Peixoto

Initial implementation of partition modes

parent 2d134e6d
......@@ -31,6 +31,10 @@ libgraph_tool_inference_la_SOURCES = \
partition_centroid/graph_partition_centroid.cc \
partition_centroid/graph_partition_centroid_mcmc.cc \
partition_centroid/graph_partition_centroid_multiflip_mcmc.cc \
partition_modes/graph_partition_mode.cc \
partition_modes/graph_partition_mode_clustering.cc \
partition_modes/graph_partition_mode_clustering_mcmc.cc \
partition_modes/graph_partition_mode_clustering_multiflip_mcmc.cc \
overlap/graph_blockmodel_overlap.cc \
overlap/graph_blockmodel_overlap_exhaustive.cc \
overlap/graph_blockmodel_overlap_gibbs.cc \
......@@ -100,6 +104,8 @@ libgraph_tool_inference_la_include_HEADERS = \
blockmodel/graph_blockmodel_util.hh \
blockmodel/graph_blockmodel_weights.hh \
partition_centroid/graph_partition_centroid.hh \
partition_modes/graph_partition_mode.hh \
partition_modes/graph_partition_mode_clustering.hh \
overlap/graph_blockmodel_overlap.hh \
overlap/graph_blockmodel_overlap_mcmc_bundled.hh \
overlap/graph_blockmodel_overlap_util.hh \
......
......@@ -2584,6 +2584,12 @@ public:
assert(size_t(_wr[r]) == wr[r]);
}
template <class V>
void push_state(V&) {}
void pop_state() {}
void store_next_state(size_t) {}
void clear_next_state() {}
//private:
typedef typename
std::conditional<is_directed_::apply<g_t>::type::value,
......
......@@ -130,6 +130,7 @@ struct MCMC
auto& back = _bstack.back();
for (auto v : vs)
back.emplace_back(v, _state._b[v]);
_state.push_state(vs);
_push_b_dispatch(std::forward<Vs>(vvs)...);
}
......@@ -150,6 +151,7 @@ struct MCMC
move_vertex(v, s);
}
_bstack.pop_back();
_state.pop_state();
}
std::vector<size_t> _rlist;
......@@ -214,10 +216,10 @@ struct MCMC
void move_vertex(size_t v, size_t r)
{
size_t s = _state._b[v];
_state.move_vertex(v, r);
if (s == r)
return;
remove_element(_groups[s], _vpos, v);
_state.move_vertex(v, r);
add_element(_groups[r], _vpos, v);
_nmoves++;
}
......@@ -281,11 +283,11 @@ struct MCMC
stage_split_random(std::vector<size_t>& vs, size_t r, size_t s, RNG& rng)
{
std::array<size_t, 2> rt = {null_group, null_group};
std::array<double, 2> ps;
double dS = 0;
std::uniform_real_distribution<> unit(0, 1);
double p = unit(rng);
double p0 = unit(rng);
std::bernoulli_distribution sample(p0);
std::shuffle(vs.begin(), vs.end(), rng);
for (auto v : vs)
......@@ -311,12 +313,6 @@ struct MCMC
continue;
}
ps[0] = log(p);
ps[1] = log1p(-p);
double Z = log_sum(ps[0], ps[1]);
double p0 = ps[0] - Z;
std::bernoulli_distribution sample(exp(p0));
if (sample(rng))
{
dS += _state.virtual_move(v, _state._b[v], rt[0],
......@@ -417,8 +413,7 @@ struct MCMC
}
template <class RNG, bool forward=true>
std::tuple<size_t, double, double> split(size_t r, size_t s,
RNG& rng)
std::tuple<size_t, double, double> split(size_t r, size_t s, RNG& rng)
{
auto vs = _groups[r];
......@@ -602,6 +597,7 @@ struct MCMC
_dS = _a = 0;
_vs.clear();
_nmoves = 0;
_state.clear_next_state();
move_t move = _move_sampler.sample(rng);
......@@ -668,7 +664,10 @@ struct MCMC
<< " " << -_dS + pb - pf << endl;
for (auto v : _vs)
{
_bnext[v] = _state._b[v];
_state.store_next_state(v);
}
pop_b();
_state._egroups_update = true;
......@@ -699,7 +698,10 @@ struct MCMC
_dS = merge(r, s);
for (auto v : _vs)
{
_bnext[v] = _state._b[v];
_state.store_next_state(v);
}
pop_b();
_state._egroups_update = true;
......@@ -748,6 +750,7 @@ struct MCMC
auto v = get<0>(vb);
_vs.push_back(v);
_bnext[v] = _state._b[v];
_state.store_next_state(v);
}
while (!_bstack.empty())
......
......@@ -127,6 +127,10 @@ extern void export_pseudo_ising_mcmc_h();
extern void export_vi_center_state();
extern void export_vi_center_mcmc();
extern void export_vi_multiflip_mcmc();
extern void export_partition_mode();
extern void export_mode_cluster_state();
extern void export_mode_cluster_mcmc();
extern void export_mode_cluster_multiflip_mcmc();
BOOST_PYTHON_MODULE(libgraph_tool_inference)
{
......@@ -190,6 +194,10 @@ BOOST_PYTHON_MODULE(libgraph_tool_inference)
export_vi_center_state();
export_vi_center_mcmc();
export_vi_multiflip_mcmc();
export_partition_mode();
export_mode_cluster_state();
export_mode_cluster_mcmc();
export_mode_cluster_multiflip_mcmc();
def("vector_map", vector_map<int32_t>);
def("vector_map64", vector_map<int64_t>);
......
......@@ -1262,6 +1262,11 @@ public:
void coupled_resize_vertex(size_t) { }
void update_block_edge(const GraphInterface::edge_t&,
const std::vector<double>&) { }
template <class V>
void push_state(V&) {}
void pop_state() {}
void store_next_state(size_t) {}
void clear_next_state() {}
//private:
typedef typename
......
......@@ -36,7 +36,7 @@ typedef multi_array_ref<int32_t,2> bs_t;
typedef multi_array_ref<int32_t,1> b_t;
#define BLOCK_STATE_params \
((g, &, all_graph_views, 1)) \
((g, &, always_directed_never_reversed, 1)) \
((_abg, &, boost::any&, 0)) \
((bs,, bs_t, 0)) \
((b,, b_t, 0))
......@@ -283,6 +283,12 @@ public:
return true;
}
template <class V>
void push_state(V&) {}
void pop_state() {}
void store_next_state(size_t) {}
void clear_next_state() {}
};
} // graph_tool namespace
......
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_tool.hh"
#include "random.hh"
#include "numpy_bind.hh"
#include <boost/python.hpp>
#include "graph_partition_mode.hh"
using namespace boost;
using namespace graph_tool;
void export_partition_mode()
{
using namespace boost::python;
class_<PartitionModeState>
("PartitionModeState", init<size_t>())
.def("add_partition",
+[](PartitionModeState& state, object ob, bool relabel)
{
auto b = get_array<int32_t, 1>(ob);
state.add_partition(b, relabel);
})
.def("remove_partition",
+[](PartitionModeState& state, size_t i)
{
state.remove_partition(i);
})
.def("replace_partitions", &PartitionModeState::replace_partitions)
.def("get_marginal",
+[](PartitionModeState& state,
GraphInterface& gi, boost::any obm)
{
run_action<>()
(gi, [&](auto& g, auto bm)
{
state.get_marginal(g, bm);
}, vertex_scalar_vector_properties())(obm);
})
.def("get_partition",
+[](PartitionModeState& state, size_t i)
{
auto b = state.get_partition(i);
return wrap_multi_array_not_owned(b);
})
.def("get_partitions",
+[](PartitionModeState& state)
{
python::dict obs;
auto& bs = state.get_partitions();
for (auto& kb : bs)
{
auto b = state.get_partition(kb.first);
obs[kb.first] = wrap_multi_array_not_owned(b);
}
return obs;
})
.def("relabel", &PartitionModeState::relabel)
.def("entropy", &PartitionModeState::entropy)
.def("posterior_entropy", &PartitionModeState::posterior_entropy);
}
This diff is collapsed.
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_tool.hh"
#include "random.hh"
#include <boost/python.hpp>
#include "graph_partition_mode_clustering.hh"
#include "../support/graph_state.hh"
using namespace boost;
using namespace graph_tool;
GEN_DISPATCH(block_state, ModeClusterState, BLOCK_STATE_params)
python::object make_mode_cluster_state(boost::python::object ostate)
{
python::object state;
block_state::make_dispatch(ostate,
[&](auto& s){state = python::object(s);});
return state;
}
void export_mode_cluster_state()
{
using namespace boost::python;
def("make_mode_cluster_state", &make_mode_cluster_state);
block_state::dispatch
([&](auto* s)
{
typedef typename std::remove_reference<decltype(*s)>::type state_t;
void (state_t::*move_vertex)(size_t, size_t) =
&state_t::move_vertex;
double (state_t::*virtual_move)(size_t, size_t, size_t) =
&state_t::virtual_move;
class_<state_t>
c(name_demangle(typeid(state_t).name()).c_str(),
no_init);
c.def("move_vertex", move_vertex)
.def("virtual_move", virtual_move)
.def("entropy", &state_t::entropy)
.def("posterior_entropy", &state_t::posterior_entropy)
.def("relabel_modes", &state_t::relabel_modes)
.def("replace_partitions", &state_t::replace_partitions)
.def("get_mode",
+[](state_t& state, size_t r)
{
return state.get_mode(r);
});
});
}
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_tool.hh"
#include "random.hh"
#include <boost/python.hpp>
#include "graph_partition_mode_clustering.hh"
#include "../blockmodel/graph_blockmodel_mcmc.hh"
#include "../loops/mcmc_loop.hh"
using namespace boost;
using namespace graph_tool;
GEN_DISPATCH(block_state, ModeClusterState, BLOCK_STATE_params)
template <class State>
GEN_DISPATCH(mcmc_block_state, MCMC<State>::template MCMCBlockState,
MCMC_BLOCK_STATE_params(State))
python::object mode_clustering_mcmc_sweep(python::object omcmc_state,
python::object oblock_state,
rng_t& rng)
{
python::object ret;
auto dispatch = [&](auto& block_state)
{
typedef typename std::remove_reference<decltype(block_state)>::type
state_t;
mcmc_block_state<state_t>::make_dispatch
(omcmc_state,
[&](auto& s)
{
auto ret_ = mcmc_sweep(s, rng);
ret = tuple_apply([&](auto&... args){ return python::make_tuple(args...); }, ret_);
});
};
block_state::dispatch(oblock_state, dispatch);
return ret;
}
void export_mode_cluster_mcmc()
{
using namespace boost::python;
def("mode_clustering_mcmc_sweep", &mode_clustering_mcmc_sweep);
}
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_tool.hh"
#include "random.hh"
#include <boost/python.hpp>
#include "graph_partition_mode_clustering.hh"
#include "../blockmodel/graph_blockmodel_multiflip_mcmc.hh"
#include "../loops/mcmc_loop.hh"
using namespace boost;
using namespace graph_tool;
GEN_DISPATCH(block_state, ModeClusterState, BLOCK_STATE_params)
template <class State>
GEN_DISPATCH(mcmc_block_state, MCMC<State>::template MCMCBlockState,
MCMC_BLOCK_STATE_params(State))
python::object mode_clustering_multiflip_mcmc_sweep(python::object omcmc_state,
python::object oblock_state,
rng_t& rng)
{
python::object ret;
auto dispatch = [&](auto& block_state)
{
typedef typename std::remove_reference<decltype(block_state)>::type
state_t;
mcmc_block_state<state_t>::make_dispatch
(omcmc_state,
[&](auto& s)
{
auto ret_ = mcmc_sweep(s, rng);
ret = tuple_apply([&](auto&... args){ return python::make_tuple(args...); }, ret_);
});
};
block_state::dispatch(oblock_state, dispatch);
return ret;
}
void export_mode_cluster_multiflip_mcmc()
{
using namespace boost::python;
def("mode_clustering_multiflip_mcmc_sweep",
&mode_clustering_multiflip_mcmc_sweep);
}
......@@ -76,6 +76,7 @@ graph_tool_inference_PYTHON = \
inference/minimize.py \
inference/modularity.py \
inference/partition_centroid.py \
inference/partition_modes.py \
inference/latent_multigraph.py \
inference/util.py
graph_tool_inferencedir = $(MOD_DIR)/inference
......
......@@ -157,6 +157,8 @@ __all__ = ["minimize_blockmodel_dl",
"LayeredBlockState",
"NestedBlockState",
"VICenterState",
"PartitionModeState",
"ModeClusterState",
"LatentMultigraphBlockState",
"UncertainBlockState",
"MeasuredBlockState",
......@@ -203,3 +205,4 @@ from . util import *
from . modularity import *
from . latent_multigraph import *
from . partition_centroid import *
from . partition_modes import *
......@@ -53,7 +53,7 @@ class VICenterState(object):
b = np.zeros(bs.shape[1], dtype="int32")
self.b = np.array(b, dtype="int32")
self.g = Graph(directed=False)
self.g = Graph()
self.g.add_vertex(bs.shape[1])
self.bg = self.g
self._abg = self.bg._get_any()
......
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# graph_tool -- a general graph manipulation python module
#
# Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division, absolute_import, print_function
import sys
if sys.version_info < (3,):
range = xrange
from .. import _degree, _prop, Graph, GraphView, _get_rng, Vector_size_t
from . blockmodel import DictState, get_entropy_args, _bm_test
from .. dl_import import dl_import
dl_import("from . import libgraph_tool_inference as libinference")
import numpy as np
import math
class PartitionModeState(object):
def __init__(self, bs, relabel=True, **kwargs):
self.bs = {}
if len(kwargs) > 0:
N = kwargs["N"]
self._base = kwargs["base"]
else:
N = len(bs[0])
self._base = libinference.PartitionModeState(N)
for b in bs:
self.add_partition(b, relabel)
def add_partition(self, b, relabel=True):
b = np.array(b, dtype="int32")
i = self._base.add_partition(b, relabel)
self.bs[i] = b
def remove_partition(self, i):
self._base.remove_partition(i)
if self.bs.has_key(i):
del self.bs[i]
def replace_partitions(self):
return self._base.replace_partitions()
def get_partition(self, i):
return self._base.get_partition(i)
def get_partitions(self):
return self._base.get_partitions()
def relabel(self):
return self._base.relabel()
def entropy(self):
return self._base.entropy()
def posterior_entropy(self):
return self._base.posterior_entropy()
def get_marginal(self, g):
bm = g.new_vp("vector<int>")
self._base.get_marginal(g._Graph__graph, bm._get_any())
return bm
class ModeClusterState(object):
def __init__(self, bs, b=None, B=1, relabel=True):
self.bs = np.asarray(bs, dtype="int32")
if b is None:
self.b = np.random.randint(0, B, self.bs.shape[0], dtype="int32")
else:
self.b = np.asarray(b, dtype="int32")
self.relabel_init = relabel
self.g = Graph()
self.g.add_vertex(self.b.shape[0])
self.bg = self.g
self._abg = self.bg._get_any()
self._state = libinference.make_mode_cluster_state(self)
self._entropy_args = dict(adjacency=True, deg_entropy=True, dl=True,
partition_dl=True, degree_dl=True,
degree_dl_kind="distributed", edges_dl=True,
dense=False, multigraph=True, exact=True,
recs=True, recs_dl=True, beta_dl=1.,
Bfield=True)
def __copy__(self):
return self.copy()
def __deepcopy__(self, memo):
b = copy.deepcopy(self.b, memo)
bs = copy.deepcopy(self.bs, memo)
return self.copy(bs=bs, b=b)
def copy(self, bs=None, b=None):