// Copyright 2004 The Trustees of Indiana University. // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // Authors: Douglas Gregor // Andrew Lumsdaine #include #include #include #include #include #include #include #include #include #include using namespace boost; const double error_tolerance = 0.001; typedef property > EdgeProperties; struct weighted_edge { int source, target; double weight; }; template void run_weighted_test(Graph*, int V, weighted_edge edge_init[], int E, double correct_centrality[]) { Graph g(V); typedef typename graph_traits::vertex_descriptor Vertex; typedef typename graph_traits::vertex_iterator vertex_iterator; typedef typename graph_traits::edge_descriptor Edge; std::vector vertices(V); { vertex_iterator v, v_end; int index = 0; for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) { put(vertex_index, g, *v, index); vertices[index] = *v; } } std::vector edges(E); for (int e = 0; e < E; ++e) { edges[e] = add_edge(vertices[edge_init[e].source], vertices[edge_init[e].target], g).first; put(edge_weight, g, edges[e], 1.0); } std::vector centrality(V); brandes_betweenness_centrality( g, centrality_map( make_iterator_property_map(centrality.begin(), get(vertex_index, g), double())) .vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g))); for (int v = 0; v < V; ++v) { BOOST_CHECK(centrality[v] == correct_centrality[v]); } } struct unweighted_edge { int source, target; }; template void run_unweighted_test(Graph*, int V, unweighted_edge edge_init[], int E, double correct_centrality[], double* correct_edge_centrality = 0) { Graph g(V); typedef typename graph_traits::vertex_descriptor Vertex; typedef typename graph_traits::vertex_iterator vertex_iterator; typedef typename graph_traits::edge_descriptor Edge; std::vector vertices(V); { vertex_iterator v, v_end; int index = 0; for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) { put(vertex_index, g, *v, index); vertices[index] = *v; } } std::vector edges(E); for (int e = 0; e < E; ++e) { edges[e] = add_edge(vertices[edge_init[e].source], vertices[edge_init[e].target], g).first; put(edge_weight, g, edges[e], 1.0); put(edge_index, g, edges[e], e); } std::vector centrality(V); std::vector edge_centrality1(E); brandes_betweenness_centrality( g, centrality_map( make_iterator_property_map(centrality.begin(), get(vertex_index, g), double())) .edge_centrality_map( make_iterator_property_map(edge_centrality1.begin(), get(edge_index, g), double())) .vertex_index_map(get(vertex_index, g))); std::vector centrality2(V); std::vector edge_centrality2(E); brandes_betweenness_centrality( g, vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g)) .centrality_map( make_iterator_property_map(centrality2.begin(), get(vertex_index, g), double())) .edge_centrality_map( make_iterator_property_map(edge_centrality2.begin(), get(edge_index, g), double()))); std::vector edge_centrality3(E); brandes_betweenness_centrality( g, edge_centrality_map( make_iterator_property_map(edge_centrality3.begin(), get(edge_index, g), double()))); for (int v = 0; v < V; ++v) { BOOST_CHECK(centrality[v] == centrality2[v]); double relative_error = correct_centrality[v] == 0.0? centrality[v] : (centrality[v] - correct_centrality[v]) / correct_centrality[v]; if (relative_error < 0) relative_error = -relative_error; BOOST_CHECK(relative_error < error_tolerance); } for (int e = 0; e < E; ++e) { BOOST_CHECK(edge_centrality1[e] == edge_centrality2[e]); BOOST_CHECK(edge_centrality1[e] == edge_centrality3[e]); if (correct_edge_centrality) { double relative_error = correct_edge_centrality[e] == 0.0? edge_centrality1[e] : (edge_centrality1[e] - correct_edge_centrality[e]) / correct_edge_centrality[e]; if (relative_error < 0) relative_error = -relative_error; BOOST_CHECK(relative_error < error_tolerance); if (relative_error >= error_tolerance) { std::cerr << "Edge " << e << " has edge centrality " << edge_centrality1[e] << ", should be " << correct_edge_centrality[e] << std::endl; } } } } template void run_wheel_test(Graph*, int V) { typedef typename graph_traits::vertex_descriptor Vertex; typedef typename graph_traits::vertex_iterator vertex_iterator; typedef typename graph_traits::edge_descriptor Edge; Graph g(V); Vertex center = *boost::vertices(g).first; std::vector vertices(V); { vertex_iterator v, v_end; int index = 0; for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) { put(vertex_index, g, *v, index); vertices[index] = *v; if (*v != center) { Edge e = add_edge(*v, center, g).first; put(edge_weight, g, e, 1.0); } } } std::vector centrality(V); brandes_betweenness_centrality( g, make_iterator_property_map(centrality.begin(), get(vertex_index, g), double())); std::vector centrality2(V); brandes_betweenness_centrality( g, centrality_map( make_iterator_property_map(centrality2.begin(), get(vertex_index, g), double())) .vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g))); relative_betweenness_centrality( g, make_iterator_property_map(centrality.begin(), get(vertex_index, g), double())); relative_betweenness_centrality( g, make_iterator_property_map(centrality2.begin(), get(vertex_index, g), double())); for (int v = 0; v < V; ++v) { BOOST_CHECK(centrality[v] == centrality2[v]); BOOST_CHECK((v == 0 && centrality[v] == 1) || (v != 0 && centrality[v] == 0)); } double dominance = central_point_dominance( g, make_iterator_property_map(centrality2.begin(), get(vertex_index, g), double())); BOOST_CHECK(dominance == 1.0); } template void randomly_add_edges(MutableGraph& g, double edge_probability) { typedef typename graph_traits::directed_category directed_category; minstd_rand gen; uniform_01 rand_gen(gen); typedef typename graph_traits::vertex_descriptor vertex; typename graph_traits::vertex_iterator vi, vi_end; for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) { vertex v = *vi; typename graph_traits::vertex_iterator wi = is_same::value ? vi : vertices(g).first; while (wi != vi_end) { vertex w = *wi++; if (v != w) { if (rand_gen() < edge_probability) add_edge(v, w, g); } } } } template void simple_unweighted_betweenness_centrality(const Graph& g, VertexIndexMap index, CentralityMap centrality) { typedef typename boost::graph_traits::vertex_descriptor vertex; typedef typename boost::graph_traits::vertex_iterator vertex_iterator; typedef typename boost::graph_traits::adjacency_iterator adjacency_iterator; typedef typename boost::graph_traits::vertices_size_type vertices_size_type; typedef typename boost::property_traits::value_type centrality_type; vertex_iterator vi, vi_end; for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) put(centrality, *vi, 0); vertex_iterator si, si_end; for (boost::tie(si, si_end) = vertices(g); si != si_end; ++si) { vertex s = *si; // S <-- empty stack std::stack S; // P[w] <-- empty list, w \in V typedef std::vector Predecessors; std::vector predecessors(num_vertices(g)); // sigma[t] <-- 0, t \in V std::vector sigma(num_vertices(g), 0); // sigma[s] <-- 1 sigma[get(index, s)] = 1; // d[t] <-- -1, t \in V std::vector d(num_vertices(g), -1); // d[s] <-- 0 d[get(index, s)] = 0; // Q <-- empty queue std::queue Q; // enqueue s --> Q Q.push(s); while (!Q.empty()) { // dequeue v <-- Q vertex v = Q.front(); Q.pop(); // push v --> S S.push(v); adjacency_iterator wi, wi_end; for (boost::tie(wi, wi_end) = adjacent_vertices(v, g); wi != wi_end; ++wi) { vertex w = *wi; // w found for the first time? if (d[get(index, w)] < 0) { // enqueue w --> Q Q.push(w); // d[w] <-- d[v] + 1 d[get(index, w)] = d[get(index, v)] + 1; } // shortest path to w via v? if (d[get(index, w)] == d[get(index, v)] + 1) { // sigma[w] = sigma[w] + sigma[v] sigma[get(index, w)] += sigma[get(index, v)]; // append v --> P[w] predecessors[get(index, w)].push_back(v); } } } // delta[v] <-- 0, v \in V std::vector delta(num_vertices(g), 0); // S returns vertices in order of non-increasing distance from s while (!S.empty()) { // pop w <-- S vertex w = S.top(); S.pop(); const Predecessors& w_preds = predecessors[get(index, w)]; for (typename Predecessors::const_iterator vi = w_preds.begin(); vi != w_preds.end(); ++vi) { vertex v = *vi; // delta[v] <-- delta[v] + (sigma[v]/sigma[w])*(1 + delta[w]) delta[get(index, v)] += ((centrality_type)sigma[get(index, v)]/sigma[get(index, w)]) * (1 + delta[get(index, w)]); } if (w != s) { // C_B[w] <-- C_B[w] + delta[w] centrality[w] += delta[get(index, w)]; } } } typedef typename graph_traits::directed_category directed_category; const bool is_undirected = is_same::value; if (is_undirected) { vertex_iterator v, v_end; for(boost::tie(v, v_end) = vertices(g); v != v_end; ++v) { put(centrality, *v, get(centrality, *v) / centrality_type(2)); } } } template void random_unweighted_test(Graph*, int n) { Graph g(n); { typename graph_traits::vertex_iterator v, v_end; int index = 0; for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) { put(vertex_index, g, *v, index); } } randomly_add_edges(g, 0.20); std::cout << "Random graph with " << n << " vertices and " << num_edges(g) << " edges.\n"; std::cout << " Direct translation of Brandes' algorithm..."; std::vector centrality(n); simple_unweighted_betweenness_centrality(g, get(vertex_index, g), make_iterator_property_map(centrality.begin(), get(vertex_index, g), double())); std::cout << "DONE.\n"; std::cout << " Real version, unweighted..."; std::vector centrality2(n); brandes_betweenness_centrality(g, make_iterator_property_map(centrality2.begin(), get(vertex_index, g), double())); std::cout << "DONE.\n"; if (!std::equal(centrality.begin(), centrality.end(), centrality2.begin())) { for (std::size_t v = 0; v < centrality.size(); ++v) { double relative_error = centrality[v] == 0.0? centrality2[v] : (centrality2[v] - centrality[v]) / centrality[v]; if (relative_error < 0) relative_error = -relative_error; BOOST_CHECK(relative_error < error_tolerance); } } std::cout << " Real version, weighted..."; std::vector centrality3(n); for (typename graph_traits::edge_iterator ei = edges(g).first; ei != edges(g).second; ++ei) put(edge_weight, g, *ei, 1); brandes_betweenness_centrality(g, weight_map(get(edge_weight, g)) .centrality_map( make_iterator_property_map(centrality3.begin(), get(vertex_index, g), double()))); std::cout << "DONE.\n"; if (!std::equal(centrality.begin(), centrality.end(), centrality3.begin())) { for (std::size_t v = 0; v < centrality.size(); ++v) { double relative_error = centrality[v] == 0.0? centrality3[v] : (centrality3[v] - centrality[v]) / centrality[v]; if (relative_error < 0) relative_error = -relative_error; BOOST_CHECK(relative_error < error_tolerance); } } } int test_main(int argc, char* argv[]) { int random_test_num_vertices = 300; if (argc >= 2) random_test_num_vertices = boost::lexical_cast(argv[1]); typedef adjacency_list, EdgeProperties> Graph; typedef adjacency_list, EdgeProperties> Digraph; struct unweighted_edge ud_edge_init1[5] = { { 0, 1 }, { 0, 3 }, { 1, 2 }, { 3, 2 }, { 2, 4 } }; double ud_centrality1[5] = { 0.5, 1.0, 3.5, 1.0, 0.0 }; run_unweighted_test((Graph*)0, 5, ud_edge_init1, 5, ud_centrality1); // Example borrowed from the JUNG test suite struct unweighted_edge ud_edge_init2[10] = { { 0, 1 }, { 0, 6 }, { 1, 2 }, { 1, 3 }, { 2, 4 }, { 3, 4 }, { 4, 5 }, { 5, 8 }, { 7, 8 }, { 6, 7 }, }; double ud_centrality2[9] = { 0.2142 * 28, 0.2797 * 28, 0.0892 * 28, 0.0892 * 28, 0.2797 * 28, 0.2142 * 28, 0.1666 * 28, 0.1428 * 28, 0.1666 * 28 }; double ud_edge_centrality2[10] = { 10.66666, 9.33333, 6.5, 6.5, 6.5, 6.5, 10.66666, 9.33333, 8.0, 8.0 }; run_unweighted_test((Graph*)0, 9, ud_edge_init2, 10, ud_centrality2, ud_edge_centrality2); weighted_edge dw_edge_init1[6] = { { 0, 1, 1.0 }, { 0, 3, 1.0 }, { 1, 2, 0.5 }, { 3, 1, 1.0 }, { 3, 4, 1.0 }, { 4, 2, 0.5 } }; double dw_centrality1[5] = { 0.0, 1.5, 0.0, 1.0, 0.5 }; run_weighted_test((Digraph*)0, 5, dw_edge_init1, 6, dw_centrality1); run_wheel_test((Graph*)0, 15); random_unweighted_test((Graph*)0, random_test_num_vertices); return 0; }