Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/distances_all_pairs.pyx
8815 views
1
r"""
2
Distances/shortest paths between all pairs of vertices
3
4
This module implements a few functions that deal with the computation of
5
distances or shortest paths between all pairs of vertices.
6
7
**Efficiency** : Because these functions involve listing many times the
8
(out)-neighborhoods of (di)-graphs, it is useful in terms of efficiency to build
9
a temporary copy of the graph in a data structure that makes it easy to compute
10
quickly. These functions also work on large volume of data, typically dense
11
matrices of size `n^2`, and are expected to return corresponding dictionaries of
12
size `n^2`, where the integers corresponding to the vertices have first been
13
converted to the vertices' labels. Sadly, this last translating operation turns
14
out to be the most time-consuming, and for this reason it is also nice to have a
15
Cython module, and version of these functions that return C arrays, in order to
16
avoid these operations when they are not necessary.
17
18
**Memory cost** : The methods implemented in the current module sometimes need large
19
amounts of memory to return their result. Storing the distances between all
20
pairs of vertices in a graph on `1500` vertices as a dictionary of dictionaries
21
takes around 200MB, while storing the same information as a C array requires
22
4MB.
23
24
25
The module's main function
26
--------------------------
27
28
The C function ``all_pairs_shortest_path_BFS`` actually does all the
29
computations, and all the others (except for ``Floyd_Warshall``) are just
30
wrapping it. This function begins with copying the graph in a data structure
31
that makes it fast to query the out-neighbors of a vertex, then starts one
32
Breadth First Search per vertex of the (di)graph.
33
34
**What can this function compute ?**
35
36
- The matrix of predecessors.
37
38
This matrix `P` has size `n^2`, and is such that vertex `P[u,v]` is a
39
predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently
40
encodes the information of a shortest `uv`-path for any `u,v\in G` :
41
indeed, to go from `u` to `v` you should first find a shortest
42
`uP[u,v]`-path, then jump from `P[u,v]` to `v` as it is one of its
43
outneighbors. Apply recursively and find out what the whole path is !.
44
45
- The matrix of distances.
46
47
This matrix has size `n^2` and associates to any `uv` the distance from
48
`u` to `v`.
49
50
- The vector of eccentricities.
51
52
This vector of size `n` encodes for each vertex `v` the distance to vertex
53
which is furthest from `v` in the graph. In particular, the diameter of
54
the graph is the maximum of these values.
55
56
**What does it take as input ?**
57
58
- ``gg`` a (Di)Graph.
59
60
- ``unsigned short * predecessors`` -- a pointer toward an array of size
61
`n^2\cdot\text{sizeof(unsigned short)}`. Set to ``NULL`` if you do not
62
want to compute the predecessors.
63
64
- ``unsigned short * distances`` -- a pointer toward an array of size
65
`n^2\cdot\text{sizeof(unsigned short)}`. The computation of the distances
66
is necessary for the algorithm, so this value can **not** be set to
67
``NULL``.
68
69
- ``int * eccentricity`` -- a pointer toward an array of size
70
`n\cdot\text{sizeof(int)}`. Set to ``NULL`` if you do not want to compute
71
the eccentricity.
72
73
**Technical details**
74
75
- The vertices are encoded as `1, ..., n` as they appear in the ordering of
76
``G.vertices()``.
77
78
- Because this function works on matrices whose size is quadratic compared
79
to the number of vertices when computing all distances or predecessors, it
80
uses short variables to store the vertices' names instead of long ones to
81
divide by 2 the size in memory. This means that only the
82
diameter/eccentricities can be computed on a graph of more than 65536
83
nodes. For information, the current version of the algorithm on a graph
84
with `65536=2^{16}` nodes creates in memory `2` tables on `2^{32}` short
85
elements (2bytes each), for a total of `2^{33}` bytes or `8` gigabytes. In
86
order to support larger sizes, we would have to replace shorts by 32-bits
87
int or 64-bits int, which would then require respectively 16GB or 32GB.
88
89
- In the C version of these functions, infinite distances are represented
90
with ``<unsigned short> -1 = 65535`` for ``unsigned short`` variables, and
91
by ``INT32_MAX`` otherwise. These case happens when the input is a
92
disconnected graph, or a non-strongly-connected digraph.
93
94
- A memory error is raised when data structures allocation failed. This
95
could happen with large graphs on computers with low memory space.
96
97
.. WARNING::
98
99
The function ``all_pairs_shortest_path_BFS`` has **no reason** to be
100
called by the user, even though he would be writing his code in Cython
101
and look for efficiency. This module contains wrappers for this function
102
that feed it with the good parameters. As the function is inlined, using
103
those wrappers actually saves time as it should avoid testing the
104
parameters again and again in the main function's body.
105
106
AUTHOR:
107
108
- Nathann Cohen (2011)
109
110
REFERENCE:
111
112
.. [KRG96b] S. Klavzar, A. Rajapakse, and I. Gutman. The Szeged and the
113
Wiener index of graphs. *Applied Mathematics Letters*, 9(5):45--49, 1996.
114
115
.. [GYLL93c] I. Gutman, Y.-N. Yeh, S.-L. Lee, and Y.-L. Luo. Some recent
116
results in the theory of the Wiener number. *Indian Journal of
117
Chemistry*, 32A:651--661, 1993.
118
119
Functions
120
---------
121
"""
122
123
##############################################################################
124
# Copyright (C) 2011 Nathann Cohen <[email protected]>
125
# Distributed under the terms of the GNU General Public License (GPL)
126
# The full text of the GPL is available at:
127
# http://www.gnu.org/licenses/
128
##############################################################################
129
130
include "sage/misc/bitset.pxi"
131
from libc.stdint cimport uint64_t, uint32_t, INT32_MAX
132
from sage.graphs.base.c_graph cimport CGraph
133
from sage.graphs.base.c_graph cimport vertex_label
134
from sage.graphs.base.c_graph cimport get_vertex
135
136
from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph
137
138
139
cdef inline all_pairs_shortest_path_BFS(gg,
140
unsigned short * predecessors,
141
unsigned short * distances,
142
int * eccentricity):
143
"""
144
See the module's documentation.
145
"""
146
147
from sage.rings.infinity import Infinity
148
149
cdef CGraph cg = <CGraph> gg._backend._cg
150
151
cdef list int_to_vertex = gg.vertices()
152
cdef int i
153
154
cdef int n = len(int_to_vertex)
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 != NULL or distances != NULL) 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
# The vertices which have already been visited
166
cdef bitset_t seen
167
bitset_init(seen, n)
168
169
# The list of waiting vertices, the beginning and the end of the list
170
cdef int * waiting_list = <int *> sage_malloc(n*sizeof(int))
171
if waiting_list == NULL:
172
raise MemoryError()
173
cdef int waiting_beginning = 0
174
cdef int waiting_end = 0
175
176
cdef int source
177
cdef int v, u
178
cdef uint32_t * p_tmp
179
cdef uint32_t * end
180
181
cdef unsigned short * c_predecessors = predecessors
182
cdef int * c_eccentricity = eccentricity
183
cdef int * c_distances = <int *> sage_malloc( n * sizeof(int))
184
if c_distances == NULL:
185
sage_free(waiting_list)
186
raise MemoryError()
187
188
# Copying the whole graph to obtain the list of neighbors quicker than by
189
# calling out_neighbors
190
191
# The edges are stored in the vector p_edges. This vector contains, from
192
# left to right The list of the first vertex's outneighbors, then the
193
# second's, then the third's, ...
194
#
195
# The outneighbors of vertex i are enumerated from
196
#
197
# p_vertices[i] to p_vertices[i+1] - 1
198
# (if p_vertices[i] is equal to p_vertices[i+1], then i has no outneighbours)
199
#
200
# This data structure is well documented in the module
201
# sage.graphs.base.static_sparse_graph
202
203
cdef short_digraph sd
204
init_short_digraph(sd, gg)
205
cdef uint32_t ** p_vertices = sd.neighbors
206
cdef uint32_t * p_edges = sd.edges
207
cdef uint32_t * p_next = p_edges
208
209
# We run n different BFS taking each vertex as a source
210
for source in range(n):
211
212
# The source is seen
213
bitset_set_first_n(seen, 0)
214
bitset_add(seen, source)
215
216
# Its parameters can already be set
217
c_distances[source] = 0
218
219
if predecessors != NULL:
220
c_predecessors[source] = source
221
222
# and added to the queue
223
waiting_list[0] = source
224
waiting_beginning = 0
225
waiting_end = 0
226
227
# For as long as there are vertices left to explore
228
while waiting_beginning <= waiting_end:
229
230
# We pick the first one
231
v = waiting_list[waiting_beginning]
232
233
p_tmp = p_vertices[v]
234
end = p_vertices[v+1]
235
236
# Iterating over all the outneighbors u of v
237
while p_tmp < end:
238
u = p_tmp[0]
239
240
# If we notice one of these neighbors is not seen yet, we set
241
# its parameters and add it to the queue to be explored later.
242
if not bitset_in(seen, u):
243
c_distances[u] = c_distances[v]+1
244
if predecessors != NULL:
245
c_predecessors[u] = v
246
bitset_add(seen, u)
247
waiting_end += 1
248
waiting_list[waiting_end] = u
249
250
p_tmp += 1
251
252
waiting_beginning += 1
253
254
# If not all the vertices have been met
255
for v in range(n):
256
if not bitset_in(seen, v):
257
c_distances[v] = INT32_MAX
258
if predecessors != NULL:
259
c_predecessors[v] = -1
260
261
if predecessors != NULL:
262
c_predecessors += n
263
264
if eccentricity != NULL:
265
c_eccentricity[source] = 0
266
for i in range(n):
267
c_eccentricity[source] = c_eccentricity[source] if c_eccentricity[source] >= c_distances[i] else c_distances[i]
268
269
if distances != NULL:
270
for i in range(n):
271
distances[i] = <unsigned short> c_distances[i]
272
distances += n
273
274
bitset_free(seen)
275
sage_free(waiting_list)
276
free_short_digraph(sd)
277
sage_free(c_distances)
278
279
################
280
# Predecessors #
281
################
282
283
cdef unsigned short * c_shortest_path_all_pairs(G) except NULL:
284
r"""
285
Returns the matrix of predecessors in G.
286
287
The matrix `P` returned has size `n^2`, and is such that vertex `P[u,v]` is
288
a predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently
289
encodes the information of a shortest `uv`-path for any `u,v\in G` : indeed,
290
to go from `u` to `v` you should first find a shortest `uP[u,v]`-path, then
291
jump from `P[u,v]` to `v` as it is one of its outneighbors.
292
"""
293
294
cdef unsigned int n = G.order()
295
cdef unsigned short * distances = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))
296
if distances == NULL:
297
raise MemoryError()
298
cdef unsigned short * predecessors = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))
299
if predecessors == NULL:
300
sage_free(distances)
301
raise MemoryError()
302
all_pairs_shortest_path_BFS(G, predecessors, distances, NULL)
303
304
sage_free(distances)
305
306
return predecessors
307
308
def shortest_path_all_pairs(G):
309
r"""
310
Returns the matrix of predecessors in G.
311
312
The matrix `P` returned has size `n^2`, and is such that vertex `P[u,v]` is
313
a predecessor of `v` on a shortest `uv`-path. Hence, this matrix efficiently
314
encodes the information of a shortest `uv`-path for any `u,v\in G` : indeed,
315
to go from `u` to `v` you should first find a shortest `uP[u,v]`-path, then
316
jump from `P[u,v]` to `v` as it is one of its outneighbors.
317
318
The integer corresponding to a vertex is its index in the list
319
``G.vertices()``.
320
321
EXAMPLE::
322
323
sage: from sage.graphs.distances_all_pairs import shortest_path_all_pairs
324
sage: g = graphs.PetersenGraph()
325
sage: shortest_path_all_pairs(g)
326
{0: {0: None, 1: 0, 2: 1, 3: 4, 4: 0, 5: 0, 6: 1, 7: 5, 8: 5, 9: 4},
327
1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0, 5: 0, 6: 1, 7: 2, 8: 6, 9: 6},
328
2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3, 5: 7, 6: 1, 7: 2, 8: 3, 9: 7},
329
3: {0: 4, 1: 2, 2: 3, 3: None, 4: 3, 5: 8, 6: 8, 7: 2, 8: 3, 9: 4},
330
4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None, 5: 0, 6: 9, 7: 9, 8: 3, 9: 4},
331
5: {0: 5, 1: 0, 2: 7, 3: 8, 4: 0, 5: None, 6: 8, 7: 5, 8: 5, 9: 7},
332
6: {0: 1, 1: 6, 2: 1, 3: 8, 4: 9, 5: 8, 6: None, 7: 9, 8: 6, 9: 6},
333
7: {0: 5, 1: 2, 2: 7, 3: 2, 4: 9, 5: 7, 6: 9, 7: None, 8: 5, 9: 7},
334
8: {0: 5, 1: 6, 2: 3, 3: 8, 4: 3, 5: 8, 6: 8, 7: 5, 8: None, 9: 6},
335
9: {0: 4, 1: 6, 2: 7, 3: 4, 4: 9, 5: 7, 6: 9, 7: 9, 8: 6, 9: None}}
336
"""
337
338
cdef int n = G.order()
339
340
if n == 0:
341
return {}
342
343
cdef unsigned short * predecessors = c_shortest_path_all_pairs(G)
344
cdef unsigned short * c_predecessors = predecessors
345
346
cdef dict d = {}
347
cdef dict d_tmp
348
349
cdef CGraph cg = <CGraph> G._backend._cg
350
351
cdef list int_to_vertex = G.vertices()
352
cdef int i, j
353
354
for i, l in enumerate(int_to_vertex):
355
int_to_vertex[i] = get_vertex(l, G._backend.vertex_ints, G._backend.vertex_labels, cg)
356
357
for j in range(n):
358
d_tmp = {}
359
for i in range(n):
360
if c_predecessors[i] == <unsigned short> -1:
361
d_tmp[int_to_vertex[i]] = None
362
else:
363
d_tmp[int_to_vertex[i]] = int_to_vertex[c_predecessors[i]]
364
365
d_tmp[int_to_vertex[j]] = None
366
d[int_to_vertex[j]] = d_tmp
367
368
c_predecessors += n
369
370
sage_free(predecessors)
371
return d
372
373
#############
374
# Distances #
375
#############
376
377
cdef unsigned short * c_distances_all_pairs(G):
378
r"""
379
Returns the matrix of distances in G.
380
381
The matrix `M` returned is of length `n^2`, and the distance between
382
vertices `u` and `v` is `M[u,v]`. The integer corresponding to a vertex is
383
its index in the list ``G.vertices()``.
384
"""
385
386
cdef unsigned int n = G.order()
387
cdef unsigned short * distances = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))
388
if distances == NULL:
389
raise MemoryError()
390
all_pairs_shortest_path_BFS(G, NULL, distances, NULL)
391
392
return distances
393
394
def distances_all_pairs(G):
395
r"""
396
Returns the matrix of distances in G.
397
398
This function returns a double dictionary ``D`` of vertices, in which the
399
distance between vertices ``u`` and ``v`` is ``D[u][v]``.
400
401
EXAMPLE::
402
403
sage: from sage.graphs.distances_all_pairs import distances_all_pairs
404
sage: g = graphs.PetersenGraph()
405
sage: distances_all_pairs(g)
406
{0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2},
407
1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2},
408
2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2},
409
3: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2},
410
4: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1},
411
5: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2},
412
6: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1},
413
7: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1},
414
8: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2},
415
9: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}}
416
"""
417
418
from sage.rings.infinity import Infinity
419
420
cdef int n = G.order()
421
422
if n == 0:
423
return {}
424
425
cdef unsigned short * distances = c_distances_all_pairs(G)
426
cdef unsigned short * c_distances = distances
427
428
cdef dict d = {}
429
cdef dict d_tmp
430
431
cdef list int_to_vertex = G.vertices()
432
cdef int i, j
433
434
for j in range(n):
435
d_tmp = {}
436
for i in range(n):
437
if c_distances[i] == <unsigned short> -1:
438
d_tmp[int_to_vertex[i]] = Infinity
439
else:
440
d_tmp[int_to_vertex[i]] = c_distances[i]
441
442
d[int_to_vertex[j]] = d_tmp
443
c_distances += n
444
445
sage_free(distances)
446
return d
447
448
def is_distance_regular(G, parameters = False):
449
r"""
450
Tests if the graph is distance-regular
451
452
A graph `G` is distance-regular if there exist integers `d_1,...,d_n` such
453
that for every vertex `v\in G` we have `d_i=\#\{u:d_G(u,v) =i\}`. Thus a
454
strongly-regular graph is also distance-regular, and a distance-regular
455
graph is necessarily regular too.
456
457
For more information on distance-regular graphs, see its associated
458
:wikipedia:`wikipedia page <Distance-regular_graph>`.
459
460
INPUT:
461
462
- ``parameters`` (boolean) -- whether to replace ``True`` answers with a
463
dictionary associating `d_i` to an integer `i>0` if `d_i>0` (one can then
464
obtain `d_i` by doing ``dictionary.get(i,0)``). Set to ``False`` by
465
default.
466
467
.. SEEALSO::
468
469
* :meth:`~sage.graphs.generic_graph.GenericGraph.is_regular`
470
* :meth:`~Graph.is_strongly_regular`
471
472
EXAMPLES::
473
474
sage: g = graphs.PetersenGraph()
475
sage: g.is_distance_regular()
476
True
477
sage: g.is_distance_regular(parameters = True)
478
{1: 3, 2: 6}
479
480
Cube graphs, which are not strongly regular, are a bit more interesting::
481
482
sage: graphs.CubeGraph(4).is_distance_regular(parameters = True)
483
{1: 4, 2: 6, 3: 4, 4: 1}
484
485
TESTS::
486
487
sage: graphs.PathGraph(2).is_distance_regular(parameters = True)
488
{1: 1}
489
490
"""
491
cdef int i,l
492
cdef int n = G.order()
493
494
if n <= 1:
495
return {} if parameters else True
496
497
if not G.is_regular():
498
return False
499
500
cdef unsigned short * distance_matrix = c_distances_all_pairs(G)
501
502
# - d_array is the vector of d_i corresponding to the first vertex
503
#
504
# - d_tmp is a vector that we use to check that d_array is the same for
505
# every vertex v
506
cdef unsigned short * d_array = <unsigned short *> sage_calloc(2*n, sizeof(unsigned short))
507
cdef unsigned short * d_tmp = d_array + n
508
509
if d_array == NULL:
510
sage_free(distance_matrix)
511
raise MemoryError()
512
513
# Filling d_array
514
cdef unsigned short * pointer = distance_matrix
515
for i in range(n):
516
if pointer[i] < n:
517
d_array[pointer[i]] += 1
518
pointer += n
519
520
# For each of the n-1 other vertices
521
for l in range(1,n):
522
523
# We set d_tmp and fill it with the data from the l^th row
524
memset(d_tmp, 0, n*sizeof(unsigned short))
525
for i in range(n):
526
if pointer[i] < n:
527
d_tmp[pointer[i]] += 1
528
529
# If d_tmp != d_array, we are done
530
if memcmp(d_array, d_tmp, n*sizeof(unsigned short)) != 0:
531
sage_free(distance_matrix)
532
sage_free(d_array)
533
return False
534
535
pointer += n
536
537
cdef dict dict_parameters
538
if parameters:
539
dict_parameters = {i:d_array[i] for i in range(n) if i and d_array[i] > 0}
540
541
sage_free(distance_matrix)
542
sage_free(d_array)
543
544
if parameters:
545
return dict_parameters
546
else:
547
return True
548
549
###################################
550
# Both distances and predecessors #
551
###################################
552
553
def distances_and_predecessors_all_pairs(G):
554
r"""
555
Returns the matrix of distances in G and the matrix of predecessors.
556
557
Distances : the matrix `M` returned is of length `n^2`, and the distance
558
between vertices `u` and `v` is `M[u,v]`. The integer corresponding to a
559
vertex is its index in the list ``G.vertices()``.
560
561
Predecessors : the matrix `P` returned has size `n^2`, and is such that
562
vertex `P[u,v]` is a predecessor of `v` on a shortest `uv`-path. Hence, this
563
matrix efficiently encodes the information of a shortest `uv`-path for any
564
`u,v\in G` : indeed, to go from `u` to `v` you should first find a shortest
565
`uP[u,v]`-path, then jump from `P[u,v]` to `v` as it is one of its
566
outneighbors.
567
568
The integer corresponding to a vertex is its index in the list
569
``G.vertices()``.
570
571
572
EXAMPLE::
573
574
sage: from sage.graphs.distances_all_pairs import distances_and_predecessors_all_pairs
575
sage: g = graphs.PetersenGraph()
576
sage: distances_and_predecessors_all_pairs(g)
577
({0: {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2},
578
1: {0: 1, 1: 0, 2: 1, 3: 2, 4: 2, 5: 2, 6: 1, 7: 2, 8: 2, 9: 2},
579
2: {0: 2, 1: 1, 2: 0, 3: 1, 4: 2, 5: 2, 6: 2, 7: 1, 8: 2, 9: 2},
580
3: {0: 2, 1: 2, 2: 1, 3: 0, 4: 1, 5: 2, 6: 2, 7: 2, 8: 1, 9: 2},
581
4: {0: 1, 1: 2, 2: 2, 3: 1, 4: 0, 5: 2, 6: 2, 7: 2, 8: 2, 9: 1},
582
5: {0: 1, 1: 2, 2: 2, 3: 2, 4: 2, 5: 0, 6: 2, 7: 1, 8: 1, 9: 2},
583
6: {0: 2, 1: 1, 2: 2, 3: 2, 4: 2, 5: 2, 6: 0, 7: 2, 8: 1, 9: 1},
584
7: {0: 2, 1: 2, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 0, 8: 2, 9: 1},
585
8: {0: 2, 1: 2, 2: 2, 3: 1, 4: 2, 5: 1, 6: 1, 7: 2, 8: 0, 9: 2},
586
9: {0: 2, 1: 2, 2: 2, 3: 2, 4: 1, 5: 2, 6: 1, 7: 1, 8: 2, 9: 0}},
587
{0: {0: None, 1: 0, 2: 1, 3: 4, 4: 0, 5: 0, 6: 1, 7: 5, 8: 5, 9: 4},
588
1: {0: 1, 1: None, 2: 1, 3: 2, 4: 0, 5: 0, 6: 1, 7: 2, 8: 6, 9: 6},
589
2: {0: 1, 1: 2, 2: None, 3: 2, 4: 3, 5: 7, 6: 1, 7: 2, 8: 3, 9: 7},
590
3: {0: 4, 1: 2, 2: 3, 3: None, 4: 3, 5: 8, 6: 8, 7: 2, 8: 3, 9: 4},
591
4: {0: 4, 1: 0, 2: 3, 3: 4, 4: None, 5: 0, 6: 9, 7: 9, 8: 3, 9: 4},
592
5: {0: 5, 1: 0, 2: 7, 3: 8, 4: 0, 5: None, 6: 8, 7: 5, 8: 5, 9: 7},
593
6: {0: 1, 1: 6, 2: 1, 3: 8, 4: 9, 5: 8, 6: None, 7: 9, 8: 6, 9: 6},
594
7: {0: 5, 1: 2, 2: 7, 3: 2, 4: 9, 5: 7, 6: 9, 7: None, 8: 5, 9: 7},
595
8: {0: 5, 1: 6, 2: 3, 3: 8, 4: 3, 5: 8, 6: 8, 7: 5, 8: None, 9: 6},
596
9: {0: 4, 1: 6, 2: 7, 3: 4, 4: 9, 5: 7, 6: 9, 7: 9, 8: 6, 9: None}})
597
"""
598
599
from sage.rings.infinity import Infinity
600
cdef unsigned int n = G.order()
601
602
if n == 0:
603
return {}, {}
604
605
cdef unsigned short * distances = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))
606
if distances == NULL:
607
raise MemoryError()
608
cdef unsigned short * c_distances = distances
609
cdef unsigned short * predecessor = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))
610
if predecessor == NULL:
611
sage_free(distances)
612
raise MemoryError()
613
cdef unsigned short * c_predecessor = predecessor
614
615
all_pairs_shortest_path_BFS(G, predecessor, distances, NULL)
616
617
618
cdef dict d_distance = {}
619
cdef dict d_predecessor = {}
620
cdef dict t_distance = {}
621
cdef dict t_predecessor = {}
622
623
cdef list int_to_vertex = G.vertices()
624
cdef int i, j
625
626
for j in range(n):
627
t_distance = {}
628
t_predecessor = {}
629
630
for i in range(n):
631
632
if c_distances[i] == <unsigned short> -1:
633
t_distance[int_to_vertex[i]] = Infinity
634
t_predecessor[int_to_vertex[i]] = None
635
else:
636
t_distance[int_to_vertex[i]] = c_distances[i]
637
t_predecessor[int_to_vertex[i]] = int_to_vertex[c_predecessor[i]]
638
639
t_predecessor[int_to_vertex[j]] = None
640
641
d_distance[int_to_vertex[j]] = t_distance
642
d_predecessor[int_to_vertex[j]] = t_predecessor
643
644
c_distances += n
645
c_predecessor += n
646
647
sage_free(distances)
648
sage_free(predecessor)
649
650
return d_distance, d_predecessor
651
652
################
653
# Eccentricity #
654
################
655
656
cdef int * c_eccentricity(G) except NULL:
657
r"""
658
Returns the vector of eccentricities in G.
659
660
The array returned is of length n, and its ith component is the eccentricity
661
of the ith vertex in ``G.vertices()``.
662
"""
663
cdef unsigned int n = G.order()
664
665
cdef int * ecc = <int *> sage_malloc(n*sizeof(int))
666
if ecc == NULL:
667
raise MemoryError()
668
all_pairs_shortest_path_BFS(G, NULL, NULL, ecc)
669
670
return ecc
671
672
def eccentricity(G):
673
r"""
674
Returns the vector of eccentricities in G.
675
676
The array returned is of length n, and its ith component is the eccentricity
677
of the ith vertex in ``G.vertices()``.
678
679
EXAMPLE::
680
681
sage: from sage.graphs.distances_all_pairs import eccentricity
682
sage: g = graphs.PetersenGraph()
683
sage: eccentricity(g)
684
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
685
"""
686
from sage.rings.infinity import Infinity
687
cdef int n = G.order()
688
689
if n == 0:
690
return []
691
692
cdef int * ecc = c_eccentricity(G)
693
694
cdef list l_ecc = []
695
cdef int i
696
for i in range(n):
697
if ecc[i] == INT32_MAX:
698
l_ecc.append(Infinity)
699
else:
700
l_ecc.append(ecc[i])
701
702
sage_free(ecc)
703
704
return l_ecc
705
706
def diameter(G):
707
r"""
708
Returns the diameter of `G`.
709
710
EXAMPLE::
711
712
sage: from sage.graphs.distances_all_pairs import diameter
713
sage: g = graphs.PetersenGraph()
714
sage: diameter(g)
715
2
716
"""
717
return max(eccentricity(G))
718
719
720
################
721
# Wiener index #
722
################
723
724
def wiener_index(G):
725
r"""
726
Returns the Wiener index of the graph.
727
728
The Wiener index of a graph `G` can be defined in two equivalent
729
ways [KRG96b]_ :
730
731
- `W(G) = \frac 1 2 \sum_{u,v\in G} d(u,v)` where `d(u,v)` denotes the
732
distance between vertices `u` and `v`.
733
734
- Let `\Omega` be a set of `\frac {n(n-1)} 2` paths in `G` such that `\Omega`
735
contains exactly one shortest `u-v` path for each set `\{u,v\}` of
736
vertices in `G`. Besides, `\forall e\in E(G)`, let `\Omega(e)` denote the
737
paths from `\Omega` containing `e`. We then have
738
`W(G) = \sum_{e\in E(G)}|\Omega(e)|`.
739
740
EXAMPLE:
741
742
From [GYLL93c]_, cited in [KRG96b]_::
743
744
sage: g=graphs.PathGraph(10)
745
sage: w=lambda x: (x*(x*x -1)/6)
746
sage: g.wiener_index()==w(10)
747
True
748
"""
749
if not G.is_connected():
750
from sage.rings.infinity import Infinity
751
return +Infinity
752
753
from sage.rings.integer import Integer
754
cdef unsigned short * distances = c_distances_all_pairs(G)
755
cdef unsigned int NN = G.order()*G.order()
756
cdef unsigned int i
757
cdef uint64_t s = 0
758
for 0 <= i < NN:
759
s += distances[i]
760
sage_free(distances)
761
return Integer(s)/2
762
763
##########################
764
# Distances distribution #
765
##########################
766
767
def distances_distribution(G):
768
r"""
769
Returns the distances distribution of the (di)graph in a dictionary.
770
771
This method *ignores all edge labels*, so that the distance considered is
772
the topological distance.
773
774
OUTPUT:
775
776
A dictionary ``d`` such that the number of pairs of vertices at distance
777
``k`` (if any) is equal to `d[k] \cdot |V(G)| \cdot (|V(G)|-1)`.
778
779
.. NOTE::
780
781
We consider that two vertices that do not belong to the same connected
782
component are at infinite distance, and we do not take the trivial pairs
783
of vertices `(v, v)` at distance `0` into account. Empty (di)graphs and
784
(di)graphs of order 1 have no paths and so we return the empty
785
dictionary ``{}``.
786
787
EXAMPLES:
788
789
An empty Graph::
790
791
sage: g = Graph()
792
sage: g.distances_distribution()
793
{}
794
795
A Graph of order 1::
796
797
sage: g = Graph()
798
sage: g.add_vertex(1)
799
sage: g.distances_distribution()
800
{}
801
802
A Graph of order 2 without edge::
803
804
sage: g = Graph()
805
sage: g.add_vertices([1,2])
806
sage: g.distances_distribution()
807
{+Infinity: 1}
808
809
The Petersen Graph::
810
811
sage: g = graphs.PetersenGraph()
812
sage: g.distances_distribution()
813
{1: 1/3, 2: 2/3}
814
815
A graph with multiple disconnected components::
816
817
sage: g = graphs.PetersenGraph()
818
sage: g.add_edge('good','wine')
819
sage: g.distances_distribution()
820
{1: 8/33, 2: 5/11, +Infinity: 10/33}
821
822
The de Bruijn digraph dB(2,3)::
823
824
sage: D = digraphs.DeBruijn(2,3)
825
sage: D.distances_distribution()
826
{1: 1/4, 2: 11/28, 3: 5/14}
827
"""
828
if G.order() <= 1:
829
return {}
830
831
from sage.rings.infinity import Infinity
832
from sage.rings.integer import Integer
833
834
cdef unsigned short * distances = c_distances_all_pairs(G)
835
cdef unsigned int NN = G.order()*G.order()
836
cdef dict count = {}
837
cdef dict distr = {}
838
cdef unsigned int i
839
NNN = Integer(NN-G.order())
840
841
# We count the number of pairs at equal distances
842
for 0 <= i < NN:
843
count[ distances[i] ] = count.get(distances[i],0) + 1
844
845
sage_free(distances)
846
847
# We normalize the distribution
848
for j in count:
849
if j == <unsigned short> -1:
850
distr[ +Infinity ] = Integer(count[j])/NNN
851
elif j > 0:
852
distr[j] = Integer(count[j])/NNN
853
854
return distr
855
856
##################
857
# Floyd-Warshall #
858
##################
859
860
def floyd_warshall(gg, paths = True, distances = False):
861
r"""
862
Computes the shortest path/distances between all pairs of vertices.
863
864
For more information on the Floyd-Warshall algorithm, see the `Wikipedia
865
article on Floyd-Warshall
866
<http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm>`_.
867
868
INPUT:
869
870
- ``gg`` -- the graph on which to work.
871
872
- ``paths`` (boolean) -- whether to return the dictionary of shortest
873
paths. Set to ``True`` by default.
874
875
- ``distances`` (boolean) -- whether to return the dictionary of
876
distances. Set to ``False`` by default.
877
878
OUTPUT:
879
880
Depending on the input, this function return the dictionary of paths,
881
the dictionary of distances, or a pair of dictionaries
882
``(distances, paths)`` where ``distance[u][v]`` denotes the distance of a
883
shortest path from `u` to `v` and ``paths[u][v]`` denotes an inneighbor
884
`w` of `v` such that `dist(u,v)= 1 + dist(u,w)`.
885
886
.. WARNING::
887
888
Because this function works on matrices whose size is quadratic compared
889
to the number of vertices, it uses short variables instead of long ones
890
to divide by 2 the size in memory. This means that the current
891
implementation does not run on a graph of more than 65536 nodes (this
892
can be easily changed if necessary, but would require much more
893
memory. It may be worth writing two versions). For information, the
894
current version of the algorithm on a graph with `65536=2^{16}` nodes
895
creates in memory `2` tables on `2^{32}` short elements (2bytes each),
896
for a total of `2^{34}` bytes or `16` gigabytes. Let us also remember
897
that if the memory size is quadratic, the algorithm runs in cubic time.
898
899
.. NOTE::
900
901
When ``paths = False`` the algorithm saves roughly half of the memory as
902
it does not have to maintain the matrix of predecessors. However,
903
setting ``distances=False`` produces no such effect as the algorithm can
904
not run without computing them. They will not be returned, but they will
905
be stored while the method is running.
906
907
EXAMPLES:
908
909
Shortest paths in a small grid ::
910
911
sage: g = graphs.Grid2dGraph(2,2)
912
sage: from sage.graphs.distances_all_pairs import floyd_warshall
913
sage: print floyd_warshall(g)
914
{(0, 1): {(0, 1): None, (1, 0): (0, 0), (0, 0): (0, 1), (1, 1): (0, 1)},
915
(1, 0): {(0, 1): (0, 0), (1, 0): None, (0, 0): (1, 0), (1, 1): (1, 0)},
916
(0, 0): {(0, 1): (0, 0), (1, 0): (0, 0), (0, 0): None, (1, 1): (0, 1)},
917
(1, 1): {(0, 1): (1, 1), (1, 0): (1, 1), (0, 0): (0, 1), (1, 1): None}}
918
919
Checking the distances are correct ::
920
921
sage: g = graphs.Grid2dGraph(5,5)
922
sage: dist,path = floyd_warshall(g, distances = True)
923
sage: all( dist[u][v] == g.distance(u,v) for u in g for v in g )
924
True
925
926
Checking a random path is valid ::
927
928
sage: u,v = g.random_vertex(), g.random_vertex()
929
sage: p = [v]
930
sage: while p[0] != None:
931
... p.insert(0,path[u][p[0]])
932
sage: len(p) == dist[u][v] + 2
933
True
934
935
Distances for all pairs of vertices in a diamond::
936
937
sage: g = graphs.DiamondGraph()
938
sage: floyd_warshall(g, paths = False, distances = True)
939
{0: {0: 0, 1: 1, 2: 1, 3: 2},
940
1: {0: 1, 1: 0, 2: 1, 3: 1},
941
2: {0: 1, 1: 1, 2: 0, 3: 1},
942
3: {0: 2, 1: 1, 2: 1, 3: 0}}
943
944
TESTS:
945
946
Too large graphs::
947
948
sage: from sage.graphs.distances_all_pairs import floyd_warshall
949
sage: floyd_warshall(Graph(65536))
950
Traceback (most recent call last):
951
...
952
ValueError: The graph backend contains more than 65535 nodes
953
"""
954
955
from sage.rings.infinity import Infinity
956
cdef CGraph g = <CGraph> gg._backend._cg
957
958
cdef list gverts = g.verts()
959
960
if gverts == []:
961
if distances and paths:
962
return {}, {}
963
else:
964
return {}
965
966
cdef unsigned int n = max(gverts) + 1
967
968
if n >= <unsigned short> -1:
969
raise ValueError("The graph backend contains more than "+str(<unsigned short> -1)+" nodes")
970
971
# All this just creates two tables prec[n][n] and dist[n][n]
972
cdef unsigned short * t_prec
973
cdef unsigned short * t_dist
974
cdef unsigned short ** prec
975
cdef unsigned short ** dist
976
977
cdef int i
978
cdef int v_int
979
cdef int u_int
980
cdef int w_int
981
982
# init dist
983
t_dist = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))
984
if t_dist == NULL:
985
raise MemoryError()
986
dist = <unsigned short **> sage_malloc(n*sizeof(unsigned short *))
987
if dist == NULL:
988
sage_free(t_dist)
989
raise MemoryError()
990
dist[0] = t_dist
991
for 1 <= i< n:
992
dist[i] = dist[i-1] + n
993
memset(t_dist, -1, n*n*sizeof(short))
994
# Copying the adjacency matrix (vertices at distance 1)
995
for v_int in gverts:
996
dist[v_int][v_int] = 0
997
for u_int in g.out_neighbors(v_int):
998
dist[v_int][u_int] = 1
999
1000
if paths:
1001
# init prec
1002
t_prec = <unsigned short *> sage_malloc(n*n*sizeof(unsigned short))
1003
if t_prec == NULL:
1004
sage_free(t_dist)
1005
sage_free(dist)
1006
raise MemoryError()
1007
prec = <unsigned short **> sage_malloc(n*sizeof(unsigned short *))
1008
if prec == NULL:
1009
sage_free(t_dist)
1010
sage_free(dist)
1011
sage_free(t_prec)
1012
raise MemoryError()
1013
prec[0] = t_prec
1014
for 1 <= i< n:
1015
prec[i] = prec[i-1] + n
1016
memset(t_prec, 0, n*n*sizeof(short))
1017
# Copying the adjacency matrix (vertices at distance 1)
1018
for v_int in gverts:
1019
prec[v_int][v_int] = v_int
1020
for u_int in g.out_neighbors(v_int):
1021
prec[v_int][u_int] = v_int
1022
1023
# The algorithm itself.
1024
cdef unsigned short *dv, *dw
1025
cdef int dvw
1026
cdef int val
1027
1028
for w_int in gverts:
1029
dw = dist[w_int]
1030
for v_int in gverts:
1031
dv = dist[v_int]
1032
dvw = dv[w_int]
1033
for u_int in gverts:
1034
val = dvw + dw[u_int]
1035
# If it is shorter to go from u to v through w, do it
1036
if dv[u_int] > val:
1037
dv[u_int] = val
1038
if paths:
1039
prec[v_int][u_int] = prec[w_int][u_int]
1040
1041
# Dictionaries of distance, precedent element, and integers
1042
cdef dict d_prec
1043
cdef dict d_dist
1044
cdef dict tmp_prec
1045
cdef dict tmp_dist
1046
1047
cdef dict ggbvi = gg._backend.vertex_ints
1048
cdef dict ggbvl = gg._backend.vertex_labels
1049
1050
if paths: d_prec = {}
1051
if distances: d_dist = {}
1052
for v_int in gverts:
1053
if paths: tmp_prec = {}
1054
if distances: tmp_dist = {}
1055
v = vertex_label(v_int, ggbvi, ggbvl, g)
1056
dv = dist[v_int]
1057
for u_int in gverts:
1058
u = vertex_label(u_int, ggbvi, ggbvl, g)
1059
if paths:
1060
tmp_prec[u] = (None if v == u
1061
else vertex_label(prec[v_int][u_int], ggbvi, ggbvl, g))
1062
if distances:
1063
tmp_dist[u] = (dv[u_int] if (dv[u_int] != <unsigned short> -1)
1064
else Infinity)
1065
if paths: d_prec[v] = tmp_prec
1066
if distances: d_dist[v] = tmp_dist
1067
1068
if paths:
1069
sage_free(t_prec)
1070
sage_free(prec)
1071
1072
sage_free(t_dist)
1073
sage_free(dist)
1074
1075
if distances and paths:
1076
return d_dist, d_prec
1077
if paths:
1078
return d_prec
1079
if distances:
1080
return d_dist
1081
1082