edsger 0.1.3__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {edsger-0.1.3 → edsger-0.1.5}/.github/workflows/docs.yml +8 -6
- {edsger-0.1.3 → edsger-0.1.5}/.github/workflows/publish.yml +3 -4
- {edsger-0.1.3 → edsger-0.1.5}/.github/workflows/tests.yml +14 -1
- {edsger-0.1.3/src/edsger.egg-info → edsger-0.1.5}/PKG-INFO +69 -5
- {edsger-0.1.3 → edsger-0.1.5}/README.md +68 -4
- edsger-0.1.5/docs/source/api.md +248 -0
- edsger-0.1.5/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/docs/source/index.md +12 -12
- {edsger-0.1.3 → edsger-0.1.5}/docs/source/quickstart.md +113 -6
- {edsger-0.1.3 → edsger-0.1.5}/pyproject.toml +8 -0
- edsger-0.1.5/scripts/README_benchmark_os.md +72 -0
- edsger-0.1.5/scripts/bellman_ford_dimacs.py +371 -0
- edsger-0.1.5/scripts/benchmark_comparison_os.py +409 -0
- edsger-0.1.5/scripts/benchmark_dimacs_USA_linux.json +84 -0
- edsger-0.1.5/scripts/benchmark_dimacs_USA_windows.json +69 -0
- edsger-0.1.5/scripts/compare_bellman_ford_scipy.py +538 -0
- edsger-0.1.5/scripts/compare_dijkstra_scipy.py +361 -0
- {edsger-0.1.3 → edsger-0.1.5}/scripts/dijkstra_dimacs.py +26 -2
- edsger-0.1.5/scripts/plot_benchmark_comparison.py +414 -0
- edsger-0.1.3/scripts/requirements.txt → edsger-0.1.5/scripts/requirements_linux.txt +0 -1
- edsger-0.1.5/scripts/requirements_windows.txt +9 -0
- {edsger-0.1.3 → edsger-0.1.5}/setup.py +30 -8
- edsger-0.1.5/src/edsger/_version.py +1 -0
- edsger-0.1.5/src/edsger/bellman_ford.c +34907 -0
- edsger-0.1.5/src/edsger/bellman_ford.pyx +544 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/commons.c +246 -243
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/dijkstra.c +277 -262
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/path.py +622 -23
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/path_tracking.c +259 -244
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/pq_4ary_dec_0b.c +267 -252
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/spiess_florian.c +276 -261
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/star.c +259 -244
- edsger-0.1.5/src/edsger/utils.py +127 -0
- {edsger-0.1.3 → edsger-0.1.5/src/edsger.egg-info}/PKG-INFO +69 -5
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/SOURCES.txt +13 -2
- edsger-0.1.5/tests/test_bellman_ford.py +684 -0
- {edsger-0.1.3 → edsger-0.1.5}/tests/test_dijkstra.py +0 -5
- {edsger-0.1.3 → edsger-0.1.5}/tests/test_path.py +173 -6
- {edsger-0.1.3 → edsger-0.1.5}/tests/test_path_tracking.py +0 -5
- {edsger-0.1.3 → edsger-0.1.5}/tests/test_pq_4ary_dec_0b.py +0 -5
- {edsger-0.1.3 → edsger-0.1.5}/tests/test_spiess_florian.py +0 -5
- edsger-0.1.3/docs/source/api.md +0 -116
- edsger-0.1.3/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
- edsger-0.1.3/scripts/benchmark_comparison.py +0 -442
- edsger-0.1.3/src/edsger/_version.py +0 -1
- edsger-0.1.3/src/edsger/utils.py +0 -63
- {edsger-0.1.3 → edsger-0.1.5}/.gitignore +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/.pre-commit-config.yaml +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/.readthedocs.yaml +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/AUTHORS.rst +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/CHANGELOG.rst +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/CONTRIBUTING.rst +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/LICENSE +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/MANIFEST.in +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/docs/Makefile +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/docs/requirements.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/docs/source/conf.py +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/docs/source/contributing.md +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/docs/source/installation.md +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/requirements-dev.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/requirements.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/setup.cfg +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/.gitignore +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/__init__.py +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/commons.pxd +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/commons.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/dijkstra.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/networks.py +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/path_tracking.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/pq_4ary_dec_0b.pxd +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/pq_4ary_dec_0b.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/prefetch_compat.h +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/spiess_florian.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger/star.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/dependency_links.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/not-zip-safe +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/requires.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/top_level.txt +0 -0
@@ -1,10 +1,8 @@
|
|
1
1
|
name: Documentation
|
2
2
|
|
3
3
|
on:
|
4
|
-
|
5
|
-
|
6
|
-
pull_request:
|
7
|
-
branches: [ release ]
|
4
|
+
release:
|
5
|
+
types: [created]
|
8
6
|
|
9
7
|
jobs:
|
10
8
|
build-docs:
|
@@ -12,6 +10,9 @@ jobs:
|
|
12
10
|
|
13
11
|
steps:
|
14
12
|
- uses: actions/checkout@v4
|
13
|
+
with:
|
14
|
+
fetch-depth: 0 # Fetch all history for all tags and branches
|
15
|
+
ref: ${{ github.event.release.tag_name }}
|
15
16
|
|
16
17
|
- name: Set up Python
|
17
18
|
uses: actions/setup-python@v5
|
@@ -22,6 +23,7 @@ jobs:
|
|
22
23
|
run: |
|
23
24
|
python -m pip install --upgrade pip
|
24
25
|
pip install -r docs/requirements.txt
|
26
|
+
pip install .
|
25
27
|
|
26
28
|
- name: Build documentation
|
27
29
|
run: |
|
@@ -34,8 +36,8 @@ jobs:
|
|
34
36
|
name: documentation-html
|
35
37
|
path: docs/build/html/
|
36
38
|
|
37
|
-
- name: Deploy to GitHub Pages
|
38
|
-
if: github.
|
39
|
+
- name: Deploy to GitHub Pages
|
40
|
+
if: github.event_name == 'release'
|
39
41
|
uses: peaceiris/actions-gh-pages@v4
|
40
42
|
with:
|
41
43
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
@@ -1,9 +1,8 @@
|
|
1
1
|
name: ci-publish # Publish Python distribution to PyPI
|
2
2
|
|
3
3
|
on:
|
4
|
-
|
5
|
-
|
6
|
-
- 'v[0-9]+.[0-9]+.[0-9]+'
|
4
|
+
release:
|
5
|
+
types: [created]
|
7
6
|
|
8
7
|
jobs:
|
9
8
|
|
@@ -49,7 +48,7 @@ jobs:
|
|
49
48
|
runs-on: ${{ matrix.os }}
|
50
49
|
strategy:
|
51
50
|
matrix:
|
52
|
-
os: [ubuntu-latest, windows-
|
51
|
+
os: [ubuntu-latest, windows-2022, macos-13, macos-14]
|
53
52
|
|
54
53
|
steps:
|
55
54
|
- uses: actions/checkout@v4
|
@@ -4,6 +4,8 @@ on:
|
|
4
4
|
push:
|
5
5
|
branches:
|
6
6
|
- release
|
7
|
+
release:
|
8
|
+
types: [created]
|
7
9
|
|
8
10
|
jobs:
|
9
11
|
test:
|
@@ -27,16 +29,27 @@ jobs:
|
|
27
29
|
python -m pip install --upgrade pip
|
28
30
|
pip install -r requirements-dev.txt
|
29
31
|
pip install .
|
32
|
+
- name: Reinstall package with coverage support
|
33
|
+
if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
|
34
|
+
run: |
|
35
|
+
pip uninstall -y edsger
|
36
|
+
CYTHON_TRACE=1 pip install -e .
|
30
37
|
- name: Format Python code with black
|
31
38
|
run: |
|
32
39
|
black --check --diff .
|
33
40
|
- name: Lint Cython code
|
34
41
|
run: |
|
35
42
|
cython-lint src/edsger/commons.pyx src/edsger/dijkstra.pyx src/edsger/path_tracking.pyx src/edsger/pq_4ary_dec_0b.pyx src/edsger/spiess_florian.pyx src/edsger/star.pyx src/edsger/commons.pxd src/edsger/pq_4ary_dec_0b.pxd
|
36
|
-
- name: Run tests
|
43
|
+
- name: Run tests with coverage
|
44
|
+
if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
|
37
45
|
run: |
|
38
46
|
pytest --cov=src/edsger --cov-branch --cov-report=xml tests/
|
47
|
+
- name: Run tests without coverage
|
48
|
+
if: matrix.os != 'ubuntu-22.04' || matrix.python-version != '3.11'
|
49
|
+
run: |
|
50
|
+
pytest tests/
|
39
51
|
- name: Upload results to Codecov
|
52
|
+
if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
|
40
53
|
uses: codecov/codecov-action@v5
|
41
54
|
with:
|
42
55
|
token: ${{ secrets.CODECOV_TOKEN }}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: edsger
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.5
|
4
4
|
Summary: Graph algorithms in Cython.
|
5
5
|
Author-email: François Pacull <francois.pacull@architecture-performance.fr>
|
6
6
|
Maintainer-email: François Pacull <francois.pacull@architecture-performance.fr>
|
@@ -42,6 +42,7 @@ Dynamic: license-file
|
|
42
42
|
|
43
43
|

|
44
44
|
[](https://codecov.io/gh/aetperf/edsger)
|
45
|
+
[](https://edsger.readthedocs.io/en/latest/?badge=latest)
|
45
46
|
[](https://pypi.org/project/edsger/)
|
46
47
|
[](https://pepy.tech/project/edsger)
|
47
48
|
[](https://pypi.org/project/edsger/)
|
@@ -53,7 +54,7 @@ Dynamic: license-file
|
|
53
54
|
|
54
55
|
*Graph algorithms in Cython*
|
55
56
|
|
56
|
-
Welcome to our Python library for graph algorithms.
|
57
|
+
Welcome to our Python library for graph algorithms. The library includes both Dijkstra's and Bellman-Ford's algorithms, with plans to add more common path algorithms later. It is also open-source and easy to integrate with other Python libraries. To get started, simply install the library using pip, and import it into your Python project.
|
57
58
|
|
58
59
|
Documentation : [https://edsger.readthedocs.io/en/latest/](https://edsger.readthedocs.io/en/latest/)
|
59
60
|
|
@@ -97,14 +98,77 @@ print("Shortest paths:", shortest_paths)
|
|
97
98
|
|
98
99
|
We get the shortest paths from the source node 0 to all other nodes in the graph. The output is an array with the shortest path length to each node. A path length is the sum of the weights of the edges in the path.
|
99
100
|
|
101
|
+
## Bellman-Ford Algorithm: Handling Negative Weights
|
102
|
+
|
103
|
+
The Bellman-Ford algorithm can handle graphs with negative edge weights and detect negative cycles, making it suitable for more complex scenarios than Dijkstra's algorithm.
|
104
|
+
|
105
|
+
```python
|
106
|
+
from edsger.path import BellmanFord
|
107
|
+
|
108
|
+
# Create a graph with negative weights
|
109
|
+
edges_negative = pd.DataFrame({
|
110
|
+
'tail': [0, 0, 1, 1, 2, 3],
|
111
|
+
'head': [1, 2, 2, 3, 3, 4],
|
112
|
+
'weight': [1, 4, -2, 5, 1, 3] # Note the negative weight
|
113
|
+
})
|
114
|
+
edges_negative
|
115
|
+
```
|
116
|
+
|
117
|
+
| | tail | head | weight |
|
118
|
+
|---:|-------:|-------:|---------:|
|
119
|
+
| 0 | 0 | 1 | 1.0 |
|
120
|
+
| 1 | 0 | 2 | 4.0 |
|
121
|
+
| 2 | 1 | 2 | -2.0 |
|
122
|
+
| 3 | 1 | 3 | 5.0 |
|
123
|
+
| 4 | 2 | 3 | 1.0 |
|
124
|
+
| 5 | 3 | 4 | 3.0 |
|
125
|
+
|
126
|
+
```python
|
127
|
+
# Initialize and run Bellman-Ford
|
128
|
+
bf = BellmanFord(edges_negative)
|
129
|
+
shortest_paths = bf.run(vertex_idx=0)
|
130
|
+
print("Shortest paths:", shortest_paths)
|
131
|
+
```
|
132
|
+
|
133
|
+
Shortest paths: [ 0. 1. -1. 0. 3.]
|
134
|
+
|
135
|
+
The Bellman-Ford algorithm finds the optimal path even with negative weights. In this example, the shortest path from node 0 to node 2 has length -1 (going 0→1→2 with weights 1 + (-2) = -1), which is shorter than the direct path 0→2 with weight 4.
|
136
|
+
|
137
|
+
### Negative Cycle Detection
|
138
|
+
|
139
|
+
Bellman-Ford can also detect negative cycles, which indicate that no shortest path exists:
|
140
|
+
|
141
|
+
```python
|
142
|
+
# Create a graph with a negative cycle
|
143
|
+
edges_cycle = pd.DataFrame({
|
144
|
+
'tail': [0, 1, 2],
|
145
|
+
'head': [1, 2, 0],
|
146
|
+
'weight': [1, -2, -1] # Cycle 0→1→2→0 has total weight -2
|
147
|
+
})
|
148
|
+
|
149
|
+
bf_cycle = BellmanFord(edges_cycle)
|
150
|
+
try:
|
151
|
+
bf_cycle.run(vertex_idx=0)
|
152
|
+
except ValueError as e:
|
153
|
+
print("Error:", e)
|
154
|
+
```
|
155
|
+
|
156
|
+
Error: Negative cycle detected in the graph
|
157
|
+
|
158
|
+
## Installation
|
159
|
+
|
160
|
+
### Standard Installation
|
161
|
+
|
162
|
+
```bash
|
163
|
+
pip install edsger
|
164
|
+
```
|
165
|
+
|
100
166
|
## Why Use Edsger?
|
101
167
|
|
102
|
-
Edsger is designed to be **dataframe-friendly**, providing seamless integration with pandas workflows for graph algorithms. Also it is rather efficient. Our benchmarks on the USA road network (23.9M vertices, 57.7M edges) demonstrate nice performance:
|
168
|
+
Edsger is designed to be **dataframe-friendly**, providing seamless integration with pandas workflows for graph algorithms. Also it is rather efficient on Linux. Our benchmarks on the USA road network (23.9M vertices, 57.7M edges) demonstrate nice performance:
|
103
169
|
|
104
170
|
<img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
|
105
171
|
|
106
|
-
*Benchmark performed on Intel i9-12900H Linux laptop.*
|
107
|
-
|
108
172
|
## Contributing
|
109
173
|
|
110
174
|
We welcome contributions to the Edsger library. If you have any suggestions, bug reports, or feature requests, please open an issue on our [GitHub repository](https://github.com/aetperf/Edsger).
|
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
2
|

|
3
3
|
[](https://codecov.io/gh/aetperf/edsger)
|
4
|
+
[](https://edsger.readthedocs.io/en/latest/?badge=latest)
|
4
5
|
[](https://pypi.org/project/edsger/)
|
5
6
|
[](https://pepy.tech/project/edsger)
|
6
7
|
[](https://pypi.org/project/edsger/)
|
@@ -12,7 +13,7 @@
|
|
12
13
|
|
13
14
|
*Graph algorithms in Cython*
|
14
15
|
|
15
|
-
Welcome to our Python library for graph algorithms.
|
16
|
+
Welcome to our Python library for graph algorithms. The library includes both Dijkstra's and Bellman-Ford's algorithms, with plans to add more common path algorithms later. It is also open-source and easy to integrate with other Python libraries. To get started, simply install the library using pip, and import it into your Python project.
|
16
17
|
|
17
18
|
Documentation : [https://edsger.readthedocs.io/en/latest/](https://edsger.readthedocs.io/en/latest/)
|
18
19
|
|
@@ -56,14 +57,77 @@ print("Shortest paths:", shortest_paths)
|
|
56
57
|
|
57
58
|
We get the shortest paths from the source node 0 to all other nodes in the graph. The output is an array with the shortest path length to each node. A path length is the sum of the weights of the edges in the path.
|
58
59
|
|
60
|
+
## Bellman-Ford Algorithm: Handling Negative Weights
|
61
|
+
|
62
|
+
The Bellman-Ford algorithm can handle graphs with negative edge weights and detect negative cycles, making it suitable for more complex scenarios than Dijkstra's algorithm.
|
63
|
+
|
64
|
+
```python
|
65
|
+
from edsger.path import BellmanFord
|
66
|
+
|
67
|
+
# Create a graph with negative weights
|
68
|
+
edges_negative = pd.DataFrame({
|
69
|
+
'tail': [0, 0, 1, 1, 2, 3],
|
70
|
+
'head': [1, 2, 2, 3, 3, 4],
|
71
|
+
'weight': [1, 4, -2, 5, 1, 3] # Note the negative weight
|
72
|
+
})
|
73
|
+
edges_negative
|
74
|
+
```
|
75
|
+
|
76
|
+
| | tail | head | weight |
|
77
|
+
|---:|-------:|-------:|---------:|
|
78
|
+
| 0 | 0 | 1 | 1.0 |
|
79
|
+
| 1 | 0 | 2 | 4.0 |
|
80
|
+
| 2 | 1 | 2 | -2.0 |
|
81
|
+
| 3 | 1 | 3 | 5.0 |
|
82
|
+
| 4 | 2 | 3 | 1.0 |
|
83
|
+
| 5 | 3 | 4 | 3.0 |
|
84
|
+
|
85
|
+
```python
|
86
|
+
# Initialize and run Bellman-Ford
|
87
|
+
bf = BellmanFord(edges_negative)
|
88
|
+
shortest_paths = bf.run(vertex_idx=0)
|
89
|
+
print("Shortest paths:", shortest_paths)
|
90
|
+
```
|
91
|
+
|
92
|
+
Shortest paths: [ 0. 1. -1. 0. 3.]
|
93
|
+
|
94
|
+
The Bellman-Ford algorithm finds the optimal path even with negative weights. In this example, the shortest path from node 0 to node 2 has length -1 (going 0→1→2 with weights 1 + (-2) = -1), which is shorter than the direct path 0→2 with weight 4.
|
95
|
+
|
96
|
+
### Negative Cycle Detection
|
97
|
+
|
98
|
+
Bellman-Ford can also detect negative cycles, which indicate that no shortest path exists:
|
99
|
+
|
100
|
+
```python
|
101
|
+
# Create a graph with a negative cycle
|
102
|
+
edges_cycle = pd.DataFrame({
|
103
|
+
'tail': [0, 1, 2],
|
104
|
+
'head': [1, 2, 0],
|
105
|
+
'weight': [1, -2, -1] # Cycle 0→1→2→0 has total weight -2
|
106
|
+
})
|
107
|
+
|
108
|
+
bf_cycle = BellmanFord(edges_cycle)
|
109
|
+
try:
|
110
|
+
bf_cycle.run(vertex_idx=0)
|
111
|
+
except ValueError as e:
|
112
|
+
print("Error:", e)
|
113
|
+
```
|
114
|
+
|
115
|
+
Error: Negative cycle detected in the graph
|
116
|
+
|
117
|
+
## Installation
|
118
|
+
|
119
|
+
### Standard Installation
|
120
|
+
|
121
|
+
```bash
|
122
|
+
pip install edsger
|
123
|
+
```
|
124
|
+
|
59
125
|
## Why Use Edsger?
|
60
126
|
|
61
|
-
Edsger is designed to be **dataframe-friendly**, providing seamless integration with pandas workflows for graph algorithms. Also it is rather efficient. Our benchmarks on the USA road network (23.9M vertices, 57.7M edges) demonstrate nice performance:
|
127
|
+
Edsger is designed to be **dataframe-friendly**, providing seamless integration with pandas workflows for graph algorithms. Also it is rather efficient on Linux. Our benchmarks on the USA road network (23.9M vertices, 57.7M edges) demonstrate nice performance:
|
62
128
|
|
63
129
|
<img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
|
64
130
|
|
65
|
-
*Benchmark performed on Intel i9-12900H Linux laptop.*
|
66
|
-
|
67
131
|
## Contributing
|
68
132
|
|
69
133
|
We welcome contributions to the Edsger library. If you have any suggestions, bug reports, or feature requests, please open an issue on our [GitHub repository](https://github.com/aetperf/Edsger).
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# API Reference
|
2
|
+
|
3
|
+
This section provides detailed documentation for all public classes and functions in Edsger.
|
4
|
+
|
5
|
+
## edsger.path Module
|
6
|
+
|
7
|
+
### Dijkstra Class
|
8
|
+
|
9
|
+
The main class for performing Dijkstra's shortest path algorithm.
|
10
|
+
|
11
|
+
#### Constructor Parameters
|
12
|
+
|
13
|
+
- `edges`: pandas.DataFrame containing the graph edges
|
14
|
+
- `tail`: Column name for edge source nodes (default: 'tail')
|
15
|
+
- `head`: Column name for edge destination nodes (default: 'head')
|
16
|
+
- `weight`: Column name for edge weights (default: 'weight')
|
17
|
+
- `orientation`: Either 'out' (single-source) or 'in' (single-target) (default: 'out')
|
18
|
+
- `check_edges`: Whether to validate edge data (default: False)
|
19
|
+
- `permute`: Whether to optimize node indexing (default: False)
|
20
|
+
|
21
|
+
#### Methods
|
22
|
+
|
23
|
+
##### run
|
24
|
+
|
25
|
+
```python
|
26
|
+
def run(self, vertex_idx, path_tracking=False, return_inf=True,
|
27
|
+
return_series=False, heap_length_ratio=1.0)
|
28
|
+
```
|
29
|
+
|
30
|
+
Runs the shortest path algorithm from/to the specified vertex.
|
31
|
+
|
32
|
+
**Parameters:**
|
33
|
+
- `vertex_idx`: Source/target vertex index
|
34
|
+
- `path_tracking`: Whether to track paths for reconstruction (default: False)
|
35
|
+
- `return_inf`: Return infinity for unreachable vertices (default: True)
|
36
|
+
- `return_series`: Return results as pandas Series (default: False)
|
37
|
+
- `heap_length_ratio`: Heap size as fraction of vertices (default: 1.0)
|
38
|
+
|
39
|
+
**Returns:**
|
40
|
+
- Array or Series of shortest path lengths
|
41
|
+
|
42
|
+
##### get_path
|
43
|
+
|
44
|
+
```python
|
45
|
+
def get_path(self, vertex_idx)
|
46
|
+
```
|
47
|
+
|
48
|
+
Reconstructs the shortest path to/from a vertex (requires `path_tracking=True`).
|
49
|
+
|
50
|
+
**Parameters:**
|
51
|
+
- `vertex_idx`: Destination/source vertex index
|
52
|
+
|
53
|
+
**Returns:**
|
54
|
+
- Array of vertex indices forming the path
|
55
|
+
|
56
|
+
##### get_vertices
|
57
|
+
|
58
|
+
```python
|
59
|
+
def get_vertices(self)
|
60
|
+
```
|
61
|
+
|
62
|
+
Returns all vertices in the graph.
|
63
|
+
|
64
|
+
**Returns:**
|
65
|
+
- Array of vertex indices
|
66
|
+
|
67
|
+
### Examples
|
68
|
+
|
69
|
+
#### Basic Usage
|
70
|
+
|
71
|
+
```python
|
72
|
+
from edsger.path import Dijkstra
|
73
|
+
import pandas as pd
|
74
|
+
|
75
|
+
# Create a graph
|
76
|
+
edges = pd.DataFrame({
|
77
|
+
'tail': [0, 0, 1],
|
78
|
+
'head': [1, 2, 2],
|
79
|
+
'weight': [1, 4, 2]
|
80
|
+
})
|
81
|
+
|
82
|
+
# Initialize Dijkstra
|
83
|
+
dijkstra = Dijkstra(edges)
|
84
|
+
|
85
|
+
# Find shortest paths from vertex 0
|
86
|
+
paths = dijkstra.run(vertex_idx=0)
|
87
|
+
```
|
88
|
+
|
89
|
+
#### With Path Tracking
|
90
|
+
|
91
|
+
```python
|
92
|
+
# Enable path tracking
|
93
|
+
paths = dijkstra.run(vertex_idx=0, path_tracking=True)
|
94
|
+
|
95
|
+
# Get the actual path to vertex 2
|
96
|
+
path = dijkstra.get_path(vertex_idx=2)
|
97
|
+
```
|
98
|
+
|
99
|
+
#### Custom Parameters
|
100
|
+
|
101
|
+
```python
|
102
|
+
# Create with custom settings
|
103
|
+
dijkstra = Dijkstra(
|
104
|
+
edges,
|
105
|
+
orientation='in',
|
106
|
+
check_edges=True,
|
107
|
+
permute=True
|
108
|
+
)
|
109
|
+
|
110
|
+
# Run with custom parameters
|
111
|
+
paths = dijkstra.run(
|
112
|
+
vertex_idx=2,
|
113
|
+
return_series=True,
|
114
|
+
heap_length_ratio=0.5
|
115
|
+
)
|
116
|
+
```
|
117
|
+
|
118
|
+
### BellmanFord Class
|
119
|
+
|
120
|
+
The class for performing Bellman-Ford shortest path algorithm, which handles graphs with negative edge weights and detects negative cycles.
|
121
|
+
|
122
|
+
#### Constructor Parameters
|
123
|
+
|
124
|
+
- `edges`: pandas.DataFrame containing the graph edges
|
125
|
+
- `tail`: Column name for edge source nodes (default: 'tail')
|
126
|
+
- `head`: Column name for edge destination nodes (default: 'head')
|
127
|
+
- `weight`: Column name for edge weights (default: 'weight')
|
128
|
+
- `orientation`: Either 'out' (single-source) or 'in' (single-target) (default: 'out')
|
129
|
+
- `check_edges`: Whether to validate edge data (default: False)
|
130
|
+
- `permute`: Whether to optimize node indexing (default: False)
|
131
|
+
|
132
|
+
#### Methods
|
133
|
+
|
134
|
+
##### run
|
135
|
+
|
136
|
+
```python
|
137
|
+
def run(self, vertex_idx, path_tracking=False, return_inf=True,
|
138
|
+
return_series=False, detect_negative_cycles=True)
|
139
|
+
```
|
140
|
+
|
141
|
+
Runs the Bellman-Ford algorithm from/to the specified vertex.
|
142
|
+
|
143
|
+
**Parameters:**
|
144
|
+
- `vertex_idx`: Source/target vertex index
|
145
|
+
- `path_tracking`: Whether to track paths for reconstruction (default: False)
|
146
|
+
- `return_inf`: Return infinity for unreachable vertices (default: True)
|
147
|
+
- `return_series`: Return results as pandas Series (default: False)
|
148
|
+
- `detect_negative_cycles`: Whether to detect negative cycles (default: True)
|
149
|
+
|
150
|
+
**Returns:**
|
151
|
+
- Array or Series of shortest path lengths
|
152
|
+
|
153
|
+
**Raises:**
|
154
|
+
- `ValueError`: If a negative cycle is detected and `detect_negative_cycles=True`
|
155
|
+
|
156
|
+
##### get_path
|
157
|
+
|
158
|
+
```python
|
159
|
+
def get_path(self, vertex_idx)
|
160
|
+
```
|
161
|
+
|
162
|
+
Reconstructs the shortest path to/from a vertex (requires `path_tracking=True`).
|
163
|
+
|
164
|
+
**Parameters:**
|
165
|
+
- `vertex_idx`: Destination/source vertex index
|
166
|
+
|
167
|
+
**Returns:**
|
168
|
+
- Array of vertex indices forming the path
|
169
|
+
|
170
|
+
##### get_vertices
|
171
|
+
|
172
|
+
```python
|
173
|
+
def get_vertices(self)
|
174
|
+
```
|
175
|
+
|
176
|
+
Returns all vertices in the graph.
|
177
|
+
|
178
|
+
**Returns:**
|
179
|
+
- Array of vertex indices
|
180
|
+
|
181
|
+
#### Examples
|
182
|
+
|
183
|
+
##### Basic Usage with Negative Weights
|
184
|
+
|
185
|
+
```python
|
186
|
+
from edsger.path import BellmanFord
|
187
|
+
import pandas as pd
|
188
|
+
|
189
|
+
# Create a graph with negative weights
|
190
|
+
edges = pd.DataFrame({
|
191
|
+
'tail': [0, 0, 1, 1, 2, 3],
|
192
|
+
'head': [1, 2, 2, 3, 3, 4],
|
193
|
+
'weight': [1, 4, -2, 5, 1, 3] # Note the negative weight
|
194
|
+
})
|
195
|
+
|
196
|
+
# Initialize Bellman-Ford
|
197
|
+
bf = BellmanFord(edges)
|
198
|
+
|
199
|
+
# Find shortest paths from vertex 0
|
200
|
+
paths = bf.run(vertex_idx=0)
|
201
|
+
print(paths) # [ 0. 1. -1. 0. 3.]
|
202
|
+
```
|
203
|
+
|
204
|
+
##### Negative Cycle Detection
|
205
|
+
|
206
|
+
```python
|
207
|
+
# Create a graph with a negative cycle
|
208
|
+
edges_cycle = pd.DataFrame({
|
209
|
+
'tail': [0, 1, 2],
|
210
|
+
'head': [1, 2, 0],
|
211
|
+
'weight': [1, -2, -1] # Cycle 0→1→2→0 has total weight -2
|
212
|
+
})
|
213
|
+
|
214
|
+
bf_cycle = BellmanFord(edges_cycle)
|
215
|
+
try:
|
216
|
+
bf_cycle.run(vertex_idx=0)
|
217
|
+
except ValueError as e:
|
218
|
+
print(f"Error: {e}") # Error: Negative cycle detected in the graph
|
219
|
+
```
|
220
|
+
|
221
|
+
##### Path Tracking with Negative Weights
|
222
|
+
|
223
|
+
```python
|
224
|
+
# Enable path tracking
|
225
|
+
paths = bf.run(vertex_idx=0, path_tracking=True)
|
226
|
+
|
227
|
+
# Get the actual path to vertex 2 (using negative weight path)
|
228
|
+
path = bf.get_path(vertex_idx=2)
|
229
|
+
print(path) # Path using the negative weight edge
|
230
|
+
```
|
231
|
+
|
232
|
+
##### Performance: Disabling Cycle Detection
|
233
|
+
|
234
|
+
```python
|
235
|
+
# For performance when you know there are no negative cycles
|
236
|
+
paths = bf.run(vertex_idx=0, detect_negative_cycles=False)
|
237
|
+
```
|
238
|
+
|
239
|
+
## Algorithm Comparison
|
240
|
+
|
241
|
+
| Feature | Dijkstra | BellmanFord |
|
242
|
+
|---------|----------|-------------|
|
243
|
+
| **Negative weights** | ❌ No | ✅ Yes |
|
244
|
+
| **Negative cycle detection** | ❌ No | ✅ Yes |
|
245
|
+
| **Time complexity** | O((V + E) log V) | O(VE) |
|
246
|
+
| **Space complexity** | O(V) | O(V) |
|
247
|
+
| **Use case** | Positive weights only | Any weights, cycle detection |
|
248
|
+
| **Performance** | Faster | Slower but more versatile |
|
Binary file
|
@@ -6,21 +6,19 @@ github_url: https://github.com/aetperf/Edsger
|
|
6
6
|
|
7
7
|
*Graph algorithms in Cython*
|
8
8
|
|
9
|
-
Welcome to the Edsger documentation! Edsger is a Python library for efficient graph algorithms implemented in Cython. The library
|
9
|
+
Welcome to the Edsger documentation! Edsger is a Python library for efficient graph algorithms implemented in Cython. The library focuses on shortest path algorithms, featuring so far both Dijkstra's algorithm for positive-weight graphs and Bellman-Ford algorithm for graphs with negative weights and cycle detection.
|
10
10
|
|
11
11
|
## Why Use Edsger?
|
12
12
|
|
13
|
-
Edsger is designed to be **dataframe-friendly**, providing seamless integration with pandas workflows for graph algorithms. Also it is rather efficient. Our benchmarks on the USA road network (23.9M vertices, 57.7M edges) demonstrate nice performance:
|
13
|
+
Edsger is designed to be **dataframe-friendly**, providing seamless integration with pandas workflows for graph algorithms. Also it is rather efficient on Linux. Our benchmarks on the USA road network (23.9M vertices, 57.7M edges) demonstrate nice performance:
|
14
14
|
|
15
15
|
<img src="assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
|
16
16
|
|
17
|
-
*Benchmark performed on Intel i9-12900H Linux laptop.*
|
18
|
-
|
19
17
|
### Pandas Integration Made Simple
|
20
18
|
|
21
19
|
```python
|
22
20
|
import pandas as pd
|
23
|
-
from edsger.path import Dijkstra
|
21
|
+
from edsger.path import Dijkstra, BellmanFord
|
24
22
|
|
25
23
|
# Your graph data is already in a DataFrame
|
26
24
|
edges = pd.DataFrame({
|
@@ -29,19 +27,21 @@ edges = pd.DataFrame({
|
|
29
27
|
'weight': [1.0, 2.0, 1.5, 1.0]
|
30
28
|
})
|
31
29
|
|
32
|
-
#
|
33
|
-
dijkstra = Dijkstra(edges
|
30
|
+
# Use Dijkstra for positive weights (faster)
|
31
|
+
dijkstra = Dijkstra(edges)
|
34
32
|
distances = dijkstra.run(vertex_idx=0)
|
35
|
-
|
33
|
+
|
34
|
+
# Use Bellman-Ford for negative weights or cycle detection
|
35
|
+
bf = BellmanFord(edges)
|
36
|
+
distances = bf.run(vertex_idx=0)
|
36
37
|
```
|
37
|
-
array([0., 1., 2., 3.])
|
38
38
|
|
39
39
|
## Key Features
|
40
40
|
|
41
41
|
- **Native pandas DataFrame support** - No graph object conversion required
|
42
|
-
- **High performance** - Cython implementation
|
43
|
-
- **Memory efficient** - Optimized for
|
44
|
-
- **Easy integration** with NumPy and
|
42
|
+
- **High performance** - Cython implementation
|
43
|
+
- **Memory efficient** - Optimized for real-world datasets
|
44
|
+
- **Easy integration** with NumPy and Pandas workflows
|
45
45
|
- **Production ready** - Comprehensive testing across Python 3.9-3.13
|
46
46
|
|
47
47
|
## Quick Links
|