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