Skip to content

Commit

Permalink
Merge branch 'feature/graph-algorithms' into develop
Browse files Browse the repository at this point in the history
resolve conflicts
  • Loading branch information
codejsha committed Feb 11, 2024
2 parents 49496e8 + 5c000e7 commit 4768b66
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 51 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ algorithm DFS-VISIT(G, u):
u.finished = time
```
- Dijkstra's algorithm: [c++](cpp-algorithm/src/graph), [java](java-algorithm/src/main/java/com/example/algorithm/graph) | A single source the shortest path algorithm that handle non-negative edge weights. It find the shortest path between two vertices in a graph.
- Dijkstra's algorithm, CCSP#4.5.1: [c++](cpp-algorithm/src/graph),[python](python-algorithm/algorithm/graph/test)(test), [java](java-algorithm/src/main/java/com/example/algorithm/graph) | A single source the shortest path algorithm that handle non-negative edge weights. It find the shortest path between two vertices in a graph.

```txt
algorithm Dijkstra(G, source):
Expand Down Expand Up @@ -347,6 +347,7 @@ algorithm Prim(G, root):
**Examples**

- Maze problem: [java](java-algorithm/src/main/java/com/example/algorithm/graph) | A maze problem is that find a path from the start to the goal. The maze is represented by a graph. The start and the goal are represented by vertices. The path is represented by a sequence of vertices.
- Minimum spanning tree (Kruskal, Prim, Boruvka), CCSP#4.4.2: [python](python-algorithm/algorithm/graph/test)(test) | Find the minimum spanning tree of a graph.

