Skip to content

Implemented Bellman-Ford algorithm #283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pydatastructs/graphs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
minimum_spanning_tree,
minimum_spanning_tree_parallel,
strongly_connected_components,
depth_first_search
depth_first_search,
shortest_paths
)

__all__.extend(algorithms.__all__)
90 changes: 89 additions & 1 deletion pydatastructs/graphs/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
'minimum_spanning_tree',
'minimum_spanning_tree_parallel',
'strongly_connected_components',
'depth_first_search'
'depth_first_search',
'shortest_paths'
]

Stack = Queue = deque
Expand Down Expand Up @@ -637,3 +638,90 @@ def _depth_first_search_adjacency_list(
return None

_depth_first_search_adjacency_matrix = _depth_first_search_adjacency_list

def shortest_paths(graph: Graph, algorithm: str,
source: str, target: str="") -> tuple:
"""
Finds shortest paths in the given graph from a given source.

Parameters
==========

graph: Graph
The graph under consideration.
algorithm: str
The algorithm to be used. Currently, the following algorithms
are implemented,
'bellman_ford' -> Bellman-Ford algorithm as given in [1].
source: str
The name of the source the node.
target: str
The name of the target node.
Optional, by default, all pair shortest paths
are returned.

Returns
=======

(distances, predecessors): (dict, dict)
If target is not provided and algorithm used
is 'bellman_ford'.
(distances[target], predecessors): (float, dict)
If target is provided and algorithm used is
'bellman_ford'.

Examples
========

>>> from pydatastructs import Graph, AdjacencyListGraphNode
>>> from pydatastructs import shortest_paths
>>> V1 = AdjacencyListGraphNode("V1")
>>> V2 = AdjacencyListGraphNode("V2")
>>> V3 = AdjacencyListGraphNode("V3")
>>> G = Graph(V1, V2, V3)
>>> G.add_edge('V2', 'V3', 10)
>>> G.add_edge('V1', 'V2', 11)
>>> shortest_paths(G, 'bellman_ford', 'V1')
({'V1': 0, 'V2': 11, 'V3': 21}, {'V1': None, 'V2': 'V1', 'V3': 'V2'})

References
==========

.. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
"""
import pydatastructs.graphs.algorithms as algorithms
func = "_" + algorithm + "_" + graph._impl
if not hasattr(algorithms, func):
raise NotImplementedError(
"Currently %s algorithm isn't implemented for "
"finding shortest paths in graphs."%(algorithm))
return getattr(algorithms, func)(graph, source, target)

def _bellman_ford_adjacency_list(graph: Graph, source: str, target: str) -> tuple:
distances, predecessor = dict(), dict()

for v in graph.vertices:
distances[v] = float('inf')
predecessor[v] = None
distances[source] = 0

edges = graph.edge_weights.values()
for _ in range(len(graph.vertices) - 1):
for edge in edges:
u, v = edge.source.name, edge.target.name
w = edge.value
if distances[u] + edge.value < distances[v]:
distances[v] = distances[u] + w
predecessor[v] = u

for edge in edges:
u, v = edge.source.name, edge.target.name
w = edge.value
if distances[u] + w < distances[v]:
raise ValueError("Graph contains a negative weight cycle.")

if target != "":
return (distances[target], predecessor)
return (distances, predecessor)

_bellman_ford_adjacency_matrix = _bellman_ford_adjacency_list
35 changes: 33 additions & 2 deletions pydatastructs/graphs/tests/test_algorithms.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from pydatastructs import (breadth_first_search, Graph,
breadth_first_search_parallel, minimum_spanning_tree,
minimum_spanning_tree_parallel, strongly_connected_components,
depth_first_search)

depth_first_search, shortest_paths)
from pydatastructs.utils.raises_util import raises

def test_breadth_first_search():

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

_test_depth_first_search("List")
_test_depth_first_search("Matrix")

def test_shortest_paths():

def _test_shortest_paths(ds, algorithm):
import pydatastructs.utils.misc_util as utils
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
vertices = [GraphNode('S'), GraphNode('C'),
GraphNode('SLC'), GraphNode('SF'),
GraphNode('D')]

graph = Graph(*vertices)
graph.add_edge('S', 'SLC', 2)
graph.add_edge('C', 'S', 4)
graph.add_edge('C', 'D', 2)
graph.add_edge('SLC', 'C', 2)
graph.add_edge('SLC', 'D', 3)
graph.add_edge('SF', 'SLC', 2)
graph.add_edge('SF', 'S', 2)
graph.add_edge('D', 'SF', 3)
dist, pred = shortest_paths(graph, algorithm, 'SLC')
assert dist == {'S': 6, 'C': 2, 'SLC': 0, 'SF': 6, 'D': 3}
assert pred == {'S': 'C', 'C': 'SLC', 'SLC': None, 'SF': 'D', 'D': 'SLC'}
dist, pred = shortest_paths(graph, algorithm, 'SLC', 'SF')
assert dist == 6
assert pred == {'S': 'C', 'C': 'SLC', 'SLC': None, 'SF': 'D', 'D': 'SLC'}
graph.remove_edge('SLC', 'D')
graph.add_edge('D', 'SLC', -10)
assert raises(ValueError, lambda: shortest_paths(graph, 'bellman_ford', 'SLC'))

_test_shortest_paths("List", 'bellman_ford')
_test_shortest_paths("Matrix", 'bellman_ford')