Skip to content

First Implementation of Johnson Graph Algorithm #12281

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

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
11bfe18
Implementation of Johnson Graph Algorithm
joelkurien Oct 25, 2024
da0717b
updating DIRECTORY.md
joelkurien Oct 25, 2024
18319a8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 25, 2024
8286b58
Resolving ruff issues
joelkurien Oct 25, 2024
346a955
Resolved issues regarding ruff testing and merge conflicts
joelkurien Oct 25, 2024
e3db05b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 25, 2024
a1ff5c9
Added return types to the functions
joelkurien Oct 25, 2024
5a87d4f
Added method return type and resolved merge conflics
joelkurien Oct 25, 2024
4982ff0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 25, 2024
f80afa3
Added type annotation for variables
joelkurien Oct 25, 2024
b8c6bea
Merge branch 'joel_graph' of https://github.com/joelkurien/Python int…
joelkurien Oct 25, 2024
f62921c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 25, 2024
8006d48
Added type annotation changes
joelkurien Oct 25, 2024
1fca8c6
Merge branch 'joel_graph' of https://github.com/joelkurien/Python int…
joelkurien Oct 25, 2024
9a4e1d3
Handled type annotation
joelkurien Oct 28, 2024
a00d41c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 28, 2024
af534ad
Handled type annotations and added doctests
joelkurien Oct 28, 2024
14ddb93
Handled type annotation and doctests
joelkurien Oct 28, 2024
32b9b8f
Handled doctests
joelkurien Oct 28, 2024
b911a55
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 28, 2024
6269fd6
Handled type annotation
joelkurien Oct 28, 2024
8fc6818
Merge branch 'joel_graph' of https://github.com/joelkurien/Python int…
joelkurien Oct 28, 2024
debba8c
Handled names
joelkurien Oct 28, 2024
65f0d0c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 28, 2024
bcf5bed
handled name
joelkurien Oct 28, 2024
f9bf655
Handled names
joelkurien Oct 28, 2024
39fdc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 28, 2024
5fe9e9b
Added tests
joelkurien Oct 28, 2024
3d4acaa
Added doctests
joelkurien Oct 28, 2024
2fb5bec
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 28, 2024
2584473
Handled word length
joelkurien Oct 28, 2024
81fb07b
Merge branch 'joel_graph' of https://github.com/joelkurien/Python int…
joelkurien Oct 28, 2024
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
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@
* [Graphs Floyd Warshall](graphs/graphs_floyd_warshall.py)
* [Greedy Best First](graphs/greedy_best_first.py)
* [Greedy Min Vertex Cover](graphs/greedy_min_vertex_cover.py)
* [Johnson Graph](graphs/johnson_graph.py)
* [Kahns Algorithm Long](graphs/kahns_algorithm_long.py)
* [Kahns Algorithm Topo](graphs/kahns_algorithm_topo.py)
* [Karger](graphs/karger.py)
Expand Down
189 changes: 189 additions & 0 deletions graphs/johnson_graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import heapq
import sys


# First implementation of johnson algorithm
# Steps followed to implement this algorithm is given in the below link:
# https://brilliant.org/wiki/johnsons-algorithm/
class JohnsonGraph:
def __init__(self) -> None:
"""
Initializes an empty graph with no edges.
>>> g = JohnsonGraph()
>>> g.edges
[]
>>> g.graph
{}
"""
self.edges: list[tuple[str, str, int]] = []
self.graph: dict[str, list[tuple[str, int]]] = {}

# add vertices for a graph
def add_vertices(self, vertex: str) -> None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file graphs/johnson_graph.py, please provide doctest for the function add_vertices

"""
Adds a vertex `vertex` to the graph with an empty adjacency list.
>>> g = JohnsonGraph()
>>> g.add_vertices("A")
>>> g.graph
{'A': []}
"""
self.graph[vertex] = []

# assign weights for each edges formed of the directed graph
def add_edge(self, vertex_a: str, vertex_b: str, weight: int) -> None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file graphs/johnson_graph.py, please provide doctest for the function add_edge

"""
Adds a directed edge from vertex `vertex_a`
to vertex `vertex_b` with weight `weight`.
>>> g = JohnsonGraph()
>>> g.add_vertices("A")
>>> g.add_vertices("B")
>>> g.add_edge("A", "B", 5)
>>> g.edges
[('A', 'B', 5)]
>>> g.graph
{'A': [('B', 5)], 'B': []}
"""
self.edges.append((vertex_a, vertex_b, weight))
self.graph[vertex_a].append((vertex_b, weight))

# perform a dijkstra algorithm on a directed graph
def dijkstra(self, start: str) -> dict:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file graphs/johnson_graph.py, please provide doctest for the function dijkstra