[:arrow_up_small: back to toc](#table-of-contents)

Expand Down
85 changes: 85 additions & 0 deletions python-algorithm/algorithm/graph/test/graph_data_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import networkx

seattle = 'Seattle'
san_francisco = 'San Francisco'
los_angeles = 'Los Angeles'
riverside = 'Riverside'
phoenix = 'Phoenix'
dallas = 'Dallas'
houston = 'Houston'
atlanta = 'Atlanta'
miami = 'Miami'
chicago = 'Chicago'
detroit = 'Detroit'
boston = 'Boston'
new_york = 'New York'
philadelphia = 'Philadelphia'
washington = 'Washington'


def create_city_graph() -> networkx.Graph:
edges = [
(seattle, chicago),
(seattle, san_francisco),
(san_francisco, riverside),
(san_francisco, los_angeles),
(los_angeles, riverside),
(los_angeles, phoenix),
(riverside, phoenix),
(riverside, chicago),
(phoenix, dallas),
(phoenix, houston),
(dallas, chicago),
(dallas, atlanta),
(dallas, houston),
(houston, atlanta),
(houston, miami),
(atlanta, chicago),
(atlanta, washington),
(atlanta, miami),
(miami, washington),
(chicago, detroit),
(detroit, boston),
(detroit, washington),
(detroit, new_york),
(boston, new_york),
(new_york, philadelphia),
(philadelphia, washington)
]
graph = networkx.Graph()
graph.add_edges_from(edges)
return graph


def create_weighted_city_graph() -> networkx.Graph:
weighted_edges = [
(seattle, chicago, 1737),
(seattle, san_francisco, 678),
(san_francisco, riverside, 386),
(san_francisco, los_angeles, 348),
(los_angeles, riverside, 50),
(los_angeles, phoenix, 357),
(riverside, phoenix, 307),
(riverside, chicago, 1704),
(phoenix, dallas, 887),
(phoenix, houston, 1015),
(dallas, chicago, 805),
(dallas, atlanta, 721),
(dallas, houston, 225),
(houston, atlanta, 702),
(houston, miami, 968),
(atlanta, chicago, 588),
(atlanta, washington, 543),
(atlanta, miami, 604),
(miami, washington, 923),
(chicago, detroit, 238),
(detroit, boston, 613),
(detroit, washington, 396),
(detroit, new_york, 482),
(boston, new_york, 190),
(new_york, philadelphia, 81),
(philadelphia, washington, 123)
]
graph = networkx.Graph()
graph.add_weighted_edges_from(weighted_edges)
return graph
71 changes: 21 additions & 50 deletions python-algorithm/algorithm/graph/test/test_breadth_first_search.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,7 @@
import pytest, networkx


def create_city_graph() -> networkx.Graph:
graph = networkx.Graph()
graph.add_edge("Seattle", "Chicago")
graph.add_edge("Seattle", "San Francisco")
graph.add_edge("San Francisco", "Riverside")
graph.add_edge("San Francisco", "Los Angeles")
graph.add_edge("Los Angeles", "Riverside")
graph.add_edge("Los Angeles", "Phoenix")
graph.add_edge("Riverside", "Phoenix")
graph.add_edge("Riverside", "Chicago")
graph.add_edge("Phoenix", "Dallas")
graph.add_edge("Phoenix", "Houston")
graph.add_edge("Dallas", "Chicago")
graph.add_edge("Dallas", "Atlanta")
graph.add_edge("Dallas", "Houston")
graph.add_edge("Houston", "Atlanta")
graph.add_edge("Houston", "Miami")
graph.add_edge("Atlanta", "Chicago")
graph.add_edge("Atlanta", "Washington")
graph.add_edge("Atlanta", "Miami")
graph.add_edge("Miami", "Washington")
graph.add_edge("Chicago", "Detroit")
graph.add_edge("Detroit", "Boston")
graph.add_edge("Detroit", "Washington")
graph.add_edge("Detroit", "New York")
graph.add_edge("Boston", "New York")
graph.add_edge("New York", "Philadelphia")
graph.add_edge("Philadelphia", "Washington")
return graph
import networkx
import pytest

from algorithm.graph.test.graph_data_utils import create_city_graph

city_graph = create_city_graph()

Expand All @@ -39,21 +10,21 @@ def create_city_graph() -> networkx.Graph:
@pytest.mark.parametrize(
argnames='graph, source, expected_neighbors',
argvalues=[
(city_graph, "Seattle", ["Chicago", "San Francisco"]),
(city_graph, "San Francisco", ["Seattle", "Riverside", "Los Angeles"]),
(city_graph, "Los Angeles", ["San Francisco", "Riverside", "Phoenix"]),
(city_graph, "Riverside", ["San Francisco", "Los Angeles", "Phoenix", "Chicago"]),
(city_graph, "Phoenix", ["Los Angeles", "Riverside", "Dallas", "Houston"]),
(city_graph, "Chicago", ["Seattle", "Riverside", "Dallas", "Atlanta", "Detroit"]),
(city_graph, "Boston", ["Detroit", "New York"]),
(city_graph, "New York", ["Detroit", "Boston", "Philadelphia"]),
(city_graph, "Atlanta", ["Dallas", "Houston", "Chicago", "Washington", "Miami"]),
(city_graph, "Miami", ["Houston", "Atlanta", "Washington"]),
(city_graph, "Dallas", ["Phoenix", "Chicago", "Atlanta", "Houston"]),
(city_graph, "Houston", ["Phoenix", "Dallas", "Atlanta", "Miami"]),
(city_graph, "Detroit", ["Chicago", "Boston", "Washington", "New York"]),
(city_graph, "Philadelphia", ["New York", "Washington"]),
(city_graph, "Washington", ["Atlanta", "Miami", "Detroit", "Philadelphia"])
(city_graph, 'Seattle', ['Chicago', 'San Francisco']),
(city_graph, 'San Francisco', ['Seattle', 'Riverside', 'Los Angeles']),
(city_graph, 'Los Angeles', ['San Francisco', 'Riverside', 'Phoenix']),
(city_graph, 'Riverside', ['San Francisco', 'Los Angeles', 'Phoenix', 'Chicago']),
(city_graph, 'Phoenix', ['Los Angeles', 'Riverside', 'Dallas', 'Houston']),
(city_graph, 'Chicago', ['Seattle', 'Riverside', 'Dallas', 'Atlanta', 'Detroit']),
(city_graph, 'Boston', ['Detroit', 'New York']),
(city_graph, 'New York', ['Detroit', 'Boston', 'Philadelphia']),
(city_graph, 'Atlanta', ['Dallas', 'Houston', 'Chicago', 'Washington', 'Miami']),
(city_graph, 'Miami', ['Houston', 'Atlanta', 'Washington']),
(city_graph, 'Dallas', ['Phoenix', 'Chicago', 'Atlanta', 'Houston']),
(city_graph, 'Houston', ['Phoenix', 'Dallas', 'Atlanta', 'Miami']),
(city_graph, 'Detroit', ['Chicago', 'Boston', 'Washington', 'New York']),
(city_graph, 'Philadelphia', ['New York', 'Washington']),
(city_graph, 'Washington', ['Atlanta', 'Miami', 'Detroit', 'Philadelphia'])
])
def test_graph_neighbors(benchmark, graph, source, expected_neighbors):
"""
Expand All @@ -71,9 +42,9 @@ def test_graph_neighbors(benchmark, graph, source, expected_neighbors):
@pytest.mark.parametrize(
argnames='graph, source, distance, expected_nodes',
argvalues=[
(city_graph, "Boston", 1, ["Detroit", "New York"]),
(city_graph, "Boston", 2, ["Chicago", "Washington", "Philadelphia"]),
(city_graph, "Boston", 3, ["Seattle", "Riverside", "Dallas", "Atlanta", "Miami"]),
(city_graph, 'Boston', 1, ['Detroit', 'New York']),
(city_graph, 'Boston', 2, ['Chicago', 'Washington', 'Philadelphia']),
(city_graph, 'Boston', 3, ['Seattle', 'Riverside', 'Dallas', 'Atlanta', 'Miami']),
],
ids=['distance1', 'distance2', 'distance3'])
def test_graph_breadth_first_search(benchmark, graph, source, distance, expected_nodes):
Expand Down
46 changes: 46 additions & 0 deletions python-algorithm/algorithm/graph/test/test_dijkstra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import networkx
import pytest

from algorithm.graph.test.graph_data_utils import create_weighted_city_graph

city_graph = create_weighted_city_graph()


@pytest.mark.benchmark(group='graph_dijkstra')
@pytest.mark.parametrize(
argnames='graph, source, target, expected_path',
argvalues=[
(city_graph, 'Los Angeles', 'Boston', ['Los Angeles', 'Riverside', 'Chicago', 'Detroit', 'Boston']),
],
ids=['los_angeles_to_boston'])
def test_graph_dijkstra_path(benchmark, graph, source, target, expected_path):
"""
Find the shortest path between two nodes in a graph using Dijkstra's algorithm.
:param benchmark: benchmark fixture
:param graph: city graph of the United States
:param source: source city node
:param target: target city node
:param expected_path: expected shortest path
"""
shortest_path = benchmark(networkx.dijkstra_path, graph, source, target)
assert shortest_path == expected_path


@pytest.mark.benchmark(group='graph_dijkstra')
@pytest.mark.parametrize(
argnames='graph, source, target, expected_distance',
argvalues=[
(city_graph, 'Los Angeles', 'Boston', 2605),
],
ids=['los_angeles_to_boston'])
def test_graph_dijkstra_distance(benchmark, graph, source, target, expected_distance):
"""
Find the shortest distance between two nodes in a graph using Dijkstra's algorithm.
:param benchmark: benchmark fixture
:param graph: city graph of the United States
:param source: source city node
:param target: target city node
:param expected_distance: expected shortest distance (weight)
"""
total_weight = benchmark(networkx.dijkstra_path_length, graph, source, target)
assert total_weight == expected_distance
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import matplotlib.pyplot as plt
import networkx
import pytest

from algorithm.graph.test.graph_data_utils import create_weighted_city_graph

city_graph = create_weighted_city_graph()


@pytest.mark.skip(reason='This test is for visualization only')
def test_graph_mst_temp():
mst = networkx.minimum_spanning_tree(city_graph, algorithm='kruskal')
pos = networkx.spring_layout(mst)
networkx.draw_networkx_nodes(mst, pos)
networkx.draw_networkx_edges(mst, pos, width=1)
networkx.draw_networkx_labels(mst, pos, font_size=10)
plt.show()


city_edges = {('Seattle', 'San Francisco'), ('San Francisco', 'Los Angeles'), ('Los Angeles', 'Riverside'),
('Riverside', 'Phoenix'), ('Phoenix', 'Dallas'), ('Dallas', 'Houston'), ('Houston', 'Atlanta'),
('Atlanta', 'Miami'), ('Atlanta', 'Washington'), ('Washington', 'Philadelphia'),
('Philadelphia', 'New York'), ('New York', 'Boston'), ('Washington', 'Detroit'), ('Detroit', 'Chicago')}


@pytest.mark.benchmark(group='graph_minimum_spanning_tree')
@pytest.mark.parametrize(
argnames='graph, algorithm, expected_total_weight, expected_edges',
argvalues=[
(city_graph, 'kruskal', 5372, city_edges),
(city_graph, 'prim', 5372, city_edges),
(city_graph, 'boruvka', 5372, city_edges),
],
ids=['kruskal', 'prim', 'boruvka'])
def test_graph_mst(benchmark, graph, algorithm, expected_total_weight, expected_edges):
mst = benchmark(networkx.minimum_spanning_tree, graph, algorithm=algorithm)

mst_total_weight = sum(d['weight'] for u, v, d in mst.edges(data=True))
assert expected_total_weight == mst_total_weight

mst_edges = set((u, v) for u, v, d in mst.edges(data=True))
normalized_set1 = {tuple(sorted(edge)) for edge in expected_edges}
normalized_set2 = {tuple(sorted(edge)) for edge in mst_edges}
assert normalized_set1 == normalized_set2
1 change: 1 addition & 0 deletions python-algorithm/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ python = ">=3.11,<3.13"
numpy = "^1.26.0"
scipy = "^1.12.0"
networkx = "^3.2.1"
matplotlib = "^3.8.2"

[tool.poetry.group.test.dependencies]
pytest = "^8.0.0"
Expand Down

0 comments on commit 4768b66

Please sign in to comment.