Skip to content

Commit 3f3faad

Browse files
Added Floyd Warshall algorithm (#302)
1 parent 8877bf7 commit 3f3faad

File tree

3 files changed

+135
-6
lines changed

3 files changed

+135
-6
lines changed

pydatastructs/graphs/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
strongly_connected_components,
1616
depth_first_search,
1717
shortest_paths,
18+
all_pair_shortest_paths,
1819
topological_sort,
1920
topological_sort_parallel
2021
)

pydatastructs/graphs/algorithms.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'strongly_connected_components',
2121
'depth_first_search',
2222
'shortest_paths',
23+
'all_pair_shortest_paths',
2324
'topological_sort',
2425
'topological_sort_parallel'
2526
]
@@ -672,7 +673,7 @@ def shortest_paths(graph: Graph, algorithm: str,
672673
is 'bellman_ford'/'dijkstra'.
673674
(distances[target], predecessors): (float, dict)
674675
If target is provided and algorithm used is
675-
'bellman_ford'.
676+
'bellman_ford'/'dijkstra'.
676677
677678
Examples
678679
========
@@ -762,6 +763,86 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str):
762763

763764
_dijkstra_adjacency_matrix = _dijkstra_adjacency_list
764765

766+
def all_pair_shortest_paths(graph: Graph, algorithm: str) -> tuple:
767+
"""
768+
Finds shortest paths between all pairs of vertices in the given graph.
769+
770+
Parameters
771+
==========
772+
773+
graph: Graph
774+
The graph under consideration.
775+
algorithm: str
776+
The algorithm to be used. Currently, the following algorithms
777+
are implemented,
778+
'floyd_warshall' -> Floyd Warshall algorithm as given in [1].
779+
780+
Returns
781+
=======
782+
783+
(distances, predecessors): (dict, dict)
784+
785+
Examples
786+
========
787+
788+
>>> from pydatastructs import Graph, AdjacencyListGraphNode
789+
>>> from pydatastructs import all_pair_shortest_paths
790+
>>> V1 = AdjacencyListGraphNode("V1")
791+
>>> V2 = AdjacencyListGraphNode("V2")
792+
>>> V3 = AdjacencyListGraphNode("V3")
793+
>>> G = Graph(V1, V2, V3)
794+
>>> G.add_edge('V2', 'V3', 10)
795+
>>> G.add_edge('V1', 'V2', 11)
796+
>>> G.add_edge('V3', 'V1', 5)
797+
>>> dist, _ = all_pair_shortest_paths(G, 'floyd_warshall')
798+
>>> dist['V1']['V3']
799+
21
800+
>>> dist['V3']['V1']
801+
5
802+
803+
References
804+
==========
805+
806+
.. [1] https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
807+
"""
808+
import pydatastructs.graphs.algorithms as algorithms
809+
func = "_" + algorithm + "_" + graph._impl
810+
if not hasattr(algorithms, func):
811+
raise NotImplementedError(
812+
"Currently %s algorithm isn't implemented for "
813+
"finding shortest paths in graphs."%(algorithm))
814+
return getattr(algorithms, func)(graph)
815+
816+
def _floyd_warshall_adjacency_list(graph: Graph):
817+
dist, next_vertex = dict(), dict()
818+
V, E = graph.vertices, graph.edge_weights
819+
820+
for v in V:
821+
dist[v] = dict()
822+
next_vertex[v] = dict()
823+
824+
for name, edge in E.items():
825+
dist[edge.source.name][edge.target.name] = edge.value
826+
next_vertex[edge.source.name][edge.target.name] = edge.source.name
827+
828+
for v in V:
829+
dist[v][v] = 0
830+
next_vertex[v][v] = v
831+
832+
for k in V:
833+
for i in V:
834+
for j in V:
835+
dist_i_j = dist.get(i, dict()).get(j, float('inf'))
836+
dist_i_k = dist.get(i, dict()).get(k, float('inf'))
837+
dist_k_j = dist.get(k, dict()).get(j, float('inf'))
838+
next_i_k = next_vertex.get(i + '_' + k, None)
839+
if dist_i_j > dist_i_k + dist_k_j:
840+
dist[i][j] = dist_i_k + dist_k_j
841+
next_vertex[i][j] = next_i_k
842+
843+
return (dist, next_vertex)
844+
845+
_floyd_warshall_adjacency_matrix = _floyd_warshall_adjacency_list
765846

