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,2938 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # cython: binding=True
3
+ r"""
4
+ Distances/shortest paths between all pairs of vertices
5
+
6
+ This module implements a few functions that deal with the computation of
7
+ distances or shortest paths between all pairs of vertices.
8
+
9
+ **Efficiency** : Because these functions involve listing many times the
10
+ (out)-neighborhoods of (di)-graphs, it is useful in terms of efficiency to build
11
+ a temporary copy of the graph in a data structure that makes it easy to compute
12
+ quickly. These functions also work on large volume of data, typically dense
13
+ matrices of size `n^2`, and are expected to return corresponding dictionaries of
14
+ size `n^2`, where the integers corresponding to the vertices have first been
15
+ converted to the vertices' labels. Sadly, this last translating operation turns
16
+ out to be the most time-consuming, and for this reason it is also nice to have a
17
+ Cython module, and version of these functions that return C arrays, in order to
18
+ avoid these operations when they are not necessary.
19
+
20
+ **Memory cost** : The methods implemented in the current module sometimes need
21
+ large amounts of memory to return their result. Storing the distances between
22
+ all pairs of vertices in a graph on `1500` vertices as a dictionary of
23
+ dictionaries takes around 200MB, while storing the same information as a C array
24
+ requires 4MB.
25
+
26
+
27
+ The module's main function
28
+ --------------------------
29
+
30
+ The C function ``all_pairs_shortest_path_BFS`` actually does all the
31
+ computations, and all the others (except for ``Floyd_Warshall``) are just
32
+ wrapping it. This function begins with copying the graph in a data structure
33
+ that makes it fast to query the out-neighbors of a vertex, then starts one
34
+ Breadth First Search per vertex of the (di)graph.
35
+
36
+ **What can this function compute ?**
37
+
38
+ - The matrix of predecessors.
39
+
40
+ This matrix `P` has size `n^2`, and is such that vertex `P[u,v]` is a
41
+ predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently
42
+ encodes the information of a shortest `uv`-path for any `u,v\in G` : indeed,
43
+ to go from `u` to `v` you should first find a shortest `uP[u,v]`-path, then
44
+ jump from `P[u,v]` to `v` as it is one of its outneighbors. Apply recursively
45
+ and find out what the whole path is !.
46
+
47
+ - The matrix of distances.
48
+
49
+ This matrix has size `n^2` and associates to any `uv` the distance
50
+ from `u` to `v`.
51
+
52
+ - The vector of eccentricities.
53
+
54
+ This vector of size `n` encodes for each vertex `v` the distance to vertex
55
+ which is furthest from `v` in the graph. In particular, the diameter of the
56
+ graph is the maximum of these values.
57
+
58
+ **What does it take as input ?**
59
+
60
+ - ``gg`` a (Di)Graph.
61
+
62
+ - ``unsigned short * predecessors`` -- a pointer toward an array of size
63
+ `n^2\cdot\text{sizeof(unsigned short)}`. Set to ``NULL`` if you do not want to
64
+ compute the predecessors.
65
+
66
+ - ``unsigned short * distances`` -- a pointer toward an array of size
67
+ `n^2\cdot\text{sizeof(unsigned short)}`. The computation of the distances is
68
+ necessary for the algorithm, so this value can **not** be set to ``NULL``.
69
+
70
+ - ``int * eccentricity`` -- a pointer toward an array of size
71
+ `n\cdot\text{sizeof(int)}`. Set to ``NULL`` if you do not want to compute the
72
+ eccentricity.
73
+
74
+ **Technical details**
75
+
76
+ - The vertices are encoded as `1, ..., n` as they appear in the ordering of
77
+ ``G.vertices(sort=True)``, unless another ordering is specified by the user.
78
+
79
+ - Because this function works on matrices whose size is quadratic compared to
80
+ the number of vertices when computing all distances or predecessors, it uses
81
+ short variables to store the vertices' names instead of long ones to divide by
82
+ 2 the size in memory. This means that only the diameter/eccentricities can be
83
+ computed on a graph of more than 65536 nodes. For information, the current
84
+ version of the algorithm on a graph with `65536=2^{16}` nodes creates in
85
+ memory `2` tables on `2^{32}` short elements (2bytes each), for a total of
86
+ `2^{33}` bytes or `8` gigabytes. In order to support larger sizes, we would
87
+ have to replace shorts by 32-bits int or 64-bits int, which would then require
88
+ respectively 16GB or 32GB.
89
+
90
+ - In the C version of these functions, infinite distances are represented with
91
+ ``<unsigned short> -1 = 65535`` for ``unsigned short`` variables, and by
92
+ ``INT32_MAX`` otherwise. These case happens when the input is a disconnected
93
+ graph, or a non-strongly-connected digraph.
94
+
95
+ - A memory error is raised when data structures allocation failed. This could
96
+ happen with large graphs on computers with low memory space.
97
+
98
+ .. WARNING::
99
+
100
+ The function ``all_pairs_shortest_path_BFS`` has **no reason** to be called
101
+ by the user, even though he would be writing his code in Cython and look for
102
+ efficiency. This module contains wrappers for this function that feed it
103
+ with the good parameters. As the function is inlined, using those wrappers
104
+ actually saves time as it should avoid testing the parameters again and
105
+ again in the main function's body.
106
+
107
+ AUTHOR:
108
+
109
+ - Nathann Cohen (2011)
110
+ - David Coudert (2014) -- 2sweep, multi-sweep and iFUB for diameter computation
111
+
112
+ Functions
113
+ ---------
114
+ """
115
+
116
+ # ****************************************************************************
117
+ # Copyright (C) 2011 Nathann Cohen <nathann.cohen@gmail.com>
118
+ #
119
+ # This program is free software: you can redistribute it and/or modify
120
+ # it under the terms of the GNU General Public License as published by
121
+ # the Free Software Foundation, either version 2 of the License, or
122
+ # (at your option) any later version.
123
+ # https://www.gnu.org/licenses/
124
+ # ****************************************************************************
125
+
126
+ from libc.string cimport memset
127
+ from libc.stdint cimport uint64_t, UINT64_MAX
128
+ from libc.stdint cimport uint32_t, INT32_MAX, UINT32_MAX
129
+ from cysignals.memory cimport sig_malloc, sig_calloc, sig_free
130
+ from cysignals.signals cimport sig_on, sig_off
131
+ from memory_allocator cimport MemoryAllocator
132
+
133
+ from sage.data_structures.binary_matrix cimport *
134
+ from sage.graphs.base.c_graph cimport CGraphBackend
135
+ from sage.graphs.base.c_graph cimport CGraph
136
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseCGraph
137
+ from sage.graphs.base.static_sparse_backend cimport StaticSparseBackend
138
+ from sage.graphs.base.static_sparse_graph cimport (short_digraph,
139
+ init_short_digraph,
140
+ init_reverse,
141
+ free_short_digraph,
142
+ out_degree,
143
+ has_edge,
144
+ simple_BFS)
145
+
146
+
147
+ cdef inline c_all_pairs_shortest_path_BFS(short_digraph sd,
148
+ unsigned short* predecessors,
149
+ unsigned short* distances,
150
+ uint32_t* eccentricity):
151
+ r"""
152
+ See the module's documentation.
153
+ """
154
+ cdef int n = sd.n
155
+
156
+ # Computing the predecessors/distances can only be done if we have less than
157
+ # MAX_UNSIGNED_SHORT vertices. No problem with the eccentricities though as
158
+ # we store them on an integer vector.
159
+ if (predecessors or distances) and n > <unsigned short> -1:
160
+ raise ValueError("The graph backend contains more than " +
161
+ str(<unsigned short> -1) + " nodes and we cannot " +
162
+ "compute the matrix of distances/predecessors on " +
163
+ "something like that !")
164
+
165
+ cdef int i
166
+ cdef MemoryAllocator mem = MemoryAllocator()
167
+
168
+ # The vertices which have already been visited
169
+ cdef bitset_t seen
170
+ bitset_init(seen, n)
171
+
172
+ # The list of waiting vertices, the beginning and the end of the list
173
+ cdef int* waiting_list = <int*> mem.allocarray(n, sizeof(int))
174
+ cdef int waiting_beginning = 0
175
+ cdef int waiting_end = 0
176
+
177
+ cdef int source
178
+ cdef int v, u
179
+ cdef uint32_t* p_tmp
180
+ cdef uint32_t* end
181
+
182
+ cdef unsigned short *c_predecessors = predecessors
183
+ cdef int* c_distances = <int*> mem.allocarray(n, sizeof(int))
184
+
185
+ # The edges are stored in the vector p_edges. This vector contains, from
186
+ # left to right The list of the first vertex's outneighbors, then the
187
+ # second's, then the third's, ...
188
+ #
189
+ # The outneighbors of vertex i are enumerated from
190
+ #
191
+ # p_vertices[i] to p_vertices[i+1] - 1
192
+ # (if p_vertices[i] is equal to p_vertices[i+1], then i has no outneighbours)
193
+ #
194
+ # This data structure is well documented in the module
195
+ # sage.graphs.base.static_sparse_graph
196
+ cdef uint32_t** p_vertices = sd.neighbors
197
+
198
+ # We run n different BFS taking each vertex as a source
199
+ for source in range(n):
200
+
201
+ # The source is seen
202
+ bitset_set_first_n(seen, 0)
203
+ bitset_add(seen, source)
204
+
205
+ # Its parameters can already be set
206
+ c_distances[source] = 0
207
+
208
+ if predecessors != NULL:
209
+ c_predecessors[source] = source
210
+
211
+ # and added to the queue
212
+ waiting_list[0] = source
213
+ waiting_beginning = 0
214
+ waiting_end = 0
215
+
216
+ # For as long as there are vertices left to explore
217
+ while waiting_beginning <= waiting_end:
218
+
219
+ # We pick the first one
220
+ v = waiting_list[waiting_beginning]
221
+
222
+ p_tmp = p_vertices[v]
223
+ end = p_vertices[v + 1]
224
+
225
+ # Iterating over all the outneighbors u of v
226
+ while p_tmp < end:
227
+ u = p_tmp[0]
228
+
229
+ # If we notice one of these neighbors is not seen yet, we set
230
+ # its parameters and add it to the queue to be explored later.
231
+ if not bitset_in(seen, u):
232
+ c_distances[u] = c_distances[v] + 1
233
+ if predecessors:
234
+ c_predecessors[u] = v
235
+ bitset_add(seen, u)
236
+ waiting_end += 1
237
+ waiting_list[waiting_end] = u
238
+
239
+ p_tmp += 1
240
+
241
+ waiting_beginning += 1
242
+
243
+ # If not all the vertices have been met
244
+ if bitset_len(seen) < n:
245
+ bitset_complement(seen, seen)
246
+ v = bitset_next(seen, 0)
247
+ while v >= 0:
248
+ c_distances[v] = INT32_MAX
249
+ if predecessors:
250
+ c_predecessors[v] = -1
251
+ v = bitset_next(seen, v + 1)
252
+
253
+ if eccentricity:
254
+ eccentricity[source] = UINT32_MAX
255
+
256
+ elif eccentricity:
257
+ eccentricity[source] = c_distances[waiting_list[n - 1]]
258
+
259
+ if predecessors:
260
+ c_predecessors += n
261
+
262
+ if distances:
263
+ for i in range(n):
264
+ distances[i] = <unsigned short> c_distances[i]
265
+ distances += n
266
+
267
+ bitset_free(seen)
268
+
269
+
270
+ cdef inline all_pairs_shortest_path_BFS(gg,
271
+ unsigned short* predecessors,
272
+ unsigned short* distances,
273
+ uint32_t* eccentricity,
274
+ vertex_list=None):
275
+ r"""
276
+ See the module's documentation.
277
+
278
+ Optional parameter ``vertex_list`` is a list of `n` vertices
279
+ specifying a mapping from `(0, \ldots, n-1)` to vertex labels in
280
+ ``gg``. When ``vertex_list`` is ``None`` (default), the mapping is
281
+ given by the ordering of ``gg.vertices(sort=True)``. When set,
282
+ ``distances[i * n + j]`` is the shortest BFS distance between
283
+ vertices ``vertex_list[i]`` and ``vertex_list[j]``.
284
+ """
285
+ from sage.rings.infinity import Infinity
286
+
287
+ cdef list int_to_vertex
288
+ if vertex_list is None:
289
+ int_to_vertex = gg.vertices(sort=True)
290
+ elif set(gg.vertex_iterator()) == set(vertex_list):
291
+ int_to_vertex = vertex_list
292
+ else:
293
+ raise ValueError("parameter vertex_list is incorrect for this graph")
294
+
295
+ cdef int n = gg.order()
296
+
297
+ # Computing the predecessors/distances can only be done if we have less than
298
+ # MAX_UNSIGNED_SHORT vertices. No problem with the eccentricities though as
299
+ # we store them on an integer vector.
300
+ if (predecessors or distances) and n > <unsigned short> -1:
301
+ raise ValueError("The graph backend contains more than "+
302
+ str(<unsigned short> -1)+" nodes and we cannot "+
303
+ "compute the matrix of distances/predecessors on "+
304
+ "something like that !")
305
+
306
+ # Copying the whole graph to obtain the list of neighbors quicker than by
307
+ # calling out_neighbors
308
+ cdef short_digraph sd
309
+ init_short_digraph(sd, gg, edge_labelled=False, vertex_list=int_to_vertex)
310
+
311
+ c_all_pairs_shortest_path_BFS(sd, predecessors, distances, eccentricity)
312
+
313
+ free_short_digraph(sd)
314
+
315
+
316
+ ################
317
+ # Predecessors #
318
+ ################
319
+
320
+ cdef unsigned short* c_shortest_path_all_pairs(G, vertex_list=None) except NULL:
321
+ r"""
322
+ Return the matrix of predecessors in G.
323
+
324
+ The matrix `P` returned has size `n^2`, and is such that vertex `P[u,v]` is
325
+ a predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently
326
+ encodes the information of a shortest `uv`-path for any `u,v\in G` : indeed,
327
+ to go from `u` to `v` you should first find a shortest `uP[u,v]`-path, then
328
+ jump from `P[u,v]` to `v` as it is one of its outneighbors.
329
+
330
+ Optional parameter ``vertex_list`` is a list of `n` vertices specifying a
331
+ mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When
332
+ ``vertex_list`` is ``None`` (default), the mapping is given by the ordering
333
+ of ``G.vertices(sort=True)``. When set, ``predecessors[i * n + j]`` is the
334
+ predecessor of ``vertex_list[j]`` on the shortest path from
335
+ ``vertex_list[i]`` to ``vertex_list[j]``.
336
+ """
337
+
338
+ cdef unsigned int n = G.order()
339
+ cdef unsigned short* distances = <unsigned short*> sig_malloc(n * n * sizeof(unsigned short))
340
+ if not distances:
341
+ raise MemoryError()
342
+ cdef unsigned short* predecessors = <unsigned short*> sig_malloc(n * n * sizeof(unsigned short))
343
+ if not predecessors:
344
+ sig_free(distances)
345
+ raise MemoryError()
346
+ all_pairs_shortest_path_BFS(G, predecessors, distances, NULL, vertex_list=vertex_list)
347
+
348
+ sig_free(distances)
349
+
350
+ return predecessors
351
+
352
+
353
+ def shortest_path_all_pairs(G):
354
+ r"""
355
+ Return the matrix of predecessors in G.
356
+
357
+ The matrix `P` returned has size `n^2`, and is such that vertex `P[u,v]` is
358
+ a predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently
359
+ encodes the information of a shortest `uv`-path for any `u,v\in G` : indeed,
360
+ to go from `u` to `v` you should first find a shortest `uP[u,v]`-path, then
361
+ jump from `P[u,v]` to `v` as it is one of its outneighbors.
362
+
363
+ EXAMPLES::
364
+
365
+ sage: from sage.graphs.distances_all_pairs import shortest_path_all_pairs
366
+ sage: g = graphs.PetersenGraph()
367
+ sage: shortest_path_all_pairs(g)
368
+ {0: {0: None, 1: 0, 2: 1, 3: 4, 4: 0, 5: 0, 6: 1, 7: 5, 8: 5, 9: 4},
369
+ 1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0, 5: 0, 6: 1, 7: 2, 8: 6, 9: 6},
370
+ 2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3, 5: 7, 6: 1, 7: 2, 8: 3, 9: 7},
371
+ 3: {0: 4, 1: 2, 2: 3, 3: None, 4: 3, 5: 8, 6: 8, 7: 2, 8: 3, 9: 4},
372
+ 4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None, 5: 0, 6: 9, 7: 9, 8: 3, 9: 4},
373
+ 5: {0: 5, 1: 0, 2: 7, 3: 8, 4: 0, 5: None, 6: 8, 7: 5, 8: 5, 9: 7},
374
+ 6: {0: 1, 1: 6, 2: 1, 3: 8, 4: 9, 5: 8, 6: None, 7: 9, 8: 6, 9: 6},
375
+ 7: {0: 5, 1: 2, 2: 7, 3: 2, 4: 9, 5: 7, 6: 9, 7: None, 8: 5, 9: 7},
376
+ 8: {0: 5, 1: 6, 2: 3, 3: 8, 4: 3, 5: 8, 6: 8, 7: 5, 8: None, 9: 6},
377
+ 9: {0: 4, 1: 6, 2: 7, 3: 4, 4: 9, 5: 7, 6: 9, 7: 9, 8: 6, 9: None}}
378
+ """
379
+ cdef int n = G.order()
380
+
381
+ if not n:
382
+ return {}
383
+
384
+ # The order of vertices must be the same as in init_short_digraph
385
+ cdef list int_to_vertex = list(G)
386
+ cdef unsigned short* predecessors = c_shortest_path_all_pairs(G, vertex_list=int_to_vertex)
387
+ cdef unsigned short* c_predecessors = predecessors
388
+
389
+ cdef dict d = {}
390
+ cdef dict d_tmp
391
+
392
+ cdef int i, j
393
+
394
+ for j in range(n):
395
+ d_tmp = {}
396
+ for i in range(n):
397
+ if c_predecessors[i] == <unsigned short> -1:
398
+ d_tmp[int_to_vertex[i]] = None
399
+ else:
400
+ d_tmp[int_to_vertex[i]] = int_to_vertex[c_predecessors[i]]
401
+
402
+ d_tmp[int_to_vertex[j]] = None
403
+ d[int_to_vertex[j]] = d_tmp
404
+
405
+ c_predecessors += n
406
+
407
+ sig_free(predecessors)
408
+ return d
409
+
410
+
411
+ #############
412
+ # Distances #
413
+ #############
414
+
415
+ cdef unsigned short * c_distances_all_pairs(G, vertex_list=None) noexcept:
416
+ r"""
417
+ Return the matrix of distances in G.
418
+
419
+ The matrix `M` returned is of length `n^2`, and the distance between
420
+ vertices `u` and `v` is `M[u,v]`. The integer corresponding to a vertex is
421
+ its index in the list ``G.vertices(sort=True)`` unless parameter
422
+ ``vertex_list`` is set.
423
+
424
+ Optional parameter ``vertex_list`` is a list of `n` vertices specifying a
425
+ mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When set,
426
+ ``distances[i * n + j]`` is the shortest BFS distance between vertices
427
+ ``vertex_list[i]`` and ``vertex_list[j]``.
428
+ """
429
+ cdef unsigned int n = G.order()
430
+ cdef unsigned short* distances = <unsigned short*> sig_malloc(n * n * sizeof(unsigned short))
431
+ if not distances:
432
+ raise MemoryError()
433
+ all_pairs_shortest_path_BFS(G, NULL, distances, NULL, vertex_list=vertex_list)
434
+
435
+ return distances
436
+
437
+
438
+ def distances_all_pairs(G):
439
+ r"""
440
+ Return the matrix of distances in G.
441
+
442
+ This function returns a double dictionary ``D`` of vertices, in which the
443
+ distance between vertices ``u`` and ``v`` is ``D[u][v]``.
444
+
445
+ EXAMPLES::
446
+
447
+ sage: from sage.graphs.distances_all_pairs import distances_all_pairs
448
+ sage: g = graphs.PetersenGraph()
449
+ sage: distances_all_pairs(g)
450
+ {0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2},
451
+ 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2},
452
+ 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2},
453
+ 3: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2},
454
+ 4: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1},
455
+ 5: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2},
456
+ 6: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1},
457
+ 7: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1},
458
+ 8: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2},
459
+ 9: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}}
460
+ """
461
+ from sage.rings.infinity import Infinity
462
+
463
+ cdef int n = G.order()
464
+
465
+ if not n:
466
+ return {}
467
+
468
+ # The order of vertices must be the same as in init_short_digraph
469
+ cdef list int_to_vertex = list(G)
470
+
471
+ cdef unsigned short* distances = c_distances_all_pairs(G, vertex_list=int_to_vertex)
472
+ cdef unsigned short* c_distances = distances
473
+
474
+ cdef dict d = {}
475
+ cdef dict d_tmp
476
+
477
+ cdef int i, j
478
+
479
+ for j in range(n):
480
+ d_tmp = {}
481
+ for i in range(n):
482
+ if c_distances[i] == <unsigned short> -1:
483
+ d_tmp[int_to_vertex[i]] = Infinity
484
+ else:
485
+ d_tmp[int_to_vertex[i]] = c_distances[i]
486
+
487
+ d[int_to_vertex[j]] = d_tmp
488
+ c_distances += n
489
+
490
+ sig_free(distances)
491
+ return d
492
+
493
+
494
+ def is_distance_regular(G, parameters=False):
495
+ r"""
496
+ Test if the graph is distance-regular.
497
+
498
+ A graph `G` is distance-regular if for any integers `j,k` the value of
499
+ `|\{x:d_G(x,u)=j,x\in V(G)\} \cap \{y:d_G(y,v)=j,y\in V(G)\}|` is constant
500
+ for any two vertices `u,v\in V(G)` at distance `i` from each other. In
501
+ particular `G` is regular, of degree `b_0` (see below), as one can take
502
+ `u=v`.
503
+
504
+ Equivalently a graph is distance-regular if there exist integers `b_i,c_i`
505
+ such that for any two vertices `u,v` at distance `i` we have
506
+
507
+ * `b_i = |\{x:d_G(x,u)=i+1,x\in V(G)\}\cap N_G(v)\}|, \ 0\leq i\leq d-1`
508
+ * `c_i = |\{x:d_G(x,u)=i-1,x\in V(G)\}\cap N_G(v)\}|, \ 1\leq i\leq d,`
509
+
510
+ where `d` is the diameter of the graph. For more information on
511
+ distance-regular graphs, see the :wikipedia:`Distance-regular_graph`.
512
+
513
+ INPUT:
514
+
515
+ - ``parameters`` -- boolean (default: ``False``); if set to ``True``, the
516
+ function returns the pair ``(b, c)`` of lists of integers instead of
517
+ a boolean answer (see the definition above)
518
+
519
+ .. SEEALSO::
520
+
521
+ * :meth:`~sage.graphs.generic_graph.GenericGraph.is_regular`
522
+ * :meth:`~Graph.is_strongly_regular`
523
+
524
+ EXAMPLES::
525
+
526
+ sage: g = graphs.PetersenGraph()
527
+ sage: g.is_distance_regular()
528
+ True
529
+ sage: g.is_distance_regular(parameters = True)
530
+ ([3, 2, None], [None, 1, 1])
531
+
532
+ Cube graphs, which are not strongly regular, are a bit more interesting::
533
+
534
+ sage: graphs.CubeGraph(4).is_distance_regular()
535
+ True
536
+ sage: graphs.OddGraph(5).is_distance_regular()
537
+ True
538
+
539
+ Disconnected graph::
540
+
541
+ sage: (2*graphs.CubeGraph(4)).is_distance_regular()
542
+ True
543
+
544
+ TESTS::
545
+
546
+ sage: graphs.PathGraph(2).is_distance_regular(parameters=True)
547
+ ([1, None], [None, 1])
548
+ sage: graphs.Tutte12Cage().is_distance_regular(parameters=True) # needs networkx
549
+ ([3, 2, 2, 2, 2, 2, None], [None, 1, 1, 1, 1, 1, 3])
550
+ """
551
+ cdef int i, u, v, d, b, c, k
552
+ cdef int n = G.order()
553
+ cdef int infinity = <unsigned short> -1
554
+
555
+ if n <= 1:
556
+ return ([], []) if parameters else True
557
+
558
+ if not G.is_regular():
559
+ return False
560
+ k = G.degree(next(G.vertex_iterator()))
561
+
562
+ # Matrix of distances
563
+ cdef unsigned short* distance_matrix = c_distances_all_pairs(G, vertex_list=list(G))
564
+
565
+ # The diameter, i.e. the longest *finite* distance between two vertices
566
+ cdef int diameter = 0
567
+ for i in range(n * n):
568
+ if distance_matrix[i] > diameter and distance_matrix[i] != infinity:
569
+ diameter = distance_matrix[i]
570
+
571
+ cdef bitset_t b_tmp
572
+ bitset_init(b_tmp, n)
573
+
574
+ # b_distance_matrix[d*n+v] is the set of vertices at distance d from v.
575
+ cdef binary_matrix_t b_distance_matrix
576
+ try:
577
+ binary_matrix_init(b_distance_matrix, n * (diameter + 2), n)
578
+ except MemoryError:
579
+ sig_free(distance_matrix)
580
+ bitset_free(b_tmp)
581
+ raise
582
+
583
+ # Fills b_distance_matrix
584
+ for u in range(n):
585
+ for v in range(u, n):
586
+ d = distance_matrix[u * n + v]
587
+ if d != infinity:
588
+ binary_matrix_set1(b_distance_matrix, d * n + u, v)
589
+ binary_matrix_set1(b_distance_matrix, d * n + v, u)
590
+
591
+ cdef list bi = [-1 for i in range(diameter + 1)]
592
+ cdef list ci = [-1 for i in range(diameter + 1)]
593
+
594
+ # Applying the definition with b_i,c_i
595
+ for u in range(n):
596
+ for v in range(n):
597
+ if u == v:
598
+ continue
599
+
600
+ d = distance_matrix[u * n + v]
601
+ if d == infinity:
602
+ continue
603
+
604
+ # Computations of b_d and c_d for u,v. We intersect sets stored in
605
+ # b_distance_matrix.
606
+ bitset_and(b_tmp, b_distance_matrix.rows[(d + 1) * n + u], b_distance_matrix.rows[n + v])
607
+ b = bitset_len(b_tmp)
608
+ bitset_and(b_tmp, b_distance_matrix.rows[(d - 1) * n + u], b_distance_matrix.rows[n + v])
609
+ c = bitset_len(b_tmp)
610
+
611
+ # Consistency of b_d and c_d
612
+ if bi[d] == -1:
613
+ bi[d] = b
614
+ ci[d] = c
615
+
616
+ elif bi[d] != b or ci[d] != c:
617
+ sig_free(distance_matrix)
618
+ binary_matrix_free(b_distance_matrix)
619
+ bitset_free(b_tmp)
620
+ return False
621
+
622
+ sig_free(distance_matrix)
623
+ binary_matrix_free(b_distance_matrix)
624
+ bitset_free(b_tmp)
625
+
626
+ if parameters:
627
+ bi[0] = k
628
+ bi[diameter] = None
629
+ ci[0] = None
630
+ return bi, ci
631
+ return True
632
+
633
+
634
+ ###################################
635
+ # Both distances and predecessors #
636
+ ###################################
637
+
638
+ def distances_and_predecessors_all_pairs(G):
639
+ r"""
640
+ Return the matrix of distances in G and the matrix of predecessors.
641
+
642
+ Distances : the matrix `M` returned is of length `n^2`, and the distance
643
+ between vertices `u` and `v` is `M[u,v]`. The integer corresponding to a
644
+ vertex is its index in the list ``G.vertices(sort=True)``.
645
+
646
+ Predecessors : the matrix `P` returned has size `n^2`, and is such that
647
+ vertex `P[u,v]` is a predecessor of `v` on a shortest `uv`-path. Hence, this
648
+ matrix efficiently encodes the information of a shortest `uv`-path for any
649
+ `u,v\in G` : indeed, to go from `u` to `v` you should first find a shortest
650
+ `uP[u,v]`-path, then jump from `P[u,v]` to `v` as it is one of its
651
+ outneighbors.
652
+
653
+ EXAMPLES::
654
+
655
+ sage: from sage.graphs.distances_all_pairs import distances_and_predecessors_all_pairs
656
+ sage: g = graphs.PetersenGraph()
657
+ sage: distances_and_predecessors_all_pairs(g)
658
+ ({0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2},
659
+ 1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2},
660
+ 2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2},
661
+ 3: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2},
662
+ 4: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1},
663
+ 5: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2},
664
+ 6: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1},
665
+ 7: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1},
666
+ 8: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2},
667
+ 9: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}},
668
+ {0: {0: None, 1: 0, 2: 1, 3: 4, 4: 0, 5: 0, 6: 1, 7: 5, 8: 5, 9: 4},
669
+ 1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0, 5: 0, 6: 1, 7: 2, 8: 6, 9: 6},
670
+ 2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3, 5: 7, 6: 1, 7: 2, 8: 3, 9: 7},
671
+ 3: {0: 4, 1: 2, 2: 3, 3: None, 4: 3, 5: 8, 6: 8, 7: 2, 8: 3, 9: 4},
672
+ 4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None, 5: 0, 6: 9, 7: 9, 8: 3, 9: 4},
673
+ 5: {0: 5, 1: 0, 2: 7, 3: 8, 4: 0, 5: None, 6: 8, 7: 5, 8: 5, 9: 7},
674
+ 6: {0: 1, 1: 6, 2: 1, 3: 8, 4: 9, 5: 8, 6: None, 7: 9, 8: 6, 9: 6},
675
+ 7: {0: 5, 1: 2, 2: 7, 3: 2, 4: 9, 5: 7, 6: 9, 7: None, 8: 5, 9: 7},
676
+ 8: {0: 5, 1: 6, 2: 3, 3: 8, 4: 3, 5: 8, 6: 8, 7: 5, 8: None, 9: 6},
677
+ 9: {0: 4, 1: 6, 2: 7, 3: 4, 4: 9, 5: 7, 6: 9, 7: 9, 8: 6, 9: None}})
678
+ """
679
+ from sage.rings.infinity import Infinity
680
+ cdef unsigned int n = G.order()
681
+
682
+ if not n:
683
+ return {}, {}
684
+
685
+ cdef MemoryAllocator mem = MemoryAllocator()
686
+ cdef unsigned short* c_distances = <unsigned short*> mem.malloc(n * n * sizeof(unsigned short))
687
+ cdef unsigned short* c_predecessor = <unsigned short*> mem.malloc(n * n * sizeof(unsigned short))
688
+
689
+ # The order of vertices must be the same as in init_short_digraph
690
+ cdef list int_to_vertex = list(G)
691
+
692
+ all_pairs_shortest_path_BFS(G, c_predecessor, c_distances, NULL, vertex_list=int_to_vertex)
693
+
694
+ cdef dict d_distance = {}
695
+ cdef dict d_predecessor = {}
696
+ cdef dict t_distance = {}
697
+ cdef dict t_predecessor = {}
698
+
699
+ cdef unsigned int i, j
700
+
701
+ for j in range(n):
702
+ t_distance = {}
703
+ t_predecessor = {}
704
+
705
+ for i in range(n):
706
+
707
+ if c_distances[i] != <unsigned short> -1:
708
+ t_distance[int_to_vertex[i]] = c_distances[i]
709
+ t_predecessor[int_to_vertex[i]] = int_to_vertex[c_predecessor[i]]
710
+
711
+ t_predecessor[int_to_vertex[j]] = None
712
+
713
+ d_distance[int_to_vertex[j]] = t_distance
714
+ d_predecessor[int_to_vertex[j]] = t_predecessor
715
+
716
+ c_distances += n
717
+ c_predecessor += n
718
+
719
+ return d_distance, d_predecessor
720
+
721
+
722
+ ################
723
+ # Eccentricity #
724
+ ################
725
+
726
+ cdef uint32_t * c_eccentricity(G, vertex_list=None) except NULL:
727
+ r"""
728
+ Return the vector of eccentricities in G.
729
+
730
+ The array returned is of length `n`, and by default its `i`-th component is
731
+ the eccentricity of the `i`-th vertex in ``G.vertices(sort=True)``.
732
+
733
+ Optional parameter ``vertex_list`` is a list of `n` vertices specifying a
734
+ mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When set,
735
+ ``ecc[i]`` is the eccentricity of vertex ``vertex_list[i]``.
736
+ """
737
+ cdef unsigned int n = G.order()
738
+
739
+ cdef uint32_t * ecc = <uint32_t *> sig_calloc(n, sizeof(uint32_t))
740
+ if not ecc:
741
+ raise MemoryError()
742
+ all_pairs_shortest_path_BFS(G, NULL, NULL, ecc, vertex_list=vertex_list)
743
+
744
+ return ecc
745
+
746
+
747
+ cdef uint32_t * c_eccentricity_bounding(short_digraph sd) except NULL:
748
+ r"""
749
+ Return the vector of eccentricities using the algorithm of [TK2013]_.
750
+
751
+ The array returned is of length `n`, and its `i`-th component is the
752
+ eccentricity of vertex `i` in ``sd``.
753
+
754
+ This method assumes that ``sd`` is an undirected graph.
755
+
756
+ The algorithm proposed in [TK2013]_ is based on the observation that for all
757
+ nodes `v,w\in V`, we have `\max(ecc[v]-d(v,w), d(v,w))\leq ecc[w] \leq
758
+ ecc[v] + d(v,w)`. Also the algorithms iteratively improves upper and lower
759
+ bounds on the eccentricity of each node until no further improvements can be
760
+ done. This algorithm offers good running time reduction on scale-free graphs.
761
+ """
762
+ cdef unsigned int n = sd.n
763
+
764
+ # allocated some data structures
765
+ cdef MemoryAllocator mem = MemoryAllocator()
766
+ cdef uint32_t * distances = <uint32_t *> mem.malloc(3 * n * sizeof(uint32_t))
767
+ cdef uint32_t * LB = <uint32_t *> sig_calloc(n, sizeof(uint32_t))
768
+ if not distances or not LB:
769
+ sig_free(LB)
770
+ free_short_digraph(sd)
771
+ raise MemoryError()
772
+ cdef uint32_t * waiting_list = distances + n
773
+ cdef uint32_t * UB = distances + 2 * n
774
+ memset(UB, -1, n * sizeof(uint32_t))
775
+ cdef bitset_t seen
776
+ bitset_init(seen, n)
777
+
778
+ cdef uint32_t v, w, next_v, cpt = 0
779
+
780
+ # The first vertex is the one with largest degree
781
+ next_v = max((out_degree(sd, v), v) for v in range(n))[1]
782
+
783
+ sig_on()
784
+ while next_v != UINT32_MAX:
785
+
786
+ v = next_v
787
+ cpt += 1
788
+
789
+ # Compute the exact eccentricity of v
790
+ LB[v] = simple_BFS(sd, v, distances, NULL, waiting_list, seen)
791
+
792
+ if LB[v] == UINT32_MAX:
793
+ # The graph is not connected. We set maximum value and exit.
794
+ for w in range(n):
795
+ LB[w] = UINT32_MAX
796
+ break
797
+
798
+ # Improve the bounds on the eccentricity of other vertices and select
799
+ # source of the next BFS
800
+ next_v = UINT32_MAX
801
+ for w in range(n):
802
+ LB[w] = max(LB[w], max(LB[v] - distances[w], distances[w]))
803
+ UB[w] = min(UB[w], LB[v] + distances[w])
804
+ if LB[w] == UB[w]:
805
+ continue
806
+ elif next_v == UINT32_MAX or (not cpt % 2 and LB[w] < LB[next_v]) or (cpt % 2 and UB[w] > UB[next_v]):
807
+ # The next vertex is either the vertex with largest upper bound
808
+ # or smallest lower bound
809
+ next_v = w
810
+
811
+ sig_off()
812
+
813
+ bitset_free(seen)
814
+
815
+ return LB
816
+
817
+
818
+ cdef uint32_t * c_eccentricity_DHV(short_digraph sd) except NULL:
819
+ r"""
820
+ Return the vector of eccentricities using the algorithm of [Dragan2018]_.
821
+
822
+ The array returned is of length `n`, and its `i`-th component is the
823
+ eccentricity of vertex `i` in ``sd``.
824
+
825
+ This method assumes that ``sd`` is an undirected graph.
826
+
827
+ The algorithm proposed in [Dragan2018]_ is an improvement of the algorithm
828
+ proposed in [TK2013]_. It is also based on the observation that for all
829
+ nodes `v,w\in V`, we have `\max(ecc[v]-d(v,w), d(v,w))\leq ecc[w] \leq
830
+ ecc[v] + d(v,w)`. Also the algorithms iteratively improves upper and lower
831
+ bounds on the eccentricity of each vertex until no further improvements can
832
+ be done. The difference with [TK2013]_ is in the order in which improvements
833
+ are done.
834
+
835
+ EXAMPLES::
836
+
837
+ sage: from sage.graphs.distances_all_pairs import eccentricity
838
+ sage: G = graphs.PathGraph(5)
839
+ sage: eccentricity(G, algorithm='DHV')
840
+ [4, 3, 2, 3, 4]
841
+
842
+ TESTS:
843
+
844
+ sage: G = graphs.RandomBarabasiAlbert(50, 2) # needs networkx
845
+ sage: eccentricity(G, algorithm='bounds') == eccentricity(G, algorithm='DHV') # needs networkx
846
+ True
847
+ """
848
+ cdef uint32_t n = sd.n
849
+ if not n:
850
+ return NULL
851
+
852
+ cdef MemoryAllocator mem = MemoryAllocator()
853
+ cdef uint32_t * distances = <uint32_t *> mem.malloc(3 * n * sizeof(uint32_t))
854
+ # For storing upper bounds on eccentricity of nodes
855
+ cdef uint32_t * ecc_upper_bound = <uint32_t *>sig_calloc(n, sizeof(uint32_t))
856
+ if not distances or not ecc_upper_bound:
857
+ sig_free(ecc_upper_bound)
858
+ free_short_digraph(sd)
859
+ raise MemoryError()
860
+
861
+ cdef uint32_t * waiting_list = distances + n
862
+ # For storing lower bounds on eccentricity of nodes
863
+ cdef uint32_t * ecc_lower_bound = distances + 2 * n
864
+ memset(ecc_upper_bound, <char>-1, n * sizeof(uint32_t))
865
+ memset(ecc_lower_bound, 0, n * sizeof(uint32_t))
866
+
867
+ cdef uint32_t u, ecc_u
868
+ cdef uint32_t antipode, ecc_antipode
869
+ cdef uint32_t v, tmp
870
+ cdef size_t i, idx
871
+ cdef bitset_t seen
872
+ bitset_init(seen, n)
873
+
874
+ cdef list active = list(range(n))
875
+
876
+ # Algorithm
877
+ while active:
878
+ # Select vertex with minimum eccentricity in active and update
879
+ # eccentricity upper bounds.
880
+ # For this, we select u with minimum eccentricity lower bound in active
881
+ # if ecc_u == ecc_lb[u], we are done. Otherwise, we update eccentricity
882
+ # lower bounds and repeat
883
+
884
+ tmp = UINT32_MAX
885
+ for i, v in enumerate(active):
886
+ if ecc_lower_bound[v] < tmp:
887
+ tmp = ecc_lower_bound[v]
888
+ idx = i
889
+ active[idx], active[-1] = active[-1], active[idx]
890
+ u = active.pop()
891
+ ecc_u = simple_BFS(sd, u, distances, NULL, waiting_list, seen)
892
+ ecc_upper_bound[u] = ecc_u
893
+
894
+ if ecc_u == UINT32_MAX: # Disconnected graph
895
+ break
896
+
897
+ if ecc_u == ecc_lower_bound[u]:
898
+ # We found the good vertex.
899
+ # Update eccentricity upper bounds and remove from active those
900
+ # vertices for which gap is closed
901
+ i = 0
902
+ while i < len(active):
903
+ v = active[i]
904
+ ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u)
905
+ if ecc_upper_bound[v] == ecc_lower_bound[v]:
906
+ active[i] = active[-1]
907
+ active.pop()
908
+ else:
909
+ i += 1
910
+
911
+ else:
912
+ # u was not a good choice.
913
+ # We use its antipode to update eccentricity lower bounds.
914
+ # Observe that this antipode might have already been seen.
915
+ antipode = waiting_list[n-1]
916
+ for i, v in enumerate(active):
917
+ if v == antipode:
918
+ active[i] = active[-1]
919
+ active.pop()
920
+ break
921
+
922
+ ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen)
923
+ ecc_upper_bound[antipode] = ecc_antipode
924
+
925
+ # Update eccentricity lower bounds and remove from active those
926
+ # vertices for which the gap is closed
927
+ i = 0
928
+ while i < len(active):
929
+ v = active[i]
930
+ ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v])
931
+ if ecc_upper_bound[v] == ecc_lower_bound[v]:
932
+ active[i] = active[-1]
933
+ active.pop()
934
+ else:
935
+ i += 1
936
+
937
+ bitset_free(seen)
938
+
939
+ return ecc_upper_bound
940
+
941
+
942
+ def eccentricity(G, algorithm='standard', vertex_list=None):
943
+ r"""
944
+ Return the vector of eccentricities in G.
945
+
946
+ The array returned is of length `n`, and its `i`-th component is the
947
+ eccentricity of the `i`-th vertex in ``G.vertices(sort=True)``.
948
+
949
+ INPUT:
950
+
951
+ - ``G`` -- a Graph or a DiGraph
952
+
953
+ - ``algorithm`` -- string (default: ``'standard'``); name of the method used
954
+ to compute the eccentricity of the vertices
955
+
956
+ - ``'standard'`` -- computes eccentricity by performing a BFS from each
957
+ vertex
958
+
959
+ - ``'bounds'`` -- computes eccentricity using the fast algorithm proposed
960
+ in [TK2013]_ for undirected graphs
961
+
962
+ - ``'DHV'`` -- computes all eccentricities of undirected graph using the
963
+ algorithm proposed in [Dragan2018]_
964
+
965
+ - ``vertex_list`` -- list (default: ``None``); a list of `n` vertices
966
+ specifying a mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When
967
+ set, ``ecc[i]`` is the eccentricity of vertex ``vertex_list[i]``. When
968
+ ``vertex_list`` is ``None``, ``ecc[i]`` is the eccentricity of vertex
969
+ ``G.vertices(sort=True)[i]``.
970
+
971
+ EXAMPLES::
972
+
973
+ sage: from sage.graphs.distances_all_pairs import eccentricity
974
+ sage: g = graphs.PetersenGraph()
975
+ sage: eccentricity(g)
976
+ [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
977
+ sage: g.add_edge(0, g.add_vertex())
978
+ sage: V = list(g)
979
+ sage: eccentricity(g, vertex_list=V)
980
+ [2, 2, 3, 3, 2, 2, 3, 3, 3, 3, 3]
981
+ sage: eccentricity(g, vertex_list=V[::-1])
982
+ [3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 2]
983
+
984
+ TESTS:
985
+
986
+ All algorithms are valid::
987
+
988
+ sage: from sage.graphs.distances_all_pairs import eccentricity
989
+ sage: g = graphs.RandomGNP(50, .1)
990
+ sage: ecc = eccentricity(g, algorithm='standard')
991
+ sage: ecc == eccentricity(g, algorithm='bounds')
992
+ True
993
+ sage: ecc == eccentricity(g, algorithm='DHV')
994
+ True
995
+
996
+ Case of not (strongly) connected (directed) graph::
997
+
998
+ sage: from sage.graphs.distances_all_pairs import eccentricity
999
+ sage: g = 2*graphs.PathGraph(2)
1000
+ sage: eccentricity(g, algorithm='bounds')
1001
+ [+Infinity, +Infinity, +Infinity, +Infinity]
1002
+ sage: g = digraphs.Path(3)
1003
+ sage: eccentricity(g, algorithm='standard')
1004
+ [2, +Infinity, +Infinity]
1005
+
1006
+ The bounds algorithm is for Graph only::
1007
+
1008
+ sage: from sage.graphs.distances_all_pairs import eccentricity
1009
+ sage: g = digraphs.Circuit(2)
1010
+ sage: eccentricity(g, algorithm='bounds')
1011
+ Traceback (most recent call last):
1012
+ ...
1013
+ ValueError: the 'bounds' algorithm only works on undirected graphs
1014
+
1015
+ Asking for unknown algorithm::
1016
+
1017
+ sage: from sage.graphs.distances_all_pairs import eccentricity
1018
+ sage: g = graphs.PathGraph(2)
1019
+ sage: eccentricity(g, algorithm='Nice Jazz Festival')
1020
+ Traceback (most recent call last):
1021
+ ...
1022
+ ValueError: unknown algorithm 'Nice Jazz Festival', please contribute
1023
+
1024
+ Invalid value for parameter vertex_list::
1025
+
1026
+ sage: from sage.graphs.distances_all_pairs import eccentricity
1027
+ sage: g = graphs.PathGraph(2)
1028
+ sage: eccentricity(g, vertex_list=[0, 1, 2])
1029
+ Traceback (most recent call last):
1030
+ ...
1031
+ ValueError: parameter vertex_list is incorrect for this graph
1032
+ """
1033
+ from sage.rings.infinity import Infinity
1034
+ cdef int n = G.order()
1035
+ cdef short_digraph sd
1036
+
1037
+ # Trivial cases
1038
+ if algorithm not in ['standard', 'bounds', 'DHV']:
1039
+ raise ValueError("unknown algorithm '{}', please contribute".format(algorithm))
1040
+ if not n:
1041
+ return []
1042
+ elif G.is_directed() and algorithm in ['bounds', 'DHV']:
1043
+ raise ValueError("the 'bounds' algorithm only works on undirected graphs")
1044
+ elif not G.is_connected():
1045
+ return [Infinity] * n
1046
+
1047
+ cdef list int_to_vertex
1048
+ if vertex_list is None:
1049
+ int_to_vertex = G.vertices(sort=True)
1050
+ elif len(vertex_list) == n and set(vertex_list) == set(G):
1051
+ int_to_vertex = vertex_list
1052
+ else:
1053
+ raise ValueError("parameter vertex_list is incorrect for this graph")
1054
+
1055
+ cdef uint32_t* ecc
1056
+
1057
+ if algorithm == "standard":
1058
+ ecc = c_eccentricity(G, vertex_list=int_to_vertex)
1059
+
1060
+ else:
1061
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list)
1062
+
1063
+ if algorithm == "DHV":
1064
+ ecc = c_eccentricity_DHV(sd)
1065
+ else: # "bounds"
1066
+ ecc = c_eccentricity_bounding(sd)
1067
+
1068
+ free_short_digraph(sd)
1069
+
1070
+ from sage.rings.integer import Integer
1071
+ cdef list l_ecc = [Integer(ecc[i]) if ecc[i] != UINT32_MAX else +Infinity for i in range(n)]
1072
+
1073
+ sig_free(ecc)
1074
+
1075
+ return l_ecc
1076
+
1077
+
1078
+ ############
1079
+ # Diameter #
1080
+ ############
1081
+
1082
+ cdef uint32_t diameter_lower_bound_2sweep(short_digraph g,
1083
+ uint32_t source,
1084
+ uint32_t* distances,
1085
+ uint32_t* predecessors,
1086
+ uint32_t* waiting_list,
1087
+ bitset_t seen) noexcept:
1088
+ """
1089
+ Compute a lower bound on the diameter using the 2-sweep algorithm.
1090
+
1091
+ This method computes a lower bound on the diameter of an unweighted
1092
+ undirected graph using 2 BFS, as proposed in [MLH2008]_. It first selects a
1093
+ vertex `v` that is at largest distance from an initial vertex `source` using
1094
+ BFS. Then it performs a second BFS from `v`. The largest distance from `v`
1095
+ is returned as a lower bound on the diameter of `G`. The time complexity of
1096
+ this method is linear in the size of the graph.
1097
+
1098
+
1099
+ INPUT:
1100
+
1101
+ - ``g`` -- a short_digraph
1102
+
1103
+ - ``source`` -- starting node of the BFS
1104
+
1105
+ - ``distances`` -- array of size ``n`` to store BFS distances from `v`, the
1106
+ vertex at largest distance from ``source`` from which we start the second
1107
+ BFS. This method assumes that this array has already been allocated.
1108
+ However, there is no need to initialize it.
1109
+
1110
+ - ``predecessors`` -- array of size ``n`` to store the first predecessor of
1111
+ each vertex during the BFS search from `v`. The predecessor of `v` is
1112
+ itself. This method assumes that this array has already been allocated.
1113
+ However, it is possible to pass a ``NULL`` pointer in which case the
1114
+ predecessors are not recorded.
1115
+
1116
+ - ``waiting_list`` -- array of size ``n`` to store the order in which the
1117
+ vertices are visited during the BFS search from `v`. This method assumes
1118
+ that this array has already been allocated. However, there is no need to
1119
+ initialize it.
1120
+
1121
+ - ``seen`` -- bitset of size ``n`` that must be initialized before calling
1122
+ this method (i.e., bitset_init(seen, n)). However, there is no need to
1123
+ clear it.
1124
+ """
1125
+ cdef uint32_t LB
1126
+
1127
+ # We do a first BFS from source and get the eccentricity of source
1128
+ LB = simple_BFS(g, source, distances, NULL, waiting_list, seen)
1129
+
1130
+ # If the eccentricity of the source is infinite (very large number), the
1131
+ # graph is not connected and so its diameter is infinite.
1132
+ if LB == UINT32_MAX:
1133
+ return UINT32_MAX
1134
+
1135
+ # Then we perform a second BFS from the last visited vertex
1136
+ source = waiting_list[g.n - 1]
1137
+ LB = simple_BFS(g, source, distances, predecessors, waiting_list, seen)
1138
+
1139
+ # We return the computed lower bound
1140
+ return LB
1141
+
1142
+
1143
+ cdef tuple diameter_lower_bound_2Dsweep(short_digraph g,
1144
+ short_digraph rev_g,
1145
+ uint32_t source):
1146
+ r"""
1147
+ Lower bound on the diameter of digraph using directed version of 2-sweep.
1148
+
1149
+ This method computes a lower bound on the diameter of an unweighted directed
1150
+ graph using directed version of the 2-sweep algorithm [Broder2000]_.
1151
+ In first part, it performs a forward BFS from `source` and selects a vertex
1152
+ `vf` at a maximum distance from `source` and then it calculates backward
1153
+ eccentricity of `vf` using a backward BFS from `vf`. In second part, it
1154
+ performs backward BFS from `source` and selects a vertex `vb` from which
1155
+ `source` is at maximum distance and then it calculates forward eccentricity
1156
+ of `vb` using a forward BFS from `vb`. It then calculates lower bound LB of
1157
+ diameter as the maximum of backward eccentricity of `vf` and forward
1158
+ eccentricity of `vb` and `s` as respective vertex.
1159
+ This method returns (`LB`, `s`, `m`, `d`), where `LB` is best found lower
1160
+ bound on diameter, `s` is vertex whose forward/backward eccentricity is
1161
+ `LB`, `d` is vertex at a distance `LB` from/to `s`, `m` is vertex at
1162
+ distance `LB/2` from/to both `s` and `d`.
1163
+
1164
+ INPUT:
1165
+
1166
+ - ``g`` -- a short_digraph
1167
+
1168
+ - ``rev_g`` -- a copy of `g` with edges reversed
1169
+
1170
+ - ``source`` -- starting node of the forward and backward BFS
1171
+
1172
+ TESTS:
1173
+
1174
+ Diameter of weakly connected digraph is infinity ::
1175
+
1176
+ sage: from sage.graphs.distances_all_pairs import diameter
1177
+ sage: G = DiGraph([(0,1)])
1178
+ sage: diameter(G, algorithm='2Dsweep')
1179
+ +Infinity
1180
+ """
1181
+ cdef uint32_t LB_1, LB_2, LB, LB_m, m, s, d
1182
+ cdef uint32_t n = g.n
1183
+ cdef uint32_t source_1 = source
1184
+ cdef uint32_t source_2 = source
1185
+ cdef bitset_t seen_1, seen_2
1186
+
1187
+ # Memory allocation
1188
+ cdef MemoryAllocator mem = MemoryAllocator()
1189
+ bitset_init(seen_1, n)
1190
+ bitset_init(seen_2, n)
1191
+ cdef uint32_t * distances_1 = <uint32_t *>mem.malloc(3 * n * sizeof(uint32_t))
1192
+ cdef uint32_t * distances_2 = <uint32_t *>mem.malloc(3 * n * sizeof(uint32_t))
1193
+ if not distances_1 or not distances_2:
1194
+ bitset_free(seen_1)
1195
+ bitset_free(seen_2)
1196
+ raise MemoryError()
1197
+
1198
+ cdef uint32_t * predecessors_1 = distances_1 + n
1199
+ cdef uint32_t * predecessors_2 = distances_2 + n
1200
+ cdef uint32_t * waiting_list_1 = distances_1 + 2 * n
1201
+ cdef uint32_t * waiting_list_2 = distances_2 + 2 * n
1202
+
1203
+ # we perform forward BFS from source and get its forward eccentricity
1204
+ LB_1 = simple_BFS(g, source_1, distances_1, NULL, waiting_list_1, seen_1)
1205
+
1206
+ # if forward eccentricity of source is infinite, then graph is
1207
+ # not strongly connected and its diameter is infinite
1208
+ if LB_1 == UINT32_MAX:
1209
+ bitset_free(seen_1)
1210
+ bitset_free(seen_2)
1211
+ return (UINT32_MAX, 0, 0, 0)
1212
+
1213
+ # we perform backward BFS from source and get its backward eccentricity
1214
+ LB_2 = simple_BFS(rev_g, source_2, distances_2, NULL, waiting_list_2, seen_2)
1215
+
1216
+ # if backward eccentricity of source is infinite, then graph is
1217
+ # not strongly connected and its diameter is infinite
1218
+ if LB_2 == UINT32_MAX:
1219
+ bitset_free(seen_1)
1220
+ bitset_free(seen_2)
1221
+ return (UINT32_MAX, 0, 0, 0)
1222
+
1223
+ # Then we perform backward BFS from the last visited vertex of forward BFS
1224
+ # from source and obtain its backward eccentricity.
1225
+ source_1 = waiting_list_1[n - 1]
1226
+ LB_1 = simple_BFS(rev_g, source_1, distances_1, predecessors_1, waiting_list_1, seen_1)
1227
+
1228
+ # Then we perform forward BFS from the last visited vertex of backward BFS
1229
+ # from source and obtain its forward eccentricity.
1230
+ source_2 = waiting_list_2[n - 1]
1231
+ LB_2 = simple_BFS(g, source_2, distances_2, predecessors_2, waiting_list_2, seen_2)
1232
+
1233
+ # we select best found lower bound as LB, s and d as source and destination
1234
+ # of that BFS call and m as vertex at a distance LB/2 from/to both s and d
1235
+ if LB_1 < LB_2:
1236
+ LB = LB_2
1237
+ s = waiting_list_2[0]
1238
+ d = waiting_list_2[n - 1]
1239
+ LB_m = LB_2 / 2
1240
+ m = d
1241
+ while distances_2[m] > LB_m:
1242
+ m = predecessors_2[m]
1243
+ else:
1244
+ LB = LB_1
1245
+ s = waiting_list_1[0]
1246
+ d = waiting_list_1[n - 1]
1247
+ LB_m = LB_1 / 2
1248
+ m = d
1249
+ while distances_1[m] > LB_m:
1250
+ m = predecessors_1[m]
1251
+
1252
+ bitset_free(seen_1)
1253
+ bitset_free(seen_2)
1254
+
1255
+ return (LB, s, m, d)
1256
+
1257
+
1258
+ cdef tuple diameter_lower_bound_multi_sweep(short_digraph g,
1259
+ uint32_t source):
1260
+ """
1261
+ Lower bound on the diameter using multi-sweep.
1262
+
1263
+ This method computes a lower bound on the diameter of an unweighted
1264
+ undirected graph using several iterations of the 2-sweep algorithms
1265
+ [CGHLM2013]_. Roughly, it first uses 2-sweep to identify two vertices `s`
1266
+ and `d` that are far apart. Then it selects a vertex `m` that is at same
1267
+ distance from `s` and `d`. This vertex `m` will serve as the new source for
1268
+ another iteration of the 2-sweep algorithm that may improve the current
1269
+ lower bound on the diameter. This process is repeated as long as the lower
1270
+ bound on the diameter is improved.
1271
+
1272
+ The method returns a 4-tuple (LB, s, m, d), where LB is the best found lower
1273
+ bound on the diameter, s is a vertex of eccentricity LB, d is a vertex at
1274
+ distance LB from s, and m is a vertex at distance LB/2 from both s and d.
1275
+
1276
+ INPUT:
1277
+
1278
+ - ``g`` -- a short_digraph
1279
+
1280
+ - ``source`` -- starting node of the BFS
1281
+ """
1282
+ # The while loop below might not be entered so we have to make sure that
1283
+ # s and d which are returned are initialized.
1284
+ cdef uint32_t LB, tmp, m
1285
+ cdef uint32_t s = 0
1286
+ cdef uint32_t d = 0
1287
+ cdef uint32_t n = g.n
1288
+
1289
+ # Allocate some arrays and a bitset
1290
+ cdef bitset_t seen
1291
+ bitset_init(seen, n)
1292
+ cdef uint32_t * distances = <uint32_t *>sig_malloc(3 * n * sizeof(uint32_t))
1293
+ if not distances:
1294
+ bitset_free(seen)
1295
+ raise MemoryError()
1296
+ cdef uint32_t * predecessors = distances + n
1297
+ cdef uint32_t * waiting_list = distances + 2 * n
1298
+
1299
+ # We perform a first 2sweep call from source. If the returned value is a
1300
+ # very large number, the graph is not connected and so the diameter is
1301
+ # infinite.
1302
+ tmp = diameter_lower_bound_2sweep(g, source, distances, predecessors, waiting_list, seen)
1303
+ if tmp == UINT32_MAX:
1304
+ sig_free(distances)
1305
+ bitset_free(seen)
1306
+ return (UINT32_MAX, 0, 0, 0)
1307
+
1308
+ # We perform new 2sweep calls for as long as we are able to improve the
1309
+ # lower bound.
1310
+ LB = 0
1311
+ m = source
1312
+ while tmp > LB:
1313
+
1314
+ LB = tmp
1315
+
1316
+ # We store the vertices s, m, d of the last BFS call. For vertex m, we
1317
+ # search for a vertex of eccentricity LB/2. This vertex will serve as
1318
+ # the source for the next 2sweep call.
1319
+ s = waiting_list[0]
1320
+ d = waiting_list[n - 1]
1321
+ LB_2 = LB / 2
1322
+ m = d
1323
+ while distances[m] > LB_2:
1324
+ m = predecessors[m]
1325
+
1326
+ # We perform a new 2sweep call from m
1327
+ tmp = diameter_lower_bound_2sweep(g, m, distances, predecessors, waiting_list, seen)
1328
+
1329
+ sig_free(distances)
1330
+ bitset_free(seen)
1331
+
1332
+ return (LB, s, m, d)
1333
+
1334
+
1335
+ cdef uint32_t diameter_iFUB(short_digraph g,
1336
+ uint32_t source) noexcept:
1337
+ """
1338
+ Compute the diameter of the input Graph using the ``iFUB`` algorithm.
1339
+
1340
+ The ``iFUB`` (iterative Fringe Upper Bound) algorithm calculates the exact
1341
+ value of the diameter of a unweighted undirected graph [CGILM2010]_. This
1342
+ algorithms starts with a vertex found through a multi-sweep call (a
1343
+ refinement of the 4sweep method). The worst case time complexity of the iFUB
1344
+ algorithm is `O(nm)`, but it can be very fast in practice. See the code's
1345
+ documentation and [CGHLM2013]_ for more details.
1346
+
1347
+ INPUT:
1348
+
1349
+ - ``g`` -- a short_digraph
1350
+
1351
+ - ``source`` -- starting node of the first BFS
1352
+ """
1353
+ cdef uint32_t i, LB, m
1354
+ cdef uint32_t n = g.n
1355
+
1356
+ # We select a vertex m with low eccentricity using multi-sweep
1357
+ LB, _, m, _ = diameter_lower_bound_multi_sweep(g, source)
1358
+
1359
+ # If the lower bound is a very large number, it means that the graph is not
1360
+ # connected and so the diameter is infinite.
1361
+ if LB == UINT32_MAX:
1362
+ return LB
1363
+
1364
+ # We allocate some arrays and a bitset
1365
+ cdef bitset_t seen
1366
+ bitset_init(seen, n)
1367
+ cdef uint32_t * distances = <uint32_t *>sig_malloc(4 * n * sizeof(uint32_t))
1368
+ if not distances:
1369
+ bitset_free(seen)
1370
+ raise MemoryError()
1371
+ cdef uint32_t* waiting_list = distances + n
1372
+ cdef uint32_t* layer = distances + 2 * n
1373
+ cdef uint32_t* order = distances + 3 * n
1374
+
1375
+ # We order the vertices by decreasing layers. This is the inverse order of a
1376
+ # BFS from m, and so the inverse order of array waiting_list. Distances are
1377
+ # stored in array layer.
1378
+ LB = simple_BFS(g, m, layer, NULL, waiting_list, seen)
1379
+ for i in range(n):
1380
+ order[i] = waiting_list[n - i - 1]
1381
+
1382
+ # The algorithm:
1383
+ #
1384
+ # The diameter of the graph is equal to the maximum eccentricity of a
1385
+ # vertex. Let m be any vertex, and let V be partitioned into A u B where:
1386
+ #
1387
+ # d(m,a)<=i for all a \in A
1388
+ # d(m,b)>=i for all b \in B
1389
+ #
1390
+ # As all vertices from A are at distance <=2i from each other, a vertex a
1391
+ # from A with eccentricity ecc(a)>2i is at distance ecc(a) from some vertex
1392
+ # b\in B.
1393
+ #
1394
+ # Consequently, if we have already computed the eccentricity of all
1395
+ # vertices in B and know that the diameter is >2i, then we do not have to
1396
+ # compute the eccentricity of vertices in A.
1397
+ #
1398
+ # Now, we compute the maximum eccentricity of all vertices, ordered
1399
+ # decreasingly according to their distance to m. We stop when we know that
1400
+ # the eccentricity of the unexplored vertices is smaller than the max
1401
+ # eccentricity already found.
1402
+ i = 0
1403
+ while 2 * layer[order[i]] > LB and i < n:
1404
+ tmp = simple_BFS(g, order[i], distances, NULL, waiting_list, seen)
1405
+ i += 1
1406
+
1407
+ # We update the lower bound
1408
+ if tmp > LB:
1409
+ LB = tmp
1410
+
1411
+ sig_free(distances)
1412
+ bitset_free(seen)
1413
+
1414
+ # We finally return the computed diameter
1415
+ return LB
1416
+
1417
+
1418
+ cdef uint32_t diameter_DiFUB(short_digraph sd,
1419
+ uint32_t source) noexcept:
1420
+ r"""
1421
+ Return the diameter of unweighted directed graph.
1422
+
1423
+ The ``DiFUB`` (Directed iterative Fringe Upper Bound) algorithm calculates
1424
+ the exact value of the diameter of an unweighted directed graph [CGLM2012]_.
1425
+
1426
+ This algorithm starts from a vertex found through a 2Dsweep call (a directed
1427
+ version of the 2sweep method). The worst case time complexity of the DiFUB
1428
+ algorithm is `O(nm)`, but it can be very fast in practice. See the code's
1429
+ documentation and [CGLM2012]_ for more details.
1430
+
1431
+ If the digraph is not strongly connected, the returned value is infinity.
1432
+
1433
+ INPUT:
1434
+
1435
+ - ``sd`` -- a short_digraph
1436
+
1437
+ - ``source`` -- starting node of the first BFS
1438
+
1439
+ TESTS:
1440
+
1441
+ The diameter of a weakly connected digraph is infinity ::
1442
+
1443
+ sage: from sage.graphs.distances_all_pairs import diameter
1444
+ sage: G = digraphs.Path(5)
1445
+ sage: diameter(G, algorithm='DiFUB')
1446
+ +Infinity
1447
+ """
1448
+ cdef uint32_t n = sd.n
1449
+
1450
+ if n <= 1: # Trivial case
1451
+ return 0
1452
+
1453
+ cdef short_digraph rev_sd # Copy of sd with edges reversed
1454
+ init_reverse(rev_sd, sd)
1455
+
1456
+ cdef uint32_t LB, m, LB_1, LB_2, UB
1457
+ cdef size_t i
1458
+ cdef bitset_t seen
1459
+
1460
+ # We select a vertex with low eccentricity using 2Dsweep
1461
+ LB, _, m, _ = diameter_lower_bound_2Dsweep(sd, rev_sd, source)
1462
+
1463
+ # If the lower bound is a very large number, it means that the digraph is
1464
+ # not strongly connected and so the diameter is infinite.
1465
+ if LB == UINT32_MAX:
1466
+ return LB
1467
+
1468
+ # We allocate some arrays and a bitset
1469
+ bitset_init(seen, n)
1470
+ cdef MemoryAllocator mem = MemoryAllocator()
1471
+ cdef uint32_t * distances = <uint32_t *>mem.malloc(6 * n * sizeof(uint32_t))
1472
+
1473
+ if not distances:
1474
+ bitset_free(seen)
1475
+ raise MemoryError()
1476
+
1477
+ cdef uint32_t * waiting_list = distances + n
1478
+ cdef uint32_t * order_1 = distances + 2 * n
1479
+ cdef uint32_t * order_2 = distances + 3 * n
1480
+ cdef uint32_t * layer_1 = distances + 4 * n
1481
+ cdef uint32_t * layer_2 = distances + 5 * n
1482
+
1483
+ # We order the vertices by decreasing forward / backward layers. This is the
1484
+ # inverse order of a forward / backward BFS from m, and so the inverse order
1485
+ # of array waiting_list. Forward / Backward distances are stored in arrays
1486
+ # layer_1 / layer_2 respectively.
1487
+ LB_1 = simple_BFS(sd, m, layer_1, NULL, waiting_list, seen)
1488
+ for i in range(n):
1489
+ order_1[i] = waiting_list[n - i - 1]
1490
+
1491
+ LB_2 = simple_BFS(rev_sd, m, layer_2, NULL, waiting_list, seen)
1492
+ for i in range(n):
1493
+ order_2[i] = waiting_list[n - i - 1]
1494
+
1495
+ # update the lower bound
1496
+ LB = max(LB, LB_1, LB_2)
1497
+
1498
+ if LB == UINT32_MAX: # Not strongly connected case
1499
+ return LB
1500
+
1501
+ # The algorithm:
1502
+ #
1503
+ # The diameter of the digraph is equal to the maximum forward or backward
1504
+ # eccentricity of a vertex. The algorithm is based on the following two
1505
+ # observations:
1506
+ # 1). All the nodes `x` above the level `i` in Backward BFS of `m` having
1507
+ # forward eccentricity greater than `2(i-1)` have a corresponding node `y`,
1508
+ # whose backward eccentricity is greater than or equal to the forward
1509
+ # eccentricity of `x`, below or on the level `i` in Forward BFS of `m`.
1510
+ #
1511
+ # 2). All the nodes `x` above the level `i` in Forward BFS of `m` having
1512
+ # backward eccentricity greater than `2(i-1)` have a corresponding node `y`,
1513
+ # whose forward eccentricity is greater than or equal to the backward
1514
+ # eccentricity of `x`, below or on the level `i` in Backward BFS of `m`
1515
+ #
1516
+ # Therefore, we calculate backward / forward eccentricity of all nodes at
1517
+ # level `i` in Forward / Backward BFS of `m` respectively. And their
1518
+ # maximum is `LB`. If `LB` is greater than `2(max distance at next level)`
1519
+ # then we are done, else we proceed further.
1520
+
1521
+ i = 0
1522
+ UB = max(2 * layer_1[order_1[i]], 2 * layer_2[order_2[i]])
1523
+
1524
+ while LB < UB:
1525
+ LB_1 = simple_BFS(rev_sd, order_1[i], distances, NULL, waiting_list, seen)
1526
+ LB_2 = simple_BFS(sd, order_2[i], distances, NULL, waiting_list, seen)
1527
+
1528
+ # update the lower bound
1529
+ LB = max(LB, LB_1, LB_2)
1530
+ i += 1
1531
+
1532
+ if LB == UINT32_MAX or i == n:
1533
+ break
1534
+ # maximum distance at next level
1535
+ UB = max(2 * layer_1[order_1[i]], 2 * layer_2[order_2[i]])
1536
+
1537
+ bitset_free(seen)
1538
+ free_short_digraph(rev_sd)
1539
+
1540
+ # Finally return the computed diameter
1541
+ return LB
1542
+
1543
+
1544
+ cdef uint32_t diameter_DHV(short_digraph g) noexcept:
1545
+ r"""
1546
+ Return the diameter of unweighted graph `g`.
1547
+
1548
+ This method computes the diameter of unweighted undirected graph using the
1549
+ algorithm proposed in [Dragan2018]_.
1550
+
1551
+ This method returns Infinity if graph is not connected.
1552
+
1553
+ INPUT:
1554
+
1555
+ - ``g`` -- a short_digraph
1556
+
1557
+ EXAMPLES::
1558
+
1559
+ sage: from sage.graphs.distances_all_pairs import diameter
1560
+ sage: G = graphs.PathGraph(5)
1561
+ sage: diameter(G, algorithm='DHV')
1562
+ 4
1563
+
1564
+ TESTS:
1565
+
1566
+ sage: G = graphs.RandomGNP(20,0.3)
1567
+ sage: G.diameter() == diameter(G, algorithm='DHV')
1568
+ True
1569
+
1570
+ sage: G = Graph([(0, 1)])
1571
+ sage: diameter(G, algorithm='DHV')
1572
+ 1
1573
+ """
1574
+ cdef uint32_t n = g.n
1575
+ if n <= 1:
1576
+ return 0
1577
+
1578
+ cdef MemoryAllocator mem = MemoryAllocator()
1579
+ cdef uint32_t * distances = <uint32_t *>mem.malloc(4 * n * sizeof(uint32_t))
1580
+ if not distances:
1581
+ raise MemoryError()
1582
+
1583
+ cdef uint32_t * waiting_list = distances + n
1584
+
1585
+ # For storing upper and lower bounds on eccentricity of nodes
1586
+ cdef uint32_t * ecc_upper_bound = distances + 2 * n
1587
+ cdef uint32_t * ecc_lower_bound = distances + 3 * n
1588
+ memset(ecc_upper_bound, <char>-1, n * sizeof(uint32_t))
1589
+ memset(ecc_lower_bound, 0, n * sizeof(uint32_t))
1590
+
1591
+ cdef uint32_t u, ecc_u
1592
+ cdef uint32_t x, ecc_x
1593
+ cdef uint32_t antipode, ecc_antipode
1594
+ cdef uint32_t LB = 0
1595
+ cdef uint32_t UB = UINT32_MAX
1596
+ cdef uint32_t v, tmp
1597
+ cdef size_t i, idx
1598
+ cdef bitset_t seen
1599
+ bitset_init(seen, n)
1600
+
1601
+ cdef list active = list(range(n))
1602
+
1603
+ # Algorithm
1604
+ while LB < UB and active:
1605
+ # 1. Select vertex u with maximum eccentricity upper bound
1606
+ tmp = 0
1607
+ for i, v in enumerate(active):
1608
+ if ecc_upper_bound[v] > tmp:
1609
+ tmp = ecc_upper_bound[v]
1610
+ idx = i
1611
+ active[idx], active[-1] = active[-1], active[idx]
1612
+ u = active.pop()
1613
+
1614
+ # Compute the eccentricity of u and update eccentricity lower bounds
1615
+ ecc_u = simple_BFS(g, u, distances, NULL, waiting_list, seen)
1616
+ LB = max(LB, ecc_u)
1617
+ if LB == UINT32_MAX: # Disconnected graph
1618
+ break
1619
+
1620
+ for v in active:
1621
+ ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v])
1622
+
1623
+ # 2. Select x such that dist(u, x) + ecc[x] == ecc[u].
1624
+ # Since we don't know ecc[x], we select x with minimum eccentricity
1625
+ # lower bound. If ecc[x] == ecc_lb[x], we are done. Otherwise, we
1626
+ # update eccentricity lower bounds and repeat
1627
+ while active:
1628
+ # Select v with minimum eccentricity lower bound
1629
+ tmp = UINT32_MAX
1630
+ for i, v in enumerate(active):
1631
+ if ecc_lower_bound[v] < tmp:
1632
+ tmp = ecc_lower_bound[v]
1633
+ idx = i
1634
+ active[idx], active[-1] = active[-1], active[idx]
1635
+ x = active.pop()
1636
+ ecc_x = simple_BFS(g, x, distances, NULL, waiting_list, seen)
1637
+ LB = max(LB, ecc_x)
1638
+
1639
+ if ecc_x == ecc_lower_bound[x]:
1640
+ # We found the good vertex x
1641
+ # We update eccentricity upper bounds of the remaining vertices,
1642
+ # set UB to the largest of these values and break
1643
+ UB = 0
1644
+ for v in active:
1645
+ ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_x)
1646
+ UB = max(UB, ecc_upper_bound[v])
1647
+ break
1648
+
1649
+ else:
1650
+ # x was not a good choice
1651
+ # We use its antipode to update eccentricity lower bounds.
1652
+ # Observe that this antipode might have already been seen.
1653
+ antipode = waiting_list[n-1]
1654
+ for i, v in enumerate(active):
1655
+ if v == antipode:
1656
+ active[i] = active[-1]
1657
+ tmp = active.pop()
1658
+ break
1659
+
1660
+ ecc_antipode = simple_BFS(g, antipode, distances, NULL, waiting_list, seen)
1661
+ LB = max(LB, ecc_antipode)
1662
+ for v in active:
1663
+ ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v])
1664
+
1665
+ bitset_free(seen)
1666
+ return LB
1667
+
1668
+
1669
+ def diameter(G, algorithm=None, source=None):
1670
+ r"""
1671
+ Return the diameter of `G`.
1672
+
1673
+ This method returns Infinity if the (di)graph is not connected. It can
1674
+ also quickly return a lower bound on the diameter using the ``2sweep``,
1675
+ ``2Dsweep`` and ``multi-sweep`` schemes.
1676
+
1677
+ INPUT:
1678
+
1679
+ - ``algorithm`` -- string (default: ``None``); specifies the algorithm to
1680
+ use among:
1681
+
1682
+ - ``'standard'`` -- computes the diameter of the input (di)graph as the
1683
+ largest eccentricity of its vertices. This is the classical algorithm
1684
+ with time complexity in `O(nm)`.
1685
+
1686
+ - ``'2sweep'`` -- computes a lower bound on the diameter of an
1687
+ unweighted undirected graph using 2 BFS, as proposed in [MLH2008]_. It
1688
+ first selects a vertex `v` that is at largest distance from an initial
1689
+ vertex source using BFS. Then it performs a second BFS from `v`. The
1690
+ largest distance from `v` is returned as a lower bound on the diameter
1691
+ of `G`. The time complexity of this algorithm is linear in the size of
1692
+ `G`.
1693
+
1694
+ - ``'2Dsweep'`` -- computes lower bound on the diameter of an unweighted
1695
+ directed graph using directed version of ``2sweep`` as proposed in
1696
+ [Broder2000]_. If the digraph is not strongly connected, the returned
1697
+ value is infinity.
1698
+
1699
+ - ``'DHV'`` -- computes diameter of unweighted undirected graph using the
1700
+ algorithm proposed in [Dragan2018]_
1701
+
1702
+ - ``'multi-sweep'`` -- computes a lower bound on the diameter of an
1703
+ unweighted undirected graph using several iterations of the ``2sweep``
1704
+ algorithms [CGHLM2013]_. Roughly, it first uses ``2sweep`` to identify
1705
+ two vertices `u` and `v` that are far apart. Then it selects a vertex
1706
+ `w` that is at same distance from `u` and `v`. This vertex `w` will
1707
+ serve as the new source for another iteration of the ``2sweep``
1708
+ algorithm that may improve the current lower bound on the diameter.
1709
+ This process is repeated as long as the lower bound on the diameter
1710
+ is improved.
1711
+
1712
+ - ``'iFUB'`` -- the iFUB (iterative Fringe Upper Bound) algorithm,
1713
+ proposed in [CGILM2010]_, computes the exact value of the diameter of an
1714
+ unweighted undirected graph. It is based on the following observation:
1715
+
1716
+ The diameter of the graph is equal to the maximum eccentricity of
1717
+ a vertex. Let `v` be any vertex, and let `V` be partitioned into
1718
+ `A\cup B` where:
1719
+
1720
+ .. MATH::
1721
+
1722
+ d(v,a) \leq i, \forall a \in A\\
1723
+ d(v,b) \geq i, \forall b \in B
1724
+
1725
+ As all vertices from `A` are at distance `\leq 2i` from each
1726
+ other, a vertex `a\in A` with eccentricity `ecc(a)>2i` is at
1727
+ distance `ecc(a)` from some vertex `b\in B`.
1728
+
1729
+ Consequently, if we have already computed the maximum eccentricity
1730
+ `m` of all vertices in `B` and if `m>2i`, then we do not need to
1731
+ compute the eccentricity of the vertices in `A`.
1732
+
1733
+ Starting from a vertex `v` obtained through a multi-sweep computation
1734
+ (which refines the 4sweep algorithm used in [CGHLM2013]_), we compute
1735
+ the diameter by computing the eccentricity of all vertices sorted
1736
+ decreasingly according to their distance to `v`, and stop as allowed
1737
+ by the remark above. The worst case time complexity of the iFUB
1738
+ algorithm is `O(nm)`, but it can be very fast in practice.
1739
+
1740
+ - ``'DiFUB'`` -- the directed version of iFUB (iterative Fringe Upper
1741
+ Bound) algorithm. See the code's documentation and [CGLM2012]_ for more
1742
+ details. If the digraph is not strongly connected, the returned value is
1743
+ infinity.
1744
+
1745
+ - ``source`` -- (default: ``None``) vertex from which to start the first BFS.
1746
+ If ``source==None``, an arbitrary vertex of the graph is chosen. Raise an
1747
+ error if the initial vertex is not in `G`. This parameter is not used
1748
+ when ``algorithm=='standard'``.
1749
+
1750
+ .. NOTE::
1751
+ As the graph is first converted to a short_digraph, all complexity
1752
+ have an extra `O(m+n)` for ``SparseGraph`` and `O(n^2)` for
1753
+ ``DenseGraph``.
1754
+
1755
+ EXAMPLES::
1756
+
1757
+ sage: from sage.graphs.distances_all_pairs import diameter
1758
+ sage: G = graphs.PetersenGraph()
1759
+ sage: diameter(G, algorithm='iFUB')
1760
+ 2
1761
+ sage: G = Graph({0: [], 1: [], 2: [1]})
1762
+ sage: diameter(G, algorithm='iFUB')
1763
+ +Infinity
1764
+ sage: G = digraphs.Circuit(6)
1765
+ sage: diameter(G, algorithm='2Dsweep')
1766
+ 5
1767
+ sage: G = graphs.PathGraph(7).to_directed()
1768
+ sage: diameter(G, algorithm='DiFUB')
1769
+ 6
1770
+
1771
+ Although max( ) is usually defined as -Infinity, since the diameter will
1772
+ never be negative, we define it to be zero::
1773
+
1774
+ sage: G = graphs.EmptyGraph()
1775
+ sage: diameter(G, algorithm='iFUB')
1776
+ 0
1777
+
1778
+ Comparison of exact algorithms for graphs::
1779
+
1780
+ sage: # needs networkx
1781
+ sage: G = graphs.RandomBarabasiAlbert(100, 2)
1782
+ sage: d1 = diameter(G, algorithm='standard')
1783
+ sage: d2 = diameter(G, algorithm='iFUB')
1784
+ sage: d3 = diameter(G, algorithm='iFUB', source=G.random_vertex())
1785
+ sage: d4 = diameter(G, algorithm='DHV')
1786
+ sage: if d1 != d2 or d1 != d3 or d1 != d4: print("Something goes wrong!")
1787
+
1788
+ Comparison of lower bound algorithms::
1789
+
1790
+ sage: lb2 = diameter(G, algorithm='2sweep') # needs networkx
1791
+ sage: lbm = diameter(G, algorithm='multi-sweep') # needs networkx
1792
+ sage: if not (lb2 <= lbm and lbm <= d3): print("Something goes wrong!") # needs networkx
1793
+
1794
+ Comparison of exact algorithms for digraphs::
1795
+
1796
+ sage: # needs networkx
1797
+ sage: D = DiGraph(graphs.RandomBarabasiAlbert(50, 2))
1798
+ sage: d1 = diameter(D, algorithm='standard')
1799
+ sage: d2 = diameter(D, algorithm='DiFUB')
1800
+ sage: d3 = diameter(D, algorithm='DiFUB', source=D.random_vertex())
1801
+ sage: d1 == d2 and d1 == d3
1802
+ True
1803
+
1804
+ TESTS:
1805
+
1806
+ This was causing a segfault. Fixed in :issue:`17873` ::
1807
+
1808
+ sage: G = graphs.PathGraph(1)
1809
+ sage: diameter(G, algorithm='iFUB')
1810
+ 0
1811
+
1812
+ Immutable graphs::
1813
+
1814
+ sage: G = graphs.RandomGNP(10, .5)
1815
+ sage: G._backend
1816
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
1817
+ sage: H = Graph(G, immutable=True)
1818
+ sage: H._backend
1819
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
1820
+ sage: diameter(G, algorithm='iFUB') == diameter(H, algorithm='iFUB')
1821
+ True
1822
+ """
1823
+ cdef uint32_t n = G.order()
1824
+ if n <= 1:
1825
+ return 0
1826
+
1827
+ if G.is_directed():
1828
+ if algorithm is None:
1829
+ algorithm = 'DiFUB'
1830
+ elif algorithm not in ['2Dsweep', 'standard', 'DiFUB']:
1831
+ raise ValueError("unknown algorithm for computing the diameter of directed graph")
1832
+ else:
1833
+ if algorithm is None:
1834
+ algorithm = 'iFUB'
1835
+ elif algorithm not in ['2sweep', 'multi-sweep', 'iFUB', 'standard', 'DHV']:
1836
+ raise ValueError("unknown algorithm for computing the diameter of undirected graph")
1837
+
1838
+ if algorithm == 'standard':
1839
+ return max(G.eccentricity())
1840
+ if source is None:
1841
+ source = next(G.vertex_iterator())
1842
+ elif not G.has_vertex(source):
1843
+ raise ValueError("the specified source is not a vertex of the input Graph")
1844
+
1845
+ # Copying the whole graph to obtain the list of neighbors quicker than by
1846
+ # calling out_neighbors. This data structure is well documented in the
1847
+ # module sage.graphs.base.static_sparse_graph
1848
+ cdef list int_to_vertex
1849
+ cdef StaticSparseCGraph cg
1850
+ cdef short_digraph sd
1851
+ if isinstance(G, StaticSparseBackend):
1852
+ cg = <StaticSparseCGraph> G._cg
1853
+ sd = <short_digraph> cg.g
1854
+ int_to_vertex = cg._vertex_to_labels
1855
+ else:
1856
+ int_to_vertex = list(G)
1857
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
1858
+ cdef short_digraph rev_sd # to store copy of sd with edges reversed
1859
+
1860
+ # and we map the source to an int in [0,n-1]
1861
+ cdef uint32_t isource = 0 if source is None else int_to_vertex.index(source)
1862
+
1863
+ cdef bitset_t seen
1864
+ cdef uint32_t* tab
1865
+ cdef uint32_t LB
1866
+
1867
+ if algorithm == '2sweep':
1868
+ # We need to allocate arrays and bitset
1869
+ bitset_init(seen, n)
1870
+ tab = <uint32_t*> sig_malloc(2* n * sizeof(uint32_t))
1871
+ if not tab:
1872
+ if not isinstance(G, StaticSparseBackend):
1873
+ free_short_digraph(sd)
1874
+ bitset_free(seen)
1875
+ raise MemoryError()
1876
+
1877
+ LB = diameter_lower_bound_2sweep(sd, isource, tab, NULL, tab + n, seen)
1878
+
1879
+ bitset_free(seen)
1880
+ sig_free(tab)
1881
+
1882
+ elif algorithm == '2Dsweep':
1883
+ init_reverse(rev_sd, sd)
1884
+ LB = diameter_lower_bound_2Dsweep(sd, rev_sd, isource)[0]
1885
+ free_short_digraph(rev_sd)
1886
+
1887
+ elif algorithm == 'multi-sweep':
1888
+ LB = diameter_lower_bound_multi_sweep(sd, isource)[0]
1889
+
1890
+ elif algorithm == 'DiFUB':
1891
+ LB = diameter_DiFUB(sd, isource)
1892
+
1893
+ elif algorithm == 'DHV':
1894
+ LB = diameter_DHV(sd)
1895
+
1896
+ else: # algorithm == 'iFUB'
1897
+ LB = diameter_iFUB(sd, isource)
1898
+
1899
+ if not isinstance(G, StaticSparseBackend):
1900
+ free_short_digraph(sd)
1901
+
1902
+ if LB < 0 or LB > n:
1903
+ from sage.rings.infinity import Infinity
1904
+ return +Infinity
1905
+ return int(LB)
1906
+
1907
+
1908
+ ###########
1909
+ # Radius #
1910
+ ###########
1911
+
1912
+ def radius_DHV(G):
1913
+ r"""
1914
+ Return the radius of unweighted graph `G`.
1915
+
1916
+ This method computes the radius of unweighted undirected graph using the
1917
+ algorithm given in [Dragan2018]_.
1918
+
1919
+ This method returns Infinity if graph is not connected.
1920
+
1921
+ EXAMPLES::
1922
+
1923
+ sage: from sage.graphs.distances_all_pairs import radius_DHV
1924
+ sage: G = graphs.PetersenGraph()
1925
+ sage: radius_DHV(G)
1926
+ 2
1927
+ sage: G = graphs.RandomGNP(20,0.3)
1928
+ sage: from sage.graphs.distances_all_pairs import eccentricity
1929
+ sage: radius_DHV(G) == min(eccentricity(G, algorithm='bounds'))
1930
+ True
1931
+
1932
+ TESTS:
1933
+
1934
+ sage: G = Graph()
1935
+ sage: radius_DHV(G)
1936
+ 0
1937
+ sage: G = Graph(1)
1938
+ sage: radius_DHV(G)
1939
+ 0
1940
+ sage: G = Graph(2)
1941
+ sage: radius_DHV(G)
1942
+ +Infinity
1943
+ sage: G = graphs.PathGraph(2)
1944
+ sage: radius_DHV(G)
1945
+ 1
1946
+ sage: G = DiGraph(1)
1947
+ sage: radius_DHV(G)
1948
+ Traceback (most recent call last):
1949
+ ...
1950
+ TypeError: this method works for unweighted undirected graphs only
1951
+
1952
+ Immutable graphs::
1953
+
1954
+ sage: G = graphs.RandomGNP(10, .5)
1955
+ sage: G._backend
1956
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
1957
+ sage: H = Graph(G, immutable=True)
1958
+ sage: H._backend
1959
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
1960
+ sage: radius_DHV(G) == radius_DHV(H)
1961
+ True
1962
+ """
1963
+ if G.is_directed():
1964
+ raise TypeError("this method works for unweighted undirected graphs only")
1965
+
1966
+ cdef uint32_t n = G.order()
1967
+ if n <= 1:
1968
+ return 0
1969
+
1970
+ cdef list int_to_vertex
1971
+ cdef StaticSparseCGraph cg
1972
+ cdef short_digraph sd
1973
+ if isinstance(G, StaticSparseBackend):
1974
+ cg = <StaticSparseCGraph> G._cg
1975
+ sd = <short_digraph> cg.g
1976
+ int_to_vertex = cg._vertex_to_labels
1977
+ else:
1978
+ int_to_vertex = list(G)
1979
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
1980
+
1981
+ cdef uint32_t source, ecc_source
1982
+ cdef uint32_t antipode, ecc_antipode
1983
+ cdef uint32_t UB = UINT32_MAX
1984
+ cdef uint32_t LB = 0
1985
+
1986
+ cdef MemoryAllocator mem = MemoryAllocator()
1987
+ cdef uint32_t * distances = <uint32_t *>mem.malloc(3 * n * sizeof(uint32_t))
1988
+ if not distances:
1989
+ if not isinstance(G, StaticSparseBackend):
1990
+ free_short_digraph(sd)
1991
+ raise MemoryError()
1992
+
1993
+ cdef uint32_t * waiting_list = distances + n
1994
+
1995
+ # For storing lower bound on eccentricity of nodes
1996
+ cdef uint32_t * ecc_lower_bound = distances + 2 * n
1997
+ memset(ecc_lower_bound, 0, n * sizeof(uint32_t))
1998
+
1999
+ cdef bitset_t seen
2000
+ bitset_init(seen, n)
2001
+
2002
+ # Algorithm
2003
+ source = 0
2004
+ while LB < UB:
2005
+ # 1) pick vertex with minimum eccentricity lower bound
2006
+ # and compute its eccentricity
2007
+ ecc_source = simple_BFS(sd, source, distances, NULL, waiting_list, seen)
2008
+
2009
+ if ecc_source == UINT32_MAX: # Disconnected graph
2010
+ break
2011
+
2012
+ UB = min(UB, ecc_source) # minimum among exact computed eccentricities
2013
+ if ecc_source == ecc_lower_bound[source]:
2014
+ # we have found minimum eccentricity vertex and hence the radius
2015
+ break
2016
+
2017
+ # 2) Take vertex at largest distance from source, called antipode (last
2018
+ # vertex visited in simple_BFS), and compute its BFS distances.
2019
+ # By definition of antipode, we have ecc_antipode >= ecc_source.
2020
+ antipode = waiting_list[n-1]
2021
+ ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen)
2022
+
2023
+ # 3) Use distances from antipode to improve eccentricity lower bounds.
2024
+ # We also determine the next source
2025
+ LB = UINT32_MAX
2026
+ for v in range(n):
2027
+ ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v])
2028
+ if LB > ecc_lower_bound[v]:
2029
+ LB = ecc_lower_bound[v]
2030
+ source = v # vertex with minimum eccentricity lower bound
2031
+
2032
+ if not isinstance(G, StaticSparseBackend):
2033
+ free_short_digraph(sd)
2034
+ bitset_free(seen)
2035
+ if UB == UINT32_MAX:
2036
+ from sage.rings.infinity import Infinity
2037
+ return +Infinity
2038
+
2039
+ return UB
2040
+
2041
+
2042
+ ################
2043
+ # Wiener index #
2044
+ ################
2045
+
2046
+ def wiener_index(G):
2047
+ r"""
2048
+ Return the Wiener index of the graph.
2049
+
2050
+ The Wiener index of an undirected graph `G` is defined as
2051
+ `W(G) = \frac{1}{2} \sum_{u,v\in G} d(u,v)` where `d(u,v)` denotes the
2052
+ distance between vertices `u` and `v` (see [KRG1996]_).
2053
+
2054
+ The Wiener index of a directed graph `G` is defined as the sum of the
2055
+ distances between each pairs of vertices, `W(G) = \sum_{u,v\in G} d(u,v)`.
2056
+
2057
+ EXAMPLES:
2058
+
2059
+ From [GYLL1993]_, cited in [KRG1996]_::
2060
+
2061
+ sage: g=graphs.PathGraph(10)
2062
+ sage: w=lambda x: (x*(x*x -1)/6)
2063
+ sage: g.wiener_index()==w(10)
2064
+ True
2065
+
2066
+ Wiener index of complete (di)graphs::
2067
+
2068
+ sage: n = 5
2069
+ sage: g = graphs.CompleteGraph(n)
2070
+ sage: g.wiener_index() == (n * (n - 1)) / 2
2071
+ True
2072
+ sage: g = digraphs.Complete(n)
2073
+ sage: g.wiener_index() == n * (n - 1)
2074
+ True
2075
+
2076
+ Wiener index of a graph of order 1::
2077
+
2078
+ sage: Graph(1).wiener_index()
2079
+ 0
2080
+
2081
+ The Wiener index is not defined on the empty graph::
2082
+
2083
+ sage: Graph().wiener_index()
2084
+ Traceback (most recent call last):
2085
+ ...
2086
+ ValueError: Wiener index is not defined for the empty graph
2087
+
2088
+ TESTS:
2089
+
2090
+ Immutable graphs::
2091
+
2092
+ sage: G = graphs.RandomGNP(10, .5)
2093
+ sage: G._backend
2094
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
2095
+ sage: H = Graph(G, immutable=True)
2096
+ sage: H._backend
2097
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
2098
+ sage: G.wiener_index() == H.wiener_index()
2099
+ True
2100
+ """
2101
+ if not G:
2102
+ raise ValueError("Wiener index is not defined for the empty graph")
2103
+
2104
+ cdef unsigned int n = G.order()
2105
+ if n == 1:
2106
+ return 0
2107
+
2108
+ # Copying the whole graph to obtain the list of neighbors quicker than by
2109
+ # calling out_neighbors. This data structure is well documented in the
2110
+ # module sage.graphs.base.static_sparse_graph
2111
+ cdef StaticSparseCGraph cg
2112
+ cdef short_digraph sd
2113
+ if isinstance(G, StaticSparseBackend):
2114
+ cg = <StaticSparseCGraph> G._cg
2115
+ sd = <short_digraph> cg.g
2116
+ else:
2117
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G))
2118
+
2119
+ # allocated some data structures
2120
+ cdef bitset_t seen
2121
+ bitset_init(seen, n)
2122
+ cdef MemoryAllocator mem = MemoryAllocator()
2123
+ cdef uint32_t * distances = <uint32_t *> mem.allocarray(2 * n, sizeof(uint32_t))
2124
+ cdef uint32_t * waiting_list = distances + n
2125
+
2126
+ cdef uint64_t s = 0
2127
+ cdef uint32_t u, v
2128
+ cdef uint32_t ecc
2129
+ for u in range(n):
2130
+ ecc = simple_BFS(sd, u, distances, NULL, waiting_list, seen)
2131
+ if ecc == UINT32_MAX:
2132
+ # the graph is not connected
2133
+ s = UINT64_MAX
2134
+ break
2135
+ for v in range(0 if G.is_directed() else (u + 1), n):
2136
+ s += distances[v]
2137
+
2138
+ if not isinstance(G, StaticSparseBackend):
2139
+ free_short_digraph(sd)
2140
+ bitset_free(seen)
2141
+
2142
+ if s == UINT64_MAX:
2143
+ from sage.rings.infinity import Infinity
2144
+ return +Infinity
2145
+ return s
2146
+
2147
+
2148
+ ################
2149
+ # Szeged index #
2150
+ ################
2151
+
2152
+ cdef uint64_t c_szeged_index_low_memory(short_digraph sd) noexcept:
2153
+ r"""
2154
+ Return the Szeged index of the graph.
2155
+
2156
+ Let `G = (V, E)` be a connected simple graph, and for any `uv\in E`, let
2157
+ `N_u(uv) = \{w\in V:d(u,w)<d(v,w)\}` and `n_u(uv)=|N_u(uv)|`. The Szeged
2158
+ index of `G` is then defined as [KRG1996]_ as `\sum_{uv \in
2159
+ E(G)}n_u(uv)\times n_v(uv)`.
2160
+
2161
+ To determine `N_u(uv)`, this method performs a breadth first search (BFS)
2162
+ from each vertex `s \in V`. Then, each time an edge `uv` visited by the BFS
2163
+ is such that `d(s, u) < d(s, v)`, it adds 1 to `N_u(uv)`. Since this method
2164
+ assumes that the graph is undirected, the graph `sd` has both arcs `uv` and
2165
+ `vu`. Using one counter per arc, the counter for arc `uv` records the number
2166
+ of vertices that are closer to the side `u` of edge `uv`, and the counter
2167
+ for arc `vu` records the number of vertices that are closer to the side `v`
2168
+ of edge `uv`.
2169
+
2170
+ This method assumes that the input graph has no loops or multiple edges.
2171
+
2172
+ EXAMPLES::
2173
+
2174
+ sage: graphs.CycleGraph(4).szeged_index(algorithm='low')
2175
+ 16
2176
+ """
2177
+ cdef size_t n = sd.n
2178
+ if n <= 1:
2179
+ return 0
2180
+ if n == 2:
2181
+ return 1
2182
+
2183
+ cdef MemoryAllocator mem = MemoryAllocator()
2184
+ cdef uint32_t * current_layer = <uint32_t *> mem.malloc(n * sizeof(uint32_t))
2185
+ cdef uint32_t n_current
2186
+ cdef uint32_t * next_layer = <uint32_t *> mem.malloc(n * sizeof(uint32_t))
2187
+ cdef uint32_t n_next
2188
+ cdef uint32_t * seen = <uint32_t *> mem.calloc(n, sizeof(uint32_t))
2189
+ cdef uint32_t seen_value = 0
2190
+
2191
+ # For each edge e = uv, we have 2 arcs. Let p_uv be a pointer to arc uv and
2192
+ # p_vu a pointer to arc vu. The index of arc uv is p_uv - sd.edges. During a
2193
+ # BFS from source, we add 1 to counter[p_uv - sd.edges] if vertex u is closer
2194
+ # from source than v and 1 to counter[p_vu - sd.edges] if vertex v is closer
2195
+ # from source than u. The Szeged index is then
2196
+ # sum_{e=uv} counter[p_uv - sd.edges] * counter[p_vu - sd.edges]
2197
+ cdef uint32_t * counter = <uint32_t *> mem.calloc(2 * sd.m, sizeof(uint32_t))
2198
+
2199
+ cdef uint32_t source, u, v, i
2200
+ cdef uint32_t* p_uv
2201
+ cdef uint32_t* p_end
2202
+
2203
+ sig_on()
2204
+ for source in range(n):
2205
+
2206
+ next_layer[0] = source
2207
+ n_next = 1
2208
+ seen_value += 1
2209
+
2210
+ while n_next:
2211
+ # Go to next layer
2212
+ current_layer, next_layer = next_layer, current_layer
2213
+ n_current, n_next = n_next, 0
2214
+
2215
+ # Mark all vertices in current layer as seen
2216
+ for i in range(n_current):
2217
+ seen[current_layer[i]] = seen_value
2218
+
2219
+ for i in range(n_current):
2220
+ u = current_layer[i]
2221
+
2222
+ # Visit all (out) neighbors of u
2223
+ p_uv = sd.neighbors[u]
2224
+ p_end = sd.neighbors[u + 1]
2225
+ while p_uv < p_end:
2226
+ v = p_uv[0]
2227
+ if seen[v] != seen_value:
2228
+ # u is closer to the source
2229
+ counter[p_uv - sd.edges] += 1
2230
+
2231
+ # Ensure that v is added only once for next_level
2232
+ if seen[v] != seen_value + 1:
2233
+ next_layer[n_next] = v
2234
+ n_next += 1
2235
+ seen[v] = seen_value + 1
2236
+
2237
+ p_uv += 1
2238
+ sig_off()
2239
+
2240
+ cdef uint64_t s = 0
2241
+ cdef uint32_t* p_vu
2242
+
2243
+ sig_on()
2244
+ for u in range(n - 1):
2245
+ p_uv = sd.neighbors[u]
2246
+ p_end = sd.neighbors[u + 1]
2247
+ while p_uv < p_end:
2248
+ v = p_uv[0]
2249
+ if u < v:
2250
+ # Get the pointer to arc vu
2251
+ p_vu = has_edge(sd, v, u)
2252
+ s += counter[p_uv - sd.edges] * counter[p_vu - sd.edges]
2253
+
2254
+ p_uv += 1
2255
+ sig_off()
2256
+
2257
+ return s
2258
+
2259
+
2260
+ cdef uint64_t c_szeged_index_high_memory(short_digraph sd) noexcept:
2261
+ r"""
2262
+ Return the Szeged index of the graph.
2263
+
2264
+ Let `G = (V, E)` be a connected graph, and for any `uv\in E`, let `N_u(uv) =
2265
+ \{w\in V:d(u,w)<d(v,w)\}` and `n_u(uv)=|N_u(uv)|`. The Szeged index of `G`
2266
+ is then defined as [KRG1996]_ as `\sum_{uv \in E(G)}n_u(uv)\times n_v(uv)`.
2267
+
2268
+ EXAMPLES::
2269
+
2270
+ sage: graphs.CycleGraph(4).szeged_index(algorithm='high')
2271
+ 16
2272
+ """
2273
+ cdef int n = sd.n
2274
+ cdef MemoryAllocator mem = MemoryAllocator()
2275
+ cdef unsigned short* distances = <unsigned short*> mem.malloc(n * n * sizeof(unsigned short))
2276
+
2277
+ # Compute all pairs shortest path
2278
+ c_all_pairs_shortest_path_BFS(sd, NULL, distances, NULL)
2279
+
2280
+ cdef uint32_t* p_uv
2281
+ cdef uint32_t* p_end
2282
+ cdef uint32_t u, v, w
2283
+ cdef unsigned short* du
2284
+ cdef unsigned short* dv
2285
+ cdef uint32_t n1, n2
2286
+ cdef uint64_t s = 0
2287
+
2288
+ for u in range(n):
2289
+ du = distances + u * n
2290
+ p_uv = sd.neighbors[u]
2291
+ p_end = sd.neighbors[u + 1]
2292
+ while p_uv < p_end:
2293
+ v = p_uv[0]
2294
+ if u < v:
2295
+ dv = distances + v * n
2296
+ n1 = n2 = 0
2297
+ for w in range(n):
2298
+ if du[w] < dv[w]:
2299
+ n1 += 1
2300
+ elif dv[w] < du[w]:
2301
+ n2 += 1
2302
+
2303
+ s += n1 * n2
2304
+ p_uv += 1
2305
+
2306
+ return s
2307
+
2308
+
2309
+ def szeged_index(G, algorithm=None):
2310
+ r"""
2311
+ Return the Szeged index of the graph `G`.
2312
+
2313
+ Let `G = (V, E)` be a connected graph, and for any `uv\in E`, let `N_u(uv) =
2314
+ \{w\in V:d(u,w)<d(v,w)\}` and `n_u(uv)=|N_u(uv)|`. The Szeged index of `G`
2315
+ is then defined as [KRG1996]_
2316
+
2317
+ .. MATH::
2318
+
2319
+ `\sum_{uv \in E(G)}n_u(uv)\times n_v(uv)`
2320
+
2321
+ See the :wikipedia:`Szeged_index` for more details.
2322
+
2323
+ INPUT:
2324
+
2325
+ - ``G`` -- a Sage graph
2326
+
2327
+ - ``algorithm`` -- string (default: ``None``); algorithm to use among:
2328
+
2329
+ - ``'low'`` -- algorithm with time complexity in `O(nm)` and space
2330
+ complexity in `O(m)`. This implementation is currently valid only for
2331
+ simple (without loops or multiple edges) connected graphs.
2332
+
2333
+ - ``'high'`` -- algorithm with time complexity in `O(nm)` and space
2334
+ complexity in `O(n^2)`. It cannot be used on graphs with more than
2335
+ `65536 = 2^{16}` vertices.
2336
+
2337
+ By default (``None``), the ``'low'`` algorithm is used for graphs and the
2338
+ ``'high'`` algorithm for digraphs.
2339
+
2340
+ EXAMPLES:
2341
+
2342
+ True for any connected graph [KRG1996]_::
2343
+
2344
+ sage: from sage.graphs.distances_all_pairs import szeged_index
2345
+ sage: g = graphs.PetersenGraph()
2346
+ sage: g.wiener_index() <= szeged_index(g)
2347
+ True
2348
+
2349
+ True for all trees [KRG1996]_::
2350
+
2351
+ sage: g = Graph()
2352
+ sage: g.add_edges(graphs.CubeGraph(5).min_spanning_tree())
2353
+ sage: g.wiener_index() == szeged_index(g)
2354
+ True
2355
+
2356
+ Check that both algorithms return same value::
2357
+
2358
+ sage: # long time, needs networkx
2359
+ sage: G = graphs.RandomBarabasiAlbert(100, 2)
2360
+ sage: a = szeged_index(G, algorithm='low')
2361
+ sage: b = szeged_index(G, algorithm='high')
2362
+ sage: a == b
2363
+ True
2364
+
2365
+ The Szeged index of a directed circuit of order `n` is `(n-1)^2`::
2366
+
2367
+ sage: [digraphs.Circuit(n).szeged_index() for n in range(1, 8)]
2368
+ [0, 1, 4, 9, 16, 25, 36]
2369
+
2370
+ TESTS:
2371
+
2372
+ Not defined when the graph is not connected (:issue:`26803`)::
2373
+
2374
+ sage: szeged_index(Graph({0: [1], 2: []}))
2375
+ Traceback (most recent call last):
2376
+ ...
2377
+ ValueError: the Szeged index is defined for connected graphs only
2378
+
2379
+ Directed graphs must be strongly connected::
2380
+
2381
+ sage: szeged_index(digraphs.Path(2))
2382
+ Traceback (most recent call last):
2383
+ ...
2384
+ ValueError: the Szeged index is defined for strongly connected digraphs only
2385
+
2386
+ Wrong name of algorithm::
2387
+
2388
+ sage: szeged_index(Graph(1), algorithm='wheel')
2389
+ Traceback (most recent call last):
2390
+ ...
2391
+ ValueError: unknown algorithm 'wheel'
2392
+
2393
+ Algorithm `"low"` is for graphs without loops or multiple edges::
2394
+
2395
+ sage: szeged_index(Graph([(0, 0)], loops=True), algorithm='low')
2396
+ Traceback (most recent call last):
2397
+ ...
2398
+ ValueError: the 'low' algorithm is for simple connected undirected graphs only
2399
+ sage: szeged_index(Graph([(0, 1), (0, 1)], multiedges=True), algorithm='low')
2400
+ Traceback (most recent call last):
2401
+ ...
2402
+ ValueError: the 'low' algorithm is for simple connected undirected graphs only
2403
+ sage: szeged_index(digraphs.Circuit(3), algorithm='low')
2404
+ Traceback (most recent call last):
2405
+ ...
2406
+ ValueError: the 'low' algorithm cannot be used on digraphs
2407
+
2408
+ Immutable graphs::
2409
+
2410
+ sage: G = graphs.RandomGNP(10, .3)
2411
+ sage: G.add_edges(graphs.RandomTree(10).edges())
2412
+ sage: G._backend
2413
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
2414
+ sage: H = Graph(G, immutable=True)
2415
+ sage: H._backend
2416
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
2417
+ sage: szeged_index(G) == szeged_index(H)
2418
+ True
2419
+ """
2420
+ if not G.is_connected():
2421
+ raise ValueError("the Szeged index is defined for connected graphs only")
2422
+ if G.is_directed() and not G.is_strongly_connected():
2423
+ raise ValueError("the Szeged index is defined for "
2424
+ "strongly connected digraphs only")
2425
+ if G.is_directed() and algorithm == "low":
2426
+ raise ValueError("the 'low' algorithm cannot be used on digraphs")
2427
+
2428
+ if algorithm is None:
2429
+ algorithm = "high" if G.is_directed() else "low"
2430
+
2431
+ elif algorithm not in ["low", "high"]:
2432
+ raise ValueError(f"unknown algorithm '{algorithm}'")
2433
+
2434
+ if algorithm == "low" and (G.has_loops() or G.has_multiple_edges()):
2435
+ raise ValueError("the 'low' algorithm is for simple connected "
2436
+ "undirected graphs only")
2437
+
2438
+ if G.order() <= 1:
2439
+ return 0
2440
+
2441
+ cdef StaticSparseCGraph cg
2442
+ cdef short_digraph sd
2443
+ if isinstance(G, StaticSparseBackend):
2444
+ cg = <StaticSparseCGraph> G._cg
2445
+ sd = <short_digraph> cg.g
2446
+ else:
2447
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G))
2448
+
2449
+ cdef uint64_t s
2450
+
2451
+ if algorithm == "low":
2452
+ s = c_szeged_index_low_memory(sd)
2453
+ else:
2454
+ s = c_szeged_index_high_memory(sd)
2455
+
2456
+ if not isinstance(G, StaticSparseBackend):
2457
+ free_short_digraph(sd)
2458
+ return s
2459
+
2460
+
2461
+ ##########################
2462
+ # Distances distribution #
2463
+ ##########################
2464
+
2465
+ def distances_distribution(G):
2466
+ r"""
2467
+ Return the distances distribution of the (di)graph in a dictionary.
2468
+
2469
+ This method *ignores all edge labels*, so that the distance considered is
2470
+ the topological distance.
2471
+
2472
+ OUTPUT:
2473
+
2474
+ A dictionary ``d`` such that the number of pairs of vertices at distance
2475
+ ``k`` (if any) is equal to `d[k] \cdot |V(G)| \cdot (|V(G)|-1)`.
2476
+
2477
+ .. NOTE::
2478
+
2479
+ We consider that two vertices that do not belong to the same connected
2480
+ component are at infinite distance, and we do not take the trivial pairs
2481
+ of vertices `(v, v)` at distance `0` into account. Empty (di)graphs and
2482
+ (di)graphs of order 1 have no paths and so we return the empty
2483
+ dictionary ``{}``.
2484
+
2485
+ EXAMPLES:
2486
+
2487
+ An empty Graph::
2488
+
2489
+ sage: g = Graph()
2490
+ sage: g.distances_distribution()
2491
+ {}
2492
+
2493
+ A Graph of order 1::
2494
+
2495
+ sage: g = Graph()
2496
+ sage: g.add_vertex(1)
2497
+ sage: g.distances_distribution()
2498
+ {}
2499
+
2500
+ A Graph of order 2 without edge::
2501
+
2502
+ sage: g = Graph()
2503
+ sage: g.add_vertices([1,2])
2504
+ sage: g.distances_distribution()
2505
+ {+Infinity: 1}
2506
+
2507
+ The Petersen Graph::
2508
+
2509
+ sage: g = graphs.PetersenGraph()
2510
+ sage: g.distances_distribution()
2511
+ {1: 1/3, 2: 2/3}
2512
+
2513
+ A graph with multiple disconnected components::
2514
+
2515
+ sage: g = graphs.PetersenGraph()
2516
+ sage: g.add_edge('good','wine')
2517
+ sage: g.distances_distribution()
2518
+ {1: 8/33, 2: 5/11, +Infinity: 10/33}
2519
+
2520
+ The de Bruijn digraph dB(2,3)::
2521
+
2522
+ sage: D = digraphs.DeBruijn(2,3) # needs sage.combinat
2523
+ sage: D.distances_distribution() # needs sage.combinat
2524
+ {1: 1/4, 2: 11/28, 3: 5/14}
2525
+
2526
+ TESTS:
2527
+
2528
+ Immutable graphs::
2529
+
2530
+ sage: G = graphs.RandomGNP(10, .5)
2531
+ sage: G._backend
2532
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
2533
+ sage: H = Graph(G, immutable=True)
2534
+ sage: H._backend
2535
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
2536
+ sage: G.distances_distribution() == H.distances_distribution()
2537
+ True
2538
+ """
2539
+ cdef size_t n = G.order()
2540
+ if n <= 1:
2541
+ return {}
2542
+
2543
+ cdef StaticSparseCGraph cg
2544
+ cdef short_digraph sd
2545
+ if isinstance(G, StaticSparseBackend):
2546
+ cg = <StaticSparseCGraph> G._cg
2547
+ sd = <short_digraph> cg.g
2548
+ else:
2549
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=list(G))
2550
+
2551
+ cdef MemoryAllocator mem = MemoryAllocator()
2552
+ cdef uint32_t * distances = <uint32_t *> mem.allocarray(2 * n, sizeof(uint32_t))
2553
+ cdef uint32_t * waiting_list = distances + n
2554
+ cdef uint64_t * count = <uint64_t *> mem.calloc(n, sizeof(uint64_t))
2555
+ cdef bitset_t seen
2556
+ bitset_init(seen, n)
2557
+
2558
+ # We count the number of pairs at equal distances
2559
+ cdef uint32_t u, v
2560
+ cdef uint64_t count_inf = 0
2561
+ for u in range(n):
2562
+ ecc = simple_BFS(sd, u, distances, NULL, waiting_list, seen)
2563
+ if ecc == UINT32_MAX:
2564
+ for v in range(n):
2565
+ if bitset_in(seen, v):
2566
+ count[distances[v]] += 1
2567
+ count_inf += n - bitset_len(seen)
2568
+ else:
2569
+ for v in range(n):
2570
+ count[distances[v]] += 1
2571
+
2572
+ if not isinstance(G, StaticSparseBackend):
2573
+ free_short_digraph(sd)
2574
+ bitset_free(seen)
2575
+
2576
+ from sage.rings.infinity import Infinity
2577
+ from sage.rings.rational_field import QQ
2578
+
2579
+ # We normalize the distribution
2580
+ cdef uint64_t NN = n * (n - 1)
2581
+ cdef dict distr = {+Infinity: QQ((count_inf, NN))} if count_inf else {}
2582
+ cdef size_t d
2583
+ for d in range(1, n):
2584
+ if count[d]:
2585
+ distr[d] = QQ((count[d], NN))
2586
+
2587
+ return distr
2588
+
2589
+
2590
+ ###################
2591
+ # Antipodal graph #
2592
+ ###################
2593
+
2594
+ def antipodal_graph(G):
2595
+ r"""
2596
+ Return the antipodal graph of `G`.
2597
+
2598
+ The antipodal graph of a graph `G` has the same vertex set of `G` and
2599
+ two vertices are adjacent if their distance in `G` is equal to the
2600
+ diameter of `G`.
2601
+
2602
+ This method first computes the eccentricity of all vertices and determines
2603
+ the diameter of the graph. Then, it for each vertex `u` with eccentricity
2604
+ the diameter, it computes BFS distances from `u` and add an edge in the
2605
+ antipodal graph for each vertex `v` at diameter distance from `u`
2606
+ (i.e., for each antipodal vertex).
2607
+
2608
+ The drawback of this method is that some BFS distances may be computed
2609
+ twice, one time to determine the eccentricities and another time is the
2610
+ vertex has eccentricity equal to the diameter. However, in practive, this is
2611
+ much more efficient. See the documentation of method
2612
+ :meth:`c_eccentricity_DHV`.
2613
+
2614
+ EXAMPLES:
2615
+
2616
+ The antipodal graph of a grid graph has only 2 edges::
2617
+
2618
+ sage: from sage.graphs.distances_all_pairs import antipodal_graph
2619
+ sage: G = graphs.Grid2dGraph(5, 5)
2620
+ sage: A = antipodal_graph(G)
2621
+ sage: A.order(), A.size()
2622
+ (25, 2)
2623
+
2624
+ The antipodal graph of a disjoint union of cliques is its complement::
2625
+
2626
+ sage: from sage.graphs.distances_all_pairs import antipodal_graph
2627
+ sage: G = graphs.CompleteGraph(3) * 3
2628
+ sage: A = antipodal_graph(G)
2629
+ sage: A.is_isomorphic(G.complement())
2630
+ True
2631
+
2632
+ The antipodal graph can also be constructed as the
2633
+ :meth:`sage.graphs.generic_graph.distance_graph` for diameter distance::
2634
+
2635
+ sage: from sage.graphs.distances_all_pairs import antipodal_graph
2636
+ sage: G = graphs.RandomGNP(10, .2)
2637
+ sage: A = antipodal_graph(G)
2638
+ sage: B = G.distance_graph(G.diameter())
2639
+ sage: A.is_isomorphic(B)
2640
+ True
2641
+
2642
+ TESTS::
2643
+
2644
+ sage: from sage.graphs.distances_all_pairs import antipodal_graph
2645
+ sage: antipodal_graph(Graph())
2646
+ Traceback (most recent call last):
2647
+ ...
2648
+ ValueError: the antipodal graph of the empty graph is not defined
2649
+ sage: antipodal_graph(DiGraph(1))
2650
+ Traceback (most recent call last):
2651
+ ...
2652
+ ValueError: this method is defined for undirected graphs only
2653
+ sage: antipodal_graph(Graph(1))
2654
+ Antipodal graph of Graph on 1 vertex: Looped graph on 1 vertex
2655
+ sage: antipodal_graph(Graph(2)).edges(sort=True, labels=False)
2656
+ [(0, 1)]
2657
+
2658
+ Immutable graphs::
2659
+
2660
+ sage: G = graphs.RandomGNP(10, .5)
2661
+ sage: G._backend
2662
+ <sage.graphs.base.sparse_graph.SparseGraphBackend ...>
2663
+ sage: H = Graph(G, immutable=True)
2664
+ sage: H._backend
2665
+ <sage.graphs.base.static_sparse_backend.StaticSparseBackend ...>
2666
+ sage: antipodal_graph(G).is_isomorphic(antipodal_graph(H))
2667
+ True
2668
+ """
2669
+ if not G:
2670
+ raise ValueError("the antipodal graph of the empty graph is not defined")
2671
+ if G.is_directed():
2672
+ raise ValueError("this method is defined for undirected graphs only")
2673
+
2674
+ from sage.graphs.graph import Graph
2675
+
2676
+ cdef uint32_t n = G.order()
2677
+ name = f"Antipodal graph of {G}"
2678
+ if n == 1:
2679
+ return Graph(list(zip(G, G)), loops=True, name=name)
2680
+
2681
+ import copy
2682
+ A = Graph(name=name, pos=copy.deepcopy(G.get_pos()))
2683
+
2684
+ if not G.is_connected():
2685
+ import itertools
2686
+ CC = G.connected_components(sort=False)
2687
+ for c1, c2 in itertools.combinations(CC, 2):
2688
+ A.add_edges(itertools.product(c1, c2))
2689
+ return A
2690
+
2691
+ cdef list int_to_vertex
2692
+ cdef StaticSparseCGraph cg
2693
+ cdef short_digraph sd
2694
+ if isinstance(G, StaticSparseBackend):
2695
+ cg = <StaticSparseCGraph> G._cg
2696
+ sd = <short_digraph> cg.g
2697
+ int_to_vertex = cg._vertex_to_labels
2698
+ else:
2699
+ int_to_vertex = list(G)
2700
+ init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex)
2701
+
2702
+ cdef MemoryAllocator mem = MemoryAllocator()
2703
+ cdef uint32_t * distances = <uint32_t *> mem.allocarray(2 * n, sizeof(uint32_t))
2704
+ cdef uint32_t * waiting_list = distances + n
2705
+ cdef bitset_t seen
2706
+ bitset_init(seen, n)
2707
+
2708
+ # Get the eccentricity of all vertices
2709
+ cdef uint32_t* ecc = c_eccentricity_DHV(sd)
2710
+ cdef uint32_t i
2711
+ cdef uint32_t diam = 0
2712
+ for i in range(n):
2713
+ if ecc[i] > diam:
2714
+ diam = ecc[i]
2715
+
2716
+ cdef uint32_t ui, vj, j
2717
+ for ui in range(n):
2718
+ if ecc[ui] == diam:
2719
+ _ = simple_BFS(sd, ui, distances, NULL, waiting_list, seen)
2720
+ u = int_to_vertex[ui]
2721
+ j = n - 1
2722
+ while distances[waiting_list[j]] == diam:
2723
+ vj = waiting_list[j]
2724
+ if ui < vj: # avoid adding twice the same edge
2725
+ A.add_edge(u, int_to_vertex[vj])
2726
+ j -= 1
2727
+
2728
+ if not isinstance(G, StaticSparseBackend):
2729
+ free_short_digraph(sd)
2730
+ bitset_free(seen)
2731
+
2732
+ A.add_vertices(G)
2733
+ return A
2734
+
2735
+
2736
+ ##################
2737
+ # Floyd-Warshall #
2738
+ ##################
2739
+
2740
+ def floyd_warshall(gg, paths=True, distances=False):
2741
+ r"""
2742
+ Compute the shortest path/distances between all pairs of vertices.
2743
+
2744
+ For more information on the Floyd-Warshall algorithm, see
2745
+ the :wikipedia:`Floyd-Warshall_algorithm`.
2746
+
2747
+ INPUT:
2748
+
2749
+ - ``gg`` -- the graph on which to work
2750
+
2751
+ - ``paths`` -- boolean (default: ``True``); whether to return the dictionary
2752
+ of shortest paths
2753
+
2754
+ - ``distances`` -- boolean (default: ``False``); whether to return the
2755
+ dictionary of distances
2756
+
2757
+ OUTPUT:
2758
+
2759
+ Depending on the input, this function return the dictionary of paths, the
2760
+ dictionary of distances, or a pair of dictionaries ``(distances, paths)``
2761
+ where ``distance[u][v]`` denotes the distance of a shortest path from `u` to
2762
+ `v` and ``paths[u][v]`` denotes an inneighbor `w` of `v` such that
2763
+ `dist(u,v) = 1 + dist(u,w)`.
2764
+
2765
+ .. WARNING::
2766
+
2767
+ Because this function works on matrices whose size is quadratic compared
2768
+ to the number of vertices, it uses short variables instead of long ones
2769
+ to divide by 2 the size in memory. This means that the current
2770
+ implementation does not run on a graph of more than 65536 nodes (this
2771
+ can be easily changed if necessary, but would require much more
2772
+ memory. It may be worth writing two versions). For information, the
2773
+ current version of the algorithm on a graph with `65536 = 2^{16}` nodes
2774
+ creates in memory `2` tables on `2^{32}` short elements (2bytes each),
2775
+ for a total of `2^{34}` bytes or `16` gigabytes. Let us also remember
2776
+ that if the memory size is quadratic, the algorithm runs in cubic time.
2777
+
2778
+ .. NOTE::
2779
+
2780
+ When ``paths = False`` the algorithm saves roughly half of the memory as
2781
+ it does not have to maintain the matrix of predecessors. However,
2782
+ setting ``distances=False`` produces no such effect as the algorithm can
2783
+ not run without computing them. They will not be returned, but they will
2784
+ be stored while the method is running.
2785
+
2786
+ EXAMPLES:
2787
+
2788
+ Shortest paths in a small grid ::
2789
+
2790
+ sage: g = graphs.Grid2dGraph(2,2)
2791
+ sage: from sage.graphs.distances_all_pairs import floyd_warshall
2792
+ sage: print(floyd_warshall(g))
2793
+ {(0, 0): {(0, 0): None, (0, 1): (0, 0), (1, 0): (0, 0), (1, 1): (0, 1)},
2794
+ (0, 1): {(0, 1): None, (0, 0): (0, 1), (1, 0): (0, 0), (1, 1): (0, 1)},
2795
+ (1, 0): {(1, 0): None, (0, 0): (1, 0), (0, 1): (0, 0), (1, 1): (1, 0)},
2796
+ (1, 1): {(1, 1): None, (0, 0): (0, 1), (0, 1): (1, 1), (1, 0): (1, 1)}}
2797
+
2798
+ Checking the distances are correct ::
2799
+
2800
+ sage: g = graphs.Grid2dGraph(5,5)
2801
+ sage: dist,path = floyd_warshall(g, distances=True)
2802
+ sage: all(dist[u][v] == g.distance(u, v) for u in g for v in g)
2803
+ True
2804
+
2805
+ Checking a random path is valid ::
2806
+
2807
+ sage: u,v = g.random_vertex(), g.random_vertex()
2808
+ sage: p = [v]
2809
+ sage: while p[0] is not None:
2810
+ ....: p.insert(0,path[u][p[0]])
2811
+ sage: len(p) == dist[u][v] + 2
2812
+ True
2813
+
2814
+ Distances for all pairs of vertices in a diamond::
2815
+
2816
+ sage: g = graphs.DiamondGraph()
2817
+ sage: floyd_warshall(g, paths=False, distances=True)
2818
+ {0: {0: 0, 1: 1, 2: 1, 3: 2},
2819
+ 1: {0: 1, 1: 0, 2: 1, 3: 1},
2820
+ 2: {0: 1, 1: 1, 2: 0, 3: 1},
2821
+ 3: {0: 2, 1: 1, 2: 1, 3: 0}}
2822
+
2823
+ TESTS:
2824
+
2825
+ Too large graphs::
2826
+
2827
+ sage: from sage.graphs.distances_all_pairs import floyd_warshall
2828
+ sage: floyd_warshall(Graph(65536))
2829
+ Traceback (most recent call last):
2830
+ ...
2831
+ ValueError: the graph backend contains more than 65535 nodes
2832
+ """
2833
+ from sage.rings.infinity import Infinity
2834
+ cdef CGraph g = <CGraph> gg._backend.c_graph()[0]
2835
+
2836
+ cdef list gverts = g.verts()
2837
+
2838
+ if not gverts:
2839
+ if distances and paths:
2840
+ return {}, {}
2841
+ return {}
2842
+
2843
+ cdef unsigned int n = max(gverts) + 1
2844
+
2845
+ if n >= <unsigned short> -1:
2846
+ raise ValueError("the graph backend contains more than " + str(<unsigned short> -1) + " nodes")
2847
+
2848
+ # All this just creates two tables prec[n][n] and dist[n][n]
2849
+ cdef MemoryAllocator mem = MemoryAllocator()
2850
+ cdef unsigned short* t_prec = NULL
2851
+ cdef unsigned short** prec = NULL
2852
+ # init dist
2853
+ cdef unsigned short* t_dist = <unsigned short*> mem.allocarray(n * n, sizeof(unsigned short))
2854
+ cdef unsigned short** dist = <unsigned short**> mem.allocarray(n, sizeof(unsigned short*))
2855
+ dist[0] = t_dist
2856
+ cdef unsigned int i
2857
+ for i in range(1, n):
2858
+ dist[i] = dist[i - 1] + n
2859
+ memset(t_dist, -1, n * n * sizeof(short))
2860
+
2861
+ cdef int v_int
2862
+ cdef int u_int
2863
+ cdef int w_int
2864
+
2865
+ # Copying the adjacency matrix (vertices at distance 1)
2866
+ for v_int in gverts:
2867
+ dist[v_int][v_int] = 0
2868
+ for u_int in g.out_neighbors(v_int):
2869
+ dist[v_int][u_int] = 1
2870
+
2871
+ if paths:
2872
+ # init prec
2873
+ t_prec = <unsigned short*> mem.allocarray(n * n, sizeof(unsigned short))
2874
+ prec = <unsigned short**> mem.allocarray(n, sizeof(unsigned short*))
2875
+ prec[0] = t_prec
2876
+ for i in range(1, n):
2877
+ prec[i] = prec[i - 1] + n
2878
+ memset(t_prec, 0, n * n * sizeof(short))
2879
+ # Copying the adjacency matrix (vertices at distance 1)
2880
+ for v_int in gverts:
2881
+ prec[v_int][v_int] = v_int
2882
+ for u_int in g.out_neighbors(v_int):
2883
+ prec[v_int][u_int] = v_int
2884
+
2885
+ # The algorithm itself.
2886
+ cdef unsigned short* dv
2887
+ cdef unsigned short* dw
2888
+ cdef int dvw
2889
+ cdef int val
2890
+
2891
+ for w_int in gverts:
2892
+ dw = dist[w_int]
2893
+ for v_int in gverts:
2894
+ dv = dist[v_int]
2895
+ dvw = dv[w_int]
2896
+ for u_int in gverts:
2897
+ val = dvw + dw[u_int]
2898
+ # If it is shorter to go from u to v through w, do it
2899
+ if dv[u_int] > val:
2900
+ dv[u_int] = val
2901
+ if paths:
2902
+ prec[v_int][u_int] = prec[w_int][u_int]
2903
+
2904
+ # Dictionaries of distance, precedent element, and integers
2905
+ cdef dict d_prec = {}
2906
+ cdef dict d_dist = {}
2907
+ cdef dict tmp_prec
2908
+ cdef dict tmp_dist
2909
+
2910
+ cdef CGraphBackend cgb = <CGraphBackend> gg._backend
2911
+
2912
+ for v_int in gverts:
2913
+ v = cgb.vertex_label(v_int)
2914
+ if paths:
2915
+ tmp_prec = {v: None}
2916
+ if distances:
2917
+ tmp_dist = {v: 0}
2918
+ dv = dist[v_int]
2919
+ for u_int in gverts:
2920
+ u = cgb.vertex_label(u_int)
2921
+ if v != u and dv[u_int] != <unsigned short> -1:
2922
+ if paths:
2923
+ tmp_prec[u] = cgb.vertex_label(prec[v_int][u_int])
2924
+
2925
+ if distances:
2926
+ tmp_dist[u] = dv[u_int]
2927
+
2928
+ if paths:
2929
+ d_prec[v] = tmp_prec
2930
+ if distances:
2931
+ d_dist[v] = tmp_dist
2932
+
2933
+ if distances and paths:
2934
+ return d_dist, d_prec
2935
+ if paths:
2936
+ return d_prec
2937
+ if distances:
2938
+ return d_dist