From 2a83fec4146edfb46e4315a566c154e6556094ff Mon Sep 17 00:00:00 2001 From: TripleCamellya Date: Tue, 11 Mar 2025 11:16:48 +0800 Subject: [PATCH 1/3] feat: add queue improved bellman ford algorithm --- pydatastructs/graphs/algorithms.py | 34 +++++++++++++++++++ pydatastructs/graphs/tests/test_algorithms.py | 4 +++ 2 files changed, 38 insertions(+) diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index 204c2fbd..4b071ca9 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -700,6 +700,8 @@ def shortest_paths(graph: Graph, algorithm: str, 'bellman_ford' -> Bellman-Ford algorithm as given in [1]. 'dijkstra' -> Dijkstra algorithm as given in [2]. + + 'queue_improved_bellman_ford' -> Queue Improved Bellman-Ford algorithm as given in [3]. source: str The name of the source the node. target: str @@ -742,6 +744,7 @@ def shortest_paths(graph: Graph, algorithm: str, .. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm .. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + .. [3] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm#Improvements """ raise_if_backend_is_not_python( shortest_paths, kwargs.get('backend', Backend.PYTHON)) @@ -811,6 +814,37 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str): _dijkstra_adjacency_matrix = _dijkstra_adjacency_list +def _queue_improved_bellman_ford_adjacency_list(graph: Graph, source: str, target: str) -> tuple: + distances, predecessor, visited = {}, {}, {} + + for v in graph.vertices: + distances[v] = float('inf') + predecessor[v] = None + visited[v] = False + distances[source] = 0 + + que = Queue([source]) + + while que: + u = que.popleft() + visited[u] = False + neighbors = graph.neighbors(u) + for neighbor in neighbors: + v = neighbor.name + edge_str = u + '_' + v + if distances[u] != float('inf') and distances[u] + graph.edge_weights[edge_str].value < distances[v]: + distances[v] = distances[u] + graph.edge_weights[edge_str].value + predecessor[v] = u + if not visited[v]: + que.append(v) + visited[v] = True + + if target != "": + return (distances[target], predecessor) + return (distances, predecessor) + +_queue_improved_bellman_ford_adjacency_matrix = _queue_improved_bellman_ford_adjacency_list + def all_pair_shortest_paths(graph: Graph, algorithm: str, **kwargs) -> tuple: """ diff --git a/pydatastructs/graphs/tests/test_algorithms.py b/pydatastructs/graphs/tests/test_algorithms.py index f1586f51..e1681fc3 100644 --- a/pydatastructs/graphs/tests/test_algorithms.py +++ b/pydatastructs/graphs/tests/test_algorithms.py @@ -321,6 +321,10 @@ def _test_shortest_paths_negative_edges(ds, algorithm): _test_shortest_paths_negative_edges("Matrix", 'bellman_ford') _test_shortest_paths_positive_edges("List", 'dijkstra') _test_shortest_paths_positive_edges("Matrix", 'dijkstra') + _test_shortest_paths_positive_edges("List", 'queue_improved_bellman_ford') + _test_shortest_paths_positive_edges("Matrix", 'queue_improved_bellman_ford') + _test_shortest_paths_negative_edges("List", 'queue_improved_bellman_ford') + _test_shortest_paths_negative_edges("Matrix", 'queue_improved_bellman_ford') def test_all_pair_shortest_paths(): From b2937eedaec8eaf40164184d4a95e893f2127e9e Mon Sep 17 00:00:00 2001 From: TripleCamellya Date: Tue, 11 Mar 2025 14:38:28 +0800 Subject: [PATCH 2/3] feat: replace Bellman Ford Algorithm with queue version --- pydatastructs/graphs/algorithms.py | 71 ++++++------------- pydatastructs/graphs/tests/test_algorithms.py | 4 -- 2 files changed, 22 insertions(+), 53 deletions(-) diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index 4b071ca9..3dfaf68c 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -697,11 +697,9 @@ def shortest_paths(graph: Graph, algorithm: str, The algorithm to be used. Currently, the following algorithms are implemented, - 'bellman_ford' -> Bellman-Ford algorithm as given in [1]. + 'bellman_ford' -> Bellman-Ford algorithm as given in [1], with a queue to improve performance on sparse graphs. 'dijkstra' -> Dijkstra algorithm as given in [2]. - - 'queue_improved_bellman_ford' -> Queue Improved Bellman-Ford algorithm as given in [3]. source: str The name of the source the node. target: str @@ -744,7 +742,6 @@ def shortest_paths(graph: Graph, algorithm: str, .. [1] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm .. [2] https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm - .. [3] https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm#Improvements """ raise_if_backend_is_not_python( shortest_paths, kwargs.get('backend', Backend.PYTHON)) @@ -757,27 +754,34 @@ def shortest_paths(graph: Graph, algorithm: str, return getattr(algorithms, func)(graph, source, target) def _bellman_ford_adjacency_list(graph: Graph, source: str, target: str) -> tuple: - distances, predecessor = {}, {} + distances, predecessor, visited, cnts = {}, {}, {}, {} for v in graph.vertices: distances[v] = float('inf') predecessor[v] = None + visited[v] = False + cnts[v] = 0 distances[source] = 0 + verticy_num = len(graph.vertices) - 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 + que = Queue([source]) - 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.") + while que: + u = que.popleft() + visited[u] = False + neighbors = graph.neighbors(u) + for neighbor in neighbors: + v = neighbor.name + edge_str = u + '_' + v + if distances[u] != float('inf') and distances[u] + graph.edge_weights[edge_str].value < distances[v]: + distances[v] = distances[u] + graph.edge_weights[edge_str].value + predecessor[v] = u + cnts[v] = cnts[u] + 1 + if cnts[v] >= verticy_num: + raise ValueError("Graph contains a negative weight cycle.") + if not visited[v]: + que.append(v) + visited[v] = True if target != "": return (distances[target], predecessor) @@ -814,37 +818,6 @@ def _dijkstra_adjacency_list(graph: Graph, start: str, target: str): _dijkstra_adjacency_matrix = _dijkstra_adjacency_list -def _queue_improved_bellman_ford_adjacency_list(graph: Graph, source: str, target: str) -> tuple: - distances, predecessor, visited = {}, {}, {} - - for v in graph.vertices: - distances[v] = float('inf') - predecessor[v] = None - visited[v] = False - distances[source] = 0 - - que = Queue([source]) - - while que: - u = que.popleft() - visited[u] = False - neighbors = graph.neighbors(u) - for neighbor in neighbors: - v = neighbor.name - edge_str = u + '_' + v - if distances[u] != float('inf') and distances[u] + graph.edge_weights[edge_str].value < distances[v]: - distances[v] = distances[u] + graph.edge_weights[edge_str].value - predecessor[v] = u - if not visited[v]: - que.append(v) - visited[v] = True - - if target != "": - return (distances[target], predecessor) - return (distances, predecessor) - -_queue_improved_bellman_ford_adjacency_matrix = _queue_improved_bellman_ford_adjacency_list - def all_pair_shortest_paths(graph: Graph, algorithm: str, **kwargs) -> tuple: """ diff --git a/pydatastructs/graphs/tests/test_algorithms.py b/pydatastructs/graphs/tests/test_algorithms.py index e1681fc3..f1586f51 100644 --- a/pydatastructs/graphs/tests/test_algorithms.py +++ b/pydatastructs/graphs/tests/test_algorithms.py @@ -321,10 +321,6 @@ def _test_shortest_paths_negative_edges(ds, algorithm): _test_shortest_paths_negative_edges("Matrix", 'bellman_ford') _test_shortest_paths_positive_edges("List", 'dijkstra') _test_shortest_paths_positive_edges("Matrix", 'dijkstra') - _test_shortest_paths_positive_edges("List", 'queue_improved_bellman_ford') - _test_shortest_paths_positive_edges("Matrix", 'queue_improved_bellman_ford') - _test_shortest_paths_negative_edges("List", 'queue_improved_bellman_ford') - _test_shortest_paths_negative_edges("Matrix", 'queue_improved_bellman_ford') def test_all_pair_shortest_paths(): From ba3e731d60c201f2a7f0b98db8ecc2ae82806fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A8=97=E0=A8=97=E0=A8=A8=E0=A8=A6=E0=A9=80=E0=A8=AA=20?= =?UTF-8?q?=E0=A8=B8=E0=A8=BF=E0=A9=B0=E0=A8=98=20=28Gagandeep=20Singh=29?= Date: Tue, 11 Mar 2025 14:14:04 +0530 Subject: [PATCH 3/3] Apply suggestions from code review --- pydatastructs/graphs/algorithms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/graphs/algorithms.py b/pydatastructs/graphs/algorithms.py index 3dfaf68c..9de50e7c 100644 --- a/pydatastructs/graphs/algorithms.py +++ b/pydatastructs/graphs/algorithms.py @@ -697,7 +697,7 @@ def shortest_paths(graph: Graph, algorithm: str, The algorithm to be used. Currently, the following algorithms are implemented, - 'bellman_ford' -> Bellman-Ford algorithm as given in [1], with a queue to improve performance on sparse graphs. + 'bellman_ford' -> Bellman-Ford algorithm as given in [1] 'dijkstra' -> Dijkstra algorithm as given in [2]. source: str