766847
def topological_sort(graph: Graph, algorithm: str) -> list:
767848
"""

pydatastructs/graphs/tests/test_algorithms.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def path_finder(curr_node, next_node, dest_node, parent, path):
262262

263263
def test_shortest_paths():
264264

265-
def _test_shortest_paths(ds, algorithm):
265+
def _test_shortest_paths_positive_edges(ds, algorithm):
266266
import pydatastructs.utils.misc_util as utils
267267
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
268268
vertices = [GraphNode('S'), GraphNode('C'),
@@ -288,10 +288,57 @@ def _test_shortest_paths(ds, algorithm):
288288
graph.add_edge('D', 'SLC', -10)
289289
assert raises(ValueError, lambda: shortest_paths(graph, 'bellman_ford', 'SLC'))
290290

291-
_test_shortest_paths("List", 'bellman_ford')
292-
_test_shortest_paths("Matrix", 'bellman_ford')
293-
_test_shortest_paths("List", 'dijkstra')
294-
_test_shortest_paths("Matrix", 'dijkstra')
291+
def _test_shortest_paths_negative_edges(ds, algorithm):
292+
import pydatastructs.utils.misc_util as utils
293+
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
294+
vertices = [GraphNode('s'), GraphNode('a'),
295+
GraphNode('b'), GraphNode('c'),
296+
GraphNode('d')]
297+
298+
graph = Graph(*vertices)
299+
graph.add_edge('s', 'a', 3)
300+
graph.add_edge('s', 'b', 2)
301+
graph.add_edge('a', 'c', 1)
302+
graph.add_edge('b', 'd', 1)
303+
graph.add_edge('b', 'a', -2)
304+
graph.add_edge('c', 'd', 1)
305+
dist, pred = shortest_paths(graph, algorithm, 's')
306+
assert dist == {'s': 0, 'a': 0, 'b': 2, 'c': 1, 'd': 2}
307+
assert pred == {'s': None, 'a': 'b', 'b': 's', 'c': 'a', 'd': 'c'}
308+
dist, pred = shortest_paths(graph, algorithm, 's', 'd')
309+
assert dist == 2
310+
assert pred == {'s': None, 'a': 'b', 'b': 's', 'c': 'a', 'd': 'c'}
311+
312+
_test_shortest_paths_positive_edges("List", 'bellman_ford')
313+
_test_shortest_paths_positive_edges("Matrix", 'bellman_ford')
314+
_test_shortest_paths_negative_edges("List", 'bellman_ford')
315+
_test_shortest_paths_negative_edges("Matrix", 'bellman_ford')
316+
_test_shortest_paths_positive_edges("List", 'dijkstra')
317+
_test_shortest_paths_positive_edges("Matrix", 'dijkstra')
318+
319+
def test_all_pair_shortest_paths():
320+
321+
def _test_shortest_paths_negative_edges(ds, algorithm):
322+
import pydatastructs.utils.misc_util as utils
323+
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
324+
vertices = [GraphNode('1'), GraphNode('2'),
325+
GraphNode('3'), GraphNode('4')]
326+
327+
graph = Graph(*vertices)
328+
graph.add_edge('1', '3', -2)
329+
graph.add_edge('2', '1', 4)
330+
graph.add_edge('2', '3', 3)
331+
graph.add_edge('3', '4', 2)
332+
graph.add_edge('4', '2', -1)
333+
dist, next_v = shortest_paths(graph, algorithm, 's')
334+
assert dist == {'1': {'3': -2, '1': 0, '4': 0, '2': -1},
335+
'2': {'1': 4, '3': 2, '2': 0, '4': 4},
336+
'3': {'4': 2, '3': 0, '1': 5, '2': 1},
337+
'4': {'2': -1, '4': 0, '1': 3, '3': 1}}
338+
assert next_v == {'1': {'3': '1', '1': '1', '4': None, '2': None},
339+
'2': {'1': '2', '3': None, '2': '2', '4': None},
340+
'3': {'4': '3', '3': '3', '1': None, '2': None},
341+
'4': {'2': '4', '4': '4', '1': None, '3': None}}
295342

296343
def test_topological_sort():
297344

0 commit comments

Comments
 (0)