2010-08-26 04:00:03 +08:00
|
|
|
"""
|
|
|
|
|
Graph utilities and algorithms
|
|
|
|
|
|
2010-11-06 22:42:55 +08:00
|
|
|
Graphs are represented with their adjacency matrices, preferably using
|
2010-08-26 04:00:03 +08:00
|
|
|
sparse matrices.
|
|
|
|
|
"""
|
|
|
|
|
|
2010-11-06 22:42:55 +08:00
|
|
|
# Authors: Aric Hagberg <hagberg@lanl.gov>
|
2010-08-26 04:00:03 +08:00
|
|
|
# Gael Varoquaux <gael.varoquaux@normalesup.org>
|
2013-02-27 07:25:51 +08:00
|
|
|
# Jake Vanderplas <vanderplas@astro.washington.edu>
|
2013-04-30 14:23:46 +08:00
|
|
|
# License: BSD 3 clause
|
2010-08-26 04:00:03 +08:00
|
|
|
|
|
|
|
|
from scipy import sparse
|
|
|
|
|
|
2017-07-01 20:57:54 +08:00
|
|
|
from .graph_shortest_path import graph_shortest_path # noqa
|
2011-12-20 02:59:00 +08:00
|
|
|
|
2011-05-17 16:26:46 +08:00
|
|
|
|
|
|
|
|
###############################################################################
|
2010-08-26 04:00:03 +08:00
|
|
|
# Path and connected component analysis.
|
|
|
|
|
# Code adapted from networkx
|
|
|
|
|
|
|
|
|
|
def single_source_shortest_path_length(graph, source, cutoff=None):
|
|
|
|
|
"""Return the shortest path length from source to all reachable nodes.
|
|
|
|
|
|
|
|
|
|
Returns a dictionary of shortest path lengths keyed by target.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2016-11-25 17:59:22 +08:00
|
|
|
graph : sparse matrix or 2D array (preferably LIL matrix)
|
2012-02-15 17:01:18 +08:00
|
|
|
Adjacency matrix of the graph
|
2018-01-11 06:11:17 +08:00
|
|
|
source : integer
|
2010-08-26 04:00:03 +08:00
|
|
|
Starting node for path
|
|
|
|
|
cutoff : integer, optional
|
|
|
|
|
Depth to stop the search - only
|
|
|
|
|
paths of length <= cutoff are returned.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
2011-09-02 17:00:02 +08:00
|
|
|
>>> from sklearn.utils.graph import single_source_shortest_path_length
|
2010-08-26 04:00:03 +08:00
|
|
|
>>> import numpy as np
|
|
|
|
|
>>> graph = np.array([[ 0, 1, 0, 0],
|
|
|
|
|
... [ 1, 0, 1, 0],
|
|
|
|
|
... [ 0, 1, 0, 1],
|
|
|
|
|
... [ 0, 0, 1, 0]])
|
2016-12-28 02:50:25 +08:00
|
|
|
>>> list(sorted(single_source_shortest_path_length(graph, 0).items()))
|
|
|
|
|
[(0, 0), (1, 1), (2, 2), (3, 3)]
|
|
|
|
|
>>> graph = np.ones((6, 6))
|
|
|
|
|
>>> list(sorted(single_source_shortest_path_length(graph, 2).items()))
|
|
|
|
|
[(0, 1), (1, 1), (2, 0), (3, 1), (4, 1), (5, 1)]
|
2010-08-26 04:00:03 +08:00
|
|
|
"""
|
|
|
|
|
if sparse.isspmatrix(graph):
|
|
|
|
|
graph = graph.tolil()
|
|
|
|
|
else:
|
|
|
|
|
graph = sparse.lil_matrix(graph)
|
2011-05-17 16:26:46 +08:00
|
|
|
seen = {} # level (number of hops) when seen in BFS
|
|
|
|
|
level = 0 # the current level
|
|
|
|
|
next_level = [source] # dict of nodes to check at next level
|
2010-08-26 04:00:03 +08:00
|
|
|
while next_level:
|
2011-05-17 16:26:46 +08:00
|
|
|
this_level = next_level # advance to next level
|
|
|
|
|
next_level = set() # and start a new list (fringe)
|
2010-08-26 04:00:03 +08:00
|
|
|
for v in this_level:
|
2010-11-06 22:42:55 +08:00
|
|
|
if v not in seen:
|
2011-05-17 16:26:46 +08:00
|
|
|
seen[v] = level # set the level of vertex v
|
2012-02-26 02:26:41 +08:00
|
|
|
next_level.update(graph.rows[v])
|
2010-08-26 04:00:03 +08:00
|
|
|
if cutoff is not None and cutoff <= level:
|
|
|
|
|
break
|
|
|
|
|
level += 1
|
|
|
|
|
return seen # return all path lengths as dictionary
|