"""
Computes the shortest path from vertex `start`
to all other vertices using Dijkstra's algorithm.
>>> g = JohnsonGraph()
>>> g.add_vertices("A")
>>> g.add_vertices("B")
>>> g.add_edge("A", "B", 1)
>>> g.dijkstra("A")
{'A': 0, 'B': 1}
>>> g.add_vertices("C")
>>> g.add_edge("B", "C", 2)
>>> g.dijkstra("A")
{'A': 0, 'B': 1, 'C': 3}
"""
distances = {vertex: sys.maxsize - 1 for vertex in self.graph}
pq = [(0, start)]
distances[start] = 0
while pq:
weight, vertex = heapq.heappop(pq)

if weight > distances[vertex]:
continue

for node, node_weight in self.graph[vertex]:
if distances[vertex] + node_weight < distances[node]:
distances[node] = distances[vertex] + node_weight
heapq.heappush(pq, (distances[node], node))
return distances

# carry out the bellman ford algorithm for a node and estimate its distance vector
def bellman_ford(self, start: str) -> dict:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file graphs/johnson_graph.py, please provide doctest for the function bellman_ford

"""
Computes the shortest path from vertex `start` to
all other vertices using the Bellman-Ford algorithm.
>>> g = JohnsonGraph()
>>> g.add_vertices("A")
>>> g.add_vertices("B")
>>> g.add_edge("A", "B", 1)
>>> g.bellman_ford("A")
{'A': 0, 'B': 1}
>>> g.add_vertices("C")
>>> g.add_edge("B", "C", 2)
>>> g.bellman_ford("A")
{'A': 0, 'B': 1, 'C': 3}
"""
distances = {vertex: sys.maxsize - 1 for vertex in self.graph}
distances[start] = 0

for vertex_a in self.graph:
for vertex_a, vertex_b, weight in self.edges:
if (
distances[vertex_a] != sys.maxsize - 1
and distances[vertex_a] + weight < distances[vertex_b]
):
distances[vertex_b] = distances[vertex_a] + weight

return distances

# perform the johnson algorithm to handle the negative weights that
# could not be handled by either the dijkstra
# or the bellman ford algorithm efficiently
def johnson_algo(self) -> list[dict]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file graphs/johnson_graph.py, please provide doctest for the function johnson_algo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file graphs/johnson_graph.py, please provide doctest for the function johnson_algo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file graphs/johnson_graph.py, please provide doctest for the function johnson_algo

"""
Computes the shortest paths between
all pairs of vertices using Johnson's algorithm
for a directed graph.
>>> g = JohnsonGraph()
>>> g.add_vertices("A")
>>> g.add_vertices("B")
>>> g.add_edge("A", "B", 1)
>>> g.add_edge("B", "A", 2)
>>> optimal_paths = g.johnson_algo()
>>> optimal_paths
[{'A': 0, 'B': 1}, {'A': 2, 'B': 0}]
"""
self.add_vertices("#")
for vertex in self.graph:
if vertex != "#":
self.add_edge("#", vertex, 0)

hash_path = self.bellman_ford("#")

for i in range(len(self.edges)):
vertex_a, vertex_b, weight = self.edges[i]
self.edges[i] = (
vertex_a,
vertex_b,
weight + hash_path[vertex_a] - hash_path[vertex_b],
)
self.edges[i] = (
vertex_a,
vertex_b,
weight + hash_path[vertex_a] - hash_path[vertex_b],
)

self.graph.pop("#")
filtered_edges = []
for vertex1, vertex2, node_weight in self.edges:
filtered_edges.append((vertex1, vertex2, node_weight))
self.edges = filtered_edges

for vertex in self.graph:
self.graph[vertex] = []
for vertex1, vertex2, node_weight in self.edges:
if vertex1 == vertex:
self.graph[vertex].append((vertex2, node_weight))

distances = []
for vertex1 in self.graph:
new_dist = self.dijkstra(vertex1)
for vertex2 in self.graph:
if new_dist[vertex2] < sys.maxsize - 1:
new_dist[vertex2] += hash_path[vertex2] - hash_path[vertex1]
for key in new_dist:
if new_dist[key] == sys.maxsize - 1:
new_dist[key] = None
distances.append(new_dist)
return distances


g = JohnsonGraph()
# this a complete connected graph
g.add_vertices("A")
g.add_vertices("B")
g.add_vertices("C")
g.add_vertices("D")
g.add_vertices("E")

g.add_edge("A", "B", 1)
g.add_edge("A", "C", 3)
g.add_edge("B", "D", 4)
g.add_edge("D", "E", 2)
g.add_edge("E", "C", -2)


optimal_paths = g.johnson_algo()
print("Print all optimal paths of a graph using Johnson Algorithm")
for i, row in enumerate(optimal_paths):
print(f"{i}: {row}")