edsger 0.1.2__tar.gz → 0.1.4__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 (72) hide show
  1. {edsger-0.1.2 → edsger-0.1.4}/.github/workflows/docs.yml +4 -6
  2. {edsger-0.1.2 → edsger-0.1.4}/.github/workflows/publish.yml +30 -48
  3. edsger-0.1.4/.github/workflows/tests.yml +52 -0
  4. {edsger-0.1.2 → edsger-0.1.4}/PKG-INFO +21 -14
  5. {edsger-0.1.2 → edsger-0.1.4}/README.md +20 -13
  6. edsger-0.1.4/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
  7. {edsger-0.1.2 → edsger-0.1.4}/docs/source/index.md +3 -3
  8. {edsger-0.1.2 → edsger-0.1.4}/docs/source/quickstart.md +119 -10
  9. edsger-0.1.4/scripts/README_benchmark_os.md +72 -0
  10. edsger-0.1.4/scripts/benchmark_comparison_os.py +409 -0
  11. edsger-0.1.4/scripts/benchmark_dimacs_USA_linux.json +84 -0
  12. edsger-0.1.4/scripts/benchmark_dimacs_USA_windows.json +69 -0
  13. {edsger-0.1.2 → edsger-0.1.4}/scripts/dijkstra_dimacs.py +26 -2
  14. edsger-0.1.4/scripts/plot_benchmark_comparison.py +414 -0
  15. edsger-0.1.2/scripts/requirements.txt → edsger-0.1.4/scripts/requirements_linux.txt +0 -1
  16. edsger-0.1.4/scripts/requirements_windows.txt +9 -0
  17. {edsger-0.1.2 → edsger-0.1.4}/setup.py +23 -8
  18. edsger-0.1.4/src/edsger/_version.py +1 -0
  19. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/commons.c +146 -146
  20. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/dijkstra.c +7058 -2696
  21. edsger-0.1.4/src/edsger/dijkstra.pyx +1029 -0
  22. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/path.py +145 -37
  23. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/path_tracking.c +146 -146
  24. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.c +146 -146
  25. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/spiess_florian.c +146 -146
  26. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/star.c +146 -146
  27. {edsger-0.1.2 → edsger-0.1.4}/src/edsger.egg-info/PKG-INFO +21 -14
  28. {edsger-0.1.2 → edsger-0.1.4}/src/edsger.egg-info/SOURCES.txt +7 -2
  29. {edsger-0.1.2 → edsger-0.1.4}/tests/test_path.py +181 -0
  30. edsger-0.1.2/.github/workflows/tests.yml +0 -41
  31. edsger-0.1.2/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
  32. edsger-0.1.2/scripts/benchmark_comparison.py +0 -442
  33. edsger-0.1.2/src/edsger/_version.py +0 -1
  34. edsger-0.1.2/src/edsger/dijkstra.pyx +0 -527
  35. {edsger-0.1.2 → edsger-0.1.4}/.gitignore +0 -0
  36. {edsger-0.1.2 → edsger-0.1.4}/.pre-commit-config.yaml +0 -0
  37. {edsger-0.1.2 → edsger-0.1.4}/.readthedocs.yaml +0 -0
  38. {edsger-0.1.2 → edsger-0.1.4}/AUTHORS.rst +0 -0
  39. {edsger-0.1.2 → edsger-0.1.4}/CHANGELOG.rst +0 -0
  40. {edsger-0.1.2 → edsger-0.1.4}/CONTRIBUTING.rst +0 -0
  41. {edsger-0.1.2 → edsger-0.1.4}/LICENSE +0 -0
  42. {edsger-0.1.2 → edsger-0.1.4}/MANIFEST.in +0 -0
  43. {edsger-0.1.2 → edsger-0.1.4}/docs/Makefile +0 -0
  44. {edsger-0.1.2 → edsger-0.1.4}/docs/requirements.txt +0 -0
  45. {edsger-0.1.2 → edsger-0.1.4}/docs/source/api.md +0 -0
  46. {edsger-0.1.2 → edsger-0.1.4}/docs/source/conf.py +0 -0
  47. {edsger-0.1.2 → edsger-0.1.4}/docs/source/contributing.md +0 -0
  48. {edsger-0.1.2 → edsger-0.1.4}/docs/source/installation.md +0 -0
  49. {edsger-0.1.2 → edsger-0.1.4}/pyproject.toml +0 -0
  50. {edsger-0.1.2 → edsger-0.1.4}/requirements-dev.txt +0 -0
  51. {edsger-0.1.2 → edsger-0.1.4}/requirements.txt +0 -0
  52. {edsger-0.1.2 → edsger-0.1.4}/setup.cfg +0 -0
  53. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/.gitignore +0 -0
  54. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/__init__.py +0 -0
  55. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/commons.pxd +0 -0
  56. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/commons.pyx +0 -0
  57. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/networks.py +0 -0
  58. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/path_tracking.pyx +0 -0
  59. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.pxd +0 -0
  60. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.pyx +0 -0
  61. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/prefetch_compat.h +0 -0
  62. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/spiess_florian.pyx +0 -0
  63. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/star.pyx +0 -0
  64. {edsger-0.1.2 → edsger-0.1.4}/src/edsger/utils.py +0 -0
  65. {edsger-0.1.2 → edsger-0.1.4}/src/edsger.egg-info/dependency_links.txt +0 -0
  66. {edsger-0.1.2 → edsger-0.1.4}/src/edsger.egg-info/not-zip-safe +0 -0
  67. {edsger-0.1.2 → edsger-0.1.4}/src/edsger.egg-info/requires.txt +0 -0
  68. {edsger-0.1.2 → edsger-0.1.4}/src/edsger.egg-info/top_level.txt +0 -0
  69. {edsger-0.1.2 → edsger-0.1.4}/tests/test_dijkstra.py +0 -0
  70. {edsger-0.1.2 → edsger-0.1.4}/tests/test_path_tracking.py +0 -0
  71. {edsger-0.1.2 → edsger-0.1.4}/tests/test_pq_4ary_dec_0b.py +0 -0
  72. {edsger-0.1.2 → edsger-0.1.4}/tests/test_spiess_florian.py +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:
