Path: blob/master/src/sage/graphs/distances_all_pairs.pyx
8815 views
r"""1Distances/shortest paths between all pairs of vertices23This module implements a few functions that deal with the computation of4distances or shortest paths between all pairs of vertices.56**Efficiency** : Because these functions involve listing many times the7(out)-neighborhoods of (di)-graphs, it is useful in terms of efficiency to build8a temporary copy of the graph in a data structure that makes it easy to compute9quickly. These functions also work on large volume of data, typically dense10matrices of size `n^2`, and are expected to return corresponding dictionaries of11size `n^2`, where the integers corresponding to the vertices have first been12converted to the vertices' labels. Sadly, this last translating operation turns13out to be the most time-consuming, and for this reason it is also nice to have a14Cython module, and version of these functions that return C arrays, in order to15avoid these operations when they are not necessary.1617**Memory cost** : The methods implemented in the current module sometimes need large18amounts of memory to return their result. Storing the distances between all19pairs of vertices in a graph on `1500` vertices as a dictionary of dictionaries20takes around 200MB, while storing the same information as a C array requires214MB.222324The module's main function25--------------------------2627The C function ``all_pairs_shortest_path_BFS`` actually does all the28computations, and all the others (except for ``Floyd_Warshall``) are just29wrapping it. This function begins with copying the graph in a data structure30that makes it fast to query the out-neighbors of a vertex, then starts one31Breadth First Search per vertex of the (di)graph.3233**What can this function compute ?**3435- The matrix of predecessors.3637This matrix `P` has size `n^2`, and is such that vertex `P[u,v]` is a38predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently39encodes the information of a shortest `uv`-path for any `u,v\in G` :40indeed, to go from `u` to `v` you should first find a shortest41`uP[u,v]`-path, then jump from `P[u,v]` to `v` as it is one of its42outneighbors. Apply recursively and find out what the whole path is !.4344- The matrix of distances.4546This matrix has size `n^2` and associates to any `uv` the distance from47`u` to `v`.4849- The vector of eccentricities.5051This vector of size `n` encodes for each vertex `v` the distance to vertex52which is furthest from `v` in the graph. In particular, the diameter of53the graph is the maximum of these values.5455**What does it take as input ?**5657- ``gg`` a (Di)Graph.5859- ``unsigned short * predecessors`` -- a pointer toward an array of size60`n^2\cdot\text{sizeof(unsigned short)}`. Set to ``NULL`` if you do not61want to compute the predecessors.6263- ``unsigned short * distances`` -- a pointer toward an array of size64`n^2\cdot\text{sizeof(unsigned short)}`. The computation of the distances65is necessary for the algorithm, so this value can **not** be set to66``NULL``.6768- ``int * eccentricity`` -- a pointer toward an array of size69`n\cdot\text{sizeof(int)}`. Set to ``NULL`` if you do not want to compute70the eccentricity.7172**Technical details**7374- The vertices are encoded as `1, ..., n` as they appear in the ordering of75``G.vertices()``.7677- Because this function works on matrices whose size is quadratic compared78to the number of vertices when computing all distances or predecessors, it79uses short variables to store the vertices' names instead of long ones to80divide by 2 the size in memory. This means that only the81diameter/eccentricities can be computed on a graph of more than 6553682nodes. For information, the current version of the algorithm on a graph83with `65536=2^{16}` nodes creates in memory `2` tables on `2^{32}` short84elements (2bytes each), for a total of `2^{33}` bytes or `8` gigabytes. In85order to support larger sizes, we would have to replace shorts by 32-bits86int or 64-bits int, which would then require respectively 16GB or 32GB.8788- In the C version of these functions, infinite distances are represented89with ``<unsigned short> -1 = 65535`` for ``unsigned short`` variables, and90by ``INT32_MAX`` otherwise. These case happens when the input is a91disconnected graph, or a non-strongly-connected digraph.9293- A memory error is raised when data structures allocation failed. This94could happen with large graphs on computers with low memory space.9596.. WARNING::9798The function ``all_pairs_shortest_path_BFS`` has **no reason** to be99called by the user, even though he would be writing his code in Cython100and look for efficiency. This module contains wrappers for this function101that feed it with the good parameters. As the function is inlined, using102those wrappers actually saves time as it should avoid testing the103parameters again and again in the main function's body.104105AUTHOR:106107- Nathann Cohen (2011)108109REFERENCE:110111.. [KRG96b] S. Klavzar, A. Rajapakse, and I. Gutman. The Szeged and the112Wiener index of graphs. *Applied Mathematics Letters*, 9(5):45--49, 1996.113114.. [GYLL93c] I. Gutman, Y.-N. Yeh, S.-L. Lee, and Y.-L. Luo. Some recent115results in the theory of the Wiener number. *Indian Journal of116Chemistry*, 32A:651--661, 1993.117118Functions119---------120"""121122##############################################################################123# Copyright (C) 2011 Nathann Cohen <[email protected]>124# Distributed under the terms of the GNU General Public License (GPL)125# The full text of the GPL is available at:126# http://www.gnu.org/licenses/127##############################################################################128129include "sage/misc/bitset.pxi"130from libc.stdint cimport uint64_t, uint32_t, INT32_MAX131from sage.graphs.base.c_graph cimport CGraph132from sage.graphs.base.c_graph cimport vertex_label133from sage.graphs.base.c_graph cimport get_vertex134135from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph136137138cdef inline all_pairs_shortest_path_BFS(gg,139unsigned short * predecessors,140unsigned short * distances,141int * eccentricity):142"""143See the module's documentation.144"""145146from sage.rings.infinity import Infinity147148cdef CGraph cg = <CGraph> gg._backend._cg149150cdef list int_to_vertex = gg.vertices()151cdef int i152153cdef int n = len(int_to_vertex)154155# Computing the predecessors/distances can only be done if we have less than156# MAX_UNSIGNED_SHORT vertices. No problem with the eccentricities though as157# we store them on an integer vector.158if (predecessors != NULL or distances != NULL) and n > <unsigned short> -1:159raise ValueError("The graph backend contains more than "+160str(<unsigned short> -1)+" nodes and we cannot "+161"compute the matrix of distances/predecessors on "+162"something like that !")163164# The vertices which have already been visited165cdef bitset_t seen166bitset_init(seen, n)167168# The list of waiting vertices, the beginning and the end of the list169cdef int * waiting_list = <int *> sage_malloc(n*sizeof(int))170if waiting_list == NULL:171raise MemoryError()172cdef int waiting_beginning = 0173cdef int waiting_end = 0174175cdef int source176cdef int v, u177cdef uint32_t * p_tmp178cdef uint32_t * end179180cdef unsigned short * c_predecessors = predecessors181cdef int * c_eccentricity = eccentricity182cdef int * c_distances = <int *> sage_malloc( n * sizeof(int))183if c_distances == NULL:184sage_free(waiting_list)185raise MemoryError()186187# Copying the whole graph to obtain the list of neighbors quicker than by188# calling out_neighbors189190# The edges are stored in the vector p_edges. This vector contains, from191# left to right The list of the first vertex's outneighbors, then the192# second's, then the third's, ...193#194# The outneighbors of vertex i are enumerated from195#196# p_vertices[i] to p_vertices[i+1] - 1197# (if p_vertices[i] is equal to p_vertices[i+1], then i has no outneighbours)198#199# This data structure is well documented in the module200# sage.graphs.base.static_sparse_graph201202cdef short_digraph sd203init_short_digraph(sd, gg)204cdef uint32_t ** p_vertices = sd.neighbors205cdef uint32_t * p_edges = sd.edges206cdef uint32_t * p_next = p_edges207208# We run n different BFS taking each vertex as a source209for source in range(n):210211# The source is seen212bitset_set_first_n(seen, 0)213bitset_add(seen, source)214215# Its parameters can already be set216c_distances[source] = 0217218if predecessors != NULL:219c_predecessors[source] = source220221# and added to the queue222waiting_list[0] = source223waiting_beginning = 0224waiting_end = 0225226# For as long as there are vertices left to explore227while waiting_beginning <= waiting_end:228229# We pick the first one230v = waiting_list[waiting_beginning]231232p_tmp = p_vertices[v]233end = p_vertices[v+1]234235# Iterating over all the outneighbors u of v236while p_tmp < end:237u = p_tmp[0]238239# If we notice one of these neighbors is not seen yet, we set240# its parameters and add it to the queue to be explored later.241if not bitset_in(seen, u):242c_distances[u] = c_distances[v]+1243if predecessors != NULL:244c_predecessors[u] = v245bitset_add(seen, u)246waiting_end += 1247waiting_list[waiting_end] = u248249p_tmp += 1250251waiting_beginning += 1252253# If not all the vertices have been met254for v in range(n):255if not bitset_in(seen, v):256c_distances[v] = INT32_MAX257if predecessors != NULL:258c_predecessors[v] = -1259260if predecessors != NULL:261c_predecessors += n262263if eccentricity != NULL:264c_eccentricity[source] = 0265for i in range(n):266c_eccentricity[source] = c_eccentricity[source] if c_eccentricity[source] >= c_distances[i] else c_distances[i]267268if distances != NULL:269for i in range(n):270distances[i] = <unsigned short> c_distances[i]271distances += n272273bitset_free(seen)274sage_free(waiting_list)275free_short_digraph(sd)276sage_free(c_distances)277278################279# Predecessors #280################281282cdef unsigned short * c_shortest_path_all_pairs(G) except NULL:283r"""284Returns the matrix of predecessors in G.285286The matrix `P` returned has size `n^2`, and is such that vertex `P[u,v]` is287a predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently288encodes the information of a shortest `uv`-path for any `u,v\in G` : indeed,289to go from `u` to `v` you should first find a shortest `uP[u,v]`-path, then290jump from `P[u,v]` to `v` as it is one of its outneighbors.291"""292293cdef unsigned int n = G.order()294cdef unsigned short * distances = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))295if distances == NULL:296raise MemoryError()297cdef unsigned short * predecessors = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))298if predecessors == NULL:299sage_free(distances)300raise MemoryError()301all_pairs_shortest_path_BFS(G, predecessors, distances, NULL)302303sage_free(distances)304305return predecessors306307def shortest_path_all_pairs(G):308r"""309Returns the matrix of predecessors in G.310311The matrix `P` returned has size `n^2`, and is such that vertex `P[u,v]` is312a predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently313encodes the information of a shortest `uv`-path for any `u,v\in G` : indeed,314to go from `u` to `v` you should first find a shortest `uP[u,v]`-path, then315jump from `P[u,v]` to `v` as it is one of its outneighbors.316317The integer corresponding to a vertex is its index in the list318``G.vertices()``.319320EXAMPLE::321322sage: from sage.graphs.distances_all_pairs import shortest_path_all_pairs323sage: g = graphs.PetersenGraph()324sage: shortest_path_all_pairs(g)325{0: {0: None, 1: 0, 2: 1, 3: 4, 4: 0, 5: 0, 6: 1, 7: 5, 8: 5, 9: 4},3261: {0: 1, 1: None, 2: 1, 3: 2, 4: 0, 5: 0, 6: 1, 7: 2, 8: 6, 9: 6},3272: {0: 1, 1: 2, 2: None, 3: 2, 4: 3, 5: 7, 6: 1, 7: 2, 8: 3, 9: 7},3283: {0: 4, 1: 2, 2: 3, 3: None, 4: 3, 5: 8, 6: 8, 7: 2, 8: 3, 9: 4},3294: {0: 4, 1: 0, 2: 3, 3: 4, 4: None, 5: 0, 6: 9, 7: 9, 8: 3, 9: 4},3305: {0: 5, 1: 0, 2: 7, 3: 8, 4: 0, 5: None, 6: 8, 7: 5, 8: 5, 9: 7},3316: {0: 1, 1: 6, 2: 1, 3: 8, 4: 9, 5: 8, 6: None, 7: 9, 8: 6, 9: 6},3327: {0: 5, 1: 2, 2: 7, 3: 2, 4: 9, 5: 7, 6: 9, 7: None, 8: 5, 9: 7},3338: {0: 5, 1: 6, 2: 3, 3: 8, 4: 3, 5: 8, 6: 8, 7: 5, 8: None, 9: 6},3349: {0: 4, 1: 6, 2: 7, 3: 4, 4: 9, 5: 7, 6: 9, 7: 9, 8: 6, 9: None}}335"""336337cdef int n = G.order()338339if n == 0:340return {}341342cdef unsigned short * predecessors = c_shortest_path_all_pairs(G)343cdef unsigned short * c_predecessors = predecessors344345cdef dict d = {}346cdef dict d_tmp347348cdef CGraph cg = <CGraph> G._backend._cg349350cdef list int_to_vertex = G.vertices()351cdef int i, j352353for i, l in enumerate(int_to_vertex):354int_to_vertex[i] = get_vertex(l, G._backend.vertex_ints, G._backend.vertex_labels, cg)355356for j in range(n):357d_tmp = {}358for i in range(n):359if c_predecessors[i] == <unsigned short> -1:360d_tmp[int_to_vertex[i]] = None361else:362d_tmp[int_to_vertex[i]] = int_to_vertex[c_predecessors[i]]363364d_tmp[int_to_vertex[j]] = None365d[int_to_vertex[j]] = d_tmp366367c_predecessors += n368369sage_free(predecessors)370return d371372#############373# Distances #374#############375376cdef unsigned short * c_distances_all_pairs(G):377r"""378Returns the matrix of distances in G.379380The matrix `M` returned is of length `n^2`, and the distance between381vertices `u` and `v` is `M[u,v]`. The integer corresponding to a vertex is382its index in the list ``G.vertices()``.383"""384385cdef unsigned int n = G.order()386cdef unsigned short * distances = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))387if distances == NULL:388raise MemoryError()389all_pairs_shortest_path_BFS(G, NULL, distances, NULL)390391return distances392393def distances_all_pairs(G):394r"""395Returns the matrix of distances in G.396397This function returns a double dictionary ``D`` of vertices, in which the398distance between vertices ``u`` and ``v`` is ``D[u][v]``.399400EXAMPLE::401402sage: from sage.graphs.distances_all_pairs import distances_all_pairs403sage: g = graphs.PetersenGraph()404sage: distances_all_pairs(g)405{0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2},4061: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2},4072: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2},4083: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2},4094: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1},4105: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2},4116: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1},4127: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1},4138: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2},4149: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}}415"""416417from sage.rings.infinity import Infinity418419cdef int n = G.order()420421if n == 0:422return {}423424cdef unsigned short * distances = c_distances_all_pairs(G)425cdef unsigned short * c_distances = distances426427cdef dict d = {}428cdef dict d_tmp429430cdef list int_to_vertex = G.vertices()431cdef int i, j432433for j in range(n):434d_tmp = {}435for i in range(n):436if c_distances[i] == <unsigned short> -1:437d_tmp[int_to_vertex[i]] = Infinity438else:439d_tmp[int_to_vertex[i]] = c_distances[i]440441d[int_to_vertex[j]] = d_tmp442c_distances += n443444sage_free(distances)445return d446447def is_distance_regular(G, parameters = False):448r"""449Tests if the graph is distance-regular450451A graph `G` is distance-regular if there exist integers `d_1,...,d_n` such452that for every vertex `v\in G` we have `d_i=\#\{u:d_G(u,v) =i\}`. Thus a453strongly-regular graph is also distance-regular, and a distance-regular454graph is necessarily regular too.455456For more information on distance-regular graphs, see its associated457:wikipedia:`wikipedia page <Distance-regular_graph>`.458459INPUT:460461- ``parameters`` (boolean) -- whether to replace ``True`` answers with a462dictionary associating `d_i` to an integer `i>0` if `d_i>0` (one can then463obtain `d_i` by doing ``dictionary.get(i,0)``). Set to ``False`` by464default.465466.. SEEALSO::467468* :meth:`~sage.graphs.generic_graph.GenericGraph.is_regular`469* :meth:`~Graph.is_strongly_regular`470471EXAMPLES::472473sage: g = graphs.PetersenGraph()474sage: g.is_distance_regular()475True476sage: g.is_distance_regular(parameters = True)477{1: 3, 2: 6}478479Cube graphs, which are not strongly regular, are a bit more interesting::480481sage: graphs.CubeGraph(4).is_distance_regular(parameters = True)482{1: 4, 2: 6, 3: 4, 4: 1}483484TESTS::485486sage: graphs.PathGraph(2).is_distance_regular(parameters = True)487{1: 1}488489"""490cdef int i,l491cdef int n = G.order()492493if n <= 1:494return {} if parameters else True495496if not G.is_regular():497return False498499cdef unsigned short * distance_matrix = c_distances_all_pairs(G)500501# - d_array is the vector of d_i corresponding to the first vertex502#503# - d_tmp is a vector that we use to check that d_array is the same for504# every vertex v505cdef unsigned short * d_array = <unsigned short *> sage_calloc(2*n, sizeof(unsigned short))506cdef unsigned short * d_tmp = d_array + n507508if d_array == NULL:509sage_free(distance_matrix)510raise MemoryError()511512# Filling d_array513cdef unsigned short * pointer = distance_matrix514for i in range(n):515if pointer[i] < n:516d_array[pointer[i]] += 1517pointer += n518519# For each of the n-1 other vertices520for l in range(1,n):521522# We set d_tmp and fill it with the data from the l^th row523memset(d_tmp, 0, n*sizeof(unsigned short))524for i in range(n):525if pointer[i] < n:526d_tmp[pointer[i]] += 1527528# If d_tmp != d_array, we are done529if memcmp(d_array, d_tmp, n*sizeof(unsigned short)) != 0:530sage_free(distance_matrix)531sage_free(d_array)532return False533534pointer += n535536cdef dict dict_parameters537if parameters:538dict_parameters = {i:d_array[i] for i in range(n) if i and d_array[i] > 0}539540sage_free(distance_matrix)541sage_free(d_array)542543if parameters:544return dict_parameters545else:546return True547548###################################549# Both distances and predecessors #550###################################551552def distances_and_predecessors_all_pairs(G):553r"""554Returns the matrix of distances in G and the matrix of predecessors.555556Distances : the matrix `M` returned is of length `n^2`, and the distance557between vertices `u` and `v` is `M[u,v]`. The integer corresponding to a558vertex is its index in the list ``G.vertices()``.559560Predecessors : the matrix `P` returned has size `n^2`, and is such that561vertex `P[u,v]` is a predecessor of `v` on a shortest `uv`-path. Hence, this562matrix efficiently encodes the information of a shortest `uv`-path for any563`u,v\in G` : indeed, to go from `u` to `v` you should first find a shortest564`uP[u,v]`-path, then jump from `P[u,v]` to `v` as it is one of its565outneighbors.566567The integer corresponding to a vertex is its index in the list568``G.vertices()``.569570571EXAMPLE::572573sage: from sage.graphs.distances_all_pairs import distances_and_predecessors_all_pairs574sage: g = graphs.PetersenGraph()575sage: distances_and_predecessors_all_pairs(g)576({0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2},5771: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2},5782: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2},5793: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2},5804: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1},5815: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2},5826: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1},5837: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1},5848: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2},5859: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}},586{0: {0: None, 1: 0, 2: 1, 3: 4, 4: 0, 5: 0, 6: 1, 7: 5, 8: 5, 9: 4},5871: {0: 1, 1: None, 2: 1, 3: 2, 4: 0, 5: 0, 6: 1, 7: 2, 8: 6, 9: 6},5882: {0: 1, 1: 2, 2: None, 3: 2, 4: 3, 5: 7, 6: 1, 7: 2, 8: 3, 9: 7},5893: {0: 4, 1: 2, 2: 3, 3: None, 4: 3, 5: 8, 6: 8, 7: 2, 8: 3, 9: 4},5904: {0: 4, 1: 0, 2: 3, 3: 4, 4: None, 5: 0, 6: 9, 7: 9, 8: 3, 9: 4},5915: {0: 5, 1: 0, 2: 7, 3: 8, 4: 0, 5: None, 6: 8, 7: 5, 8: 5, 9: 7},5926: {0: 1, 1: 6, 2: 1, 3: 8, 4: 9, 5: 8, 6: None, 7: 9, 8: 6, 9: 6},5937: {0: 5, 1: 2, 2: 7, 3: 2, 4: 9, 5: 7, 6: 9, 7: None, 8: 5, 9: 7},5948: {0: 5, 1: 6, 2: 3, 3: 8, 4: 3, 5: 8, 6: 8, 7: 5, 8: None, 9: 6},5959: {0: 4, 1: 6, 2: 7, 3: 4, 4: 9, 5: 7, 6: 9, 7: 9, 8: 6, 9: None}})596"""597598from sage.rings.infinity import Infinity599cdef unsigned int n = G.order()600601if n == 0:602return {}, {}603604cdef unsigned short * distances = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))605if distances == NULL:606raise MemoryError()607cdef unsigned short * c_distances = distances608cdef unsigned short * predecessor = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))609if predecessor == NULL:610sage_free(distances)611raise MemoryError()612cdef unsigned short * c_predecessor = predecessor613614all_pairs_shortest_path_BFS(G, predecessor, distances, NULL)615616617cdef dict d_distance = {}618cdef dict d_predecessor = {}619cdef dict t_distance = {}620cdef dict t_predecessor = {}621622cdef list int_to_vertex = G.vertices()623cdef int i, j624625for j in range(n):626t_distance = {}627t_predecessor = {}628629for i in range(n):630631if c_distances[i] == <unsigned short> -1:632t_distance[int_to_vertex[i]] = Infinity633t_predecessor[int_to_vertex[i]] = None634else:635t_distance[int_to_vertex[i]] = c_distances[i]636t_predecessor[int_to_vertex[i]] = int_to_vertex[c_predecessor[i]]637638t_predecessor[int_to_vertex[j]] = None639640d_distance[int_to_vertex[j]] = t_distance641d_predecessor[int_to_vertex[j]] = t_predecessor642643c_distances += n644c_predecessor += n645646sage_free(distances)647sage_free(predecessor)648649return d_distance, d_predecessor650651################652# Eccentricity #653################654655cdef int * c_eccentricity(G) except NULL:656r"""657Returns the vector of eccentricities in G.658659The array returned is of length n, and its ith component is the eccentricity660of the ith vertex in ``G.vertices()``.661"""662cdef unsigned int n = G.order()663664cdef int * ecc = <int *> sage_malloc(n*sizeof(int))665if ecc == NULL:666raise MemoryError()667all_pairs_shortest_path_BFS(G, NULL, NULL, ecc)668669return ecc670671def eccentricity(G):672r"""673Returns the vector of eccentricities in G.674675The array returned is of length n, and its ith component is the eccentricity676of the ith vertex in ``G.vertices()``.677678EXAMPLE::679680sage: from sage.graphs.distances_all_pairs import eccentricity681sage: g = graphs.PetersenGraph()682sage: eccentricity(g)683[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]684"""685from sage.rings.infinity import Infinity686cdef int n = G.order()687688if n == 0:689return []690691cdef int * ecc = c_eccentricity(G)692693cdef list l_ecc = []694cdef int i695for i in range(n):696if ecc[i] == INT32_MAX:697l_ecc.append(Infinity)698else:699l_ecc.append(ecc[i])700701sage_free(ecc)702703return l_ecc704705def diameter(G):706r"""707Returns the diameter of `G`.708709EXAMPLE::710711sage: from sage.graphs.distances_all_pairs import diameter712sage: g = graphs.PetersenGraph()713sage: diameter(g)7142715"""716return max(eccentricity(G))717718719################720# Wiener index #721################722723def wiener_index(G):724r"""725Returns the Wiener index of the graph.726727The Wiener index of a graph `G` can be defined in two equivalent728ways [KRG96b]_ :729730- `W(G) = \frac 1 2 \sum_{u,v\in G} d(u,v)` where `d(u,v)` denotes the731distance between vertices `u` and `v`.732733- Let `\Omega` be a set of `\frac {n(n-1)} 2` paths in `G` such that `\Omega`734contains exactly one shortest `u-v` path for each set `\{u,v\}` of735vertices in `G`. Besides, `\forall e\in E(G)`, let `\Omega(e)` denote the736paths from `\Omega` containing `e`. We then have737`W(G) = \sum_{e\in E(G)}|\Omega(e)|`.738739EXAMPLE:740741From [GYLL93c]_, cited in [KRG96b]_::742743sage: g=graphs.PathGraph(10)744sage: w=lambda x: (x*(x*x -1)/6)745sage: g.wiener_index()==w(10)746True747"""748if not G.is_connected():749from sage.rings.infinity import Infinity750return +Infinity751752from sage.rings.integer import Integer753cdef unsigned short * distances = c_distances_all_pairs(G)754cdef unsigned int NN = G.order()*G.order()755cdef unsigned int i756cdef uint64_t s = 0757for 0 <= i < NN:758s += distances[i]759sage_free(distances)760return Integer(s)/2761762##########################763# Distances distribution #764##########################765766def distances_distribution(G):767r"""768Returns the distances distribution of the (di)graph in a dictionary.769770This method *ignores all edge labels*, so that the distance considered is771the topological distance.772773OUTPUT:774775A dictionary ``d`` such that the number of pairs of vertices at distance776``k`` (if any) is equal to `d[k] \cdot |V(G)| \cdot (|V(G)|-1)`.777778.. NOTE::779780We consider that two vertices that do not belong to the same connected781component are at infinite distance, and we do not take the trivial pairs782of vertices `(v, v)` at distance `0` into account. Empty (di)graphs and783(di)graphs of order 1 have no paths and so we return the empty784dictionary ``{}``.785786EXAMPLES:787788An empty Graph::789790sage: g = Graph()791sage: g.distances_distribution()792{}793794A Graph of order 1::795796sage: g = Graph()797sage: g.add_vertex(1)798sage: g.distances_distribution()799{}800801A Graph of order 2 without edge::802803sage: g = Graph()804sage: g.add_vertices([1,2])805sage: g.distances_distribution()806{+Infinity: 1}807808The Petersen Graph::809810sage: g = graphs.PetersenGraph()811sage: g.distances_distribution()812{1: 1/3, 2: 2/3}813814A graph with multiple disconnected components::815816sage: g = graphs.PetersenGraph()817sage: g.add_edge('good','wine')818sage: g.distances_distribution()819{1: 8/33, 2: 5/11, +Infinity: 10/33}820821The de Bruijn digraph dB(2,3)::822823sage: D = digraphs.DeBruijn(2,3)824sage: D.distances_distribution()825{1: 1/4, 2: 11/28, 3: 5/14}826"""827if G.order() <= 1:828return {}829830from sage.rings.infinity import Infinity831from sage.rings.integer import Integer832833cdef unsigned short * distances = c_distances_all_pairs(G)834cdef unsigned int NN = G.order()*G.order()835cdef dict count = {}836cdef dict distr = {}837cdef unsigned int i838NNN = Integer(NN-G.order())839840# We count the number of pairs at equal distances841for 0 <= i < NN:842count[ distances[i] ] = count.get(distances[i],0) + 1843844sage_free(distances)845846# We normalize the distribution847for j in count:848if j == <unsigned short> -1:849distr[ +Infinity ] = Integer(count[j])/NNN850elif j > 0:851distr[j] = Integer(count[j])/NNN852853return distr854855##################856# Floyd-Warshall #857##################858859def floyd_warshall(gg, paths = True, distances = False):860r"""861Computes the shortest path/distances between all pairs of vertices.862863For more information on the Floyd-Warshall algorithm, see the `Wikipedia864article on Floyd-Warshall865<http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm>`_.866867INPUT:868869- ``gg`` -- the graph on which to work.870871- ``paths`` (boolean) -- whether to return the dictionary of shortest872paths. Set to ``True`` by default.873874- ``distances`` (boolean) -- whether to return the dictionary of875distances. Set to ``False`` by default.876877OUTPUT:878879Depending on the input, this function return the dictionary of paths,880the dictionary of distances, or a pair of dictionaries881``(distances, paths)`` where ``distance[u][v]`` denotes the distance of a882shortest path from `u` to `v` and ``paths[u][v]`` denotes an inneighbor883`w` of `v` such that `dist(u,v)= 1 + dist(u,w)`.884885.. WARNING::886887Because this function works on matrices whose size is quadratic compared888to the number of vertices, it uses short variables instead of long ones889to divide by 2 the size in memory. This means that the current890implementation does not run on a graph of more than 65536 nodes (this891can be easily changed if necessary, but would require much more892memory. It may be worth writing two versions). For information, the893current version of the algorithm on a graph with `65536=2^{16}` nodes894creates in memory `2` tables on `2^{32}` short elements (2bytes each),895for a total of `2^{34}` bytes or `16` gigabytes. Let us also remember896that if the memory size is quadratic, the algorithm runs in cubic time.897898.. NOTE::899900When ``paths = False`` the algorithm saves roughly half of the memory as901it does not have to maintain the matrix of predecessors. However,902setting ``distances=False`` produces no such effect as the algorithm can903not run without computing them. They will not be returned, but they will904be stored while the method is running.905906EXAMPLES:907908Shortest paths in a small grid ::909910sage: g = graphs.Grid2dGraph(2,2)911sage: from sage.graphs.distances_all_pairs import floyd_warshall912sage: print floyd_warshall(g)913{(0, 1): {(0, 1): None, (1, 0): (0, 0), (0, 0): (0, 1), (1, 1): (0, 1)},914(1, 0): {(0, 1): (0, 0), (1, 0): None, (0, 0): (1, 0), (1, 1): (1, 0)},915(0, 0): {(0, 1): (0, 0), (1, 0): (0, 0), (0, 0): None, (1, 1): (0, 1)},916(1, 1): {(0, 1): (1, 1), (1, 0): (1, 1), (0, 0): (0, 1), (1, 1): None}}917918Checking the distances are correct ::919920sage: g = graphs.Grid2dGraph(5,5)921sage: dist,path = floyd_warshall(g, distances = True)922sage: all( dist[u][v] == g.distance(u,v) for u in g for v in g )923True924925Checking a random path is valid ::926927sage: u,v = g.random_vertex(), g.random_vertex()928sage: p = [v]929sage: while p[0] != None:930... p.insert(0,path[u][p[0]])931sage: len(p) == dist[u][v] + 2932True933934Distances for all pairs of vertices in a diamond::935936sage: g = graphs.DiamondGraph()937sage: floyd_warshall(g, paths = False, distances = True)938{0: {0: 0, 1: 1, 2: 1, 3: 2},9391: {0: 1, 1: 0, 2: 1, 3: 1},9402: {0: 1, 1: 1, 2: 0, 3: 1},9413: {0: 2, 1: 1, 2: 1, 3: 0}}942943TESTS:944945Too large graphs::946947sage: from sage.graphs.distances_all_pairs import floyd_warshall948sage: floyd_warshall(Graph(65536))949Traceback (most recent call last):950...951ValueError: The graph backend contains more than 65535 nodes952"""953954from sage.rings.infinity import Infinity955cdef CGraph g = <CGraph> gg._backend._cg956957cdef list gverts = g.verts()958959if gverts == []:960if distances and paths:961return {}, {}962else:963return {}964965cdef unsigned int n = max(gverts) + 1966967if n >= <unsigned short> -1:968raise ValueError("The graph backend contains more than "+str(<unsigned short> -1)+" nodes")969970# All this just creates two tables prec[n][n] and dist[n][n]971cdef unsigned short * t_prec972cdef unsigned short * t_dist973cdef unsigned short ** prec974cdef unsigned short ** dist975976cdef int i977cdef int v_int978cdef int u_int979cdef int w_int980981# init dist982t_dist = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))983if t_dist == NULL:984raise MemoryError()985dist = <unsigned short **> sage_malloc(n*sizeof(unsigned short *))986if dist == NULL:987sage_free(t_dist)988raise MemoryError()989dist[0] = t_dist990for 1 <= i< n:991dist[i] = dist[i-1] + n992memset(t_dist, -1, n*n*sizeof(short))993# Copying the adjacency matrix (vertices at distance 1)994for v_int in gverts:995dist[v_int][v_int] = 0996for u_int in g.out_neighbors(v_int):997dist[v_int][u_int] = 1998999if paths:1000# init prec1001t_prec = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))1002if t_prec == NULL:1003sage_free(t_dist)1004sage_free(dist)1005raise MemoryError()1006prec = <unsigned short **> sage_malloc(n*sizeof(unsigned short *))1007if prec == NULL:1008sage_free(t_dist)1009sage_free(dist)1010sage_free(t_prec)1011raise MemoryError()1012prec[0] = t_prec1013for 1 <= i< n:1014prec[i] = prec[i-1] + n1015memset(t_prec, 0, n*n*sizeof(short))1016# Copying the adjacency matrix (vertices at distance 1)1017for v_int in gverts:1018prec[v_int][v_int] = v_int1019for u_int in g.out_neighbors(v_int):1020prec[v_int][u_int] = v_int10211022# The algorithm itself.1023cdef unsigned short *dv, *dw1024cdef int dvw1025cdef int val10261027for w_int in gverts:1028dw = dist[w_int]1029for v_int in gverts:1030dv = dist[v_int]1031dvw = dv[w_int]1032for u_int in gverts:1033val = dvw + dw[u_int]1034# If it is shorter to go from u to v through w, do it1035if dv[u_int] > val:1036dv[u_int] = val1037if paths:1038prec[v_int][u_int] = prec[w_int][u_int]10391040# Dictionaries of distance, precedent element, and integers1041cdef dict d_prec1042cdef dict d_dist1043cdef dict tmp_prec1044cdef dict tmp_dist10451046cdef dict ggbvi = gg._backend.vertex_ints1047cdef dict ggbvl = gg._backend.vertex_labels10481049if paths: d_prec = {}1050if distances: d_dist = {}1051for v_int in gverts:1052if paths: tmp_prec = {}1053if distances: tmp_dist = {}1054v = vertex_label(v_int, ggbvi, ggbvl, g)1055dv = dist[v_int]1056for u_int in gverts:1057u = vertex_label(u_int, ggbvi, ggbvl, g)1058if paths:1059tmp_prec[u] = (None if v == u1060else vertex_label(prec[v_int][u_int], ggbvi, ggbvl, g))1061if distances:1062tmp_dist[u] = (dv[u_int] if (dv[u_int] != <unsigned short> -1)1063else Infinity)1064if paths: d_prec[v] = tmp_prec1065if distances: d_dist[v] = tmp_dist10661067if paths:1068sage_free(t_prec)1069sage_free(prec)10701071sage_free(t_dist)1072sage_free(dist)10731074if distances and paths:1075return d_dist, d_prec1076if paths:1077return d_prec1078if distances:1079return d_dist108010811082