Commit 2a8034cc authored by Tiago Peixoto's avatar Tiago Peixoto

betweenness(): Add `pivots` parameter

This enables the fast computation of unbiased estimators.
parent 2c920ab5
......@@ -299,6 +299,7 @@ namespace detail { namespace graph {
typename VertexIndexMap, typename ShortestPaths>
void
brandes_betweenness_centrality_impl(const Graph& g,
std::vector<size_t>& pivots,
CentralityMap centrality, // C_B
EdgeCentralityMap edge_centrality_map,
IncomingMap, //incoming, // P
......@@ -320,13 +321,13 @@ namespace detail { namespace graph {
std::vector<typename property_traits<DependencyMap>::value_type> vdependency(num_vertices(g));
std::vector<typename property_traits<PathCountMap>::value_type> vpath_count(num_vertices(g));
int i, N = num_vertices(g);
int i, N = pivots.size();
#pragma omp parallel for default(shared) private(i) \
firstprivate(vincoming, vdistance, vdependency, vpath_count) \
schedule(runtime)
for (i = 0; i < N; ++i)
{
auto s = vertex(i, g);
auto s = vertex(pivots[i], g);
if (s == graph_traits<Graph>::null_vertex())
continue;
......@@ -394,6 +395,7 @@ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
typename VertexIndexMap>
void
brandes_betweenness_centrality(const Graph& g,
std::vector<size_t>& pivots,
CentralityMap centrality, // C_B
EdgeCentralityMap edge_centrality_map,
IncomingMap incoming, // P
......@@ -404,7 +406,7 @@ brandes_betweenness_centrality(const Graph& g,
{
detail::graph::brandes_unweighted_shortest_paths shortest_paths;
detail::graph::brandes_betweenness_centrality_impl(g, centrality,
detail::graph::brandes_betweenness_centrality_impl(g, pivots, centrality,
edge_centrality_map,
incoming, distance,
dependency, path_count,
......@@ -418,6 +420,7 @@ template<typename Graph, typename CentralityMap, typename EdgeCentralityMap,
typename VertexIndexMap, typename WeightMap>
void
brandes_betweenness_centrality(const Graph& g,
std::vector<size_t>& pivots,
CentralityMap centrality, // C_B
EdgeCentralityMap edge_centrality_map,
IncomingMap incoming, // P
......@@ -430,7 +433,7 @@ brandes_betweenness_centrality(const Graph& g,
detail::graph::brandes_dijkstra_shortest_paths<WeightMap>
shortest_paths(weight_map);
detail::graph::brandes_betweenness_centrality_impl(g, centrality,
detail::graph::brandes_betweenness_centrality_impl(g, pivots, centrality,
edge_centrality_map,
incoming, distance,
dependency, path_count,
......@@ -443,6 +446,7 @@ namespace detail { namespace graph {
typename WeightMap, typename VertexIndexMap>
void
brandes_betweenness_centrality_dispatch2(const Graph& g,
std::vector<size_t>& pivots,
CentralityMap centrality,
EdgeCentralityMap edge_centrality_map,
WeightMap weight_map,
......@@ -465,7 +469,7 @@ namespace detail { namespace graph {
std::vector<degree_size_type> path_count(V);
brandes_betweenness_centrality(
g, centrality, edge_centrality_map,
g, pivots, centrality, edge_centrality_map,
make_iterator_property_map(incoming.begin(), vertex_index),
make_iterator_property_map(distance.begin(), vertex_index),
make_iterator_property_map(dependency.begin(), vertex_index),
......@@ -479,6 +483,7 @@ namespace detail { namespace graph {
typename VertexIndexMap>
void
brandes_betweenness_centrality_dispatch2(const Graph& g,
std::vector<size_t>& pivots,
CentralityMap centrality,
EdgeCentralityMap edge_centrality_map,
VertexIndexMap vertex_index)
......@@ -500,7 +505,7 @@ namespace detail { namespace graph {
std::vector<degree_size_type> path_count(V);
brandes_betweenness_centrality(
g, centrality, edge_centrality_map,
g, pivots, centrality, edge_centrality_map,
make_iterator_property_map(incoming.begin(), vertex_index),
make_iterator_property_map(distance.begin(), vertex_index),
make_iterator_property_map(dependency.begin(), vertex_index),
......@@ -514,12 +519,13 @@ namespace detail { namespace graph {
template<typename Graph, typename CentralityMap,
typename EdgeCentralityMap, typename VertexIndexMap>
static void
run(const Graph& g, CentralityMap centrality,
run(const Graph& g, std::vector<size_t>& pivots, CentralityMap centrality,
EdgeCentralityMap edge_centrality_map, VertexIndexMap vertex_index,
WeightMap weight_map)
{
brandes_betweenness_centrality_dispatch2(g, centrality, edge_centrality_map,
weight_map, vertex_index);
brandes_betweenness_centrality_dispatch2(g, pivots, centrality,
edge_centrality_map,
weight_map, vertex_index);
}
};
......@@ -529,12 +535,13 @@ namespace detail { namespace graph {
template<typename Graph, typename CentralityMap,
typename EdgeCentralityMap, typename VertexIndexMap>
static void
run(const Graph& g, CentralityMap centrality,
run(const Graph& g, std::vector<size_t>& pivots, CentralityMap centrality,
EdgeCentralityMap edge_centrality_map, VertexIndexMap vertex_index,
error_property_not_found)
{
brandes_betweenness_centrality_dispatch2(g, centrality, edge_centrality_map,
vertex_index);
brandes_betweenness_centrality_dispatch2(g, pivots, centrality,
edge_centrality_map,
vertex_index);
}
};
......@@ -543,13 +550,14 @@ namespace detail { namespace graph {
template<typename Graph, typename Param, typename Tag, typename Rest>
void
brandes_betweenness_centrality(const Graph& g,
std::vector<size_t>& pivots,
const bgl_named_params<Param,Tag,Rest>& params)
{
typedef bgl_named_params<Param,Tag,Rest> named_params;
typedef typename property_value<named_params, edge_weight_t>::type ew;
detail::graph::brandes_betweenness_centrality_dispatch1<ew>::run(
g,
g, pivots,
choose_param(get_param(params, vertex_centrality),
dummy_property_map()),
choose_param(get_param(params, edge_centrality),
......@@ -560,19 +568,21 @@ brandes_betweenness_centrality(const Graph& g,
template<typename Graph, typename CentralityMap>
void
brandes_betweenness_centrality(const Graph& g, CentralityMap centrality)
brandes_betweenness_centrality(const Graph& g, std::vector<size_t>& pivots,
CentralityMap centrality)
{
detail::graph::brandes_betweenness_centrality_dispatch2(
g, centrality, dummy_property_map(), get(vertex_index, g));
detail::graph::brandes_betweenness_centrality_dispatch2(
g, pivots, centrality, dummy_property_map(), get(vertex_index, g));
}
template<typename Graph, typename CentralityMap, typename EdgeCentralityMap>
void
brandes_betweenness_centrality(const Graph& g, CentralityMap centrality,
brandes_betweenness_centrality(const Graph& g, std::vector<size_t>& pivots,
CentralityMap centrality,
EdgeCentralityMap edge_centrality_map)
{
detail::graph::brandes_betweenness_centrality_dispatch2(
g, centrality, edge_centrality_map, get(vertex_index, g));
g, pivots, centrality, edge_centrality_map, get(vertex_index, g));
}
/**
......
......@@ -30,24 +30,40 @@ using namespace graph_tool;
template <class Graph, class EdgeBetweenness, class VertexBetweenness>
void normalize_betweenness(const Graph& g,
std::vector<size_t>& pivots,
EdgeBetweenness edge_betweenness,
VertexBetweenness vertex_betweenness,
size_t n)
{
double vfactor = (n > 2) ? 1.0 / ((n - 1) * (n - 2)) : 1.0;
double efactor = (n > 1) ? 1.0 / (n * (n - 1)) : 1.0;
size_t p = pivots.size();
double pfactor = (p > 1 && n > 2) ? ((p - 1) * (n - 2)) : .0;
double vfactor = (p > 0 && n > 2) ? (p * (n - 2)) : .0;
double efactor = (p > 0 && n > 1) ? (p * (n - 1)) : .0;
if (std::is_convertible<typename graph_traits<Graph>::directed_category,
undirected_tag>::value)
{
vfactor *= 2;
efactor *= 2;
pfactor /= 2;
vfactor /= 2;
efactor /= 2;
}
pfactor = (pfactor > 0) ? 1./pfactor : 0;
vfactor = (vfactor > 0) ? 1./vfactor : 0;
efactor = (efactor > 0) ? 1./efactor : 0;
typedef typename vprop_map_t<bool>::type::unchecked_t vprop_t;
vprop_t is_pivot(get(vertex_index, g), num_vertices(g));
parallel_loop(pivots, [&](size_t, auto v){ is_pivot[v] = true;});
parallel_vertex_loop
(g,
[&](auto v)
{
put(vertex_betweenness, v, vfactor * get(vertex_betweenness, v));
if (is_pivot[v])
put(vertex_betweenness, v, pfactor * get(vertex_betweenness, v));
else
put(vertex_betweenness, v, vfactor * get(vertex_betweenness, v));
});
parallel_edge_loop
......@@ -63,6 +79,7 @@ struct get_betweenness
typedef void result_type;
template <class Graph, class EdgeBetweenness, class VertexBetweenness>
void operator()(Graph& g,
std::vector<size_t>& pivots,
GraphInterface::vertex_index_map_t index_map,
EdgeBetweenness edge_betweenness,
VertexBetweenness vertex_betweenness,
......@@ -75,14 +92,14 @@ struct get_betweenness
dependency_map(num_vertices(g));
vector<size_t> path_count_map(num_vertices(g));
brandes_betweenness_centrality
(g, vertex_betweenness, edge_betweenness,
(g, pivots, vertex_betweenness, edge_betweenness,
make_iterator_property_map(incoming_map.begin(), index_map),
make_iterator_property_map(distance_map.begin(), index_map),
make_iterator_property_map(dependency_map.begin(), index_map),
make_iterator_property_map(path_count_map.begin(), index_map),
index_map);
if (normalize)
normalize_betweenness(g, edge_betweenness, vertex_betweenness, n);
normalize_betweenness(g, pivots, edge_betweenness, vertex_betweenness, n);
}
};
......@@ -91,11 +108,12 @@ struct get_weighted_betweenness
typedef void result_type;
template <class Graph, class EdgeBetweenness, class VertexBetweenness,
class VertexIndexMap>
void operator()(Graph& g, VertexIndexMap vertex_index,
EdgeBetweenness edge_betweenness,
VertexBetweenness vertex_betweenness,
boost::any weight_map, bool normalize,
size_t n, size_t max_eindex) const
void operator()(Graph& g, std::vector<size_t>& pivots,
VertexIndexMap vertex_index,
EdgeBetweenness edge_betweenness,
VertexBetweenness vertex_betweenness,
boost::any weight_map, bool normalize,
size_t n, size_t max_eindex) const
{
vector<vector<typename graph_traits<Graph>::edge_descriptor> >
incoming_map(num_vertices(g));
......@@ -109,18 +127,19 @@ struct get_weighted_betweenness
any_cast<typename EdgeBetweenness::checked_t>(weight_map);
brandes_betweenness_centrality
(g, vertex_betweenness, edge_betweenness,
(g, pivots, vertex_betweenness, edge_betweenness,
make_iterator_property_map(incoming_map.begin(), vertex_index),
make_iterator_property_map(distance_map.begin(), vertex_index),
make_iterator_property_map(dependency_map.begin(), vertex_index),
make_iterator_property_map(path_count_map.begin(), vertex_index),
vertex_index, weight.get_unchecked(max_eindex+1));
if (normalize)
normalize_betweenness(g, edge_betweenness, vertex_betweenness, n);
normalize_betweenness(g, pivots, edge_betweenness, vertex_betweenness, n);
}
};
void betweenness(GraphInterface& g, boost::any weight,
void betweenness(GraphInterface& g, std::vector<size_t>& pivots,
boost::any weight,
boost::any edge_betweenness,
boost::any vertex_betweenness,
bool normalize)
......@@ -137,7 +156,9 @@ void betweenness(GraphInterface& g, boost::any weight,
{
run_action<>()
(g, std::bind<>(get_weighted_betweenness(),
std::placeholders::_1, g.get_vertex_index(),
std::placeholders::_1,
std::ref(pivots),
g.get_vertex_index(),
std::placeholders::_2,
std::placeholders::_3, weight, normalize,
g.get_num_vertices(), g.get_edge_index_range()),
......@@ -149,6 +170,7 @@ void betweenness(GraphInterface& g, boost::any weight,
{
run_action<>()
(g, std::bind<void>(get_betweenness(), std::placeholders::_1,
std::ref(pivots),
g.get_vertex_index(), std::placeholders::_2,
std::placeholders::_3, normalize,
g.get_num_vertices()),
......
......@@ -49,7 +49,7 @@ from __future__ import division, absolute_import, print_function
from .. dl_import import dl_import
dl_import("from . import libgraph_tool_centrality")
from .. import _prop, ungroup_vector_property
from .. import _prop, ungroup_vector_property, Vector_size_t
from .. topology import shortest_distance
import sys
import numpy
......@@ -228,14 +228,18 @@ def pagerank(g, damping=0.85, pers=None, weight=None, prop=None, epsilon=1e-6,
return prop
def betweenness(g, vprop=None, eprop=None, weight=None, norm=True):
r"""
Calculate the betweenness centrality for each vertex and edge.
def betweenness(g, pivots=None, vprop=None, eprop=None, weight=None, norm=True):
r"""Calculate the betweenness centrality for each vertex and edge.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
pivots : list or :class:`~numpy.ndarray`, optional (default: None)
If provided, the betweenness will be estimated using the vertices in
this list as pivots. If the list contains all nodes (the default) the
algorithm will be exact, and if the vertices are randomly chosen the
result will be an unbiased estimator.
vprop : :class:`~graph_tool.PropertyMap`, optional (default: None)
Vertex property map to store the vertex betweenness values.
eprop : :class:`~graph_tool.PropertyMap`, optional (default: None)
......@@ -268,15 +272,19 @@ def betweenness(g, vprop=None, eprop=None, weight=None, norm=True):
C_B(v)= \sum_{s \neq v \neq t \in V \atop s \neq t}
\frac{\sigma_{st}(v)}{\sigma_{st}}
where :math:`\sigma_{st}` is the number of shortest geodesic paths from s to
t, and :math:`\sigma_{st}(v)` is the number of shortest geodesic paths from
s to t that pass through a vertex v. This may be normalised by dividing
through the number of pairs of vertices not including v, which is
:math:`(n-1)(n-2)/2`.
where :math:`\sigma_{st}` is the number of shortest paths from s to t, and
:math:`\sigma_{st}(v)` is the number of shortest paths from s to t that pass
through a vertex :math:`v`. This may be normalised by dividing through the
number of pairs of vertices not including v, which is :math:`(n-1)(n-2)/2`,
for undirected graphs, or :math:`(n-1)(n-2)` for directed ones.
The algorithm used here is defined in [brandes-faster-2001]_, and has a
complexity of :math:`O(VE)` for unweighted graphs and :math:`O(VE + V(V+E)
\log V)` for weighted graphs. The space complexity is :math:`O(VE)`.
complexity of :math:`O(VE)` for unweighted graphs and :math:`O(VE +
V(V+E)\log V)` for weighted graphs. The space complexity is :math:`O(VE)`.
If the ``pivots`` parameter is given, the complexity will be instead
:math:`O(PE)` for unweighted graphs and :math:`O(PE + P(V+E)\log V)` for
weighted graphs, where :math:`P` is the number of pivot vertices.
If enabled during compilation, this algorithm runs in parallel.
......@@ -319,9 +327,13 @@ def betweenness(g, vprop=None, eprop=None, weight=None, norm=True):
.. [betweenness-wikipedia] http://en.wikipedia.org/wiki/Centrality#Betweenness_centrality
.. [brandes-faster-2001] U. Brandes, "A faster algorithm for betweenness
centrality", Journal of Mathematical Sociology, 2001, :doi:`10.1080/0022250X.2001.9990249`
.. [brandes-centrality-2007] U. Brandes, C. Pich, "Centrality estimation in
large networks", Int. J. Bifurcation Chaos 17, 2303 (2007).
:DOI:`10.1142/S0218127407018403`
.. [adamic-polblogs] L. A. Adamic and N. Glance, "The political blogosphere
and the 2004 US Election", in Proceedings of the WWW-2005 Workshop on the
Weblogging Ecosystem (2005). :DOI:`10.1145/1134271.1134277`
"""
if vprop is None:
vprop = g.new_vertex_property("double")
......@@ -331,8 +343,14 @@ def betweenness(g, vprop=None, eprop=None, weight=None, norm=True):
nw = g.new_edge_property(eprop.value_type())
g.copy_property(weight, nw)
weight = nw
if pivots is not None:
pivots = numpy.asarray(pivots, dtype="uint64")
else:
pivots = g.get_vertices()
vpivots = Vector_size_t(len(pivots))
vpivots.a = pivots
libgraph_tool_centrality.\
get_betweenness(g._Graph__graph, _prop("e", g, weight),
get_betweenness(g._Graph__graph, vpivots, _prop("e", g, weight),
_prop("e", g, eprop), _prop("v", g, vprop), norm)
return vprop, eprop
......
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