@@ -34,8 +32,8 @@ jobs:
34
32
  name: documentation-html
35
33
  path: docs/build/html/
36
34
 
37
- - name: Deploy to GitHub Pages (on release branch)
38
- if: github.ref == 'refs/heads/release' && github.event_name == 'push'
35
+ - name: Deploy to GitHub Pages
36
+ if: github.event_name == 'release'
39
37
  uses: peaceiris/actions-gh-pages@v4
40
38
  with:
41
39
  github_token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,9 +1,9 @@
1
- name: ci-publish # Publish Python distribution to PyPI
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]
6
+
7
7
  jobs:
8
8
 
9
9
  test:
@@ -13,45 +13,31 @@ jobs:
13
13
  python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
14
14
  runs-on: ${{ matrix.os }}
15
15
  steps:
16
- - uses: actions/checkout@v4
17
- - name: Set up Python ${{ matrix.python-version }}
18
- uses: actions/setup-python@v4
19
- with:
20
- python-version: ${{ matrix.python-version }}
21
- - name: Install dependencies
22
- run: |
23
- python -m pip install --upgrade pip
24
- pip install -r requirements-dev.txt
25
- pip install .
26
- - name: Testing
27
- run: |
28
- python -m pytest tests
16
+ - uses: actions/checkout@v4
17
+ - name: Set up Python ${{ matrix.python-version }}
18
+ uses: actions/setup-python@v4
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - name: Install dependencies
22
+ run: |
23
+ python -m pip install --upgrade pip
24
+ pip install -r requirements-dev.txt
25
+ pip install .
26
+ - name: Testing
27
+ run: python -m pytest tests
29
28
 
30
29
  build_source_dist:
31
30
  name: Build source distribution
32
31
  runs-on: ubuntu-latest
33
32
  steps:
34
33
  - uses: actions/checkout@v4
35
- # - if: github.event.ref_type != 'tag'
36
- # run: |
37
- # git fetch --prune --unshallow
38
- # git tag -d $(git tag --points-at HEAD)
39
- - if: github.event.ref_type == 'tag'
40
- uses: actions/checkout@v3
41
- - if: github.event_name == 'workflow_dispatch'
42
- uses: actions/checkout@v3
43
- with:
44
- fetch-depth: 0
45
34
  - uses: actions/setup-python@v4
