Commit e9cc8f1c authored by Tiago Peixoto's avatar Tiago Peixoto

Implement generate_maxent_sbm()

parent 0b01305b
......@@ -25,6 +25,7 @@ libgraph_tool_generation_la_SOURCES = \
graph_geometric.cc \
graph_lattice.cc \
graph_line_graph.cc \
graph_maxent_sbm.cc \
graph_predecessor.cc \
graph_price.cc \
graph_rewiring.cc \
......@@ -42,6 +43,7 @@ libgraph_tool_generation_la_include_HEADERS = \
graph_generation.hh \
graph_geometric.hh \
graph_lattice.hh \
graph_maxent_sbm.hh \
graph_predecessor.hh \
graph_price.hh \
graph_rewiring.hh \
......
......@@ -122,6 +122,8 @@ void community_network_eavg(GraphInterface& gi, GraphInterface& cgi,
boost::any eweight, boost::python::list aeprops,
bool self_loops, bool parallel_edges);
void export_maxent_sbm();
using namespace boost::python;
BOOST_PYTHON_MODULE(libgraph_tool_generation)
......@@ -144,6 +146,7 @@ BOOST_PYTHON_MODULE(libgraph_tool_generation)
def("community_network", &community_network);
def("community_network_vavg", &community_network_vavg);
def("community_network_eavg", &community_network_eavg);
export_maxent_sbm();
class_<Sampler<int, boost::mpl::false_>>("Sampler",
init<const vector<int>&, const vector<double>&>())
......
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2019 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph_maxent_sbm.hh"
#include "numpy_bind.hh"
using namespace std;
using namespace boost;
using namespace graph_tool;
void generate_maxent_sbm(GraphInterface& gi, boost::any ab,
boost::python::object ors, boost::python::object oss,
boost::python::object omrs, boost::any ain_theta,
boost::any aout_theta, bool multi, bool self_loops,
rng_t& rng)
{
auto rs = get_array<int64_t, 1>(ors);
auto ss = get_array<int64_t, 1>(oss);
auto mrs = get_array<double, 1>(omrs);
typedef vprop_map_t<int32_t>::type bmap_t;
auto b = any_cast<bmap_t>(ab).get_unchecked();
typedef vprop_map_t<double>::type dmap_t;
auto in_theta = any_cast<dmap_t>(ain_theta).get_unchecked();
auto out_theta = any_cast<dmap_t>(aout_theta).get_unchecked();
if (multi)
{
run_action<>()
(gi, [&](auto& g) { gen_maxent_sbm<true>(g, b, rs, ss, mrs, in_theta,
out_theta, self_loops, rng); })();
}
else
{
run_action<>()
(gi, [&](auto& g) { gen_maxent_sbm<false>(g, b, rs, ss, mrs, in_theta,
out_theta, self_loops, rng); })();
}
}
SBMFugacities get_sbm_fugacities(boost::python::object ors,
boost::python::object oss,
boost::python::object oers,
boost::python::object odegs_in,
boost::python::object odegs_out,
boost::python::object ob, bool directed,
bool multigraph, bool self_loops)
{
auto rs = get_array<int64_t,1>(ors);
auto ss = get_array<int64_t,1>(oss);
auto ers = get_array<double,1>(oers);
auto degs_in = get_array<double,1>(odegs_in);
auto degs_out = get_array<double,1>(odegs_out);
auto b = get_array<int32_t,1>(ob);
return SBMFugacities(rs, ss, ers, degs_in, degs_out, b, directed,
multigraph, self_loops);
}
using namespace boost::python;
void export_maxent_sbm()
{
def("get_sbm_fugacities", &get_sbm_fugacities);
def("gen_maxent_sbm", &generate_maxent_sbm);
class_<SBMFugacities>("SBMFugacities", no_init)
.def("pack", &SBMFugacities::pack)
.def("unpack", &SBMFugacities::unpack)
.def("get_f", &SBMFugacities::get_f)
.def("get_diff", &SBMFugacities::get_diff)
.def("new_x", &SBMFugacities::new_x)
.def("norm", &SBMFugacities::norm)
.def("export_args",
+[](SBMFugacities& state, python::object ors, python::object oss,
python::object omrs, python::object odegs_in,
python::object odegs_out, python::object otheta_in,
python::object otheta_out, python::object ob)
{
auto rs = get_array<int64_t,1>(ors);
auto ss = get_array<int64_t,1>(oss);
auto mrs = get_array<double,1>(omrs);
auto degs_in = get_array<double,1>(odegs_in);
auto degs_out = get_array<double,1>(odegs_out);
auto theta_in = get_array<double,1>(otheta_in);
auto theta_out = get_array<double,1>(otheta_out);
auto b = get_array<int32_t,1>(ob);
state.export_args(rs, ss, mrs, degs_in, degs_out, theta_in,
theta_out, b);
});
}
This diff is collapsed.
This diff is collapsed.
......@@ -26,7 +26,8 @@ if sys.version_info < (3,):
from .. import _degree, _prop, Graph, GraphView, libcore, _get_rng, PropertyMap, \
conv_pickle_state, Vector_size_t, Vector_double, group_vector_property, \
perfect_prop_hash
from .. generation import condensation_graph, random_rewire, generate_sbm
from .. generation import condensation_graph, random_rewire, generate_sbm, \
solve_sbm_fugacities, generate_maxent_sbm
from .. stats import label_self_loops, remove_parallel_edges, remove_self_loops
from .. spectral import adjacency
import random
......@@ -2232,7 +2233,8 @@ class BlockState(object):
"vertex_color",
"edge_gradient"]))
def sample_graph(self, canonical=False, multigraph=True, self_loops=True):
def sample_graph(self, canonical=False, multigraph=True, self_loops=True,
max_ent=False, n_iter=1000):
r"""Sample a new graph from the fitted model.
Parameters
......@@ -2245,6 +2247,11 @@ class BlockState(object):
If ``True``, parallel edges will be allowed.
self-loops : ``bool`` (optional, default: ``True``)
If ``True``, self-loops will be allowed.
max_ent : ``bool`` (optional, default: ``False``)
If ``True``, maximum-entropy model variants will be used.
n_iter : ``int`` (optional, default: ``1000``)
Number of iterations used (only relevant if ``canonical == False``
and ``max_ent == True``).
Returns
-------
......@@ -2290,15 +2297,42 @@ class BlockState(object):
else:
in_degs = None
probs = adjacency(self.bg, weight=self.mrs).T
g = generate_sbm(b=self.b.fa, probs=probs,
in_degs=in_degs, out_degs=out_degs,
directed=self.g.is_directed(),
micro_ers=not canonical,
micro_degs=not canonical and self.deg_corr)
if not multigraph:
remove_parallel_edges(g)
if not self_loops:
remove_self_loops(g)
if not max_ent:
g = generate_sbm(b=self.b.fa, probs=probs,
in_degs=in_degs, out_degs=out_degs,
directed=self.g.is_directed(),
micro_ers=not canonical,
micro_degs=not canonical and self.deg_corr)
if not multigraph:
remove_parallel_edges(g)
if not self_loops:
remove_self_loops(g)
else:
if canonical:
ret = solve_sbm_fugacities(self.b.fa, probs, out_degs, in_degs,
multigraph=multigraph,
self_loops=self_loops)
if in_degs is None:
mrs, theta_out = ret
theta_in = None
else:
mrs, theta_out, theta_in = ret
g = generate_maxent_sbm(self.b.fa, mrs, theta_out, theta_in,
directed=self.g.is_directed(),
multigraph=multigraph,
self_loops=self_loops)
else:
g = self.g.copy()
if self.deg_corr:
random_rewire(g, model="constrained-configuration",
block_membership=g.own_property(self.b),
configuration=False, parallel_edges=multigraph,
self_loops=self_loops, n_iter=n_iter)
else:
random_rewire(g, model="blockmodel-micro",
block_membership=g.own_property(self.b),
configuration=False, parallel_edges=multigraph,
self_loops=self_loops, n_iter=n_iter)
return g
def model_entropy(B, N, E, directed=False, nr=None, allow_empty=True):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment