Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a71e12f

Browse files
authoredMay 30, 2020
Implemented Bellman-Ford algorithm (#283)
1 parent 7e3a3cd commit a71e12f

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed
 

‎pydatastructs/graphs/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
minimum_spanning_tree,
1414
minimum_spanning_tree_parallel,
1515
strongly_connected_components,
16-
depth_first_search
16+
depth_first_search,
17+
shortest_paths
1718
)
1819

1920
__all__.extend(algorithms.__all__)

‎pydatastructs/graphs/algorithms.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
'minimum_spanning_tree',
1818
'minimum_spanning_tree_parallel',
1919
'strongly_connected_components',
20-
'depth_first_search'
20+
'depth_first_search',
21+
'shortest_paths'
2122
]
2223

2324
Stack = Queue = deque
@@ -637,3 +638,90 @@ def _depth_first_search_adjacency_list(
637638
return None
638639

639640
_depth_first_search_adjacency_matrix = _depth_first_search_adjacency_list
641+
642+
def shortest_paths(graph: Graph, algorithm: str,
643+
source: str, target: str="") -> tuple:
644+
"""
645+
Finds shortest paths in the given graph from a given source.
646+
647+
Parameters
648+
==========
649+
650+
graph: Graph
651+
The graph under consideration.
652+
algorithm: str
653+
The algorithm to be used. Currently, the following algorithms
654+
are implemented,
655+
'bellman_ford' -> Bellman-Ford algorithm as given in [1].
656+
source: str
657+
The name of the source the node.
658+
target: str
659+
The name of the target node.
660+
Optional, by default, all pair shortest paths
661+
are returned.
662+
663+
Returns
664+
=======
665+
666+
(distances, predecessors): (dict, dict)
667+
If target is not provided and algorithm used
668+
is 'bellman_ford'.
669+
(distances[target], predecessors): (float, dict)
670+
If target is provided and algorithm used is
671+
'bellman_ford'.
672+
673+
Examples
674+
========
675+
676+
>>> from pydatastructs import Graph, AdjacencyListGraphNode
677+
>>> from pydatastructs import shortest_paths
678+
>>> V1 = AdjacencyListGraphNode("V1")
679+
>>> V2 = AdjacencyListGraphNode("V2")
680+
>>> V3 = AdjacencyListGraphNode("V3")
681+
>>> G = Graph(V1, V2, V3)
682+
>>> G.add_edge('V2', 'V3', 10)
683+
>>> G.add_edge('V1', 'V2', 11)
684+
>>> shortest_paths(G, 'bellman_ford', 'V1')
685+
({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})
686+
687+
References
688+
==========
689+
690+
.. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
691+
"""
692+
import pydatastructs.graphs.algorithms as algorithms
693+
func = "_" + algorithm + "_" + graph._impl
694+
if not hasattr(algorithms, func):
695+
raise NotImplementedError(
696+
"Currently %s algorithm isn't implemented for "
697+
"finding shortest paths in graphs."%(algorithm))
698+
return getattr(algorithms, func)(graph, source, target)
699+
700+
def _bellman_ford_adjacency_list(graph: Graph, source: str, target: str) -> tuple:
701+
distances, predecessor = dict(), dict()
702+
703+
for v in graph.vertices:
704+
distances[v] = float('inf')
705+
predecessor[v] = None
706+
distances[source] = 0
707+
708+
edges = graph.edge_weights.values()
709+
for _ in range(len(graph.vertices) - 1):
710+
for edge in edges:
711+
u, v = edge.source.name, edge.target.name
712+
w = edge.value
713+
if distances[u] + edge.value < distances[v]:
714+
distances[v] = distances[u] + w
715+
predecessor[v] = u
716+
717+
for edge in edges:
718+
u, v = edge.source.name, edge.target.name
719+
w = edge.value
720+
if distances[u] + w < distances[v]:
721+
raise ValueError("Graph contains a negative weight cycle.")
722+
723+
if target != "":
724+
return (distances[target], predecessor)
725+
return (distances, predecessor)
726+
727+
_bellman_ford_adjacency_matrix = _bellman_ford_adjacency_list

‎pydatastructs/graphs/tests/test_algorithms.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from pydatastructs import (breadth_first_search, Graph,
22
breadth_first_search_parallel, minimum_spanning_tree,
33
minimum_spanning_tree_parallel, strongly_connected_components,
4-
depth_first_search)
5-
4+
depth_first_search, shortest_paths)
5+
from pydatastructs.utils.raises_util import raises
66

77
def test_breadth_first_search():
88

@@ -258,3 +258,34 @@ def path_finder(curr_node, next_node, dest_node, parent, path):
258258

259259
_test_depth_first_search("List")
260260
_test_depth_first_search("Matrix")
261+
262+
def test_shortest_paths():
263+
264+
def _test_shortest_paths(ds, algorithm):
265+
import pydatastructs.utils.misc_util as utils
266+
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
267+
vertices = [GraphNode('S'), GraphNode('C'),
268+
GraphNode('SLC'), GraphNode('SF'),
269+
GraphNode('D')]
270+
271+
graph = Graph(*vertices)
272+
graph.add_edge('S', 'SLC', 2)
273+
graph.add_edge('C', 'S', 4)
274+
graph.add_edge('C', 'D', 2)
275+
graph.add_edge('SLC', 'C', 2)
276+
graph.add_edge('SLC', 'D', 3)
277+
graph.add_edge('SF', 'SLC', 2)
278+
graph.add_edge('SF', 'S', 2)
279+
graph.add_edge('D', 'SF', 3)
280+
dist, pred = shortest_paths(graph, algorithm, 'SLC')
281+
assert dist == {'S': 6, 'C': 2, 'SLC': 0, 'SF': 6, 'D': 3}
282+
assert pred == {'S': 'C', 'C': 'SLC', 'SLC': None, 'SF': 'D', 'D': 'SLC'}
283+
dist, pred = shortest_paths(graph, algorithm, 'SLC', 'SF')
284+
assert dist == 6
285+
assert pred == {'S': 'C', 'C': 'SLC', 'SLC': None, 'SF': 'D', 'D': 'SLC'}
286+
graph.remove_edge('SLC', 'D')
287+
graph.add_edge('D', 'SLC', -10)
288+
assert raises(ValueError, lambda: shortest_paths(graph, 'bellman_ford', 'SLC'))
289+
290+
_test_shortest_paths("List", 'bellman_ford')
291+
_test_shortest_paths("Matrix", 'bellman_ford')

0 commit comments

Comments
 (0)
Please sign in to comment.