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