diff --git a/Makefile.am b/Makefile.am index f18f3549e261761f974ba0263147ee336d1ba2b3..1cdfc99f1eb8f4ad3726d71d7d032bded6456371 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,7 +18,7 @@ nobase_dist_graphtooldoc_DATA = \ doc/draw.rst \ doc/index.rst \ doc/spectral.rst \ - doc/community.rst \ + doc/inference.rst \ doc/flow.rst \ doc/Makefile \ doc/quickstart.rst \ @@ -30,7 +30,6 @@ nobase_dist_graphtooldoc_DATA = \ doc/topology.rst \ doc/conf.py \ doc/pyenv.py \ - doc/community.xml \ doc/search_example.xml \ doc/sphinxext/README.txt \ doc/sphinxext/LICENSE.txt \ diff --git a/configure.ac b/configure.ac index b044e9d302486bebcff3e1d2e2dd35266d683094..b6bda80eaf05eb155c5decc70d6518b634b0392c 100644 --- a/configure.ac +++ b/configure.ac @@ -457,11 +457,12 @@ src/Makefile src/graph/Makefile src/graph/centrality/Makefile src/graph/clustering/Makefile -src/graph/community/Makefile +src/graph/community_old/Makefile src/graph/correlations/Makefile src/graph/draw/Makefile src/graph/flow/Makefile src/graph/generation/Makefile +src/graph/inference/Makefile src/graph/layout/Makefile src/graph/search/Makefile src/graph/spectral/Makefile diff --git a/doc/community.rst b/doc/community.rst deleted file mode 100644 index ae1cbdab12e8760e05078b50855b005648911d99..0000000000000000000000000000000000000000 --- a/doc/community.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. automodule:: graph_tool.community - :no-members: - :no-undoc-members: - - .. autofunction:: minimize_blockmodel_dl - .. autoclass:: BlockState - .. autoclass:: OverlapBlockState - .. autoclass:: CovariateBlockState - .. autofunction:: mcmc_sweep - .. autofunction:: multilevel_minimize - .. autofunction:: collect_edge_marginals - .. autofunction:: collect_vertex_marginals - .. autofunction:: mf_entropy - .. autofunction:: bethe_entropy - .. autofunction:: model_entropy - .. autofunction:: get_max_B - .. autofunction:: get_akc - .. autofunction:: condensation_graph - .. autofunction:: minimize_nested_blockmodel_dl - .. autoclass:: NestedBlockState - .. autofunction:: init_nested_state - .. autofunction:: nested_mcmc_sweep - .. autofunction:: nested_tree_sweep - .. autofunction:: get_hierarchy_tree - .. autofunction:: get_block_edge_gradient - .. autofunction:: community_structure - .. autofunction:: modularity diff --git a/doc/community.xml b/doc/community.xml deleted file mode 100644 index d0a41332bcba8a603ced64232e34ab8f8174abc4..0000000000000000000000000000000000000000 --- a/doc/community.xml +++ /dev/null @@ -1,12562 +0,0 @@ - - - - - - - - - - - - - 0x1.ep+5, 0x1.ep+5 - - - 0x1.ep+5, 0x1.4p+6 - - - 0x1.ep+5, 0x1.9p+6 - - - 0x1.ep+5, 0x1.ep+6 - - - 0x1.ep+5, 0x1.18p+7 - - - 0x1.ep+5, 0x1.4p+7 - - - 0x1.ep+5, 0x1.68p+7 - - - 0x1.ep+5, 0x1.9p+7 - - - 0x1.4p+6, 0x1.ep+5 - - - 0x1.4p+6, 0x1.4p+6 - - - 0x1.4p+6, 0x1.9p+6 - - - 0x1.4p+6, 0x1.ep+6 - - - 0x1.4p+6, 0x1.18p+7 - - - 0x1.4p+6, 0x1.4p+7 - - - 0x1.4p+6, 0x1.68p+7 - - - 0x1.4p+6, 0x1.9p+7 - - - 0x1.9p+6, 0x1.ep+5 - - - 0x1.9p+6, 0x1.4p+6 - - - 0x1.9p+6, 0x1.9p+6 - - - 0x1.9p+6, 0x1.ep+6 - - - 0x1.9p+6, 0x1.18p+7 - - - 0x1.9p+6, 0x1.4p+7 - - - 0x1.9p+6, 0x1.68p+7 - - - 0x1.9p+6, 0x1.9p+7 - - - 0x1.ep+6, 0x1.ep+5 - - - 0x1.ep+6, 0x1.4p+6 - - - 0x1.ep+6, 0x1.9p+6 - - - 0x1.ep+6, 0x1.ep+6 - - - 0x1.ep+6, 0x1.18p+7 - - - 0x1.ep+6, 0x1.4p+7 - - - 0x1.ep+6, 0x1.68p+7 - - - 0x1.ep+6, 0x1.9p+7 - - - 0x1.18p+7, 0x1.ep+5 - - - 0x1.18p+7, 0x1.4p+6 - - - 0x1.18p+7, 0x1.9p+6 - - - 0x1.18p+7, 0x1.ep+6 - - - 0x1.18p+7, 0x1.18p+7 - - - 0x1.18p+7, 0x1.4p+7 - - - 0x1.18p+7, 0x1.68p+7 - - - 0x1.18p+7, 0x1.9p+7 - - - 0x1.4p+7, 0x1.ep+5 - - - 0x1.4p+7, 0x1.4p+6 - - - 0x1.4p+7, 0x1.9p+6 - - - 0x1.4p+7, 0x1.ep+6 - - - 0x1.4p+7, 0x1.18p+7 - - - 0x1.4p+7, 0x1.4p+7 - - - 0x1.4p+7, 0x1.68p+7 - - - 0x1.4p+7, 0x1.9p+7 - - - 0x1.68p+7, 0x1.ep+5 - - - 0x1.68p+7, 0x1.4p+6 - - - 0x1.68p+7, 0x1.9p+6 - - - 0x1.68p+7, 0x1.ep+6 - - - 0x1.68p+7, 0x1.18p+7 - - - 0x1.68p+7, 0x1.4p+7 - - - 0x1.68p+7, 0x1.68p+7 - - - 0x1.68p+7, 0x1.9p+7 - - - 0x1.9p+7, 0x1.ep+5 - - - 0x1.9p+7, 0x1.4p+6 - - - 0x1.9p+7, 0x1.9p+6 - - - 0x1.9p+7, 0x1.ep+6 - - - 0x1.9p+7, 0x1.18p+7 - - - 0x1.9p+7, 0x1.4p+7 - - - 0x1.9p+7, 0x1.68p+7 - - - 0x1.9p+7, 0x1.9p+7 - - - 0x1.4p+8, 0x1.54p+8 - - - 0x1.9p+6, 0x1.2cp+8 - - - 0x1.9p+7, 0x1.04p+8 - - - 0x1.68p+8, 0x1.68p+7 - - - 0x1.54p+8, 0x1.68p+8 - - - 0x1.b8p+7, 0x1.ep+6 - - - 0x1.ep+6, 0x1.2cp+8 - - - 0x1.b8p+7, 0x1.9p+7 - - - 0x1.04p+8, 0x1.ep+6 - - - 0x1.68p+7, 0x1.18p+8 - - - 0x1.ep+7, 0x1.54p+8 - - - 0x1.04p+8, 0x1.4p+7 - - - 0x1.4p+8, 0x1.18p+7 - - - 0x1.04p+8, 0x1.04p+8 - - - 0x1.04p+8, 0x1.54p+8 - - - 0x1.18p+7, 0x1.ep+7 - - - 0x1.ep+5, 0x1.18p+8 - - - 0x1.ep+6, 0x1.68p+8 - - - 0x1.04p+8, 0x1.4p+6 - - - 0x1.18p+8, 0x1.ep+7 - - - 0x1.68p+8, 0x1.9p+7 - - - 0x1.4p+8, 0x1.4p+6 - - - 0x1.68p+7, 0x1.54p+8 - - - 0x1.54p+8, 0x1.18p+8 - - - 0x1.2cp+8, 0x1.68p+8 - - - 0x1.2cp+8, 0x1.4p+7 - - - 0x1.54p+8, 0x1.9p+7 - - - 0x1.4p+6, 0x1.04p+8 - - - 0x1.9p+6, 0x1.4p+8 - - - 0x1.04p+8, 0x1.18p+8 - - - 0x1.b8p+7, 0x1.4p+7 - - - 0x1.ep+6, 0x1.ep+7 - - - 0x1.18p+8, 0x1.9p+6 - - - 0x1.2cp+8, 0x1.ep+7 - - - 0x1.04p+8, 0x1.b8p+7 - - - 0x1.ep+5, 0x1.2cp+8 - - - 0x1.18p+8, 0x1.18p+8 - - - 0x1.4p+7, 0x1.54p+8 - - - 0x1.ep+5, 0x1.04p+8 - - - 0x1.4p+8, 0x1.2cp+8 - - - 0x1.ep+6, 0x1.04p+8 - - - 0x1.68p+8, 0x1.4p+8 - - - 0x1.18p+8, 0x1.4p+8 - - - 0x1.04p+8, 0x1.2cp+8 - - - 0x1.18p+8, 0x1.2cp+8 - - - 0x1.ep+7, 0x1.ep+5 - - - 0x1.68p+7, 0x1.04p+8 - - - 0x1.ep+7, 0x1.9p+7 - - - 0x1.4p+8, 0x1.ep+7 - - - 0x1.68p+7, 0x1.2cp+8 - - - 0x1.54p+8, 0x1.9p+6 - - - 0x1.ep+7, 0x1.4p+8 - - - 0x1.b8p+7, 0x1.4p+6 - - - 0x1.4p+7, 0x1.2cp+8 - - - 0x1.2cp+8, 0x1.18p+8 - - - 0x1.68p+8, 0x1.54p+8 - - - 0x1.18p+8, 0x1.68p+8 - - - 0x1.9p+7, 0x1.ep+7 - - - 0x1.2cp+8, 0x1.54p+8 - - - 0x1.2cp+8, 0x1.4p+6 - - - 0x1.9p+6, 0x1.68p+8 - - - 0x1.54p+8, 0x1.4p+7 - - - 0x1.18p+8, 0x1.b8p+7 - - - 0x1.ep+5, 0x1.ep+7 - - - 0x1.18p+7, 0x1.04p+8 - - - 0x1.68p+8, 0x1.4p+6 - - - 0x1.68p+8, 0x1.4p+7 - - - 0x1.68p+8, 0x1.ep+5 - - - 0x1.68p+8, 0x1.b8p+7 - - - 0x1.b8p+7, 0x1.ep+5 - - - 0x1.54p+8, 0x1.2cp+8 - - - 0x1.ep+6, 0x1.b8p+7 - - - 0x1.54p+8, 0x1.ep+6 - - - 0x1.9p+6, 0x1.04p+8 - - - 0x1.b8p+7, 0x1.2cp+8 - - - 0x1.9p+7, 0x1.18p+8 - - - 0x1.4p+7, 0x1.68p+8 - - - 0x1.ep+7, 0x1.4p+7 - - - 0x1.4p+6, 0x1.68p+8 - - - 0x1.ep+7, 0x1.68p+7 - - - 0x1.18p+8, 0x1.4p+7 - - - 0x1.4p+7, 0x1.b8p+7 - - - 0x1.ep+7, 0x1.04p+8 - - - 0x1.04p+8, 0x1.9p+7 - - - 0x1.b8p+7, 0x1.18p+8 - - - 0x1.04p+8, 0x1.68p+7 - - - 0x1.18p+8, 0x1.9p+7 - - - 0x1.68p+8, 0x1.ep+6 - - - 0x1.2cp+8, 0x1.68p+7 - - - 0x1.b8p+7, 0x1.04p+8 - - - 0x1.04p+8, 0x1.ep+7 - - - 0x1.68p+7, 0x1.ep+7 - - - 0x1.9p+6, 0x1.ep+7 - - - 0x1.18p+8, 0x1.ep+6 - - - 0x1.ep+6, 0x1.4p+8 - - - 0x1.4p+7, 0x1.4p+8 - - - 0x1.b8p+7, 0x1.54p+8 - - - 0x1.9p+7, 0x1.4p+8 - - - 0x1.54p+8, 0x1.54p+8 - - - 0x1.18p+7, 0x1.b8p+7 - - - 0x1.18p+8, 0x1.ep+5 - - - 0x1.ep+6, 0x1.18p+8 - - - 0x1.ep+5, 0x1.68p+8 - - - 0x1.ep+5, 0x1.54p+8 - - - 0x1.b8p+7, 0x1.4p+8 - - - 0x1.18p+7, 0x1.4p+8 - - - 0x1.68p+8, 0x1.18p+7 - - - 0x1.b8p+7, 0x1.ep+7 - - - 0x1.9p+7, 0x1.68p+8 - - - 0x1.68p+8, 0x1.18p+8 - - - 0x1.ep+7, 0x1.2cp+8 - - - 0x1.4p+8, 0x1.68p+7 - - - 0x1.ep+7, 0x1.ep+7 - - - 0x1.9p+6, 0x1.54p+8 - - - 0x1.9p+7, 0x1.2cp+8 - - - 0x1.54p+8, 0x1.04p+8 - - - 0x1.4p+6, 0x1.b8p+7 - - - 0x1.4p+8, 0x1.18p+8 - - - 0x1.2cp+8, 0x1.4p+8 - - - 0x1.68p+8, 0x1.9p+6 - - - 0x1.2cp+8, 0x1.9p+6 - - - 0x1.18p+8, 0x1.18p+7 - - - 0x1.2cp+8, 0x1.ep+6 - - - 0x1.18p+7, 0x1.68p+8 - - - 0x1.4p+8, 0x1.b8p+7 - - - 0x1.ep+7, 0x1.18p+7 - - - 0x1.54p+8, 0x1.4p+8 - - - 0x1.54p+8, 0x1.ep+7 - - - 0x1.ep+5, 0x1.4p+8 - - - 0x1.4p+8, 0x1.4p+7 - - - 0x1.4p+7, 0x1.04p+8 - - - 0x1.68p+8, 0x1.04p+8 - - - 0x1.ep+7, 0x1.4p+6 - - - 0x1.54p+8, 0x1.68p+7 - - - 0x1.4p+8, 0x1.9p+6 - - - 0x1.9p+6, 0x1.b8p+7 - - - 0x1.4p+6, 0x1.2cp+8 - - - 0x1.4p+8, 0x1.ep+6 - - - 0x1.ep+7, 0x1.b8p+7 - - - 0x1.b8p+7, 0x1.18p+7 - - - 0x1.18p+8, 0x1.4p+6 - - - 0x1.b8p+7, 0x1.b8p+7 - - - 0x1.68p+8, 0x1.2cp+8 - - - 0x1.9p+7, 0x1.54p+8 - - - 0x1.54p+8, 0x1.4p+6 - - - 0x1.68p+8, 0x1.68p+8 - - - 0x1.2cp+8, 0x1.04p+8 - - - 0x1.4p+7, 0x1.18p+8 - - - 0x1.18p+7, 0x1.2cp+8 - - - 0x1.2cp+8, 0x1.b8p+7 - - - 0x1.68p+7, 0x1.b8p+7 - - - 0x1.ep+7, 0x1.9p+6 - - - 0x1.68p+8, 0x1.ep+7 - - - 0x1.68p+7, 0x1.68p+8 - - - 0x1.04p+8, 0x1.68p+8 - - - 0x1.04p+8, 0x1.18p+7 - - - 0x1.b8p+7, 0x1.68p+8 - - - 0x1.ep+7, 0x1.18p+8 - - - 0x1.54p+8, 0x1.ep+5 - - - 0x1.4p+6, 0x1.4p+8 - - - 0x1.2cp+8, 0x1.2cp+8 - - - 0x1.18p+7, 0x1.18p+8 - - - 0x1.54p+8, 0x1.b8p+7 - - - 0x1.ep+7, 0x1.ep+6 - - - 0x1.4p+8, 0x1.4p+8 - - - 0x1.2cp+8, 0x1.9p+7 - - - 0x1.18p+8, 0x1.68p+7 - - - 0x1.4p+6, 0x1.54p+8 - - - 0x1.ep+6, 0x1.54p+8 - - - 0x1.4p+8, 0x1.68p+8 - - - 0x1.04p+8, 0x1.4p+8 - - - 0x1.04p+8, 0x1.ep+5 - - - 0x1.4p+8, 0x1.9p+7 - - - 0x1.4p+6, 0x1.ep+7 - - - 0x1.18p+7, 0x1.54p+8 - - - 0x1.4p+7, 0x1.ep+7 - - - 0x1.b8p+7, 0x1.68p+7 - - - 0x1.9p+6, 0x1.18p+8 - - - 0x1.2cp+8, 0x1.ep+5 - - - 0x1.4p+6, 0x1.18p+8 - - - 0x1.18p+8, 0x1.54p+8 - - - 0x1.54p+8, 0x1.18p+7 - - - 0x1.ep+7, 0x1.68p+8 - - - 0x1.4p+8, 0x1.ep+5 - - - 0x1.4p+8, 0x1.04p+8 - - - 0x1.18p+8, 0x1.04p+8 - - - 0x1.04p+8, 0x1.9p+6 - - - 0x1.2cp+8, 0x1.18p+7 - - - 0x1.9p+7, 0x1.b8p+7 - - - 0x1.68p+7, 0x1.4p+8 - - - 0x1.ep+5, 0x1.b8p+7 - - - 0x1.b8p+7, 0x1.9p+6 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/graph_tool.rst b/doc/graph_tool.rst index 4f0e30f6da82b9f3da59de4c96cad6b03a960501..de7e5f4229415fea58ec39b3018b152ad9ca6dbb 100644 --- a/doc/graph_tool.rst +++ b/doc/graph_tool.rst @@ -207,11 +207,11 @@ Available subpackages centrality clustering collection - community correlations draw flow generation + inference search_module spectral stats diff --git a/doc/inference.rst b/doc/inference.rst new file mode 100644 index 0000000000000000000000000000000000000000..9dec439fe2748a6e448c761966589fdee34c2390 --- /dev/null +++ b/doc/inference.rst @@ -0,0 +1,1160 @@ +.. automodule:: graph_tool.inference + :no-undoc-members: + :show-inheritance: + +.. testcode:: inference_detailed + :hide: + + import test_inference + +.. testoutput:: inference_detailed + :hide: + :options: -ELLIPSIS, +NORMALIZE_WHITESPACE + + directed: True overlap: False layered: False deg-corr: False dl: False + mcmc (unweighted) + (653.7368247664258, 100) 72 + mcmc + (709.1284329780722, 103) 67 + (26.555814693961594, 102) 68 + (-108.54759577258069, 115) 73 + merge + (327.7692003897153, 50) + (240.08404807493113, 110) + (80.17105004298814, 115) + shrink + 5 + + directed: True overlap: False layered: False deg-corr: False dl: True + mcmc (unweighted) + (37.79906768205631, 105) 77 + mcmc + (55.97800577471912, 107) 72 + (-83.84539912129875, 99) 61 + (60.363819212130494, 114) 73 + merge + (-403.6228272212937, 50) + (-2.5333139051913243, 104) + (19.27758710817686, 115) + shrink + 5 + + directed: True overlap: False layered: False deg-corr: True dl: False + mcmc (unweighted) + (448.9786966541199, 102) 79 + mcmc + (523.3965114137179, 106) 74 + (-25.920853037839876, 103) 74 + (25.289255024521793, 113) 74 + merge + (277.5286735275392, 50) + (358.72935230058386, 101) + (-115.7860369979704, 115) + shrink + 5 + + directed: True overlap: False layered: False deg-corr: True dl: True + mcmc (unweighted) + (-370.02300429105844, 97) 68 + mcmc + (-236.5251583848658, 104) 77 + (-90.66222214522463, 104) 71 + (22.38348339446021, 112) 72 + merge + (-747.2417795796297, 50) + (-137.5051248827061, 100) + (-22.19343083384164, 113) + shrink + 5 + + directed: True overlap: False layered: covariates deg-corr: False dl: False + mcmc (unweighted) + (676.3594764878382, 114) 72 + mcmc + (659.612425515866, 109) 73 + (-7.114556958148668, 115) 75 + (12.450844591087305, 115) 73 + merge + (546.8411931837169, 50) + (446.7766774894063, 111) + (-233.62505581815967, 115) + shrink + 5 + + directed: True overlap: False layered: covariates deg-corr: False dl: True + mcmc (unweighted) + (11.134477993729128, 113) 71 + mcmc + (39.69116889632038, 110) 70 + (23.949240858672248, 114) 72 + (-20.311156765735674, 115) 75 + merge + (-185.8315002121304, 50) + (-8.659869616217907, 113) + (7.1698821344710115, 114) + shrink + 5 + + directed: True overlap: False layered: covariates deg-corr: True dl: False + mcmc (unweighted) + (625.1252715930277, 113) 70 + mcmc + (687.1284762185751, 113) 64 + (88.17735417358503, 113) 59 + (-267.6672513613203, 114) 73 + merge + (461.52365374179743, 50) + (554.2643481837697, 109) + (-311.10773578568643, 114) + shrink + 5 + + directed: True overlap: False layered: covariates deg-corr: True dl: True + mcmc (unweighted) + (-265.4943379846294, 111) 76 + mcmc + (-260.48845827199153, 115) 75 + (-41.58828679977694, 114) 72 + (20.07067071778924, 114) 74 + merge + (-584.6644045599711, 50) + (-124.7745711915025, 112) + (70.39505964132627, 115) + shrink + 5 + + directed: True overlap: False layered: True deg-corr: False dl: False + mcmc (unweighted) + (649.827058787757, 109) 63 + mcmc + (460.97415340386925, 115) 74 + (20.20550023851644, 113) 71 + (-11.482268963688943, 114) 75 + merge + (382.0710697233173, 50) + (659.1089055216413, 112) + (-384.8843394117986, 115) + shrink + 5 + + directed: True overlap: False layered: True deg-corr: False dl: True + mcmc (unweighted) + (-146.93776337069448, 114) 67 + mcmc + (-118.54952723454903, 112) 67 + (-22.25121342666771, 111) 70 + (0.09101846869118524, 113) 72 + merge + (-345.98550748381786, 50) + (-19.856093212550192, 111) + (24.406541323617336, 115) + shrink + 5 + + directed: True overlap: False layered: True deg-corr: True dl: False + mcmc (unweighted) + (299.47637000759045, 113) 71 + mcmc + (339.7882309572504, 113) 73 + (-22.951725336949753, 113) 70 + (-71.18214115859378, 115) 76 + merge + (112.65674339105865, 50) + (270.7282008346159, 115) + (-48.227031269529164, 115) + shrink + 5 + + directed: True overlap: False layered: True deg-corr: True dl: True + mcmc (unweighted) + (-489.54010405218867, 115) 72 + mcmc + (-477.0610489947019, 115) 73 + (-44.315852689841975, 111) 70 + (-6.031490138403797, 113) 70 + merge + (-694.3072536306113, 50) + (-260.61832985101154, 112) + (32.31080727878356, 115) + shrink + 5 + + directed: True overlap: True layered: False deg-corr: False dl: False + mcmc (unweighted) + (684.2122420163477, 1222) 784 + (-14.493178594335856, 1340) 787 + merge + (0.0, 50) + shrink + 5 + + directed: True overlap: True layered: False deg-corr: False dl: True + mcmc (unweighted) + (-451.273408828854, 1223) 777 + (-27.657518258027803, 1340) 769 + merge + (-308.96675893350766, 50) + shrink + 5 + + directed: True overlap: True layered: False deg-corr: True dl: False + mcmc (unweighted) + (410.11174108492406, 1226) 756 + (-11.783502069519063, 1337) 760 + merge + (1.3862943611198906, 50) + shrink + 5 + + directed: True overlap: True layered: False deg-corr: True dl: True + mcmc (unweighted) + (-160.75919034773452, 1225) 785 + (-37.94994320552587, 1340) 763 + merge + (-326.8859937813693, 50) + shrink + 5 + + directed: True overlap: True layered: covariates deg-corr: False dl: False + mcmc (unweighted) + (6.4407729047092115, 1197) 115 + (15.090003687444668, 1306) 115 + merge + (-53.01104703350246, 50) + shrink + 5 + + directed: True overlap: True layered: covariates deg-corr: False dl: True + mcmc (unweighted) + (0.7469127360648091, 1189) 115 + (-4.454066106756823, 1307) 115 + merge + (-1060.3966480302265, 50) + shrink + 5 + + directed: True overlap: True layered: covariates deg-corr: True dl: False + mcmc (unweighted) + (19.34655635911719, 1201) 115 + (13.021627155555198, 1302) 115 + merge + (139.75704771951064, 50) + shrink + 5 + + directed: True overlap: True layered: covariates deg-corr: True dl: True + mcmc (unweighted) + (-42.609890899551715, 1196) 115 + (-20.85691258865186, 1310) 115 + merge + (-1234.0387385376016, 50) + shrink + 5 + + directed: True overlap: True layered: True deg-corr: False dl: False + mcmc (unweighted) + (-32.53034167952721, 1201) 115 + (56.45069492553456, 1294) 115 + merge + (-114.8981755030546, 50) + shrink + 5 + + directed: True overlap: True layered: True deg-corr: False dl: True + mcmc (unweighted) + (-1.035017095430904, 1191) 115 + (-4.378896497938934, 1305) 115 + merge + (-993.1502579858155, 50) + shrink + 5 + + directed: True overlap: True layered: True deg-corr: True dl: False + mcmc (unweighted) + (6.508901229588357, 1189) 115 + (-13.11412599497536, 1301) 115 + merge + (-77.33333924925819, 50) + shrink + 5 + + directed: True overlap: True layered: True deg-corr: True dl: True + mcmc (unweighted) + (62.56398098817939, 1195) 115 + (-3.5377428709883647, 1304) 115 + merge + (-1052.7716211643888, 50) + shrink + 5 + + directed: False overlap: False layered: False deg-corr: False dl: False + mcmc (unweighted) + (693.169084122821, 106) 69 + mcmc + (575.1255994230743, 106) 74 + (-18.268855666875268, 102) 74 + (84.23562085572648, 114) 68 + merge + (254.062569976686, 50) + (294.07064838987327, 108) + (-35.54039844306018, 115) + shrink + 5 + + directed: False overlap: False layered: False deg-corr: False dl: True + mcmc (unweighted) + (6.089470037990972, 99) 74 + mcmc + (31.337138743014393, 96) 71 + (-32.348734568160104, 94) 68 + (11.13293108914912, 114) 73 + merge + (-452.35567315108005, 50) + (-14.94414868569813, 108) + (-26.47985161842624, 115) + shrink + 5 + + directed: False overlap: False layered: False deg-corr: True dl: False + mcmc (unweighted) + (474.71492598670625, 100) 79 + mcmc + (575.0190616712077, 103) 74 + (35.03115802167634, 99) 72 + (-44.92187114668639, 114) 73 + merge + (249.97743129324903, 50) + (425.23174309430004, 110) + (-159.32783404320577, 114) + shrink + 5 + + directed: False overlap: False layered: False deg-corr: True dl: True + mcmc (unweighted) + (-193.2001940253293, 99) 74 + mcmc + (-174.90080436460175, 102) 76 + (-39.52484390424932, 104) 72 + (48.052018152160485, 113) 72 + merge + (-717.2044026904009, 50) + (-71.77424635033832, 105) + (-25.114234051660894, 114) + shrink + 5 + + directed: False overlap: False layered: covariates deg-corr: False dl: False + mcmc (unweighted) + (772.5453980570721, 114) 67 + mcmc + (720.1691858052512, 112) 69 + (68.31407337248119, 111) 65 + (-146.74986338638587, 114) 72 + merge + (473.38852115957025, 50) + (376.67392866586084, 114) + (1.1626252512182127, 114) + shrink + 5 + + directed: False overlap: False layered: covariates deg-corr: False dl: True + mcmc (unweighted) + (39.60067070934042, 115) 72 + mcmc + (-9.194100392728705, 115) 77 + (23.758390517950442, 110) 75 + (7.399872077643064, 115) 78 + merge + (-254.49516803072956, 50) + (-9.215586113589342, 112) + (23.632624271547694, 112) + shrink + 5 + + directed: False overlap: False layered: covariates deg-corr: True dl: False + mcmc (unweighted) + (679.6815899057593, 111) 70 + mcmc + (737.5066421528536, 114) 68 + (-41.630424206875865, 111) 68 + (-71.64216522170558, 114) 73 + merge + (483.74983009336034, 50) + (465.972158053492, 114) + (-168.20407971455091, 114) + shrink + 5 + + directed: False overlap: False layered: covariates deg-corr: True dl: True + mcmc (unweighted) + (-246.9705243300452, 113) 67 + mcmc + (-236.51398935319358, 113) 68 + (83.02986995537107, 113) 78 + (-33.29065578699246, 114) 73 + merge + (-504.63687228651776, 50) + (-171.50178946195857, 108) + (49.50677409599346, 113) + shrink + 5 + + directed: False overlap: False layered: True deg-corr: False dl: False + mcmc (unweighted) + (540.8663037198617, 113) 68 + mcmc + (507.7047847062117, 115) 69 + (-56.73592493601191, 113) 74 + (14.478960856237514, 114) 75 + merge + (385.119877617005, 50) + (609.4426771976545, 112) + (-95.63740447873565, 114) + shrink + 5 + + directed: False overlap: False layered: True deg-corr: False dl: True + mcmc (unweighted) + (-135.48400339347597, 114) 69 + mcmc + (-177.55059658149057, 112) 67 + (77.30739145915234, 114) 77 + (-37.788312169996864, 115) 78 + merge + (-378.09608309928564, 50) + (-9.759576031614198, 114) + (-0.9536321167456725, 115) + shrink + 5 + + directed: False overlap: False layered: True deg-corr: True dl: False + mcmc (unweighted) + (416.8858712424856, 114) 72 + mcmc + (543.6668155389088, 115) 65 + (-116.88680318312439, 114) 78 + (64.10454898532073, 114) 70 + merge + (344.14747553149675, 50) + (395.5036614885022, 112) + (-144.23929897210388, 115) + shrink + 5 + + directed: False overlap: False layered: True deg-corr: True dl: True + mcmc (unweighted) + (-515.0632865530644, 114) 71 + mcmc + (-429.52721402339705, 112) 75 + (-97.8538715008062, 110) 69 + (61.234048124156786, 115) 73 + merge + (-750.3186792142322, 50) + (-289.89054259425507, 109) + (117.72715613340667, 115) + shrink + 5 + + directed: False overlap: True layered: False deg-corr: False dl: False + mcmc (unweighted) + (668.3427656597461, 1225) 785 + (25.29309657374884, 1337) 773 + merge + (0.0, 50) + shrink + 5 + + directed: False overlap: True layered: False deg-corr: False dl: True + mcmc (unweighted) + (-454.0663525173459, 1223) 770 + (-3.768593665481344, 1341) 772 + merge + (-302.1438895586243, 50) + shrink + 5 + + directed: False overlap: True layered: False deg-corr: True dl: False + mcmc (unweighted) + (715.484682404301, 1225) 765 + (-34.61523962571205, 1341) 784 + merge + (34.77514206365362, 50) + shrink + 5 + + directed: False overlap: True layered: False deg-corr: True dl: True + mcmc (unweighted) + (51.108409710889575, 1226) 775 + (-5.747478822563901, 1338) 771 + merge + (-288.7443154032152, 50) + shrink + 5 + + directed: False overlap: True layered: covariates deg-corr: False dl: False + mcmc (unweighted) + (30.51641860515133, 1198) 115 + (-10.213872530535728, 1312) 115 + merge + (-279.91158404351484, 50) + shrink + 5 + + directed: False overlap: True layered: covariates deg-corr: False dl: True + mcmc (unweighted) + (-47.83809678456382, 1197) 115 + (30.994946483584556, 1306) 115 + merge + (-1451.2844212406185, 50) + shrink + 5 + + directed: False overlap: True layered: covariates deg-corr: True dl: False + mcmc (unweighted) + (17.114695467713297, 1193) 115 + (6.74992551722728, 1308) 115 + merge + (-141.66901107706147, 50) + shrink + 5 + + directed: False overlap: True layered: covariates deg-corr: True dl: True + mcmc (unweighted) + (18.28144731527671, 1186) 115 + (-8.899892485955935, 1320) 115 + merge + (-2351.4411047024337, 50) + shrink + 5 + + directed: False overlap: True layered: True deg-corr: False dl: False + mcmc (unweighted) + (-30.414601069667068, 1192) 115 + (23.38533942611877, 1317) 115 + merge + (-160.6578412585288, 50) + shrink + 5 + + directed: False overlap: True layered: True deg-corr: False dl: True + mcmc (unweighted) + (-15.9501258457887, 1185) 115 + (43.8463674972257, 1312) 115 + merge + (-1100.5828201139577, 50) + shrink + 5 + + directed: False overlap: True layered: True deg-corr: True dl: False + mcmc (unweighted) + (38.98178711619759, 1197) 115 + (18.742075941875243, 1319) 115 + merge + (-116.24155086445886, 50) + shrink + 5 + + directed: False overlap: True layered: True deg-corr: True dl: True + mcmc (unweighted) + (-0.7081085763732451, 1190) 115 + (-31.565140167020655, 1307) 115 + merge + (-1350.8161986552966, 50) + shrink + 5 + + directed: True overlap: False layered: False deg-corr: False + Current bracket: (1, 60, 115) (2495.8290836444471, 3204.2321625907725, 3708.414497288833) + Current bracket: (1, 26, 60) (2495.8290836444471, 2617.8774215566509, 3204.2321625907725) + Current bracket: (1, 13, 26) (2495.8290836444471, 2363.1981701253062, 2617.8774215566509) + Current bracket: (1, 13, 26) (2495.8290836444471, 2363.1981701253062, 2617.8774215566509) + Bisect at B = 18 with S = 2453.367089312351 + Current bracket: (1, 13, 18) (2495.8290836444471, 2363.1981701253062, 2453.3670893123513) + Bisect at B = 8 with S = 2300.994045668983 + Current bracket: (1, 8, 13) (2495.8290836444471, 2300.9940456689828, 2363.1981701253062) + Bisect at B = 5 with S = 2285.052011050861 + Current bracket: (1, 5, 8) (2495.8290836444471, 2285.052011050861, 2300.9940456689828) + Bisect at B = 3 with S = 2317.091178612431 + Current bracket: (3, 5, 8) (2317.0911786124307, 2285.052011050861, 2300.9940456689828) + Bisect at B = 6 with S = 2284.866879995794 + Current bracket: (5, 6, 8) (2285.052011050861, 2284.8668799957941, 2300.9940456689828) + Bisect at B = 7 with S = 2294.655787376431 + Current bracket: (5, 6, 7) (2285.052011050861, 2284.8668799957941, 2294.6557873764309) + Bisect at B = 5 with S = 2285.052011050861 + Best result: B = 6, S = 2284.866879995794 + 6 2284.86688 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 5) , dS: -210.777072594 2 + level 1 : replaced (5, 1) -> (5, 2) , dS: -2.66350792975 2 + level 2 : rejected replacement (2, 1) -> (2, 1) , dS: 0.0 + level 2 : rejected insert 2 , dS: 1.79175946923 + level 1 : skipping 2 + level 0 : replaced (115, 5) -> (115, 8) , dS: -5.91339284569 3 + level 1 : rejected replacement (8, 2) -> (8, 2) , dS: 0.0 + level 1 : rejected insert 8 , dS: 19.3741100228 + level 0 : skipping 8 + l: 0, N: 115, B: 8 + l: 1, N: 8, B: 2 + l: 2, N: 2, B: 1 + 2276.47511028 + + directed: True overlap: False layered: False deg-corr: True + Current bracket: (1, 60, 115) (2318.8696624551153, 3051.6877329249223, 3337.5708321100378) + Current bracket: (1, 26, 60) (2318.8696624551153, 2579.1905163091492, 3051.6877329249223) + Current bracket: (1, 13, 26) (2318.8696624551153, 2292.6909131845441, 2579.1905163091492) + Current bracket: (1, 13, 26) (2318.8696624551153, 2292.6909131845441, 2579.1905163091492) + Bisect at B = 18 with S = 2424.807155254194 + Current bracket: (1, 13, 18) (2318.8696624551153, 2292.6909131845441, 2424.8071552541942) + Bisect at B = 8 with S = 2196.047650916988 + Current bracket: (1, 8, 13) (2318.8696624551153, 2196.0476509169875, 2292.6909131845441) + Bisect at B = 5 with S = 2165.076125813918 + Current bracket: (1, 5, 8) (2318.8696624551153, 2165.0761258139182, 2196.0476509169875) + Bisect at B = 3 with S = 2178.869483800998 + Current bracket: (3, 5, 8) (2178.8694838009978, 2165.0761258139182, 2196.0476509169875) + Bisect at B = 6 with S = 2170.665883964396 + Current bracket: (3, 5, 6) (2178.8694838009978, 2165.0761258139182, 2170.665883964396) + Bisect at B = 4 with S = 2164.554037334525 + Best result: B = 4, S = 2164.554037334525 + 4 2164.55403733 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 4) , dS: -158.455012563 2 + level 1 : replaced (4, 1) -> (4, 2) , dS: -0.230629487184 2 + level 2 : rejected replacement (2, 1) -> (2, 1) , dS: 0.0 + level 2 : rejected insert 2 , dS: 1.79175946923 + level 1 : skipping 2 + level 0 : rejected replacement (115, 4) -> (115, 5) , dS: 1.88971212096 + l: 0, N: 115, B: 4 + l: 1, N: 4, B: 2 + l: 2, N: 2, B: 1 + 2160.1840204 + + directed: True overlap: False layered: covariates deg-corr: False + Current bracket: (1, 60, 115) (3876.323037275662, 4770.369390710639, 5076.3584970855154) + Current bracket: (1, 26, 60) (3876.323037275662, 4390.4129518186537, 4770.369390710639) + Current bracket: (1, 13, 26) (3876.323037275662, 4090.8678102257359, 4390.4129518186537) + Current bracket: (1, 8, 13) (3876.323037275662, 3928.4123438440784, 4090.8678102257359) + Current bracket: (1, 5, 8) (3876.323037275662, 3815.3301172970105, 3928.4123438440784) + Current bracket: (1, 5, 8) (3876.323037275662, 3815.3301172970105, 3928.4123438440784) + Bisect at B = 3 with S = 3770.207464051877 + Current bracket: (1, 3, 5) (3876.323037275662, 3770.2074640518772, 3815.3301172970105) + Bisect at B = 2 with S = 3787.213321435399 + Current bracket: (2, 3, 5) (3787.2133214353989, 3770.2074640518772, 3815.3301172970105) + Bisect at B = 4 with S = 3781.892332292212 + Current bracket: (2, 3, 4) (3787.2133214353989, 3770.2074640518772, 3781.8923322922124) + Bisect at B = 2 with S = 3787.213321435399 + Best result: B = 3, S = 3770.207464051877 + 3 3770.20746405 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 6) , dS: -201.16791632 2 + level 1 : rejected replacement (6, 1) -> (6, 1) , dS: 0.0 + level 1 : rejected insert 6 , dS: 12.7148161031 + level 0 : skipping 6 + l: 0, N: 115, B: 6 + l: 1, N: 6, B: 1 + 3675.15512096 + + directed: True overlap: False layered: covariates deg-corr: True + Current bracket: (1, 60, 115) (3699.3636160863302, 4524.7650859192672, 4705.5148319067212) + Current bracket: (1, 26, 60) (3699.3636160863302, 4280.5863383999085, 4524.7650859192672) + Current bracket: (1, 13, 26) (3699.3636160863302, 4013.0275490563249, 4280.5863383999085) + Current bracket: (1, 8, 13) (3699.3636160863302, 3825.7420416344448, 4013.0275490563249) + Current bracket: (1, 5, 8) (3699.3636160863302, 3716.3154093581552, 3825.7420416344448) + Current bracket: (1, 3, 5) (3699.3636160863302, 3636.3009795251073, 3716.3154093581552) + Current bracket: (1, 3, 5) (3699.3636160863302, 3636.3009795251073, 3716.3154093581552) + Bisect at B = 2 with S = 3638.452352760167 + Current bracket: (2, 3, 5) (3638.4523527601673, 3636.3009795251073, 3716.3154093581552) + Bisect at B = 4 with S = 3673.832571602863 + Current bracket: (2, 3, 4) (3638.4523527601673, 3636.3009795251073, 3673.8325716028635) + Bisect at B = 2 with S = 3638.452352760167 + Best result: B = 3, S = 3636.300979525107 + 3 3636.30097953 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 5) , dS: -150.163186164 2 + level 1 : rejected replacement (5, 1) -> (5, 1) , dS: 0.0 + level 1 : rejected insert 5 , dS: 9.62377364973 + level 0 : skipping 5 + l: 0, N: 115, B: 5 + l: 1, N: 5, B: 1 + 3549.20042992 + + directed: True overlap: False layered: True deg-corr: False + Current bracket: (1, 60, 115) (4149.3841336310379, 5284.3845874087538, 5806.3692226513531) + Current bracket: (1, 26, 60) (4149.3841336310379, 4970.2156440906747, 5284.3845874087538) + Current bracket: (1, 13, 26) (4149.3841336310379, 4665.913218701382, 4970.2156440906747) + Current bracket: (1, 8, 13) (4149.3841336310379, 4448.1593590070361, 4665.913218701382) + Current bracket: (1, 5, 8) (4149.3841336310379, 4259.415534978637, 4448.1593590070361) + Current bracket: (1, 3, 5) (4149.3841336310379, 4156.7168613952836, 4259.415534978637) + Current bracket: (1, 2, 3) (4149.3841336310379, 4108.7083204548444, 4156.7168613952836) + Bisect at B = 1 with S = 4149.384133631038 + Best result: B = 2, S = 4108.708320454844 + 2 4108.70832045 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : rejected replacement (115, 1) -> (115, 1) , dS: 0.0 + l: 0, N: 115, B: 1 + l: 1, N: 1, B: 1 + 4151.68671872 + + directed: True overlap: False layered: True deg-corr: True + Current bracket: (1, 60, 115) (4079.1051067893313, 4596.3061341987523, 4808.8764024295706) + Current bracket: (1, 26, 60) (4079.1051067893313, 4496.5820872632921, 4596.3061341987523) + Current bracket: (1, 13, 26) (4079.1051067893313, 4370.6873281497483, 4496.5820872632921) + Current bracket: (1, 8, 13) (4079.1051067893313, 4273.0526544098466, 4370.6873281497483) + Current bracket: (1, 5, 8) (4079.1051067893313, 4185.3762872671505, 4273.0526544098466) + Current bracket: (1, 3, 5) (4079.1051067893313, 4116.5895360878512, 4185.3762872671505) + Current bracket: (1, 2, 3) (4079.1051067893313, 4113.7990467107265, 4116.5895360878512) + Bisect at B = 1 with S = 4079.105106789331 + Best result: B = 1, S = 4079.105106789331 + 1 4079.10510679 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : rejected replacement (115, 1) -> (115, 1) , dS: 0.0 + l: 0, N: 115, B: 1 + l: 1, N: 1, B: 1 + 4081.40769188 + + directed: True overlap: True layered: False deg-corr: False + Current bracket: (1, 60, 115) (2495.8290836444471, 3243.3591987389864, 3708.4144972888325) + Current bracket: (1, 26, 60) (2495.8290836444471, 2673.966224376918, 3243.3591987389864) + Current bracket: (1, 13, 26) (2495.8290836444471, 2441.2363917608513, 2673.966224376918) + Current bracket: (1, 13, 26) (2495.8290836444471, 2441.2363917608513, 2673.966224376918) + Bisect at B = 18 with S = 2510.597188861897 + Current bracket: (1, 13, 18) (2495.8290836444471, 2441.2363917608513, 2510.5971888618969) + Bisect at B = 8 with S = 2337.458764563424 + Current bracket: (1, 8, 13) (2495.8290836444471, 2337.4587645634238, 2441.2363917608513) + Bisect at B = 5 with S = 2301.092707087893 + Current bracket: (1, 5, 8) (2495.8290836444471, 2301.0927070878934, 2337.4587645634238) + Bisect at B = 3 with S = 2357.912801442382 + Current bracket: (3, 5, 8) (2357.9128014423818, 2301.0927070878934, 2337.4587645634238) + Bisect at B = 6 with S = 2309.754290101913 + Current bracket: (3, 5, 6) (2357.9128014423818, 2301.0927070878934, 2309.7542901019133) + Bisect at B = 4 with S = 2298.969652099869 + Best result: B = 4, S = 2298.969652099869 + 4 2298.9696521 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 4) , dS: -167.727455603 2 + level 1 : rejected replacement (4, 1) -> (4, 1) , dS: 0.0 + level 1 : rejected insert 4 , dS: 6.73340189184 + level 0 : skipping 4 + l: 0, N: 115, B: 4 + l: 1, N: 4, B: 1 + 2328.10162804 + + directed: True overlap: True layered: False deg-corr: True + Current bracket: (1, 60, 115) (2318.8696624551112, 3614.0932889394471, 3337.5708321100401) + Current bracket: (1, 26, 60) (2318.8696624551112, 3110.1951905894075, 3614.0932889394471) + Current bracket: (1, 13, 26) (2318.8696624551112, 2710.2613205743455, 3110.1951905894075) + Current bracket: (1, 8, 13) (2318.8696624551112, 2528.3426363372578, 2710.2613205743455) + Current bracket: (1, 5, 8) (2318.8696624551112, 2440.4089835968939, 2528.3426363372578) + Current bracket: (1, 3, 5) (2318.8696624551112, 2353.166621031437, 2440.4089835968939) + Current bracket: (1, 2, 3) (2318.8696624551112, 2264.6889302483723, 2353.166621031437) + Bisect at B = 1 with S = 2318.869662455111 + Best result: B = 2, S = 2264.688930248372 + 2 2264.68893025 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 2) , dS: -55.0106699939 2 + level 1 : rejected replacement (2, 1) -> (2, 1) , dS: 0.0 + level 1 : rejected insert 2 , dS: 1.79175946923 + level 0 : skipping 2 + l: 0, N: 115, B: 2 + l: 1, N: 2, B: 1 + 2263.85899246 + + directed: True overlap: True layered: covariates deg-corr: False + Current bracket: (1, 60, 115) (3876.323037275662, 4795.4571396071951, 5076.3584970855154) + Current bracket: (1, 26, 60) (3876.323037275662, 4480.189057603242, 4795.4571396071951) + Current bracket: (1, 13, 26) (3876.323037275662, 4232.1382878497698, 4480.189057603242) + Current bracket: (1, 8, 13) (3876.323037275662, 4069.039478169384, 4232.1382878497698) + Current bracket: (1, 5, 8) (3876.323037275662, 3951.8135764459576, 4069.039478169384) + Current bracket: (1, 3, 5) (3876.323037275662, 3878.0269892829961, 3951.8135764459576) + Current bracket: (1, 2, 3) (3876.323037275662, 3867.7472612920978, 3878.0269892829961) + Bisect at B = 1 with S = 3876.323037275662 + Best result: B = 2, S = 3867.747261292098 + 2 3867.74726129 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 5) , dS: -178.717049511 2 + level 1 : rejected replacement (5, 1) -> (5, 1) , dS: 0.0 + level 1 : rejected insert 5 , dS: 9.62377364973 + level 0 : skipping 5 + l: 0, N: 115, B: 5 + l: 1, N: 5, B: 1 + 3697.60598777 + + directed: True overlap: True layered: covariates deg-corr: True + Current bracket: (1, 60, 115) (3699.3636160863266, 5167.062731660124, 4705.514831906723) + Current bracket: (1, 26, 60) (3699.3636160863266, 4845.8008598492379, 5167.062731660124) + Current bracket: (1, 13, 26) (3699.3636160863266, 4614.9766720583375, 4845.8008598492379) + Current bracket: (1, 8, 13) (3699.3636160863266, 4368.643894604631, 4614.9766720583375) + Current bracket: (1, 5, 8) (3699.3636160863266, 4154.7195000910806, 4368.643894604631) + Current bracket: (1, 3, 5) (3699.3636160863266, 3929.3119299306791, 4154.7195000910806) + Current bracket: (1, 2, 3) (3699.3636160863266, 3780.6025531339842, 3929.3119299306791) + Bisect at B = 1 with S = 3699.363616086327 + Best result: B = 1, S = 3699.363616086327 + 1 3699.36361609 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 5) , dS: -135.304469399 2 + level 1 : rejected replacement (5, 1) -> (5, 1) , dS: 0.0 + level 1 : rejected insert 5 , dS: 9.62377364973 + level 0 : skipping 5 + l: 0, N: 115, B: 5 + l: 1, N: 5, B: 1 + 3564.05914669 + + directed: True overlap: True layered: True deg-corr: False + Current bracket: (1, 60, 115) (4149.3841336310379, 5634.8056324677736, 5806.3692226513531) + Current bracket: (1, 26, 60) (4149.3841336310379, 5253.8064287341322, 5634.8056324677736) + Current bracket: (1, 13, 26) (4149.3841336310379, 4830.7710230477469, 5253.8064287341322) + Current bracket: (1, 8, 13) (4149.3841336310379, 4295.682486401598, 4830.7710230477469) + Current bracket: (1, 5, 8) (4149.3841336310379, 4052.3861265593323, 4295.682486401598) + Current bracket: (1, 5, 8) (4149.3841336310379, 4052.3861265593323, 4295.682486401598) + Bisect at B = 3 with S = 3833.118315845553 + Current bracket: (1, 3, 5) (4149.3841336310379, 3833.1183158455533, 4052.3861265593323) + Bisect at B = 2 with S = 4039.690876848615 + Current bracket: (2, 3, 5) (4039.6908768486151, 3833.1183158455533, 4052.3861265593323) + Bisect at B = 4 with S = 4036.591921918258 + Current bracket: (2, 3, 4) (4039.6908768486151, 3833.1183158455533, 4036.5919219182579) + Bisect at B = 2 with S = 4039.690876848615 + Best result: B = 3, S = 3833.118315845553 + 3 3833.11831585 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : rejected replacement (115, 1) -> (115, 1) , dS: 0.0 + l: 0, N: 115, B: 1 + l: 1, N: 1, B: 1 + 4151.68671872 + + directed: True overlap: True layered: True deg-corr: True + Current bracket: (1, 60, 115) (4079.1051067893313, 5197.2037808565174, 4808.8764024295706) + Current bracket: (1, 26, 60) (4079.1051067893313, 4947.0264093741907, 5197.2037808565174) + Current bracket: (1, 13, 26) (4079.1051067893313, 4812.7634310564736, 4947.0264093741907) + Current bracket: (1, 8, 13) (4079.1051067893313, 4602.6320859157631, 4812.7634310564736) + Current bracket: (1, 5, 8) (4079.1051067893313, 4252.8246786173822, 4602.6320859157631) + Current bracket: (1, 3, 5) (4079.1051067893313, 4049.5683588147385, 4252.8246786173822) + Current bracket: (1, 3, 5) (4079.1051067893313, 4049.5683588147385, 4252.8246786173822) + Bisect at B = 2 with S = 4074.521377773606 + Current bracket: (2, 3, 5) (4074.5213777736058, 4049.5683588147385, 4252.8246786173822) + Bisect at B = 4 with S = 4118.559112781375 + Current bracket: (2, 3, 4) (4074.5213777736058, 4049.5683588147385, 4118.559112781375) + Bisect at B = 2 with S = 4074.521377773606 + Best result: B = 3, S = 4049.568358814739 + 3 4049.56835881 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : rejected replacement (115, 1) -> (115, 1) , dS: 0.0 + l: 0, N: 115, B: 1 + l: 1, N: 1, B: 1 + 4081.40769188 + + directed: False overlap: False layered: False deg-corr: False + Current bracket: (1, 60, 115) (2070.9298619612, 2742.0286346974412, 3302.1523929668851) + Current bracket: (1, 26, 60) (2070.9298619612, 2163.2011864326296, 2742.0286346974412) + Current bracket: (1, 13, 26) (2070.9298619612, 1884.6261937918878, 2163.2011864326296) + Current bracket: (1, 13, 26) (2070.9298619612, 1884.6261937918878, 2163.2011864326296) + Bisect at B = 18 with S = 1991.644976112192 + Current bracket: (1, 13, 18) (2070.9298619612, 1884.6261937918878, 1991.6449761121924) + Bisect at B = 8 with S = 1824.47791693959 + Current bracket: (1, 8, 13) (2070.9298619612, 1824.4779169395899, 1884.6261937918878) + Bisect at B = 5 with S = 1832.933393330268 + Current bracket: (5, 8, 13) (1832.933393330268, 1824.4779169395899, 1884.6261937918878) + Bisect at B = 10 with S = 1833.549647859825 + Current bracket: (5, 8, 10) (1832.933393330268, 1824.4779169395899, 1833.5496478598247) + Bisect at B = 6 with S = 1824.864187113754 + Current bracket: (6, 8, 10) (1824.8641871137543, 1824.4779169395899, 1833.5496478598247) + Bisect at B = 7 with S = 1821.898539100332 + Current bracket: (6, 7, 8) (1824.8641871137543, 1821.8985391003318, 1824.4779169395899) + Bisect at B = 6 with S = 1824.864187113754 + Best result: B = 7, S = 1821.898539100332 + 7 1821.8985391 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 10) , dS: -247.539097789 2 + level 1 : replaced (10, 1) -> (10, 2) , dS: -8.57441720825 2 + level 2 : rejected replacement (2, 1) -> (2, 1) , dS: 0.0 + level 2 : rejected insert 2 , dS: 1.79175946923 + level 1 : skipping 2 + level 0 : rejected replacement (115, 10) -> (115, 9) , dS: 1.04063514461 + l: 0, N: 115, B: 10 + l: 1, N: 10, B: 2 + l: 2, N: 2, B: 1 + 1814.81634696 + + directed: False overlap: False layered: False deg-corr: True + Current bracket: (1, 60, 115) (2039.438415802616, 2687.4979970801901, 3059.7072673586936) + Current bracket: (1, 26, 60) (2039.438415802616, 2210.1885588389487, 2687.4979970801901) + Current bracket: (1, 13, 26) (2039.438415802616, 1956.4109872858012, 2210.1885588389487) + Current bracket: (1, 13, 26) (2039.438415802616, 1956.4109872858012, 2210.1885588389487) + Bisect at B = 18 with S = 2056.248563407142 + Current bracket: (1, 13, 18) (2039.438415802616, 1956.4109872858012, 2056.2485634071418) + Bisect at B = 8 with S = 1887.564759326295 + Current bracket: (1, 8, 13) (2039.438415802616, 1887.5647593262947, 1956.4109872858012) + Bisect at B = 5 with S = 1878.123470790204 + Current bracket: (1, 5, 8) (2039.438415802616, 1878.1234707902045, 1887.5647593262947) + Bisect at B = 3 with S = 1900.997091163706 + Current bracket: (3, 5, 8) (1900.9970911637056, 1878.1234707902045, 1887.5647593262947) + Bisect at B = 6 with S = 1873.870847992987 + Current bracket: (5, 6, 8) (1878.1234707902045, 1873.8708479929869, 1887.5647593262947) + Bisect at B = 7 with S = 1878.609827955009 + Current bracket: (5, 6, 7) (1878.1234707902045, 1873.8708479929869, 1878.6098279550095) + Bisect at B = 5 with S = 1878.123470790204 + Best result: B = 6, S = 1873.870847992987 + 6 1873.87084799 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 5) , dS: -164.055568622 2 + level 1 : rejected replacement (5, 1) -> (5, 1) , dS: 0.0 + level 1 : rejected insert 5 , dS: 9.62377364973 + level 0 : skipping 5 + l: 0, N: 115, B: 5 + l: 1, N: 5, B: 1 + 1875.38284718 + + directed: False overlap: False layered: covariates deg-corr: False + Current bracket: (1, 60, 115) (3451.4238155924145, 4295.9957091835586, 4658.1574421850237) + Current bracket: (1, 26, 60) (3451.4238155924145, 3824.5164704145741, 4295.9957091835586) + Current bracket: (1, 13, 26) (3451.4238155924145, 3481.1896063728163, 3824.5164704145741) + Current bracket: (1, 8, 13) (3451.4238155924145, 3350.3690166608876, 3481.1896063728163) + Current bracket: (1, 8, 13) (3451.4238155924145, 3350.3690166608876, 3481.1896063728163) + Bisect at B = 5 with S = 3305.96690163836 + Current bracket: (1, 5, 8) (3451.4238155924145, 3305.9669016383605, 3350.3690166608876) + Bisect at B = 3 with S = 3305.958349260824 + Current bracket: (1, 3, 5) (3451.4238155924145, 3305.958349260824, 3305.9669016383605) + Bisect at B = 2 with S = 3344.118688610963 + Current bracket: (2, 3, 5) (3344.1186886109631, 3305.958349260824, 3305.9669016383605) + Bisect at B = 4 with S = 3290.938676531715 + Current bracket: (3, 4, 5) (3305.958349260824, 3290.9386765317154, 3305.9669016383605) + Bisect at B = 3 with S = 3305.958349260824 + Best result: B = 4, S = 3290.938676531715 + 4 3290.93867653 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 9) , dS: -236.856154187 2 + level 1 : rejected replacement (9, 1) -> (9, 1) , dS: 0.0 + level 1 : rejected insert 9 , dS: 22.9004705474 + level 0 : skipping 9 + l: 0, N: 115, B: 9 + l: 1, N: 9, B: 1 + 3214.56766141 + + directed: False overlap: False layered: covariates deg-corr: True + Current bracket: (1, 60, 115) (3419.9323694338309, 4223.9918797736118, 4415.7123165768317) + Current bracket: (1, 26, 60) (3419.9323694338309, 3875.1607535720391, 4223.9918797736118) + Current bracket: (1, 13, 26) (3419.9323694338309, 3553.4454991243847, 3875.1607535720391) + Current bracket: (1, 8, 13) (3419.9323694338309, 3406.1491969341596, 3553.4454991243847) + Current bracket: (1, 8, 13) (3419.9323694338309, 3406.1491969341596, 3553.4454991243847) + Bisect at B = 5 with S = 3330.744584199459 + Current bracket: (1, 5, 8) (3419.9323694338309, 3330.7445841994586, 3406.1491969341596) + Bisect at B = 3 with S = 3317.96165911753 + Current bracket: (1, 3, 5) (3419.9323694338309, 3317.9616591175295, 3330.7445841994586) + Bisect at B = 2 with S = 3338.6229117634 + Current bracket: (2, 3, 5) (3338.6229117633998, 3317.9616591175295, 3330.7445841994586) + Bisect at B = 4 with S = 3318.361316320577 + Current bracket: (2, 3, 4) (3338.6229117633998, 3317.9616591175295, 3318.3613163205769) + Bisect at B = 2 with S = 3338.6229117634 + Best result: B = 3, S = 3317.96165911753 + 3 3317.96165912 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 6) , dS: -152.814489428 2 + level 1 : rejected replacement (6, 1) -> (6, 1) , dS: 0.0 + level 1 : rejected insert 6 , dS: 12.7148161031 + level 0 : skipping 6 + l: 0, N: 115, B: 6 + l: 1, N: 6, B: 1 + 3267.11788001 + + directed: False overlap: False layered: True deg-corr: False + Current bracket: (1, 60, 115) (3724.4849119477913, 4846.7272154433076, 5388.1681677508614) + Current bracket: (1, 26, 60) (3724.4849119477913, 4415.9298484742922, 4846.7272154433076) + Current bracket: (1, 13, 26) (3724.4849119477913, 4043.547617400895, 4415.9298484742922) + Current bracket: (1, 8, 13) (3724.4849119477913, 3831.4149302559326, 4043.547617400895) + Current bracket: (1, 5, 8) (3724.4849119477913, 3716.0144978844455, 3831.4149302559326) + Current bracket: (1, 5, 8) (3724.4849119477913, 3716.0144978844455, 3831.4149302559326) + Bisect at B = 3 with S = 3664.900838079229 + Current bracket: (1, 3, 5) (3724.4849119477913, 3664.9008380792288, 3716.0144978844455) + Bisect at B = 2 with S = 3657.203439841218 + Current bracket: (1, 2, 3) (3724.4849119477913, 3657.2034398412184, 3664.9008380792288) + Bisect at B = 1 with S = 3724.484911947791 + Best result: B = 2, S = 3657.203439841218 + 2 3657.20343984 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : replaced (115, 1) -> (115, 2) , dS: -52.0783689941 2 + level 1 : rejected replacement (2, 1) -> (2, 1) , dS: 0.0 + level 1 : rejected insert 2 , dS: 5.79909265446 + level 0 : skipping 2 + l: 0, N: 115, B: 2 + l: 1, N: 2, B: 1 + 3674.70912805 + + directed: False overlap: False layered: True deg-corr: True + Current bracket: (1, 60, 115) (3690.8748461232653, 4390.7235884784623, 4494.4233125699411) + Current bracket: (1, 26, 60) (3690.8748461232653, 4212.4807663850643, 4390.7235884784623) + Current bracket: (1, 13, 26) (3690.8748461232653, 4029.1609658456218, 4212.4807663850643) + Current bracket: (1, 8, 13) (3690.8748461232653, 3913.6020693472092, 4029.1609658456218) + Current bracket: (1, 5, 8) (3690.8748461232653, 3834.8957858009617, 3913.6020693472092) + Current bracket: (1, 3, 5) (3690.8748461232653, 3727.2813995894244, 3834.8957858009617) + Current bracket: (1, 2, 3) (3690.8748461232653, 3690.288221840598, 3727.2813995894244) + Bisect at B = 1 with S = 3690.874846123265 + Best result: B = 2, S = 3690.288221840598 + 2 3690.28822184 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : rejected replacement (115, 1) -> (115, 1) , dS: 0.0 + l: 0, N: 115, B: 1 + l: 1, N: 1, B: 1 + 3693.17743122 + + directed: False overlap: True layered: False deg-corr: False + Current bracket: (1, 60, 115) (2070.9298619612, 2773.8103951391176, 3302.1523929668847) + Current bracket: (1, 26, 60) (2070.9298619612, 2197.5369172261844, 2773.8103951391176) + Current bracket: (1, 13, 26) (2070.9298619612, 1888.693285109395, 2197.5369172261844) + Current bracket: (1, 13, 26) (2070.9298619612, 1888.693285109395, 2197.5369172261844) + Bisect at B = 18 with S = 2008.358125927789 + Current bracket: (1, 13, 18) (2070.9298619612, 1888.693285109395, 2008.3581259277889) + Bisect at B = 8 with S = 1821.812463004034 + Current bracket: (1, 8, 13) (2070.9298619612, 1821.8124630040336, 1888.693285109395) + Bisect at B = 5 with S = 1853.367979967209 + Current bracket: (5, 8, 13) (1853.3679799672091, 1821.8124630040336, 1888.693285109395) + Bisect at B = 10 with S = 1823.97846127917 + Current bracket: (5, 8, 10) (1853.3679799672091, 1821.8124630040336, 1823.9784612791696) + Bisect at B = 6 with S = 1828.525381763233 + Current bracket: (6, 8, 10) (1828.5253817632329, 1821.8124630040336, 1823.9784612791696) + Bisect at B = 7 with S = 1823.245540811884 + Current bracket: (7, 8, 10) (1823.2455408118842, 1821.8124630040336, 1823.9784612791696) + Bisect at B = 9 with S = 1822.086944968345 + Current bracket: (7, 8, 9) (1823.2455408118842, 1821.8124630040336, 1822.0869449683451) + Bisect at B = 7 with S = 1823.245540811884 + Best result: B = 8, S = 1821.812463004034 + 8 1821.812463 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 8) , dS: -232.661496277 2 + level 1 : replaced (8, 1) -> (8, 2) , dS: -2.95970180508 2 + level 2 : rejected replacement (2, 1) -> (2, 1) , dS: 0.0 + level 2 : rejected insert 2 , dS: 1.79175946923 + level 1 : skipping 2 + level 0 : replaced (115, 8) -> (115, 8) , dS: -4.93282634306 3 + level 1 : rejected replacement (8, 2) -> (8, 2) , dS: 0.0 + level 1 : rejected insert 8 , dS: 19.3741100228 + level 0 : skipping 8 + l: 0, N: 115, B: 8 + l: 1, N: 8, B: 2 + l: 2, N: 2, B: 1 + 1830.37583754 + + directed: False overlap: True layered: False deg-corr: True + Current bracket: (1, 60, 115) (2039.438415802616, 2791.218655464937, 3059.7072673586931) + Current bracket: (1, 26, 60) (2039.438415802616, 2596.1318468202285, 2791.218655464937) + Current bracket: (1, 13, 26) (2039.438415802616, 2249.7395717949853, 2596.1318468202285) + Current bracket: (1, 8, 13) (2039.438415802616, 2192.5567166861247, 2249.7395717949853) + Current bracket: (1, 5, 8) (2039.438415802616, 2155.8270672199023, 2192.5567166861247) + Current bracket: (1, 3, 5) (2039.438415802616, 2097.064562904091, 2155.8270672199023) + Current bracket: (1, 2, 3) (2039.438415802616, 2067.0267866042686, 2097.064562904091) + Bisect at B = 1 with S = 2039.438415802616 + Best result: B = 1, S = 2039.438415802616 + 1 2039.4384158 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : rejected replacement (115, 1) -> (115, 1) , dS: 0.0 + l: 0, N: 115, B: 1 + l: 1, N: 1, B: 1 + 2039.4384158 + + directed: False overlap: True layered: covariates deg-corr: False + Current bracket: (1, 60, 115) (3451.4238155924145, 4331.561281582568, 4658.1574421850237) + Current bracket: (1, 26, 60) (3451.4238155924145, 3901.5868258047312, 4331.561281582568) + Current bracket: (1, 13, 26) (3451.4238155924145, 3673.3309590473609, 3901.5868258047312) + Current bracket: (1, 8, 13) (3451.4238155924145, 3552.8365768937738, 3673.3309590473609) + Current bracket: (1, 5, 8) (3451.4238155924145, 3445.7715685052244, 3552.8365768937738) + Current bracket: (1, 5, 8) (3451.4238155924145, 3445.7715685052244, 3552.8365768937738) + Bisect at B = 3 with S = 3378.659618732226 + Current bracket: (1, 3, 5) (3451.4238155924145, 3378.6596187322261, 3445.7715685052244) + Bisect at B = 2 with S = 3384.190161466892 + Current bracket: (2, 3, 5) (3384.1901614668918, 3378.6596187322261, 3445.7715685052244) + Bisect at B = 4 with S = 3395.991818320718 + Current bracket: (2, 3, 4) (3384.1901614668918, 3378.6596187322261, 3395.9918183207178) + Bisect at B = 2 with S = 3384.190161466892 + Best result: B = 3, S = 3378.659618732226 + 3 3378.65961873 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 8) , dS: -236.467003238 2 + level 1 : rejected replacement (8, 1) -> (8, 1) , dS: 0.0 + level 1 : rejected insert 8 , dS: 19.3741100228 + level 0 : skipping 8 + l: 0, N: 115, B: 8 + l: 1, N: 8, B: 1 + 3214.95681235 + + directed: False overlap: True layered: covariates deg-corr: True + Current bracket: (1, 60, 115) (3419.9323694338309, 4273.5270137403832, 4415.7123165768317) + Current bracket: (1, 26, 60) (3419.9323694338309, 4003.9207484397484, 4273.5270137403832) + Current bracket: (1, 13, 26) (3419.9323694338309, 3747.5119493111124, 4003.9207484397484) + Current bracket: (1, 8, 13) (3419.9323694338309, 3587.8678820583082, 3747.5119493111124) + Current bracket: (1, 5, 8) (3419.9323694338309, 3527.8285413311705, 3587.8678820583082) + Current bracket: (1, 3, 5) (3419.9323694338309, 3450.2450966889314, 3527.8285413311705) + Current bracket: (1, 2, 3) (3419.9323694338309, 3388.2681843019941, 3450.2450966889314) + Bisect at B = 1 with S = 3419.932369433831 + Best result: B = 2, S = 3388.268184301994 + 2 3388.2681843 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 0.0 + level 0 : replaced (115, 1) -> (115, 6) , dS: -164.980561445 2 + level 1 : rejected replacement (6, 1) -> (6, 1) , dS: 0.0 + level 1 : rejected insert 6 , dS: 12.7148161031 + level 0 : skipping 6 + l: 0, N: 115, B: 6 + l: 1, N: 6, B: 1 + 3254.95180799 + + directed: False overlap: True layered: True deg-corr: False + Current bracket: (1, 60, 115) (3724.4849119477913, 5222.4917279727715, 5388.1681677508614) + Current bracket: (1, 26, 60) (3724.4849119477913, 4745.7356091147667, 5222.4917279727715) + Current bracket: (1, 13, 26) (3724.4849119477913, 4150.3055871227125, 4745.7356091147667) + Current bracket: (1, 8, 13) (3724.4849119477913, 3778.92601352782, 4150.3055871227125) + Current bracket: (1, 5, 8) (3724.4849119477913, 3587.5603537522038, 3778.92601352782) + Current bracket: (1, 5, 8) (3724.4849119477913, 3587.5603537522038, 3778.92601352782) + Bisect at B = 3 with S = 3529.792099317828 + Current bracket: (1, 3, 5) (3724.4849119477913, 3529.7920993178282, 3587.5603537522038) + Bisect at B = 2 with S = 3545.492577998865 + Current bracket: (2, 3, 5) (3545.4925779988648, 3529.7920993178282, 3587.5603537522038) + Bisect at B = 4 with S = 3552.535413509081 + Current bracket: (2, 3, 4) (3545.4925779988648, 3529.7920993178282, 3552.5354135090811) + Bisect at B = 2 with S = 3545.492577998865 + Best result: B = 3, S = 3529.792099317828 + 3 3529.79209932 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : replaced (115, 1) -> (115, 2) , dS: -52.0783689941 2 + level 1 : rejected replacement (2, 1) -> (2, 1) , dS: 0.0 + level 1 : rejected insert 2 , dS: 5.79909265446 + level 0 : skipping 2 + l: 0, N: 115, B: 2 + l: 1, N: 2, B: 1 + 3674.70912805 + + directed: False overlap: True layered: True deg-corr: True + Current bracket: (1, 60, 115) (3690.8748461232653, 4757.8922348797005, 4494.4233125699411) + Current bracket: (1, 26, 60) (3690.8748461232653, 4578.68710077166, 4757.8922348797005) + Current bracket: (1, 13, 26) (3690.8748461232653, 4249.4783928541656, 4578.68710077166) + Current bracket: (1, 8, 13) (3690.8748461232653, 3986.881326217177, 4249.4783928541656) + Current bracket: (1, 5, 8) (3690.8748461232653, 3856.2684964836299, 3986.881326217177) + Current bracket: (1, 3, 5) (3690.8748461232653, 3699.7810945700962, 3856.2684964836299) + Current bracket: (1, 2, 3) (3690.8748461232653, 3641.6685995295234, 3699.7810945700962) + Bisect at B = 1 with S = 3690.874846123265 + Best result: B = 2, S = 3641.668599529523 + 2 3641.66859953 + level 1 : rejected replacement (1, 1) -> (1, 1) , dS: 0.0 + level 1 : rejected insert 1 , dS: 2.30258509299 + level 0 : rejected replacement (115, 1) -> (115, 1) , dS: 0.0 + l: 0, N: 115, B: 1 + l: 1, N: 1, B: 1 + 3693.17743122 diff --git a/doc/test_inference.py b/doc/test_inference.py new file mode 100755 index 0000000000000000000000000000000000000000..4371e19e5305de15e455f5a4016e01610d16807e --- /dev/null +++ b/doc/test_inference.py @@ -0,0 +1,141 @@ +#!/bin/env python + +from graph_tool.all import * +import numpy.random +from numpy.random import randint + +seed_rng(42) +numpy.random.seed(42) + +graph_tool.inference.set_test(True) + +g = collection.data["football"] +ec = g.new_ep("int", randint(0, 10, g.num_edges())) + +def gen_state(directed, deg_corr, layers, overlap): + u = GraphView(g, directed=directed) + if layers != False: + state = graph_tool.inference.LayeredBlockState(u, B=u.num_vertices(), + deg_corr=deg_corr, + ec=ec.copy(), + overlap=overlap, + layers=layers == True) + elif overlap: + state = graph_tool.inference.OverlapBlockState(u, B=2 * u.num_edges(), + deg_corr=deg_corr) + else: + state = graph_tool.inference.BlockState(u, B=u.num_vertices(), + deg_corr=deg_corr) + return state + + +for directed in [True, False]: + for overlap in [False, True]: + for layered in [False, "covariates", True]: + for deg_corr in [False, True]: + for dl in [False, True]: + + print("\ndirected:", directed, "overlap:", overlap, + "layered:", layered, "deg-corr:", deg_corr, "dl:", dl) + + + print("\t mcmc (unweighted)") + state = gen_state(directed, deg_corr, layered, overlap) + + print("\t\t", state.mcmc_sweep(beta=0, allow_empty=True, + entropy_args=dict(dl=dl)), + (state.wr.a > 0).sum()) + if overlap: + print("\t\t", state.mcmc_sweep(beta=0, bundled=True, + allow_empty=True, + entropy_args=dict(dl=dl)), + (state.wr.a > 0).sum()) + + state = gen_state(directed, deg_corr, layered, overlap) + + if not overlap: + print("\t mcmc") + bstate = state.get_block_state(vweight=True, + deg_corr=deg_corr) + + print("\t\t", + bstate.mcmc_sweep(beta=0, + allow_empty=True, + entropy_args=dict(dl=dl, + multigraph=False)), + (bstate.wr.a > 0).sum()) + + print("\t\t", + bstate.mcmc_sweep(beta=0, allow_empty=True, + entropy_args=dict(dl=dl, + multigraph=False)), + (bstate.wr.a > 0).sum()) + + print("\t\t", + bstate.gibbs_sweep(beta=0, allow_empty=True, + entropy_args=dict(dl=dl, + multigraph=False)), + (bstate.wr.a > 0).sum()) + + print("\t merge") + + state = gen_state(directed, deg_corr, layered, overlap) + + if not overlap: + bstate = state.get_block_state(vweight=True, + deg_corr=deg_corr) + + print("\t\t", + bstate.merge_sweep(50, + entropy_args=dict(dl=dl, + multigraph=False))) + + bstate = bstate.copy() + + print("\t\t", + bstate.mcmc_sweep(beta=0, allow_empty=True, + entropy_args=dict(dl=dl, + multigraph=False))) + print("\t\t", + bstate.gibbs_sweep(beta=0, allow_empty=True, + entropy_args=dict(dl=dl, + multigraph=False))) + else: + print("\t\t", + state.merge_sweep(50, + entropy_args=dict(dl=dl, + multigraph=False))) + + print("\t shrink") + + state = gen_state(directed, deg_corr, layered, overlap) + state = state.shrink(B=5, entropy_args=dict(dl=dl, + multigraph=False)) + print("\t\t", state.B) + +for directed in [True, False]: + for overlap in [False, True]: + for layered in [False, "covariates", True]: + for deg_corr in [False, True]: + print("\ndirected:", directed, "overlap:", overlap, + "layered:", layered, "deg-corr:", deg_corr) + + state = minimize_blockmodel_dl(GraphView(g, directed=directed), + verbose=(1, "\t"), + deg_corr=deg_corr, + overlap=overlap, + layers=layered != False, + state_args=dict(ec=ec, + layers=(layered == True))) + print(state.B, state.entropy()) + + state = minimize_nested_blockmodel_dl(GraphView(g, directed=directed), + verbose=(1, "\t"), + deg_corr=deg_corr, + overlap=overlap, + layers=layered != False, + state_args=dict(ec=ec, + layers=(layered == True))) + state.print_summary() + print(state.entropy()) + diff --git a/src/boost-workaround/boost/graph/reverse_graph_alt.hpp b/src/boost-workaround/boost/graph/reverse_graph_alt.hpp index 1bfb8c8108e31b9d6c66a259decc736b022a62e3..a423f06e07970a4559a83b249868b0c75b531fae 100644 --- a/src/boost-workaround/boost/graph/reverse_graph_alt.hpp +++ b/src/boost-workaround/boost/graph/reverse_graph_alt.hpp @@ -306,7 +306,8 @@ namespace detail { typedef typename property_traits::reference reference; typedef typename property_traits::category category; - explicit reverse_graph_edge_property_map(const PM& pm): underlying_pm(pm) {} + reverse_graph_edge_property_map(const PM& pm): underlying_pm(pm) {} + reverse_graph_edge_property_map() = default; friend reference get(const reverse_graph_edge_property_map& m, diff --git a/src/graph/Makefile.am b/src/graph/Makefile.am index 4aa05097e1960f58400a374d92d7a5c08a470068..1519a4ab06b1abeddfb63c9223c4b2fb5b0964f9 100644 --- a/src/graph/Makefile.am +++ b/src/graph/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = centrality clustering community correlations draw flow generation layout search spectral stats topology util +SUBDIRS = centrality clustering community_old correlations draw flow generation inference layout search spectral stats topology util AM_CPPFLAGS =\ -I$(srcdir)/.. \ diff --git a/src/graph/community/Makefile.am b/src/graph/community_old/Makefile.am similarity index 69% rename from src/graph/community/Makefile.am rename to src/graph/community_old/Makefile.am index 543f26d7714cc844622f02c02a3e686724cd609f..c9e3b040f50f0769d54cdb23e49affc901769391 100644 --- a/src/graph/community/Makefile.am +++ b/src/graph/community_old/Makefile.am @@ -4,11 +4,11 @@ AM_CPPFLAGS = $(MOD_CPPFLAGS) AM_CFLAGS = $(AM_CXXFLAGS) -libgraph_tool_communitydir = $(MOD_DIR)/community +libgraph_tool_communitydir = $(MOD_DIR)/community_old libgraph_tool_community_LTLIBRARIES = libgraph_tool_community.la -libgraph_tool_community_la_includedir = $(MOD_DIR)/include/community +libgraph_tool_community_la_includedir = $(MOD_DIR)/include/community_old libgraph_tool_community_la_LIBADD = $(MOD_LIBADD) @@ -19,16 +19,10 @@ libgraph_tool_community_la_SOURCES = \ graph_blockmodel_covariates.cc \ graph_blockmodel_overlap.cc \ graph_community.cc \ - graph_community_network.cc \ - graph_community_network_edges.cc \ - graph_community_network_vavg.cc \ - graph_community_network_eavg.cc \ - graph_community_network_eavg_imp1.cc \ spence.cc libgraph_tool_community_la_include_HEADERS = \ graph_blockmodel.hh \ graph_blockmodel_covariates.hh \ graph_blockmodel_overlap.hh \ - graph_community.hh \ - graph_community_network.hh + graph_community.hh diff --git a/src/graph/community/graph_blockmodel.cc b/src/graph/community_old/graph_blockmodel.cc similarity index 95% rename from src/graph/community/graph_blockmodel.cc rename to src/graph/community_old/graph_blockmodel.cc index 6dee2ccf55a0458ebd20619a54063d68f22ddf74..778d26499420449e44171587a892e77755206193 100644 --- a/src/graph/community/graph_blockmodel.cc +++ b/src/graph/community_old/graph_blockmodel.cc @@ -58,58 +58,64 @@ using namespace graph_tool; namespace graph_tool { -vector __safelog_cache; -vector __xlogx_cache; -vector __lgamma_cache; - -void init_safelog(size_t x) -{ - size_t old_size = __safelog_cache.size(); - if (x >= old_size) - { - __safelog_cache.resize(x + 1); - for (size_t i = old_size; i < __safelog_cache.size(); ++i) - __safelog_cache[i] = safelog(double(i)); - } -} - -void clear_safelog() -{ - vector().swap(__safelog_cache); -} - - -void init_xlogx(size_t x) -{ - size_t old_size = __xlogx_cache.size(); - if (x >= old_size) - { - __xlogx_cache.resize(x + 1); - for (size_t i = old_size; i < __xlogx_cache.size(); ++i) - __xlogx_cache[i] = i * safelog(i); - } -} - -void clear_xlogx() -{ - vector().swap(__xlogx_cache); -} - -void init_lgamma(size_t x) -{ - size_t old_size = __lgamma_cache.size(); - if (x >= old_size) - { - __lgamma_cache.resize(x + 1); - for (size_t i = old_size; i < __lgamma_cache.size(); ++i) - __lgamma_cache[i] = lgamma(i); - } -} - -void clear_lgamma() -{ - vector().swap(__lgamma_cache); -} +// vector __safelog_cache; +// vector __xlogx_cache; +// vector __lgamma_cache; + +void init_safelog(size_t x); +// void init_safelog(size_t x) +// { +// size_t old_size = __safelog_cache.size(); +// if (x >= old_size) +// { +// __safelog_cache.resize(x + 1); +// for (size_t i = old_size; i < __safelog_cache.size(); ++i) +// __safelog_cache[i] = safelog(double(i)); +// } +// } + +void clear_safelog(); +// void clear_safelog() +// { +// vector().swap(__safelog_cache); +// } + +void init_xlogx(size_t x); + +// void init_xlogx(size_t x) +// { +// size_t old_size = __xlogx_cache.size(); +// if (x >= old_size) +// { +// __xlogx_cache.resize(x + 1); +// for (size_t i = old_size; i < __xlogx_cache.size(); ++i) +// __xlogx_cache[i] = i * safelog(i); +// } +// } + +void clear_xlogx(); +// void clear_xlogx() +// { +// vector().swap(__xlogx_cache); +// } + +void init_lgamma(size_t x); +// void init_lgamma(size_t x) +// { +// size_t old_size = __lgamma_cache.size(); +// if (x >= old_size) +// { +// __lgamma_cache.resize(x + 1); +// for (size_t i = old_size; i < __lgamma_cache.size(); ++i) +// __lgamma_cache[i] = lgamma(i); +// } +// } + +void clear_lgamma(); +// void clear_lgamma() +// { +// vector().swap(__lgamma_cache); +// } } diff --git a/src/graph/community/graph_blockmodel.hh b/src/graph/community_old/graph_blockmodel.hh similarity index 100% rename from src/graph/community/graph_blockmodel.hh rename to src/graph/community_old/graph_blockmodel.hh diff --git a/src/graph/community/graph_blockmodel_covariates.cc b/src/graph/community_old/graph_blockmodel_covariates.cc similarity index 95% rename from src/graph/community/graph_blockmodel_covariates.cc rename to src/graph/community_old/graph_blockmodel_covariates.cc index 382de8fd9b8cfb36c670e91cdcf006f59b3c369d..ad6e6b536f8476d9a28106f9ac13487a0d6a9930 100644 --- a/src/graph/community/graph_blockmodel_covariates.cc +++ b/src/graph/community_old/graph_blockmodel_covariates.cc @@ -690,55 +690,55 @@ void do_split_graph(GraphInterface& gi, boost::any& aec, boost::any& ab, std::ref(uvmap)))(); } -bool bmap_has(const bmap_t& bmap, size_t c, size_t r) -{ - if (c > bmap.size()) - throw GraphException("invalid covariate value:" + lexical_cast(c)); - auto iter = bmap[c].find(r); - if (iter == bmap[c].end()) - return false; - return true; -} +// bool bmap_has(const bmap_t& bmap, size_t c, size_t r) +// { +// if (c > bmap.size()) +// throw GraphException("invalid covariate value:" + lexical_cast(c)); +// auto iter = bmap[c].find(r); +// if (iter == bmap[c].end()) +// return false; +// return true; +// } -size_t bmap_get(const bmap_t& bmap, size_t c, size_t r) -{ - if (c > bmap.size()) - throw GraphException("invalid covariate value:" + lexical_cast(c)); - auto iter = bmap[c].find(r); - if (iter == bmap[c].end()) - throw GraphException("no mapping for block " + lexical_cast(r) - + " in layer " + lexical_cast(c)); - return iter->second; -} +// size_t bmap_get(const bmap_t& bmap, size_t c, size_t r) +// { +// if (c > bmap.size()) +// throw GraphException("invalid covariate value:" + lexical_cast(c)); +// auto iter = bmap[c].find(r); +// if (iter == bmap[c].end()) +// throw GraphException("no mapping for block " + lexical_cast(r) +// + " in layer " + lexical_cast(c)); +// return iter->second; +// } -void bmap_set(bmap_t& bmap, size_t c, size_t r, size_t r_u) -{ - if (c > bmap.size()) - throw GraphException("invalid covariate value:" + lexical_cast(c)); - bmap[c][r] = r_u; -} +// void bmap_set(bmap_t& bmap, size_t c, size_t r, size_t r_u) +// { +// if (c > bmap.size()) +// throw GraphException("invalid covariate value:" + lexical_cast(c)); +// bmap[c][r] = r_u; +// } -void bmap_del_c(bmap_t& bmap, size_t c) -{ - if (c > bmap.size()) - throw GraphException("invalid covariate value:" + lexical_cast(c)); - bmap.erase(bmap.begin() + c); -} +// void bmap_del_c(bmap_t& bmap, size_t c) +// { +// if (c > bmap.size()) +// throw GraphException("invalid covariate value:" + lexical_cast(c)); +// bmap.erase(bmap.begin() + c); +// } -bmap_t bmap_copy(const bmap_t& bmap) -{ - return bmap; -} +// bmap_t bmap_copy(const bmap_t& bmap) +// { +// return bmap; +// } void export_blockmodel_covariate() { - boost::python::class_("bmap_t") - .def("has", bmap_has) - .def("get", bmap_get) - .def("set", bmap_set) - .def("del_c", bmap_del_c) - .def("copy", bmap_copy); + // boost::python::class_("bmap_t") + // .def("has", bmap_has) + // .def("get", bmap_get) + // .def("set", bmap_set) + // .def("del_c", bmap_del_c) + // .def("copy", bmap_copy); boost::python::def("cov_move_sweep", do_cov_move_sweep); boost::python::def("covariate_entropy", do_covariate_entropy); diff --git a/src/graph/community/graph_blockmodel_covariates.hh b/src/graph/community_old/graph_blockmodel_covariates.hh similarity index 100% rename from src/graph/community/graph_blockmodel_covariates.hh rename to src/graph/community_old/graph_blockmodel_covariates.hh diff --git a/src/graph/community/graph_blockmodel_overlap.cc b/src/graph/community_old/graph_blockmodel_overlap.cc similarity index 100% rename from src/graph/community/graph_blockmodel_overlap.cc rename to src/graph/community_old/graph_blockmodel_overlap.cc diff --git a/src/graph/community/graph_blockmodel_overlap.hh b/src/graph/community_old/graph_blockmodel_overlap.hh similarity index 100% rename from src/graph/community/graph_blockmodel_overlap.hh rename to src/graph/community_old/graph_blockmodel_overlap.hh diff --git a/src/graph/community/graph_community.cc b/src/graph/community_old/graph_community.cc similarity index 76% rename from src/graph/community/graph_community.cc rename to src/graph/community_old/graph_community.cc index 45c7d356da8431e222bc425e82737793a38b08d8..0515d56d30bfd6779fb8c3c1442fe699b77f2767 100644 --- a/src/graph/community/graph_community.cc +++ b/src/graph/community_old/graph_community.cc @@ -97,27 +97,6 @@ double modularity(GraphInterface& g, boost::any weight, boost::any property) using namespace boost::python; -extern void community_network(GraphInterface& gi, GraphInterface& cgi, - boost::any community_property, - boost::any condensed_community_property, - boost::any vertex_count, boost::any edge_count, - boost::any vweight, boost::any eweight, - bool self_loops, bool parallel_edges); - -void community_network_vavg(GraphInterface& gi, GraphInterface& cgi, - boost::any community_property, - boost::any condensed_community_property, - boost::any vweight, - boost::python::list avprops); - -void community_network_eavg(GraphInterface& gi, GraphInterface& cgi, - boost::any community_property, - boost::any condensed_community_property, - boost::any eweight, - boost::python::list aeprops, - bool self_loops); - - extern void export_blockmodel(); extern void export_blockmodel_overlap(); extern void export_blockmodel_covariate(); @@ -126,9 +105,6 @@ BOOST_PYTHON_MODULE(libgraph_tool_community) { def("community_structure", &community_structure); def("modularity", &modularity); - def("community_network", &community_network); - def("community_network_vavg", &community_network_vavg); - def("community_network_eavg", &community_network_eavg); export_blockmodel(); export_blockmodel_overlap(); diff --git a/src/graph/community/graph_community.hh b/src/graph/community_old/graph_community.hh similarity index 100% rename from src/graph/community/graph_community.hh rename to src/graph/community_old/graph_community.hh diff --git a/src/graph/community/spence.cc b/src/graph/community_old/spence.cc similarity index 100% rename from src/graph/community/spence.cc rename to src/graph/community_old/spence.cc diff --git a/src/graph/draw/graph_cairo_draw.cc b/src/graph/draw/graph_cairo_draw.cc index 7fd13ee2e091917840b99821bb8b6fff92da08a7..c2fe4e0570837e37ac8287b21cd9e809391194fd 100644 --- a/src/graph/draw/graph_cairo_draw.cc +++ b/src/graph/draw/graph_cairo_draw.cc @@ -1045,7 +1045,7 @@ public: _attrs.template get >(EDGE_CONTROL_POINTS); vector gradient = _attrs.template get >(EDGE_GRADIENT); - if (gradient.size() == 2) + if (gradient.size() == 1) { auto e_color = _attrs.template get(EDGE_COLOR); auto s_color = _s._attrs.template get(VERTEX_FILL_COLOR); diff --git a/src/graph/generation/Makefile.am b/src/graph/generation/Makefile.am index 4cf6e379a9ad67fe3309cbfdf68feb7b6e98b9af..e31ad435bce509e2fd1621dd2dc8f52363b21a50 100644 --- a/src/graph/generation/Makefile.am +++ b/src/graph/generation/Makefile.am @@ -15,29 +15,35 @@ libgraph_tool_generation_la_LIBADD = $(MOD_LIBADD) $(CGAL_LIBADD) libgraph_tool_generation_la_LDFLAGS = $(MOD_LDFLAGS) libgraph_tool_generation_la_SOURCES = \ + graph_community_network.cc \ + graph_community_network_eavg.cc \ + graph_community_network_eavg_imp1.cc \ + graph_community_network_edges.cc \ + graph_community_network_vavg.cc \ + graph_complete.cc \ graph_generation.cc \ - graph_rewiring.cc \ - graph_predecessor.cc \ + graph_geometric.cc \ + graph_lattice.cc \ graph_line_graph.cc \ + graph_predecessor.cc \ + graph_price.cc \ + graph_rewiring.cc \ + graph_triangulation.cc \ graph_union.cc \ - graph_union_vprop.cc \ graph_union_eprop.cc \ - graph_triangulation.cc \ - graph_lattice.cc \ - graph_geometric.cc \ - graph_complete.cc \ - graph_price.cc + graph_union_vprop.cc libgraph_tool_generation_la_include_HEADERS = \ + dynamic_sampler.hh \ + graph_community_network.hh \ + graph_complete.hh \ graph_generation.hh \ - graph_rewiring.hh \ - graph_predecessor.hh \ - graph_union.hh \ - graph_triangulation.hh \ - graph_lattice.hh \ graph_geometric.hh \ - graph_complete.hh \ + graph_lattice.hh \ + graph_predecessor.hh \ graph_price.hh \ - dynamic_sampler.hh \ + graph_rewiring.hh \ + graph_triangulation.hh \ + graph_union.hh \ sampler.hh diff --git a/src/graph/community/graph_community_network.cc b/src/graph/generation/graph_community_network.cc similarity index 100% rename from src/graph/community/graph_community_network.cc rename to src/graph/generation/graph_community_network.cc diff --git a/src/graph/community/graph_community_network.hh b/src/graph/generation/graph_community_network.hh similarity index 100% rename from src/graph/community/graph_community_network.hh rename to src/graph/generation/graph_community_network.hh diff --git a/src/graph/community/graph_community_network_eavg.cc b/src/graph/generation/graph_community_network_eavg.cc similarity index 100% rename from src/graph/community/graph_community_network_eavg.cc rename to src/graph/generation/graph_community_network_eavg.cc diff --git a/src/graph/community/graph_community_network_eavg_imp1.cc b/src/graph/generation/graph_community_network_eavg_imp1.cc similarity index 100% rename from src/graph/community/graph_community_network_eavg_imp1.cc rename to src/graph/generation/graph_community_network_eavg_imp1.cc diff --git a/src/graph/community/graph_community_network_edges.cc b/src/graph/generation/graph_community_network_edges.cc similarity index 100% rename from src/graph/community/graph_community_network_edges.cc rename to src/graph/generation/graph_community_network_edges.cc diff --git a/src/graph/community/graph_community_network_vavg.cc b/src/graph/generation/graph_community_network_vavg.cc similarity index 100% rename from src/graph/community/graph_community_network_vavg.cc rename to src/graph/generation/graph_community_network_vavg.cc diff --git a/src/graph/generation/graph_generation.cc b/src/graph/generation/graph_generation.cc index 8399350ef4fd5708bc8da8bacb264ac538d20dff..82dbf05591c2292380b5caa361c79e28d5f884df 100644 --- a/src/graph/generation/graph_generation.cc +++ b/src/graph/generation/graph_generation.cc @@ -48,9 +48,10 @@ private: boost::python::object _o; }; -void generate_graph(GraphInterface& gi, size_t N, boost::python::object deg_sample, - bool no_parallel, bool no_self_loops, bool undirected, - rng_t& rng, bool verbose, bool verify) +void generate_graph(GraphInterface& gi, size_t N, + boost::python::object deg_sample, bool no_parallel, + bool no_self_loops, bool undirected, rng_t& rng, + bool verbose, bool verify) { typedef graph_tool::detail::get_all_graph_views::apply< graph_tool::detail::filt_scalar_type, boost::mpl::bool_, @@ -84,15 +85,34 @@ void vertex_property_union(GraphInterface& ugi, GraphInterface& gi, void edge_property_union(GraphInterface& ugi, GraphInterface& gi, boost::any p_vprop, boost::any p_eprop, boost::any uprop, boost::any prop); -void triangulation(GraphInterface& gi, boost::python::object points, boost::any pos, - string type, bool periodic); +void triangulation(GraphInterface& gi, boost::python::object points, + boost::any pos, string type, bool periodic); void lattice(GraphInterface& gi, boost::python::object oshape, bool periodic); void geometric(GraphInterface& gi, boost::python::object opoints, double r, boost::python::object orange, bool periodic, boost::any pos); void price(GraphInterface& gi, size_t N, double gamma, double c, size_t m, rng_t& rng); void complete(GraphInterface& gi, size_t N, bool directed, bool self_loops); -void circular(GraphInterface& gi, size_t N, size_t k, bool directed, bool self_loops); +void circular(GraphInterface& gi, size_t N, size_t k, bool directed, + bool self_loops); + +void community_network(GraphInterface& gi, GraphInterface& cgi, + boost::any community_property, + boost::any condensed_community_property, + boost::any vertex_count, boost::any edge_count, + boost::any vweight, boost::any eweight, bool self_loops, + bool parallel_edges); + +void community_network_vavg(GraphInterface& gi, GraphInterface& cgi, + boost::any community_property, + boost::any condensed_community_property, + boost::any vweight, boost::python::list avprops); + +void community_network_eavg(GraphInterface& gi, GraphInterface& cgi, + boost::any community_property, + boost::any condensed_community_property, + boost::any eweight, boost::python::list aeprops, + bool self_loops); using namespace boost::python; @@ -111,6 +131,9 @@ BOOST_PYTHON_MODULE(libgraph_tool_generation) def("price", &price); def("complete", &complete); def("circular", &circular); + def("community_network", &community_network); + def("community_network_vavg", &community_network_vavg); + def("community_network_eavg", &community_network_eavg); class_>("Sampler", init&, const vector&>()) diff --git a/src/graph/generation/sampler.hh b/src/graph/generation/sampler.hh index b1188a4edfc063ca55ecb041f43b79a5f05f3063..1d1e26b0414c50f59d9d5b1a19cb5eb44d474ebd 100644 --- a/src/graph/generation/sampler.hh +++ b/src/graph/generation/sampler.hh @@ -111,7 +111,7 @@ private: // uniform sampling from containers template -const typename Container::value_type& uniform_sample(const Container& v, RNG& rng) +auto& uniform_sample(Container& v, RNG& rng) { std::uniform_int_distribution i_rand(0, v.size() - 1); return v[i_rand(rng)]; diff --git a/src/graph/graph_bind.cc b/src/graph/graph_bind.cc index 89cb6315c5d01709d25e0b4dec1add9df1f58fad..21e713b572e13242ea4eb994de94c2fbdd1f1100 100644 --- a/src/graph/graph_bind.cc +++ b/src/graph/graph_bind.cc @@ -491,7 +491,8 @@ BOOST_PYTHON_MODULE(libgraph_tool_core) .def("get_graph_index", &GraphInterface::get_graph_index) .def("copy_vertex_property", &GraphInterface::copy_vertex_property) .def("copy_edge_property", &GraphInterface::copy_edge_property) - .def("get_graph_ptr", &GraphInterface::get_graph_ptr); + .def("get_graph_ptr", &GraphInterface::get_graph_ptr) + .def("get_graph_view", &GraphInterface::get_graph_view); class_("vertex_index_map", no_init); class_("edge_index_map", no_init); diff --git a/src/graph/graph_filtering.cc b/src/graph/graph_filtering.cc index 9f3b6448365919cb14679249f980b8f3ea11cc6f..4a98c847ef405ed2cebf0fc0b29d142e9d97da11 100644 --- a/src/graph/graph_filtering.cc +++ b/src/graph/graph_filtering.cc @@ -31,6 +31,8 @@ bool graph_tool::graph_filtering_enabled() #endif } +namespace graph_tool +{ string name_demangle(string name) { int status = 0; @@ -41,6 +43,7 @@ string name_demangle(string name) free(realname); return ret; } +} // Whenever no implementation is called, the following exception is thrown graph_tool::ActionNotFound::ActionNotFound(const type_info& action, diff --git a/src/graph/graph_filtering.hh b/src/graph/graph_filtering.hh index 1cbc4c46ee01c18ce189a32fa039479e166ccdc4..dbb83ea6d4acbd0154a3846543ab56ff894079bc 100644 --- a/src/graph/graph_filtering.hh +++ b/src/graph/graph_filtering.hh @@ -536,6 +536,8 @@ retrieve_graph_view(GraphInterface& gi, Graph& init) return *gptr; } +// symbol demangling +string name_demangle(string name); } //graph_tool namespace diff --git a/src/graph/graph_properties.hh b/src/graph/graph_properties.hh index b9350f21ea48d57a487419a91c0592c6d13a0c6a..51cb5b174a16b7b7707d1f856b3b76867f1913f6 100644 --- a/src/graph/graph_properties.hh +++ b/src/graph/graph_properties.hh @@ -616,6 +616,9 @@ public: const value_type c; }; +template +void put(const ConstantPropertyMap&, const Key&, const Value&) {} + // the following is a property map which always returns one template class UnityPropertyMap @@ -632,6 +635,9 @@ public: }; +template +void put(const UnityPropertyMap&, const Key&, const Value&) {} + template struct is_constant_property { @@ -642,7 +648,6 @@ struct is_constant_property typename std::is_same>::type>::type type; }; - // this wraps an existing property map, but always converts its values to a // given type template +// +// 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 . + +#ifndef SPLIT_MERGE_LOOP_HH +#define SPLIT_MERGE_LOOP_HH + +#include "config.h" + +#include +#include + +#include + +#include "hash_map_wrap.hh" + +#ifdef USING_OPENMP +#include +#endif +namespace graph_tool +{ + +template +auto bundled_vacate_sweep(MergeState& state, RNG& rng) +{ + // individual bundles can move in different directions + auto get_best_move = [&] (auto& bundle, auto& past_moves) + { + std::tuple best_move(state._null_move, + numeric_limits::max()); + + auto r = state.bundle_state(bundle); + + auto find_candidates = [&](bool random) + { + for (size_t iter = 0; iter < state._niter; ++iter) + { + auto s = state.move_proposal(bundle, random, rng); + if (s == state._null_move) + continue; + if (past_moves.find(s) != past_moves.end()) + continue; + past_moves.insert(s); + + double dS = state.virtual_move_dS(bundle, s); + + if (dS < get<1>(best_move)) + { + get<0>(best_move) = s; + get<1>(best_move) = dS; + } + } + }; + + find_candidates(false); + + // if no candidates were found, the group is likely to be "stuck" + // (i.e. isolated or constrained by clabel); attempt random + // movements instead + + if (get<0>(best_move) == state._null_move) + find_candidates(true); + + return best_move; + }; + + // all bundles move together + auto get_best_move_coherent = [&] (auto& bundles) + { + auto r = state.bundle_state(bundles[0]); + + gt_hash_set past_moves; + std::tuple best_move(state._null_move, + numeric_limits::max()); + + auto find_candidates = [&](bool random) + { + for (size_t iter = 0; iter < state._niter; ++iter) + { + auto s = state.move_proposal(uniform_sample(bundles, rng), + random, rng); + if ( s == state._null_move) + continue; + if (past_moves.find(s) != past_moves.end()) + continue; + past_moves.insert(s); + + double dS = 0; + for (auto& bundle : bundles) + { + dS += state.virtual_move_dS(bundle, s); + state.perform_move(bundle, s); + } + + for (auto& bundle : bundles) + state.perform_move(bundle, r); + + if (dS < get<1>(best_move)) + { + get<0>(best_move) = s; + get<1>(best_move) = dS; + } + } + }; + + find_candidates(false); + + // if no candidates were found, the group is likely to be "stuck" + // (i.e. isolated or constrained by clabel); attempt random + // movements instead + + if (get<0>(best_move) == state._null_move) + find_candidates(true); + + return best_move; + }; + + auto get_best_move_bundles = [&](auto& bundles, auto& forbidden_moves, + auto& bmoves) + { + auto r = state.bundle_state(bundles[0]); + + double dS = 0; + for (auto& bundle : bundles) + { + + gt_hash_set past_moves(forbidden_moves); + auto best_move = get_best_move(bundle, past_moves); + if (get<0>(best_move) == state._null_move) + { + bmoves.clear(); + break; + } + bmoves.push_back(get<0>(best_move)); + + dS += state.virtual_move_dS(bundle, bmoves.back()); + state.perform_move(bundle, bmoves.back()); + } + + for (auto& bundle : bundles) + state.perform_move(bundle, r); + + auto best_coherent = get_best_move_coherent(bundles); + + if (get<1>(best_coherent) < dS) + { + dS = get<1>(best_coherent); + for (auto& r : bmoves) + r = get<0>(best_coherent); + } + + return dS; + }; + + + std::vector>>, + std::vector>> best_moves; + std::vector best_moves_dS; + std::vector idx; + + for (auto& bundles : state._block_bundles) + { + std::vector bmoves; + gt_hash_set past_moves; + double dS = get_best_move_bundles(bundles, past_moves, bmoves); + if (!bmoves.empty()) + { + best_moves.emplace_back(std::ref(bundles), + std::move(bmoves)); + best_moves_dS.push_back(dS); + idx.push_back(idx.size()); + } + } + + std::shuffle(idx.begin(), idx.end(), rng); + + auto cmp = [&](auto& i, auto& j) + { return best_moves_dS[i] > best_moves_dS[j]; }; + + std::priority_queue, decltype(cmp)> queue(cmp); + + for (auto i : idx) + queue.push(i); + + double S = 0; + size_t nmerges = 0; + gt_hash_set vacated; + while (nmerges != state._nmerges && !queue.empty()) + { + auto pos = queue.top(); + queue.pop(); + + auto& bundles = get<0>(best_moves[pos]).get(); + auto& bmoves = get<1>(best_moves[pos]); + + bool redo = false; + for (auto s : bmoves) + { + if (vacated.find(s) != vacated.end()) + { + redo = true; + break; + } + } + + if (redo) + { + bmoves.clear(); + double dS = get_best_move_bundles(bundles, vacated, bmoves); + if (bmoves.empty()) + continue; + if (!queue.empty() && best_moves_dS[queue.top()] < dS) + { + best_moves_dS[pos] = dS; + queue.push(pos); + continue; + } + } + + auto r = state.bundle_state(bundles[0]); + vacated.insert(r); + + for (size_t i = 0; i < bundles.size(); ++i) + { + auto& bundle = bundles[i]; + auto s = bmoves[i]; + S += state.virtual_move_dS(bundle, s); + state.perform_move(bundle, s); + } + + nmerges++; + } + + return make_pair(S, nmerges); +} + +} // graph_tool namespace + +#endif //SPLIT_MERGE_LOOP_HH diff --git a/src/graph/inference/cache.cc b/src/graph/inference/cache.cc new file mode 100644 index 0000000000000000000000000000000000000000..4fad8fdf5a2b823a7c280b00487ce01c577deef3 --- /dev/null +++ b/src/graph/inference/cache.cc @@ -0,0 +1,86 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "cache.hh" + +namespace graph_tool +{ + +using namespace std; + +vector __safelog_cache; +vector __xlogx_cache; +vector __lgamma_cache; + +void init_safelog(size_t x) +{ + size_t old_size = __safelog_cache.size(); + if (x >= old_size) + { + __safelog_cache.resize(x + 1); + for (size_t i = old_size; i < __safelog_cache.size(); ++i) + __safelog_cache[i] = safelog(double(i)); + } +} + +void clear_safelog() +{ + vector().swap(__safelog_cache); +} + + +void init_xlogx(size_t x) +{ + size_t old_size = __xlogx_cache.size(); + if (x >= old_size) + { + __xlogx_cache.resize(x + 1); + for (size_t i = old_size; i < __xlogx_cache.size(); ++i) + __xlogx_cache[i] = i * safelog(i); + } +} + +void clear_xlogx() +{ + vector().swap(__xlogx_cache); +} + +void init_lgamma(size_t x) +{ + size_t old_size = __lgamma_cache.size(); + if (x >= old_size) + { + __lgamma_cache.resize(x + 1); + for (size_t i = old_size; i < __lgamma_cache.size(); ++i) + __lgamma_cache[i] = lgamma(i); + } +} + +void clear_lgamma() +{ + vector().swap(__lgamma_cache); +} + +void init_cache(size_t E) +{ + init_lgamma(2 * E); + init_xlogx(2 * E); + init_safelog(2 * E); +} + + +} // namespace graph_tool diff --git a/src/graph/inference/cache.hh b/src/graph/inference/cache.hh new file mode 100644 index 0000000000000000000000000000000000000000..66daa0f0da448046c8be0d735695b44f862888ac --- /dev/null +++ b/src/graph/inference/cache.hh @@ -0,0 +1,78 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef CACHE_HH +#define CACHE_HH + +#include "config.h" + +#include +#include + +namespace graph_tool +{ +using namespace std; + +// Repeated computation of x*log(x) and log(x) actually adds up to a lot of +// time. A significant speedup can be made by caching pre-computed values. + +extern vector __safelog_cache; +extern vector __xlogx_cache; +extern vector __lgamma_cache; + +void init_safelog(size_t x); + +template +inline double safelog(Type x) +{ + if (x == 0) + return 0; + return log(x); +} + +inline double safelog(size_t x) +{ + if (x >= __safelog_cache.size()) + init_safelog(x); + return __safelog_cache[x]; +} + +void init_xlogx(size_t x); + +inline double xlogx(size_t x) +{ + //return x * safelog(x); + if (x >= __xlogx_cache.size()) + init_xlogx(x); + return __xlogx_cache[x]; +} + +void init_lgamma(size_t x); + +inline double lgamma_fast(size_t x) +{ + //return lgamma(x); + if (x >= __lgamma_cache.size()) + init_lgamma(x); + return __lgamma_cache[x]; +} + +void init_cache(size_t E); + +} // graph_tool namespace + +#endif //CACHE_HH diff --git a/src/graph/inference/gibbs_loop.hh b/src/graph/inference/gibbs_loop.hh new file mode 100644 index 0000000000000000000000000000000000000000..f8a89ef7b0c26c69a3697f37edeb13149fc805d6 --- /dev/null +++ b/src/graph/inference/gibbs_loop.hh @@ -0,0 +1,181 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GIBBS_LOOP_HH +#define GIBBS_LOOP_HH + +#include "config.h" + +#include +#include + +#include + +#include "hash_map_wrap.hh" +#include "parallel_rng.hh" + +#ifdef USING_OPENMP +#include +#endif +namespace graph_tool +{ + +template +auto gibbs_sweep(GibbsState state, RNG& rng_) +{ + auto& g = state._g; + + vector> rngs; + std::vector> best_move; + + if (state._parallel) + { + init_rngs(rngs, rng_); + init_cache(state._E); + best_move.resize(num_vertices(g)); + } + + auto& vlist = state._vlist; + auto beta = state._beta; + + vector probs; + vector deltas; + vector idx; + + double S = 0; + size_t nmoves = 0; + + for (size_t iter = 0; iter < state._niter; ++iter) + { + if (!state._parallel) + { + std::shuffle(vlist.begin(), vlist.end(), rng_); + } + else + { + parallel_loop(vlist, + [&](auto v) + { + best_move[v] = + std::make_pair(state.node_state(v), + numeric_limits::max()); + }); + } + + size_t i = 0, N = vlist.size(); + #pragma omp parallel for default(shared) private(i) \ + firstprivate(state, probs, deltas, idx) \ + schedule(runtime) if (state._parallel) + for (i = 0; i < N; ++i) + { + auto& rng = get_rng(rngs, rng_); + + size_t v; + if (state._sequential) + { + v = vertex(vlist[i], g); + } + else + { + std::uniform_int_distribution v_rand(0, N - 1); + v = vertex(vlist[v_rand(rng)], g); + } + + vector& moves = state.get_moves(v); + auto& weights = state.get_weights(v); + + probs.resize(moves.size()); + deltas.resize(moves.size()); + idx.resize(moves.size()); + + double dS_min = numeric_limits::max(); + for (size_t j = 0; j < moves.size(); ++j) + { + size_t s = moves[j]; + double dS = state.virtual_move_dS(v, s); + dS_min = std::min(dS, dS_min); + deltas[j] = dS; + idx[j] = j; + } + + if (!isinf(beta)) + { + for (size_t j = 0; j < moves.size(); ++j) + { + if (std::isinf(deltas[j])) + probs[j] = 0; + else + probs[j] = exp((-deltas[j] + dS_min) * beta) * weights[j]; + } + } + else + { + for (size_t j = 0; j < moves.size(); ++j) + probs[j] = (deltas[j] == dS_min) ? weights[j] : 0; + } + + Sampler sampler(idx, probs); + + size_t j = sampler.sample(rng); + + assert(probs[j] > 0); + + size_t s = moves[j]; + size_t r = state.node_state(v); + + if (s == r) + continue; + + if (!state._parallel) + { + state.perform_move(v, s); + nmoves++; + S += deltas[j]; + } + else + { + best_move[v].first = s; + best_move[v].second = deltas[j]; + } + } + + if (state._parallel) + { + for (auto v : vlist) + { + auto s = best_move[v].first; + double dS = best_move[v].second; + if (dS != numeric_limits::max()) + { + dS = state.virtual_move_dS(v, s); + + if (dS > 0 && std::isinf(beta)) + continue; + + state.perform_move(v, s); + nmoves++; + S += dS; + } + } + } + } + return make_pair(S, nmoves); +} + +} // graph_tool namespace + +#endif //GIBBS_LOOP_HH diff --git a/src/graph/inference/graph_blockmodel.cc b/src/graph/inference/graph_blockmodel.cc new file mode 100644 index 0000000000000000000000000000000000000000..6a0fd977e7c20191a02614c4d3be03cf620022ed --- /dev/null +++ b/src/graph/inference/graph_blockmodel.cc @@ -0,0 +1,271 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#include "graph_state.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +python::object make_block_state(boost::python::object ostate, + rng_t& rng) +{ + python::object state; + block_state::make_dispatch(ostate, + [&](auto& s){state = python::object(s);}, + rng); + return state; +} + +degs_map_t get_block_degs(GraphInterface& gi, boost::any ab) +{ + degs_map_t degs; + vmap_t b = boost::any_cast(ab); + run_action<>()(gi, + [&](auto& g) + { + std::vector, + size_t>> hist; + for (auto v : vertices_range(g)) + { + size_t r = b[v]; + if (r >= hist.size()) + hist.resize(r + 1); + size_t kin = in_degreeS()(v, g); + size_t kout = out_degreeS()(v, g); + hist[r][std::make_tuple(kin, kout)]++; + } + + for (size_t r = 0; r < hist.size(); ++r) + { + auto& deg = degs[r]; + for (auto& kn : hist[r]) + deg.emplace_back(get<0>(kn.first), + get<1>(kn.first), + kn.second); + } + })(); + return degs; +} + +degs_map_t get_weighted_block_degs(GraphInterface& gi, degs_map_t& degs, + boost::any ab) +{ + degs_map_t ndegs; + vmap_t b = boost::any_cast(ab); + run_action<>()(gi, + [&](auto& g) + { + std::vector, + size_t>> hist; + for (auto v : vertices_range(g)) + { + size_t r = b[v]; + if (r >= hist.size()) + hist.resize(r + 1); + auto& h = hist[r]; + auto& ks = degs[v]; + for (auto& k : ks) + h[std::make_tuple(get<0>(k), get<1>(k))] += get<2>(k); + } + + for (size_t r = 0; r < hist.size(); ++r) + { + auto& deg = ndegs[r]; + for (auto& kn : hist[r]) + deg.emplace_back(get<0>(kn.first), + get<1>(kn.first), + kn.second); + } + })(); + return ndegs; +} + + +template +boost::any get_any(Prop& p) +{ + return any(p); +} + +void print_degs(degs_map_t& degs, size_t B) +{ + for (size_t r = 0; r < B; ++r) + { + cout << r << ":: "; + auto& ks = degs[r]; + for (auto& k : ks) + { + cout << "(" << get<0>(k) << ", " << get<1>(k) << "): " + << get<2>(k) << " "; + } + cout << endl; + } +} + +degs_map_t copy_degs(degs_map_t& degs) +{ + return degs.copy(); +} + +simple_degs_t copy_simple_degs(simple_degs_t& degs) +{ + return degs; +} + +boost::python::tuple bethe_entropy(GraphInterface& gi, size_t B, boost::any op, + boost::any opv) +{ + typedef vprop_map_t>::type vmap_t; + typedef eprop_map_t>::type emap_t; + emap_t p = any_cast(op); + vmap_t pv = any_cast(opv); + + double H=0, sH=0, Hmf=0, sHmf=0; + run_action() + (gi, + [&](auto& g) + { + for (auto v : vertices_range(g)) + { + pv[v].resize(B); + for (size_t i = 0; i < B; ++i) + pv[v][i] = 0; + } + + H = Hmf = sH = sHmf = 0; + + for (auto e : edges_range(g)) + { + auto u = min(source(e, g), target(e, g)); + auto v = max(source(e, g), target(e, g)); + + double sum = 0; + for (size_t r = 0; r < B; ++r) + for (size_t s = 0; s < B; ++s) + { + size_t i = r + B * s; + pv[u][r] += p[e][i]; + pv[v][s] += p[e][i]; + sum += p[e][i]; + } + + for (size_t i = 0; i < B * B; ++i) + { + if (p[e][i] == 0) + continue; + double pi = double(p[e][i]) / sum; + H -= pi * log(pi); + sH += pow((log(pi) + 1) * sqrt(pi / sum), 2); + } + } + + for (auto v : vertices_range(g)) + { + double sum = 0; + for (size_t i = 0; i < B; ++i) + sum += pv[v][i]; + for (size_t i = 0; i < B; ++i) + { + if (pv[v][i] == 0) + continue; + pv[v][i] /= sum; + double pi = pv[v][i]; + double kt = (1 - double(in_degreeS()(v, g)) - double(out_degree(v, g))); + if (kt != 0) + { + H -= kt * (pi * log(pi)); + sH += pow(kt * (log(pi) + 1) * sqrt(pi / sum), 2); + } + + Hmf -= pi * log(pi); + sHmf += pow((log(pi) + 1) * sqrt(pi / sum), 2); + } + } + })(); + + return boost::python::make_tuple(H, sH, Hmf, sHmf); +} + + +void export_blockmodel_state() +{ + using namespace boost::python; + + block_state::dispatch + ([&](auto* s) + { + typedef typename std::remove_reference::type state_t; + + double (state_t::*virtual_move)(size_t, size_t, bool, bool, bool, + bool, bool) = + &state_t::virtual_move; + size_t (state_t::*sample_block)(size_t, double, vector&, + rng_t&) + = &state_t::sample_block; + double (state_t::*get_move_prob)(size_t, size_t, size_t, double, + bool) + = &state_t::get_move_prob; + void (state_t::*merge_vertices)(size_t, size_t) + = &state_t::merge_vertices; + + class_ c(name_demangle(typeid(state_t).name()).c_str(), + no_init); + c.def("remove_vertex", &state_t::remove_vertex) + .def("add_vertex", &state_t::add_vertex) + .def("move_vertex", &state_t::move_vertex) + .def("virtual_move", virtual_move) + .def("merge_vertices", merge_vertices) + .def("sample_block", sample_block) + .def("entropy", &state_t::entropy) + .def("get_partition_dl", &state_t::get_partition_dl) + .def("get_deg_dl", &state_t::get_deg_dl) + .def("get_move_prob", get_move_prob) + .def("enable_partition_stats", + &state_t::enable_partition_stats) + .def("disable_partition_stats", + &state_t::disable_partition_stats) + .def("is_partition_stats_enabled", + &state_t::is_partition_stats_enabled); + }); + + class_("unity_vprop_t").def("_get_any", &get_any); + class_("unity_eprop_t").def("_get_any", &get_any); + + def("make_block_state", &make_block_state); + + class_("true_type"); + class_("false_type"); + + def("get_block_degs", &get_block_degs); + def("get_weighted_block_degs", &get_weighted_block_degs); + class_("degs_map_t") + .def("print", &print_degs) + .def("copy", ©_degs); + class_("simple_degs_t") + .def("copy", ©_simple_degs); + + def("bethe_entropy", &bethe_entropy); +} diff --git a/src/graph/inference/graph_blockmodel.hh b/src/graph/inference/graph_blockmodel.hh new file mode 100644 index 0000000000000000000000000000000000000000..da694332a10177978ac02f16ccd44d95a5bc6be6 --- /dev/null +++ b/src/graph/inference/graph_blockmodel.hh @@ -0,0 +1,1052 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_HH +#define GRAPH_BLOCKMODEL_HH + +#include "config.h" + +#include + +#include "graph_state.hh" +#include "graph_blockmodel_util.hh" + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +typedef vprop_map_t::type vmap_t; +typedef eprop_map_t::type emap_t; +typedef UnityPropertyMap vcmap_t; +typedef UnityPropertyMap ecmap_t; + +typedef mpl::vector2 eweight_tr; +typedef mpl::vector2 vweight_tr; +typedef mpl::vector2 use_hash_tr; +typedef mpl::vector2 degs_tr; + +#define BLOCK_STATE_params \ + ((g, &, all_graph_views, 1)) \ + ((degs,, degs_tr, 1)) \ + ((eweight,, eweight_tr, 1)) \ + ((vweight,, vweight_tr, 1)) \ + ((use_hash,, use_hash_tr, 1)) \ + ((_abg, &, boost::any&, 0)) \ + ((mrs,, emap_t, 0)) \ + ((mrp,, vmap_t, 0)) \ + ((mrm,, vmap_t, 0)) \ + ((wr,, vmap_t, 0)) \ + ((b,, vmap_t, 0)) \ + ((bclabel,, vmap_t, 0)) \ + ((pclabel,, vmap_t, 0)) \ + ((merge_map,, vmap_t, 0)) \ + ((deg_corr,, bool, 0)) \ + ((ignore_degree,, typename vprop_map_t::type, 0)) + +GEN_STATE_BASE(BlockStateBase, BLOCK_STATE_params) + +template +class BlockState + : public BlockStateBase +{ +public: + GET_PARAMS_USING(BlockStateBase, BLOCK_STATE_params) + GET_PARAMS_TYPEDEF(Ts, BLOCK_STATE_params) + + template * = nullptr> + BlockState(RNG& rng, ATs&&... args) + : BlockStateBase(std::forward(args)...), + _bg(boost::any_cast>(__abg)), + _c_mrs(_mrs.get_checked()), + _emat(_g, _b, _bg, rng), + _neighbour_sampler(get(vertex_index_t(), _g), num_vertices(_g)), + _m_entries(num_vertices(_bg)) + { + init_neighbour_sampler(_g, _eweight, _neighbour_sampler); + } + + BlockState(const BlockState& other) + : BlockStateBase(static_cast&>(other)), + _bg(boost::any_cast>(__abg)), + _c_mrs(_mrs.get_checked()), + _emat(other._emat), + _neighbour_sampler(other._neighbour_sampler), + _m_entries(num_vertices(_bg)) + { + if (other.is_partition_stats_enabled()) + enable_partition_stats(); + } + + // remove a vertex from its current block + void remove_vertex(size_t v) + { + typedef typename graph_traits::vertex_descriptor vertex_t; + + vertex_t r = _b[v]; + + int self_weight = 0; + for (auto e : out_edges_range(v, _g)) + { + vertex_t u = target(e, _g); + vertex_t s = _b[u]; + + auto& me = _emat.get_bedge(e); + + size_t ew = _eweight[e]; + if (u == v && !is_directed::apply::type::value) + { + self_weight += ew; + } + else + { + _mrs[me] -= ew; + + assert(_mrs[me] >= 0); + + _mrp[r] -= ew; + _mrm[s] -= ew; + + if (_mrs[me] == 0) + _emat.remove_me(r, s, me, _bg); + } + } + + if (self_weight > 0) + { + assert(self_weight % 2 == 0); + const auto& me = _emat.get_me(r, r); + _mrs[me] -= self_weight / 2; + _mrp[r] -= self_weight / 2; + _mrm[r] -= self_weight / 2; + assert(_mrs[me] >= 0); + if (_mrs[me] == 0) + _emat.remove_me(r, r, me, _bg); + } + + for (auto e : in_edges_range(v, _g)) + { + vertex_t u = source(e, _g); + if (u == v) + continue; + vertex_t s = _b[u]; + + auto& me = _emat.get_bedge(e); + + size_t ew = _eweight[e]; + _mrs[me] -= ew; + + _mrp[s] -= ew; + _mrm[r] -= ew; + + if (_mrs[me] == 0) + _emat.remove_me(s, r, me, _bg); + } + + _wr[r] -= _vweight[v]; + + if (!_egroups.empty()) + _egroups.remove_vertex(v, r, _g); + + if (is_partition_stats_enabled()) + get_partition_stats(v).remove_vertex(v, r, _deg_corr, _g, _vweight, + _eweight, _degs); + } + + // add a vertex to block r + void add_vertex(size_t v, size_t r) + { + typedef typename graph_traits::vertex_descriptor vertex_t; + typedef typename graph_traits::edge_descriptor bedge_t; + + int self_weight = 0; + for (auto e : out_edges_range(v, _g)) + { + vertex_t u = target(e, _g); + vertex_t s; + + if (u != v) + s = _b[u]; + else + s = r; + + auto me = _emat.get_me(r, s); + + if (me == bedge_t()) + { + me = add_edge(r, s, _bg).first; + _emat.put_me(r, s, me); + _c_mrs[me] = 0; + } + + _emat.get_bedge(e) = me; + + assert(_emat.get_bedge(e) != bedge_t()); + assert(me == _emat.get_me(r, s)); + + size_t ew = _eweight[e]; + + if (u == v && !is_directed::apply::type::value) + { + self_weight += ew; + } + else + { + _mrs[me] += ew; + _mrp[r] += ew; + _mrm[s] += ew; + } + } + + if (self_weight > 0) + { + assert(self_weight % 2 == 0); + const auto& me = _emat.get_me(r, r); + _mrs[me] += self_weight / 2; + _mrp[r] += self_weight / 2; + _mrm[r] += self_weight / 2; + assert(_mrs[me] >= 0); + } + + for (auto e : in_edges_range(v, _g)) + { + vertex_t u = source(e, _g); + if (u == v) + continue; + + vertex_t s = _b[u]; + + auto me = _emat.get_me(s, r); + + if (me == bedge_t()) + { + me = add_edge(s, r, _bg).first; + _emat.put_me(s, r, me); + _c_mrs[me] = 0; + } + _emat.get_bedge(e) = me; + + assert(_emat.get_bedge(e) != bedge_t()); + assert(me == _emat.get_me(s, r)); + + size_t ew = _eweight[e]; + + _mrs[me] += ew; + + _mrp[s] += ew; + _mrm[r] += ew; + } + + _wr[r] += _vweight[v]; + _b[v] = r; + + if (!_egroups.empty()) + _egroups.add_vertex(v, r, _eweight, _g); + + if (is_partition_stats_enabled()) + get_partition_stats(v).add_vertex(v, r, _deg_corr, _g, _vweight, + _eweight, _degs); + } + + // move a vertex from its current block to block nr + void move_vertex(size_t v, size_t nr) + { + size_t r = _b[v]; + if (r == nr) + return; + if (_bclabel[r] != _bclabel[nr]) + throw ValueException("cannot move vertex across clabel barriers"); + remove_vertex(v); + add_vertex(v, nr); + } + + size_t virtual_remove_size(size_t v) + { + return _wr[_b[v]] - _vweight[v]; + } + + // merge vertex u into v + void merge_vertices(size_t u, size_t v) + { + typedef typename graph_traits::edge_descriptor edge_t; + UnityPropertyMap dummy; + merge_vertices(u, v, dummy); + } + + template + void merge_vertices(size_t u, size_t v, Emap& ec) + { + if (u == v) + return; + merge_vertices(u, v, ec, + typename is_constant_property::type(), + typename is_constant_property::type()); + } + + template + void merge_vertices(size_t, size_t, Emap&, T1, T2) + { + throw ValueException("cannot merge vertices of unweighted graph"); + } + + template + void merge_vertices(size_t u, size_t v, Emap& ec, std::false_type, + std::false_type) + { + auto eweight_c = _eweight.get_checked(); + auto bedge_c = _emat.get_bedge_map().get_checked(); + + typedef typename graph_traits::vertex_descriptor vertex_t; + typedef typename graph_traits::edge_descriptor edge_t; + + gt_hash_map, vector> ns_u, ns_v; + for(auto e : out_edges_range(u, _g)) + ns_u[std::make_tuple(target(e, _g), ec[e])].push_back(e); + for(auto e : out_edges_range(v, _g)) + ns_v[std::make_tuple(target(e, _g), ec[e])].push_back(e); + + for(auto& kv : ns_u) + { + vertex_t t = get<0>(kv.first); + int l = get<1>(kv.first); + auto& es = kv.second; + + size_t w = 0; + for (auto& e : es) + w += _eweight[e]; + + if (t == u) + { + t = v; + if (!is_directed::apply::type::value) + { + assert(w % 2 == 0); + w /= 2; + } + } + + auto iter = ns_v.find(std::make_tuple(t, l)); + if (iter != ns_v.end()) + { + auto& e = iter->second.front(); + _eweight[e] += w; + } + else + { + auto e = add_edge(v, t, _g).first; + ns_v[std::make_tuple(t, l)].push_back(e); + eweight_c[e] = w; + bedge_c[e] = bedge_c[es.front()]; + set_prop(ec, e, l); + } + } + + if (is_directed::apply::type::value) + { + ns_u.clear(); + ns_v.clear(); + + for(auto e : in_edges_range(v, _g)) + ns_v[std::make_tuple(source(e, _g), ec[e])].push_back(e); + for(auto e : in_edges_range(u, _g)) + ns_u[std::make_tuple(source(e, _g), ec[e])].push_back(e); + + for(auto& kv : ns_u) + { + vertex_t s = get<0>(kv.first); + int l = get<1>(kv.first); + auto& es = kv.second; + + if (s == u) + continue; + + size_t w = 0; + for (auto& e : es) + w += _eweight[e]; + + auto iter = ns_v.find(std::make_tuple(s, l)); + if (iter != ns_v.end()) + { + auto& e = iter->second.front(); + _eweight[e] += w; + } + else + { + auto e = add_edge(s, v, _g).first; + ns_v[std::make_tuple(s, l)].push_back(e); + eweight_c[e] = w; + bedge_c[e] = bedge_c[es.front()]; + set_prop(ec, e, l); + } + } + } + + _vweight[v] +=_vweight[u]; + _vweight[u] = 0; + for (auto e : all_edges_range(u, _g)) + _eweight[e] = 0; + clear_vertex(u, _g); + _merge_map[u] = v; + merge_degs(u, v, _degs); + } + + template + void set_prop(EMap& ec, Edge& e, Val& val) + { + ec[e] = val; + } + + template + void set_prop(UnityPropertyMap&, Edge&, Val&) + { + } + + void merge_degs(size_t, size_t, const simple_degs_t&) {} + + void merge_degs(size_t u, size_t v, typename degs_map_t::unchecked_t& degs) + { + gt_hash_map, size_t> hist; + for (auto& kn : degs[u]) + hist[make_tuple(get<0>(kn), get<1>(kn))] += get<2>(kn); + for (auto& kn : degs[v]) + hist[make_tuple(get<0>(kn), get<1>(kn))] += get<2>(kn); + degs[u].clear(); + degs[v].clear(); + auto& d = degs[v]; + for (auto& kn : hist) + d.emplace_back(get<0>(kn.first), get<1>(kn.first), kn.second); + } + + // compute the entropy difference of a virtual move of vertex from block r to nr + template + double virtual_move_sparse(size_t v, size_t nr, MEntries& m_entries) + { + size_t r = _b[v]; + + if (r == nr) + return 0.; + + m_entries.clear(); + move_entries(v, nr, _b, _eweight, _mrs, _emat.get_bedge_map(), _g, _bg, + m_entries); + + size_t kout = out_degreeS()(v, _g, _eweight); + size_t kin = kout; + if (is_directed::apply::type::value) + kin = in_degreeS()(v, _g, _eweight); + + double dS = entries_dS(m_entries, _mrs, _emat, _bg); + + int dwr = _vweight[v]; + int dwnr = dwr; + + dS += vterm(_mrp[r] - kout, _mrm[r] - kin, _wr[r] - dwr , _deg_corr, _bg); + dS += vterm(_mrp[nr] + kout, _mrm[nr] + kin, _wr[nr] + dwnr, _deg_corr, _bg); + dS -= vterm(_mrp[r] , _mrm[r] , _wr[r] , _deg_corr, _bg); + dS -= vterm(_mrp[nr] , _mrm[nr] , _wr[nr] , _deg_corr, _bg); + + return dS; + } + + double virtual_move_sparse(size_t v, size_t nr) + { + return virtual_move_sparse(v, nr, _m_entries); + } + + template + double virtual_move_dense(size_t v, size_t nr, bool multigraph, + MEntries& m_entries) + { + if (_deg_corr) + throw GraphException("Dense entropy for degree corrected model not implemented!"); + + typedef typename graph_traits::vertex_descriptor vertex_t; + vertex_t r = _b[v]; + + if (r == nr) + return 0; + + // m_entries is not used in the computation below, but it is expected afterwards + m_entries.clear(); + int kin = 0, kout = 0; + move_entries(v, nr, _b, _eweight, _mrs, _emat.get_bedge_map(), _g, _bg, + m_entries); + kout += out_degreeS()(v, _g, _eweight); + if (is_directed::apply::type::value) + kin += in_degreeS()(v, _g, _eweight); + + vector deltap(num_vertices(_bg), 0); + int deltal = 0; + for (auto e : out_edges_range(v, _g)) + { + vertex_t u = target(e, _g); + vertex_t s = _b[u]; + if (u == v) + deltal += _eweight[e]; + else + deltap[s] += _eweight[e]; + } + if (!is_directed::apply::type::value) + deltal /= 2; + + vector deltam(num_vertices(_bg), 0); + for (auto e : in_edges_range(v, _g)) + { + vertex_t u = source(e, _g); + if (u == v) + continue; + vertex_t s = _b[u]; + deltam[s] += _eweight[e]; + } + + double dS = 0; + int dwr = _vweight[v]; + int dwnr = dwr; + + double Si = 0, Sf = 0; + for (vertex_t s = 0; s < num_vertices(_bg); ++s) + { + int ers = get_mrs(r, s, _mrs, _emat); + int enrs = get_mrs(nr, s, _mrs, _emat); + + if (!is_directed::apply::type::value) + { + if (s != nr && s != r) + { + Si += eterm_dense(r, s, ers, _wr[r], _wr[s], multigraph, _bg); + Sf += eterm_dense(r, s, ers - deltap[s], _wr[r] - dwr, _wr[s], multigraph, _bg); + Si += eterm_dense(nr, s, enrs, _wr[nr], _wr[s], multigraph, _bg); + Sf += eterm_dense(nr, s, enrs + deltap[s], _wr[nr] + dwnr, _wr[s], multigraph, _bg); + } + + if (s == r) + { + Si += eterm_dense(r, r, ers, _wr[r], _wr[r], multigraph, _bg); + Sf += eterm_dense(r, r, ers - deltap[r] - deltal, _wr[r] - dwr, _wr[r] - dwr, multigraph, _bg); + } + + if (s == nr) + { + Si += eterm_dense(nr, nr, enrs, _wr[nr], _wr[nr], multigraph, _bg); + Sf += eterm_dense(nr, nr, enrs + deltap[nr] + deltal, _wr[nr] + dwnr, _wr[nr] + dwnr, multigraph, _bg); + + Si += eterm_dense(r, nr, ers, _wr[r], _wr[nr], multigraph, _bg); + Sf += eterm_dense(r, nr, ers - deltap[nr] + deltap[r], _wr[r] - dwr, _wr[nr] + dwnr, multigraph, _bg); + } + } + else + { + int esr = get_mrs(s, r, _mrs, _emat); + int esnr = get_mrs(s, nr, _mrs, _emat); + + if (s != nr && s != r) + { + Si += eterm_dense(r, s, ers , _wr[r] , _wr[s] , multigraph, _bg); + Sf += eterm_dense(r, s, ers - deltap[s], _wr[r] - dwr, _wr[s] , multigraph, _bg); + Si += eterm_dense(s, r, esr , _wr[s] , _wr[r] , multigraph, _bg); + Sf += eterm_dense(s, r, esr - deltam[s], _wr[s] , _wr[r] - dwr, multigraph, _bg); + + Si += eterm_dense(nr, s, enrs , _wr[nr] , _wr[s] , multigraph, _bg); + Sf += eterm_dense(nr, s, enrs + deltap[s], _wr[nr] + dwnr, _wr[s] , multigraph, _bg); + Si += eterm_dense(s, nr, esnr , _wr[s] , _wr[nr] , multigraph, _bg); + Sf += eterm_dense(s, nr, esnr + deltam[s], _wr[s] , _wr[nr] + dwnr, multigraph, _bg); + } + + if(s == r) + { + Si += eterm_dense(r, r, ers , _wr[r] , _wr[r] , multigraph, _bg); + Sf += eterm_dense(r, r, ers - deltap[r] - deltam[r] - deltal, _wr[r] - dwr, _wr[r] - dwr, multigraph, _bg); + + Si += eterm_dense(r, nr, esnr , _wr[r] , _wr[nr] , multigraph, _bg); + Sf += eterm_dense(r, nr, esnr - deltap[nr] + deltam[r], _wr[r] - dwr, _wr[nr] + dwnr, multigraph, _bg); + } + + if(s == nr) + { + Si += eterm_dense(nr, nr, esnr , _wr[nr] , _wr[nr] , multigraph, _bg); + Sf += eterm_dense(nr, nr, esnr + deltap[nr] + deltam[nr] + deltal, _wr[nr] + dwnr, _wr[nr] + dwnr, multigraph, _bg); + + Si += eterm_dense(nr, r, esr , _wr[nr] , _wr[r] , multigraph, _bg); + Sf += eterm_dense(nr, r, esr + deltap[r] - deltam[nr], _wr[nr] + dwnr, _wr[r] - dwr, multigraph, _bg); + } + } + } + + return Sf - Si + dS; + } + + double virtual_move_dense(size_t v, size_t nr, bool multigraph) + { + return virtual_move_dense(v, nr, multigraph, _m_entries); + } + + template + double virtual_move(size_t v, size_t nr, bool dense, bool multigraph, + bool partition_dl, bool deg_dl, bool edges_dl, + MEntries& m_entries) + { + size_t r = _b[v]; + + if (_bclabel[r] != _bclabel[nr]) + return std::numeric_limits::infinity(); + + double dS; + if (dense) + dS = virtual_move_dense(v, nr, multigraph, m_entries); + else + dS = virtual_move_sparse(v, nr, m_entries); + + if (partition_dl || deg_dl || edges_dl) + { + enable_partition_stats(); + auto& ps = get_partition_stats(v); + if (partition_dl) + dS += ps.get_delta_dl(v, r, nr, _vweight); + if (_deg_corr && deg_dl) + dS += ps.get_delta_deg_dl(v, r, nr, _vweight, _eweight, + _degs, _g); + if (edges_dl) + dS += ps.get_delta_edges_dl(v, r, nr, _vweight, _g); + } + + return dS; + } + + double virtual_move(size_t v, size_t nr, bool dense, bool multigraph, + bool partition_dl, bool deg_dl, bool edges_dl) + { + return virtual_move(v, nr, dense, multigraph, partition_dl, deg_dl, + edges_dl, _m_entries); + } + + double get_delta_dl(size_t v, size_t nr) + { + enable_partition_stats(); + auto& ps = get_partition_stats(v); + return ps.get_delta_dl(v, _b[v], nr, _vweight); + } + + // Sample node placement + template + size_t sample_block(size_t v, double c, vector& block_list, + RNG& rng) + { + // attempt random block + size_t s = uniform_sample(block_list, rng); + + if (!std::isinf(c) && total_degreeS()(v, _g) > 0) + { + auto u = sample_neighbour(_neighbour_sampler[v], rng); + size_t t = _b[u]; + double p_rand = 0; + if (c > 0) + { + size_t B = num_vertices(_bg); + if (is_directed::apply::type::value) + p_rand = c * B / double(_mrp[t] + _mrm[t] + c * B); + else + p_rand = c * B / double(_mrp[t] + c * B); + } + + typedef std::uniform_real_distribution<> rdist_t; + if (c == 0 || rdist_t()(rng) >= p_rand) + { + if (_egroups.empty()) + _egroups.init(_b, _eweight, _g, _bg); + const auto& e = _egroups.sample_edge(t, rng); + s = _b[target(e, _g)]; + if (s == t) + s = _b[source(e, _g)]; + } + } + + return s; + } + + size_t sample_block(size_t v, double c, vector& block_list, + rng_t& rng) + { + return sample_block(v, c, block_list, rng); + } + + + template + size_t random_neighbour(size_t v, RNG& rng) + { + if (_neighbour_sampler[v].size() == 0) + return v; + return sample_neighbour(_neighbour_sampler[v], rng); + } + + // Computes the move proposal probability + template + double get_move_prob(size_t v, size_t r, size_t s, double c, bool reverse, + MEntries& m_entries) + { + typedef typename graph_traits::vertex_descriptor vertex_t; + size_t B = num_vertices(_bg); + double p = 0; + size_t w = 0; + + size_t kout = out_degreeS()(v, _g, _eweight); + size_t kin = kout; + if (is_directed::apply::type::value) + kin = in_degreeS()(v, _g, _eweight); + + for (auto e : all_edges_range(v, _g)) + { + vertex_t u = target(e, _g); + if (is_directed::apply::type::value && u == v) + u = source(e, _g); + vertex_t t = _b[u]; + if (u == v) + t = r; + size_t ew = _eweight[e]; + w += ew; + + int mts; + if (t == r && s == size_t(_b[u])) + mts = _mrs[_emat.get_bedge(e)]; + else + mts = get_mrs(t, s, _mrs, _emat); + int mtp = _mrp[t]; + int mst = mts; + int mtm = mtp; + + if (is_directed::apply::type::value) + { + mst = get_mrs(s, t, _mrs, _emat); + mtm = _mrm[t]; + } + + if (reverse) + { + int dts = m_entries.get_delta(t, s); + int dst = dts; + if (is_directed::apply::type::value) + dst = m_entries.get_delta(s, t); + + mts += dts; + mst += dst; + + if (t == s) + { + mtp -= kout; + mtm -= kin; + } + + if (t == r) + { + mtp += kout; + mtm += kin; + } + } + + if (is_directed::apply::type::value) + { + p += ew * ((mts + mst + c) / (mtp + mtm + c * B)); + } + else + { + if (t == s) + mts *= 2; + p += ew * (mts + c) / (mtp + c * B); + } + } + if (w > 0) + return p / w; + else + return 1. / B; + } + + double get_move_prob(size_t v, size_t r, size_t s, double c, bool reverse) + { + return get_move_prob(v, r, s, c, reverse, _m_entries); + } + + bool is_last(size_t v) + { + return _wr[_b[v]] == _vweight[v]; + } + + double get_deg_entropy(size_t v, const simple_degs_t&) + { + if (_ignore_degree[v] == 1) + return 0; + auto kin = in_degreeS()(v, _g); + auto kout = out_degreeS()(v, _g); + if (_ignore_degree[v] == 2) + kout = 0; + double S = -lgamma_fast(kin + 1) - lgamma_fast(kout + 1); + return S * _vweight[v]; + } + + double get_deg_entropy(size_t v, typename degs_map_t::unchecked_t& degs) + { + if (_ignore_degree[v] == 1) + return 0; + double S = 0; + for (auto& ks : degs[v]) + { + auto kin = get<0>(ks); + auto kout = get<1>(ks); + if (_ignore_degree[v] == 2) + kout = 0; + int n = get<2>(ks); + S -= n * (lgamma_fast(kin + 1) + lgamma_fast(kout + 1)); + } + return S; + } + + double sparse_entropy(bool multigraph, bool deg_entropy) + { + double S = 0; + for (auto e : edges_range(_bg)) + S += eterm(source(e, _bg), target(e, _bg), _mrs[e], _bg); + for (auto v : vertices_range(_bg)) + S += vterm(_mrp[v], _mrm[v], _wr[v], _deg_corr, _bg); + + if (_deg_corr && deg_entropy) + { + for (auto v : vertices_range(_g)) + S += get_deg_entropy(v, _degs); + } + + if (multigraph) + { + for (auto v : vertices_range(_g)) + { + gt_hash_map us; + for (auto e : out_edges_range(v, _g)) + { + auto u = target(e, _g); + if (u < v && !is_directed::apply::type::value) + continue; + us[u] += _eweight[e]; + } + + for (auto& uc : us) + { + auto& u = uc.first; + auto& m = uc.second; + if (m > 1) + { + if (u == v && !is_directed::apply::type::value) + { + assert(m % 2 == 0); + S += lgamma_fast(m/2 + 1); + } + else + { + S += lgamma_fast(m + 1); + } + } + } + } + } + return S; + } + + double dense_entropy(bool multigraph) + { + if (_deg_corr) + throw GraphException("Dense entropy for degree corrected model not implemented!"); + double S = 0; + for (auto e : edges_range(_bg)) + { + auto r = source(e, _bg); + auto s = target(e, _bg); + S += eterm_dense(r, s, _mrs[e], _wr[r], _wr[s], multigraph, _bg); + } + return S; + } + + double entropy(bool dense, bool multigraph, bool deg_entropy) + { + if (dense) + return dense_entropy(multigraph); + else + return sparse_entropy(multigraph, deg_entropy); + } + + double get_partition_dl() + { + enable_partition_stats(); + double S = 0; + for (auto& ps : _partition_stats) + S += ps.get_partition_dl(); + return S; + } + + double get_deg_dl(bool ent, bool dl_alt, bool xi_fast) + { + enable_partition_stats(); + double S = 0; + for (auto& ps : _partition_stats) + S += ps.get_deg_dl(ent, dl_alt, xi_fast); + return S; + } + + template + double get_parallel_neighbours_entropy(size_t v, Vlist& us) + { + double S = 0; + for (auto& uc : us) + { + auto& u = uc.first; + auto& m = uc.second; + if (m > 1) + { + if (u == v && !is_directed::apply::type::value) + { + assert(m % 2 == 0); + S += lgamma_fast(m/2 + 1); + } + else + { + S += lgamma_fast(m + 1); + } + } + } + return S; + } + + double get_parallel_entropy() + { + double S = 0; + for (auto v : vertices_range(_g)) + { + gt_hash_map us; + for (auto e : out_edges_range(v, _g)) + { + auto u = target(e, _g); + if (u < v && !is_directed::apply::type::value) + continue; + us[u] += _eweight[e]; + } + S += get_parallel_neighbours_entropy(v, us); + } + return S; + } + + void enable_partition_stats() + { + if (_partition_stats.empty()) + { + size_t E = 0; + for (auto e : edges_range(_g)) + E += _eweight[e]; + size_t B = 0; + for (auto r : vertices_range(_bg)) + if (_wr[r] > 0) + B++; + + auto vi = std::max_element(vertices(_g).first, vertices(_g).second, + [&](auto u, auto v) + { return (this->_pclabel[u] < + this->_pclabel[v]); }); + size_t C = _pclabel[*vi] + 1; + + vector> vcs(C); + vector rc(num_vertices(_bg)); + for (auto v : vertices_range(_g)) + { + vcs[_pclabel[v]].push_back(v); + rc[_b[v]] = _pclabel[v]; + } + + for (size_t c = 0; c < C; ++c) + _partition_stats.emplace_back(_g, _b, vcs[c], E, B, + _vweight, _eweight, _degs, + _ignore_degree, _bmap); + + for (auto r : vertices_range(_bg)) + _partition_stats[rc[r]].get_r(r); + } + } + + void disable_partition_stats() + { + _partition_stats.clear(); + } + + bool is_partition_stats_enabled() const + { + return !_partition_stats.empty(); + } + + partition_stats_t& get_partition_stats(size_t v) + { + return _partition_stats[_pclabel[v]]; + } + + void init_mcmc(double c, double dl) + { + if (!std::isinf(c)) + { + if (_egroups.empty()) + _egroups.init(_b, _eweight, _g, _bg); + } + else + { + _egroups.clear(); + } + + if (dl) + enable_partition_stats(); + else + disable_partition_stats(); + } + + +//private: + typedef typename + std::conditional::type::value, + GraphInterface::multigraph_t, + UndirectedAdaptor>::type + bg_t; + bg_t& _bg; + + typename mrs_t::checked_t _c_mrs; + + typedef typename std::conditional, + EMat>::type + emat_t; + emat_t _emat; + + typedef typename is_constant_property::type is_weighted; + EGroups _egroups; + + typedef typename std::conditional, + typename property_map::type>::type, + typename property_map_type::apply, + typename property_map::type>::type>::type::unchecked_t + sampler_map_t; + + sampler_map_t _neighbour_sampler; + std::vector _partition_stats; + std::vector _bmap; + + EntrySet _m_entries; +}; + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_HH diff --git a/src/graph/inference/graph_blockmodel_gibbs.cc b/src/graph/inference/graph_blockmodel_gibbs.cc new file mode 100644 index 0000000000000000000000000000000000000000..8cf35ccdd496f6e7bf8a700ccb9249f04e4acdcc --- /dev/null +++ b/src/graph/inference/graph_blockmodel_gibbs.cc @@ -0,0 +1,63 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#include "graph_blockmodel_gibbs.hh" +#include "gibbs_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(gibbs_block_state, Gibbs::template GibbsBlockState, + GIBBS_BLOCK_STATE_params(State)) + +python::object do_gibbs_sweep(python::object ogibbs_state, + python::object oblock_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + gibbs_block_state::make_dispatch + (ogibbs_state, + [&](auto& s) + { + auto ret_ = gibbs_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_blockmodel_gibbs() +{ + using namespace boost::python; + def("gibbs_sweep", &do_gibbs_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_gibbs.hh b/src/graph/inference/graph_blockmodel_gibbs.hh new file mode 100644 index 0000000000000000000000000000000000000000..811fb30f4719cc1ecefd4bb14f9e6e9d986c432f --- /dev/null +++ b/src/graph/inference/graph_blockmodel_gibbs.hh @@ -0,0 +1,209 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_GIBBS_HH +#define GRAPH_BLOCKMODEL_GIBBS_HH + +#include "config.h" + +#include + +#include "graph_tool.hh" +#include "graph_state.hh" +#include "graph_blockmodel_util.hh" +#include + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +#define GIBBS_BLOCK_STATE_params(State) \ + ((__class__,&, mpl::vector, 1)) \ + ((state, &, State&, 0)) \ + ((E,, size_t, 0)) \ + ((vlist,&, std::vector&, 0)) \ + ((block_list,&, std::vector&, 0)) \ + ((beta,, double, 0)) \ + ((multigraph,, bool, 0)) \ + ((dense,, bool, 0)) \ + ((partition_dl,, bool, 0)) \ + ((degree_dl,, bool, 0)) \ + ((edges_dl,, bool, 0)) \ + ((allow_empty,, bool, 0)) \ + ((parallel,, bool, 0)) \ + ((sequential,, bool, 0)) \ + ((verbose,, bool, 0)) \ + ((niter,, size_t, 0)) + + +template class MEntries = EntrySet> +struct Gibbs +{ + GEN_STATE_BASE(GibbsBlockStateBase, GIBBS_BLOCK_STATE_params(State)) + + template + class GibbsBlockState + : public GibbsBlockStateBase + { + public: + GET_PARAMS_USING(GibbsBlockStateBase, + GIBBS_BLOCK_STATE_params(State)) + GET_PARAMS_TYPEDEF(Ts, GIBBS_BLOCK_STATE_params(State)) + + template * = nullptr> + GibbsBlockState(ATs&&... as) + : GibbsBlockStateBase(as...), + _g(_state._g), + _m_entries(num_vertices(_state._bg)), + _B(num_vertices(_state._bg)), + _tglobal(std::make_shared()) + { + _state.init_mcmc(numeric_limits::infinity(), + _partition_dl || _degree_dl || _edges_dl); + + size_t C = 0; + for (auto v : vertices_range(_g)) + C = std::max(C, _state._bclabel[_state._b[v]]); + C++; + + auto& moves = _tglobal->moves; + auto& weights = _tglobal->weights; + auto& empty = _tglobal->empty; + + empty.resize(C); + + for(size_t c = 0; c < C; ++c) + { + moves.push_back(_B + c); + weights.push_back(0); + } + + for (auto r : _block_list) + { + if (_state._wr[r] > 0) + { + moves.push_back(r); + weights.push_back(1); + } + else + { + auto c = _state._bclabel[r]; + empty[c].push_back(r); + } + } + + for (size_t c = 0; c < C; ++c) + weights[c] = empty[c].size(); + } + + typename state_t::g_t& _g; + MEntries _m_entries; + + size_t _B; + + struct tglobal_t + { + vector moves; + vector weights; + vector> empty; + }; + + std::shared_ptr _tglobal; + + auto& get_moves(size_t) { return _tglobal->moves; } + auto& get_weights(size_t) { return _tglobal->weights; } + + size_t node_state(size_t v) + { + return _state._b[v]; + } + + double virtual_move_dS(size_t v, size_t nr) + { + if (nr >= _B) + { + if (_allow_empty) + { + auto& empty = _tglobal->empty; + auto c = nr - _B; + if (empty[c].empty()) + return numeric_limits::infinity(); + nr = empty[c].back(); + } + else + { + return numeric_limits::infinity(); + } + } + size_t r = _state._b[v]; + if (_state._bclabel[r] != _state._bclabel[nr]) + return numeric_limits::infinity(); + return _state.virtual_move(v, nr, _dense, _multigraph, + _partition_dl, _degree_dl, _edges_dl, + _m_entries); + } + + void perform_move(size_t v, size_t nr) + { + size_t r = _state._b[v]; + + if (r == nr) + return; + + auto& moves = _tglobal->moves; + auto& weights = _tglobal->weights; + auto& empty = _tglobal->empty; + + if (nr >= _B) + { + auto c = nr - _B; + assert(!empty[c].empty()); + nr = empty[c].back(); + empty[c].pop_back(); + weights[c]--; + moves.push_back(nr); + weights.push_back(1); + } + + assert(_state._wr[r] > 0); + + _state.move_vertex(v, nr); + + if (_state._wr[r] == 0) + { + auto c = _state._bclabel[r]; + empty[c].push_back(r); + weights[c]++; + + auto iter = find(moves.begin(), moves.end(), r); + assert(iter != moves.end()); + size_t pos = iter - moves.begin(); + std::swap(moves[pos], moves.back()); + moves.pop_back(); + weights.pop_back(); + } + } + }; +}; + + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_GIBBS_HH diff --git a/src/graph/inference/graph_blockmodel_layers.cc b/src/graph/inference/graph_blockmodel_layers.cc new file mode 100644 index 0000000000000000000000000000000000000000..218efa3a81e5197394cfc392e575aa2c6e8b0d11 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers.cc @@ -0,0 +1,495 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#include "graph_blockmodel_layers_util.hh" +#define BASE_STATE_params BLOCK_STATE_params +#include "graph_blockmodel_layers.hh" +#include "graph_state.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +python::object make_layered_block_state(boost::python::object oblock_state, + boost::python::object olayered_state) +{ + python::object state; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + layered_block_state::make_dispatch + (olayered_state, + [&](auto& s) + { + state = python::object(s); + }, + block_state); + }; + block_state::dispatch(oblock_state, dispatch); + return state; +} + +template +vector from_list(boost::python::object list) +{ + vector v; + for (int i = 0; i < boost::python::len(list); ++i) + v.push_back(boost::python::extract(list[i])()); + return v; +}; + +template +vector> from_rlist(boost::python::object list) +{ + vector> v; + for (int i = 0; i < boost::python::len(list); ++i) + v.emplace_back(boost::python::extract(list[i])()); + return v; +}; + +template +vector> from_any_list(boost::python::object list) +{ + vector> v; + for (int i = 0; i < boost::python::len(list); ++i) + v.emplace_back(any_cast(boost::python::extract(list[i])())); + return v; +}; + +void split_layers(GraphInterface& gi, boost::any& aec, boost::any& ab, + boost::any& aeweight, boost::any& avweight, + boost::any& avc, boost::any& avmap, boost::any& alweight, + boost::python::object& ous, boost::python::object& oub, + boost::python::object& oueweight, + boost::python::object& ouvweight, vbmap_t& block_map, + boost::python::object& obrmap, + boost::python::object& ouvmap) +{ + typedef vprop_map_t::type vmap_t; + typedef vprop_map_t>::type vvmap_t; + typedef eprop_map_t::type emap_t; + + emap_t& ec = any_cast(aec); + vmap_t& b = any_cast(ab); + vmap_t& vweight = any_cast(avweight); + emap_t& eweight = any_cast(aeweight); + vvmap_t& vc = any_cast(avc); + vvmap_t& vmap = any_cast(avmap); + vvmap_t& lweight = any_cast(alweight); + + auto us = from_rlist(ous); + auto ub = from_any_list(oub); + auto uvweight = from_any_list(ouvweight); + auto ueweight = from_any_list(oueweight); + + auto block_rmap = from_any_list(obrmap); + auto uvmap = from_any_list(ouvmap); + + run_action<>()(gi, + [&](auto& g) + { + std::vector> + vhmap(num_vertices(g)); + + typename vprop_map_t>::type + lw(get(vertex_index, g)); + for (auto v : vertices_range(g)) + { + for (size_t i = 0; i < lweight[v].size(); i += 2) + { + auto l = lweight[v][i]; + auto w = lweight[v][i + 1]; + lw[v][l] = w; + } + } + + auto get_v = [&] (size_t v, size_t l) -> size_t + { + auto iter = vhmap[v].find(l); + if (iter == vhmap[v].end()) + { + size_t u = add_vertex(us[l].get().get_graph()); + vhmap[v][l] = u; + size_t pos = lower_bound(vc[v].begin(), vc[v].end(), l) - vc[v].begin(); + vc[v].insert(vc[v].begin() + pos, l); + vmap[v].insert(vmap[v].begin() + pos, u); + uvmap[l].get()[u] = v; + if (lw[v].empty()) + uvweight[l].get()[u] = vweight[v]; + else + uvweight[l].get()[u] = lw[v][l]; + size_t r = b[v]; + size_t u_r; + + if (block_map.size() <= l) + block_map.resize(l + 1); + + auto& bmap = block_map[l]; + auto riter = bmap.find(r); + if (riter == bmap.end()) + { + u_r = bmap.size(); + bmap[r] = u_r; + block_rmap[l].get()[u_r] = r; + } + else + { + u_r = riter->second; + } + ub[l].get()[u] = u_r; + return u; + } + else + { + return iter->second; + } + }; + + for (auto e : edges_range(g)) + { + auto s = source(e, g); + auto t = target(e, g); + size_t l = ec[e]; + + auto u_s = get_v(s, l); + auto u_t = get_v(t, l); + auto ne = add_edge(u_s, u_t, us[l].get().get_graph()).first; + ueweight[l].get()[ne] = eweight[e]; + } + })(); +} + + +void get_lweights(GraphInterface& gi, boost::any& avc, boost::any& avmap, + boost::any& alweight, boost::python::object& ouvweight) +{ + typedef vprop_map_t::type vmap_t; + typedef vprop_map_t>::type vvmap_t; + + vvmap_t& vc = any_cast(avc); + vvmap_t& vmap = any_cast(avmap); + vvmap_t& lweight = any_cast(alweight); + + auto uvweight = from_any_list(ouvweight); + + run_action<>()(gi, [&](auto& g) + { + for (auto v : vertices_range(g)) + { + for (size_t i = 0; i < vc[v].size(); ++i) + { + auto l = vc[v][i]; + auto u = vmap[v][i]; + auto w = uvweight[l].get()[u]; + lweight[v].push_back(l); + lweight[v].push_back(w); + } + } + })(); +} + +void get_blweights(GraphInterface& gi, boost::any& ab, boost::any& avc, + boost::any& avmap, boost::any& alweight, + boost::python::object& ouvweight) +{ + typedef vprop_map_t::type vmap_t; + typedef vprop_map_t>::type vvmap_t; + + vmap_t& b = any_cast(ab); + vvmap_t& vc = any_cast(avc); + vvmap_t& vmap = any_cast(avmap); + vvmap_t& lweight = any_cast(alweight); + + auto uvweight = from_any_list(ouvweight); + + run_action<>()(gi, [&](auto& g) + { + gt_hash_map> blw; + for (auto v : vertices_range(g)) + { + auto r = b[v]; + for (size_t i = 0; i < vc[v].size(); ++i) + { + auto l = vc[v][i]; + auto u = vmap[v][i]; + auto w = uvweight[l].get()[u]; + blw[r][l] += w; + } + } + for (auto& rlw : blw) + { + auto r = rlw.first; + for (auto& lw : rlw.second) + { + auto l = lw.first; + auto w = lw.second; + lweight[r].push_back(l); + lweight[r].push_back(w); + } + } + })(); +} + + +bool bmap_has(const vbmap_t& bmap, size_t c, size_t r) +{ + if (c > bmap.size()) + throw GraphException("invalid covariate value:" + lexical_cast(c)); + auto iter = bmap[c].find(r); + if (iter == bmap[c].end()) + return false; + return true; +} + +size_t bmap_get(const vbmap_t& bmap, size_t c, size_t r) +{ + if (c > bmap.size()) + throw GraphException("invalid covariate value:" + lexical_cast(c)); + auto iter = bmap[c].find(r); + if (iter == bmap[c].end()) + throw GraphException("no mapping for block " + lexical_cast(r) + + " in layer " + lexical_cast(c)); + return iter->second; +} + +void bmap_set(vbmap_t& bmap, size_t c, size_t r, size_t r_u) +{ + if (c > bmap.size()) + throw GraphException("invalid covariate value:" + lexical_cast(c)); + bmap[c][r] = r_u; +} + +void bmap_del_c(vbmap_t& bmap, size_t c) +{ + if (c > bmap.size()) + throw GraphException("invalid covariate value:" + lexical_cast(c)); + bmap.erase(bmap.begin() + c); +} + +vbmap_t bmap_copy(const vbmap_t& bmap) +{ + return bmap; +} + +size_t bmap_size(const vbmap_t& bmap) +{ + return bmap.size(); +} + +typedef gt_hash_map, + gt_hash_map, size_t>> + ldegs_map_t; + +ldegs_map_t get_layered_block_degs(GraphInterface& gi, boost::any aeweight, + boost::any avweight, boost::any aec, + boost::any ab) +{ + ldegs_map_t degs; + vmap_t b = boost::any_cast(ab); + emap_t eweight = boost::any_cast(aeweight); + vmap_t vweight = boost::any_cast(avweight); + emap_t ec = boost::any_cast(aec); + run_action<>()(gi, + [&](auto& g) + { + for (auto v : vertices_range(g)) + { + gt_hash_map kin, kout; + gt_hash_set ls; + + for (auto e : out_edges_range(v, g)) + { + auto w = eweight[e]; + auto l = ec[e]; + kout[l] += w; + ls.insert(l); + } + + for (auto e : in_edges_range(v, g)) + { + auto w = eweight[e]; + auto l = ec[e]; + kin[l] += w; + ls.insert(l); + } + + for (auto l : ls) + { + size_t skin = 0, skout = 0; + auto iter = kin.find(l); + if (iter != kin.end()) + skin = iter->second; + iter = kout.find(l); + if (iter != kout.end()) + skout = iter->second; + auto& h = degs[std::make_tuple(l + 1, b[v])]; + h[std::make_tuple(skin, skout)] += vweight[v]; + } + + size_t skin = in_degreeS()(v, g, eweight); + size_t skout = out_degreeS()(v, g, eweight); + auto& h = degs[std::make_tuple(0, b[v])]; + h[std::make_tuple(skin, skout)] += vweight[v]; + } + })(); + return degs; +} + +degs_map_t get_mapped_block_degs(GraphInterface& gi, ldegs_map_t& ldegs, + int l, boost::any avmap) +{ + degs_map_t ndegs; + vmap_t vmap = boost::any_cast(avmap); + run_action<>()(gi, + [&](auto& g) + { + for (auto u : vertices_range(g)) + { + int v = vmap[u]; + auto& d = ndegs[u]; + for (auto& ks : ldegs[std::make_tuple(l, v)]) + d.emplace_back(get<0>(ks.first), get<1>(ks.first), + ks.second); + } + })(); + return ndegs; +} + +ldegs_map_t get_ldegs(GraphInterface& gi, boost::any& avc, boost::any& avmap, + boost::python::object& oudegs) +{ + typedef vprop_map_t>::type vvmap_t; + + vvmap_t& vc = any_cast(avc); + vvmap_t& vmap = any_cast(avmap); + + auto udegs = from_rlist(oudegs); + + ldegs_map_t ndegs; + run_action<>()(gi, [&](auto& g) + { + gt_hash_map> blw; + for (int v : vertices_range(g)) + { + auto& d = udegs[0].get()[v]; + auto& h = ndegs[std::make_tuple(0, v)]; + for (auto& kn : d) + h[std::make_tuple(get<0>(kn), get<1>(kn))] = + get<2>(kn); + + for (size_t i = 0; i < vc[v].size(); ++i) + { + int l = vc[v][i]; + auto u = vmap[v][i]; + auto& d = udegs[l + 1].get()[u]; + auto& h = ndegs[std::make_tuple(l + 1, v)]; + for (auto& kn : d) + h[std::make_tuple(get<0>(kn), get<1>(kn))] = + get<2>(kn); + } + } + })(); + return ndegs; +} + + +ldegs_map_t ldegs_map_copy(ldegs_map_t& ldegs) +{ + return ldegs; +} + + +void export_layered_blockmodel_state() +{ + using namespace boost::python; + + block_state::dispatch + ([&](auto* bs) + { + typedef typename std::remove_reference::type block_state_t; + + layered_block_state::dispatch + ([&](auto* s) + { + typedef typename std::remove_reference::type state_t; + + double (state_t::*virtual_move)(size_t, size_t, bool, bool, bool, + bool, bool) = + &state_t::virtual_move; + size_t (state_t::*sample_block)(size_t, double, vector&, + rng_t&) + = &state_t::sample_block; + double (state_t::*get_move_prob)(size_t, size_t, size_t, double, + bool) + = &state_t::get_move_prob; + void (state_t::*merge_vertices)(size_t, size_t) + = &state_t::merge_vertices; + + class_ c(name_demangle(typeid(state_t).name()).c_str(), + no_init); + c.def("remove_vertex", &state_t::remove_vertex) + .def("add_vertex", &state_t::add_vertex) + .def("move_vertex", &state_t::move_vertex) + .def("virtual_move", virtual_move) + .def("merge_vertices", merge_vertices) + .def("sample_block", sample_block) + .def("entropy", &state_t::entropy) + .def("get_partition_dl", &state_t::get_partition_dl) + .def("get_deg_dl", &state_t::get_deg_dl) + .def("get_move_prob", get_move_prob) + .def("enable_partition_stats", + &state_t::enable_partition_stats) + .def("disable_partition_stats", + &state_t::disable_partition_stats) + .def("is_partition_stats_enabled", + &state_t::is_partition_stats_enabled); + }); + }); + + def("make_layered_block_state", &make_layered_block_state); + def("split_layers", &split_layers); + def("get_layered_block_degs", &get_layered_block_degs); + def("get_mapped_block_degs", &get_mapped_block_degs); + def("get_ldegs", &get_ldegs); + def("get_lweights", &get_lweights); + def("get_blweights", &get_blweights); + + class_("ldegs_map_t") + .def("copy", &ldegs_map_copy); + + class_("bmap_t") + .def("has", bmap_has) + .def("get", bmap_get) + .def("set", bmap_set) + .def("del_c", bmap_del_c) + .def("copy", bmap_copy) + .def("size", bmap_size); +} diff --git a/src/graph/inference/graph_blockmodel_layers.hh b/src/graph/inference/graph_blockmodel_layers.hh new file mode 100644 index 0000000000000000000000000000000000000000..515cca695dd67a62333f152631e452f48f8f06c3 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers.hh @@ -0,0 +1,419 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_LAYERS_HH +#define GRAPH_BLOCKMODEL_LAYERS_HH + +#include "config.h" + +#include + +#include "graph_state.hh" +#include "graph_blockmodel_layers_util.hh" + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +typedef eprop_map_t::type emap_t; +typedef vprop_map_t>::type vcvmap_t; + +typedef gt_hash_map bmap_t; +typedef std::vector vbmap_t; + +#define LAYERED_BLOCK_STATE_params \ + ((__class__,&, mpl::vector, 1)) \ + ((layer_states,, python::object, 0)) \ + ((ec,, emap_t, 0)) \ + ((vc,, vcvmap_t, 0)) \ + ((vmap,, vcvmap_t, 0)) \ + ((block_map, &, vbmap_t&, 0)) \ + ((master,, bool, 0)) + +template +struct Layers +{ + GEN_STATE_BASE(LayeredBlockStateBase, LAYERED_BLOCK_STATE_params) + + template + class LayeredBlockState + : public LayeredBlockStateBase, + public BaseState + { + public: + GET_PARAMS_USING(LayeredBlockStateBase, + LAYERED_BLOCK_STATE_params) + GET_PARAMS_TYPEDEF(Ts, LAYERED_BLOCK_STATE_params) + + GET_PARAMS_USING(BaseState, BASE_STATE_params) + using BaseState::_bg; + using BaseState::_m_entries; + using BaseState::_emat; + using BaseState::_partition_stats; + using BaseState::is_partition_stats_enabled; + + typedef vprop_map_t::type block_rmap_t; + + class LayerState + : public BaseState + { + public: + LayerState(const BaseState& base_state, bmap_t& block_map, + block_rmap_t block_rmap, vector& free_blocks) + : BaseState(base_state), + _block_map(block_map), + _block_rmap(block_rmap), + _free_blocks(free_blocks), + _E(0) + { + for (auto e : edges_range(BaseState::_g)) + _E += BaseState::_eweight[e]; + } + + bmap_t& _block_map; + block_rmap_t _block_rmap; + vector& _free_blocks; + size_t _E; + + size_t get_block_map(size_t r, bool put_new=true) + { + size_t r_u; + auto iter = _block_map.find(r); + if (iter == _block_map.end()) + { + if (_free_blocks.empty()) + { + r_u = _block_map.size(); + } + else + { + r_u = _free_blocks.back(); + if (put_new) + _free_blocks.pop_back(); + } + if (put_new) + { + _block_map[r] = r_u; + _block_rmap[r_u] = r; + } + } + else + { + r_u = iter->second; + } + assert(r_u < num_vertices(_bg)); + return r_u; + } + + void remove_block_map(size_t r, bool free_block=true) + { + auto iter = _block_map.find(r); + if (free_block) + _free_blocks.push_back(iter->second); + _block_map.erase(iter); + } + + void put_block_map(size_t r, size_t r_u) + { + _block_map[r] = r_u; + } + + bool has_block_map(size_t r) + { + return _block_map.find(r) != _block_map.end(); + } + }; + + template * = nullptr> + LayeredBlockState(const BaseState& base_state, ATs&&... args) + : LayeredBlockStateBase(std::forward(args)...), + BaseState(base_state), _total_B(0), + _is_partition_stats_enabled(false) + { + for (int i = 0; i < python::len(_layer_states); ++i) + { + auto ostate = _layer_states[i]; + BaseState& state = python::extract(ostate.attr("_state")); + boost::python::object temp = ostate.attr("block_rmap").attr("_get_any")(); + boost::any& a = python::extract(temp); + block_rmap_t block_rmap = boost::any_cast(a); + std::vector& free_blocks = + python::extract&>(ostate.attr("free_blocks")); + bmap_t& block_map = _block_map[i]; + _layers.emplace_back(state, block_map, block_rmap, free_blocks); + } + for (auto r : vertices_range(BaseState::_bg)) + if (BaseState::_wr[r] > 0) + _total_B++; + } + + std::vector _layers; + size_t _total_B; + bool _is_partition_stats_enabled; + + //TODO: remove_vertex and add_vertex, etc. + + void move_vertex(size_t v, size_t s) + { + if (BaseState::_vweight[v] == 0) + { + _b[v] = s; + return; + } + + size_t r = _b[v]; + + if (r == s) + return; + + if (_wr[s] == 0) + _total_B++; + + BaseState::move_vertex(v, s); + + if (_wr[r] == 0) + _total_B--; + + auto& ls = _vc[v]; + auto& vs = _vmap[v]; + for (size_t j = 0; j < ls.size(); ++j) + { + int l = ls[j]; + size_t u = vs[j]; + + auto& state = _layers[l]; + size_t r_u = state._b[u]; + + assert(r_u < num_vertices(state._bg)); + + if (state.virtual_remove_size(u) == 0 && !state.has_block_map(s)) + { + state.remove_block_map(r, false); + state.put_block_map(s, r_u); + } + else + { + size_t s_u = state.get_block_map(s); + state.move_vertex(u, s_u); + if (state._wr[r_u] == 0) + state.remove_block_map(r); + } + } + } + + template + double virtual_move(size_t v, size_t s, bool dense, bool multigraph, + bool partition_dl, bool deg_dl, bool edges_dl, + MEntries& m_entries) + { + size_t r = _b[v]; + + if (s == r) + return 0; + + double dS = 0; + + if (_master) + { + dS += BaseState::virtual_move(v, s, dense, multigraph, + partition_dl, deg_dl, false, + m_entries); + dS -= virtual_move_covariate(v, s, *this, m_entries, false); + } + else + { + if (partition_dl) + { + enable_partition_stats(); + dS += BaseState::get_delta_dl(v, s); + } + } + + if (edges_dl) + dS += get_delta_edges_dl(v, s); + + auto& ls = _vc[v]; + auto& vs = _vmap[v]; + for (size_t j = 0; j < ls.size(); ++j) + { + size_t l = ls[j]; + size_t u = vs[j]; + + auto& state = _layers[l]; + + size_t s_u = state.get_block_map(s, false); + + if (_master) + dS += virtual_move_covariate(u, s_u, state, m_entries, + true); + else + dS += state.virtual_move(u, s_u, dense, multigraph, false, + deg_dl, false, m_entries); + + } + return dS; + } + + double virtual_move(size_t v, size_t s, bool dense, bool multigraph, + bool partition_dl, bool deg_dl, bool edges_dl) + { + return virtual_move(v, s, dense, multigraph, partition_dl, deg_dl, + edges_dl, _m_entries); + } + + double get_delta_edges_dl(size_t v, size_t s) + { + if (BaseState::_vweight[v] == 0) + return 0; + int dB = 0; + if (BaseState::virtual_remove_size(v) == 0) + --dB; + if (_wr[s] == 0) + ++dB; + double S_a = 0, S_b = 0; + if (dB != 0) + { + auto get_x = [](size_t B) + { + if (is_directed::apply::type::value) + return B * B; + else + return (B * (B + 1)) / 2; + }; + + for (auto& state : _layers) + { + S_b += lbinom(get_x(_total_B) + state._E - 1, state._E); + S_a += lbinom(get_x(_total_B + dB) + state._E - 1, state._E); + } + } + return S_a - S_b; + } + + void merge_vertices(size_t u, size_t v) + { + std::set ls; + gt_hash_map ls_u, ls_v; + for (size_t i = 0; i < _vc[u].size(); ++i) + { + size_t l = _vc[u][i]; + ls_u[l] = _vmap[u][i]; + ls.insert(l); + } + + for (size_t i = 0; i < _vc[v].size(); ++i) + { + size_t l = _vc[v][i]; + ls_v[l] = _vmap[v][i]; + ls.insert(l); + } + + _vc[u].clear(); + _vmap[u].clear(); + _vc[v].clear(); + _vmap[v].clear(); + + for (auto l : ls) + { + auto iter_u = ls_u.find(l); + auto iter_v = ls_v.find(l); + + size_t uu = (iter_u != ls_u.end()) ? iter_u->second : iter_v->second; + size_t vv = (iter_v != ls_v.end()) ? iter_v->second : iter_u->second; + + _layers[l].merge_vertices(uu, vv); + + _vc[v].push_back(l); + _vmap[v].push_back(vv); + } + + auto ec = _ec.get_checked(); + BaseState::merge_vertices(u, v, ec); + } + + double entropy(bool dense, bool multigraph, bool deg_entropy) + { + double S = 0; + if (_master) + { + S += BaseState::entropy(dense, multigraph, deg_entropy); + S -= covariate_entropy(_bg, _mrs); + if (multigraph) + S -= BaseState::get_parallel_entropy(); + for (auto& state : _layers) + { + S += covariate_entropy(state._bg, state._mrs); + if (multigraph) + S += state.get_parallel_entropy(); + } + } + else + { + for (auto& state : _layers) + S += state.entropy(dense, multigraph, deg_entropy); + } + return S; + } + + double get_deg_dl(bool ent, bool dl_alt, bool xi_fast) + { + if (_master) + { + return BaseState::get_deg_dl(ent, dl_alt, xi_fast); + } + else + { + double S = 0; + for (auto& state : _layers) + S += state.get_deg_dl(ent, dl_alt, xi_fast); + return S; + } + } + + void enable_partition_stats() + { + if (!_is_partition_stats_enabled) + { + BaseState::enable_partition_stats(); + for (auto& state : _layers) + state.enable_partition_stats(); + _is_partition_stats_enabled = true; + } + } + + void disable_partition_stats() + { + BaseState::disable_partition_stats(); + for (auto& state : _layers) + state.disable_partition_stats(); + _is_partition_stats_enabled = false; + } + + void init_mcmc(double c, double dl) + { + BaseState::init_mcmc(c, dl); + for (auto& state : _layers) + state.init_mcmc(numeric_limits::infinity(), dl); + } + }; +}; + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_LAYERS_HH diff --git a/src/graph/inference/graph_blockmodel_layers_gibbs.cc b/src/graph/inference/graph_blockmodel_layers_gibbs.cc new file mode 100644 index 0000000000000000000000000000000000000000..8cb41854b920bc81ea0177adaa8ffd141abba331 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_gibbs.cc @@ -0,0 +1,78 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#define BASE_STATE_params BLOCK_STATE_params +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_gibbs.hh" +#include "gibbs_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(gibbs_block_state, Gibbs::template GibbsBlockState, + GIBBS_BLOCK_STATE_params(State)) + +python::object gibbs_layered_sweep(python::object ogibbs_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + gibbs_block_state::make_dispatch + (ogibbs_state, + [&](auto& s) + { + auto ret_ = gibbs_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + block_state::dispatch(dispatch); + return ret; +} + +void export_layered_blockmodel_gibbs() +{ + using namespace boost::python; + def("gibbs_layered_sweep", &gibbs_layered_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_mcmc.cc b/src/graph/inference/graph_blockmodel_layers_mcmc.cc new file mode 100644 index 0000000000000000000000000000000000000000..e08efaa1ebe696424c1ee2c6b8be2bcced9b57e7 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_mcmc.cc @@ -0,0 +1,78 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#define BASE_STATE_params BLOCK_STATE_params +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_mcmc.hh" +#include "mcmc_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(mcmc_block_state, MCMC::template MCMCBlockState, + MCMC_BLOCK_STATE_params(State)) + +python::object mcmc_layered_sweep(python::object omcmc_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + mcmc_block_state::make_dispatch + (omcmc_state, + [&](auto& s) + { + auto ret_ = mcmc_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + block_state::dispatch(dispatch); + return ret; +} + +void export_layered_blockmodel_mcmc() +{ + using namespace boost::python; + def("mcmc_layered_sweep", &mcmc_layered_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_merge.cc b/src/graph/inference/graph_blockmodel_layers_merge.cc new file mode 100644 index 0000000000000000000000000000000000000000..e7cd47d2adfc7da727eb1b2c78551109cd4e7ea4 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_merge.cc @@ -0,0 +1,78 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#define BASE_STATE_params BLOCK_STATE_params +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_merge.hh" +#include "merge_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(merge_block_state, Merge::template MergeBlockState, + MERGE_BLOCK_STATE_params(State)) + +python::object merge_layered_sweep(python::object omerge_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + merge_block_state::make_dispatch + (omerge_state, + [&](auto& s) + { + auto ret_ = merge_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + block_state::dispatch(dispatch); + return ret; +} + +void export_layered_blockmodel_merge() +{ + using namespace boost::python; + def("merge_layered_sweep", &merge_layered_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_multicanonical.cc b/src/graph/inference/graph_blockmodel_layers_multicanonical.cc new file mode 100644 index 0000000000000000000000000000000000000000..d1b0c318fdc0286408630f88888c04610a8def51 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_multicanonical.cc @@ -0,0 +1,79 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#define BASE_STATE_params BLOCK_STATE_params +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_multicanonical.hh" +#include "multicanonical_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(multicanonical_block_state, + Multicanonical::template MulticanonicalBlockState, + MULTICANONICAL_BLOCK_STATE_params(State)) + +python::object multicanonical_layered_sweep(python::object omulticanonical_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + multicanonical_block_state::make_dispatch + (omulticanonical_state, + [&](auto& s) + { + auto ret_ = multicanonical_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + block_state::dispatch(dispatch); + return ret; +} + +void export_layered_blockmodel_multicanonical() +{ + using namespace boost::python; + def("multicanonical_layered_sweep", &multicanonical_layered_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_overlap.cc b/src/graph/inference/graph_blockmodel_layers_overlap.cc new file mode 100644 index 0000000000000000000000000000000000000000..d0826e3a34c3b68f0bf14f4b753f2cd46a92a768 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_overlap.cc @@ -0,0 +1,106 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#include "graph_blockmodel_layers_util.hh" +#define BASE_STATE_params OVERLAP_BLOCK_STATE_params ((eweight,,,0)) +#include "graph_blockmodel_layers.hh" +#include "graph_state.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +python::object +make_layered_overlap_block_state(boost::python::object oblock_state, + boost::python::object olayered_state) +{ + python::object state; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + layered_block_state::make_dispatch + (olayered_state, + [&](auto& s) + { + state = python::object(s); + }, + block_state); + }; + overlap_block_state::dispatch(oblock_state, dispatch); + return state; +} + +void export_layered_overlap_blockmodel_state() +{ + using namespace boost::python; + + overlap_block_state::dispatch + ([&](auto* bs) + { + typedef typename std::remove_reference::type block_state_t; + + layered_block_state::dispatch + ([&](auto* s) + { + typedef typename std::remove_reference::type state_t; + + double (state_t::*virtual_move)(size_t, size_t, bool, bool, bool, + bool, bool) = + &state_t::virtual_move; + size_t (state_t::*sample_block)(size_t, double, vector&, + rng_t&) + = &state_t::sample_block; + double (state_t::*get_move_prob)(size_t, size_t, size_t, double, + bool) + = &state_t::get_move_prob; + + class_ c(name_demangle(typeid(state_t).name()).c_str(), + no_init); + c.def("remove_vertex", &state_t::remove_vertex) + .def("add_vertex", &state_t::add_vertex) + .def("move_vertex", &state_t::move_vertex) + .def("virtual_move", virtual_move) + .def("sample_block", sample_block) + .def("entropy", &state_t::entropy) + .def("get_partition_dl", &state_t::get_partition_dl) + .def("get_deg_dl", &state_t::get_deg_dl) + .def("get_move_prob", get_move_prob) + .def("enable_partition_stats", + &state_t::enable_partition_stats) + .def("disable_partition_stats", + &state_t::disable_partition_stats) + .def("is_partition_stats_enabled", + &state_t::is_partition_stats_enabled); + }); + }); + + def("make_layered_overlap_block_state", &make_layered_overlap_block_state); +} diff --git a/src/graph/inference/graph_blockmodel_layers_overlap_gibbs.cc b/src/graph/inference/graph_blockmodel_layers_overlap_gibbs.cc new file mode 100644 index 0000000000000000000000000000000000000000..3b56958fb06e8c5cfecbea7ee18b81e4e361d930 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_overlap_gibbs.cc @@ -0,0 +1,78 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#define BASE_STATE_params OVERLAP_BLOCK_STATE_params ((eweight,,,0)) +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_gibbs.hh" +#include "gibbs_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(gibbs_block_state, Gibbs::template GibbsBlockState, + GIBBS_BLOCK_STATE_params(State)) + +python::object gibbs_layered_overlap_sweep(python::object ogibbs_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + gibbs_block_state::make_dispatch + (ogibbs_state, + [&](auto& s) + { + auto ret_ = gibbs_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + overlap_block_state::dispatch(dispatch); + return ret; +} + +void export_layered_overlap_blockmodel_gibbs() +{ + using namespace boost::python; + def("gibbs_layered_overlap_sweep", &gibbs_layered_overlap_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_overlap_mcmc.cc b/src/graph/inference/graph_blockmodel_layers_overlap_mcmc.cc new file mode 100644 index 0000000000000000000000000000000000000000..e9605894781441d85c01534f9621af135c38373b --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_overlap_mcmc.cc @@ -0,0 +1,78 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#define BASE_STATE_params OVERLAP_BLOCK_STATE_params ((eweight,,,0)) +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_mcmc.hh" +#include "mcmc_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(mcmc_block_state, MCMC::template MCMCBlockState, + MCMC_BLOCK_STATE_params(State)) + +python::object mcmc_layered_overlap_sweep(python::object omcmc_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + mcmc_block_state::make_dispatch + (omcmc_state, + [&](auto& s) + { + auto ret_ = mcmc_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + overlap_block_state::dispatch(dispatch); + return ret; +} + +void export_layered_overlap_blockmodel_mcmc() +{ + using namespace boost::python; + def("mcmc_layered_overlap_sweep", &mcmc_layered_overlap_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_overlap_mcmc_bundled.cc b/src/graph/inference/graph_blockmodel_layers_overlap_mcmc_bundled.cc new file mode 100644 index 0000000000000000000000000000000000000000..9e77457032ba996f852453790c084adb374e88b4 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_overlap_mcmc_bundled.cc @@ -0,0 +1,80 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#define BASE_STATE_params OVERLAP_BLOCK_STATE_params ((eweight,,,0)) +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_overlap_mcmc_bundled.hh" +#include "mcmc_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(bundled_mcmc_overlap_block_state, + MCMC::template BundledMCMCOverlapBlockState, + BUNDLED_MCMC_OVERLAP_BLOCK_STATE_params(State)) + +python::object mcmc_layered_overlap_bundled_sweep(python::object omcmc_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + bundled_mcmc_overlap_block_state::make_dispatch + (omcmc_state, + [&](auto& s) + { + auto ret_ = mcmc_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + overlap_block_state::dispatch(dispatch); + return ret; +} + +void export_layered_overlap_blockmodel_bundled_mcmc() +{ + using namespace boost::python; + def("mcmc_layered_overlap_bundled_sweep", + &mcmc_layered_overlap_bundled_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_overlap_multicanonical.cc b/src/graph/inference/graph_blockmodel_layers_overlap_multicanonical.cc new file mode 100644 index 0000000000000000000000000000000000000000..102069dc26aa549d8f5c41411a504e5563e0dc3a --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_overlap_multicanonical.cc @@ -0,0 +1,81 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#define BASE_STATE_params OVERLAP_BLOCK_STATE_params ((eweight,,,0)) +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_multicanonical.hh" +#include "multicanonical_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(multicanonical_block_state, + Multicanonical::template MulticanonicalBlockState, + MULTICANONICAL_BLOCK_STATE_params(State)) + +python::object +multicanonical_layered_overlap_sweep(python::object omulticanonical_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + multicanonical_block_state::make_dispatch + (omulticanonical_state, + [&](auto& s) + { + auto ret_ = multicanonical_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + overlap_block_state::dispatch(dispatch); + return ret; +} + +void export_layered_overlap_blockmodel_multicanonical() +{ + using namespace boost::python; + def("multicanonical_layered_overlap_sweep", + &multicanonical_layered_overlap_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_overlap_vacate.cc b/src/graph/inference/graph_blockmodel_layers_overlap_vacate.cc new file mode 100644 index 0000000000000000000000000000000000000000..630e73bb4de3adffe8d0bcb6bc14e33bb6ffe947 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_overlap_vacate.cc @@ -0,0 +1,79 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#define BASE_STATE_params OVERLAP_BLOCK_STATE_params ((eweight,,,0)) +#include "graph_blockmodel_layers.hh" +#include "graph_blockmodel_overlap_vacate.hh" +#include "bundled_vacate_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(layered_block_state, Layers::template LayeredBlockState, + LAYERED_BLOCK_STATE_params) + +template +GEN_DISPATCH(merge_overlap_block_state, + Merge::template MergeOverlapBlockState, + MERGE_OVERLAP_BLOCK_STATE_params(State)) + +python::object vacate_layered_overlap_sweep(python::object ovacate_state, + python::object olayered_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto* block_state) + { + typedef typename std::remove_pointer::type + state_t; + + layered_block_state::dispatch + (olayered_state, + [&](auto& ls) + { + typedef typename std::remove_reference::type + layered_state_t; + + merge_overlap_block_state::make_dispatch + (ovacate_state, + [&](auto& s) + { + auto ret_ = bundled_vacate_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }, + false); + }; + overlap_block_state::dispatch(dispatch); + return ret; +} + +void export_layered_overlap_blockmodel_vacate() +{ + using namespace boost::python; + def("vacate_layered_overlap_sweep", &vacate_layered_overlap_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_layers_util.hh b/src/graph/inference/graph_blockmodel_layers_util.hh new file mode 100644 index 0000000000000000000000000000000000000000..36ca058d5cbf1f7568dc28c67c270bb42e931a64 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_layers_util.hh @@ -0,0 +1,73 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_LAYERS_UTIL_HH +#define GRAPH_BLOCKMODEL_LAYERS_UTIL_HH + +#include "config.h" + +#include + +namespace graph_tool +{ +using namespace boost; +using namespace std; + + +template +double virtual_move_covariate(size_t v, size_t s, State& state, + MEntries& m_entries, bool reset) +{ + if (reset) + { + m_entries.clear(); + move_entries(v, s, state._b, state._eweight, state._mrs, + state._emat.get_bedge_map(), state._g, state._bg, + m_entries); + } + + auto& entries = m_entries.get_entries(); + auto& delta = m_entries.get_delta(); + + double dS = 0; + for (size_t i = 0; i < entries.size(); ++i) + { + auto& entry = entries[i]; + auto er = entry.first; + auto es = entry.second; + int d = delta[i]; + + int ers = get_mrs(er, es, state._mrs, state._emat); + assert(ers + d >= 0); + dS -= -lgamma_fast(ers + 1); + dS += -lgamma_fast(ers + d + 1); + } + return dS; +} + +template +double covariate_entropy(Graph& bg, EMap& mrs) +{ + double S = 0; + for (auto e : edges_range(bg)) + S -= lgamma_fast(mrs[e] + 1); + return S; +} + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_LAYERS_UTIL_HH diff --git a/src/graph/inference/graph_blockmodel_mcmc.cc b/src/graph/inference/graph_blockmodel_mcmc.cc new file mode 100644 index 0000000000000000000000000000000000000000..750d30eb5cb12d05492da6a86b090166dd1f1ff0 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_mcmc.cc @@ -0,0 +1,63 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#include "graph_blockmodel_mcmc.hh" +#include "mcmc_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(mcmc_block_state, MCMC::template MCMCBlockState, + MCMC_BLOCK_STATE_params(State)) + +python::object do_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::type + state_t; + + mcmc_block_state::make_dispatch + (omcmc_state, + [&](auto& s) + { + auto ret_ = mcmc_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_blockmodel_mcmc() +{ + using namespace boost::python; + def("mcmc_sweep", &do_mcmc_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_mcmc.hh b/src/graph/inference/graph_blockmodel_mcmc.hh new file mode 100644 index 0000000000000000000000000000000000000000..ef8c46f3e43bfe993d80c63ddd39919ebcfb9954 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_mcmc.hh @@ -0,0 +1,132 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_MCMC_HH +#define GRAPH_BLOCKMODEL_MCMC_HH + +#include "config.h" + +#include + +#include "graph_tool.hh" +#include "graph_state.hh" +#include "graph_blockmodel_util.hh" +#include + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +#define MCMC_BLOCK_STATE_params(State) \ + ((__class__,&, mpl::vector, 1)) \ + ((state, &, State&, 0)) \ + ((E,, size_t, 0)) \ + ((vlist,&, std::vector&, 0)) \ + ((block_list,&, std::vector&, 0)) \ + ((beta,, double, 0)) \ + ((c,, double, 0)) \ + ((multigraph,, bool, 0)) \ + ((dense,, bool, 0)) \ + ((partition_dl,, bool, 0)) \ + ((degree_dl,, bool, 0)) \ + ((edges_dl,, bool, 0)) \ + ((allow_empty,, bool, 0)) \ + ((parallel,, bool, 0)) \ + ((sequential,, bool, 0)) \ + ((verbose,, bool, 0)) \ + ((niter,, size_t, 0)) + + +template class MEntries = EntrySet> +struct MCMC +{ + GEN_STATE_BASE(MCMCBlockStateBase, MCMC_BLOCK_STATE_params(State)) + + template + class MCMCBlockState + : public MCMCBlockStateBase + { + public: + GET_PARAMS_USING(MCMCBlockStateBase, + MCMC_BLOCK_STATE_params(State)) + GET_PARAMS_TYPEDEF(Ts, MCMC_BLOCK_STATE_params(State)) + + template * = nullptr> + MCMCBlockState(ATs&&... as) + : MCMCBlockStateBase(as...), + _g(_state._g), + _m_entries(num_vertices(_state._bg)) + { + _state.init_mcmc(_c, _partition_dl || _degree_dl || _edges_dl); + } + + typename state_t::g_t& _g; + MEntries _m_entries; + + size_t node_state(size_t v) + { + return _state._b[v]; + } + + template + size_t move_proposal(size_t v, RNG& rng) + { + auto r = _state._b[v]; + + if (!_allow_empty && _state.is_last(v)) + return r; + + size_t s = _state.sample_block(v, _c, _block_list, rng); + + if (_state._bclabel[s] != _state._bclabel[r]) + return r; + + return s; + } + + std::pair virtual_move_dS(size_t v, size_t nr) + { + double dS = _state.virtual_move(v, nr, _dense, _multigraph, + _partition_dl, _degree_dl, + _edges_dl, _m_entries); + double a = 0; + if (!std::isinf(_c)) + { + size_t r = _state._b[v]; + double pf = _state.get_move_prob(v, r, nr, _c, false, + _m_entries); + double pb = _state.get_move_prob(v, nr, r, _c, true, + _m_entries); + a = log(pb) - log(pf); + } + return make_pair(dS, a); + } + + void perform_move(size_t v, size_t nr) + { + _state.move_vertex(v, nr); + } + }; +}; + + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_MCMC_HH diff --git a/src/graph/inference/graph_blockmodel_merge.cc b/src/graph/inference/graph_blockmodel_merge.cc new file mode 100644 index 0000000000000000000000000000000000000000..555cd4460f1f3c7b960fbb9b5f3eaabb3df66b4a --- /dev/null +++ b/src/graph/inference/graph_blockmodel_merge.cc @@ -0,0 +1,63 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#include "graph_blockmodel_merge.hh" +#include "merge_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(merge_block_state, Merge::template MergeBlockState, + MERGE_BLOCK_STATE_params(State)) + +python::object do_merge_sweep(python::object omerge_state, + python::object oblock_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + merge_block_state::make_dispatch + (omerge_state, + [&](auto& s) + { + auto ret_ = merge_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_blockmodel_merge() +{ + using namespace boost::python; + def("merge_sweep", &do_merge_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_merge.hh b/src/graph/inference/graph_blockmodel_merge.hh new file mode 100644 index 0000000000000000000000000000000000000000..1278c90bf6b4ab56eb7cd148f5125c2daf2ce630 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_merge.hh @@ -0,0 +1,157 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_MERGE_HH +#define GRAPH_BLOCKMODEL_MERGE_HH + +#include "config.h" + +#include + +#include "graph_tool.hh" +#include "graph_state.hh" +#include "graph_blockmodel_util.hh" +#include + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +typedef vprop_map_t::type vmap_t; + +#define MERGE_BLOCK_STATE_params(State) \ + ((__class__, &, mpl::vector, 1)) \ + ((state, &, State&, 0)) \ + ((E,, size_t, 0)) \ + ((multigraph,, bool, 0)) \ + ((dense,, bool, 0)) \ + ((partition_dl,, bool, 0)) \ + ((degree_dl,, bool, 0)) \ + ((edges_dl,, bool, 0)) \ + ((parallel,, bool, 0)) \ + ((verbose,, bool, 0)) \ + ((niter,, size_t, 0)) \ + ((nmerges,, size_t, 0)) + +template +struct Merge +{ + GEN_STATE_BASE(MergeBlockStateBase, MERGE_BLOCK_STATE_params(State)) + + template + class MergeBlockState + : public MergeBlockStateBase + { + public: + GET_PARAMS_USING(MergeBlockStateBase, + MERGE_BLOCK_STATE_params(State)) + GET_PARAMS_TYPEDEF(Ts, MERGE_BLOCK_STATE_params(State)) + + template * = nullptr> + MergeBlockState(ATs&&... as) + : MergeBlockStateBase(as...), + _g(_state._g), + _m_entries(num_vertices(_state._bg)), + _null_move(numeric_limits::max()) + { + _state._egroups.clear(); + + if (_partition_dl || _degree_dl || _edges_dl) + _state.enable_partition_stats(); + else + _state.disable_partition_stats(); + + for (auto v : vertices_range(_state._g)) + { + if (_state._vweight[v] > 0) + _available.push_back(v); + } + } + + typename state_t::g_t& _g; + EntrySet _m_entries; + const size_t _null_move; + vector _available; + + size_t node_state(size_t v) + { + return _state._b[v]; + } + + template + size_t move_proposal(size_t v, bool random, RNG& rng) + { + size_t s; + if (!random) + { + size_t r = _state.random_neighbour(v, rng); + s = _state.random_neighbour(r, rng); + } + else + { + s = uniform_sample(_available, rng); + } + + if (s == v || _state._bclabel[v] != _state._bclabel[s]) + return _null_move; + + return s; + } + + double virtual_move_dS(size_t v, size_t nr) + { + return _state.virtual_move(v, nr, _dense, _multigraph, + _partition_dl, _degree_dl, _edges_dl, + _m_entries); + } + + void perform_merge(size_t r, size_t s) + { + //assert(_state._vweight[r] > 0 && _state._vweight[s] > 0); + _state.move_vertex(r, s); + _state.merge_vertices(r, s); + } + + size_t get_root(size_t s) + { + int r = s; + while (_state._merge_map[r] != r) + r = _state._merge_map[r]; + _state._merge_map[s] = r; + return r; + }; + + bool is_empty_vertex(size_t r) + { + return _state._vweight[r] == 0; + } + + void finalize() + { + for (auto v : vertices_range(_state._g)) + get_root(v); + } + }; +}; + + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_MERGE_HH diff --git a/src/graph/inference/graph_blockmodel_multicanonical.cc b/src/graph/inference/graph_blockmodel_multicanonical.cc new file mode 100644 index 0000000000000000000000000000000000000000..c1a7c9bc25699c489c525a3d237fb1cab8d89f94 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_multicanonical.cc @@ -0,0 +1,64 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel.hh" +#include "graph_blockmodel_multicanonical.hh" +#include "multicanonical_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(block_state, BlockState, BLOCK_STATE_params) + +template +GEN_DISPATCH(multicanonical_block_state, + Multicanonical::template MulticanonicalBlockState, + MULTICANONICAL_BLOCK_STATE_params(State)) + +python::object do_multicanonical_sweep(python::object omulticanonical_state, + python::object oblock_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + multicanonical_block_state::make_dispatch + (omulticanonical_state, + [&](auto& s) + { + auto ret_ = multicanonical_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_blockmodel_multicanonical() +{ + using namespace boost::python; + def("multicanonical_sweep", &do_multicanonical_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_multicanonical.hh b/src/graph/inference/graph_blockmodel_multicanonical.hh new file mode 100644 index 0000000000000000000000000000000000000000..4d3b529e2a8a75a80c622a47ed3f3805540eb7b7 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_multicanonical.hh @@ -0,0 +1,133 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_MULTICANONICAL_HH +#define GRAPH_BLOCKMODEL_MULTICANONICAL_HH + +#include "config.h" + +#include + +#include "graph_tool.hh" +#include "graph_state.hh" +#include "graph_blockmodel_util.hh" +#include + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +#define MULTICANONICAL_BLOCK_STATE_params(State) \ + ((__class__,&, mpl::vector, 1)) \ + ((state, &, State&, 0)) \ + ((hist, &, std::vector&, 0)) \ + ((dens, &, std::vector&, 0)) \ + ((S_min, , double, 0)) \ + ((S_max, , double, 0)) \ + ((f, , double, 0)) \ + ((S, , double, 0)) \ + ((E,, size_t, 0)) \ + ((vlist,&, std::vector&, 0)) \ + ((block_list,&, std::vector&, 0)) \ + ((c,, double, 0)) \ + ((multigraph,, bool, 0)) \ + ((dense,, bool, 0)) \ + ((partition_dl,, bool, 0)) \ + ((degree_dl,, bool, 0)) \ + ((edges_dl,, bool, 0)) \ + ((allow_empty,, bool, 0)) \ + ((verbose,, bool, 0)) \ + ((niter,, size_t, 0)) + + +template +struct Multicanonical +{ + GEN_STATE_BASE(MulticanonicalBlockStateBase, + MULTICANONICAL_BLOCK_STATE_params(State)) + + template + class MulticanonicalBlockState + : public MulticanonicalBlockStateBase + { + public: + GET_PARAMS_USING(MulticanonicalBlockStateBase, + MULTICANONICAL_BLOCK_STATE_params(State)) + GET_PARAMS_TYPEDEF(Ts, MULTICANONICAL_BLOCK_STATE_params(State)) + + template * = nullptr> + MulticanonicalBlockState(ATs&&... as) + : MulticanonicalBlockStateBase(as...), + _g(_state._g) + { + _state.init_mcmc(_c, _partition_dl || _degree_dl || _edges_dl); + } + + typename state_t::g_t& _g; + + size_t node_state(size_t v) + { + return _state._b[v]; + } + + template + size_t move_proposal(size_t v, RNG& rng) + { + auto r = _state._b[v]; + + size_t s = _state.sample_block(v, _c, _block_list, rng); + + if (_state._bclabel[s] != _state._bclabel[r]) + return r; + + if (!_allow_empty && _state.virtual_remove_size(v) == 0) + return r; + + return s; + } + + std::pair virtual_move_dS(size_t v, size_t nr) + { + double dS = _state.virtual_move(v, nr, _dense, _multigraph, + _partition_dl, _degree_dl, + _edges_dl); + + double a = 0; + if (!std::isinf(_c)) + { + size_t r = _state._b[v]; + double pf = _state.get_move_prob(v, r, nr, _c, false); + double pb = _state.get_move_prob(v, nr, r, _c, true); + a = log(pb) - log(pf); + } + return make_pair(dS, a); + } + + void perform_move(size_t v, size_t nr) + { + _state.move_vertex(v, nr); + } + }; +}; + + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_MULTICANONICAL_HH diff --git a/src/graph/inference/graph_blockmodel_overlap.cc b/src/graph/inference/graph_blockmodel_overlap.cc new file mode 100644 index 0000000000000000000000000000000000000000..943d3e822fa7a9fccc52a72e490b8531977c7211 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap.cc @@ -0,0 +1,258 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#include "graph_state.hh" + +using namespace boost; +using namespace graph_tool; + +constexpr size_t overlap_stats_t::_null; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +python::object make_overlap_block_state(boost::python::object ostate, + rng_t& rng) +{ + python::object state; + overlap_block_state::make_dispatch(ostate, + [&](auto& s){state = python::object(s);}, + rng); + return state; +} + +void get_eg_overlap(GraphInterface& gi, GraphInterface& egi, boost::any obe, + boost::any ob, boost::any onode_index, + boost::any ohalf_edges, boost::any oeindex) +{ + typedef vprop_map_t::type vmap_t; + typedef vprop_map_t::type vimap_t; + typedef vprop_map_t>::type vvmap_t; + typedef eprop_map_t>::type evmap_t; + typedef eprop_map_t::type emap_t; + + vmap_t b = any_cast(ob); + evmap_t be = any_cast(obe); + vimap_t node_index = any_cast(onode_index); + vvmap_t half_edges = any_cast(ohalf_edges); + emap_t egindex = any_cast(oeindex); + + run_action<>()(gi, + [&](auto& g) + { + auto& eg = egi.get_graph(); + auto eindex = get(edge_index, g); + for (auto e : edges_range(g)) + { + auto s = get_source(e, g); + auto t = get_target(e, g); + auto u = add_vertex(eg); + auto v = add_vertex(eg); + auto ne = add_edge(u, v, eg).first; + egindex[ne] = eindex[e]; + if (be[e].size() != 2) + throw GraphException("Edge block property map must have exactly two values per edge"); + b[u] = be[e][0]; + b[v] = be[e][1]; + node_index[u] = s; + node_index[v] = t; + half_edges[s].push_back(u); + half_edges[t].push_back(v); + } + })(); +} + +void get_be_from_b_overlap(GraphInterface& gi, boost::any obe, boost::any ob) +{ + typedef vprop_map_t::type vmap_t; + typedef eprop_map_t>::type evmap_t; + + vmap_t b = any_cast(ob); + evmap_t be = any_cast(obe); + + run_action<>()(gi, + [&](auto& g) + { + for (auto e : edges_range(g)) + { + auto s = get_source(e, g); + auto t = get_target(e, g); + be[e] = {b[s], b[t]}; + } + })(); +} + +struct get_nodeset_overlap +{ + template + void operator()(Graph& g, VProp node_index, VVProp half_edges) + const + { + typedef typename graph_traits::vertex_descriptor vertex_t; + for (auto e : edges_range(g)) + { + vertex_t s = get_source(e, g); + vertex_t t = get_target(e, g); + half_edges[node_index[s]].push_back(s); + half_edges[node_index[t]].push_back(t); + } + } +}; + +void get_nodeset_overlap(GraphInterface& gi, boost::any onode_index, + boost::any ohalf_edges) +{ + typedef vprop_map_t::type vmap_t; + typedef vprop_map_t>::type vvmap_t; + + vmap_t node_index = any_cast(onode_index); + vvmap_t half_edges = any_cast(ohalf_edges); + + run_action<>()(gi, [&](auto& g) + { + for (auto e : edges_range(g)) + { + auto s = get_source(e, g); + auto t = get_target(e, g); + half_edges[node_index[s]].push_back(s); + half_edges[node_index[t]].push_back(t); + } + })(); +} + + +void export_overlap_blockmodel_state() +{ + using namespace boost::python; + + overlap_block_state::dispatch + ([&](auto* s) + { + typedef typename std::remove_reference::type state_t; + + double (state_t::*virtual_move)(size_t, size_t, bool, bool, bool, + bool, bool) = + &state_t::virtual_move; + size_t (state_t::*sample_block)(size_t, double, vector&, + rng_t&) + = &state_t::sample_block; + double (state_t::*get_move_prob)(size_t, size_t, size_t, double, + bool) + = &state_t::get_move_prob; + + class_ c(name_demangle(typeid(state_t).name()).c_str(), + no_init); + c.def("remove_vertex", &state_t::remove_vertex) + .def("add_vertex", &state_t::add_vertex) + .def("move_vertex", &state_t::move_vertex) + .def("virtual_move", virtual_move) + .def("sample_block", sample_block) + .def("entropy", &state_t::entropy) + .def("get_partition_dl", &state_t::get_partition_dl) + .def("get_deg_dl", &state_t::get_deg_dl) + .def("get_move_prob", get_move_prob) + .def("enable_partition_stats", + &state_t::enable_partition_stats) + .def("disable_partition_stats", + &state_t::disable_partition_stats) + .def("is_partition_stats_enabled", + &state_t::is_partition_stats_enabled) + .def("get_be_overlap", + +[](state_t& state, GraphInterface& gi, + boost::any obe) + { + typedef eprop_map_t>::type evmap_t; + evmap_t be = any_cast(obe); + run_action<>()(gi, + [&](auto& g) + { + state.get_be_overlap(g, be); + })(); + }) + .def("get_bv_overlap", + +[](state_t& state, GraphInterface& gi, boost::any obv, + boost::any obc_in, boost::any obc_out, + boost::any obc_total) + { + typedef vprop_map_t>::type vvmap_t; + vvmap_t bv = any_cast(obv); + vvmap_t bc_in = any_cast(obc_in); + vvmap_t bc_out = any_cast(obc_out); + vvmap_t bc_total = any_cast(obc_total); + run_action<>()(gi, + [&](auto& g) + { + state.get_bv_overlap(g, bv, bc_in, + bc_out, + bc_total); + })(); + }) + .def("get_overlap_split", + +[](state_t& state, GraphInterface& gi, boost::any obv, + boost::any ob) + { + typedef vprop_map_t::type vmap_t; + typedef vprop_map_t>::type vvmap_t; + + vvmap_t bv = any_cast(obv); + vmap_t b = any_cast(ob); + run_action<>()(gi, + [&](auto& g) + { + state.get_overlap_split(g, bv, b); + })(); + }) + .def("get_maj_overlap", + +[](state_t&, GraphInterface& gi, boost::any obv, + boost::any obc_total, boost::any ob) + { + typedef vprop_map_t::type vmap_t; + typedef vprop_map_t>::type vvmap_t; + + vmap_t b = any_cast(ob); + vvmap_t bv = any_cast(obv); + vvmap_t bc_total = any_cast(obc_total); + run_action<>()(gi, + [&](auto& g) + { + for (auto v : vertices_range(g)) + { + if (bv[v].empty()) + { + b[v] = numeric_limits::max(); + continue; + } + auto& c = bc_total[v]; + auto pos = std::max_element(c.begin(), c.end()); + auto r = *(bv[v].begin() + (pos - c.begin())); + b[v] = r; + } + })(); + }); + }); + + def("make_overlap_block_state", &make_overlap_block_state); + def("get_be_from_b_overlap", &get_be_from_b_overlap); + def("get_eg_overlap", &get_eg_overlap); + def("get_nodeset_overlap", &get_nodeset_overlap); +} diff --git a/src/graph/inference/graph_blockmodel_overlap.hh b/src/graph/inference/graph_blockmodel_overlap.hh new file mode 100644 index 0000000000000000000000000000000000000000..c6b1d65a01de3e22d766052fc795f1b2d75925dd --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap.hh @@ -0,0 +1,789 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_OVERLAP_HH +#define GRAPH_BLOCKMODEL_OVERLAP_HH + +#include "config.h" +#include + +#include "graph_state.hh" +#include "graph_blockmodel_util.hh" +#include "graph_blockmodel_overlap_util.hh" + +namespace graph_tool +{ + +using namespace boost; +using namespace std; + +typedef vprop_map_t::type vmap_t; +typedef eprop_map_t::type emap_t; + +typedef vprop_map_t::type vimap_t; +typedef vprop_map_t>::type vvmap_t; + +typedef mpl::vector2 use_hash_tr; + +#define OVERLAP_BLOCK_STATE_params \ + ((g, &, never_filtered_never_reversed, 1)) \ + ((use_hash,, use_hash_tr, 1)) \ + ((_abg, &, boost::any&, 0)) \ + ((node_index,, vimap_t, 0)) \ + ((half_edges,, vvmap_t, 0)) \ + ((mrs,, emap_t, 0)) \ + ((mrp,, vmap_t, 0)) \ + ((mrm,, vmap_t, 0)) \ + ((wr,, vmap_t, 0)) \ + ((b,, vmap_t, 0)) \ + ((bclabel,, vmap_t, 0)) \ + ((pclabel,, vmap_t, 0)) \ + ((deg_corr,, bool, 0)) + +GEN_STATE_BASE(OverlapBlockStateBase, OVERLAP_BLOCK_STATE_params) + +template +class OverlapBlockState + : public OverlapBlockStateBase +{ +public: + GET_PARAMS_USING(OverlapBlockStateBase, OVERLAP_BLOCK_STATE_params) + GET_PARAMS_TYPEDEF(Ts, OVERLAP_BLOCK_STATE_params) + + template * = nullptr> + OverlapBlockState(RNG& rng, ATs&&... args) + : OverlapBlockStateBase(std::forward(args)...), + _bg(boost::any_cast>(__abg)), + _c_mrs(_mrs.get_checked()), + _emat(_g, _b, _bg, rng), + _overlap_stats(_g, _b, _half_edges, _node_index, num_vertices(_bg)) + { + for (auto r : vertices_range(_bg)) + _wr[r] = _overlap_stats.get_block_size(r); + } + + OverlapBlockState(const OverlapBlockState& other) + : OverlapBlockStateBase + (static_cast&>(other)), + _bg(other._bg), + _c_mrs(other._c_mrs), + _emat(other._emat), + _overlap_stats(other._overlap_stats) + { + if (other.is_partition_stats_enabled()) + enable_partition_stats(); + } + + void remove_vertex(size_t v) + { + size_t r = _b[v]; + + auto u = _overlap_stats.get_out_neighbour(v); + if (u != _overlap_stats._null) + { + size_t s = _b[u]; + + auto e = *out_edges(v, _g).first; + auto& me = _emat.get_bedge(e); + + _mrs[me] -= 1; + + assert(_mrs[me] >= 0); + + _mrp[r] -= 1; + _mrm[s] -= 1; + + if (_mrs[me] == 0) + _emat.remove_me(r, s, me, _bg); + } + + u = _overlap_stats.get_in_neighbour(v); + if (u != _overlap_stats._null) + { + size_t s = _b[u]; + + auto e = *in_edge_iteratorS().get_edges(v, _g).first; + auto& me = _emat.get_bedge(e); + + _mrs[me] -= 1; + + _mrp[s] -= 1; + _mrm[r] -= 1; + + if (_mrs[me] == 0) + _emat.remove_me(s, r, me, _bg); + } + + _overlap_stats.remove_half_edge(v, r, _b, _g); + _wr[r] = _overlap_stats.get_block_size(r); + + if (!_egroups.empty()) + _egroups.remove_vertex(v, size_t(r), _g); + } + + void add_vertex(size_t v, size_t r) + { + typedef typename graph_traits::edge_descriptor bedge_t; + + auto u = _overlap_stats.get_out_neighbour(v); + if (u != _overlap_stats._null) + { + size_t s = r; + if (u != v) + s = _b[u]; + + auto me = _emat.get_me(r, s); + + if (me == bedge_t()) + { + me = add_edge(r, s, _bg).first; + _emat.put_me(r, s, me); + _c_mrs[me] = 0; + } + + auto e = *out_edges(v, _g).first; + _emat.get_bedge(e) = me; + + assert(me == _emat.get_me(r, s)); + + _mrs[me] += 1; + _mrp[r] += 1; + _mrm[s] += 1; + } + + u = _overlap_stats.get_in_neighbour(v); + if (u != _overlap_stats._null) + { + size_t s = _b[u]; + + auto me = _emat.get_me(s, r); + + if (me == bedge_t()) + { + me = add_edge(s, r, _bg).first; + _emat.put_me(s, r, me); + _c_mrs[me] = 0; + } + + auto e = *in_edge_iteratorS().get_edges(v, _g).first; + _emat.get_bedge(e) = me; + + assert(me == _emat.get_me(s, r)); + + _mrs[me] += 1; + + _mrp[s] += 1; + _mrm[r] += 1; + } + + _overlap_stats.add_half_edge(v, r, _b, _g); + _wr[r] = _overlap_stats.get_block_size(r); + + _b[v] = r; + + if (!_egroups.empty()) + _egroups.add_vertex(v, r, _eweight, _g); + } + + // move a vertex from its current block to block nr + void move_vertex(size_t v, size_t nr) + { + size_t r = _b[v]; + if (r == nr) + return; + + if (_bclabel[r] != _bclabel[nr]) + throw ValueException("cannot move vertex across clabel barriers"); + + if (is_partition_stats_enabled()) + get_partition_stats(v).move_vertex(v, _b[v], nr, _deg_corr, _g); + + remove_vertex(v); + add_vertex(v, nr); + } + + size_t virtual_remove_size(size_t v) + { + return _overlap_stats.virtual_remove_size(v, _b[v]); + } + + // compute the entropy difference of a virtual move of vertex from block r to nr + template + double virtual_move_sparse(size_t v, size_t nr, bool multigraph, + MEntries& m_entries) const + { + size_t r = _b[v]; + + if (r == nr) + return 0.; + + m_entries.clear(); + move_entries(v, nr, _b, _eweight, _mrs, _emat.get_bedge_map(), _g, _bg, + m_entries); + + size_t kout = out_degreeS()(v, _g); + size_t kin = 0; + if (is_directed::apply::type::value) + kin = in_degreeS()(v, _g); + + double dS = entries_dS(m_entries, _mrs, _emat, _bg); + + int dwr = _wr[r] - _overlap_stats.virtual_remove_size(v, r, kin, kout); + int dwnr = _overlap_stats.virtual_add_size(v, nr) - _wr[nr]; + + if (_deg_corr) + dS += _overlap_stats.virtual_move_dS(v, r, nr, _g, kin, kout); + + if (multigraph) + dS += _overlap_stats.virtual_move_parallel_dS(v, r, nr, _b, _g); + + if (!is_directed::apply::type::value) + kin = kout; + + dS += vterm(_mrp[r] - kout, _mrm[r] - kin, _wr[r] - dwr , _deg_corr, _bg); + dS += vterm(_mrp[nr] + kout, _mrm[nr] + kin, _wr[nr] + dwnr, _deg_corr, _bg); + dS -= vterm(_mrp[r] , _mrm[r] , _wr[r] , _deg_corr, _bg); + dS -= vterm(_mrp[nr] , _mrm[nr] , _wr[nr] , _deg_corr, _bg); + + return dS; + } + + double virtual_move_sparse(size_t v, size_t nr, bool multigraph) + { + return virtual_move_sparse(v, nr, multigraph, _m_entries); + } + + template + double virtual_move_dense(size_t, size_t, bool, MEntries&) const + { + throw GraphException("Dense entropy for overlapping model not implemented!"); + } + + double virtual_move_dense(size_t v, size_t nr, bool multigraph) + { + return virtual_move_dense(v, nr, multigraph, _m_entries); + } + + template + double virtual_move(size_t v, size_t nr, bool dense, bool multigraph, + bool partition_dl, bool deg_dl, bool edges_dl, + MEntries& m_entries) + { + size_t r = _b[v]; + + if (_bclabel[r] != _bclabel[nr]) + return std::numeric_limits::infinity(); + + double dS; + if (dense) + dS = virtual_move_dense(v, nr, multigraph, m_entries); + else + dS = virtual_move_sparse(v, nr, multigraph, m_entries); + + if (partition_dl || deg_dl || edges_dl) + { + enable_partition_stats(); + auto& ps = get_partition_stats(v); + if (partition_dl) + dS += ps.get_delta_dl(v, r, nr, _g); + if (_deg_corr && deg_dl) + dS += ps.get_delta_deg_dl(v, r, nr, _eweight, _g); + if (edges_dl) + dS += ps.get_delta_edges_dl(v, r, nr, _g); + } + + return dS; + } + + double virtual_move(size_t v, size_t nr, bool dense, bool multigraph, + bool partition_dl, bool deg_dl, bool edges_dl) + { + return virtual_move(v, nr, dense, multigraph, partition_dl, deg_dl, + edges_dl, _m_entries); + } + + double get_delta_dl(size_t v, size_t nr) + { + enable_partition_stats(); + return get_partition_stats(v).get_delta_dl(v, _b[v], nr, _g); + } + + // Sample node placement + template + size_t sample_block(size_t v, double c, const vector& block_list, + RNG& rng) + { + // attempt random block + size_t s = uniform_sample(block_list, rng); + + if (!std::isinf(c)) + { + size_t w = get_lateral_half_edge(v, rng); + + size_t u = _overlap_stats.get_out_neighbour(w); + if (u >= num_vertices(_g)) + u = _overlap_stats.get_in_neighbour(w); + + size_t t = _b[u]; + double p_rand = 0; + if (c > 0) + { + size_t B = num_vertices(_bg); + if (is_directed::apply::type::value) + p_rand = c * B / double(_mrp[t] + _mrm[t] + c * B); + else + p_rand = c * B / double(_mrp[t] + c * B); + } + + typedef std::uniform_real_distribution<> rdist_t; + if (c == 0 || rdist_t()(rng) >= p_rand) + { + if (_egroups.empty()) + _egroups.init(_b, _eweight, _g, _bg); + const auto& e = _egroups.sample_edge(t, rng); + s = _b[target(e, _g)]; + if (s == t) + s = _b[source(e, _g)]; + } + } + + return s; + } + + size_t sample_block(size_t v, double c, vector& block_list, + rng_t& rng) + { + return sample_block(v, c, block_list, rng); + } + + template + size_t get_lateral_half_edge(size_t v, RNG& rng) + { + size_t vv = _overlap_stats.get_node(v); + size_t w = _overlap_stats.sample_half_edge(vv, rng); + return w; + } + + template + size_t random_neighbour(size_t v, RNG& rng) + { + size_t w = get_lateral_half_edge(v, _overlap_stats, rng); + + size_t u = _overlap_stats.get_out_neighbour(w); + if (u >= num_vertices(_g)) + u = _overlap_stats.get_in_neighbour(w); + return u; + } + + // Computes the move proposal probability + template + double get_move_prob(size_t v, size_t r, size_t s, double c, bool reverse, + MEntries& m_entries) + { + typedef typename graph_traits::vertex_descriptor vertex_t; + size_t B = num_vertices(_bg); + double p = 0; + size_t w = 0; + + size_t kout = out_degreeS()(v, _g, _eweight); + size_t kin = kout; + if (is_directed::apply::type::value) + kin = in_degreeS()(v, _g, _eweight); + + size_t vi = _overlap_stats.get_node(v); + auto& ns = _overlap_stats.get_half_edges(vi); + + for (size_t v: ns) + { + for (auto e : all_edges_range(v, _g)) + { + vertex_t u = target(e, _g); + if (is_directed::apply::type::value && u == v) + u = source(e, _g); + vertex_t t = _b[u]; + if (u == v) + t = r; + size_t ew = _eweight[e]; + w += ew; + + int mts; + if (t == r && s == size_t(_b[u])) + mts = _mrs[_emat.get_bedge(e)]; + else + mts = get_mrs(t, s, _mrs, _emat); + int mtp = _mrp[t]; + int mst = mts; + int mtm = mtp; + + if (is_directed::apply::type::value) + { + mst = get_mrs(s, t, _mrs, _emat); + mtm = _mrm[t]; + } + + if (reverse) + { + int dts = m_entries.get_delta(t, s); + int dst = dts; + if (is_directed::apply::type::value) + dst = m_entries.get_delta(s, t); + + mts += dts; + mst += dst; + + if (t == s) + { + mtp -= kout; + mtm -= kin; + } + + if (t == r) + { + mtp += kout; + mtm += kin; + } + } + + if (is_directed::apply::type::value) + { + p += ew * ((mts + mst + c) / (mtp + mtm + c * B)); + } + else + { + if (t == s) + mts *= 2; + p += ew * (mts + c) / (mtp + c * B); + } + } + } + if (w > 0) + return p / w; + else + return 1. / B; + } + + double get_move_prob(size_t v, size_t r, size_t s, double c, bool reverse) + { + return get_move_prob(v, r, s, c, reverse, _m_entries); + } + + bool is_last(size_t v) + { + auto r = _b[v]; + return _overlap_stats.virtual_remove_size(v, r) == 0; + } + + double sparse_entropy(bool multigraph, bool deg_entropy) const + { + double S = 0; + for (auto e : edges_range(_bg)) + S += eterm(source(e, _bg), target(e, _bg), _mrs[e], _bg); + for (auto v : vertices_range(_bg)) + S += vterm(_mrp[v], _mrm[v], _wr[v], _deg_corr, _bg); + + if (_deg_corr && deg_entropy) + { + typedef gt_hash_map map_t; + + map_t in_hist, out_hist; + size_t N = _overlap_stats.get_N(); + + for (size_t v = 0; v < N; ++v) + { + in_hist.clear(); + out_hist.clear(); + + const auto& half_edges = _overlap_stats.get_half_edges(v); + for (size_t u : half_edges) + { + in_hist[_b[u]] += in_degreeS()(u, _g); + out_hist[_b[u]] += out_degree(u, _g); + } + + for (auto& k_c : in_hist) + S -= lgamma_fast(k_c.second + 1); + for (auto& k_c : out_hist) + S -= lgamma_fast(k_c.second + 1); + } + } + + if (multigraph) + { + for(const auto& h : _overlap_stats.get_parallel_bundles()) + { + for (const auto& kc : h) + S += lgamma_fast(kc.second + 1); + } + } + return S; + } + + double dense_entropy(bool) + { + throw GraphException("Dense entropy for overlapping model not implemented!"); + } + + double entropy(bool dense, bool multigraph, bool deg_entropy) + { + if (dense) + return dense_entropy(multigraph); + else + return sparse_entropy(multigraph, deg_entropy); + } + + double get_partition_dl() + { + if (!is_partition_stats_enabled()) + enable_partition_stats(); + double S = 0; + for (auto& ps : _partition_stats) + S += ps.get_partition_dl(); + return S; + } + + double get_deg_dl(bool ent, bool dl_alt, bool xi_fast) + { + if (!is_partition_stats_enabled()) + enable_partition_stats(); + double S = 0; + for (auto& ps : _partition_stats) + S += ps.get_deg_dl(ent, dl_alt, xi_fast); + return S; + } + + double get_parallel_entropy() + { + double S = 0; + for(auto& h : _overlap_stats.get_parallel_bundles()) + { + for (auto& kc : h) + S += lgamma_fast(kc.second + 1); + } + return S; + } + + void enable_partition_stats() + { + if (_partition_stats.empty()) + { + + size_t E = num_vertices(_g) / 2; + size_t B = 0; + for (auto r : vertices_range(_bg)) + if (_wr[r] > 0) + B++; + + auto vi = std::max_element(vertices(_g).first, vertices(_g).second, + [&](auto u, auto v) + { return this->_pclabel[u] < this->_pclabel[v];}); + size_t C = _bclabel[*vi] + 1; + + vector> vcs(C); + vector rc(num_vertices(_bg)); + for (auto v : vertices_range(_g)) + { + vcs[_pclabel[v]].insert(_overlap_stats.get_node(v)); + rc[_b[v]] = _pclabel[v]; + } + + for (size_t c = 0; c < C; ++c) + _partition_stats.emplace_back(_g, _b, vcs[c], E, B, + _eweight, _overlap_stats, + _bmap, _vmap); + + for (size_t r = 0; r < num_vertices(_bg); ++r) + _partition_stats[rc[r]].get_r(r); + } + } + + void disable_partition_stats() + { + _partition_stats.clear(); + } + + bool is_partition_stats_enabled() const + { + return !_partition_stats.empty(); + } + + overlap_partition_stats_t& get_partition_stats(size_t v) + { + return _partition_stats[_pclabel[v]]; + } + + template + void get_be_overlap(Graph& g, EMap be) + { + for (auto ei : edges_range(_g)) + { + auto u = source(ei, _g); + auto v = target(ei, _g); + + auto s = vertex(_node_index[u], g); + auto t = vertex(_node_index[v], g); + + for (auto e : out_edges_range(s, g)) + { + if (!be[e].empty() || target(e, g) != t) + continue; + if (is_directed::apply::type::value || s < target(e, g)) + be[e] = {_b[u], _b[v]}; + else + be[e] = {_b[v], _b[u]}; + break; + } + + for (auto e : in_edges_range(t, g)) + { + if (!be[e].empty() || source(e, g) != s) + continue; + be[e] = {_b[u], _b[v]}; + break; + } + } + } + + template + void get_bv_overlap(Graph& g, VMap bv, VMap bc_in, VMap bc_out, + VMap bc_total) + { + typedef gt_hash_map map_t; + vector hist_in; + vector hist_out; + + for (auto v : vertices_range(_g)) + { + if (out_degree(v, _g) > 0) + { + size_t s = _node_index[v]; + if (s >= hist_out.size()) + hist_out.resize(s + 1); + hist_out[s][_b[v]]++; + } + + if (in_degreeS()(v, _g) > 0) + { + size_t t = _node_index[v]; + if (t >= hist_in.size()) + hist_in.resize(t + 1); + hist_in[t][_b[v]]++; + } + } + + hist_in.resize(num_vertices(g)); + hist_out.resize(num_vertices(g)); + + set rs; + for (auto i : vertices_range(g)) + { + rs.clear(); + for (auto iter = hist_out[i].begin(); iter != hist_out[i].end(); ++iter) + rs.insert(iter->first); + for (auto iter = hist_in[i].begin(); iter != hist_in[i].end(); ++iter) + rs.insert(iter->first); + // if (rs.empty()) + // throw GraphException("Cannot have empty overlapping block membership!"); + for (size_t r : rs) + { + bv[i].push_back(r); + + auto iter_in = hist_in[i].find(r); + if (iter_in != hist_in[i].end()) + bc_in[i].push_back(iter_in->second); + else + bc_in[i].push_back(0); + + auto iter_out = hist_out[i].find(r); + if (iter_out != hist_out[i].end()) + bc_out[i].push_back(iter_out->second); + else + bc_out[i].push_back(0); + + bc_total[i].push_back(bc_in[i].back() + + bc_out[i].back()); + } + } + } + + template + void get_overlap_split(Graph& g, VVProp bv, VProp b) const + { + gt_hash_map, size_t> bvset; + + for (auto v : vertices_range(g)) + { + auto r = bv[v]; + auto iter = bvset.find(r); + if (iter == bvset.end()) + iter = bvset.insert(make_pair(r, bvset.size())).first; + b[v] = iter->second; + } + } + + + void init_mcmc(double c, double dl) + { + if (!std::isinf(c)) + { + if (_egroups.empty()) + _egroups.init(_b, _eweight, _g, _bg); + } + else + { + _egroups.clear(); + } + + if (dl) + enable_partition_stats(); + else + disable_partition_stats(); + } + + +//private: + typedef typename + std::conditional::type::value, + GraphInterface::multigraph_t, + UndirectedAdaptor>::type + bg_t; + bg_t& _bg; + + typename mrs_t::checked_t _c_mrs; + + typedef typename std::conditional, + EMat>::type + emat_t; + emat_t _emat; + + EGroups _egroups; + + overlap_stats_t _overlap_stats; + std::vector _partition_stats; + std::vector _bmap; + std::vector _vmap; + + SingleEntrySet _m_entries; + + UnityPropertyMap _eweight; + UnityPropertyMap _vweight; +}; + +} // namespace graph_tool + +#endif // GRAPH_BLOCKMODEL_OVERLAP_HH diff --git a/src/graph/inference/graph_blockmodel_overlap_gibbs.cc b/src/graph/inference/graph_blockmodel_overlap_gibbs.cc new file mode 100644 index 0000000000000000000000000000000000000000..5b638b320d86a2b2bbd1e8619188e5af27810e60 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_gibbs.cc @@ -0,0 +1,67 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#include "graph_blockmodel_gibbs.hh" +#include "gibbs_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +using OverlapGibbs = Gibbs; + +template +GEN_DISPATCH(overlap_gibbs_block_state, + OverlapGibbs::template GibbsBlockState, + GIBBS_BLOCK_STATE_params(State)) + +python::object gibbs_overlap_sweep(python::object ogibbs_state, + python::object oblock_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + overlap_gibbs_block_state::make_dispatch + (ogibbs_state, + [&](auto& s) + { + auto ret_ = gibbs_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + overlap_block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_overlap_blockmodel_gibbs() +{ + using namespace boost::python; + def("gibbs_overlap_sweep", &gibbs_overlap_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_overlap_mcmc.cc b/src/graph/inference/graph_blockmodel_overlap_mcmc.cc new file mode 100644 index 0000000000000000000000000000000000000000..f3b71a7e9ec6fe8f9f74a12aa72c99011eb24958 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_mcmc.cc @@ -0,0 +1,67 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#include "graph_blockmodel_mcmc.hh" +#include "mcmc_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +using OverlapMCMC = MCMC; + +template +GEN_DISPATCH(mcmc_overlap_block_state, + OverlapMCMC::template MCMCBlockState, + MCMC_BLOCK_STATE_params(State)) + +python::object overlap_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::type + state_t; + + mcmc_overlap_block_state::make_dispatch + (omcmc_state, + [&](auto& s) + { + auto ret_ = mcmc_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + overlap_block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_overlap_blockmodel_mcmc() +{ + using namespace boost::python; + def("overlap_mcmc_sweep", &overlap_mcmc_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_overlap_mcmc_bundled.cc b/src/graph/inference/graph_blockmodel_overlap_mcmc_bundled.cc new file mode 100644 index 0000000000000000000000000000000000000000..c5eb50122105e5c4fa06956b95a3421f3fb0447a --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_mcmc_bundled.cc @@ -0,0 +1,64 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#include "graph_blockmodel_overlap_mcmc_bundled.hh" +#include "mcmc_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(bundled_mcmc_overlap_block_state, + MCMC::template BundledMCMCOverlapBlockState, + BUNDLED_MCMC_OVERLAP_BLOCK_STATE_params(State)) + +python::object do_overlap_mcmc_bundled_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::type + state_t; + + bundled_mcmc_overlap_block_state::make_dispatch + (omcmc_state, + [&](auto& s) + { + auto ret_ = mcmc_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + overlap_block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_overlap_blockmodel_mcmc_bundled() +{ + using namespace boost::python; + def("overlap_mcmc_bundled_sweep", &do_overlap_mcmc_bundled_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_overlap_mcmc_bundled.hh b/src/graph/inference/graph_blockmodel_overlap_mcmc_bundled.hh new file mode 100644 index 0000000000000000000000000000000000000000..6856c004e6b287e84ed12296d5c8a21a1bdff38d --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_mcmc_bundled.hh @@ -0,0 +1,174 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_OVERLAP_MCMC_BUNDLED_HH +#define GRAPH_BLOCKMODEL_OVERLAP_MCMC_BUNDLED_HH + +#include "config.h" + +#include + +#include "graph_tool.hh" +#include "graph_state.hh" +#include "graph_blockmodel_overlap_util.hh" +#include + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +#define BUNDLED_MCMC_OVERLAP_BLOCK_STATE_params(State) \ + ((__class__,&, mpl::vector, 1)) \ + ((state, &, State&, 0)) \ + ((E,, size_t, 0)) \ + ((vlist,, std::vector, 0)) \ + ((block_list,&, std::vector&, 0)) \ + ((beta,, double, 0)) \ + ((c,, double, 0)) \ + ((multigraph,, bool, 0)) \ + ((dense,, bool, 0)) \ + ((partition_dl,, bool, 0)) \ + ((degree_dl,, bool, 0)) \ + ((edges_dl,, bool, 0)) \ + ((sequential,, bool, 0)) \ + ((verbose,, bool, 0)) \ + ((niter,, size_t, 0)) + + +template +struct MCMC +{ + GEN_STATE_BASE(BundledMCMCOverlapBlockStateBase, + BUNDLED_MCMC_OVERLAP_BLOCK_STATE_params(State)) + + template + class BundledMCMCOverlapBlockState + : public BundledMCMCOverlapBlockStateBase + { + public: + GET_PARAMS_USING(BundledMCMCOverlapBlockStateBase, + BUNDLED_MCMC_OVERLAP_BLOCK_STATE_params(State)) + GET_PARAMS_TYPEDEF(Ts, BUNDLED_MCMC_OVERLAP_BLOCK_STATE_params(State)) + + template * = nullptr> + BundledMCMCOverlapBlockState(ATs&&... as) + : BundledMCMCOverlapBlockStateBase(as...), + _g(_state._g), + _m_entries(num_vertices(_state._bg)), + _parallel(false) + { + _state.init_mcmc(_c, _partition_dl || _degree_dl || _edges_dl); + + for (auto v : _vlist) + { + auto i = _state._overlap_stats.get_node(v); + if (i >= _half_edges.size()) + _half_edges.resize(i + 1); + _half_edges[i].push_back(i); + } + + for (auto& he : _half_edges) + { + gt_hash_map> bundle; + for (auto v : he) + bundle[_state._b[v]].push_back(v); + for (auto& kv : bundle) + { + _bundles.emplace_back(); + _bundles.back().swap(kv.second); + } + } + + _vlist.clear(); + std::generate_n(std::back_inserter(_vlist), _bundles.size(), + [&](){ return _vlist.size(); }); + } + + typename state_t::g_t& _g; + EntrySet _m_entries; + std::vector> _half_edges; + std::vector> _bundles; + bool _parallel; + + size_t node_state(size_t i) + { + auto v = _bundles[i][0]; + return _state._b[v]; + } + + template + size_t move_proposal(size_t i, RNG& rng) + { + auto v = _bundles[i][0]; + auto r = _state._b[v]; + + size_t s = _state.sample_block(v, _c, _block_list, rng); + + if (_state._bclabel[s] != _state._bclabel[r]) + return r; + + return s; + } + + std::pair virtual_move_dS(size_t i, size_t nr) + { + double dS = 0; + + auto r = _state._b[_bundles[i][0]]; + + for (auto v : _bundles[i]) + { + dS += _state.virtual_move(v, nr, _dense, _multigraph, + _partition_dl, _degree_dl, _edges_dl, + _m_entries); + _state.move_vertex(v, nr); + } + + double a = 0; + // TODO: bundled moves are not ergodic, so we give up on detailed + // balance too. However, this can be improved. + + // if (!std::isinf(_c)) + // { + // size_t r = _state._b[v]; + // double pf = _state.get_move_prob(v, r, nr, _c, false); // forward + // double pb = _state.get_move_prob(v, nr, r, _c, true); // backward + // a = log(pb) - log(pf); + // } + + for (auto v : _bundles[i]) + { + _state.move_vertex(v, r); + } + + return make_pair(dS, a); + } + + void perform_move(size_t i, size_t nr) + { + for (auto v : _bundles[i]) + _state.move_vertex(v, nr); + } + }; +}; + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_OVERLAP_MCMC_BUNDLED_HH diff --git a/src/graph/inference/graph_blockmodel_overlap_multicanonical.cc b/src/graph/inference/graph_blockmodel_overlap_multicanonical.cc new file mode 100644 index 0000000000000000000000000000000000000000..c87de13800e366cb57d3e0ad24c2a9cd877097fa --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_multicanonical.cc @@ -0,0 +1,64 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#include "graph_blockmodel_multicanonical.hh" +#include "multicanonical_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(multicanonical_overlap_block_state, + Multicanonical::template MulticanonicalBlockState, + MULTICANONICAL_BLOCK_STATE_params(State)) + +python::object multicanonical_overlap_sweep(python::object omulticanonical_state, + python::object oblock_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + multicanonical_overlap_block_state::make_dispatch + (omulticanonical_state, + [&](auto& s) + { + auto ret_ = multicanonical_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + overlap_block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_overlap_blockmodel_multicanonical() +{ + using namespace boost::python; + def("multicanonical_overlap_sweep", &multicanonical_overlap_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_overlap_util.hh b/src/graph/inference/graph_blockmodel_overlap_util.hh new file mode 100644 index 0000000000000000000000000000000000000000..4a2bfbd3e90f218fb9e2c156555e84dc7d37050e --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_util.hh @@ -0,0 +1,1252 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_OVERLAP_UTIL_HH +#define GRAPH_BLOCKMODEL_OVERLAP_UTIL_HH + +#include "config.h" +#include + +#include "graph_blockmodel.hh" + +namespace graph_tool +{ + +using namespace boost; + +//=============== +// Overlap stats +//=============== + +class overlap_stats_t +{ +public: + typedef pair deg_t; + + typedef vprop_map_t::type ::unchecked_t + vmap_t; + typedef vprop_map_t::type ::unchecked_t + vimap_t; + typedef vprop_map_t>::type ::unchecked_t + vvmap_t; + + template + overlap_stats_t(Graph& g, vmap_t b, vvmap_t half_edges, vimap_t node_index, + size_t B) + : _half_edges(half_edges), _node_index(node_index), + _out_neighbours(num_vertices(g), _null), + _in_neighbours(num_vertices(g), _null) + { + _block_nodes.resize(B); + + _N = 0; + for (auto v : vertices_range(g)) + { + size_t vi = node_index[v]; + _N = std::max(_N, vi + 1); + size_t kin = in_degreeS()(v, g); + size_t kout = out_degreeS()(v, g); + + size_t r = b[v]; + auto& bnodes = _block_nodes[r]; + auto& k = bnodes[vi]; + k.first += kin; + k.second += kout; + + for (auto e : out_edges_range(v, g)) + _out_neighbours[v] = target(e, g); + for (auto e : in_edges_range(v, g)) + _in_neighbours[v] = source(e, g); + } + + // parallel edges + _mi.resize(num_vertices(g), -1); + + for (size_t i = 0; i < _N; ++i) + { + auto& he = half_edges[i]; + + gt_hash_map> out_us; + for (auto u : he) + { + auto w = _out_neighbours[u]; + if (w == _null) + continue; + if (!is_directed::apply::type::value && size_t(node_index[w]) < i) + continue; + out_us[node_index[w]].push_back(u); + } + + for (auto& uc : out_us) + { + if (uc.second.size() > 1) + { + _parallel_bundles.resize(_parallel_bundles.size() + 1); + auto& h = _parallel_bundles.back(); + for (auto u : uc.second) + { + auto w = _out_neighbours[u]; + assert(w != _null); + _mi[u] = _mi[w] = _parallel_bundles.size() - 1; + size_t r = b[u]; + size_t s = b[w]; + if (!is_directed::apply::type::value && r > s) + std::swap(r, s); + h[make_pair(r, s)]++; + } + } + } + } + } + + template + void add_half_edge(size_t v, size_t v_r, VProp& b, Graph&) + { + size_t u = _node_index[v]; + size_t kin = (_in_neighbours[v] != _null); + size_t kout = (_out_neighbours[v] != _null); + assert(kin + kout == 1); + auto& k = _block_nodes[v_r][u]; + k.first += kin; + k.second += kout; + + int m = _mi[v]; + if (m != -1) + { + size_t r, s; + auto u = _out_neighbours[v]; + if (u == _null) + { + u = _in_neighbours[v]; + r = b[u]; + s = v_r; + } + else + { + r = v_r; + s = b[u]; + } + auto& h = _parallel_bundles[m]; + if (!is_directed::apply::type::value && r > s) + std::swap(r, s); + h[make_pair(r, s)]++; + } + } + + template + void remove_half_edge(size_t v, size_t v_r, VProp& b, Graph&) + { + size_t u = _node_index[v]; + size_t kin = (_in_neighbours[v] != _null); + size_t kout = (_out_neighbours[v] != _null); + assert(kin + kout == 1); + auto& k = _block_nodes[v_r][u]; + k.first -= kin; + k.second -= kout; + + if (k.first + k.second == 0) + _block_nodes[v_r].erase(u); + + int m = _mi[v]; + if (m != -1) + { + size_t r, s; + auto u = _out_neighbours[v]; + if (u == _null) + { + u = _in_neighbours[v]; + r = b[u]; + s = v_r; + } + else + { + r = v_r; + s = b[u]; + } + auto& h = _parallel_bundles[m]; + if (!is_directed::apply::type::value && r > s) + std::swap(r, s); + auto iter = h.find(make_pair(r, s)); + assert(iter->second > 0); + iter->second--; + if (iter->second == 0) + h.erase(iter); + } + } + + size_t get_block_size(size_t r) const + { + return _block_nodes[r].size(); + } + + size_t virtual_remove_size(size_t v, size_t r, size_t in_deg = 0, + size_t out_deg = 0) const + { + size_t nr = _block_nodes[r].size(); + size_t u = _node_index[v]; + size_t kin = (in_deg + out_deg) > 0 ? + in_deg : (_in_neighbours[v] != _null); + size_t kout = (in_deg + out_deg) > 0 ? + out_deg : (_out_neighbours[v] != _null); + const auto iter = _block_nodes[r].find(u); + const auto& deg = iter->second; + if (deg.first == kin && deg.second == kout) + nr--; + return nr; + } + + size_t virtual_add_size(size_t v, size_t r) const + { + size_t nr = _block_nodes[r].size(); + size_t u = _node_index[v]; + const auto& bnodes = _block_nodes[r]; + if (bnodes.find(u) == bnodes.end()) + nr++; + return nr; + } + + template + double virtual_move_dS(size_t v, size_t r, size_t nr, Graph& g, + size_t in_deg = 0, size_t out_deg = 0) const + { + double dS = 0; + + size_t u = _node_index[v]; + size_t u_kin = ((in_deg + out_deg) > 0) ? in_deg : in_degreeS()(v, g); + size_t u_kout = ((in_deg + out_deg) > 0) ? out_deg : out_degreeS()(v, g); + + auto deg = _block_nodes[r].find(u)->second; + auto ndeg = deg; + ndeg.first -= u_kin; + ndeg.second -= u_kout; + + dS -= lgamma_fast(ndeg.first + 1) + lgamma_fast(ndeg.second + 1); + dS += lgamma_fast(deg.first + 1) + lgamma_fast(deg.second + 1); + + const auto iter = _block_nodes[nr].find(u); + if (iter != _block_nodes[nr].end()) + deg = iter->second; + else + deg = make_pair(0, 0); + ndeg = deg; + ndeg.first += u_kin; + ndeg.second += u_kout; + + dS -= lgamma_fast(ndeg.first + 1) + lgamma_fast(ndeg.second + 1); + dS += lgamma_fast(deg.first + 1) + lgamma_fast(deg.second + 1); + + return dS; + } + + template + double virtual_move_parallel_dS(size_t v, size_t v_r, size_t v_nr, VProp& b, + Graph&, bool bundled=false) const + { + int m = _mi[v]; + if (m == -1) + return 0; + + size_t r, s, nr, ns; + size_t u = _out_neighbours[v]; + if (u == _null) + { + u = _in_neighbours[v]; + r = b[u]; + s = v_r; + nr = r; + ns = v_nr; + } + else + { + r = v_r; + s = b[u]; + nr = v_nr; + ns = s; + } + + if (!is_directed::apply::type::value && r > s) + std::swap(r, s); + if (!is_directed::apply::type::value && nr > ns) + std::swap(nr, ns); + + auto& h = _parallel_bundles[m]; + + auto get_h = [&](const pair& k) -> int + { + const auto iter = h.find(k); + if (iter == h.end()) + return 0; + return iter->second; + }; + + int c = get_h(make_pair(r, s)); + int nc = get_h(make_pair(nr, ns)); + + assert(c > 0); + assert(nc >= 0); + assert(v_r != v_nr); + assert(make_pair(r, s) != make_pair(nr, ns)); + + double S = 0; + S -= lgamma_fast(c + 1) + lgamma_fast(nc + 1); + if (!bundled) + S += lgamma_fast(c) + lgamma_fast(nc + 2); + else + S += lgamma_fast(c + nc + 1); + + return S; + } + + // sample another half-edge adjacent to the node w + template + size_t sample_half_edge(size_t w, RNG& rng) const + { + auto& half_edges = _half_edges[w]; + return uniform_sample(half_edges, rng); + } + + size_t get_node(size_t v) const { return _node_index[v]; } + const vector& get_half_edges(size_t v) const { return _half_edges[v]; } + + auto get_out_neighbour(size_t v) const { return _out_neighbours[v]; } + auto get_in_neighbour(size_t v) const { return _in_neighbours[v]; } + + + typedef gt_hash_map, int> phist_t; + + const vector& get_parallel_bundles() const { return _parallel_bundles; } + const vector& get_mi() const { return _mi; } + + size_t get_N() const { return _N; } + + static constexpr size_t _null = numeric_limits::max(); + +private: + + vvmap_t _half_edges; // half-edges to each node + vimap_t _node_index; // node to each half edges + size_t _N; + + typedef gt_hash_map node_map_t; + + vector _block_nodes; // nodes (and degrees) in each block + + vector _out_neighbours; + vector _in_neighbours; + + + vector _mi; + vector _parallel_bundles; // parallel edge bundles +}; + +//============================= +// Partition Description length +//============================= + +extern double lbinom(double N, double k); +extern double xlogx(size_t x); + +struct overlap_partition_stats_t +{ + typedef std::tuple deg_t; + typedef vector cdeg_t; + + typedef vector bv_t; + + typedef gt_hash_map bhist_t; + typedef gt_hash_map> cdeg_hist_t; + + typedef gt_hash_map deg_hist_t; + + typedef gt_hash_map> ebhist_t; + + typedef gt_hash_map dmap_t; + + template + overlap_partition_stats_t(Graph& g, Vprop& b, Vlist& vlist, size_t E, + size_t B, Eprop& eweight, overlap_stats_t& ostats, + std::vector& bmap, + std::vector& vmap) + : _overlap_stats(ostats), _bmap(bmap), _vmap(vmap) + { + _D = 0; + _N = vlist.size(); + _E = E; + _total_B = B; + _dhist.resize(1); + + dmap_t in_hist, out_hist; + for (size_t v : vlist) + { + auto nv = get_v(v); + + dmap_t in_hist, out_hist; + set rs; + + get_bv_deg(v, b, eweight, g, rs, in_hist, out_hist); + + cdeg_t cdeg; + for (auto r : rs) + { + deg_t deg = std::make_tuple(in_hist[r], out_hist[r]); + cdeg.push_back(deg); + } + + bv_t bv(rs.begin(), rs.end()); + + assert(bv.size() > 0); + + _bvs[nv] = bv; + _degs[nv] = cdeg; + + auto & cdh = _deg_hist[bv]; + cdh[cdeg]++; + + size_t d = bv.size(); + _D = max(_D, d); + _dhist[d]++; + _bhist[bv]++; + + auto& bmh = _embhist[bv]; + auto& bph = _epbhist[bv]; + bmh.resize(bv.size()); + bph.resize(bv.size()); + + for (size_t i = 0; i < bv.size(); ++i) + { + size_t r = bv[i]; + _emhist[r] += get<0>(cdeg[i]); + _ephist[r] += get<1>(cdeg[i]); + bmh[i] += get<0>(cdeg[i]); + bph[i] += get<1>(cdeg[i]); + } + } + + for (auto& bv_c : _bhist) + { + assert(bv_c.second > 0); + for (auto r : bv_c.first) + _r_count[r] += 1; + } + + _actual_B = _r_count.size(); + } + + size_t get_v(size_t v) + { + constexpr size_t null = + std::numeric_limits::max(); + if (v >= _vmap.size()) + _vmap.resize(v + 1, null); + size_t nv = _vmap[v]; + if (nv == null) + nv = _vmap[v] = _bvs.size(); + if (nv >= _bvs.size()) + { + _bvs.resize(nv + 1); + _degs.resize(nv + 1); + } + return nv; + } + + size_t get_r(size_t r) + { + constexpr size_t null = + std::numeric_limits::max(); + if (r >= _bmap.size()) + _bmap.resize(r + 1, null); + size_t nr = _bmap[r]; + if (nr == null) + nr = _bmap[r] = _r_count.size(); + if (nr >= _r_count.size()) + { + _r_count.resize(nr + 1); + _dhist.resize(nr + 2); + _emhist.resize(nr + 1); + _ephist.resize(nr + 1); + } + return nr; + } + + + template + void get_bv_deg(size_t v, Vprop& b, Eprop&, Graph& g, set& rs, + dmap_t& in_hist, dmap_t& out_hist) + { + auto& half_edges = _overlap_stats.get_half_edges(v); + for (size_t u : half_edges) + { + size_t kin = in_degreeS()(u, g); + size_t kout = out_degreeS()(u, g); + + auto r = get_r(b[u]); + in_hist[r] += kin; + out_hist[r] += kout; + } + + for (auto& rk : in_hist) + rs.insert(rk.first); + } + + + double get_partition_dl() const + { + double S = 0; + for (size_t d = 1; d < _dhist.size(); ++d) + { + size_t nd = _dhist[d]; + if (nd == 0) + continue; + double x = lbinom_fast(_actual_B, d); + double ss = lbinom_careful((exp(x) + nd) - 1, nd); // not fast + if (std::isinf(ss) || std::isnan(ss)) + ss = nd * x - lgamma_fast(nd + 1); + assert(!std::isinf(ss)); + assert(!std::isnan(ss)); + S += ss; + } + + S += lbinom_fast(_D + _N - 1, _N) + lgamma_fast(_N + 1); + + for (auto& bh : _bhist) + S -= lgamma_fast(bh.second + 1); + + return S; + } + + double get_deg_dl(bool ent, bool deg_alt, bool xi_fast) const + { + double S = 0; + if (ent) + { + for (auto& ch : _deg_hist) + { + auto& bv = ch.first; + auto& cdeg_hist = ch.second; + + size_t n_bv = _bhist.find(bv)->second; + + S += xlogx(n_bv); + for (auto& dh : cdeg_hist) + S -= xlogx(dh.second); + } + } + else + { + S = 0; + + for (auto& ch : _deg_hist) + { + auto& bv = ch.first; + auto& cdeg_hist = ch.second; + + size_t n_bv = _bhist.find(bv)->second; + + if (n_bv == 0) + continue; + + const auto& bmh = _embhist.find(bv)->second; + const auto& bph = _epbhist.find(bv)->second; + + double S1 = 0; + for (size_t i = 0; i < bv.size(); ++i) + { + if (xi_fast) + { + S1 += get_xi_fast(n_bv, bmh[i]); + S1 += get_xi_fast(n_bv, bph[i]); + } + else + { + S1 += get_xi(n_bv, bmh[i]); + S1 += get_xi(n_bv, bph[i]); + } + } + + S1 += lgamma_fast(n_bv + 1); + + for (auto& dh : cdeg_hist) + S1 -= lgamma_fast(dh.second + 1); + + if (deg_alt) + { + double S2 = 0; + for (size_t i = 0; i < bv.size(); ++i) + { + S2 += lbinom(n_bv + bmh[i] - 1, bmh[i]); + S2 += lbinom(n_bv + bph[i] - 1, bph[i]); + } + S += min(S1, S2); + } + else + { + S += S1; + } + } + + for (size_t r = 0; r < _r_count.size(); ++r) + { + if (_r_count[r] == 0) + continue; + S += lbinom(_r_count[r] + _emhist[r] - 1, _emhist[r]); + S += lbinom(_r_count[r] + _ephist[r] - 1, _ephist[r]); + } + } + return S; + } + + + template + bool get_n_bv(size_t v, size_t r, size_t nr, const bv_t& bv, + const cdeg_t& deg, bv_t& n_bv, cdeg_t& n_deg, Graph& g, + size_t in_deg = 0, size_t out_deg = 0) const + { + size_t kin = (in_deg + out_deg == 0) ? in_degreeS()(v, g) : in_deg; + size_t kout = (in_deg + out_deg == 0) ? out_degreeS()(v, g) : out_deg; + + gt_hash_map> deg_delta; + + auto& d_r = deg_delta[r]; + d_r.first -= kin; + d_r.second -= kout; + + auto& d_nr = deg_delta[nr]; + d_nr.first += kin; + d_nr.second += kout; + + n_deg.clear(); + n_bv.clear(); + bool is_same_bv = true; + bool has_r = false, has_nr = false; + for (size_t i = 0; i < bv.size(); ++i) + { + size_t s = bv[i]; + auto k_s = deg[i]; + + auto& d_s = deg_delta[s]; + get<0>(k_s) += d_s.first; + get<1>(k_s) += d_s.second; + + d_s.first = d_s.second = 0; + + if (s == r) + has_r = true; + + if (s == nr) + has_nr = true; + + if ((get<0>(k_s) + get<1>(k_s)) > 0) + { + n_bv.push_back(s); + n_deg.push_back(k_s); + } + else + { + is_same_bv = false; + } + } + + if (!has_r || !has_nr) + { + is_same_bv = false; + std::array ss = {{r, nr}}; + for (auto s : ss) + { + auto& d_s = deg_delta[s]; + if (d_s.first + d_s.second == 0) + continue; + size_t kin = d_s.first; + size_t kout = d_s.second; + auto pos = std::lower_bound(n_bv.begin(), n_bv.end(), s); + auto dpos = n_deg.begin(); + std::advance(dpos, pos - n_bv.begin()); + n_bv.insert(pos, s); + n_deg.insert(dpos, make_pair(kin, kout)); + } + } + return is_same_bv; + } + + // get deg counts without increasing the container + size_t get_deg_count(const bv_t& bv, const cdeg_t& deg) const + { + auto iter = _deg_hist.find(bv); + if (iter == _deg_hist.end()) + return 0; + auto& hist = iter->second; + if (hist.empty()) + return 0; + auto diter = hist.find(deg); + if (diter == hist.end()) + return 0; + return diter->second; + } + + // get bv counts without increasing the container + size_t get_bv_count(const bv_t& bv) const + { + auto iter = _bhist.find(bv); + if (iter == _bhist.end()) + return 0; + return iter->second; + } + + template + double get_delta_dl(size_t v, size_t r, size_t nr, const Graph& g, + size_t in_deg = 0, size_t out_deg = 0) + { + if (r == nr) + return 0; + + size_t o_r = r; + size_t o_nr = nr; + r = get_r(r); + nr = get_r(nr); + + size_t u = _overlap_stats.get_node(v); + + u = get_v(u); + + auto& bv = _bvs[u]; + assert(bv.size() > 0); + bv_t n_bv; + size_t d = bv.size(); + const cdeg_t& deg = _degs[u]; + cdeg_t n_deg; + + bool is_same_bv = get_n_bv(v, r, nr, bv, deg, n_bv, n_deg, g, in_deg, + out_deg); + + assert(n_bv.size() > 0); + if (is_same_bv) + return 0; + + size_t n_d = n_bv.size(); + size_t n_D = _D; + + if (d == _D && n_d < d && _dhist[d] == 1) + { + n_D = 1; + for (auto& bc : _bhist) + { + if (bc.first.size() == d || bc.second == 0) + continue; + n_D = max(n_D, bc.first.size()); + } + } + + n_D = max(n_D, n_d); + + double S_a = 0, S_b = 0; + + if (n_D != _D) + { + S_b += lbinom_fast(_D + _N - 1, _N); + S_a += lbinom_fast(n_D + _N - 1, _N); + } + + int dB = 0; + if (_overlap_stats.virtual_remove_size(v, o_r, in_deg, out_deg) == 0) + dB--; + if (_overlap_stats.get_block_size(o_nr) == 0) + dB++; + + auto get_S_d = [&] (size_t d_i, int delta, int dB) -> double + { + int nd = int(_dhist[d_i]) + delta; + if (nd == 0) + return 0.; + double x = lbinom_fast(_actual_B + dB, d_i); + double S = lbinom_careful(exp(x) + nd - 1, nd); // not fast + if (std::isinf(S) || std::isnan(S)) + S = nd * x - lgamma_fast(nd + 1); + return S; + }; + + if (dB == 0) + { + if (n_d != d) + { + S_b += get_S_d(d, 0, 0) + get_S_d(n_d, 0, 0); + S_a += get_S_d(d, -1, 0) + get_S_d(n_d, 1, 0); + } + } + else + { + for (size_t di = 0; di < min(_actual_B + abs(dB) + 1, _dhist.size()); ++di) + { + if (d != n_d) + { + if (di == d) + { + S_b += get_S_d(d, 0, 0); + S_a += get_S_d(d, -1, dB); + continue; + } + if (di == n_d) + { + S_b += get_S_d(n_d, 0, 0); + S_a += get_S_d(n_d, 1, dB); + continue; + } + } + if (_dhist[di] == 0) + continue; + S_b += get_S_d(di, 0, 0); + S_a += get_S_d(di, 0, dB); + } + } + + size_t bv_count = get_bv_count(bv); + assert(bv_count > 0); + size_t n_bv_count = get_bv_count(n_bv); + + auto get_S_b = [&] (bool is_bv, int delta) -> double + { + assert(int(bv_count) + delta >= 0); + if (is_bv) + return -lgamma_fast(bv_count + delta + 1); + return -lgamma_fast(n_bv_count + delta + 1); + }; + + S_b += get_S_b(true, 0) + get_S_b(false, 0); + S_a += get_S_b(true, -1) + get_S_b(false, 1); + + return S_a - S_b; + } + + template + double get_delta_edges_dl(size_t v, size_t r, size_t nr, const Graph&) + { + if (r == nr) + return 0; + + double S_b = 0, S_a = 0; + + int dB = 0; + if (_overlap_stats.virtual_remove_size(v, r) == 0) + dB--; + if (_overlap_stats.get_block_size(nr) == 0) + dB++; + + if (dB != 0) + { + auto get_x = [](size_t B) -> size_t + { + if (is_directed::apply::type::value) + return B * B; + else + return (B * (B + 1)) / 2; + }; + + S_b += lbinom(get_x(_total_B) + _E - 1, _E); + S_a += lbinom(get_x(_total_B + dB) + _E - 1, _E); + } + + return S_a - S_b; + } + + + template + double get_delta_deg_dl(size_t v, size_t r, size_t nr, const EWeight&, + const Graph& g, size_t in_deg = 0, + size_t out_deg = 0) + { + if (r == nr) + return 0; + + r = get_r(r); + nr = get_r(nr); + + double S_b = 0, S_a = 0; + + size_t u = get_v(_overlap_stats.get_node(v)); + auto& bv = _bvs[u]; + bv_t n_bv; + + const cdeg_t& deg = _degs[u]; + cdeg_t n_deg; + + bool is_same_bv = get_n_bv(v, r, nr, bv, deg, n_bv, n_deg, g, in_deg, + out_deg); + + size_t bv_count = get_bv_count(bv); + size_t n_bv_count = bv_count; + + auto get_S_bv = [&] (bool is_bv, int delta) -> double + { + if (is_bv) + return lgamma_fast(bv_count + delta + 1); + return lgamma_fast(n_bv_count + delta + 1); + }; + + auto get_S_e = [&] (bool is_bv, int bdelta, int deg_delta) -> double + { + size_t bv_c = ((is_bv) ? bv_count : n_bv_count) + bdelta; + if (bv_c == 0) + return 0.; + + const cdeg_t& deg_i = (is_bv) ? deg : n_deg; + const auto& bv_i = (is_bv) ? bv : n_bv; + + double S = 0; + if (((is_bv) ? bv_count : n_bv_count) > 0) + { + const auto& bmh = _embhist.find(bv_i)->second; + const auto& bph = _epbhist.find(bv_i)->second; + + assert(bmh.size() == bv_i.size()); + assert(bph.size() == bv_i.size()); + + for (size_t i = 0; i < bv_i.size(); ++i) + { + S += get_xi_fast(bv_c, bmh[i] + deg_delta * int(get<0>(deg_i[i]))); + S += get_xi_fast(bv_c, bph[i] + deg_delta * int(get<1>(deg_i[i]))); + } + } + else + { + for (size_t i = 0; i < bv_i.size(); ++i) + { + S += get_xi_fast(bv_c, deg_delta * int(get<0>(deg_i[i]))); + S += get_xi_fast(bv_c, deg_delta * int(get<1>(deg_i[i]))); + } + } + + return S; + }; + + auto get_S_e2 = [&] (int deg_delta, int ndeg_delta) -> double + { + double S = 0; + const auto& bmh = _embhist.find(bv)->second; + const auto& bph = _epbhist.find(bv)->second; + + for (size_t i = 0; i < bv.size(); ++i) + { + S += get_xi_fast(bv_count, bmh[i] + + deg_delta * int(get<0>(deg[i])) + + ndeg_delta * int(get<0>(n_deg[i]))); + S += get_xi_fast(bv_count, bph[i] + + deg_delta * int(get<1>(deg[i])) + + ndeg_delta * int(get<1>(n_deg[i]))); + } + return S; + }; + + if (!is_same_bv) + { + n_bv_count = get_bv_count(n_bv); + + S_b += get_S_bv(true, 0) + get_S_bv(false, 0); + S_a += get_S_bv(true, -1) + get_S_bv(false, 1); + + S_b += get_S_e(true, 0, 0) + get_S_e(false, 0, 0); + S_a += get_S_e(true, -1, -1) + get_S_e(false, 1, 1); + } + else + { + S_b += get_S_e2( 0, 0); + S_a += get_S_e2(-1, 1); + } + + size_t deg_count = get_deg_count(bv, deg); + size_t n_deg_count = get_deg_count(n_bv, n_deg); + + auto get_S_deg = [&] (bool is_deg, int delta) -> double + { + if (is_deg) + return -lgamma_fast(deg_count + delta + 1); + return -lgamma_fast(n_deg_count + delta + 1); + }; + + S_b += get_S_deg(true, 0) + get_S_deg(false, 0); + S_a += get_S_deg(true, -1) + get_S_deg(false, 1); + + auto is_in = [&] (const bv_t& bv, size_t r) -> bool + { + auto iter = lower_bound(bv.begin(), bv.end(), r); + if (iter == bv.end()) + return false; + if (size_t(*iter) != r) + return false; + return true; + }; + + for (size_t s : bv) + { + S_b += lbinom_fast(_r_count[s] + _emhist[s] - 1, _emhist[s]); + S_b += lbinom_fast(_r_count[s] + _ephist[s] - 1, _ephist[s]); + } + + for (size_t s : n_bv) + { + if (is_in(bv, s)) + continue; + S_b += lbinom_fast(_r_count[s] + _emhist[s] - 1, _emhist[s]); + S_b += lbinom_fast(_r_count[s] + _ephist[s] - 1, _ephist[s]); + } + + gt_hash_map> deg_delta; + gt_hash_map r_count_delta; + + if (bv != n_bv) + { + if (n_bv_count == 0) + { + for (auto s : n_bv) + r_count_delta[s] += 1; + } + + if (bv_count == 1) + { + for (auto s : bv) + r_count_delta[s] -= 1; + } + } + + if (r != nr) + { + size_t kin = (in_deg + out_deg == 0) ? in_degreeS()(v, g) : in_deg; + size_t kout = (in_deg + out_deg == 0) ? out_degreeS()(v, g) : out_deg; + + auto& d_r = deg_delta[r]; + d_r.first -= kin; + d_r.second -= kout; + + auto& d_nr = deg_delta[nr]; + d_nr.first += kin; + d_nr.second += kout; + } + + for (size_t s : bv) + { + S_a += lbinom_fast(_r_count[s] + r_count_delta[s] + _emhist[s] + deg_delta[s].first - 1, + _emhist[s] + deg_delta[s].first); + S_a += lbinom_fast(_r_count[s] + r_count_delta[s] + _ephist[s] + deg_delta[s].second - 1, + _ephist[s] + deg_delta[s].second); + } + + for (size_t s : n_bv) + { + if (!is_in(bv, s)) + { + S_a += lbinom_fast(_r_count[s] + r_count_delta[s] + _emhist[s] + deg_delta[s].first - 1, + _emhist[s] + deg_delta[s].first); + S_a += lbinom_fast(_r_count[s] + r_count_delta[s] + _ephist[s] + deg_delta[s].second - 1, + _ephist[s] + deg_delta[s].second); + } + } + + return S_a - S_b; + } + + template + void move_vertex(size_t v, size_t r, size_t nr, bool, Graph& g, + size_t in_deg = 0, size_t out_deg = 0) + { + if (r == nr) + return; + + r = get_r(r); + nr = get_r(nr); + + auto u =_overlap_stats.get_node(v); + u = get_v(u); + auto& bv = _bvs[u]; + assert(!bv.empty()); + bv_t n_bv; + cdeg_t& deg = _degs[u]; + cdeg_t n_deg; + size_t d = bv.size(); + + bool is_same_bv = get_n_bv(v, r, nr, bv, deg, n_bv, n_deg, g, in_deg, + out_deg); + assert(!n_bv.empty()); + size_t n_d = n_bv.size(); + + if (!is_same_bv) + { + _dhist[d] -= 1; + auto& bv_count = _bhist[bv]; + bv_count -= 1; + + if (bv_count == 0) + { + _bhist.erase(bv); + for (auto s : bv) + { + _r_count[s]--; + if (_r_count[s] == 0) + { + _actual_B--; + _total_B--; + } + } + } + + if (d == _D && _dhist[d] == 0) + { + _D = 1; + for (auto& bc : _bhist) + { + if (bc.second == 0) + continue; + _D = max(_D, bc.first.size()); + } + } + + _dhist[n_d] += 1; + auto& n_bv_count = _bhist[n_bv]; + n_bv_count += 1; + + if (n_bv_count == 1) + { + for (auto s : n_bv) + { + if (_r_count[s] == 0) + { + _actual_B++; + _total_B++; + } + _r_count[s]++; + } + } + + if (n_d > _D) + _D = n_d; + } + + auto& deg_h = _deg_hist[bv]; + auto& deg_count = deg_h[deg]; + deg_count -= 1; + if (deg_count == 0) + deg_h.erase(deg); + auto& bmh = _embhist[bv]; + auto& bph = _epbhist[bv]; + assert(bmh.size() == bv.size()); + assert(bph.size() == bv.size()); + for (size_t i = 0; i < bv.size(); ++i) + { + bmh[i] -= get<0>(deg[i]); + bph[i] -= get<1>(deg[i]); + } + + if (deg_h.empty()) + { + _deg_hist.erase(bv); + _embhist.erase(bv); + _epbhist.erase(bv); + } + + size_t kin = (in_deg + out_deg == 0) ? in_degreeS()(v, g) : in_deg; + size_t kout = (in_deg + out_deg == 0) ? out_degreeS()(v, g) : out_deg; + _emhist[r] -= kin; + _ephist[r] -= kout; + + auto& hist = _deg_hist[n_bv]; + hist[n_deg] += 1; + auto& n_bmh = _embhist[n_bv]; + auto& n_bph = _epbhist[n_bv]; + n_bmh.resize(n_bv.size()); + n_bph.resize(n_bv.size()); + for (size_t i = 0; i < n_bv.size(); ++i) + { + n_bmh[i] += get<0>(n_deg[i]); + n_bph[i] += get<1>(n_deg[i]); + } + + _emhist[nr] += kin; + _ephist[nr] += kout; + + _bvs[u].swap(n_bv); + _degs[u].swap(n_deg); + assert(_bvs[u].size() > 0); + + } + +private: + overlap_stats_t& _overlap_stats; + vector& _bmap; + vector& _vmap; + size_t _N; + size_t _E; + size_t _actual_B; + size_t _total_B; + size_t _D; + vector _dhist; // d-histogram + vector _r_count; // m_r + bhist_t _bhist; // b-histogram + vector _emhist; // e-_r histogram + vector _ephist; // e+_r histogram + ebhist_t _embhist; // e+^r_b histogram + ebhist_t _epbhist; // e+^r_b histogram + deg_hist_t _deg_hist; // n_k^b histogram + vector _bvs; // bv node map + vector _degs; // deg node map +}; + + +template +class SingleEntrySet +{ +public: + SingleEntrySet() : _pos(0) {} + SingleEntrySet(size_t) : SingleEntrySet() {} + + void set_move(size_t, size_t) {} + + void insert_delta(size_t r, size_t s, int delta, bool source, + size_t mrs = numeric_limits::max()) + { + auto& entry = _entries[_pos]; + if (source) + entry = make_pair(s, r); + else + entry = make_pair(r, s); + if (!is_directed::apply::type::value && + entry.second < entry.first) + std::swap(entry.first, entry.second); + _delta[_pos] = delta; + _mrs[_pos] = mrs; + ++_pos; + } + + int get_delta(size_t t, size_t s) + { + auto& entry = _entries[0]; + if (entry.first == t && entry.second == s) + return _delta[0]; + return 0; + } + + void clear() { _pos = 0; } + + const std::array,2>& get_entries() { return _entries; } + const std::array& get_delta() { return _delta; } + std::array& get_mrs() { return _mrs; } + +private: + size_t _pos; + std::array, 2> _entries; + std::array _delta; + std::array _mrs; +}; + +} // namespace graph_tool + +#endif // GRAPH_BLOCKMODEL_OVERLAP_UTIL_HH diff --git a/src/graph/inference/graph_blockmodel_overlap_vacate.cc b/src/graph/inference/graph_blockmodel_overlap_vacate.cc new file mode 100644 index 0000000000000000000000000000000000000000..9c986ba59fea6bbba7d15b383e578f3615fcacf7 --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_vacate.cc @@ -0,0 +1,64 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" +#include "random.hh" + +#include + +#include "graph_blockmodel_overlap_util.hh" +#include "graph_blockmodel_overlap.hh" +#include "graph_blockmodel_overlap_vacate.hh" +#include "bundled_vacate_loop.hh" + +using namespace boost; +using namespace graph_tool; + +GEN_DISPATCH(overlap_block_state, OverlapBlockState, OVERLAP_BLOCK_STATE_params) + +template +GEN_DISPATCH(merge_overlap_block_state, + Merge::template MergeOverlapBlockState, + MERGE_OVERLAP_BLOCK_STATE_params(State)) + +python::object vacate_overlap_sweep(python::object omerge_state, + python::object oblock_state, + rng_t& rng) +{ + python::object ret; + auto dispatch = [&](auto& block_state) + { + typedef typename std::remove_reference::type + state_t; + + merge_overlap_block_state::make_dispatch + (omerge_state, + [&](auto& s) + { + auto ret_ = bundled_vacate_sweep(s, rng); + ret = python::make_tuple(ret_.first, ret_.second); + }); + }; + overlap_block_state::dispatch(oblock_state, dispatch); + return ret; +} + +void export_overlap_blockmodel_vacate() +{ + using namespace boost::python; + def("vacate_overlap_sweep", &vacate_overlap_sweep); +} diff --git a/src/graph/inference/graph_blockmodel_overlap_vacate.hh b/src/graph/inference/graph_blockmodel_overlap_vacate.hh new file mode 100644 index 0000000000000000000000000000000000000000..11a18d8db520b928c6334f401e20491ec538ba6a --- /dev/null +++ b/src/graph/inference/graph_blockmodel_overlap_vacate.hh @@ -0,0 +1,168 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_OVERLAP_BLOCKMODEL_MERGE_HH +#define GRAPH_OVERLAP_BLOCKMODEL_MERGE_HH + +#include "config.h" + +#include + +#include "graph_tool.hh" +#include "graph_state.hh" +#include "graph_blockmodel_overlap_util.hh" +#include + +namespace graph_tool +{ +using namespace boost; +using namespace std; + +typedef vprop_map_t::type vmap_t; + +#define MERGE_OVERLAP_BLOCK_STATE_params(State) \ + ((__class__, &, mpl::vector, 1)) \ + ((state, &, State&, 0)) \ + ((E,, size_t, 0)) \ + ((multigraph,, bool, 0)) \ + ((dense,, bool, 0)) \ + ((partition_dl,, bool, 0)) \ + ((degree_dl,, bool, 0)) \ + ((edges_dl,, bool, 0)) \ + ((verbose,, bool, 0)) \ + ((niter,, size_t, 0)) \ + ((nmerges,, size_t, 0)) + +template +struct Merge +{ + GEN_STATE_BASE(MergeOverlapBlockStateBase, + MERGE_OVERLAP_BLOCK_STATE_params(State)) + + template + class MergeOverlapBlockState + : public MergeOverlapBlockStateBase + { + public: + GET_PARAMS_USING(MergeOverlapBlockStateBase, + MERGE_OVERLAP_BLOCK_STATE_params(State)) + GET_PARAMS_TYPEDEF(Ts, MERGE_OVERLAP_BLOCK_STATE_params(State)) + + template * = nullptr> + MergeOverlapBlockState(ATs&&... as) + : MergeOverlapBlockStateBase(as...), + _g(_state._g), + _null_move(std::numeric_limits::max()) + { + _state._egroups.clear(); + + gt_hash_map>> + block_bundles; + + for (auto v : vertices_range(_g)) + { + auto r = _state._b[v]; + auto& bundles = block_bundles[r]; + for (auto e : all_edges_range(v, _g)) + { + auto w = target(e, _g); + if (w == v) + source(e, _g); + auto s = _state._b[w]; + bundles[s].push_back(v); + } + } + + for (auto& rb : block_bundles) + { + std::vector> bundle; + for (auto& sv : rb.second) + bundle.push_back(std::move(sv.second)); + _block_bundles.push_back(std::move(bundle)); + _block_list.push_back(rb.first); + } + } + + typename state_t::g_t& _g; + std::vector>> _block_bundles; + std::vector _block_list; + const size_t _null_move; + + size_t bundle_state(vector& bundle) + { + return _state._b[bundle[0]]; + } + + template + size_t move_proposal(vector& bundle, bool random, RNG& rng) + { + size_t r = bundle_state(bundle); + + size_t s; + + if (random) + { + s = uniform_sample(_block_list, rng); + if (group_size(s) == 0) + s = r; + } + else + { + auto v = uniform_sample(bundle, rng); + s = _state.sample_block(v, 0, _block_list, rng); + } + + if (s == r || _state._bclabel[r] != _state._bclabel[s]) + return _null_move; + + return s; + } + + double virtual_move_dS(vector& bundle, size_t nr) + { + double dS = 0; + auto r = _state._b[bundle[0]]; + for (auto v : bundle) + { + dS += _state.virtual_move(v, nr, _dense, _multigraph, + _partition_dl, _degree_dl, _edges_dl); + _state.move_vertex(v, nr); + } + for (auto v : bundle) + _state.move_vertex(v, r); + return dS; + } + + void perform_move(vector& bundle, size_t nr) + { + for (auto v : bundle) + _state.move_vertex(v, nr); + } + + size_t group_size(size_t r) + { + return _state._wr[r]; + } + }; +}; + + +} // graph_tool namespace + +#endif //GRAPH_OVERLAP_BLOCKMODEL_MERGE_HH diff --git a/src/graph/inference/graph_blockmodel_util.hh b/src/graph/inference/graph_blockmodel_util.hh new file mode 100644 index 0000000000000000000000000000000000000000..efbfe1039bc93c7c88af1f274131a542000e2c7c --- /dev/null +++ b/src/graph/inference/graph_blockmodel_util.hh @@ -0,0 +1,1297 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_BLOCKMODEL_UTIL_HH +#define GRAPH_BLOCKMODEL_UTIL_HH + +#include "config.h" + +#include + +#include "hash_map_wrap.hh" + +#include "../generation/sampler.hh" +#include "../generation/dynamic_sampler.hh" + +#include "util.hh" +#include "cache.hh" + +#include + +#ifdef USING_OPENMP +#include +#endif + +namespace graph_tool +{ + +// ==================== +// Entropy calculation +// ==================== + + +// Sparse entropy terms +// ==================== + +// "edge" term of the entropy +template +inline double eterm(size_t r, size_t s, size_t mrs, const Graph&) +{ + if (!is_directed::apply::type::value && r == s) + mrs *= 2; + + double val = xlogx(mrs); + + if (is_directed::apply::type::value || r != s) + return -val; + else + return -val / 2; +} + +// "vertex" term of the entropy +template +inline double vterm(size_t mrp, size_t mrm, size_t wr, bool deg_corr, + Graph&) +{ + double one = 0.5; + + if (is_directed::apply::type::value) + one = 1; + + if (deg_corr) + return one * (xlogx(mrm) + xlogx(mrp)); + else + return one * (mrm * safelog(wr) + mrp * safelog(wr)); +} + +// Dense entropy +// ============= + +// "edge" term of the entropy +template +inline double eterm_dense(size_t r, size_t s, int ers, double wr_r, + double wr_s, bool multigraph, const Graph&) +{ + // we should not use integers here, since they may overflow + double nrns; + + if (ers == 0) + return 0.; + + if (r != s || is_directed::apply::type::value) + { + nrns = wr_r * wr_s; + } + else + { + if (multigraph) + nrns = (wr_r * (wr_r + 1)) / 2; + else + nrns = (wr_r * (wr_r - 1)) / 2; + } + + double S; + if (multigraph) + S = lbinom(nrns + ers - 1, ers); // do not use lbinom_fast! + else + S = lbinom(nrns, ers); + return S; +} + +// =============== +// Partition stats +// =============== + +typedef vprop_map_t>>::type + degs_map_t; + +struct simple_degs_t {}; + +template +auto get_degs(size_t v, Vprop& vweight, Eprop& eweight, const simple_degs_t&, + Graph& g) +{ + std::array, 1> + degs {{make_tuple(in_degreeS()(v, g, eweight), + out_degreeS()(v, g, eweight), + vweight[v])}}; + return degs; +} + +template +auto& get_degs(size_t v, Vprop&, Eprop&, typename degs_map_t::unchecked_t& degs, + Graph&) +{ + return degs[v]; +} + +class partition_stats_t +{ +public: + + typedef gt_hash_map, int> map_t; + + template + partition_stats_t(Graph& g, Vprop& b, Vlist& vlist, size_t E, size_t B, + VWprop& vweight, Eprop& eweight, Degs& degs, + const Mprop& ignore_degree, std::vector& bmap) + : _bmap(bmap) + { + _N = 0; + _E = E; + _total_B = B; + + for (auto v : vlist) + { + auto r = get_r(b[v]); + + auto&& ks = get_degs(v, vweight, eweight, degs, g); + if (v >= _ignore_degree.size()) + _ignore_degree.resize(v + 1, 0); + _ignore_degree[v] = ignore_degree[v]; + for (auto& k : ks) + { + size_t kin = get<0>(k); + size_t kout = get<1>(k); + size_t n = get<2>(k); + if (_ignore_degree[v] == 2) + kout = 0; + if (_ignore_degree[v] != 1) + { + _hist[r][make_pair(kin, kout)] += n; + _em[r] += kin * n; + _ep[r] += kout * n; + } + _total[r] += n; + _N += n; + } + } + + _actual_B = 0; + for (auto n : _total) + { + if (n > 0) + _actual_B++; + } + } + + size_t get_r(size_t r) + { + constexpr size_t null = + std::numeric_limits::max(); + if (r >= _bmap.size()) + _bmap.resize(r + 1, null); + size_t nr = _bmap[r]; + if (nr == null) + nr = _bmap[r] = _hist.size(); + if (nr >= _hist.size()) + { + _hist.resize(nr + 1); + _total.resize(nr + 1); + _ep.resize(nr + 1); + _em.resize(nr + 1); + } + return nr; + } + + double get_partition_dl() + { + double S = 0; + S += lbinom(_actual_B + _N - 1, _N); + S += lgamma(_N + 1); + for (auto nr : _total) + S -= lgamma(nr + 1); + return S; + } + + double get_deg_dl(bool ent, bool dl_alt, bool xi_fast) + { + double S = 0; + for (size_t r = 0; r < _ep.size(); ++r) + { + if (_ep[r] + _em[r] == 0) + continue; + + if (ent) + { + for (auto& k_c : _hist[r]) + { + double p = k_c.second / double(_total[r]); + S -= p * log(p) * _total[r]; + } + } + else + { + double S1 = 0; + + if (xi_fast) + { + S1 += get_xi_fast(_total[r], _ep[r]); + S1 += get_xi_fast(_total[r], _em[r]); + } + else + { + S1 += get_xi(_total[r], _ep[r]); + S1 += get_xi(_total[r], _em[r]); + } + + S1 += lgamma(_total[r] + 1); + for (auto& k_c : _hist[r]) + S1 -= lgamma(k_c.second + 1); + + if (dl_alt) + { + double S2 = 0; + S2 += lbinom(_total[r] + _ep[r] - 1, _ep[r]); + S2 += lbinom(_total[r] + _em[r] - 1, _em[r]); + S += min(S1, S2); + } + else + { + S += S1; + } + } + } + return S; + } + + template + double get_delta_dl(size_t v, size_t r, size_t nr, VProp& vweight) + { + if (r == nr) + return 0; + r = get_r(r); + nr = get_r(nr); + int n = vweight[v]; + + if (n == 0) + return 0; + + double S_b = 0, S_a = 0; + + S_b += -lgamma_fast(_total[r] + 1); + S_a += -lgamma_fast(_total[r] - n + 1); + S_b += -lgamma_fast(_total[nr] + 1); + S_a += -lgamma_fast(_total[nr] + n + 1); + + int dB = 0; + if (_total[r] == n) + dB--; + if (_total[nr] == 0) + dB++; + + if (dB != 0) + { + S_b += lbinom(_actual_B + _N - 1, _N); + S_a += lbinom(_actual_B + dB + _N - 1, _N); + } + + return S_a - S_b; + } + + template + double get_delta_edges_dl(size_t v, size_t r, size_t nr, VProp& vweight, + Graph&) + { + if (r == nr) + return 0; + + r = get_r(r); + nr = get_r(nr); + + double S_b = 0, S_a = 0; + + int n = vweight[v]; + + if (n == 0) + return 0; + + int dB = 0; + if (_total[r] == n) + dB--; + if (_total[nr] == 0) + dB++; + + if (dB != 0) + { + auto get_x = [](size_t B) + { + if (is_directed::apply::type::value) + return B * B; + else + return (B * (B + 1)) / 2; + }; + + S_b += lbinom(get_x(_total_B) + _E - 1, _E); + S_a += lbinom(get_x(_total_B + dB) + _E - 1, _E); + } + + return S_a - S_b; + } + + template + double get_delta_deg_dl(size_t v, size_t r, size_t nr, VProp& vweight, + EProp& eweight, Degs& degs, Graph& g) + { + if (r == nr || _ignore_degree[v] == 1) + return 0; + r = get_r(r); + nr = get_r(nr); + auto&& ks = get_degs(v, vweight, eweight, degs, g); + double dS = 0; + dS += get_delta_deg_dl_change(v, r, ks, -1); + dS += get_delta_deg_dl_change(v, nr, ks, +1); + return dS; + } + + double get_Se (size_t s, int delta, int kin, int kout) + { + double S = 0; + assert(_total[s] + delta >= 0); + assert(_em[s] + kin >= 0); + assert(_ep[s] + kout >= 0); + S += get_xi_fast(_total[s] + delta, _em[s] + kin); + S += get_xi_fast(_total[s] + delta, _ep[s] + kout); + return S; + }; + + double get_Sr(size_t s, int delta) + { + assert(_total[s] + delta + 1 >= 0); + return lgamma_fast(_total[s] + delta + 1); + }; + + double get_Sk(size_t s, pair& deg, int delta) + { + int nd = 0; + auto iter = _hist[s].find(deg); + if (iter != _hist[s].end()) + nd = iter->second; + assert(nd + delta >= 0); + return -lgamma_fast(nd + delta + 1); + }; + + + template + double get_delta_deg_dl_change(size_t v, size_t r, Ks& ks, int diff) + { + double S_b = 0, S_a = 0; + int tkin = 0, tkout = 0, n = 0; + for (auto& k : ks) + { + size_t kin = get<0>(k); + size_t kout = get<1>(k); + int nk = get<2>(k); + tkin += kin * nk; + if (_ignore_degree[v] != 2) + tkout += kout * nk; + n += nk; + + auto deg = make_pair(kin, kout); + S_b += get_Sk(r, deg, 0); + S_a += get_Sk(r, deg, diff * nk); + } + + S_b += get_Se(r, 0, 0, 0); + S_a += get_Se(r, diff * n, diff * tkin, diff * tkout); + + S_b += get_Sr(r, 0); + S_a += get_Sr(r, diff * n); + + return S_a - S_b; + } + + template + void change_vertex(size_t v, size_t r, bool deg_corr, Graph& g, + VWeight& vweight, EWeight& eweight, Degs& degs, + int diff) + { + auto&& ks = get_degs(v, vweight, eweight, degs, g); + for (auto& k : ks) + { + auto kin = get<0>(k); + auto kout = get<1>(k); + auto n = get<2>(k); + change_k(v, r, deg_corr, n, kin, kout, diff); + } + } + + template + void remove_vertex(size_t v, size_t r, bool deg_corr, Graph& g, + VWeight& vweight, EWeight& eweight, Degs& degs) + { + r = get_r(r); + change_vertex(v, r, deg_corr, g, vweight, eweight, degs, -1); + } + + template + void add_vertex(size_t v, size_t nr, bool deg_corr, Graph& g, + VWeight& vweight, EWeight& eweight, Degs& degs) + { + nr = get_r(nr); + change_vertex(v, nr, deg_corr, g, vweight, eweight, degs, 1); + } + + void change_k(size_t v, size_t r, bool deg_corr, int vweight, + int kin, int kout, int diff) + { + if (_total[r] == 0 && diff * vweight > 0) + { + _actual_B++; + _total_B++; + } + + if (_total[r] == vweight && diff * vweight < 0) + { + _actual_B--; + _total_B--; + } + + _total[r] += diff * vweight; + _N += diff * vweight; + + if (deg_corr && _ignore_degree[v] != 1) + { + if (_ignore_degree[v] == 2) + kout = 0; + auto deg = make_pair(kin, kout); + _hist[r][deg] += diff * vweight; + _em[r] += diff * deg.first * vweight; + _ep[r] += diff * deg.second * vweight; + } + } + +private: + vector& _bmap; + size_t _N; + size_t _E; + size_t _actual_B; + size_t _total_B; + vector _hist; + vector _total; + vector _ep; + vector _em; + vector _ignore_degree; +}; + +// =============================== +// Block moves +// =============================== + +// this structure speeds up the access to the edges between given blocks, since +// we're using an adjacency list to store the block structure (it is simply an +// adjacency matrix) + +template +class EMat +{ +public: + template + EMat(Graph& g, Vprop b, BGraph& bg, RNG&) + : _bedge(get(edge_index_t(), g), 0) + { + size_t B = num_vertices(bg); + _mat.resize(boost::extents[B][B]); + for (auto e : edges_range(bg)) + { + _mat[source(e, bg)][target(e, bg)] = e; + if (!is_directed::apply::type::value) + _mat[target(e, bg)][source(e, bg)] = e; + } + + auto bedge_c = _bedge.get_checked(); + for (auto e : edges_range(g)) + { + auto r = b[source(e, g)]; + auto s = b[target(e, g)]; + bedge_c[e] = _mat[r][s]; + } + } + + typedef typename graph_traits::vertex_descriptor vertex_t; + typedef typename graph_traits::edge_descriptor edge_t; + + const auto& get_me(vertex_t r, vertex_t s) const + { + return _mat[r][s]; + } + + void put_me(vertex_t r, vertex_t s, const edge_t& e) + { + _mat[r][s] = e; + if (!is_directed::apply::type::value && r != s) + _mat[s][r] = e; + } + + void remove_me(vertex_t r, vertex_t s, const edge_t& me, BGraph& bg, + bool delete_edge=false) + { + if (delete_edge) + { + _mat[r][s] = edge_t(); + if (!is_directed::apply::type::value) + _mat[s][r] = edge_t(); + remove_edge(me, bg); + } + } + + template + auto& get_bedge(const Edge& e) { return _bedge[e]; } + auto& get_bedge_map() { return _bedge; } + const auto& get_bedge_map() const { return _bedge; } + const auto& get_null_edge() const { return _null_edge; } + +private: + multi_array _mat; + typedef typename property_map_type::apply + ::type>::type bedge_t; + typename bedge_t::unchecked_t _bedge; + static const edge_t _null_edge; +}; + +template +const typename EMat::edge_t EMat::_null_edge; + + +template +class perfect_hash_t +{ +public: + template + perfect_hash_t(size_t N, RNG& rng) + : _index(std::make_shared>()) + { + auto& index = *_index; + index.reserve(N); + for (size_t i = 0; i < N; ++i) + index.push_back(i); + std::shuffle(index.begin(), index.end(), rng); + } + perfect_hash_t() {} + size_t operator()(const Key& k) const {return (*_index)[k];} +private: + std::shared_ptr> _index; +}; + +// this structure speeds up the access to the edges between given blocks, since +// we're using an adjacency list to store the block structure (this is like +// EMat above, but takes less space and is slower) + +template +class EHash +{ +public: + + template + EHash(Graph& g, Vprop b, BGraph& bg, RNG& rng) + : _hash_function(num_vertices(bg), rng), + _hash(num_vertices(bg), ehash_t(0, _hash_function)), + _bedge(get(edge_index_t(), g), 0) + { + + for (auto e : edges_range(bg)) + put_me(source(e, bg), target(e, bg), e); + + auto bedge_c = _bedge.get_checked(); + for (auto e : edges_range(g)) + { + auto r = b[source(e, g)]; + auto s = b[target(e, g)]; + bedge_c[e] = get_me(r, s); + } + } + + typedef typename graph_traits::vertex_descriptor vertex_t; + typedef typename graph_traits::edge_descriptor edge_t; + + const auto& get_me(vertex_t r, vertex_t s) const + { + auto& map = _hash[r]; + const auto& iter = map.find(s); + if (iter == map.end()) + return _null_edge; + return iter->second; + } + + void put_me(vertex_t r, vertex_t s, const edge_t& e) + { + assert(r < _hash.size()); + _hash[r][s] = e; + if (!is_directed::apply::type::value) + _hash[s][r] = e; + } + + void remove_me(vertex_t r, vertex_t s, const edge_t& me, BGraph& bg, + bool delete_edge = true) + { + if (delete_edge) + { + assert(r < _hash.size()); + _hash[r].erase(s); + if (!is_directed::apply::type::value) + _hash[s].erase(r); + remove_edge(me, bg); + } + } + + template + auto& get_bedge(const Edge& e) { return _bedge[e]; } + auto& get_bedge_map() { return _bedge; } + const auto& get_bedge_map() const { return _bedge; } + const auto& get_null_edge() const { return _null_edge; } + +private: + perfect_hash_t _hash_function; + typedef gt_hash_map> ehash_t; + std::vector _hash; + typedef typename property_map_type::apply + ::type>::type bedge_t; + typename bedge_t::unchecked_t _bedge; + static const edge_t _null_edge; +}; + +template +const typename EHash::edge_t EHash::_null_edge; + +template +inline size_t get_mrs(Vertex r, Vertex s, const Eprop& mrs, const Emat& emat) +{ + const auto& me = emat.get_me(r, s); + if (me != emat.get_null_edge()) + return mrs[me]; + return 0; +} + + +// Manage a set of block pairs and corresponding edge counts that will be +// updated + +template +class EntrySet +{ +public: + EntrySet(size_t B) + { + _r_field_t.resize(B, _null); + _nr_field_t.resize(B, _null); + + if (is_directed::apply::type::value) + { + _r_field_s.resize(B, _null); + _nr_field_s.resize(B, _null); + } + } + + void set_move(size_t r, size_t nr) + { + _rnr = make_pair(r, nr); + } + + void insert_delta(size_t r, size_t s, int delta, bool source, + size_t mrs = numeric_limits::max()) + { + if (s == _rnr.first || s == _rnr.second) + { + if ((!is_directed::apply::type::value && s < r) || source) + std::swap(r, s); + if (source) + source = false; + } + + if (source && (s == r)) + source = false; + + auto& r_field = (source) ? _r_field_s : _r_field_t; + auto& nr_field = (source) ? _nr_field_s : _nr_field_t; + + vector& field = (_rnr.first == r) ? r_field : nr_field; + if (field[s] == _null) + { + field[s] = _entries.size(); + if ((!is_directed::apply::type::value && s < r) || source) + _entries.emplace_back(s, r); + else + _entries.emplace_back(r, s); + _delta.push_back(delta); + _mrs.push_back(mrs); + } + else + { + _delta[field[s]] += delta; + } + } + + int get_delta(size_t t, size_t s) + { + if (is_directed::apply::type::value) + { + if (t == _rnr.first || t == _rnr.second) + return get_delta_target(t, s); + if (s == _rnr.first || s == _rnr.second) + return get_delta_source(t, s); + } + else + { + if (t == _rnr.first || t == _rnr.second) + return get_delta_target(t, s); + if (s == _rnr.first || s == _rnr.second) + return get_delta_target(s, t); + } + return 0; + } + + int get_delta_target(size_t r, size_t s) + { + vector& field = (_rnr.first == r) ? _r_field_t : _nr_field_t; + if (field[s] == _null) + return 0; + else + return _delta[field[s]]; + } + + int get_delta_source(size_t s, size_t r) + { + vector& field = (_rnr.first == r) ? _r_field_s : _nr_field_s; + if (field[s] == _null) + return 0; + else + return _delta[field[s]]; + } + + void clear() + { + for (const auto& rs : _entries) + { + size_t r = rs.first; + size_t s = rs.second; + _r_field_t[r] = _nr_field_t[r] = _null; + _r_field_t[s] = _nr_field_t[s] = _null; + if (is_directed::apply::type::value) + { + _r_field_s[r] = _nr_field_s[r] = _null; + _r_field_s[s] = _nr_field_s[s] = _null; + } + } + _entries.clear(); + _delta.clear(); + _mrs.clear(); + } + + const vector >& get_entries() { return _entries; } + const vector& get_delta() { return _delta; } + vector& get_mrs() { return _mrs; } + +private: + static constexpr size_t _null = numeric_limits::max(); + + pair _rnr; + vector _r_field_t; + vector _nr_field_t; + vector _r_field_s; + vector _nr_field_s; + vector > _entries; + vector _delta; + vector _mrs; +}; + +template +constexpr size_t EntrySet::_null; + +struct is_loop_nop +{ + bool operator()(size_t) const { return false; } +}; + + +struct standard_neighbours_policy +{ + template + IterRange::type> + get_out_edges(Vertex v, Graph& g) const + { + return out_edges_range(v, g); + } + + template + IterRange::type> + get_in_edges(Vertex v, Graph& g) const + { + return in_edges_range(v, g); + } + + template + int get_out_degree(Vertex& v, Graph& g, Weight& eweight) const + { + return out_degreeS()(v, g, eweight); + } + + template + int get_in_degree(Vertex& v, Graph& g, Weight& eweight) const + { + return in_degreeS()(v, g, eweight); + } +}; + +// obtain the necessary entries in the e_rs matrix which need to be modified +// after the move +template +void move_entries(Vertex v, Vertex nr, Vprop& b, Eprop& eweights, CEprop& mrs, + EBedge& bedge, Graph& g, BGraph& bg, MEntries& m_entries, + const NPolicy& npolicy = NPolicy(), IL is_loop = IL()) +{ + typedef typename graph_traits::vertex_descriptor vertex_t; + vertex_t r = b[v]; + + m_entries.set_move(r, nr); + + remove_entries(v, r, b, eweights, mrs, bedge, g, bg, m_entries, npolicy, + is_loop); + add_entries(v, nr, b, eweights, g, bg, m_entries, npolicy, is_loop); +} + +template +void remove_entries(Vertex v, Vertex r, Vprop& b, Eprop& eweights, CEprop& mrs, + EBedge& bedge, Graph& g, BGraph&, MEntries& m_entries, + const NPolicy& npolicy = NPolicy(), IL is_loop = IL()) +{ + typedef typename graph_traits::vertex_descriptor vertex_t; + int self_weight = 0; + for (auto e : npolicy.get_out_edges(v, g)) + { + vertex_t u = target(e, g); + vertex_t s = b[u]; + int ew = eweights[e]; + //assert(ew > 0); + + const auto& me = bedge[e]; + + m_entries.insert_delta(r, s, -ew, false, mrs[me]); + + if (u == v || is_loop(v)) + { + if (!is_directed::apply::type::value) + self_weight += ew; + } + } + + if (self_weight > 0 && self_weight % 2 == 0) + m_entries.insert_delta(r, r, self_weight / 2, false); + + for (auto e : npolicy.get_in_edges(v, g)) + { + vertex_t u = source(e, g); + if (u == v) + continue; + vertex_t s = b[u]; + int ew = eweights[e]; + + const auto& me = bedge[e]; + + m_entries.insert_delta(r, s, -ew, true, mrs[me]); + } +} + +template +void add_entries(Vertex v, Vertex nr, Vprop& b, Eprop& eweights, Graph& g, + BGraph&, MEntries& m_entries, + const NPolicy& npolicy = NPolicy(), IL is_loop = IL()) +{ + typedef typename graph_traits::vertex_descriptor vertex_t; + int self_weight = 0; + for (auto e : npolicy.get_out_edges(v, g)) + { + vertex_t u = target(e, g); + vertex_t s = b[u]; + int ew = eweights[e]; + //assert(ew > 0); + + if (u == v || is_loop(v)) + { + s = nr; + if (!is_directed::apply::type::value) + self_weight += ew; + } + m_entries.insert_delta(nr, s, +ew, false); + } + + if (self_weight > 0 && self_weight % 2 == 0) + m_entries.insert_delta(nr, nr, -self_weight / 2, false); + + for (auto e : npolicy.get_in_edges(v, g)) + { + vertex_t u = source(e, g); + if (u == v) + continue; + vertex_t s = b[u]; + int ew = eweights[e]; + m_entries.insert_delta(nr, s, +ew, true); + } +} + + +// obtain the entropy difference given a set of entries in the e_rs matrix +template +double entries_dS(MEntries& m_entries, Eprop& mrs, EMat& emat, BGraph& bg) +{ + const auto& entries = m_entries.get_entries(); + const auto& delta = m_entries.get_delta(); + auto& d_mrs = m_entries.get_mrs(); + + double dS = 0; + for (size_t i = 0; i < entries.size(); ++i) + { + auto& entry = entries[i]; + auto er = entry.first; + auto es = entry.second; + int d = delta[i]; + size_t& ers = d_mrs[i]; + if (ers == numeric_limits::max()) + ers = get_mrs(er, es, mrs, emat); // slower + assert(int(ers) + d >= 0); + dS += eterm(er, es, ers + d, bg) - eterm(er, es, ers, bg); + } + return dS; +} + + +// ==================================== +// Construct and manage half-edge lists +// ==================================== + +//the following guarantees a stable (source, target) ordering even for +//undirected graphs +template +inline typename graph_traits::vertex_descriptor +get_source(const Edge& e, const Graph &g) +{ + if (is_directed::apply::type::value) + return source(e, g); + return min(source(e, g), target(e, g)); +} + +template +inline typename graph_traits::vertex_descriptor +get_target(const Edge& e, const Graph &g) +{ + if (is_directed::apply::type::value) + return target(e, g); + return max(source(e, g), target(e, g)); +} + + +template +class EGroups +{ +public: + template + void init(Vprop b, Eprop eweight, Graph& g, BGraph& bg) + { + _egroups.clear(); + _egroups.resize(num_vertices(bg)); + + for (auto e : edges_range(g)) + { + size_t r = b[get_source(e, g)]; + auto& r_elist = _egroups[r]; + _epos[e].first = insert_edge(std::make_tuple(e, true), r_elist, + eweight[e]); + + size_t s = b[get_target(e, g)]; + auto& s_elist = _egroups[s]; + _epos[e].second = insert_edge(std::make_tuple(e, false), s_elist, + eweight[e]); + } + } + + void clear() + { + _egroups.clear(); + } + + bool empty() + { + return _egroups.empty(); + } + + + template + size_t insert_edge(const Edge& e, EV& elist, size_t) + { + elist.push_back(e); + return elist.size() - 1; + } + + template + size_t insert_edge(const Edge& e, DynamicSampler& elist, + size_t weight) + { + return elist.insert(e, weight); + } + + + template + void remove_edge(size_t pos, vector& elist) + { + if (get<1>(elist.back())) + _epos[get<0>(elist.back())].first = pos; + else + _epos[get<0>(elist.back())].second = pos; + if (get<1>(elist[pos])) + _epos[get<0>(elist[pos])].first = numeric_limits::max(); + else + _epos[get<0>(elist[pos])].second = numeric_limits::max(); + elist[pos] = elist.back(); + elist.pop_back(); + } + + template + void remove_edge(size_t pos, DynamicSampler& elist) + { + if (get<1>(elist[pos])) + _epos[get<0>(elist[pos])].first = numeric_limits::max(); + else + _epos[get<0>(elist[pos])].second = numeric_limits::max(); + elist.remove(pos); + } + + template + void remove_vertex(Vertex v, Vertex r, Graph& g) + { + if (_egroups.empty()) + return; + + typedef Vertex vertex_t; + + auto& elist = _egroups[r]; + + // update the half-edge lists + for (auto e : all_edges_range(v, g)) + { + vertex_t src = get_source(e, g); + vertex_t tgt = get_target(e, g); + + bool is_src = (src == v); + + // self-loops will appear twice; we must disambiguate + if (src == tgt) + { + size_t pos = _epos[e].first; + is_src = (pos < elist.size() && get<0>(elist[pos]) == e); + } + + size_t pos = (is_src) ? _epos[e].first : _epos[e].second; + assert(pos < elist.size()); + assert(get<0>(elist[pos]) == e); + remove_edge(pos, elist); + } + } + + template + void add_vertex(Vertex v, Vertex s, Eprop& eweight, Graph& g) + { + if (_egroups.empty()) + return; + + typedef Vertex vertex_t; + + auto& elist = _egroups[s]; + + //update the half-edge lists + for (auto e : all_edges_range(v, g)) + { + vertex_t src = get_source(e, g); + vertex_t tgt = get_target(e, g); + + bool is_src = (src == v); + + // self-loops will appear twice; we must disambiguate + if (src == tgt) + { + size_t pos = _epos[e].first; + is_src = !(pos < elist.size() && get<0>(elist[pos]) == e); + } + + typedef typename graph_traits::edge_descriptor e_type; + size_t pos = insert_edge(std::make_tuple(e_type(e), is_src), + elist, size_t(eweight[e])); + if (is_src) + _epos[e].first = pos; + else + _epos[e].second = pos; + } + } + + template + void move_vertex(Vertex v, Vertex r, Vertex s, Eprop& eweight, + Graph& g) + { + if (r == s) + return; + remove_egroups(v, r, g); + add_egroups(v, s, eweight, g); + } + + template + const auto& sample_edge(const DynamicSampler& elist, RNG& rng) + { + return get<0>(elist.sample(rng)); + } + + template + const auto& sample_edge(const vector& elist, RNG& rng) + { + std::uniform_int_distribution urand(0, elist.size() - 1); + size_t ur = urand(rng); + return get<0>(elist[ur]); + } + + template + const auto& sample_edge(Vertex r, RNG& rng) + { + return sample_edge(_egroups[r], rng); + } + +private: + typedef typename std::conditional::edge_descriptor, bool>>, + vector::edge_descriptor, bool>>>::type + sampler_t; + vector _egroups; + + typedef typename eprop_map_t>::type epos_t; + epos_t _epos; +}; + + +// Sample neighbours efficiently +// ============================= + +template +void build_neighbour_sampler(Vertex v, SMap& sampler, Eprop& eweight, Graph& g, + bool self_loops=true) +{ + vector neighbours; + vector probs; + neighbours.reserve(total_degreeS()(v, g)); + probs.reserve(total_degreeS()(v, g)); + + for (auto e : all_edges_range(v, g)) + { + Vertex u = target(e, g); + if (is_directed::apply::type::value && u == v) + u = source(e, g); + if (!self_loops && u == v) + continue; + neighbours.push_back(u); + probs.push_back(eweight[e]); // Self-loops will be added twice, and + // hence will be sampled with probability + // 2 * eweight[e] + } + + if (probs.empty()) + { + neighbours.push_back(v); + probs.push_back(1.); + } + + sampler = Sampler(neighbours, probs); +}; + +template +void build_neighbour_sampler(Vertex v, vector& sampler, Eprop&, Graph& g, + bool self_loops=true) +{ + sampler.clear(); + for (auto e : all_edges_range(v, g)) + { + Vertex u = target(e, g); + if (is_directed::apply::type::value && u == v) + u = source(e, g); + if (!self_loops && u == v) + continue; + sampler.push_back(u); // Self-loops will be added twice + } +}; + +template +void init_neighbour_sampler(Graph& g, Eprop eweight, Sampler& sampler) +{ + for (auto v : vertices_range(g)) + build_neighbour_sampler(v, sampler[v], eweight, g); +} + +template +auto sample_neighbour(Sampler& sampler, RNG& rng) +{ + return sampler.sample(rng); +} + +template +auto sample_neighbour(vector& sampler, RNG& rng) +{ + std::uniform_int_distribution rand(0, sampler.size() - 1); + return sampler[rand(rng)]; +} + + +// Sampling marginal probabilities on the edges +template +void collect_edge_marginals(size_t B, Vprop b, MEprop p, Graph& g, Graph&) +{ + for (auto e : edges_range(g)) + { + auto u = min(source(e, g), target(e, g)); + auto v = max(source(e, g), target(e, g)); + + auto r = b[u]; + auto s = b[v]; + + auto& pv = p[e]; + if (pv.size() < B * B) + pv.resize(B * B); + size_t j = r + B * s; + pv[j]++; + } +} + +template +void collect_vertex_marginals(Vprop b, VVprop p, Graph& g) +{ + for (auto v : vertices_range(g)) + { + auto r = b[v]; + auto& pv = p[v]; + if (pv.size() <= size_t(r)) + pv.resize(r + 1); + pv[r]++; + } +} + +} // graph_tool namespace + +#endif //GRAPH_BLOCKMODEL_UTIL_HH diff --git a/src/graph/inference/graph_inference.cc b/src/graph/inference/graph_inference.cc new file mode 100644 index 0000000000000000000000000000000000000000..389f35bafaad4702183820041c266c64247e52bb --- /dev/null +++ b/src/graph/inference/graph_inference.cc @@ -0,0 +1,188 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#include "graph_tool.hh" + +#include +#include "numpy_bind.hh" +#include "hash_map_wrap.hh" + +using namespace std; +using namespace boost; +using namespace graph_tool; + +void collect_vertex_marginals(GraphInterface& gi, boost::any ob, + boost::any op) +{ + typedef vprop_map_t::type vmap_t; + auto b = any_cast(ob).get_unchecked(); + + run_action<>() + (gi, [&](auto& g, auto p) + { + parallel_vertex_loop + (g, + [&](auto v) + { + auto r = b[v]; + auto& pv = p[v]; + if (pv.size() <= size_t(r)) + pv.resize(r + 1); + pv[r]++; + }); + }, + vertex_scalar_vector_properties())(op); +} + +void collect_edge_marginals(GraphInterface& gi, size_t B, boost::any ob, + boost::any op) +{ + typedef vprop_map_t::type vmap_t; + auto b = any_cast(ob).get_unchecked(); + + run_action<>() + (gi, + [&](auto& g, auto p) + { + parallel_edge_loop + (g, + [&](const auto& e) + { + auto u = min(source(e, g), target(e, g)); + auto v = max(source(e, g), target(e, g)); + + auto r = b[u]; + auto s = b[v]; + + auto& pv = p[e]; + if (pv.size() < B * B) + pv.resize(B * B); + size_t j = r + B * s; + pv[j]++; + }); + }, + edge_scalar_vector_properties())(op); +} + + +template +void vector_map(boost::python::object ovals, boost::python::object omap) +{ + + multi_array_ref vals = get_array(ovals); + multi_array_ref map = get_array(omap); + + size_t pos = 0; + for (size_t i = 0; i < vals.size(); ++i) + { + Value v = vals[i]; + if (map[v] == -1) + map[v] = pos++; + vals[i] = map[v]; + } +} + +template +void vector_continuous_map(boost::python::object ovals) +{ + + multi_array_ref vals = get_array(ovals); + gt_hash_map map; + + for (size_t i = 0; i < vals.size(); ++i) + { + Value v = vals[i]; + auto iter = map.find(v); + if (iter == map.end()) + iter = map.insert(make_pair(v, map.size())).first; + vals[i] = iter->second; + } +} + +template +void vector_rmap(boost::python::object ovals, boost::python::object omap) +{ + + multi_array_ref vals = get_array(ovals); + multi_array_ref map = get_array(omap); + + for (size_t i = 0; i < vals.size(); ++i) + { + map[vals[i]] = i; + } +} + +extern void export_blockmodel_state(); +extern void export_blockmodel_mcmc(); +extern void export_blockmodel_multicanonical(); +extern void export_blockmodel_merge(); +extern void export_blockmodel_gibbs(); +extern void export_overlap_blockmodel_state(); +extern void export_overlap_blockmodel_mcmc(); +extern void export_overlap_blockmodel_mcmc_bundled(); +extern void export_overlap_blockmodel_multicanonical(); +extern void export_overlap_blockmodel_gibbs(); +extern void export_overlap_blockmodel_vacate(); +extern void export_layered_blockmodel_state(); +extern void export_layered_blockmodel_mcmc(); +extern void export_layered_blockmodel_merge(); +extern void export_layered_blockmodel_gibbs(); +extern void export_layered_blockmodel_multicanonical(); +extern void export_layered_overlap_blockmodel_state(); +extern void export_layered_overlap_blockmodel_mcmc(); +extern void export_layered_overlap_blockmodel_bundled_mcmc(); +extern void export_layered_overlap_blockmodel_gibbs(); +extern void export_layered_overlap_blockmodel_multicanonical(); +extern void export_layered_overlap_blockmodel_vacate(); + +BOOST_PYTHON_MODULE(libgraph_tool_inference) +{ + using namespace boost::python; + export_blockmodel_state(); + export_blockmodel_mcmc(); + export_blockmodel_multicanonical(); + export_blockmodel_merge(); + export_blockmodel_gibbs(); + export_overlap_blockmodel_state(); + export_overlap_blockmodel_mcmc(); + export_overlap_blockmodel_mcmc_bundled(); + export_overlap_blockmodel_multicanonical(); + export_overlap_blockmodel_gibbs(); + export_overlap_blockmodel_vacate(); + export_layered_blockmodel_state(); + export_layered_blockmodel_mcmc(); + export_layered_blockmodel_mcmc(); + export_layered_blockmodel_merge(); + export_layered_blockmodel_gibbs(); + export_layered_blockmodel_multicanonical(); + export_layered_overlap_blockmodel_state(); + export_layered_overlap_blockmodel_mcmc(); + export_layered_overlap_blockmodel_bundled_mcmc(); + export_layered_overlap_blockmodel_gibbs(); + export_layered_overlap_blockmodel_multicanonical(); + export_layered_overlap_blockmodel_vacate(); + + def("vertex_marginals", collect_vertex_marginals); + def("edge_marginals", collect_edge_marginals); + + def("vector_map", vector_map); + def("vector_map64", vector_map); + def("vector_rmap", vector_rmap); + def("vector_rmap64", vector_rmap); + def("vector_continuous_map", vector_continuous_map); + def("vector_continuous_map64", vector_continuous_map); +} diff --git a/src/graph/inference/graph_state.hh b/src/graph/inference/graph_state.hh new file mode 100644 index 0000000000000000000000000000000000000000..bc2a221c30978a12737d7543f8e2dc977153bbc8 --- /dev/null +++ b/src/graph/inference/graph_state.hh @@ -0,0 +1,424 @@ +// graph-tool -- a general graph modification and manipulation thingy +// +// Copyright (C) 2006-2016 Tiago de Paula Peixoto +// +// 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 . + +#ifndef GRAPH_STATE_HH +#define GRAPH_STATE_HH + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "graph.hh" +#include "graph_selectors.hh" +#include "graph_properties.hh" +#include "graph_filtering.hh" +#include "graph_util.hh" + +#include "config.h" + +namespace graph_tool +{ +using namespace boost; + +template +struct nth +{ + template + auto&& operator()(T&&, Ts&&... as) const + { + static_assert(N < sizeof...(as) + 1, "wrong number of arguments"); + return nth()(std::forward(as)...); + } +}; + +template <> +struct nth<0> +{ + template + auto&& operator()(T&& a, Ts&&...) const + { + return a; + } +}; + +template +struct nth_t +{ + typedef typename std::conditional::type>::type + type; +}; + +template +struct nth_t +{ + typedef T type; +}; + +template