Suu*_*hgi 8 python dijkstra shortest-path networkx osmnx
我有一个很大的osmnx
(networkx)图,并且nx.all_pairs_dijkstra_path_length
需要很长时间才能计算。
有哪些可能性可以加快计算速度?
import osmnx as ox\nimport networkx as nx\n
Run Code Online (Sandbox Code Playgroud)\n让我们占领这个区域
\ncoords, dist = (51.5178, 9.9601), 9000\nG = ox.graph.graph_from_point( coords, dist=dist, network_type=\'drive\', simplify=True)\nG = ox.add_edge_speeds(G)\nG = ox.add_edge_travel_times(G)\n
Run Code Online (Sandbox Code Playgroud)\n以nx.all_pairs_dijkstra_path_length
作为基线。
为此,让我们创建简写bench_nx
:
bench_nx = lambda G, weight=\'travel_time\': sum((l:=[ d for t in nx.all_pairs_dijkstra_path_length(G, weight=weight) for d in t[1].values() ])) / len(l)\n
Run Code Online (Sandbox Code Playgroud)\nbench_nx(G)\n582.2692172953298\n
Run Code Online (Sandbox Code Playgroud)\n%timeit -n 3 -r 2 bench_nx(G)\n53.7 s \xc2\xb1 101 ms per loop (mean \xc2\xb1 std. dev. of 2 runs, 3 loops each)\n
Run Code Online (Sandbox Code Playgroud)\ndef mp_all_pairs_dijkstra_path_length(G, cutoff=None, weight=\'weight\'):\n """\n Multi-core version of\n nx.all_pairs_dijkstra_path_length\n """\n\n import multiprocessing as mp\n from functools import partial\n\n f = partial(nx.single_source_dijkstra_path_length,\n G, cutoff=cutoff, weight=weight)\n\n with mp.Pool(mp.cpu_count()) as p:\n \n lengths = p.map(f, G)\n for n, l in zip(G, lengths):\n yield n, l\n
Run Code Online (Sandbox Code Playgroud)\nbench_mp = lambda G, weight=\'travel_time\': sum((l:=[ d for t in mp_all_pairs_dijkstra_path_length(G, weight=weight) for d in t[1].values() ])) / len(l)\n
Run Code Online (Sandbox Code Playgroud)\nbench_mp(G)\n582.2692172953298\n
Run Code Online (Sandbox Code Playgroud)\n%timeit -n 3 -r 2 bench_mp(G)\n20.2 s \xc2\xb1 754 ms per loop (mean \xc2\xb1 std. dev. of 2 runs, 3 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n图表越大,这里的优势似乎就越大。
\n使用图形工具我们可以加快速度。
\n图形工具使用整数索引顶点和边。因此我在这里创建两个字典
\nnx_node
->gt_idx
u,v,k
(边缘)->gt_idx
能够映射从nx
到gt
。
import graph_tool.all as gt\nfrom collections import defaultdict\n\nG_gt = gt.Graph(directed=G.is_directed())\n\n# "Dict" [idx] = weight\nG_gt_weights = G_gt.new_edge_property("double")\n\n# mapping of nx vertices to gt indices\nvertices = {}\nfor node in G.nodes:\n \n v = G_gt.add_vertex()\n vertices[node] = v\n\n# mapping of nx edges to gt edge indices\nedges = defaultdict(lambda: defaultdict(dict))\n\nfor src, dst, k, data in G.edges(data=True, keys=True):\n \n # Look up the vertex idxs from our vertices mapping and add edge.\n e = G_gt.add_edge(vertices[src], vertices[dst])\n edges[src][dst][k] = e\n \n # Save weights in property map\n G_gt_weights[e] = data[\'travel_time\']\n
Run Code Online (Sandbox Code Playgroud)\n注意:我添加了截止点,1e50
因为gt
将不可到达的目的地设置为 distance 1.79e308
。
bench_gt = lambda G, weights: sum((l:=[ d for l in gt.shortest_distance(G, weights=weights) for d in l if 0 < d <= 1e50 ])) / len(l)\n
Run Code Online (Sandbox Code Playgroud)\nbench_gt(G_gt, G_gt_weights)\n582.4092142257183\n
Run Code Online (Sandbox Code Playgroud)\n%timeit -n 3 -r 2 bench_gt(G_gt, G_gt_weights)\n4.76 s \xc2\xb1 27.4 ms per loop (mean \xc2\xb1 std. dev. of 2 runs, 3 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n这至少提高了 11 倍。
\n事实证明distance_histogram()
,如果在编译时启用,则能够进行二次采样并并行运行!
import osmnx as ox\nimport networkx as nx\n
Run Code Online (Sandbox Code Playgroud)\ncoords, dist = (51.5178, 9.9601), 9000\nG = ox.graph.graph_from_point( coords, dist=dist, network_type=\'drive\', simplify=True)\nG = ox.add_edge_speeds(G)\nG = ox.add_edge_travel_times(G)\n
Run Code Online (Sandbox Code Playgroud)\n注:由于第一个 bin 内的差异,存在小偏差。如果有人知道原因,我很高兴知道!\n编辑这似乎是graph-tools 中的一个错误。
bench_nx = lambda G, weight=\'travel_time\': sum((l:=[ d for t in nx.all_pairs_dijkstra_path_length(G, weight=weight) for d in t[1].values() ])) / len(l)\n
Run Code Online (Sandbox Code Playgroud)\n这又是 50 倍,总加速为 241 倍!
\n