edsger 0.1.2__tar.gz → 0.1.3__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 (64) hide show
  1. {edsger-0.1.2 → edsger-0.1.3}/.github/workflows/publish.yml +27 -44
  2. {edsger-0.1.2 → edsger-0.1.3}/.github/workflows/tests.yml +11 -10
  3. {edsger-0.1.2 → edsger-0.1.3}/PKG-INFO +12 -12
  4. {edsger-0.1.2 → edsger-0.1.3}/README.md +11 -11
  5. {edsger-0.1.2 → edsger-0.1.3}/docs/source/index.md +3 -1
  6. {edsger-0.1.2 → edsger-0.1.3}/docs/source/quickstart.md +119 -10
  7. edsger-0.1.3/src/edsger/_version.py +1 -0
  8. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/commons.c +146 -146
  9. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/dijkstra.c +7058 -2696
  10. edsger-0.1.3/src/edsger/dijkstra.pyx +1029 -0
  11. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/path.py +145 -37
  12. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/path_tracking.c +146 -146
  13. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/pq_4ary_dec_0b.c +146 -146
  14. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/spiess_florian.c +146 -146
  15. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/star.c +146 -146
  16. {edsger-0.1.2 → edsger-0.1.3}/src/edsger.egg-info/PKG-INFO +12 -12
  17. {edsger-0.1.2 → edsger-0.1.3}/tests/test_path.py +181 -0
  18. edsger-0.1.2/src/edsger/_version.py +0 -1
  19. edsger-0.1.2/src/edsger/dijkstra.pyx +0 -527
  20. {edsger-0.1.2 → edsger-0.1.3}/.github/workflows/docs.yml +0 -0
  21. {edsger-0.1.2 → edsger-0.1.3}/.gitignore +0 -0
  22. {edsger-0.1.2 → edsger-0.1.3}/.pre-commit-config.yaml +0 -0
  23. {edsger-0.1.2 → edsger-0.1.3}/.readthedocs.yaml +0 -0
  24. {edsger-0.1.2 → edsger-0.1.3}/AUTHORS.rst +0 -0
  25. {edsger-0.1.2 → edsger-0.1.3}/CHANGELOG.rst +0 -0
  26. {edsger-0.1.2 → edsger-0.1.3}/CONTRIBUTING.rst +0 -0
  27. {edsger-0.1.2 → edsger-0.1.3}/LICENSE +0 -0
  28. {edsger-0.1.2 → edsger-0.1.3}/MANIFEST.in +0 -0
  29. {edsger-0.1.2 → edsger-0.1.3}/docs/Makefile +0 -0
  30. {edsger-0.1.2 → edsger-0.1.3}/docs/requirements.txt +0 -0
  31. {edsger-0.1.2 → edsger-0.1.3}/docs/source/api.md +0 -0
  32. {edsger-0.1.2 → edsger-0.1.3}/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
  33. {edsger-0.1.2 → edsger-0.1.3}/docs/source/conf.py +0 -0
  34. {edsger-0.1.2 → edsger-0.1.3}/docs/source/contributing.md +0 -0
  35. {edsger-0.1.2 → edsger-0.1.3}/docs/source/installation.md +0 -0
  36. {edsger-0.1.2 → edsger-0.1.3}/pyproject.toml +0 -0
  37. {edsger-0.1.2 → edsger-0.1.3}/requirements-dev.txt +0 -0
  38. {edsger-0.1.2 → edsger-0.1.3}/requirements.txt +0 -0
  39. {edsger-0.1.2 → edsger-0.1.3}/scripts/benchmark_comparison.py +0 -0
  40. {edsger-0.1.2 → edsger-0.1.3}/scripts/dijkstra_dimacs.py +0 -0
  41. {edsger-0.1.2 → edsger-0.1.3}/scripts/requirements.txt +0 -0
  42. {edsger-0.1.2 → edsger-0.1.3}/setup.cfg +0 -0
  43. {edsger-0.1.2 → edsger-0.1.3}/setup.py +0 -0
  44. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/.gitignore +0 -0
  45. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/__init__.py +0 -0
  46. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/commons.pxd +0 -0
  47. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/commons.pyx +0 -0
  48. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/networks.py +0 -0
  49. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/path_tracking.pyx +0 -0
  50. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/pq_4ary_dec_0b.pxd +0 -0
  51. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/pq_4ary_dec_0b.pyx +0 -0
  52. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/prefetch_compat.h +0 -0
  53. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/spiess_florian.pyx +0 -0
  54. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/star.pyx +0 -0
  55. {edsger-0.1.2 → edsger-0.1.3}/src/edsger/utils.py +0 -0
  56. {edsger-0.1.2 → edsger-0.1.3}/src/edsger.egg-info/SOURCES.txt +0 -0
  57. {edsger-0.1.2 → edsger-0.1.3}/src/edsger.egg-info/dependency_links.txt +0 -0
  58. {edsger-0.1.2 → edsger-0.1.3}/src/edsger.egg-info/not-zip-safe +0 -0
  59. {edsger-0.1.2 → edsger-0.1.3}/src/edsger.egg-info/requires.txt +0 -0
  60. {edsger-0.1.2 → edsger-0.1.3}/src/edsger.egg-info/top_level.txt +0 -0
  61. {edsger-0.1.2 → edsger-0.1.3}/tests/test_dijkstra.py +0 -0
  62. {edsger-0.1.2 → edsger-0.1.3}/tests/test_path_tracking.py +0 -0
  63. {edsger-0.1.2 → edsger-0.1.3}/tests/test_pq_4ary_dec_0b.py +0 -0
  64. {edsger-0.1.2 → edsger-0.1.3}/tests/test_spiess_florian.py +0 -0
