~ubuntu-branches/ubuntu/wily/python-networkx/wily

« back to all changes in this revision

Viewing changes to networkx/path.py

  • Committer: Bazaar Package Importer
  • Author(s): Cyril Brulebois
  • Date: 2008-03-02 01:06:32 UTC
  • mfrom: (1.2.1 upstream) (3.1.3 hardy)
  • Revision ID: james.westby@ubuntu.com-20080302010632-1lp6qe1orf59jl8b
* debian/control:
   + Replace python-setuptools with python-pkg-resources in the
     “Recommends:” since pkg_resources is now available in this
     separate package, thanks Matthias Klose (Closes: #468721).
* debian/copyright:
   + Use “© $years $name” instead of invalid “$name, $years” and
     “(C) $years, $name”, thanks to lintian.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
"""
 
3
Shortest path algorithms.
 
4
"""
 
5
__author__ = """Aric Hagberg (hagberg@lanl.gov)"""
 
6
___revision__ = ""
 
7
#    Copyright (C) 2004-2006 by 
 
8
#    Aric Hagberg <hagberg@lanl.gov>
 
9
#    Dan Schult <dschult@colgate.edu>
 
10
#    Pieter Swart <swart@lanl.gov>
 
11
#    Distributed under the terms of the GNU Lesser General Public License
 
12
#    http://www.gnu.org/copyleft/lesser.html
 
13
import networkx
 
14
#use deque only if networkx requires python 2.4
 
15
#from collections import deque 
 
16
import heapq
 
17
 
 
18
 
 
19
def shortest_path_length(G,source,target):
 
20
    """Return the shortest path length in the graph G between
 
21
    the source and target.  Raise an exception if no path exists.
 
22
 
 
23
    G is treated as an unweighted graph.  For weighted graphs
 
24
    see dijkstra_path_length.
 
25
    """
 
26
    path=bidirectional_shortest_path(G,source,target)
 
27
    if path is False:
 
28
        raise networkx.NetworkXError,\
 
29
              "no path from %s to %s"%(source,target)
 
30
    return len(path)-1
 
31
 
 
32
def single_source_shortest_path_length(G,source,cutoff=None):
 
33
    """
 
34
    Shortest path length from source to all reachable nodes.
 
35
 
 
36
    Returns a dictionary of shortest path lengths keyed by target.
 
37
 
 
38
    >>> G=path_graph(5)
 
39
    >>> length=single_source_shortest_path_length(G,1)
 
40
    >>> length[4]
 
41
    3
 
42
    >>> print length
 
43
    {0: 1, 1: 0, 2: 1, 3: 2, 4: 3}
 
44
 
 
45
    cutoff is optional integer depth to stop the search - only
 
46
    paths of length <= cutoff are returned.
 
47
 
 
48
    """
 
49
    seen={}                  # level (number of hops) when seen in BFS
 
50
    level=0                  # the current level
 
51
    nextlevel={source:1}  # dict of nodes to check at next level
 
52
    while nextlevel:
 
53
        thislevel=nextlevel  # advance to next level
 
54
        nextlevel={}         # and start a new list (fringe)
 
55
        for v in thislevel:
 
56
            if not seen.has_key(v): 
 
57
                seen[v]=level # set the level of vertex v
 
58
                nbrs=dict.fromkeys(G.neighbors_iter(v),1)
 
59
                nextlevel.update(nbrs) # add neighbors of v
 
60
        if (cutoff is not None and cutoff <= level):  break
 
61
        level=level+1
 
62
    return seen  # return all path lengths as dictionary
 
63
 
 
64
 
 
65
def all_pairs_shortest_path_length(G,cutoff=None):
 
66
    """ Return dictionary of shortest path lengths between all nodes in G.
 
67
 
 
68
    The dictionary only has keys for reachable node pairs.
 
69
    >>> G=path_graph(5)
 
70
    >>> length=all_pairs_shortest_path_length(G)
 
71
    >>> print length[1][4]
 
72
    3
 
73
    >>> length[1]
 
74
    {0: 1, 1: 0, 2: 1, 3: 2, 4: 3}
 
75
 
 
76
 
 
77
    cutoff is optional integer depth to stop the search - only
 
78
    paths of length <= cutoff are returned.
 
79
 
 
80
    """
 
81
    paths={}
 
82
    for n in G:
 
83
        paths[n]=single_source_shortest_path_length(G,n,cutoff=cutoff)
 
84
    return paths        
 
85
        
 
86
def shortest_path(G,source,target):
 
87
    """Return a list of nodes in G for a shortest path between source
 
88
    and target.
 
89
 
 
90
    There may be more than one shortest path.  This returns only one.
 
91
    
 
92
    """
 
93
    return bidirectional_shortest_path(G,source,target)
 
94
 
 
95
 
 
96
def bidirectional_shortest_path(G,source,target):
 
97
    """
 
98
       Return list of nodes in a shortest path between source and target.
 
99
       Return False if no path exists.
 
100
 
 
101
       Also known as shortest_path.
 
102
 
 
103
    """
 
104
    # call helper to do the real work
 
105
    results=_bidirectional_pred_succ(G,source,target)
 
106
    if results is False:
 
107
        return False  # no path from source to target
 
108
    pred,succ,w=results
 
109
 
 
110
    # build path from pred+w+succ
 
111
    path=[]
 
112
    # from w to target
 
113
    while w is not None:
 
114
        path.append(w)
 
115
        w=succ[w]
 
116
    # from source to w        
 
117
    w=pred[path[0]]
 
118
    while w is not None:
 
119
        path.insert(0,w)
 
120
        w=pred[w]
 
121
 
 
122
    return path        
 
123
 
 
124
def _bidirectional_pred_succ(G, source, target):
 
125
    """
 
126
       Bidirectional shortest path helper.
 
127
 
 
128
       Returns (pred,succ,w) where
 
129
       pred is a dictionary of predecessors from w to the source, and
 
130
       succ is a dictionary of successors from w to the target.
 
131
    """
 
132
    # does BFS from both source and target and meets in the middle
 
133
    if source is None or target is None:
 
134
        raise NetworkXException(\
 
135
            "Bidirectional shortest path called without source or target")
 
136
    if target == source:  return ({1:None},{1:None},1)
 
137
 
 
138
    # handle either directed or undirected
 
139
    if G.is_directed():
 
140
        Gpred=G.predecessors_iter
 
141
        Gsucc=G.successors_iter
 
142
    else:
 
143
        Gpred=G.neighbors_iter
 
144
        Gsucc=G.neighbors_iter
 
145
 
 
146
    # predecesssor and successors in search
 
147
    pred={source:None}
 
148
    succ={target:None}
 
149
 
 
150
    # initialize fringes, start with forward
 
151
    forward_fringe=[source] 
 
152
    reverse_fringe=[target]  
 
153
 
 
154
    while forward_fringe and reverse_fringe:
 
155
        this_level=forward_fringe
 
156
        forward_fringe=[]
 
157
        for v in this_level:
 
158
            for w in Gsucc(v):
 
159
                if w not in pred:
 
160
                    forward_fringe.append(w)
 
161
                    pred[w]=v
 
162
                if w in succ:  return pred,succ,w # found path
 
163
        this_level=reverse_fringe
 
164
        reverse_fringe=[]
 
165
        for v in this_level:
 
166
            for w in Gpred(v):
 
167
                if w not in succ:
 
168
                    succ[w]=v
 
169
                    reverse_fringe.append(w)
 
170
                if w in pred:  return pred,succ,w # found path
 
171
 
 
172
    return False  # no path found
 
173
 
 
174
 
 
175
def single_source_shortest_path(G,source,cutoff=None):
 
176
    """
 
177
    Return list of nodes in a shortest path between source
 
178
    and all other nodes in G reachable from source.
 
179
 
 
180
    There may be more than one shortest path between the
 
181
    source and target nodes - this routine returns only one.
 
182
 
 
183
    cutoff is optional integer depth to stop the search - only
 
184
    paths of length <= cutoff are returned.
 
185
 
 
186
    See also shortest_path and bidirectional_shortest_path.
 
187
    """
 
188
    level=0                  # the current level
 
189
    nextlevel={source:1}       # list of nodes to check at next level
 
190
    paths={source:[source]}  # paths dictionary  (paths to key from source)
 
191
    if cutoff==0:
 
192
        return paths
 
193
    while nextlevel:
 
194
        thislevel=nextlevel
 
195
        nextlevel={}
 
196
        for v in thislevel:
 
197
            for w in G.neighbors(v):
 
198
                if not paths.has_key(w): 
 
199
                    paths[w]=paths[v]+[w]
 
200
                    nextlevel[w]=1
 
201
        level=level+1
 
202
        if (cutoff is not None and cutoff <= level):  break
 
203
    return paths   
 
204
 
 
205
 
 
206
def all_pairs_shortest_path(G,cutoff=None):
 
207
    """ Return dictionary of shortest paths between all nodes in G.
 
208
 
 
209
    The dictionary only has keys for reachable node pairs.
 
210
 
 
211
    cutoff is optional integer depth to stop the search - only
 
212
    paths of length <= cutoff are returned.
 
213
 
 
214
    See also floyd_warshall.
 
215
 
 
216
    """
 
217
    paths={}
 
218
    for n in G:
 
219
        paths[n]=single_source_shortest_path(G,n,cutoff=cutoff)
 
220
    return paths        
 
221
 
 
222
 
 
223
def dijkstra_path(G,source,target):
 
224
    """
 
225
    Returns the shortest path from source to target in a weighted
 
226
    graph G.  Uses a bidirectional version of Dijkstra's algorithm.
 
227
 
 
228
    Edge data must be numerical values for XGraph and XDiGraphs.
 
229
    The weights are assigned to be 1 for Graphs and DiGraphs.
 
230
 
 
231
    See also bidirectional_dijkstra for more information about the algorithm.
 
232
    """
 
233
#    (length,path)=bidirectional_dijkstra(G,source,target) # faster, needs test
 
234
#     return path
 
235
    (length,path)=single_source_dijkstra(G,source)
 
236
    try:
 
237
        return path[target]
 
238
    except KeyError:
 
239
        raise networkx.NetworkXError, \
 
240
              "node %s not reachable from %s"%(source,target)
 
241
 
 
242
 
 
243
def dijkstra_path_length(G,source,target):
 
244
    """
 
245
    Returns the shortest path length from source to target in a weighted
 
246
    graph G.  Uses a bidirectional version of Dijkstra's algorithm.
 
247
 
 
248
    Edge data must be numerical values for XGraph and XDiGraphs.
 
249
    The weights are assigned to be 1 for Graphs and DiGraphs.
 
250
 
 
251
    See also bidirectional_dijkstra for more information about the algorithm.
 
252
 
 
253
    """
 
254
 
 
255
#    (length,path)=bidirectional_dijkstra(G,source,target) # faster, needs test
 
256
#    return length
 
257
    (length,path)=single_source_dijkstra(G,source)
 
258
    try:
 
259
        return length[target]
 
260
    except KeyError:
 
261
        raise networkx.NetworkXError, \
 
262
              "node %s not reachable from %s"%(source,target)
 
263
 
 
264
 
 
265
 
 
266
def bidirectional_dijkstra(G, source, target):
 
267
    """
 
268
    Dijkstra's algorithm for shortest paths using bidirectional search. 
 
269
 
 
270
    Returns a two-tuple (d,p) where d is the distance and p
 
271
    is the path from the source to the target.
 
272
 
 
273
    Distances are calculated as sums of weighted edges traversed.
 
274
 
 
275
    Edges must hold numerical values for XGraph and XDiGraphs.
 
276
    The weights are set to 1 for Graphs and DiGraphs.
 
277
    
 
278
    In practice  bidirectional Dijkstra is much more than twice as fast as 
 
279
    ordinary Dijkstra.
 
280
 
 
281
    Ordinary Dijkstra expands nodes in a sphere-like manner from the
 
282
    source. The radius of this sphere will eventually be the length 
 
283
    of the shortest path. Bidirectional Dijkstra will expand nodes 
 
284
    from both the source and the target, making two spheres of half 
 
285
    this radius. Volume of the first sphere is pi*r*r while the  
 
286
    others are 2*pi*r/2*r/2, making up half the volume. 
 
287
    
 
288
    This algorithm is not guaranteed to work if edge weights
 
289
    are negative or are floating point numbers
 
290
    (overflows and roundoff errors can cause problems). 
 
291
 
 
292
    """
 
293
    if source is None or target is None:
 
294
        raise NetworkXException(
 
295
            "Bidirectional Dijkstra called with no source or target")
 
296
    if source == target: return (0, [source])
 
297
    #Init:   Forward             Backward
 
298
    dists =  [{},                {}]# dictionary of final distances
 
299
    paths =  [{source:[source]}, {target:[target]}] # dictionary of paths 
 
300
    fringe = [[],                []] #heap of (distance, node) tuples for extracting next node to expand
 
301
    seen =   [{source:0},        {target:0} ]#dictionary of distances to nodes seen 
 
302
    #initialize fringe heap
 
303
    heapq.heappush(fringe[0], (0, source)) 
 
304
    heapq.heappush(fringe[1], (0, target))
 
305
    #neighs for extracting correct neighbor information
 
306
    if G.is_directed():
 
307
        neighs = [G.successors_iter, G.predecessors_iter]
 
308
    else:
 
309
        neighs = [G.neighbors_iter, G.neighbors_iter]
 
310
    #variables to hold shortest discovered path
 
311
    #finaldist = 1e30000
 
312
    finalpath = []
 
313
    # if unweighted graph, set the weights to 1 on edges by
 
314
    # introducing a get_edge method
 
315
    if not hasattr(G,"get_edge"): G.get_edge=lambda x,y:1
 
316
    dir = 1
 
317
    while fringe[0] and fringe[1]:
 
318
        # choose direction 
 
319
        # dir == 0 is forward direction and dir == 1 is back
 
320
        dir = 1-dir
 
321
        # extract closest to expand
 
322
        (dist, v )= heapq.heappop(fringe[dir]) 
 
323
        if v in dists[dir]:
 
324
            # Shortest path to v has already been found 
 
325
            continue
 
326
        # update distance
 
327
        dists[dir][v] = dist #equal to seen[dir][v]
 
328
        if v in dists[1-dir]:
 
329
            # if we have scanned v in both directions we are done 
 
330
            # we have now discovered the shortest path
 
331
            return (finaldist,finalpath)
 
332
        for w in neighs[dir](v):
 
333
            if(dir==0): #forward
 
334
                vwLength = dists[dir][v] + G.get_edge(v,w)
 
335
            else: #back, must remember to change v,w->w,v
 
336
                vwLength = dists[dir][v] + G.get_edge(w,v)
 
337
            
 
338
            if w in dists[dir]:
 
339
                if vwLength < dists[dir][w]:
 
340
                    raise ValueError,\
 
341
                        "Contradictory paths found: negative weights?"
 
342
            elif w not in seen[dir] or vwLength < seen[dir][w]:
 
343
                # relaxing        
 
344
                seen[dir][w] = vwLength
 
345
                heapq.heappush(fringe[dir], (vwLength,w)) 
 
346
                paths[dir][w] = paths[dir][v]+[w]
 
347
                if w in seen[0] and w in seen[1]:
 
348
                    #see if this path is better than than the already
 
349
                    #discovered shortest path
 
350
                    totaldist = seen[0][w] + seen[1][w] 
 
351
                    if finalpath == [] or finaldist > totaldist:
 
352
                        finaldist = totaldist
 
353
                        revpath = paths[1][w][:]
 
354
                        revpath.reverse()
 
355
                        finalpath = paths[0][w] + revpath[1:]
 
356
    return False
 
357
 
 
358
 
 
359
#def dijkstra(G,source,target):
 
360
#    return bidirectional_dijkstra(G,source,target)
 
361
 
 
362
 
 
363
def single_source_dijkstra_path(G,source):
 
364
    """
 
365
    Returns the shortest paths from source to all other reachable
 
366
    nodes in a weighted graph G.  Uses Dijkstra's algorithm.
 
367
 
 
368
    Returns a dictionary of shortest path lengths keyed by source.
 
369
    
 
370
    Edge data must be numerical values for XGraph and XDiGraphs.
 
371
    The weights are assigned to be 1 for Graphs and DiGraphs.
 
372
 
 
373
    See also single_source_dijkstra for more information about the algorithm.
 
374
 
 
375
    """
 
376
    (length,path)=single_source_dijkstra(G,source)
 
377
    return path
 
378
 
 
379
 
 
380
def single_source_dijkstra_path_length(G,source):
 
381
    """
 
382
    Returns the shortest path lengths from source to all other
 
383
    reachable nodes in a weighted graph G.  Uses Dijkstra's algorithm.
 
384
 
 
385
    Returns a dictionary of shortest path lengths keyed by source.
 
386
    
 
387
    Edge data must be numerical values for XGraph and XDiGraphs.
 
388
    The weights are assigned to be 1 for Graphs and DiGraphs.
 
389
 
 
390
    See also single_source_dijkstra for more information about the algorithm.
 
391
 
 
392
    """
 
393
    (length,path)=single_source_dijkstra(G,source)
 
394
    return length
 
395
 
 
396
 
 
397
def single_source_dijkstra(G,source,target=None):
 
398
    """
 
399
    Dijkstra's algorithm for shortest paths in a weighted graph G.
 
400
 
 
401
    Use:
 
402
 
 
403
    single_source_dijkstra_path() - shortest path list of nodes 
 
404
 
 
405
    single_source_dijkstra_path_length() - shortest path length
 
406
 
 
407
    Returns a tuple of two dictionaries keyed by node.
 
408
    The first stores distance from the source.
 
409
    The second stores the path from the source to that node.
 
410
 
 
411
    Distances are calculated as sums of weighted edges traversed.
 
412
    Edges must hold numerical values for XGraph and XDiGraphs.
 
413
    The weights are 1 for Graphs and DiGraphs.
 
414
 
 
415
    Optional target argument stops the search when target is found.
 
416
 
 
417
    Based on the Python cookbook recipe (119466) at 
 
418
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/119466
 
419
 
 
420
    This algorithm is not guaranteed to work if edge weights
 
421
    are negative or are floating point numbers
 
422
    (overflows and roundoff errors can cause problems). 
 
423
    
 
424
    See also 'bidirectional_dijkstra_path'
 
425
    """
 
426
    if source==target: return (0, [source])
 
427
    dist = {}  # dictionary of final distances
 
428
    paths = {source:[source]}  # dictionary of paths
 
429
    seen = {source:0} 
 
430
    fringe=[] # use heapq with (distance,label) tuples 
 
431
    heapq.heappush(fringe,(0,source))
 
432
    while fringe:
 
433
        (d,v)=heapq.heappop(fringe)
 
434
        if v in dist: continue # already searched this node.
 
435
        dist[v] = seen[v]
 
436
        if v == target: break
 
437
        for w in G.neighbors(v):
 
438
            vw_dist = dist[v] + G.get_edge(v,w)
 
439
            if w in dist:
 
440
                if vw_dist < dist[w]:
 
441
                    raise ValueError,\
 
442
                          "Contradictory paths found: negative weights?"
 
443
            elif w not in seen or vw_dist < seen[w]:
 
444
                seen[w] = vw_dist
 
445
                heapq.heappush(fringe,(vw_dist,w))
 
446
                paths[w] = paths[v]+[w]
 
447
    return (dist,paths)
 
448
 
 
449
def dijkstra_predecessor_and_distance(G,source):
 
450
    """
 
451
    Same algorithm as for single_source_dijsktra, but returns two
 
452
    dicts representing a list of predecessors of a node and the
 
453
    distance to each node respectively.  The list of predecessors
 
454
    contains more than one element only when there are more than one
 
455
    shortest paths to the key node.
 
456
 
 
457
    This routine is intended for use with the betweenness centrality
 
458
    algorithms in centrality.py.
 
459
    """
 
460
    push=heapq.heappush
 
461
    pop=heapq.heappop
 
462
    dist = {}  # dictionary of final distances
 
463
    pred = {source:[]}  # dictionary of predecessors
 
464
    seen = {source:0} 
 
465
    fringe=[] # use heapq with (distance,label) tuples 
 
466
    push(fringe,(0,source))
 
467
    while fringe:
 
468
        (d,v)=pop(fringe)
 
469
        if v in dist: continue # already searched this node.
 
470
        dist[v] = seen[v]
 
471
        for w in G.neighbors(v):
 
472
            vw_dist = dist[v] + G.get_edge(v,w)
 
473
            if w in dist:
 
474
                if vw_dist < dist[w]:
 
475
                    raise ValueError,\
 
476
                          "Contradictory paths found: negative weights?"
 
477
            elif w not in seen or vw_dist < seen[w]:
 
478
                seen[w] = vw_dist
 
479
                push(fringe,(vw_dist,w))
 
480
                pred[w] = [v]
 
481
            elif vw_dist==seen[w]:
 
482
                pred[w].append(v)
 
483
    return (pred,dist)
 
484
 
 
485
######################################################################
 
486
### Jeff A mods.  Kept very local for now.
 
487
 
 
488
def floyd_warshall_array(graph):
 
489
    """
 
490
    The Floyd-Warshall algorithm for all pairs shortest paths.
 
491
    
 
492
    Returns a tuple (distance,path) containing two arrays of shortest
 
493
    distance and paths as a predecessor matrix.
 
494
 
 
495
    This differs from
 
496
    floyd_warshall only in the types of the return values.  Thus,
 
497
    path[i,j] gives the predecessor at j on a path from i to j.  A
 
498
    value of None indicates that no path exists.  A predecessor of i
 
499
    indicates the beginning of the path.  The advantage of this
 
500
    implementation is that, while running time is O(n^3), running
 
501
    space is O(n^2).
 
502
 
 
503
    This algorithm handles negative weights.
 
504
    """
 
505
 
 
506
    # A weight that's more than any path weight
 
507
    HUGE_VAL = 1
 
508
    for e in graph.edges():
 
509
        HUGE_VAL += abs(graph.get_edge(e[0],e[1]))
 
510
 
 
511
    dist = {}
 
512
    dist_prev = {}
 
513
    pred = {}
 
514
    pred_prev = {}
 
515
    for i in graph:
 
516
        dist[i] = {}
 
517
        dist_prev[i] = {}
 
518
        pred[i] = {}
 
519
        pred_prev[i] = {}
 
520
        for j in graph:
 
521
            dist[i][j] = 0              # arbitrary, just create slot
 
522
            pred[i][j] = 0              # arbitrary, just create slot
 
523
            if i == j:
 
524
                dist_prev[i][j] = 0
 
525
                pred_prev[i][j] = -1
 
526
            elif graph.has_edge(i,j):
 
527
                val = graph.get_edge(i,j)
 
528
                dist_prev[i][j] = val
 
529
                pred_prev[i][j] = i
 
530
            else:
 
531
                # no edge, distinct vertices
 
532
                dist_prev[i][j] = HUGE_VAL
 
533
                pred_prev[i][j] = -1    # None, but has to be numerically comparable
 
534
    for k in graph:
 
535
        for i in graph:
 
536
            for j in graph:
 
537
                dist[i][j] = min(dist_prev[i][j], dist_prev[i][k] + dist_prev[k][j])
 
538
                if dist_prev[i][j] <= dist_prev[i][k] + dist[k][j]:
 
539
                    pred[i][j] = pred_prev[i][j]
 
540
                else:
 
541
                    pred[i][j] = pred_prev[k][j]
 
542
        tmp = dist_prev
 
543
        dist_prev = dist
 
544
        dist = tmp
 
545
 
 
546
        tmp = pred_prev
 
547
        pred_prev = pred
 
548
        pred = tmp
 
549
    # The last time through the loop, we exchanged for nothing, so
 
550
    # return the prev versions, since they're really the current
 
551
    # versions.
 
552
    return dist_prev, pred_prev
 
553
######################################################################
 
554
 
 
555
def floyd_warshall(G,huge=1e30000):
 
556
    """
 
557
    The Floyd-Warshall algorithm for all pairs shortest paths.
 
558
    
 
559
    Returns a tuple (distance,path) containing two dictionaries of shortest
 
560
    distance and predecessor paths.
 
561
 
 
562
    This algorithm is most appropriate for dense graphs.
 
563
    The running time is O(n^3), and running space is O(n^2)
 
564
    where n is the number of nodes in G.  
 
565
 
 
566
    For sparse graphs, see
 
567
 
 
568
    all_pairs_shortest_path
 
569
    all_pairs_shortest_path_length
 
570
 
 
571
    which are based on Dijkstra's algorithm.
 
572
 
 
573
    """
 
574
    # dictionary-of-dictionaries representation for dist and pred
 
575
    dist={} 
 
576
    # initialize path distance dictionary to be the adjacency matrix
 
577
    # but with sentinal value "huge" where there is no edge
 
578
    # also set the distance to self to 0 (zero diagonal)
 
579
    pred={}
 
580
    # initialize predecessor dictionary 
 
581
    for u in G.nodes():
 
582
        dist[u]={}
 
583
        pred[u]={}
 
584
        for v in G.nodes():
 
585
            if G.has_edge(u,v):
 
586
                dist[u][v]=G.get_edge(u,v)
 
587
                pred[u][v]=u
 
588
            else:
 
589
                dist[u][v]=huge
 
590
                pred[u][v]=None 
 
591
        dist[u][u]=0  # set 0 distance to self
 
592
 
 
593
    for w in G.nodes():
 
594
        for u in G.nodes():
 
595
            for v in G.nodes():
 
596
                if dist[u][v] > dist[u][w] + dist[w][v]:
 
597
                    dist[u][v] = dist[u][w] + dist[w][v]
 
598
                    pred[u][v] = pred[w][v]
 
599
    return dist,pred
 
600
 
 
601
 
 
602
def predecessor(G,source,target=None,cutoff=None,return_seen=None):
 
603
    """ Returns dictionary of predecessors for the path from source to all
 
604
    nodes in G.  
 
605
 
 
606
    Optional target returns only predecessors between source and target.
 
607
    Cutoff is a limit on the number of hops traversed.
 
608
 
 
609
    Example for the path graph 0-1-2-3
 
610
    
 
611
    >>> G=path_graph(4)
 
612
    >>> print G.nodes()
 
613
    [0, 1, 2, 3]
 
614
    >>> predecessor(G,0)
 
615
    {0: [], 1: [0], 2: [1], 3: [2]}
 
616
 
 
617
    """
 
618
    level=0                  # the current level
 
619
    nextlevel=[source]       # list of nodes to check at next level
 
620
    seen={source:level}      # level (number of hops) when seen in BFS
 
621
    pred={source:[]}         # predecessor dictionary
 
622
    while nextlevel:
 
623
        level=level+1
 
624
        thislevel=nextlevel
 
625
        nextlevel=[]
 
626
        for v in thislevel:
 
627
            for w in G.neighbors(v):
 
628
                if (not seen.has_key(w)): 
 
629
                    pred[w]=[v]
 
630
                    seen[w]=level
 
631
                    nextlevel.append(w)
 
632
                elif (seen[w]==level):# add v to predecessor list if it 
 
633
                    pred[w].append(v) # is at the correct level
 
634
        if (cutoff and cutoff <= level):
 
635
            break
 
636
 
 
637
    if target is not None:
 
638
        if return_seen:
 
639
            if not pred.has_key(target): return ([],-1)  # No predecessor
 
640
            return (pred[target],seen[target])
 
641
        else:
 
642
            if not pred.has_key(target): return []  # No predecessor
 
643
            return pred[target]
 
644
    else:
 
645
        if return_seen:
 
646
            return (pred,seen)
 
647
        else:
 
648
            return pred
 
649
 
 
650
 
 
651
def _test_suite():
 
652
    import doctest
 
653
    suite = doctest.DocFileSuite('tests/shortest_path.txt',package='networkx')
 
654
    return suite
 
655
 
 
656
 
 
657
if __name__ == "__main__":
 
658
    import os
 
659
    import sys
 
660
    import unittest
 
661
    if sys.version_info[:2] < (2, 4):
 
662
        print "Python version 2.4 or later required for tests (%d.%d detected)." %  sys.version_info[:2]
 
663
        sys.exit(-1)
 
664
    # directory of networkx package (relative to this)
 
665
    nxbase=sys.path[0]+os.sep+os.pardir
 
666
    sys.path.insert(0,nxbase) # prepend to search path
 
667
    unittest.TextTestRunner().run(_test_suite())
 
668