betweenness_centrality_test.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. // Copyright 2004 The Trustees of Indiana University.
  2. // Use, modification and distribution is subject to the Boost Software
  3. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. // Authors: Douglas Gregor
  6. // Andrew Lumsdaine
  7. #include <boost/graph/betweenness_centrality.hpp>
  8. #include <boost/graph/adjacency_list.hpp>
  9. #include <vector>
  10. #include <stack>
  11. #include <queue>
  12. #include <boost/property_map/property_map.hpp>
  13. #include <boost/test/minimal.hpp>
  14. #include <boost/random/uniform_01.hpp>
  15. #include <boost/random/linear_congruential.hpp>
  16. #include <boost/lexical_cast.hpp>
  17. using namespace boost;
  18. const double error_tolerance = 0.001;
  19. typedef property<edge_weight_t, double,
  20. property<edge_index_t, std::size_t> > EdgeProperties;
  21. struct weighted_edge
  22. {
  23. int source, target;
  24. double weight;
  25. };
  26. template<typename Graph>
  27. void
  28. run_weighted_test(Graph*, int V, weighted_edge edge_init[], int E,
  29. double correct_centrality[])
  30. {
  31. Graph g(V);
  32. typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
  33. typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  34. typedef typename graph_traits<Graph>::edge_descriptor Edge;
  35. std::vector<Vertex> vertices(V);
  36. {
  37. vertex_iterator v, v_end;
  38. int index = 0;
  39. for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) {
  40. put(vertex_index, g, *v, index);
  41. vertices[index] = *v;
  42. }
  43. }
  44. std::vector<Edge> edges(E);
  45. for (int e = 0; e < E; ++e) {
  46. edges[e] = add_edge(vertices[edge_init[e].source],
  47. vertices[edge_init[e].target],
  48. g).first;
  49. put(edge_weight, g, edges[e], 1.0);
  50. }
  51. std::vector<double> centrality(V);
  52. brandes_betweenness_centrality(
  53. g,
  54. centrality_map(
  55. make_iterator_property_map(centrality.begin(), get(vertex_index, g),
  56. double()))
  57. .vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g)));
  58. for (int v = 0; v < V; ++v) {
  59. BOOST_CHECK(centrality[v] == correct_centrality[v]);
  60. }
  61. }
  62. struct unweighted_edge
  63. {
  64. int source, target;
  65. };
  66. template<typename Graph>
  67. void
  68. run_unweighted_test(Graph*, int V, unweighted_edge edge_init[], int E,
  69. double correct_centrality[],
  70. double* correct_edge_centrality = 0)
  71. {
  72. Graph g(V);
  73. typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
  74. typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  75. typedef typename graph_traits<Graph>::edge_descriptor Edge;
  76. std::vector<Vertex> vertices(V);
  77. {
  78. vertex_iterator v, v_end;
  79. int index = 0;
  80. for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) {
  81. put(vertex_index, g, *v, index);
  82. vertices[index] = *v;
  83. }
  84. }
  85. std::vector<Edge> edges(E);
  86. for (int e = 0; e < E; ++e) {
  87. edges[e] = add_edge(vertices[edge_init[e].source],
  88. vertices[edge_init[e].target],
  89. g).first;
  90. put(edge_weight, g, edges[e], 1.0);
  91. put(edge_index, g, edges[e], e);
  92. }
  93. std::vector<double> centrality(V);
  94. std::vector<double> edge_centrality1(E);
  95. brandes_betweenness_centrality(
  96. g,
  97. centrality_map(
  98. make_iterator_property_map(centrality.begin(), get(vertex_index, g),
  99. double()))
  100. .edge_centrality_map(
  101. make_iterator_property_map(edge_centrality1.begin(),
  102. get(edge_index, g), double()))
  103. .vertex_index_map(get(vertex_index, g)));
  104. std::vector<double> centrality2(V);
  105. std::vector<double> edge_centrality2(E);
  106. brandes_betweenness_centrality(
  107. g,
  108. vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g))
  109. .centrality_map(
  110. make_iterator_property_map(centrality2.begin(), get(vertex_index, g),
  111. double()))
  112. .edge_centrality_map(
  113. make_iterator_property_map(edge_centrality2.begin(),
  114. get(edge_index, g), double())));
  115. std::vector<double> edge_centrality3(E);
  116. brandes_betweenness_centrality(
  117. g,
  118. edge_centrality_map(
  119. make_iterator_property_map(edge_centrality3.begin(),
  120. get(edge_index, g), double())));
  121. for (int v = 0; v < V; ++v) {
  122. BOOST_CHECK(centrality[v] == centrality2[v]);
  123. double relative_error =
  124. correct_centrality[v] == 0.0? centrality[v]
  125. : (centrality[v] - correct_centrality[v]) / correct_centrality[v];
  126. if (relative_error < 0) relative_error = -relative_error;
  127. BOOST_CHECK(relative_error < error_tolerance);
  128. }
  129. for (int e = 0; e < E; ++e) {
  130. BOOST_CHECK(edge_centrality1[e] == edge_centrality2[e]);
  131. BOOST_CHECK(edge_centrality1[e] == edge_centrality3[e]);
  132. if (correct_edge_centrality) {
  133. double relative_error =
  134. correct_edge_centrality[e] == 0.0? edge_centrality1[e]
  135. : (edge_centrality1[e] - correct_edge_centrality[e])
  136. / correct_edge_centrality[e];
  137. if (relative_error < 0) relative_error = -relative_error;
  138. BOOST_CHECK(relative_error < error_tolerance);
  139. if (relative_error >= error_tolerance) {
  140. std::cerr << "Edge " << e << " has edge centrality "
  141. << edge_centrality1[e] << ", should be "
  142. << correct_edge_centrality[e] << std::endl;
  143. }
  144. }
  145. }
  146. }
  147. template<typename Graph>
  148. void
  149. run_wheel_test(Graph*, int V)
  150. {
  151. typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
  152. typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  153. typedef typename graph_traits<Graph>::edge_descriptor Edge;
  154. Graph g(V);
  155. Vertex center = *boost::vertices(g).first;
  156. std::vector<Vertex> vertices(V);
  157. {
  158. vertex_iterator v, v_end;
  159. int index = 0;
  160. for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) {
  161. put(vertex_index, g, *v, index);
  162. vertices[index] = *v;
  163. if (*v != center) {
  164. Edge e = add_edge(*v, center, g).first;
  165. put(edge_weight, g, e, 1.0);
  166. }
  167. }
  168. }
  169. std::vector<double> centrality(V);
  170. brandes_betweenness_centrality(
  171. g,
  172. make_iterator_property_map(centrality.begin(), get(vertex_index, g),
  173. double()));
  174. std::vector<double> centrality2(V);
  175. brandes_betweenness_centrality(
  176. g,
  177. centrality_map(
  178. make_iterator_property_map(centrality2.begin(), get(vertex_index, g),
  179. double()))
  180. .vertex_index_map(get(vertex_index, g)).weight_map(get(edge_weight, g)));
  181. relative_betweenness_centrality(
  182. g,
  183. make_iterator_property_map(centrality.begin(), get(vertex_index, g),
  184. double()));
  185. relative_betweenness_centrality(
  186. g,
  187. make_iterator_property_map(centrality2.begin(), get(vertex_index, g),
  188. double()));
  189. for (int v = 0; v < V; ++v) {
  190. BOOST_CHECK(centrality[v] == centrality2[v]);
  191. BOOST_CHECK((v == 0 && centrality[v] == 1)
  192. || (v != 0 && centrality[v] == 0));
  193. }
  194. double dominance =
  195. central_point_dominance(
  196. g,
  197. make_iterator_property_map(centrality2.begin(), get(vertex_index, g),
  198. double()));
  199. BOOST_CHECK(dominance == 1.0);
  200. }
  201. template<typename MutableGraph>
  202. void randomly_add_edges(MutableGraph& g, double edge_probability)
  203. {
  204. typedef typename graph_traits<MutableGraph>::directed_category
  205. directed_category;
  206. minstd_rand gen;
  207. uniform_01<minstd_rand, double> rand_gen(gen);
  208. typedef typename graph_traits<MutableGraph>::vertex_descriptor vertex;
  209. typename graph_traits<MutableGraph>::vertex_iterator vi, vi_end;
  210. for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
  211. vertex v = *vi;
  212. typename graph_traits<MutableGraph>::vertex_iterator wi
  213. = is_same<directed_category, undirected_tag>::value ? vi : vertices(g).first;
  214. while (wi != vi_end) {
  215. vertex w = *wi++;
  216. if (v != w) {
  217. if (rand_gen() < edge_probability) add_edge(v, w, g);
  218. }
  219. }
  220. }
  221. }
  222. template<typename Graph, typename VertexIndexMap, typename CentralityMap>
  223. void
  224. simple_unweighted_betweenness_centrality(const Graph& g, VertexIndexMap index,
  225. CentralityMap centrality)
  226. {
  227. typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex;
  228. typedef typename boost::graph_traits<Graph>::vertex_iterator vertex_iterator;
  229. typedef typename boost::graph_traits<Graph>::adjacency_iterator adjacency_iterator;
  230. typedef typename boost::graph_traits<Graph>::vertices_size_type vertices_size_type;
  231. typedef typename boost::property_traits<CentralityMap>::value_type centrality_type;
  232. vertex_iterator vi, vi_end;
  233. for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
  234. put(centrality, *vi, 0);
  235. vertex_iterator si, si_end;
  236. for (boost::tie(si, si_end) = vertices(g); si != si_end; ++si) {
  237. vertex s = *si;
  238. // S <-- empty stack
  239. std::stack<vertex> S;
  240. // P[w] <-- empty list, w \in V
  241. typedef std::vector<vertex> Predecessors;
  242. std::vector<Predecessors> predecessors(num_vertices(g));
  243. // sigma[t] <-- 0, t \in V
  244. std::vector<vertices_size_type> sigma(num_vertices(g), 0);
  245. // sigma[s] <-- 1
  246. sigma[get(index, s)] = 1;
  247. // d[t] <-- -1, t \in V
  248. std::vector<int> d(num_vertices(g), -1);
  249. // d[s] <-- 0
  250. d[get(index, s)] = 0;
  251. // Q <-- empty queue
  252. std::queue<vertex> Q;
  253. // enqueue s --> Q
  254. Q.push(s);
  255. while (!Q.empty()) {
  256. // dequeue v <-- Q
  257. vertex v = Q.front(); Q.pop();
  258. // push v --> S
  259. S.push(v);
  260. adjacency_iterator wi, wi_end;
  261. for (boost::tie(wi, wi_end) = adjacent_vertices(v, g); wi != wi_end; ++wi) {
  262. vertex w = *wi;
  263. // w found for the first time?
  264. if (d[get(index, w)] < 0) {
  265. // enqueue w --> Q
  266. Q.push(w);
  267. // d[w] <-- d[v] + 1
  268. d[get(index, w)] = d[get(index, v)] + 1;
  269. }
  270. // shortest path to w via v?
  271. if (d[get(index, w)] == d[get(index, v)] + 1) {
  272. // sigma[w] = sigma[w] + sigma[v]
  273. sigma[get(index, w)] += sigma[get(index, v)];
  274. // append v --> P[w]
  275. predecessors[get(index, w)].push_back(v);
  276. }
  277. }
  278. }
  279. // delta[v] <-- 0, v \in V
  280. std::vector<centrality_type> delta(num_vertices(g), 0);
  281. // S returns vertices in order of non-increasing distance from s
  282. while (!S.empty()) {
  283. // pop w <-- S
  284. vertex w = S.top(); S.pop();
  285. const Predecessors& w_preds = predecessors[get(index, w)];
  286. for (typename Predecessors::const_iterator vi = w_preds.begin();
  287. vi != w_preds.end(); ++vi) {
  288. vertex v = *vi;
  289. // delta[v] <-- delta[v] + (sigma[v]/sigma[w])*(1 + delta[w])
  290. delta[get(index, v)] +=
  291. ((centrality_type)sigma[get(index, v)]/sigma[get(index, w)])
  292. * (1 + delta[get(index, w)]);
  293. }
  294. if (w != s) {
  295. // C_B[w] <-- C_B[w] + delta[w]
  296. centrality[w] += delta[get(index, w)];
  297. }
  298. }
  299. }
  300. typedef typename graph_traits<Graph>::directed_category directed_category;
  301. const bool is_undirected =
  302. is_same<directed_category, undirected_tag>::value;
  303. if (is_undirected) {
  304. vertex_iterator v, v_end;
  305. for(boost::tie(v, v_end) = vertices(g); v != v_end; ++v) {
  306. put(centrality, *v, get(centrality, *v) / centrality_type(2));
  307. }
  308. }
  309. }
  310. template<typename Graph>
  311. void random_unweighted_test(Graph*, int n)
  312. {
  313. Graph g(n);
  314. {
  315. typename graph_traits<Graph>::vertex_iterator v, v_end;
  316. int index = 0;
  317. for (boost::tie(v, v_end) = boost::vertices(g); v != v_end; ++v, ++index) {
  318. put(vertex_index, g, *v, index);
  319. }
  320. }
  321. randomly_add_edges(g, 0.20);
  322. std::cout << "Random graph with " << n << " vertices and "
  323. << num_edges(g) << " edges.\n";
  324. std::cout << " Direct translation of Brandes' algorithm...";
  325. std::vector<double> centrality(n);
  326. simple_unweighted_betweenness_centrality(g, get(vertex_index, g),
  327. make_iterator_property_map(centrality.begin(), get(vertex_index, g),
  328. double()));
  329. std::cout << "DONE.\n";
  330. std::cout << " Real version, unweighted...";
  331. std::vector<double> centrality2(n);
  332. brandes_betweenness_centrality(g,
  333. make_iterator_property_map(centrality2.begin(), get(vertex_index, g),
  334. double()));
  335. std::cout << "DONE.\n";
  336. if (!std::equal(centrality.begin(), centrality.end(),
  337. centrality2.begin())) {
  338. for (std::size_t v = 0; v < centrality.size(); ++v) {
  339. double relative_error =
  340. centrality[v] == 0.0? centrality2[v]
  341. : (centrality2[v] - centrality[v]) / centrality[v];
  342. if (relative_error < 0) relative_error = -relative_error;
  343. BOOST_CHECK(relative_error < error_tolerance);
  344. }
  345. }
  346. std::cout << " Real version, weighted...";
  347. std::vector<double> centrality3(n);
  348. for (typename graph_traits<Graph>::edge_iterator ei = edges(g).first;
  349. ei != edges(g).second; ++ei)
  350. put(edge_weight, g, *ei, 1);
  351. brandes_betweenness_centrality(g,
  352. weight_map(get(edge_weight, g))
  353. .centrality_map(
  354. make_iterator_property_map(centrality3.begin(), get(vertex_index, g),
  355. double())));
  356. std::cout << "DONE.\n";
  357. if (!std::equal(centrality.begin(), centrality.end(),
  358. centrality3.begin())) {
  359. for (std::size_t v = 0; v < centrality.size(); ++v) {
  360. double relative_error =
  361. centrality[v] == 0.0? centrality3[v]
  362. : (centrality3[v] - centrality[v]) / centrality[v];
  363. if (relative_error < 0) relative_error = -relative_error;
  364. BOOST_CHECK(relative_error < error_tolerance);
  365. }
  366. }
  367. }
  368. int test_main(int argc, char* argv[])
  369. {
  370. int random_test_num_vertices = 300;
  371. if (argc >= 2) random_test_num_vertices = boost::lexical_cast<int>(argv[1]);
  372. typedef adjacency_list<listS, listS, undirectedS,
  373. property<vertex_index_t, int>, EdgeProperties>
  374. Graph;
  375. typedef adjacency_list<listS, listS, directedS,
  376. property<vertex_index_t, int>, EdgeProperties>
  377. Digraph;
  378. struct unweighted_edge ud_edge_init1[5] = {
  379. { 0, 1 },
  380. { 0, 3 },
  381. { 1, 2 },
  382. { 3, 2 },
  383. { 2, 4 }
  384. };
  385. double ud_centrality1[5] = { 0.5, 1.0, 3.5, 1.0, 0.0 };
  386. run_unweighted_test((Graph*)0, 5, ud_edge_init1, 5, ud_centrality1);
  387. // Example borrowed from the JUNG test suite
  388. struct unweighted_edge ud_edge_init2[10] = {
  389. { 0, 1 },
  390. { 0, 6 },
  391. { 1, 2 },
  392. { 1, 3 },
  393. { 2, 4 },
  394. { 3, 4 },
  395. { 4, 5 },
  396. { 5, 8 },
  397. { 7, 8 },
  398. { 6, 7 },
  399. };
  400. double ud_centrality2[9] = {
  401. 0.2142 * 28,
  402. 0.2797 * 28,
  403. 0.0892 * 28,
  404. 0.0892 * 28,
  405. 0.2797 * 28,
  406. 0.2142 * 28,
  407. 0.1666 * 28,
  408. 0.1428 * 28,
  409. 0.1666 * 28
  410. };
  411. double ud_edge_centrality2[10] = {
  412. 10.66666,
  413. 9.33333,
  414. 6.5,
  415. 6.5,
  416. 6.5,
  417. 6.5,
  418. 10.66666,
  419. 9.33333,
  420. 8.0,
  421. 8.0
  422. };
  423. run_unweighted_test((Graph*)0, 9, ud_edge_init2, 10, ud_centrality2,
  424. ud_edge_centrality2);
  425. weighted_edge dw_edge_init1[6] = {
  426. { 0, 1, 1.0 },
  427. { 0, 3, 1.0 },
  428. { 1, 2, 0.5 },
  429. { 3, 1, 1.0 },
  430. { 3, 4, 1.0 },
  431. { 4, 2, 0.5 }
  432. };
  433. double dw_centrality1[5] = { 0.0, 1.5, 0.0, 1.0, 0.5 };
  434. run_weighted_test((Digraph*)0, 5, dw_edge_init1, 6, dw_centrality1);
  435. run_wheel_test((Graph*)0, 15);
  436. random_unweighted_test((Graph*)0, random_test_num_vertices);
  437. return 0;
  438. }