@@ -1,9 +1,10 @@
1
- name: ci-publish # Publish Python distribution to PyPI
1
+ name: ci-publish # Publish Python distribution to PyPI
2
2
 
3
3
  on:
4
4
  push:
5
5
  tags:
6
6
  - 'v[0-9]+.[0-9]+.[0-9]+'
7
+
7
8
  jobs:
8
9
 
9
10
  test:
@@ -13,45 +14,31 @@ jobs:
13
14
  python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
14
15
  runs-on: ${{ matrix.os }}
15
16
  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
17
+ - uses: actions/checkout@v4
18
+ - name: Set up Python ${{ matrix.python-version }}
19
+ uses: actions/setup-python@v4
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - name: Install dependencies
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install -r requirements-dev.txt
26
+ pip install .
27
+ - name: Testing
28
+ run: python -m pytest tests
29
29
 
30
30
  build_source_dist:
31
31
  name: Build source distribution
32
32
  runs-on: ubuntu-latest
33
33
  steps:
34
34
  - 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
35
  - uses: actions/setup-python@v4
46
36
  with:
47
37
  python-version: "3.11"
48
-
49
38
  - name: Install build
50
39
  run: python -m pip install build
51
-
52
40
  - name: Run build
53
41
  run: python -m build --sdist
54
-
55
42
  - uses: actions/upload-artifact@v4
56
43
  with:
57
44
  name: cibw-sdist
@@ -66,23 +53,20 @@ jobs:
66
53
 
67
54
  steps:
68
55
  - uses: actions/checkout@v4
69
-
70
- - uses: actions/setup-python@v3
71
-
56
+ - uses: actions/setup-python@v4
72
57
  - name: Install cibuildwheel
73
58
  run: python -m pip install cibuildwheel==2.16.5
74
-
75
59
  - name: Build wheels
60
+ env:
61
+ CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*"
76
62
  run: python -m cibuildwheel --output-dir wheelhouse
77
-
78
63
  - uses: actions/upload-artifact@v4
79
64
  with:
80
65
  name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
