passagemath-graphs 10.6.1rc1__cp310-cp310-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. passagemath_graphs-10.6.1rc1.dist-info/METADATA +292 -0
  2. passagemath_graphs-10.6.1rc1.dist-info/RECORD +260 -0
  3. passagemath_graphs-10.6.1rc1.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.6.1rc1.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2723 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +124 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1555 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2290 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +530 -0
  25. sage/combinat/designs/database.py +5615 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-310-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +581 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2244 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +967 -0
  44. sage/combinat/designs/resolvable_bibd.py +815 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-310-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/bubble_shuffle.py +247 -0
  57. sage/combinat/posets/cartesian_product.py +493 -0
  58. sage/combinat/posets/d_complete.py +182 -0
  59. sage/combinat/posets/elements.py +273 -0
  60. sage/combinat/posets/forest.py +30 -0
  61. sage/combinat/posets/hasse_cython.cpython-310-aarch64-linux-gnu.so +0 -0
  62. sage/combinat/posets/hasse_cython.pyx +174 -0
  63. sage/combinat/posets/hasse_diagram.py +3672 -0
  64. sage/combinat/posets/hochschild_lattice.py +158 -0
  65. sage/combinat/posets/incidence_algebras.py +794 -0
  66. sage/combinat/posets/lattices.py +5117 -0
  67. sage/combinat/posets/linear_extension_iterator.cpython-310-aarch64-linux-gnu.so +0 -0
  68. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  69. sage/combinat/posets/linear_extensions.py +1037 -0
  70. sage/combinat/posets/mobile.py +275 -0
  71. sage/combinat/posets/moebius_algebra.py +776 -0
  72. sage/combinat/posets/poset_examples.py +2178 -0
  73. sage/combinat/posets/posets.py +9360 -0
  74. sage/combinat/rooted_tree.py +1070 -0
  75. sage/combinat/shard_order.py +239 -0
  76. sage/combinat/tamari_lattices.py +384 -0
  77. sage/combinat/yang_baxter_graph.py +923 -0
  78. sage/databases/all__sagemath_graphs.py +1 -0
  79. sage/databases/knotinfo_db.py +1231 -0
  80. sage/ext_data/all__sagemath_graphs.py +1 -0
  81. sage/ext_data/graphs/graph_plot_js.html +330 -0
  82. sage/ext_data/kenzo/CP2.txt +45 -0
  83. sage/ext_data/kenzo/CP3.txt +349 -0
  84. sage/ext_data/kenzo/CP4.txt +4774 -0
  85. sage/ext_data/kenzo/README.txt +49 -0
  86. sage/ext_data/kenzo/S4.txt +20 -0
  87. sage/graphs/all.py +42 -0
  88. sage/graphs/asteroidal_triples.cpython-310-aarch64-linux-gnu.so +0 -0
  89. sage/graphs/asteroidal_triples.pyx +320 -0
  90. sage/graphs/base/all.py +1 -0
  91. sage/graphs/base/boost_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  92. sage/graphs/base/boost_graph.pxd +106 -0
  93. sage/graphs/base/boost_graph.pyx +3045 -0
  94. sage/graphs/base/c_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  95. sage/graphs/base/c_graph.pxd +106 -0
  96. sage/graphs/base/c_graph.pyx +5096 -0
  97. sage/graphs/base/dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sage/graphs/base/dense_graph.pxd +28 -0
  99. sage/graphs/base/dense_graph.pyx +801 -0
  100. sage/graphs/base/graph_backends.cpython-310-aarch64-linux-gnu.so +0 -0
  101. sage/graphs/base/graph_backends.pxd +5 -0
  102. sage/graphs/base/graph_backends.pyx +797 -0
  103. sage/graphs/base/overview.py +85 -0
  104. sage/graphs/base/sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  105. sage/graphs/base/sparse_graph.pxd +90 -0
  106. sage/graphs/base/sparse_graph.pyx +1653 -0
  107. sage/graphs/base/static_dense_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  108. sage/graphs/base/static_dense_graph.pxd +5 -0
  109. sage/graphs/base/static_dense_graph.pyx +1032 -0
  110. sage/graphs/base/static_sparse_backend.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sage/graphs/base/static_sparse_backend.pxd +27 -0
  112. sage/graphs/base/static_sparse_backend.pyx +1583 -0
  113. sage/graphs/base/static_sparse_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  114. sage/graphs/base/static_sparse_graph.pxd +37 -0
  115. sage/graphs/base/static_sparse_graph.pyx +1375 -0
  116. sage/graphs/bipartite_graph.py +2732 -0
  117. sage/graphs/centrality.cpython-310-aarch64-linux-gnu.so +0 -0
  118. sage/graphs/centrality.pyx +1038 -0
  119. sage/graphs/cographs.py +519 -0
  120. sage/graphs/comparability.cpython-310-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/comparability.pyx +851 -0
  122. sage/graphs/connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  123. sage/graphs/connectivity.pxd +157 -0
  124. sage/graphs/connectivity.pyx +4813 -0
  125. sage/graphs/convexity_properties.cpython-310-aarch64-linux-gnu.so +0 -0
  126. sage/graphs/convexity_properties.pxd +16 -0
  127. sage/graphs/convexity_properties.pyx +870 -0
  128. sage/graphs/digraph.py +4754 -0
  129. sage/graphs/digraph_generators.py +1993 -0
  130. sage/graphs/distances_all_pairs.cpython-310-aarch64-linux-gnu.so +0 -0
  131. sage/graphs/distances_all_pairs.pxd +12 -0
  132. sage/graphs/distances_all_pairs.pyx +2938 -0
  133. sage/graphs/domination.py +1363 -0
  134. sage/graphs/dot2tex_utils.py +100 -0
  135. sage/graphs/edge_connectivity.cpython-310-aarch64-linux-gnu.so +0 -0
  136. sage/graphs/edge_connectivity.pyx +1215 -0
  137. sage/graphs/generators/all.py +1 -0
  138. sage/graphs/generators/basic.py +1769 -0
  139. sage/graphs/generators/chessboard.py +538 -0
  140. sage/graphs/generators/classical_geometries.py +1611 -0
  141. sage/graphs/generators/degree_sequence.py +235 -0
  142. sage/graphs/generators/distance_regular.cpython-310-aarch64-linux-gnu.so +0 -0
  143. sage/graphs/generators/distance_regular.pyx +2846 -0
  144. sage/graphs/generators/families.py +4759 -0
  145. sage/graphs/generators/intersection.py +565 -0
  146. sage/graphs/generators/platonic_solids.py +262 -0
  147. sage/graphs/generators/random.py +2623 -0
  148. sage/graphs/generators/smallgraphs.py +5741 -0
  149. sage/graphs/generators/world_map.py +724 -0
  150. sage/graphs/generic_graph.py +26867 -0
  151. sage/graphs/generic_graph_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  152. sage/graphs/generic_graph_pyx.pxd +34 -0
  153. sage/graphs/generic_graph_pyx.pyx +1673 -0
  154. sage/graphs/genus.cpython-310-aarch64-linux-gnu.so +0 -0
  155. sage/graphs/genus.pyx +622 -0
  156. sage/graphs/graph.py +9645 -0
  157. sage/graphs/graph_coloring.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sage/graphs/graph_coloring.pyx +2284 -0
  159. sage/graphs/graph_database.py +1177 -0
  160. sage/graphs/graph_decompositions/all.py +1 -0
  161. sage/graphs/graph_decompositions/bandwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  163. sage/graphs/graph_decompositions/clique_separators.cpython-310-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/clique_separators.pyx +616 -0
  165. sage/graphs/graph_decompositions/cutwidth.cpython-310-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  167. sage/graphs/graph_decompositions/fast_digraph.cpython-310-aarch64-linux-gnu.so +0 -0
  168. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  169. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  170. sage/graphs/graph_decompositions/graph_products.cpython-310-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/graph_products.pyx +508 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  173. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  174. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  176. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  177. sage/graphs/graph_decompositions/slice_decomposition.pyx +1106 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.cpython-310-aarch64-linux-gnu.so +0 -0
  179. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  180. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  181. sage/graphs/graph_decompositions/vertex_separation.cpython-310-aarch64-linux-gnu.so +0 -0
  182. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  183. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  184. sage/graphs/graph_editor.py +82 -0
  185. sage/graphs/graph_generators.py +3314 -0
  186. sage/graphs/graph_generators_pyx.cpython-310-aarch64-linux-gnu.so +0 -0
  187. sage/graphs/graph_generators_pyx.pyx +95 -0
  188. sage/graphs/graph_input.py +812 -0
  189. sage/graphs/graph_latex.py +2064 -0
  190. sage/graphs/graph_list.py +410 -0
  191. sage/graphs/graph_plot.py +1756 -0
  192. sage/graphs/graph_plot_js.py +338 -0
  193. sage/graphs/hyperbolicity.cpython-310-aarch64-linux-gnu.so +0 -0
  194. sage/graphs/hyperbolicity.pyx +1704 -0
  195. sage/graphs/hypergraph_generators.py +364 -0
  196. sage/graphs/independent_sets.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sage/graphs/independent_sets.pxd +13 -0
  198. sage/graphs/independent_sets.pyx +402 -0
  199. sage/graphs/isgci.py +1033 -0
  200. sage/graphs/isoperimetric_inequalities.cpython-310-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/isoperimetric_inequalities.pyx +489 -0
  202. sage/graphs/line_graph.cpython-310-aarch64-linux-gnu.so +0 -0
  203. sage/graphs/line_graph.pyx +743 -0
  204. sage/graphs/lovasz_theta.py +77 -0
  205. sage/graphs/matching.py +1633 -0
  206. sage/graphs/matching_covered_graph.py +3590 -0
  207. sage/graphs/orientations.py +1489 -0
  208. sage/graphs/partial_cube.py +459 -0
  209. sage/graphs/path_enumeration.cpython-310-aarch64-linux-gnu.so +0 -0
  210. sage/graphs/path_enumeration.pyx +2040 -0
  211. sage/graphs/pq_trees.py +1129 -0
  212. sage/graphs/print_graphs.py +201 -0
  213. sage/graphs/schnyder.py +865 -0
  214. sage/graphs/spanning_tree.cpython-310-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/spanning_tree.pyx +1457 -0
  216. sage/graphs/strongly_regular_db.cpython-310-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/strongly_regular_db.pyx +3340 -0
  218. sage/graphs/traversals.cpython-310-aarch64-linux-gnu.so +0 -0
  219. sage/graphs/traversals.pxd +9 -0
  220. sage/graphs/traversals.pyx +1872 -0
  221. sage/graphs/trees.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sage/graphs/trees.pxd +15 -0
  223. sage/graphs/trees.pyx +310 -0
  224. sage/graphs/tutte_polynomial.py +713 -0
  225. sage/graphs/views.cpython-310-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/views.pyx +794 -0
  227. sage/graphs/weakly_chordal.cpython-310-aarch64-linux-gnu.so +0 -0
  228. sage/graphs/weakly_chordal.pyx +604 -0
  229. sage/groups/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  231. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-310-aarch64-linux-gnu.so +0 -0
  233. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  234. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  235. sage/knots/all.py +6 -0
  236. sage/knots/free_knotinfo_monoid.py +507 -0
  237. sage/knots/gauss_code.py +291 -0
  238. sage/knots/knot.py +682 -0
  239. sage/knots/knot_table.py +284 -0
  240. sage/knots/knotinfo.py +2900 -0
  241. sage/knots/link.py +4715 -0
  242. sage/sandpiles/all.py +13 -0
  243. sage/sandpiles/examples.py +225 -0
  244. sage/sandpiles/sandpile.py +6365 -0
  245. sage/topology/all.py +22 -0
  246. sage/topology/cell_complex.py +1214 -0
  247. sage/topology/cubical_complex.py +1976 -0
  248. sage/topology/delta_complex.py +1806 -0
  249. sage/topology/filtered_simplicial_complex.py +744 -0
  250. sage/topology/moment_angle_complex.py +823 -0
  251. sage/topology/simplicial_complex.py +5160 -0
  252. sage/topology/simplicial_complex_catalog.py +92 -0
  253. sage/topology/simplicial_complex_examples.py +1680 -0
  254. sage/topology/simplicial_complex_homset.py +205 -0
  255. sage/topology/simplicial_complex_morphism.py +836 -0
  256. sage/topology/simplicial_set.py +4102 -0
  257. sage/topology/simplicial_set_catalog.py +55 -0
  258. sage/topology/simplicial_set_constructions.py +2954 -0
  259. sage/topology/simplicial_set_examples.py +865 -0
  260. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,1038 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ r"""
3
+ Centrality
4
+
5
+ This module is meant for all functions related to centrality in networks.
6
+
7
+ .. csv-table::
8
+ :class: contentstable
9
+ :widths: 30, 70
10
+ :delim: |
11
+
12
+ :func:`centrality_betweenness` | Return the centrality betweenness of `G`
13
+ :func:`centrality_closeness_top_k` | Return the k most closeness central vertices of `G`
14
+ :func:`centrality_closeness_random_k` | Return an estimation of the closeness centrality of `G`.
15
+
16
+ Functions
17
+ ---------
18
+ """
19
+
20
+ from libc.string cimport memset
21
+ from libc.stdint cimport uint32_t
22
+ from cysignals.memory cimport check_allocarray, sig_free
23
+ from cysignals.signals cimport sig_check
24
+ from memory_allocator cimport MemoryAllocator
25
+
26
+ from sage.data_structures.bitset_base cimport *
27
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
28
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
29
+ from sage.graphs.base.static_sparse_graph cimport *
30
+ from sage.libs.gmp.mpq cimport *
31
+ from sage.rings.rational cimport Rational
32
+ from sage.graphs.base.boost_graph import shortest_paths as boost_shortest_paths
33
+ import random
34
+
35
+ ctypedef fused numerical_type:
36
+ mpq_t
37
+ double
38
+
39
+ cimport cython
40
+
41
+
42
+ def centrality_betweenness(G, bint exact=False, bint normalize=True):
43
+ r"""
44
+ Return the centrality betweenness of `G`.
45
+
46
+ The centrality betweenness of a vertex `v\in G` is defined by:
47
+
48
+ .. MATH::
49
+
50
+ c(v) = \sum_{s\neq v \neq t} \frac{\#\{\text{shortest } st-\text{paths containing v}\}}
51
+ {\#\{\text{shortest } st-\text{paths}\}}
52
+
53
+ For more information, see the :wikipedia:`Betweenness_centrality`.
54
+
55
+ INPUT:
56
+
57
+ - ``G`` -- a (di)graph
58
+
59
+ - ``exact`` -- boolean (default: ``False``); whether to compute over
60
+ rationals or on ``double`` C variables
61
+
62
+ - ``normalize`` -- boolean (default: ``True``); whether to renormalize the
63
+ values by dividing them by `\binom {n-1} 2` (for graphs) or `2\binom {n-1}
64
+ 2` (for digraphs).
65
+
66
+ ALGORITHM:
67
+
68
+ To compute `c(v)`, we fix `s` and define `c_s(v)` as the centrality of `v`
69
+ *due to* `s`, obtained from the formula above by running the sum over `t`
70
+ only. We obtain `c(v)=\sum_{s\neq v} c_s(v)`.
71
+
72
+ For every vertex `s`, we compute the value of `c_s(v)` for all `v`, using
73
+ the following remark (see [Brandes01]_):
74
+
75
+ Let `v_1,\ldots,v_k` be the out-neighbors of `v` such that
76
+ `dist(s,v_i) = dist(s,v) + 1`. Then
77
+
78
+ .. MATH::
79
+
80
+ c_s(v) = \sum_{1\leq i \leq k} c_s(v_i)
81
+ \frac{\#\{\text{shortest } sv_i-\text{paths}\}}
82
+ {\#\{\text{shortest } sv -\text{paths}\}}
83
+
84
+ The number of shortest paths between `s` and every other vertex can be
85
+ computed with a slightly modified BFS. While running this BFS we can also
86
+ store the list of the vertices `v_1,\ldots,v_k` associated with each `v`.
87
+
88
+ EXAMPLES::
89
+
90
+ sage: from sage.graphs.centrality import centrality_betweenness
91
+ sage: centrality_betweenness(digraphs.Circuit(6)) # abs tol 1e-10
92
+ {0: 0.5, 1: 0.5, 2: 0.5, 3: 0.5, 4: 0.5, 5: 0.5}
93
+ sage: centrality_betweenness(graphs.CycleGraph(6)) # abs tol 1e-10
94
+ {0: 0.2, 1: 0.2, 2: 0.2, 3: 0.2, 4: 0.2, 5: 0.2}
95
+
96
+ Exact computations::
97
+
98
+ sage: graphs.PetersenGraph().centrality_betweenness(exact=True)
99
+ {0: 1/12, 1: 1/12, 2: 1/12, 3: 1/12, 4: 1/12, 5: 1/12, 6: 1/12, 7: 1/12, 8: 1/12, 9: 1/12}
100
+
101
+ TESTS:
102
+
103
+ Compare with NetworkX::
104
+
105
+ sage: # needs networkx
106
+ sage: import networkx
107
+ sage: g = graphs.RandomGNP(100, .2)
108
+ sage: nw = networkx.betweenness_centrality(g.networkx_graph())
109
+ sage: sg = centrality_betweenness(g)
110
+ sage: max(abs(nw[x] - sg[x]) for x in g) # abs tol 1e-10
111
+ 0
112
+
113
+ Stupid cases::
114
+
115
+ sage: centrality_betweenness(Graph())
116
+ {}
117
+ sage: centrality_betweenness(Graph(2))
118
+ {0: 0.0, 1: 0.0}
119
+ sage: centrality_betweenness(Graph(2), exact=1)
120
+ {0: 0, 1: 0}
121
+
122
+ The method is valid for immutable graphs::
123
+
124
+ sage: G = graphs.RandomGNP(10, .7)
125
+ sage: G._backend
126
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
127
+ sage: H = Graph(G, immutable=True)
128
+ sage: H._backend
129
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
130
+ sage: G.centrality_betweenness() == H.centrality_betweenness()
131
+ True
132
+ sage: G.centrality_betweenness(exact=True) == H.centrality_betweenness(exact=True)
133
+ True
134
+ """
135
+ if exact:
136
+ return centrality_betweenness_C(G, <mpq_t> 0, normalize=normalize)
137
+ return centrality_betweenness_C(G, <double>0, normalize=normalize)
138
+
139
+
140
+ @cython.cdivision(True)
141
+ cdef dict centrality_betweenness_C(G, numerical_type _, bint normalize=True):
142
+ r"""
143
+ Return the centrality betweenness of G (C implementation).
144
+
145
+ INPUT:
146
+
147
+ - ``G`` -- a graph
148
+
149
+ - ``_`` -- this variable is ignored, only its type matters. If it is of type
150
+ ``mpq_t`` then computations are made on `Q`, if it is ``double`` the
151
+ computations are made on ``double``.
152
+
153
+ - ``normalize`` -- boolean (default: ``True``); whether to renormalize the
154
+ values by dividing them by `\binom {n-1} 2` (for graphs) or `2\binom {n-1}
155
+ 2` (for digraphs).
156
+
157
+ For more information, see the documentation of ``centrality_betweenness``.
158
+ """
159
+ # Trivial case
160
+ if G.order() <= 2:
161
+ zero = 0. if numerical_type is double else Rational(0)
162
+ return {v: zero for v in G}
163
+
164
+ # A copy of G, for faster neighbor enumeration
165
+ cdef StaticSparseCGraph cg
166
+ cdef short_digraph g
167
+
168
+ # A second copy, to remember the edges used during the BFS (see doc)
169
+ cdef short_digraph bfs_dag
170
+
171
+ cdef list int_to_vertex
172
+
173
+ cdef int n = G.order()
174
+
175
+ cdef bitset_t seen # Vertices whose neighbors have been explored
176
+ cdef bitset_t next_layer # Unexplored neighbors of vertices in 'seen'
177
+
178
+ cdef uint32_t* queue = NULL # BFS queue
179
+ cdef uint32_t* degrees = NULL # degree[v] = nb of vertices which discovered v
180
+
181
+ cdef numerical_type* n_paths_from_source = NULL
182
+ cdef numerical_type* betweenness_source = NULL
183
+ cdef numerical_type* betweenness = NULL
184
+ cdef numerical_type mpq_tmp
185
+
186
+ cdef int layer_current_beginning
187
+ cdef int layer_current_end
188
+ cdef int layer_next_end
189
+
190
+ cdef int source, i, j, u, v
191
+ cdef uint32_t* p_tmp
192
+ cdef list betweenness_list
193
+
194
+ if numerical_type is mpq_t:
195
+ mpq_init(mpq_tmp)
196
+
197
+ try:
198
+ if isinstance(G, StaticSparseBackend):
199
+ cg = <StaticSparseCGraph> G._cg
200
+ g = <short_digraph> cg.g
201
+ int_to_vertex = cg._vertex_to_labels
202
+ else:
203
+ int_to_vertex = list(G)
204
+ init_short_digraph(g, G, edge_labelled=False, vertex_list=int_to_vertex)
205
+
206
+ init_reverse(bfs_dag, g)
207
+
208
+ queue = <uint32_t*> check_allocarray(n, sizeof(uint32_t))
209
+ degrees = <uint32_t*> check_allocarray(n, sizeof(uint32_t))
210
+ n_paths_from_source = <numerical_type*> check_allocarray(n, sizeof(numerical_type))
211
+ betweenness_source = <numerical_type*> check_allocarray(n, sizeof(numerical_type))
212
+ betweenness = <numerical_type*> check_allocarray(n, sizeof(numerical_type))
213
+
214
+ bitset_init(seen, n)
215
+ bitset_init(next_layer, n)
216
+
217
+ if numerical_type is double:
218
+ memset(betweenness, 0, n * sizeof(double))
219
+ else:
220
+ for i in range(n):
221
+ mpq_init(betweenness[i])
222
+ mpq_set_ui(betweenness[i], 0, 1)
223
+ mpq_init(betweenness_source[i])
224
+ mpq_init(n_paths_from_source[i])
225
+
226
+ for source in range(n):
227
+
228
+ if numerical_type is double:
229
+ memset(betweenness_source, 0, n * sizeof(double))
230
+ memset(n_paths_from_source, 0, n * sizeof(double))
231
+ n_paths_from_source[source] = 1
232
+ else:
233
+ for i in range(n):
234
+ mpq_set_ui(betweenness_source[i], 0, 1)
235
+ mpq_set_ui(n_paths_from_source[i], 0, 1)
236
+ mpq_set_ui(n_paths_from_source[source], 1, 1)
237
+
238
+ # initialize data
239
+ bitset_set_first_n(seen, 0)
240
+ bitset_add(seen, source)
241
+ bitset_set_first_n(next_layer, 0)
242
+
243
+ memset(degrees, 0, n * sizeof(uint32_t))
244
+
245
+ queue[0] = source
246
+ layer_current_beginning = 0
247
+ layer_current_end = 1
248
+ layer_next_end = 1
249
+
250
+ # The number of shortest paths from 'source' to every other vertex.
251
+ #
252
+ # It is a BFS. The graph is explored layer by layer.
253
+ while layer_current_beginning<layer_current_end:
254
+
255
+ # Looking for all non-discovered neighbors of some vertex of the
256
+ # current layer.
257
+ for j in range(layer_current_beginning, layer_current_end):
258
+ u = queue[j]
259
+
260
+ # List the neighbors of u
261
+ p_tmp = g.neighbors[u]
262
+ while p_tmp < g.neighbors[u + 1]:
263
+ v = p_tmp[0]
264
+ p_tmp += 1
265
+
266
+ # Is it a new vertex ?
267
+ if bitset_in(seen, v):
268
+ continue
269
+
270
+ # Is it the first time we see it ?
271
+ elif not bitset_in(next_layer, v):
272
+ bitset_add(next_layer, v)
273
+ queue[layer_next_end] = v
274
+ layer_next_end += 1
275
+
276
+ # update the count of paths and the BFS dag.
277
+ bfs_dag.neighbors[v][degrees[v]] = u
278
+ degrees[v] += 1
279
+ if numerical_type is double:
280
+ n_paths_from_source[v] += n_paths_from_source[u]
281
+ else:
282
+ mpq_add(n_paths_from_source[v], n_paths_from_source[v], n_paths_from_source[u])
283
+
284
+ # 'next_layer' becomes 'current_layer'
285
+ for j in range(layer_current_end, layer_next_end):
286
+ bitset_add(seen, queue[j])
287
+
288
+ layer_current_beginning = layer_current_end
289
+ layer_current_end = layer_next_end
290
+
291
+ # Compute the betweenness from the number of paths
292
+ #
293
+ # We enumerate vertices in reverse order of discovery.
294
+ for i in range(layer_current_end - 1, -1, -1):
295
+ u = queue[i]
296
+ for j in range(<int>degrees[u]):
297
+ v = bfs_dag.neighbors[u][j]
298
+ if v != source: # better to not 'if' but set it to 0 afterwards?
299
+ if numerical_type is double:
300
+ betweenness_source[v] += (betweenness_source[u] + 1) * (n_paths_from_source[v] / n_paths_from_source[u])
301
+ else:
302
+ mpq_set_ui(mpq_tmp, 1, 1)
303
+ mpq_add(mpq_tmp, betweenness_source[u], mpq_tmp)
304
+ mpq_mul(mpq_tmp, mpq_tmp, n_paths_from_source[v])
305
+ mpq_div(mpq_tmp, mpq_tmp, n_paths_from_source[u])
306
+ mpq_add(betweenness_source[v], betweenness_source[v], mpq_tmp)
307
+
308
+ # update betweenness from betweenness_source
309
+ for i in range(n):
310
+ if numerical_type is double:
311
+ betweenness[i] += betweenness_source[i]
312
+ else:
313
+ mpq_add(betweenness[i], betweenness[i], betweenness_source[i])
314
+
315
+ sig_check() # check for KeyboardInterrupt
316
+
317
+ if numerical_type is double:
318
+ betweenness_list = [betweenness[i] for i in range(n)]
319
+ else:
320
+ betweenness_list = [Rational(None) for x in range(n)]
321
+
322
+ for i in range(n):
323
+ (<Rational> (betweenness_list[i])).set_from_mpq(betweenness[i])
324
+ for i in range(n):
325
+ mpq_clear(betweenness_source[i])
326
+ mpq_clear(betweenness[i])
327
+ mpq_clear(n_paths_from_source[i])
328
+ mpq_clear(mpq_tmp)
329
+
330
+ finally:
331
+ if not isinstance(G, StaticSparseBackend):
332
+ free_short_digraph(g)
333
+ free_short_digraph(bfs_dag)
334
+ bitset_free(seen)
335
+ bitset_free(next_layer)
336
+ sig_free(queue)
337
+ sig_free(n_paths_from_source)
338
+ sig_free(degrees)
339
+ sig_free(betweenness_source)
340
+ sig_free(betweenness)
341
+
342
+ if not G.is_directed():
343
+ betweenness_list = [x / 2 for x in betweenness_list]
344
+
345
+ if normalize:
346
+ if G.is_directed():
347
+ betweenness_list = [x / ((n - 1) * (n - 2)) for x in betweenness_list]
348
+ else:
349
+ betweenness_list = [2 * x / ((n - 1) * (n - 2)) for x in betweenness_list]
350
+
351
+ return {vv: betweenness_list[i] for i, vv in enumerate(int_to_vertex)}
352
+
353
+
354
+ cdef void _estimate_reachable_vertices_dir(short_digraph g, int* reachL, int* reachU) noexcept:
355
+ r"""
356
+ For each vertex ``v``, bounds the number of vertices reachable from ``v``.
357
+
358
+ The lower bound is stored in ``reachL[v]``, while the upper bound is stored
359
+ in ``reachU[v]``. These two arrays must be pre-allocated and they must have
360
+ size at least ``n``, where ``n`` is the number of nodes of ``g``.
361
+
362
+ The estimate works as follows: first, we compute the graph of strongly
363
+ connected components `\mathcal{G=(V,E)}`, then, for each SCC `C`, we set:
364
+
365
+ .. MATH::
366
+
367
+ L(C)=|C|+\max_{(C,C') \in \mathcal{E}}L(C') \\
368
+ U(C)=|C|+\max_{(C,C') \in \mathcal{E}}L(C')
369
+
370
+ By analyzing strongly connected components in reverse topological order, we
371
+ are sure that, as soon as we process component `C`, all components `C'`
372
+ appearing on the right hand side have already been processed. A further
373
+ improvement on these bounds is obtained by exactly computing the number of
374
+ vertices reachable from the biggest strongly connected component, and handle
375
+ this component separately.
376
+
377
+ Then, for each vertex ``v``, we set ``reachL[v]=L(C)``, where `C` is the
378
+ strongly connected component containing ``v``.
379
+
380
+ INPUT:
381
+
382
+ - ``g`` -- short_digraph; the input graph;
383
+
384
+ OUTPUT:
385
+
386
+ ``reachL``, ``reachU``: two arrays that should be allocated outside this
387
+ function and that should have size at least ``g.n``. At the end,
388
+ ``reachL[v]`` (resp., ``reachU[v]``) will contain the lower (resp., upper)
389
+ bound on the number of reachable vertices from ``v``.
390
+ """
391
+ cdef MemoryAllocator mem = MemoryAllocator()
392
+ cdef int n = g.n
393
+ cdef int* scc = <int*> mem.malloc(n * sizeof(int))
394
+ cdef int i, v, w, maxscc = 0
395
+ cdef int nscc = tarjan_strongly_connected_components_C(g, scc)
396
+ cdef short_digraph sccgraph
397
+ strongly_connected_components_digraph_C(g, nscc, scc, sccgraph)
398
+
399
+ cdef int* scc_sizes = <int*> mem.calloc(nscc, sizeof(int))
400
+ cdef int nreach_maxscc = 0
401
+ cdef short* reach_max_scc = <short*> mem.calloc(nscc, sizeof(short))
402
+ cdef int* reachL_scc = <int*> mem.calloc(nscc, sizeof(int))
403
+ cdef uint64_t* reachU_scc = <uint64_t*> mem.calloc(nscc, sizeof(uint64_t))
404
+ cdef uint64_t* reachU_without_maxscc = <uint64_t*> mem.calloc(nscc, sizeof(uint64_t))
405
+ # We need uint64_t because these values may become much bigger than g.n,
406
+ # up to g.n^2, during the computation. Only at the end, we set reachL and
407
+ # reachU as the maximum between g.n and the computed value (so that they
408
+ # can be converted to int without overflow).
409
+
410
+ # Variables used in BFS from the largest strongly connected component
411
+ cdef uint32_t startq, endq
412
+ cdef int* q = <int*> mem.malloc(nscc * sizeof(int))
413
+ cdef short* reached = <short*> mem.calloc(nscc, sizeof(short))
414
+ cdef uint32_t* neigh_start
415
+ cdef uint32_t* neigh_end
416
+
417
+ # Compute scc_sizes
418
+ for i in range(g.n):
419
+ scc_sizes[scc[i]] += 1
420
+
421
+ # Compute maxscc
422
+ for i in range(nscc):
423
+ if scc_sizes[maxscc] < scc_sizes[i]:
424
+ maxscc = i
425
+ reach_max_scc[maxscc] = 1
426
+
427
+ # BFS to compute number of reachable vertices for the biggest SCC.
428
+ q[0] = maxscc
429
+ nreach_maxscc = scc_sizes[maxscc]
430
+ reached[maxscc] = 1
431
+ startq = 0
432
+ endq = 1
433
+ while startq < endq:
434
+ v = q[startq]
435
+ startq += 1
436
+ neigh_start = sccgraph.neighbors[v]
437
+ neigh_end = sccgraph.neighbors[v + 1]
438
+
439
+ while neigh_start < neigh_end:
440
+ w = neigh_start[0]
441
+ if not reached[w]:
442
+ reached[w] = 1
443
+ nreach_maxscc += scc_sizes[w]
444
+ q[endq] = w
445
+ endq += 1
446
+ neigh_start += 1
447
+
448
+ reachL_scc[maxscc] = nreach_maxscc
449
+ reachU_scc[maxscc] = nreach_maxscc
450
+ reachU_without_maxscc[maxscc] = 0
451
+ # Dynamic programming to estimate number of reachable vertices for other
452
+ # SCCs
453
+ for i in range(nscc):
454
+ if i == maxscc:
455
+ continue
456
+
457
+ neigh_start = sccgraph.neighbors[i]
458
+ neigh_end = sccgraph.neighbors[i + 1]
459
+
460
+ while neigh_start < neigh_end:
461
+ w = neigh_start[0]
462
+ neigh_start += 1
463
+
464
+ reachL_scc[i] = max(reachL_scc[i], reachL_scc[w])
465
+ reachU_scc[i] += reachU_scc[w]
466
+ # Note that this might become much bigger than g.n, up to g.n*g.n.
467
+ # Hence we used uint64_t, and only at the end we take the minimum
468
+ # between this value and g.n (since g.n is an upper bound on
469
+ # the number of reachable vertices).
470
+ if not reached[w]:
471
+ reachU_without_maxscc[i] += reachU_without_maxscc[w]
472
+ reach_max_scc[i] = reach_max_scc[i] or reach_max_scc[w]
473
+
474
+ if reach_max_scc[i]:
475
+ reachU_scc[i] = reachU_without_maxscc[i] + nreach_maxscc
476
+
477
+ reachL_scc[i] += scc_sizes[i]
478
+ reachU_scc[i] += scc_sizes[i]
479
+ if not reached[i]:
480
+ reachU_without_maxscc[i] += scc_sizes[i]
481
+
482
+ for i in range(n):
483
+ reachL[i] = reachL_scc[scc[i]]
484
+ reachU[i] = min(<int>reachU_scc[scc[i]], g.n)
485
+
486
+ free_short_digraph(sccgraph)
487
+
488
+
489
+ cdef void _compute_reachable_vertices_undir(short_digraph g, int* reachable) noexcept:
490
+ r"""
491
+ For each vertex ``v``, compute the number of vertices reachable from ``v``.
492
+
493
+ The number of vertices reachable from ``v`` (which is the size of the
494
+ connected component containing ``v``) is stored in variable
495
+ ``reachable[v]``. The array `reachable` is assumed to be allocated outside
496
+ this function, and it is assumed to have size at least ``g.n``.
497
+ """
498
+ cdef MemoryAllocator mem = MemoryAllocator()
499
+ cdef int i
500
+ cdef int n = g.n
501
+ cdef int* q = <int*> mem.malloc(n * sizeof(int))
502
+ cdef short* reached = <short*> mem.calloc(n, sizeof(short))
503
+
504
+ cdef int v, w
505
+ cdef uint32_t* neigh_start
506
+ cdef uint32_t* neigh_end
507
+ cdef uint32_t startq, endq
508
+ cdef list currentcc
509
+
510
+ memset(reachable, 0, n * sizeof(int))
511
+
512
+ for i in range(n):
513
+ # BFS from i
514
+ if reachable[i]:
515
+ continue
516
+
517
+ reached[i] = 1
518
+ currentcc = [i]
519
+
520
+ q[0] = i
521
+ startq = 0
522
+ endq = 1
523
+ while startq < endq:
524
+ v = q[startq]
525
+ startq += 1
526
+ neigh_start = g.neighbors[v]
527
+ neigh_end = g.neighbors[v + 1]
528
+
529
+ while neigh_start < neigh_end:
530
+ w = neigh_start[0]
531
+ if not reached[w]:
532
+ reached[w] = 1
533
+ currentcc.append(w)
534
+ q[endq] = w
535
+ endq += 1
536
+ neigh_start += 1
537
+
538
+ for v in currentcc:
539
+ reachable[v] = len(currentcc)
540
+
541
+
542
+ cdef void _sort_vertices_degree(short_digraph g, int* sorted_verts) noexcept:
543
+ r"""
544
+ Sort vertices in decreasing order of degree.
545
+
546
+ Uses counting sort, since degrees are between `0` and `n-1`: the running
547
+ time is then `O(n)`.
548
+ """
549
+ cdef MemoryAllocator mem = MemoryAllocator()
550
+ cdef uint32_t* verts_of_degree = <uint32_t*> mem.calloc(g.n, sizeof(uint32_t))
551
+ cdef uint32_t* next_vert_of_degree = <uint32_t*> mem.malloc(g.n * sizeof(uint32_t))
552
+ cdef int d, v
553
+
554
+ # Otherwise, segmentation fault
555
+ if not g.n:
556
+ return
557
+
558
+ for v in range(g.n):
559
+ verts_of_degree[out_degree(g, v)] += 1
560
+
561
+ next_vert_of_degree[g.n - 1] = 0
562
+ for i in range(g.n - 2, -1, -1):
563
+ next_vert_of_degree[i] = next_vert_of_degree[i + 1] + verts_of_degree[i + 1]
564
+
565
+ for v in range(g.n):
566
+ d = out_degree(g, v)
567
+ sorted_verts[next_vert_of_degree[d]] = v
568
+ next_vert_of_degree[d] += 1
569
+
570
+
571
+ def centrality_closeness_top_k(G, int k=1, int verbose=0):
572
+ r"""
573
+ Compute the ``k`` vertices with largest closeness centrality.
574
+
575
+ The algorithm is based on performing a breadth-first-search (BFS) from each
576
+ vertex, and to use bounds in order to cut these BFSes as soon as possible.
577
+ If ``k`` is small, it is much more efficient than computing all centralities
578
+ with :meth:`~sage.graphs.generic_graph.GenericGraph.centrality_closeness`.
579
+ Conversely, if ``k`` is close to the number of nodes, the running-time is
580
+ approximately the same (it might even be a bit longer, because more
581
+ computations are needed).
582
+
583
+ For more information, see [BCM15]_. The algorithm does not work on
584
+ weighted graphs.
585
+
586
+ INPUT:
587
+
588
+ - ``G`` -- a Sage Graph or DiGraph;
589
+
590
+ - ``k`` -- integer (default: 1); the algorithm will return the ``k``
591
+ vertices with largest closeness centrality. This value should be between 1
592
+ and the number of vertices with positive (out)degree, because the
593
+ closeness centrality is not defined for vertices with (out)degree 0. If
594
+ ``k`` is bigger than this value, the output will contain all vertices of
595
+ positive (out)degree.
596
+
597
+ - ``verbose`` -- integer (default: 0); define how "verbose" the algorithm
598
+ should be. If 0, nothing is printed, if 1, we print only the performance
599
+ ratio at the end of the algorithm, if 2, we print partial results every
600
+ 1000 visits, if 3, we print partial results after every visit.
601
+
602
+ OUTPUT:
603
+
604
+ An ordered list of ``k`` pairs `(closv, v)`, where `v` is one of the ``k``
605
+ most central vertices, and `closv` is its closeness centrality. If `k` is
606
+ bigger than the number of vertices with positive (out)degree, the list might
607
+ be smaller.
608
+
609
+ EXAMPLES::
610
+
611
+ sage: from sage.graphs.centrality import centrality_closeness_top_k
612
+ sage: g = graphs.PathGraph(10)
613
+ sage: centrality_closeness_top_k(g, 4, 1)
614
+ Final performance ratio: 0.711111111111...
615
+ [(0.36, 5),
616
+ (0.36, 4),
617
+ (0.3333333333333333, 6),
618
+ (0.3333333333333333, 3)]
619
+ sage: g = digraphs.Path(10)
620
+ sage: centrality_closeness_top_k(g, 5, 1)
621
+ Final performance ratio: 0.422222222222...
622
+ [(0.2, 0),
623
+ (0.19753086419753085, 1),
624
+ (0.19444444444444442, 2),
625
+ (0.19047619047619047, 3),
626
+ (0.18518518518518517, 4)]
627
+
628
+ TESTS:
629
+
630
+ If ``k`` or ``verbose`` is not an integer::
631
+
632
+ sage: from sage.graphs.centrality import centrality_closeness_top_k
633
+ sage: g = digraphs.Path(10)
634
+ sage: centrality_closeness_top_k(g, 'abc', 1)
635
+ Traceback (most recent call last):
636
+ ...
637
+ TypeError: an integer is required
638
+ sage: centrality_closeness_top_k(g, 1, 'abc')
639
+ Traceback (most recent call last):
640
+ ...
641
+ TypeError: an integer is required
642
+
643
+ If ``k`` is bigger than the number of nodes::
644
+
645
+ sage: from sage.graphs.centrality import centrality_closeness_top_k
646
+ sage: g = graphs.PathGraph(5)
647
+ sage: centrality_closeness_top_k(g, 10, 0)
648
+ [(0.6666666666666666, 2),
649
+ (0.5714285714285714, 3),
650
+ (0.5714285714285714, 1),
651
+ (0.4, 4),
652
+ (0.4, 0)]
653
+
654
+ Empty graph::
655
+
656
+ sage: from sage.graphs.centrality import centrality_closeness_top_k
657
+ sage: g = Graph()
658
+ sage: centrality_closeness_top_k(g, 10, 0)
659
+ []
660
+ sage: g = Graph(10)
661
+ sage: centrality_closeness_top_k(g, 10, 0)
662
+ []
663
+
664
+ The result is correct::
665
+
666
+ sage: # needs networkx
667
+ sage: from sage.graphs.centrality import centrality_closeness_top_k
668
+ sage: import random
669
+ sage: n = 20
670
+ sage: m = random.randint(1, n * (n - 1) / 2)
671
+ sage: k = random.randint(1, n)
672
+ sage: g = graphs.RandomGNM(n, m)
673
+ sage: topk = centrality_closeness_top_k(g, k)
674
+ sage: centr = g.centrality_closeness(algorithm='BFS')
675
+ sage: sorted_centr = sorted(centr.values(), reverse=True)
676
+ sage: len(topk) == min(k, len(sorted_centr))
677
+ True
678
+ sage: all(abs(topk[i][0] - sorted_centr[i]) < 1e-12 for i in range(len(topk)))
679
+ True
680
+
681
+ Directed case::
682
+
683
+ sage: # needs networkx
684
+ sage: from sage.graphs.centrality import centrality_closeness_top_k
685
+ sage: import random
686
+ sage: n = 20
687
+ sage: m = random.randint(1, n * (n - 1))
688
+ sage: k = random.randint(1, n)
689
+ sage: g = digraphs.RandomDirectedGNM(n, m)
690
+ sage: topk = centrality_closeness_top_k(g, k)
691
+ sage: centr = g.centrality_closeness(algorithm='BFS')
692
+ sage: sorted_centr = sorted(centr.values(), reverse=True)
693
+ sage: len(topk) == min(k, len(sorted_centr))
694
+ True
695
+ sage: all(abs(topk[i][0] - sorted_centr[i]) < 1e-12 for i in range(len(topk)))
696
+ True
697
+
698
+ Immutable graphs::
699
+
700
+ sage: from sage.graphs.centrality import centrality_closeness_top_k
701
+ sage: G = graphs.RandomGNP(10, .7)
702
+ sage: G._backend
703
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
704
+ sage: H = Graph(G, immutable=True)
705
+ sage: H._backend
706
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
707
+ sage: k = randint(1, 10)
708
+ sage: centrality_closeness_top_k(G, k) == centrality_closeness_top_k(H, k)
709
+ True
710
+ """
711
+ cdef list res
712
+ if k >= G.order():
713
+ closeness_dict = G.centrality_closeness(by_weight=False, algorithm='BFS')
714
+ res = [(closz, z) for z, closz in closeness_dict.items()]
715
+ try:
716
+ res = sorted(res, reverse=True)
717
+ except TypeError:
718
+ # A TypeError may occur in Python 3 when vertex labels are of
719
+ # different types. We then sort on values only.
720
+ res = sorted(res, reverse=True, key=lambda zz: zz[0])
721
+ return res
722
+
723
+ if G.order() < 2:
724
+ return []
725
+
726
+ cdef MemoryAllocator mem = MemoryAllocator()
727
+ cdef StaticSparseCGraph cg
728
+ cdef short_digraph sd
729
+ # Copying the whole graph to obtain the list of neighbors quicker than by
730
+ # calling out_neighbors. This data structure is well documented in the
731
+ # module sage.graphs.base.static_sparse_graph
732
+ cdef list V
733
+ if isinstance(G, StaticSparseBackend):
734
+ cg = <StaticSparseCGraph> G._cg
735
+ sd = <short_digraph> cg.g
736
+ V = cg._vertex_to_labels
737
+ else:
738
+ V = list(G)
739
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=V)
740
+ cdef int n = sd.n
741
+ cdef int* reachL = <int*> mem.malloc(n * sizeof(int))
742
+ cdef int* reachU
743
+ cdef int* pred = <int*> mem.calloc(n, sizeof(int))
744
+ cdef double *farness = <double*> mem.malloc(n * sizeof(double))
745
+ cdef int d, nd, x, v
746
+ cdef long f, gamma
747
+ cdef int* queue = <int*> mem.malloc(n * sizeof(int))
748
+ cdef double tildefL, tildefU
749
+ cdef bint stopped
750
+ cdef uint32_t* p_tmp
751
+ cdef int layer_current_beginning, layer_current_end, layer_next_end=0
752
+ cdef long visited = 0
753
+ cdef int nvis = 0
754
+ cdef short* seen = <short*> mem.calloc(n, sizeof(short))
755
+ cdef bint directed = G.is_directed()
756
+
757
+ cdef int* topk = <int*> mem.malloc(k * sizeof(int))
758
+ for i in range(k):
759
+ topk[i] = -1
760
+ for i in range(n):
761
+ pred[i] = -1
762
+
763
+ cdef double kth = n
764
+ cdef int* sorted_vert = <int*> mem.malloc(n * sizeof(int))
765
+ if directed:
766
+ reachU = <int*> mem.malloc(n * sizeof(int))
767
+ _estimate_reachable_vertices_dir(sd, reachL, reachU)
768
+ else:
769
+ _compute_reachable_vertices_undir(sd, reachL)
770
+ reachU = reachL
771
+ _sort_vertices_degree(sd, sorted_vert)
772
+
773
+ for x in sorted_vert[:n]:
774
+ sig_check()
775
+
776
+ if not out_degree(sd, x):
777
+ break
778
+ # We start a BFSCut from x:
779
+
780
+ # We reset variable seen:
781
+ for v in queue[:layer_next_end]:
782
+ seen[v] = 0
783
+ pred[v] = -1
784
+
785
+ layer_current_beginning = 0
786
+ layer_current_end = 1
787
+ layer_next_end = 1
788
+ d = 0
789
+ f = 0
790
+ # We are at level 0, and gamma is the number of arcs exiting level 0
791
+ # (hence, deg(x)).
792
+ gamma = out_degree(sd, x)
793
+ nd = 1
794
+ queue[0] = x
795
+ stopped = False
796
+ seen[x] = 1
797
+ nvis += 1
798
+
799
+ # The graph is explored layer by layer.
800
+ while layer_current_beginning < layer_current_end and not stopped:
801
+ sig_check()
802
+
803
+ # We update our estimate of the farness of v.
804
+ # The estimate sets distance d+1 to gamma vertices (which is an
805
+ # upper bound on the number of vertices at distance d+1 from v),
806
+ # and distance d+2 to all other vertices reachable from x.
807
+ tildefL = ((f - gamma + (d + 2) * (<double>(reachL[x] - nd))) * (n - 1)) / ((<double>(reachL[x] - 1)) * (reachL[x] - 1))
808
+ tildefU = ((f - gamma + (d + 2) * (<double>(reachU[x] - nd))) * (n - 1)) / ((<double>(reachU[x] - 1)) * (reachU[x] - 1))
809
+ d += 1
810
+ gamma = 0
811
+
812
+ if tildefL >= kth and tildefU >= kth:
813
+ farness[x] = n
814
+ stopped = True
815
+ break
816
+ # Looking for all non-discovered neighbors of some vertex of the
817
+ # current layer.
818
+ for j in range(layer_current_beginning, layer_current_end):
819
+ sig_check()
820
+
821
+ u = queue[j]
822
+
823
+ # List the neighbors of u
824
+ p_tmp = sd.neighbors[u]
825
+ while p_tmp < sd.neighbors[u + 1] and not stopped:
826
+ sig_check()
827
+
828
+ visited += 1
829
+ v = p_tmp[0]
830
+ p_tmp += 1
831
+ # Is it a new vertex ?
832
+ if not seen[v]:
833
+ seen[v] = 1
834
+ queue[layer_next_end] = v
835
+ layer_next_end += 1
836
+ f = f + d
837
+ gamma += out_degree(sd, v) if directed else (out_degree(sd, v) - 1)
838
+ nd = nd + 1
839
+ pred[v] = u
840
+ elif directed or pred[u] != v:
841
+ tildefL += (n - 1) / (<double>(reachL[x] - 1) * (reachL[x] - 1))
842
+ tildefU += (n - 1) / (<double>(reachU[x] - 1) * (reachU[x] - 1))
843
+ if tildefL >= kth and tildefU >= kth:
844
+ farness[x] = n
845
+ stopped = True
846
+ if stopped:
847
+ break
848
+ # 'next_layer' becomes 'current_layer'
849
+ layer_current_beginning = layer_current_end
850
+ layer_current_end = layer_next_end
851
+
852
+ if not stopped:
853
+ farness[x] = ((<double> f) * (n - 1)) / (<double>(nd - 1) * (nd - 1))
854
+
855
+ if farness[x] < kth:
856
+ for i in range(k):
857
+ if topk[i] == -1 or farness[topk[i]] == kth:
858
+ topk[i] = x
859
+ break
860
+ kth = 0
861
+ for i in range(k):
862
+ if topk[i] == -1:
863
+ kth = n
864
+ break
865
+ kth = max(kth, farness[topk[i]])
866
+ if verbose >= 3 or (verbose == 2 and nvis % 1000 == 0):
867
+ print("Visit {} from {}:".format(nvis, x))
868
+ print(" Lower bound: {}".format(1 / kth))
869
+ print(" Perf. ratio: {}".format(visited / (nvis * <double> (sd.neighbors[sd.n] - sd.edges))))
870
+
871
+ if verbose > 0:
872
+ print("Final performance ratio: {}".format(visited / (n * <double> (sd.neighbors[sd.n] - sd.edges))))
873
+
874
+ if not isinstance(G, StaticSparseBackend):
875
+ free_short_digraph(sd)
876
+
877
+ res = [(1.0 / farness[v], V[v]) for v in topk[:k] if v != -1]
878
+ try:
879
+ res = sorted(res, reverse=True)
880
+ except TypeError:
881
+ # A TypeError may occur in Python 3 when vertex labels are of
882
+ # different types. We then sort on values only.
883
+ res = sorted(res, reverse=True, key=lambda vv: vv[0])
884
+ return res
885
+
886
+
887
+ def centrality_closeness_random_k(G, int k=1):
888
+ r"""
889
+ Return an estimation of the closeness centrality of `G`.
890
+
891
+ The algorithm first randomly selects a set `S` of `k` vertices. Then it
892
+ computes shortest path distances from each vertex in `S` (using Dijkstra for
893
+ weighted graph and breadth-first-search (BFS) for unweighted graph) and uses
894
+ this knowledge to estimate the closeness centrality of all vertices.
895
+
896
+ For more information, see [EDI2014]_.
897
+
898
+ INPUT:
899
+
900
+ - ``G`` -- an undirected connected Graph
901
+
902
+ - ``k`` -- integer (default: 1); number of random nodes to choose
903
+
904
+ OUTPUT: a dictionary associating to each vertex its estimated closeness centrality
905
+
906
+ EXAMPLES:
907
+
908
+ Estimation of the closeness centrality of the Petersen Graph when `k == n`::
909
+
910
+ sage: from sage.graphs.centrality import centrality_closeness_random_k
911
+ sage: G = graphs.PetersenGraph()
912
+ sage: centrality_closeness_random_k(G, 10)
913
+ {0: 0.6,
914
+ 1: 0.6,
915
+ 2: 0.6,
916
+ 3: 0.6,
917
+ 4: 0.6,
918
+ 5: 0.6,
919
+ 6: 0.6,
920
+ 7: 0.6,
921
+ 8: 0.6,
922
+ 9: 0.6}
923
+
924
+ TESTS::
925
+
926
+ sage: from sage.graphs.centrality import centrality_closeness_random_k
927
+ sage: G = Graph('Ihe\\n@GUA')
928
+ sage: centrality_closeness_random_k(G, -1)
929
+ Traceback (most recent call last):
930
+ ...
931
+ ValueError: parameter k must be a positive integer
932
+
933
+ sage: centrality_closeness_random_k(DiGraph(2), 1)
934
+ Traceback (most recent call last):
935
+ ...
936
+ ValueError: G must be an undirected Graph
937
+
938
+ The method is valid for immutable graphs::
939
+
940
+ sage: from sage.graphs.centrality import centrality_closeness_random_k
941
+ sage: G = graphs.RandomGNP(10, .7)
942
+ sage: G._backend
943
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
944
+ sage: H = Graph(G, immutable=True)
945
+ sage: H._backend
946
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
947
+ sage: centrality_closeness_random_k(G, 10) == centrality_closeness_random_k(H, 10)
948
+ True
949
+ """
950
+ G._scream_if_not_simple()
951
+ if G.is_directed():
952
+ raise ValueError("G must be an undirected Graph")
953
+
954
+ cdef int n = G.order()
955
+ if not n:
956
+ return {}
957
+ if k < 1:
958
+ raise ValueError("parameter k must be a positive integer")
959
+ if k > n:
960
+ k = n
961
+
962
+ if not G.is_connected():
963
+ raise ValueError("G must be a connected Graph")
964
+
965
+ # Initialization of some data structures
966
+ cdef MemoryAllocator mem = MemoryAllocator()
967
+ cdef double* partial_farness = <double*> mem.malloc(n * sizeof(double))
968
+ cdef uint32_t* distance
969
+ cdef uint32_t* waiting_list
970
+ cdef StaticSparseCGraph cg
971
+ cdef short_digraph sd
972
+ cdef bitset_t seen
973
+ cdef double farness
974
+ cdef int i, j
975
+ cdef dict closeness_centrality_array = {}
976
+ cdef list int_to_vertex
977
+ cdef dict vertex_to_int
978
+ if isinstance(G, StaticSparseBackend):
979
+ cg = <StaticSparseCGraph> G._cg
980
+ int_to_vertex = cg._vertex_to_labels
981
+ vertex_to_int = cg._vertex_to_int
982
+ else:
983
+ int_to_vertex = list(G)
984
+ vertex_to_int = {v: i for i, v in enumerate(int_to_vertex)}
985
+
986
+ # Initialize
987
+ for i in range(n):
988
+ partial_farness[i] = 0
989
+
990
+ # Shuffle the vertices
991
+ cdef list V = list(range(n))
992
+ random.shuffle(V)
993
+
994
+ if G.weighted():
995
+ # For all random nodes take as a source then run Dijstra and
996
+ # calculate closeness centrality for k random vertices from l.
997
+ for i in range(k):
998
+ farness = 0
999
+ distances = boost_shortest_paths(G, int_to_vertex[V[i]], algorithm='Dijkstra')[0]
1000
+ for vertex in distances:
1001
+ farness += float(distances[vertex])
1002
+ partial_farness[vertex_to_int[vertex]] += float(distances[vertex])
1003
+
1004
+ closeness_centrality_array[int_to_vertex[V[i]]] = (n - 1) / farness
1005
+
1006
+ # G is unweighted graph
1007
+ else:
1008
+
1009
+ # Copying the whole graph as a static_sparse_graph for fast shortest
1010
+ # paths computation in unweighted graph. This data structure is well
1011
+ # documented in module sage.graphs.base.static_sparse_graph
1012
+ if isinstance(G, StaticSparseBackend):
1013
+ sd = <short_digraph> cg.g
1014
+ else:
1015
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
1016
+ distance = <uint32_t*> mem.malloc(n * sizeof(uint32_t))
1017
+ waiting_list = <uint32_t*> mem.malloc(n * sizeof(uint32_t))
1018
+ bitset_init(seen, n)
1019
+
1020
+ # Run BFS for random k vertices
1021
+ for i in range(k):
1022
+ farness = 0
1023
+ simple_BFS(sd, V[i], distance, NULL, waiting_list, seen)
1024
+ for j in range(n):
1025
+ farness += distance[j]
1026
+ partial_farness[j] += distance[j]
1027
+
1028
+ closeness_centrality_array[int_to_vertex[V[i]]] = (n - 1) / farness
1029
+
1030
+ bitset_free(seen)
1031
+ if not isinstance(G, StaticSparseBackend):
1032
+ free_short_digraph(sd)
1033
+
1034
+ # Estimate the closeness centrality for remaining n-k vertices.
1035
+ for i in range(k, n):
1036
+ closeness_centrality_array[int_to_vertex[V[i]]] = k / partial_farness[V[i]]
1037
+
1038
+ return closeness_centrality_array