46
35
  with:
47
36
  python-version: "3.11"
48
-
49
37
  - name: Install build
50
38
  run: python -m pip install build
51
-
52
39
  - name: Run build
53
40
  run: python -m build --sdist
54
-
55
41
  - uses: actions/upload-artifact@v4
56
42
  with:
57
43
  name: cibw-sdist
@@ -62,27 +48,24 @@ jobs:
62
48
  runs-on: ${{ matrix.os }}
63
49
  strategy:
64
50
  matrix:
65
- os: [ubuntu-latest, windows-2019, macos-13, macos-14]
51
+ os: [ubuntu-latest, windows-2022, macos-13, macos-14]
66
52
 
67
53
  steps:
68
54
  - uses: actions/checkout@v4
69
-
70
- - uses: actions/setup-python@v3
71
-
55
+ - uses: actions/setup-python@v4
72
56
  - name: Install cibuildwheel
73
57
  run: python -m pip install cibuildwheel==2.16.5
74
-
75
58
  - name: Build wheels
59
+ env:
60
+ CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*"
76
61
  run: python -m cibuildwheel --output-dir wheelhouse
77
-
78
62
  - uses: actions/upload-artifact@v4
79
63
  with:
80
64
  name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
81
65
  path: ./wheelhouse/*.whl
82
66
 
83
67
  publish-to-pypi:
84
- name: >-
85
- Publish Python distribution to PyPI
68
+ name: Publish Python distribution to PyPI
86
69
  needs:
87
70
  - test
88
71
  - build_source_dist
@@ -90,14 +73,13 @@ jobs:
90
73
  runs-on: ubuntu-latest
91
74
  environment: release
92
75
  permissions:
93
- id-token: write # IMPORTANT: mandatory for trusted publishing
94
-
76
+ id-token: write # mandatory for trusted publishing
95
77
  steps:
96
- - name: Download all the dists
97
- uses: actions/download-artifact@v4
98
- with:
99
- pattern: cibw-*
100
- path: ./dist/
101
- merge-multiple: true
102
- - name: Publish distribution to PyPI
103
- uses: pypa/gh-action-pypi-publish@release/v1
78
+ - name: Download all the dists
79
+ uses: actions/download-artifact@v4
80
+ with:
81
+ pattern: cibw-*
82
+ path: ./dist/
83
+ merge-multiple: true
84
+ - name: Publish distribution to PyPI
85
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,52 @@
1
+ name: Run tests and upload coverage
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ jobs:
8
+ test:
9
+ name: Run tests and collect coverage
10
+ strategy:
11
+ matrix:
12
+ os: [ubuntu-22.04, windows-2022, macos-14]
13
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
14
+ runs-on: ${{ matrix.os }}
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 2
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -r requirements-dev.txt
28
+ pip install .
29
+ - name: Reinstall package with coverage support
30
+ if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
31
+ run: |
32
+ pip uninstall -y edsger
33
+ CYTHON_TRACE=1 pip install -e .
34
+ - name: Format Python code with black
35
+ run: |
36
+ black --check --diff .
37
+ - name: Lint Cython code
38
+ run: |
39
+ 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
40
+ - name: Run tests with coverage
41
+ if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
42
+ run: |
43
+ pytest --cov=src/edsger --cov-branch --cov-report=xml tests/
44
+ - name: Run tests without coverage
45
+ if: matrix.os != 'ubuntu-22.04' || matrix.python-version != '3.11'
46
+ run: |
47
+ pytest tests/
48
+ - name: Upload results to Codecov
49
+ if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
50
+ uses: codecov/codecov-action@v5
51
+ with:
52
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edsger
3
- Version: 0.1.2
3
+ Version: 0.1.4
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,7 +42,8 @@ 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
- [![PyPI version](https://img.shields.io/pypi/v/edsger.svg)](https://pypi.org/project/edsger/)
45
+ [![Documentation Status](https://readthedocs.org/projects/edsger/badge/?version=latest)](https://edsger.readthedocs.io/en/latest/?badge=latest)
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/)
48
49
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -75,14 +76,14 @@ edges = pd.DataFrame({
75
76
  edges
76
77
  ```
77
78
 
78
- | | tail | head | weight |
79
- |---:|-------:|-------:|---------:|
80
- | 0 | 0 | 1 | 1.0 |
81
- | 1 | 0 | 2 | 4.0 |
82
- | 2 | 1 | 2 | 2.0 |
83
- | 3 | 2 | 3 | 1.5 |
84
- | 4 | 2 | 4 | 3.0 |
85
- | 5 | 3 | 4 | 1.0 |
79
+ | | tail | head | weight |
80
+ |---:|-------:|-------:|---------:|
81
+ | 0 | 0 | 1 | 1.0 |
82
+ | 1 | 0 | 2 | 4.0 |
83
+ | 2 | 1 | 2 | 2.0 |
84
+ | 3 | 2 | 3 | 1.5 |
85
+ | 4 | 2 | 4 | 3.0 |
86
+ | 5 | 3 | 4 | 1.0 |
86
87
 
87
88
  ```python
88
89
  # Initialize the Dijkstra object
@@ -97,13 +98,19 @@ 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
 
100
- ## Why Use Edsger?
101
+ ## Installation
102
+
103
+ ### Standard Installation
101
104
 
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:
105
+ ```bash
106
+ pip install edsger
107
+ ```
108
+
109
+ ## Why Use Edsger?
103
110
 
104
- <img src="docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
111
+ 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:
105
112
 
106
- *Benchmark performed on Intel i9-12900H laptop.*
113
+ <img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
107
114
 
108
115
  ## Contributing
109
116
 
@@ -1,7 +1,8 @@
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
- [![PyPI version](https://img.shields.io/pypi/v/edsger.svg)](https://pypi.org/project/edsger/)
4
+ [![Documentation Status](https://readthedocs.org/projects/edsger/badge/?version=latest)](https://edsger.readthedocs.io/en/latest/?badge=latest)
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/)
7
8
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -34,14 +35,14 @@ edges = pd.DataFrame({
34
35
  edges
35
36
  ```
36
37
 
37
- | | tail | head | weight |
38
- |---:|-------:|-------:|---------:|
39
- | 0 | 0 | 1 | 1.0 |
40
- | 1 | 0 | 2 | 4.0 |
41
- | 2 | 1 | 2 | 2.0 |
42
- | 3 | 2 | 3 | 1.5 |
43
- | 4 | 2 | 4 | 3.0 |
44
- | 5 | 3 | 4 | 1.0 |
38
+ | | tail | head | weight |
39
+ |---:|-------:|-------:|---------:|
40
+ | 0 | 0 | 1 | 1.0 |
41
+ | 1 | 0 | 2 | 4.0 |
42
+ | 2 | 1 | 2 | 2.0 |
43
+ | 3 | 2 | 3 | 1.5 |
44
+ | 4 | 2 | 4 | 3.0 |
45
+ | 5 | 3 | 4 | 1.0 |
45
46
 
46
47
  ```python
47
48
  # Initialize the Dijkstra object
@@ -56,13 +57,19 @@ 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
 
59
- ## Why Use Edsger?
60
+ ## Installation
61
+
62
+ ### Standard Installation
60
63
 
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:
64
+ ```bash
65
+ pip install edsger
66
+ ```
67
+
68
+ ## Why Use Edsger?
62
69
 
63
- <img src="docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
70
+ 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:
64
71
 
65
- *Benchmark performed on Intel i9-12900H laptop.*
72
+ <img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
66
73
 
67
74
  ## Contributing
68
75
 
@@ -10,12 +10,10 @@ Welcome to the Edsger documentation! Edsger is a Python library for efficient gr
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 laptop.*
18
-
19
17
  ### Pandas Integration Made Simple
20
18
 
21
19
  ```python
@@ -32,7 +30,9 @@ edges = pd.DataFrame({
32
30
  # No conversion needed - use directly!
33
31
  dijkstra = Dijkstra(edges, orientation="out")
34
32
  distances = dijkstra.run(vertex_idx=0)
33
+ distances
35
34
  ```
35
+ array([0., 1., 2., 3.])
36
36
 
37
37
  ## Key Features
38
38
 
@@ -22,7 +22,7 @@ edges = pd.DataFrame({
22
22
  'head': [1, 2, 2, 3],
23
23
  'weight': [1.0, 4.0, 2.0, 1.0]
24
24
  })
25
- edsges
25
+ edges
26
26
  ```
27
27
 
28
28
  | | tail | head | weight |
@@ -33,6 +33,8 @@ edsges
33
33
  | 3 | 2 | 3 | 1 |
34
34
 
35
35
 
36
+ Note that it is also possible to use a graph with different column names for the tail, head and weight values, but we need then to specify the name mapping, as described in the following.
37
+
36
38
  ## Dijkstra's Algorithm
37
39
 
38
40
  To use Dijkstra's algorithm, you can import the `Dijkstra` class from the `path` module. The function takes a graph and a source node as input, and returns the shortest path from the source node to all other nodes in the graph.
@@ -49,14 +51,14 @@ edges = pd.DataFrame({
49
51
  edges
50
52
  ```
51
53
 
52
- | | tail | head | weight |
53
- |---:|-------:|-------:|---------:|
54
- | 0 | 0 | 1 | 1.0 |
55
- | 1 | 0 | 2 | 4.0 |
56
- | 2 | 1 | 2 | 2.0 |
57
- | 3 | 2 | 3 | 1.5 |
58
- | 4 | 2 | 4 | 3.0 |
59
- | 5 | 3 | 4 | 1.0 |
54
+ | | tail | head | weight |
55
+ |---:|-------:|-------:|---------:|
56
+ | 0 | 0 | 1 | 1.0 |
57
+ | 1 | 0 | 2 | 4.0 |
58
+ | 2 | 1 | 2 | 2.0 |
59
+ | 3 | 2 | 3 | 1.5 |
60
+ | 4 | 2 | 4 | 3.0 |
61
+ | 5 | 3 | 4 | 1.0 |
60
62
 
61
63
 
62
64
  ```python
@@ -72,7 +74,7 @@ print("Shortest paths:", shortest_paths)
72
74
 
73
75
  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.
74
76
 
75
- It is also possible to use a graph with different column names for the tail, head and weight values. The column names can be specified using the `tail`, `head` and `weight` arguments:
77
+ The column names can be specified using the `tail`, `head` and `weight` arguments:
76
78
 
77
79
  ```python
78
80
  other_edges = pd.DataFrame({
@@ -169,11 +171,116 @@ shortest_paths[-5:]
169
171
 
170
172
  array([0. , 1. , 3. , 4.5, 5.5])
171
173
 
174
+ ### Early Termination
175
+
176
+ Early termination is a performance optimization feature that allows Dijkstra's algorithm to stop computing once specific target nodes (termination nodes) have been reached. This can significantly reduce computation time when you only need shortest paths to a subset of vertices in the graph.
177
+
178
+ When using early termination, the algorithm will:
179
+ 1. Stop as soon as all specified termination nodes have been visited
180
+ 2. Return **only** the path lengths to the termination nodes (not all vertices)
181
+ 3. Return results in the same order as the termination nodes were specified
182
+
183
+ #### Basic Early Termination Example
184
+
185
+ ```python
186
+ import pandas as pd
187
+ from edsger.path import Dijkstra
188
+
189
+ # Create a sample graph
190
+ edges = pd.DataFrame({
191
+ "tail": [0, 0, 0, 1, 1, 2, 2, 3, 3, 4],
192
+ "head": [1, 2, 3, 2, 4, 3, 5, 4, 5, 5],
193
+ "weight": [1.0, 4.0, 2.0, 1.0, 3.0, 1.0, 2.0, 1.0, 1.0, 1.0],
194
+ })
195
+ ```
196
+
197
+ **Without early termination** (computes paths to all vertices):
198
+ ```python
199
+ dijkstra = Dijkstra(edges, orientation="out")
200
+ distances = dijkstra.run(vertex_idx=0)
201
+ print("All distances:", distances)
202
+ ```
203
+ All distances: [0. 1. 2. 2. 3. 3.]
204
+
205
+ **With early termination** (computes paths only to specified nodes):
206
+ ```python
207
+ # Only compute paths to nodes 3 and 5
208
+ termination_nodes = [3, 5]
209
+ distances = dijkstra.run(vertex_idx=0, termination_nodes=termination_nodes)
210
+ print("Distances to termination nodes:", distances)
211
+ print("Shape of result:", distances.shape)
212
+ ```
213
+ Distances to termination nodes: [2. 3.]
214
+ Shape of result: (2,)
215
+
216
+ Notice that:
217
+ - The result array has length 2 (same as number of termination nodes)
218
+ - `distances[0] = 2.0` is the shortest path length from vertex 0 to vertex 3
219
+ - `distances[1] = 3.0` is the shortest path length from vertex 0 to vertex 5
220
+
221
+ #### Early Termination with Path Tracking
222
+
223
+ Early termination also works with path tracking enabled:
224
+
225
+ ```python
226
+ dijkstra = Dijkstra(edges, orientation="out")
227
+ distances = dijkstra.run(vertex_idx=0, termination_nodes=[3, 5], path_tracking=True)
228
+ print("Distances:", distances)
229
+
230
+ # Get paths to termination nodes
231
+ path_to_3 = dijkstra.get_path(vertex_idx=3)
232
+ path_to_5 = dijkstra.get_path(vertex_idx=5)
233
+ print("Path to vertex 3:", path_to_3)
234
+ print("Path to vertex 5:", path_to_5)
235
+ ```
236
+ Distances: [2. 3.]
237
+ Path to vertex 3: [3 2 1 0]
238
+ Path to vertex 5: [5 3 2 1 0]
239
+
240
+ #### Important Notes
241
+
242
+ 1. **Return Array Size**: With early termination, the returned array size equals the number of termination nodes, not the total number of vertices in the graph.
243
+
244
+ 2. **Order Preservation**: Results are returned in the same order as the termination nodes are specified:
245
+ ```python
246
+ # Termination nodes [3, 5] → results [distance_to_3, distance_to_5]
247
+ # Termination nodes [5, 3] → results [distance_to_5, distance_to_3]
248
+ ```
249
+
250
+ 3. **Orientation Support**: Early termination works with both orientations:
251
+ ```python
252
+ # Single-source shortest paths (from source to termination nodes)
253
+ dijkstra = Dijkstra(edges, orientation="out")
254
+ distances = dijkstra.run(vertex_idx=0, termination_nodes=[3, 5])
255
+
256
+ # Single-target shortest paths (from termination nodes to target)
257
+ dijkstra = Dijkstra(edges, orientation="in")
258
+ distances = dijkstra.run(vertex_idx=5, termination_nodes=[0, 2])
259
+ ```
260
+
261
+ 4. **Unreachable Nodes**: If a termination node is unreachable, its distance will be infinity:
262
+ ```python
263
+ # If node 10 is unreachable from node 0
264
+ distances = dijkstra.run(vertex_idx=0, termination_nodes=[3, 10])
265
+ # Result: [2.0, inf]
266
+ ```
172
267
 
173
268
  ### Run Method Options
174
269
 
175
270
  The `run` method can take the following arguments besides the source/target vertex index:
176
271
 
272
+ - `termination_nodes` : list or array-like, optional (default=None)
273
+
274
+ A list or array of vertex indices where the algorithm should stop early. When specified, the algorithm will terminate as soon as all termination nodes have been reached, and will return only the path lengths to these nodes in the same order they were specified. This can provide significant performance improvements when you only need paths to a subset of vertices.
275
+
276
+ ```python
277
+ dijkstra = Dijkstra(edges)
278
+ # Get distances only to nodes 2 and 4
279
+ distances = dijkstra.run(vertex_idx=0, termination_nodes=[2, 4])
280
+ print("Distances to nodes 2 and 4:", distances)
281
+ ```
282
+ Distances to nodes 2 and 4: [1.5 3.5]
283
+
177
284
  - `path_tracking` : bool, optional (default=False)
178
285
 
179
286
  Whether to track the shortest path(s) from/to the source/target vertex to all other vertices in the graph.
@@ -194,6 +301,8 @@ dijkstra.get_path(vertex_idx=0)
194
301
 
195
302
  The path is returned as an array of vertex indices. This is an ordered list of vertices from the source to the target vertex if `orientation` is `'in'`, and from the target to the source vertex if `orientation` is `'out'`. Both the source and target vertices are included in the path.
196
303
 
304
+ **Note**: When using `termination_nodes` with `path_tracking=True`, you can still retrieve paths to any vertex that was reached during the computation using `get_path()`, even if it wasn't in the termination nodes list.
305
+
197
306
  - `return_inf` : bool, optional (default=True)
198
307
 
199
308
  Whether to return path lengths as infinity (np.inf) when no path exists.
@@ -0,0 +1,72 @@
1
+ # OS-Specific Benchmarking Scripts
2
+
3
+ These scripts allow you to run benchmarks on different operating systems and combine the results into comparison plots.
4
+
5
+ ## Scripts
6
+
7
+ 1. **benchmark_comparison_os.py** - Runs benchmarks and creates OS-specific JSON files
8
+ 2. **plot_benchmark_comparison.py** - Creates comparison plots from the JSON files
9
+
10
+ ## Usage
11
+
12
+ ### Step 1: Run benchmarks on each OS
13
+
14
+ On Linux:
15
+ ```bash
16
+ cd scripts/
17
+ python benchmark_comparison_os.py [-d DATA_DIR] [-n NETWORK] [-r REPEAT]
18
+ ```
19
+ This creates: `benchmark_dimacs_USA_linux.json` (or with different network name)
20
+
21
+ On Windows:
22
+ ```bash
23
+ cd scripts/
24
+ python benchmark_comparison_os.py [-d DATA_DIR] [-n NETWORK] [-r REPEAT]
25
+ ```
26
+ This creates: `benchmark_dimacs_USA_windows.json` (or with different network name)
27
+
28
+ On macOS:
29
+ ```bash
30
+ cd scripts/
31
+ python benchmark_comparison_os.py [-d DATA_DIR] [-n NETWORK] [-r REPEAT]
32
+ ```
33
+ This creates: `benchmark_dimacs_USA_darwin.json` (or with different network name)
34
+
35
+ #### Arguments:
36
+ - `-d, --dir`: Data folder with network sub-folders (default: from DIMACS_DATA_DIR env var or `/home/francois/Data/DIMACS_road_networks/`)
37
+ - `-n, --network`: Network name - 'NY', 'BAY', 'COL', 'FLA', 'NW', 'NE', 'CAL', 'LKS', 'E', 'W', 'CTR', 'USA' (default: USA)
38
+ - `-r, --repeat`: Number of benchmark iterations per library (default: 5)
39
+
40
+ ### Step 2: Create comparison plots
41
+
42
+ After collecting results from one or more operating systems:
43
+ ```bash
44
+ python plot_benchmark_comparison.py
45
+ ```
46
+
47
+ This will create:
48
+ - Combined comparison plot: `dijkstra_benchmark_comparison.png`
49
+
50
+ ## Features
51
+
52
+ - **Graceful handling of missing packages**: Graph-tool is automatically skipped on Windows
53
+ - **OS detection**: Automatically names output files based on the operating system
54
+ - **Multi-OS comparison**: Can compare results across Linux, Windows, and macOS
55
+ - **Version tracking**: Records package versions for each benchmark run
56
+
57
+ ## Output Files
58
+
59
+ ### JSON files
60
+ - `benchmark_dimacs_{NETWORK}_{OS}.json` - Benchmark results for specific network and OS
61
+ - Examples: `benchmark_dimacs_USA_linux.json`, `benchmark_dimacs_COL_windows.json`
62
+
63
+ ### Plot files
64
+ - `dijkstra_benchmark_comparison.png` - Comparison plot (shows all available OS results)
65
+
66
+ ## Notes
67
+
68
+ - **Graph-tool**: Not available on Windows and will be automatically skipped
69
+ - **SciPy benchmarking**: Runs `dijkstra_dimacs.py` with Edsger + comparison (`-c` flag) multiple times to collect SciPy timing statistics, since SciPy only runs once per call in comparison mode
70
+ - **Package detection**: Scripts detect available packages before running benchmarks
71
+ - **Metadata**: Results include system information, Python version, and package versions
72
+ - **Statistical reliability**: Benchmarks run 5 iterations by default for each library