81
66
  path: ./wheelhouse/*.whl
82
67
 
83
68
  publish-to-pypi:
84
- name: >-
85
- Publish Python distribution to PyPI
69
+ name: Publish Python distribution to PyPI
86
70
  needs:
87
71
  - test
88
72
  - build_source_dist
@@ -90,14 +74,13 @@ jobs:
90
74
  runs-on: ubuntu-latest
91
75
  environment: release
92
76
  permissions:
93
- id-token: write # IMPORTANT: mandatory for trusted publishing
94
-
77
+ id-token: write # mandatory for trusted publishing
95
78
  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
79
+ - name: Download all the dists
80
+ uses: actions/download-artifact@v4
81
+ with:
82
+ pattern: cibw-*
83
+ path: ./dist/
84
+ merge-multiple: true
85
+ - name: Publish distribution to PyPI
86
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -1,4 +1,4 @@
1
- name: ci-test
1
+ name: Run tests and upload coverage
2
2
 
3
3
  on:
4
4
  push:
@@ -7,13 +7,17 @@ on:
7
7
 
8
8
  jobs:
9
9
  test:
10
+ name: Run tests and collect coverage
10
11
  strategy:
11
12
  matrix:
12
13
  os: [ubuntu-22.04, windows-2022, macos-14]
13
14
  python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
14
15
  runs-on: ${{ matrix.os }}
15
16
  steps:
16
- - uses: actions/checkout@v4
17
+ - name: Checkout
18
+ uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 2
17
21
  - name: Set up Python ${{ matrix.python-version }}
18
22
  uses: actions/setup-python@v5
19
23
  with:
@@ -29,13 +33,10 @@ jobs:
29
33
  - name: Lint Cython code
30
34
  run: |
31
35
  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
32
- - name: Testing
36
+ - name: Run tests
33
37
  run: |
34
- python -m pytest --cov=src/edsger --cov-report=xml tests/
35
- - name: Upload coverage to Codecov
36
- uses: codecov/codecov-action@v4
38
+ pytest --cov=src/edsger --cov-branch --cov-report=xml tests/
39
+ - name: Upload results to Codecov
40
+ uses: codecov/codecov-action@v5
37
41
  with:
38
- file: ./coverage.xml
39
- flags: unittests
40
- name: codecov-umbrella
41
- fail_ci_if_error: false
42
+ 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.3
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,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
- [![PyPI version](https://img.shields.io/pypi/v/edsger.svg)](https://pypi.org/project/edsger/)
45
+ [![PyPI version](https://img.shields.io/pypi/v/edsger.svg?refresh=1)](https://pypi.org/project/edsger/)
46
46
  [![Downloads](https://static.pepy.tech/badge/edsger)](https://pepy.tech/project/edsger)
47
47
  [![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
48
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -75,14 +75,14 @@ edges = pd.DataFrame({
75
75
  edges
76
76
  ```
77
77
 
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 |
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 |
86
86
 
87
87
  ```python
88
88
  # Initialize the Dijkstra object
@@ -101,9 +101,9 @@ We get the shortest paths from the source node 0 to all other nodes in the graph
101
101
 
102
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:
103
103
 
104
- <img src="docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
104
+ <img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
105
105
 
106
- *Benchmark performed on Intel i9-12900H laptop.*
106
+ *Benchmark performed on Intel i9-12900H Linux laptop.*
107
107
 
108
108
  ## Contributing
109
109
 
@@ -1,7 +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
- [![PyPI version](https://img.shields.io/pypi/v/edsger.svg)](https://pypi.org/project/edsger/)
4
+ [![PyPI version](https://img.shields.io/pypi/v/edsger.svg?refresh=1)](https://pypi.org/project/edsger/)
5
5
  [![Downloads](https://static.pepy.tech/badge/edsger)](https://pepy.tech/project/edsger)
6
6
  [![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
7
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -34,14 +34,14 @@ edges = pd.DataFrame({
34
34
  edges
35
35
  ```
36
36
 
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 |
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 |
45
45
 
46
46
  ```python
47
47
  # Initialize the Dijkstra object
@@ -60,9 +60,9 @@ We get the shortest paths from the source node 0 to all other nodes in the graph
60
60
 
61
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:
62
62
 
63
- <img src="docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
63
+ <img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
64
64
 
65
- *Benchmark performed on Intel i9-12900H laptop.*
65
+ *Benchmark performed on Intel i9-12900H Linux laptop.*
66
66
 
67
67
  ## Contributing
68
68
 
@@ -14,7 +14,7 @@ Edsger is designed to be **dataframe-friendly**, providing seamless integration
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.*
17
+ *Benchmark performed on Intel i9-12900H Linux laptop.*
18
18
 
19
19
  ### Pandas Integration Made Simple
20
20
 
@@ -32,7 +32,9 @@ edges = pd.DataFrame({
32
32
  # No conversion needed - use directly!
33
33
  dijkstra = Dijkstra(edges, orientation="out")
34
34
  distances = dijkstra.run(vertex_idx=0)
35
+ distances
35
36
  ```
37
+ array([0., 1., 2., 3.])
36
38
 
37
39
  ## Key Features
38
40
 
@@ -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 @@
1
+ __version__ = "0.1.3"