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.
Files changed (78) hide show
  1. {edsger-0.1.3 → edsger-0.1.5}/.github/workflows/docs.yml +8 -6
  2. {edsger-0.1.3 → edsger-0.1.5}/.github/workflows/publish.yml +3 -4
  3. {edsger-0.1.3 → edsger-0.1.5}/.github/workflows/tests.yml +14 -1
  4. {edsger-0.1.3/src/edsger.egg-info → edsger-0.1.5}/PKG-INFO +69 -5
  5. {edsger-0.1.3 → edsger-0.1.5}/README.md +68 -4
  6. edsger-0.1.5/docs/source/api.md +248 -0
  7. edsger-0.1.5/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
  8. {edsger-0.1.3 → edsger-0.1.5}/docs/source/index.md +12 -12
  9. {edsger-0.1.3 → edsger-0.1.5}/docs/source/quickstart.md +113 -6
  10. {edsger-0.1.3 → edsger-0.1.5}/pyproject.toml +8 -0
  11. edsger-0.1.5/scripts/README_benchmark_os.md +72 -0
  12. edsger-0.1.5/scripts/bellman_ford_dimacs.py +371 -0
  13. edsger-0.1.5/scripts/benchmark_comparison_os.py +409 -0
  14. edsger-0.1.5/scripts/benchmark_dimacs_USA_linux.json +84 -0
  15. edsger-0.1.5/scripts/benchmark_dimacs_USA_windows.json +69 -0
  16. edsger-0.1.5/scripts/compare_bellman_ford_scipy.py +538 -0
  17. edsger-0.1.5/scripts/compare_dijkstra_scipy.py +361 -0
  18. {edsger-0.1.3 → edsger-0.1.5}/scripts/dijkstra_dimacs.py +26 -2
  19. edsger-0.1.5/scripts/plot_benchmark_comparison.py +414 -0
  20. edsger-0.1.3/scripts/requirements.txt → edsger-0.1.5/scripts/requirements_linux.txt +0 -1
  21. edsger-0.1.5/scripts/requirements_windows.txt +9 -0
  22. {edsger-0.1.3 → edsger-0.1.5}/setup.py +30 -8
  23. edsger-0.1.5/src/edsger/_version.py +1 -0
  24. edsger-0.1.5/src/edsger/bellman_ford.c +34907 -0
  25. edsger-0.1.5/src/edsger/bellman_ford.pyx +544 -0
  26. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/commons.c +246 -243
  27. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/dijkstra.c +277 -262
  28. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/path.py +622 -23
  29. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/path_tracking.c +259 -244
  30. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/pq_4ary_dec_0b.c +267 -252
  31. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/spiess_florian.c +276 -261
  32. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/star.c +259 -244
  33. edsger-0.1.5/src/edsger/utils.py +127 -0
  34. {edsger-0.1.3 → edsger-0.1.5/src/edsger.egg-info}/PKG-INFO +69 -5
  35. {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/SOURCES.txt +13 -2
  36. edsger-0.1.5/tests/test_bellman_ford.py +684 -0
  37. {edsger-0.1.3 → edsger-0.1.5}/tests/test_dijkstra.py +0 -5
  38. {edsger-0.1.3 → edsger-0.1.5}/tests/test_path.py +173 -6
  39. {edsger-0.1.3 → edsger-0.1.5}/tests/test_path_tracking.py +0 -5
  40. {edsger-0.1.3 → edsger-0.1.5}/tests/test_pq_4ary_dec_0b.py +0 -5
  41. {edsger-0.1.3 → edsger-0.1.5}/tests/test_spiess_florian.py +0 -5
  42. edsger-0.1.3/docs/source/api.md +0 -116
  43. edsger-0.1.3/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
  44. edsger-0.1.3/scripts/benchmark_comparison.py +0 -442
  45. edsger-0.1.3/src/edsger/_version.py +0 -1
  46. edsger-0.1.3/src/edsger/utils.py +0 -63
  47. {edsger-0.1.3 → edsger-0.1.5}/.gitignore +0 -0
  48. {edsger-0.1.3 → edsger-0.1.5}/.pre-commit-config.yaml +0 -0
  49. {edsger-0.1.3 → edsger-0.1.5}/.readthedocs.yaml +0 -0
  50. {edsger-0.1.3 → edsger-0.1.5}/AUTHORS.rst +0 -0
  51. {edsger-0.1.3 → edsger-0.1.5}/CHANGELOG.rst +0 -0
  52. {edsger-0.1.3 → edsger-0.1.5}/CONTRIBUTING.rst +0 -0
  53. {edsger-0.1.3 → edsger-0.1.5}/LICENSE +0 -0
  54. {edsger-0.1.3 → edsger-0.1.5}/MANIFEST.in +0 -0
  55. {edsger-0.1.3 → edsger-0.1.5}/docs/Makefile +0 -0
  56. {edsger-0.1.3 → edsger-0.1.5}/docs/requirements.txt +0 -0
  57. {edsger-0.1.3 → edsger-0.1.5}/docs/source/conf.py +0 -0
  58. {edsger-0.1.3 → edsger-0.1.5}/docs/source/contributing.md +0 -0
  59. {edsger-0.1.3 → edsger-0.1.5}/docs/source/installation.md +0 -0
  60. {edsger-0.1.3 → edsger-0.1.5}/requirements-dev.txt +0 -0
  61. {edsger-0.1.3 → edsger-0.1.5}/requirements.txt +0 -0
  62. {edsger-0.1.3 → edsger-0.1.5}/setup.cfg +0 -0
  63. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/.gitignore +0 -0
  64. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/__init__.py +0 -0
  65. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/commons.pxd +0 -0
  66. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/commons.pyx +0 -0
  67. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/dijkstra.pyx +0 -0
  68. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/networks.py +0 -0
  69. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/path_tracking.pyx +0 -0
  70. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/pq_4ary_dec_0b.pxd +0 -0
  71. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/pq_4ary_dec_0b.pyx +0 -0
  72. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/prefetch_compat.h +0 -0
  73. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/spiess_florian.pyx +0 -0
  74. {edsger-0.1.3 → edsger-0.1.5}/src/edsger/star.pyx +0 -0
  75. {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/dependency_links.txt +0 -0
  76. {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/not-zip-safe +0 -0
  77. {edsger-0.1.3 → edsger-0.1.5}/src/edsger.egg-info/requires.txt +0 -0
  78. {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
- push:
5
- branches: [ release ]
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 (on release branch)
38
- if: github.ref == 'refs/heads/release' && github.event_name == 'push'
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
- push:
5
- tags:
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-2019, macos-13, macos-14]
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
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
  ![Tests Status](https://github.com/aetperf/edsger/actions/workflows/tests.yml/badge.svg?branch=release)
44
44
  [![codecov](https://codecov.io/gh/aetperf/edsger/branch/release/graph/badge.svg)](https://codecov.io/gh/aetperf/edsger)
45
+ [![Documentation Status](https://readthedocs.org/projects/edsger/badge/?version=latest)](https://edsger.readthedocs.io/en/latest/?badge=latest)
45
46
  [![PyPI version](https://img.shields.io/pypi/v/edsger.svg?refresh=1)](https://pypi.org/project/edsger/)
46
47
  [![Downloads](https://static.pepy.tech/badge/edsger)](https://pepy.tech/project/edsger)
47
48
  [![Python 3.9 | 3.10 | 3.11 | 3.12 | 3.13](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](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. So far, the library only includes Dijkstra's algorithm but we should add a range of 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
+ 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
  ![Tests Status](https://github.com/aetperf/edsger/actions/workflows/tests.yml/badge.svg?branch=release)
3
3
  [![codecov](https://codecov.io/gh/aetperf/edsger/branch/release/graph/badge.svg)](https://codecov.io/gh/aetperf/edsger)
4
+ [![Documentation Status](https://readthedocs.org/projects/edsger/badge/?version=latest)](https://edsger.readthedocs.io/en/latest/?badge=latest)
4
5
  [![PyPI version](https://img.shields.io/pypi/v/edsger.svg?refresh=1)](https://pypi.org/project/edsger/)
5
6
  [![Downloads](https://static.pepy.tech/badge/edsger)](https://pepy.tech/project/edsger)
6
7
  [![Python 3.9 | 3.10 | 3.11 | 3.12 | 3.13](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](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. So far, the library only includes Dijkstra's algorithm but we should add a range of 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
+ 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 |
@@ -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 currently focuses on shortest path algorithms, with Dijkstra's algorithm fully implemented and additional algorithms planned for future releases.
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
- # No conversion needed - use directly!
33
- dijkstra = Dijkstra(edges, orientation="out")
30
+ # Use Dijkstra for positive weights (faster)
31
+ dijkstra = Dijkstra(edges)
34
32
  distances = dijkstra.run(vertex_idx=0)
35
- distances
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 with aggressive optimizations
43
- - **Memory efficient** - Optimized for large-scale real-world datasets
44
- - **Easy integration** with NumPy and pandas workflows
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