Skip to content

Commit 0b08df4

Browse files
committed
feat: add graph algorithem
1 parent a98308d commit 0b08df4

File tree

2 files changed

+52
-30
lines changed

2 files changed

+52
-30
lines changed

pydatastructs/graphs/algorithms.py

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -682,15 +682,12 @@ def _depth_first_search_adjacency_list(
682682

683683
_depth_first_search_adjacency_matrix = _depth_first_search_adjacency_list
684684

685-
def shortest_paths(graph: Graph, algorithm: str,
686-
source: str, target: str="",
687-
**kwargs) -> tuple:
685+
def shortest_paths(graph: Graph, algorithm: str, source: str, target: str="", **kwargs) -> tuple:
688686
"""
689687
Finds shortest paths in the given graph from a given source.
690688
691689
Parameters
692690
==========
693-
694691
graph: Graph
695692
The graph under consideration.
696693
algorithm: str
@@ -700,8 +697,10 @@ def shortest_paths(graph: Graph, algorithm: str,
700697
'bellman_ford' -> Bellman-Ford algorithm as given in [1].
701698
702699
'dijkstra' -> Dijkstra algorithm as given in [2].
700+
701+
'A_star' -> A* algorithm as given in [3].
703702
source: str
704-
The name of the source the node.
703+
The name of the source node.
705704
target: str
706705
The name of the target node.
707706
Optional, by default, all pair shortest paths
@@ -713,35 +712,12 @@ def shortest_paths(graph: Graph, algorithm: str,
713712
714713
Returns
715714
=======
716-
717715
(distances, predecessors): (dict, dict)
718716
If target is not provided and algorithm used
719-
is 'bellman_ford'/'dijkstra'.
717+
is 'bellman_ford'/'dijkstra'/'A_star'.
720718
(distances[target], predecessors): (float, dict)
721719
If target is provided and algorithm used is
722-
'bellman_ford'/'dijkstra'.
723-
724-
Examples
725-
========
726-
727-
>>> from pydatastructs import Graph, AdjacencyListGraphNode
728-
>>> from pydatastructs import shortest_paths
729-
>>> V1 = AdjacencyListGraphNode("V1")
730-
>>> V2 = AdjacencyListGraphNode("V2")
731-
>>> V3 = AdjacencyListGraphNode("V3")
732-
>>> G = Graph(V1, V2, V3)
733-
>>> G.add_edge('V2', 'V3', 10)
734-
>>> G.add_edge('V1', 'V2', 11)
735-
>>> shortest_paths(G, 'bellman_ford', 'V1')
736-
({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
737-
>>> shortest_paths(G, 'dijkstra', 'V1')
738-
({'V2': 11, 'V3': 21, 'V1': 0}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
739-
740-
References
741-
==========
742-
743-
.. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
744-
.. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
720+
'bellman_ford'/'dijkstra'/'A_star'.
745721
"""
746722
raise_if_backend_is_not_python(
747723
shortest_paths, kwargs.get('backend', Backend.PYTHON))
@@ -811,6 +787,49 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str):
811787

812788
_dijkstra_adjacency_matrix = _dijkstra_adjacency_list
813789

790+
def _a_star_adjacency_list(graph: Graph, source: str, target: str) -> tuple:
791+
distances, predecessor = {}, {}
792+
793+
for v in graph.vertices:
794+
distances[v] = float('inf')
795+
predecessor[v] = None
796+
distances[source] = 0
797+
798+
from pydatastructs.miscellaneous_data_structures.queue import PriorityQueue
799+
pq = PriorityQueue(implementation='binomial_heap')
800+
pq.push(source, 0)
801+
802+
def heuristic(node: str, goal: str) -> float:
803+
try:
804+
x1, y1 = map(int, node.split(','))
805+
x2, y2 = map(int, goal.split(','))
806+
return abs(x1 - x2) + abs(y1 - y2)
807+
except ValueError:
808+
raise ValueError(f"Invalid node format: {node}. Expected 'x,y'.")
809+
810+
while not pq.is_empty:
811+
current = pq.pop()
812+
813+
if current == target:
814+
return (distances[target], predecessor)
815+
816+
neighbors = graph.neighbors(current)
817+
for neighbor in neighbors:
818+
edge = graph.get_edge(current, neighbor.name)
819+
if edge:
820+
new_dist = distances[current] + edge.value
821+
if new_dist < distances[neighbor.name]:
822+
distances[neighbor.name] = new_dist
823+
predecessor[neighbor.name] = current
824+
pq.push(neighbor.name, new_dist + heuristic(neighbor.name, target))
825+
826+
if distances[target] == float('inf'):
827+
raise ValueError(f"Either source '{source}' and target '{target}' have no path between them.")
828+
829+
return (distances, predecessor)
830+
831+
_a_star_adjacency_matrix = _a_star_adjacency_list
832+
814833
def all_pair_shortest_paths(graph: Graph, algorithm: str,
815834
**kwargs) -> tuple:
816835
"""

pydatastructs/graphs/tests/test_algorithms.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import pytest
12
from pydatastructs import (breadth_first_search, Graph,
23
breadth_first_search_parallel, minimum_spanning_tree,
34
minimum_spanning_tree_parallel, strongly_connected_components,
@@ -321,6 +322,8 @@ def _test_shortest_paths_negative_edges(ds, algorithm):
321322
_test_shortest_paths_negative_edges("Matrix", 'bellman_ford')
322323
_test_shortest_paths_positive_edges("List", 'dijkstra')
323324
_test_shortest_paths_positive_edges("Matrix", 'dijkstra')
325+
_test_shortest_paths_positive_edges("List", 'A_star')
326+
_test_shortest_paths_positive_edges("Matrix", 'A_star')
324327

325328
def test_all_pair_shortest_paths():
326329

0 commit comments

Comments
 (0)