1818 'breadth_first_traverse' ,
1919 'breadth_first_paths' ,
2020 'shortest_path' ,
21+ 'astar_lightest_path' ,
2122 'astar_shortest_path' ,
2223 'dijkstra_distances' ,
2324 'dijkstra_path'
@@ -323,6 +324,8 @@ def shortest_path(adjacency, root, goal):
323324
324325
325326def reconstruct_path (came_from , current ):
327+ if current not in came_from :
328+ return None
326329 total_path = [current ]
327330 while current in came_from :
328331 current = came_from [current ]
@@ -331,97 +334,124 @@ def reconstruct_path(came_from, current):
331334 return total_path
332335
333336
334- def astar_shortest_path ( network , root , goal ):
335- """Find the shortest path between two vertices of a network using the A* search algorithm.
337+ def astar_lightest_path ( adjacency , weights , heuristic , root , goal ):
338+ """Find the path of least weight between two vertices of a graph using the A* search algorithm.
336339
337340 Parameters
338341 ----------
339- network : instance of the Network class
342+ adjacency : dict
343+ An adjacency dictionary. Each key represents a vertex
344+ and maps to a list of neighboring vertex keys.
345+ weights : dict
346+ A dictionary of edge weights.
347+ heuristic : dict
348+ A dictionary of guesses of weights of paths from a node to the goal.
340349 root : hashable
341- The identifier of the starting node .
350+ The start vertex .
342351 goal : hashable
343- The identifier of the ending node .
352+ The end vertex .
344353
345354 Returns
346355 -------
347356 list, None
348357 The path from root to goal, or None, if no path exists between the vertices.
349358
350- Examples
351- --------
352- >>>
353-
354359 References
355360 ----------
356361 https://en.wikipedia.org/wiki/A*_search_algorithm
357362 """
358- root_coords = network .vertex_coordinates (root )
359- goal_coords = network .vertex_coordinates (goal )
360-
361- # The set of nodes already evaluated
362363 visited_set = set ()
363364
364- # The set of currently discovered nodes that are not evaluated yet.
365- # Initially, only the start node is known.
366365 candidates_set = {root }
367366 best_candidate_heap = PriorityQueue ()
368- best_candidate_heap .put ((0 , root ))
367+ best_candidate_heap .put ((heuristic [ root ] , root ))
369368
370- # For each node, which node it can most efficiently be reached from.
371- # If a node can be reached from many nodes, came_from will eventually contain the
372- # most efficient previous step.
373369 came_from = dict ()
374370
375- # g_score is a dict mapping node index to the cost of getting from the root node to that node.
376- # The default value is Infinity.
377- # The cost of going from start to start is zero.
378371 g_score = dict ()
379-
380- for v in network .vertices ():
381- g_score [v ] = float ("inf" )
382-
372+ for v in adjacency :
373+ g_score [v ] = float ('inf' )
383374 g_score [root ] = 0
384375
385- # For each node, the total cost of getting from the start node to the goal
386- # by passing by that node. That value is partly known, partly heuristic.
387- # The default value of f_score is Infinity
388- f_score = dict ()
389-
390- for v in network .vertices ():
391- f_score [v ] = float ("inf" )
392-
393- # For the first node, that value is completely heuristic.
394- f_score [root ] = distance_point_point (root_coords , goal_coords )
395-
396376 while not best_candidate_heap .empty ():
397377 _ , current = best_candidate_heap .get ()
398378 if current == goal :
399379 break
400380
401381 visited_set .add (current )
402- current_coords = network .vertex_coordinates (current )
403- for neighbor in network .vertex_neighbors (current ):
382+ for neighbor in adjacency [current ]:
404383 if neighbor in visited_set :
405- continue # Ignore the neighbor which is already evaluated.
384+ continue
406385
407- # The distance from start to a neighbor
408- neighbor_coords = network .vertex_coordinates (neighbor )
409- tentative_gScore = g_score [current ] + distance_point_point (current_coords , neighbor_coords )
410- if neighbor not in candidates_set : # Discover a new node
386+ tentative_g_score = g_score [current ] + weights [(current , neighbor )]
387+ if neighbor not in candidates_set :
411388 candidates_set .add (neighbor )
412- elif tentative_gScore >= g_score [neighbor ]:
389+ elif tentative_g_score >= g_score [neighbor ]:
413390 continue
414391
415- # This path is the best until now. Record it!
416392 came_from [neighbor ] = current
417- g_score [neighbor ] = tentative_gScore
418- new_fscore = g_score [neighbor ] + distance_point_point (neighbor_coords , goal_coords )
419- f_score [neighbor ] = new_fscore
420- best_candidate_heap .put ((new_fscore , neighbor ))
393+ g_score [neighbor ] = tentative_g_score
394+ new_f_score = g_score [neighbor ] + heuristic [neighbor ]
395+ best_candidate_heap .put ((new_f_score , neighbor ))
421396
422397 return reconstruct_path (came_from , goal )
423398
424399
400+ def _get_coordinates (key , structure ):
401+ if hasattr (structure , 'node_attributes' ):
402+ return structure .node_attributes (key , 'xyz' )
403+ if hasattr (structure , 'vertex_coordinates' ):
404+ return structure .vertex_coordinates (key )
405+ raise Exception ("Coordinates cannot be found for object of type {}" .format (type (structure )))
406+
407+
408+ def _get_points (structure ):
409+ if hasattr (structure , 'nodes' ):
410+ return structure .nodes ()
411+ if hasattr (structure , 'vertices' ):
412+ return structure .vertices ()
413+ raise Exception ("Points cannot be found for object of type {}" .format (type (structure )))
414+
415+
416+ def astar_shortest_path (network , root , goal ):
417+ """Find the shortest path between two vertices of a network or mesh using the A* search algorithm.
418+
419+ Parameters
420+ ----------
421+ network : instance of the Network or Mesh class
422+ root : hashable
423+ The identifier of the starting node.
424+ goal : hashable
425+ The identifier of the ending node.
426+
427+ Returns
428+ -------
429+ list, None
430+ The path from root to goal, or None, if no path exists between the vertices.
431+
432+ References
433+ ----------
434+ https://en.wikipedia.org/wiki/A*_search_algorithm
435+ """
436+ adjacency = network .adjacency
437+ weights = {}
438+ for u , v in network .edges ():
439+ u_coords = _get_coordinates (u , network )
440+ v_coords = _get_coordinates (v , network )
441+ distance = distance_point_point (u_coords , v_coords )
442+ weights [(u , v )] = distance
443+ weights [(v , u )] = distance
444+
445+ heuristic = {}
446+ goal_coords = _get_coordinates (goal , network )
447+ points = _get_points (network )
448+ for u in points :
449+ u_coords = _get_coordinates (u , network )
450+ heuristic [u ] = distance_point_point (u_coords , goal_coords )
451+
452+ return astar_lightest_path (adjacency , weights , heuristic , root , goal )
453+
454+
425455def dijkstra_distances (adjacency , weight , target ):
426456 """Compute Dijkstra distances from all vertices in a connected set to one target vertex.
427457
0 commit comments