diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index ea3322c02..204c2fbd8 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -5,7 +5,7 @@ from collections import deque from concurrent.futures import ThreadPoolExecutor from pydatastructs.utils.misc_util import ( - _comp, raise_if_backend_is_not_python, Backend) + _comp, raise_if_backend_is_not_python, Backend, AdjacencyListGraphNode) from pydatastructs.miscellaneous_data_structures import ( DisjointSetForest, PriorityQueue) from pydatastructs.graphs.graph import Graph @@ -799,7 +799,7 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str): visited[u] = True for v in graph.vertices: edge_str = u + '_' + v - if (edge_str in graph.edge_weights and graph.edge_weights[edge_str].value > 0 and + if (edge_str in graph.edge_weights and graph.edge_weights[edge_str].value >= 0 and visited[v] is False and dist[v] > dist[u] + graph.edge_weights[edge_str].value): dist[v] = dist[u] + graph.edge_weights[edge_str].value pred[v] = u @@ -826,6 +826,7 @@ def all_pair_shortest_paths(graph: Graph, algorithm: str, are implemented, 'floyd_warshall' -> Floyd Warshall algorithm as given in [1]. + 'johnson' -> Johnson's Algorithm as given in [2] backend: pydatastructs.Backend The backend to be used. Optional, by default, the best available @@ -858,6 +859,7 @@ def all_pair_shortest_paths(graph: Graph, algorithm: str, ========== .. [1] https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm + .. [2] https://en.wikipedia.org/wiki/Johnson's_algorithm """ raise_if_backend_is_not_python( all_pair_shortest_paths, kwargs.get('backend', Backend.PYTHON)) @@ -900,6 +902,51 @@ def _floyd_warshall_adjacency_list(graph: Graph): _floyd_warshall_adjacency_matrix = _floyd_warshall_adjacency_list +def _johnson_adjacency_list(graph: Graph): + new_vertex = AdjacencyListGraphNode('__q__') + graph.add_vertex(new_vertex) + + for vertex in graph.vertices: + if vertex != '__q__': + graph.add_edge('__q__', vertex, 0) + + distances, predecessors = shortest_paths(graph, 'bellman_ford', '__q__') + + edges_to_remove = [] + for edge in graph.edge_weights: + edge_node = graph.edge_weights[edge] + if edge_node.source.name == '__q__': + edges_to_remove.append((edge_node.source.name, edge_node.target.name)) + + for u, v in edges_to_remove: + graph.remove_edge(u, v) + graph.remove_vertex('__q__') + + for edge in graph.edge_weights: + edge_node = graph.edge_weights[edge] + u, v = edge_node.source.name, edge_node.target.name + graph.edge_weights[edge].value += (distances[u] - distances[v]) + + all_distances = {} + all_next_vertex = {} + + for vertex in graph.vertices: + u = vertex + dijkstra_dist, dijkstra_pred = shortest_paths(graph, 'dijkstra', u) + all_distances[u] = {} + all_next_vertex[u] = {} + for v in graph.vertices: + if dijkstra_pred[v] is None or dijkstra_pred[v] == u : + all_next_vertex[u][v] = u + else: + all_next_vertex[u][v] = None + if v in dijkstra_dist: + all_distances[u][v] = dijkstra_dist[v] - distances[u] + distances[v] + else: + all_distances[u][v] = float('inf') + + return (all_distances, all_next_vertex) + def topological_sort(graph: Graph, algorithm: str, **kwargs) -> list: """ diff --git a/pydatastructs/graphs/tests/test_algorithms.py b/pydatastructs/graphs/tests/test_algorithms.py index fde3571da..f1586f512 100644 --- a/pydatastructs/graphs/tests/test_algorithms.py +++ b/pydatastructs/graphs/tests/test_algorithms.py @@ -1,7 +1,7 @@ from pydatastructs import (breadth_first_search, Graph, breadth_first_search_parallel, minimum_spanning_tree, minimum_spanning_tree_parallel, strongly_connected_components, -depth_first_search, shortest_paths, topological_sort, +depth_first_search, shortest_paths,all_pair_shortest_paths, topological_sort, topological_sort_parallel, max_flow) from pydatastructs.utils.raises_util import raises @@ -336,7 +336,7 @@ def _test_shortest_paths_negative_edges(ds, algorithm): 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') + dist, next_v = all_pair_shortest_paths(graph, algorithm) 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}, @@ -346,6 +346,10 @@ def _test_shortest_paths_negative_edges(ds, algorithm): '3': {'4': '3', '3': '3', '1': None, '2': None}, '4': {'2': '4', '4': '4', '1': None, '3': None}} + _test_shortest_paths_negative_edges("List", 'floyd_warshall') + _test_shortest_paths_negative_edges("Matrix", 'floyd_warshall') + _test_shortest_paths_negative_edges("List", 'johnson') + def test_topological_sort(): def _test_topological_sort(func, ds, algorithm, threads=None):