Skip to content

Commit

Permalink
Added Floyd Warshall algorithm (#302)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kiran-Venkatesh authored Nov 20, 2020
1 parent 8877bf7 commit 3f3faad
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 6 deletions.
1 change: 1 addition & 0 deletions pydatastructs/graphs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
strongly_connected_components,
depth_first_search,
shortest_paths,
all_pair_shortest_paths,
topological_sort,
topological_sort_parallel
)
Expand Down
83 changes: 82 additions & 1 deletion pydatastructs/graphs/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
'strongly_connected_components',
'depth_first_search',
'shortest_paths',
'all_pair_shortest_paths',
'topological_sort',
'topological_sort_parallel'
]
Expand Down Expand Up @@ -672,7 +673,7 @@ def shortest_paths(graph: Graph, algorithm: str,
is 'bellman_ford'/'dijkstra'.
(distances[target], predecessors): (float, dict)
If target is provided and algorithm used is
'bellman_ford'.
'bellman_ford'/'dijkstra'.
Examples
========
Expand Down Expand Up @@ -762,6 +763,86 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str):

_dijkstra_adjacency_matrix = _dijkstra_adjacency_list

def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple:
"""
Finds shortest paths between all pairs of vertices in the given graph.
Parameters
==========
graph: Graph
The graph under consideration.
algorithm: str
The algorithm to be used. Currently, the following algorithms
are implemented,
'floyd_warshall' -> Floyd Warshall algorithm as given in [1].
Returns
=======
(distances, predecessors): (dict, dict)
Examples
========
>>> from pydatastructs import Graph, AdjacencyListGraphNode
>>> from pydatastructs import all_pair_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)
>>> G.add_edge('V3', 'V1', 5)
>>> dist, _ = all_pair_shortest_paths(G, 'floyd_warshall')
>>> dist['V1']['V3']
21
>>> dist['V3']['V1']
5
References
==========
.. [1] https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_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)

def _floyd_warshall_adjacency_list(graph: Graph):
dist, next_vertex = dict(), dict()
V, E = graph.vertices, graph.edge_weights

for v in V:
dist[v] = dict()
next_vertex[v] = dict()

for name, edge in E.items():
dist[edge.source.name][edge.target.name] = edge.value
next_vertex[edge.source.name][edge.target.name] = edge.source.name

for v in V:
dist[v][v] = 0
next_vertex[v][v] = v

for k in V:
for i in V:
for j in V:
dist_i_j = dist.get(i, dict()).get(j, float('inf'))
dist_i_k = dist.get(i, dict()).get(k, float('inf'))
dist_k_j = dist.get(k, dict()).get(j, float('inf'))
next_i_k = next_vertex.get(i + '_' + k, None)
if dist_i_j > dist_i_k + dist_k_j:
dist[i][j] = dist_i_k + dist_k_j
next_vertex[i][j] = next_i_k

return (dist, next_vertex)

_floyd_warshall_adjacency_matrix = _floyd_warshall_adjacency_list

def topological_sort(graph: Graph, algorithm: str) -> list:
"""
Expand Down
57 changes: 52 additions & 5 deletions pydatastructs/graphs/tests/test_algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def path_finder(curr_node, next_node, dest_node, parent, path):

def test_shortest_paths():

def _test_shortest_paths(ds, algorithm):
def _test_shortest_paths_positive_edges(ds, algorithm):
import pydatastructs.utils.misc_util as utils
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
vertices = [GraphNode('S'), GraphNode('C'),
Expand All @@ -288,10 +288,57 @@ def _test_shortest_paths(ds, algorithm):
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')
_test_shortest_paths("List", 'dijkstra')
_test_shortest_paths("Matrix", 'dijkstra')
def _test_shortest_paths_negative_edges(ds, algorithm):
import pydatastructs.utils.misc_util as utils
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
vertices = [GraphNode('s'), GraphNode('a'),
GraphNode('b'), GraphNode('c'),
GraphNode('d')]

graph = Graph(*vertices)
graph.add_edge('s', 'a', 3)
graph.add_edge('s', 'b', 2)
graph.add_edge('a', 'c', 1)
graph.add_edge('b', 'd', 1)
graph.add_edge('b', 'a', -2)
graph.add_edge('c', 'd', 1)
dist, pred = shortest_paths(graph, algorithm, 's')
assert dist == {'s': 0, 'a': 0, 'b': 2, 'c': 1, 'd': 2}
assert pred == {'s': None, 'a': 'b', 'b': 's', 'c': 'a', 'd': 'c'}
dist, pred = shortest_paths(graph, algorithm, 's', 'd')
assert dist == 2
assert pred == {'s': None, 'a': 'b', 'b': 's', 'c': 'a', 'd': 'c'}

_test_shortest_paths_positive_edges("List", 'bellman_ford')
_test_shortest_paths_positive_edges("Matrix", 'bellman_ford')
_test_shortest_paths_negative_edges("List", 'bellman_ford')
_test_shortest_paths_negative_edges("Matrix", 'bellman_ford')
_test_shortest_paths_positive_edges("List", 'dijkstra')
_test_shortest_paths_positive_edges("Matrix", 'dijkstra')

def test_all_pair_shortest_paths():

def _test_shortest_paths_negative_edges(ds, algorithm):
import pydatastructs.utils.misc_util as utils
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
vertices = [GraphNode('1'), GraphNode('2'),
GraphNode('3'), GraphNode('4')]

graph = Graph(*vertices)
graph.add_edge('1', '3', -2)
graph.add_edge('2', '1', 4)
graph.add_edge('2', '3', 3)
graph.add_edge('3', '4', 2)
graph.add_edge('4', '2', -1)
dist, next_v = shortest_paths(graph, algorithm, 's')
assert dist == {'1': {'3': -2, '1': 0, '4': 0, '2': -1},
'2': {'1': 4, '3': 2, '2': 0, '4': 4},
'3': {'4': 2, '3': 0, '1': 5, '2': 1},
'4': {'2': -1, '4': 0, '1': 3, '3': 1}}
assert next_v == {'1': {'3': '1', '1': '1', '4': None, '2': None},
'2': {'1': '2', '3': None, '2': '2', '4': None},
'3': {'4': '3', '3': '3', '1': None, '2': None},
'4': {'2': '4', '4': '4', '1': None, '3': None}}

def test_topological_sort():

Expand Down

0 comments on commit 3f3faad

Please sign in to comment.