edsger 0.0.13__tar.gz → 0.0.15__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.0.13 → edsger-0.0.15}/.github/workflows/publish.yml +2 -4
- edsger-0.0.15/.github/workflows/tests.yml +29 -0
- edsger-0.0.15/PKG-INFO +277 -0
- edsger-0.0.15/README.md +243 -0
- {edsger-0.0.13 → edsger-0.0.15}/docs/source/conf.py +2 -3
- {edsger-0.0.13 → edsger-0.0.15}/docs/source/edsger.rst +3 -3
- edsger-0.0.15/docs/source/index.rst +22 -0
- edsger-0.0.15/docs/source/installation.rst +90 -0
- edsger-0.0.15/docs/source/introduction.rst +7 -0
- {edsger-0.0.13 → edsger-0.0.15}/requirements-dev.txt +4 -1
- {edsger-0.0.13 → edsger-0.0.15}/scripts/dijkstra_dimacs.py +5 -7
- edsger-0.0.15/src/edsger/_version.py +1 -0
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/commons.c +1607 -983
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/commons.pxd +7 -6
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/commons.pyx +6 -5
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/dijkstra.c +2692 -2062
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/dijkstra.pyx +7 -7
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/networks.py +134 -2
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/path.py +246 -100
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/path_tracking.c +1803 -1167
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/path_tracking.pyx +18 -9
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/pq_4ary_dec_0b.c +2536 -1906
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/pq_4ary_dec_0b.pxd +6 -5
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/pq_4ary_dec_0b.pyx +6 -6
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/spiess_florian.c +2854 -2223
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/spiess_florian.pyx +7 -7
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/star.c +1991 -1361
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/star.pyx +7 -1
- edsger-0.0.15/src/edsger/utils.py +63 -0
- edsger-0.0.15/src/edsger.egg-info/PKG-INFO +277 -0
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger.egg-info/SOURCES.txt +3 -0
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger.egg-info/requires.txt +0 -5
- {edsger-0.0.13 → edsger-0.0.15}/tests/test_dijkstra.py +2 -1
- {edsger-0.0.13 → edsger-0.0.15}/tests/test_path.py +6 -8
- {edsger-0.0.13 → edsger-0.0.15}/tests/test_path_tracking.py +0 -1
- edsger-0.0.13/PKG-INFO +0 -48
- edsger-0.0.13/README.md +0 -10
- edsger-0.0.13/docs/source/index.rst +0 -27
- edsger-0.0.13/src/edsger/_version.py +0 -1
- edsger-0.0.13/src/edsger/utils.py +0 -17
- edsger-0.0.13/src/edsger.egg-info/PKG-INFO +0 -48
- {edsger-0.0.13 → edsger-0.0.15}/.gitignore +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/AUTHORS.rst +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/CHANGELOG.rst +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/CONTRIBUTING.rst +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/LICENSE +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/MANIFEST.in +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/docs/Makefile +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/pyproject.toml +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/requirements.txt +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/setup.cfg +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/setup.py +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger/__init__.py +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger.egg-info/dependency_links.txt +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger.egg-info/not-zip-safe +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/src/edsger.egg-info/top_level.txt +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/tests/test_pq_4ary_dec_0b.py +0 -0
- {edsger-0.0.13 → edsger-0.0.15}/tests/test_spiess_florian.py +0 -0
@@ -2,8 +2,6 @@ name: ci-publish # Publish Python distribution to PyPI
|
|
2
2
|
|
3
3
|
on:
|
4
4
|
push:
|
5
|
-
branches:
|
6
|
-
- push
|
7
5
|
tags:
|
8
6
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
9
7
|
jobs:
|
@@ -54,7 +52,7 @@ jobs:
|
|
54
52
|
- name: Run build
|
55
53
|
run: python -m build --sdist
|
56
54
|
|
57
|
-
- uses: actions/upload-artifact@
|
55
|
+
- uses: actions/upload-artifact@v4
|
58
56
|
with:
|
59
57
|
path: ./dist/*.tar.gz
|
60
58
|
|
@@ -95,7 +93,7 @@ jobs:
|
|
95
93
|
|
96
94
|
steps:
|
97
95
|
- name: Download all the dists
|
98
|
-
uses: actions/download-artifact@
|
96
|
+
uses: actions/download-artifact@v4
|
99
97
|
with:
|
100
98
|
name: artifact
|
101
99
|
path: ./dist/
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: ci-test
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- release
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
test:
|
10
|
+
strategy:
|
11
|
+
matrix:
|
12
|
+
os: [ os: [ubuntu-22.04, windows-2022, macos-14]
|
13
|
+
]
|
14
|
+
python-version: ["3.11"]
|
15
|
+
runs-on: ${{ matrix.os }}
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@v3
|
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: |
|
29
|
+
python -m pytest tests
|
edsger-0.0.15/PKG-INFO
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: edsger
|
3
|
+
Version: 0.0.15
|
4
|
+
Summary: Graph algorithms in Cython.
|
5
|
+
Author-email: François Pacull <francois.pacull@architecture-performance.fr>
|
6
|
+
Maintainer-email: François Pacull <francois.pacull@architecture-performance.fr>
|
7
|
+
License: MIT License
|
8
|
+
Project-URL: Repository, https://github.com/aetperf/Edsger
|
9
|
+
Keywords: python,graph,shortest path,Dijkstra
|
10
|
+
Platform: any
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
14
|
+
Classifier: Operating System :: OS Independent
|
15
|
+
Classifier: Topic :: Scientific/Engineering
|
16
|
+
Requires-Python: >=3.11
|
17
|
+
Description-Content-Type: text/markdown
|
18
|
+
License-File: LICENSE
|
19
|
+
License-File: AUTHORS.rst
|
20
|
+
Requires-Dist: setuptools
|
21
|
+
Requires-Dist: setuptools_scm
|
22
|
+
Requires-Dist: numpy>=1.26
|
23
|
+
Requires-Dist: Cython>=3
|
24
|
+
Requires-Dist: pandas
|
25
|
+
Provides-Extra: dev
|
26
|
+
Requires-Dist: black; extra == "dev"
|
27
|
+
Provides-Extra: test
|
28
|
+
Requires-Dist: pytest; extra == "test"
|
29
|
+
Requires-Dist: scipy<1.11; extra == "test"
|
30
|
+
Provides-Extra: doc
|
31
|
+
Requires-Dist: sphinx; extra == "doc"
|
32
|
+
Requires-Dist: sphinx_design; extra == "doc"
|
33
|
+
Requires-Dist: sphinx_rtd_theme; extra == "doc"
|
34
|
+
|
35
|
+
|
36
|
+

|
37
|
+

|
38
|
+
[](https://opensource.org/licenses/MIT)
|
39
|
+
|
40
|
+
# Edsger
|
41
|
+
|
42
|
+
*Graph algorithms in Cython*
|
43
|
+
|
44
|
+
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.
|
45
|
+
|
46
|
+
## Installation
|
47
|
+
|
48
|
+
### Install from PyPI
|
49
|
+
|
50
|
+
To install Edsger, simply use pip:
|
51
|
+
|
52
|
+
```sh
|
53
|
+
pip install edsger
|
54
|
+
```
|
55
|
+
|
56
|
+
### Install from source
|
57
|
+
|
58
|
+
If you want to install from source, you can clone the repository and install it using pip:
|
59
|
+
|
60
|
+
```sh
|
61
|
+
git clone git@github.com:aetperf/Edsger.git
|
62
|
+
cd Edsger
|
63
|
+
pip install .
|
64
|
+
```
|
65
|
+
|
66
|
+
## Usage
|
67
|
+
|
68
|
+
### Dijkstra's algorithm
|
69
|
+
|
70
|
+
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.
|
71
|
+
|
72
|
+
```python
|
73
|
+
import pandas as pd
|
74
|
+
|
75
|
+
from edsger.path import Dijkstra
|
76
|
+
|
77
|
+
# Create a DataFrame with the edges of the graph
|
78
|
+
edges = pd.DataFrame({
|
79
|
+
'tail': [0, 0, 1, 2, 2, 3],
|
80
|
+
'head': [1, 2, 2, 3, 4, 4],
|
81
|
+
'weight': [1, 4, 2, 1, 3, 1]
|
82
|
+
})
|
83
|
+
edges
|
84
|
+
```
|
85
|
+
|
86
|
+
| | tail | head | weight |
|
87
|
+
|---:|-------:|-------:|---------:|
|
88
|
+
| 0 | 0 | 1 | 1 |
|
89
|
+
| 1 | 0 | 2 | 4 |
|
90
|
+
| 2 | 1 | 2 | 2 |
|
91
|
+
| 3 | 2 | 3 | 1 |
|
92
|
+
| 4 | 2 | 4 | 3 |
|
93
|
+
| 5 | 3 | 4 | 1 |
|
94
|
+
|
95
|
+
|
96
|
+
```python
|
97
|
+
# Initialize the Dijkstra object
|
98
|
+
dijkstra = Dijkstra(edges)
|
99
|
+
|
100
|
+
# Run the algorithm from a source vertex
|
101
|
+
shortest_paths = dijkstra.run(vertex_idx=0)
|
102
|
+
print("Shortest paths:", shortest_paths)
|
103
|
+
```
|
104
|
+
|
105
|
+
Shortest paths: [0. 1. 3. 4. 5.]
|
106
|
+
|
107
|
+
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.
|
108
|
+
|
109
|
+
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:
|
110
|
+
|
111
|
+
```python
|
112
|
+
other_edges = pd.DataFrame({
|
113
|
+
'from': [0, 0, 1, 2, 2, 3],
|
114
|
+
'to': [1, 2, 2, 3, 4, 4],
|
115
|
+
'travel_time': [1, 4, 2, 1, 3, 1]
|
116
|
+
})
|
117
|
+
other_dijkstra = Dijkstra(edges, tail='from', head='to', weight='travel_time')
|
118
|
+
```
|
119
|
+
|
120
|
+
#### Orientation
|
121
|
+
|
122
|
+
The `orientation` argument (a string with a default value of `'out'`) specifies the orientation of the algorithm. It can be either `'out'` for single source shortest paths or `'in'` for single target shortest path.
|
123
|
+
|
124
|
+
```python
|
125
|
+
dijkstra = Dijkstra(edges, orientation='in')
|
126
|
+
|
127
|
+
# Run the algorithm to a target vertex
|
128
|
+
shortest_paths = dijkstra.run(vertex_idx=0)
|
129
|
+
print("Shortest paths:", shortest_paths)
|
130
|
+
```
|
131
|
+
|
132
|
+
Shortest paths: [ 0. inf inf inf inf]
|
133
|
+
|
134
|
+
#### Run multiple times
|
135
|
+
|
136
|
+
Once the Dijkstra is instanciated with a given graph and orientation, the `run` method can be called multiple times with different source vertices.
|
137
|
+
|
138
|
+
```python
|
139
|
+
# Run the algorithm to another target vertex
|
140
|
+
shortest_paths = dijkstra.run(vertex_idx=4)
|
141
|
+
print("Shortest paths:", shortest_paths)
|
142
|
+
```
|
143
|
+
|
144
|
+
Shortest paths: [5. 4. 2. 1. 0.]
|
145
|
+
|
146
|
+
#### Check edges
|
147
|
+
|
148
|
+
The `check_edges` argument (a boolean with a default value of `False`) validates the given graph. When set to `True`, it ensures the DataFrame is well-formed by:
|
149
|
+
|
150
|
+
- Checking for the presence of required columns for edge tail, head and weigh values
|
151
|
+
- Verifying that the data types are correct (integer for tail and head, integer or float for weight)
|
152
|
+
- Ensuring there are no missing or invalid values (e.g. negative weights)
|
153
|
+
|
154
|
+
If any of these checks fail, an appropriate error is raised.
|
155
|
+
|
156
|
+
```python
|
157
|
+
invalid_edges = pd.DataFrame({
|
158
|
+
'tail': [0, 0, 1, 2, 2, 3],
|
159
|
+
'head': [1, 2, 2, 3, 4, 4],
|
160
|
+
'weight': [1, 4, 2, -1, 3, 1]
|
161
|
+
})
|
162
|
+
dijkstra = Dijkstra(invalid_edges, check_edges=True)
|
163
|
+
```
|
164
|
+
|
165
|
+
ValueError: edges['weight'] should be nonnegative
|
166
|
+
|
167
|
+
#### Permute
|
168
|
+
|
169
|
+
Finally, the `permute` argument (boolean with a default value of `False`) allows to permute the IDs of the nodes. If set to `True`, the node IDs will be reindexed to start from 0 and be contiguous for the inner computations, and the output will be reindexed to the original IDs, loading the same result as if the IDs were not permuted. The permutation may save memory and computation time for large graphs, for example if a significant ratio of the nodes are not actually used in the graph.
|
170
|
+
|
171
|
+
```python
|
172
|
+
SHIFT = 1000
|
173
|
+
|
174
|
+
shifted_edges = pd.DataFrame({
|
175
|
+
'tail': [0, 0, 1, 2, 2, 3],
|
176
|
+
'head': [1, 2, 2, 3, 4, 4],
|
177
|
+
'weight': [1, 4, 2, 1, 3, 1]
|
178
|
+
})
|
179
|
+
shifted_edges["tail"] += SHIFT
|
180
|
+
shifted_edges["head"] += SHIFT
|
181
|
+
shifted_edges.head(3)
|
182
|
+
```
|
183
|
+
|
184
|
+
| | tail | head | weight |
|
185
|
+
|---:|-------:|-------:|---------:|
|
186
|
+
| 0 | 1000 | 1001 | 1 |
|
187
|
+
| 1 | 1000 | 1002 | 4 |
|
188
|
+
| 2 | 1001 | 1002 | 2 |
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
```python
|
193
|
+
dijkstra = Dijkstra(shifted_edges, permute=True)
|
194
|
+
shortest_paths = dijkstra.run(vertex_idx=0 + SHIFT)
|
195
|
+
print("Shortest paths:", shortest_paths)
|
196
|
+
```
|
197
|
+
|
198
|
+
Shortest paths: [inf inf inf ... 3. 4. 5.]
|
199
|
+
|
200
|
+
```python
|
201
|
+
shortest_paths[-5:]
|
202
|
+
```
|
203
|
+
|
204
|
+
array([0., 1., 3., 4., 5.])
|
205
|
+
|
206
|
+
#### Run method options
|
207
|
+
|
208
|
+
The `run` method can take the following arguments besides the source/target vertex index:
|
209
|
+
|
210
|
+
- `path_tracking` : bool, optional (default=False)
|
211
|
+
|
212
|
+
Whether to track the shortest path(s) from/to the source/target vertex to all other vertices in the graph.
|
213
|
+
|
214
|
+
```python
|
215
|
+
dijkstra = Dijkstra(edges)
|
216
|
+
shortest_paths = dijkstra.run(vertex_idx=0, path_tracking=True)
|
217
|
+
dijkstra.get_path(vertex_idx=4)
|
218
|
+
```
|
219
|
+
|
220
|
+
array([4, 3, 2, 1, 0], dtype=uint32)
|
221
|
+
|
222
|
+
```python
|
223
|
+
dijkstra.get_path(vertex_idx=0)
|
224
|
+
```
|
225
|
+
|
226
|
+
array([0], dtype=uint32)
|
227
|
+
|
228
|
+
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.
|
229
|
+
|
230
|
+
- `return_inf` : bool, optional (default=True)
|
231
|
+
|
232
|
+
Whether to return path lengths as infinity (np.inf) when no path exists.
|
233
|
+
|
234
|
+
```python
|
235
|
+
dijkstra = Dijkstra(edges, orientation='in')
|
236
|
+
shortest_paths = dijkstra.run(vertex_idx=0, return_inf=False)
|
237
|
+
shortest_paths
|
238
|
+
```
|
239
|
+
|
240
|
+
array([0.00000000e+000, 1.79769313e+308, 1.79769313e+308, 1.79769313e+308,
|
241
|
+
1.79769313e+308])
|
242
|
+
|
243
|
+
The value 1.79769313e+308 actually used in the code is the largest number that can be represented in the floating point format (`np.float64`).
|
244
|
+
|
245
|
+
- `return_series` : bool, optional (default=False)
|
246
|
+
|
247
|
+
Instead of returning a NumPy array, the `run` method may return a Pandas Series object with the path lengths as values and the vertex indices as the index.
|
248
|
+
|
249
|
+
```python
|
250
|
+
shortest_paths = dijkstra.run(vertex_idx=4, return_series=True)
|
251
|
+
shortest_paths
|
252
|
+
```
|
253
|
+
|
254
|
+
| vertex_idx | path_length |
|
255
|
+
|-------------:|--------------:|
|
256
|
+
| 0 | 5 |
|
257
|
+
| 1 | 4 |
|
258
|
+
| 2 | 2 |
|
259
|
+
| 3 | 1 |
|
260
|
+
| 4 | 0 |
|
261
|
+
|
262
|
+
|
263
|
+
- `heap_length_ratio` : float, optional (default=1.0)
|
264
|
+
|
265
|
+
This is an experimental parameter that controls the size of the heap used in the algorithm. The heap is a static array that is used to store the vertices that may be visited next. A value of 1.0 means that the heap is the same size as the number of vertices, so there is no risk of overflow. Be aware that there is no guarantee that the algorithm will work with a heap length ratio smaller that 1. The lowest ratio that works for a given graph depends on the graph structure and the source vertex. For a rather sparse graph, a small ratio may work, but for a dense graph, a ratio of 1.0 is required.
|
266
|
+
|
267
|
+
## Contributing
|
268
|
+
|
269
|
+
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).
|
270
|
+
|
271
|
+
### License
|
272
|
+
|
273
|
+
Edsger is licensed under the MIT License. See the LICENSE file for more details.
|
274
|
+
|
275
|
+
### Contact
|
276
|
+
|
277
|
+
For any questions or inquiries, please contact François Pacull at [francois.pacull@architecture-performance.fr](mailto:francois.pacull@architecture-performance.fr).
|
edsger-0.0.15/README.md
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
|
2
|
+

|
3
|
+

|
4
|
+
[](https://opensource.org/licenses/MIT)
|
5
|
+
|
6
|
+
# Edsger
|
7
|
+
|
8
|
+
*Graph algorithms in Cython*
|
9
|
+
|
10
|
+
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.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
### Install from PyPI
|
15
|
+
|
16
|
+
To install Edsger, simply use pip:
|
17
|
+
|
18
|
+
```sh
|
19
|
+
pip install edsger
|
20
|
+
```
|
21
|
+
|
22
|
+
### Install from source
|
23
|
+
|
24
|
+
If you want to install from source, you can clone the repository and install it using pip:
|
25
|
+
|
26
|
+
```sh
|
27
|
+
git clone git@github.com:aetperf/Edsger.git
|
28
|
+
cd Edsger
|
29
|
+
pip install .
|
30
|
+
```
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
### Dijkstra's algorithm
|
35
|
+
|
36
|
+
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.
|
37
|
+
|
38
|
+
```python
|
39
|
+
import pandas as pd
|
40
|
+
|
41
|
+
from edsger.path import Dijkstra
|
42
|
+
|
43
|
+
# Create a DataFrame with the edges of the graph
|
44
|
+
edges = pd.DataFrame({
|
45
|
+
'tail': [0, 0, 1, 2, 2, 3],
|
46
|
+
'head': [1, 2, 2, 3, 4, 4],
|
47
|
+
'weight': [1, 4, 2, 1, 3, 1]
|
48
|
+
})
|
49
|
+
edges
|
50
|
+
```
|
51
|
+
|
52
|
+
| | tail | head | weight |
|
53
|
+
|---:|-------:|-------:|---------:|
|
54
|
+
| 0 | 0 | 1 | 1 |
|
55
|
+
| 1 | 0 | 2 | 4 |
|
56
|
+
| 2 | 1 | 2 | 2 |
|
57
|
+
| 3 | 2 | 3 | 1 |
|
58
|
+
| 4 | 2 | 4 | 3 |
|
59
|
+
| 5 | 3 | 4 | 1 |
|
60
|
+
|
61
|
+
|
62
|
+
```python
|
63
|
+
# Initialize the Dijkstra object
|
64
|
+
dijkstra = Dijkstra(edges)
|
65
|
+
|
66
|
+
# Run the algorithm from a source vertex
|
67
|
+
shortest_paths = dijkstra.run(vertex_idx=0)
|
68
|
+
print("Shortest paths:", shortest_paths)
|
69
|
+
```
|
70
|
+
|
71
|
+
Shortest paths: [0. 1. 3. 4. 5.]
|
72
|
+
|
73
|
+
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
|
+
|
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:
|
76
|
+
|
77
|
+
```python
|
78
|
+
other_edges = pd.DataFrame({
|
79
|
+
'from': [0, 0, 1, 2, 2, 3],
|
80
|
+
'to': [1, 2, 2, 3, 4, 4],
|
81
|
+
'travel_time': [1, 4, 2, 1, 3, 1]
|
82
|
+
})
|
83
|
+
other_dijkstra = Dijkstra(edges, tail='from', head='to', weight='travel_time')
|
84
|
+
```
|
85
|
+
|
86
|
+
#### Orientation
|
87
|
+
|
88
|
+
The `orientation` argument (a string with a default value of `'out'`) specifies the orientation of the algorithm. It can be either `'out'` for single source shortest paths or `'in'` for single target shortest path.
|
89
|
+
|
90
|
+
```python
|
91
|
+
dijkstra = Dijkstra(edges, orientation='in')
|
92
|
+
|
93
|
+
# Run the algorithm to a target vertex
|
94
|
+
shortest_paths = dijkstra.run(vertex_idx=0)
|
95
|
+
print("Shortest paths:", shortest_paths)
|
96
|
+
```
|
97
|
+
|
98
|
+
Shortest paths: [ 0. inf inf inf inf]
|
99
|
+
|
100
|
+
#### Run multiple times
|
101
|
+
|
102
|
+
Once the Dijkstra is instanciated with a given graph and orientation, the `run` method can be called multiple times with different source vertices.
|
103
|
+
|
104
|
+
```python
|
105
|
+
# Run the algorithm to another target vertex
|
106
|
+
shortest_paths = dijkstra.run(vertex_idx=4)
|
107
|
+
print("Shortest paths:", shortest_paths)
|
108
|
+
```
|
109
|
+
|
110
|
+
Shortest paths: [5. 4. 2. 1. 0.]
|
111
|
+
|
112
|
+
#### Check edges
|
113
|
+
|
114
|
+
The `check_edges` argument (a boolean with a default value of `False`) validates the given graph. When set to `True`, it ensures the DataFrame is well-formed by:
|
115
|
+
|
116
|
+
- Checking for the presence of required columns for edge tail, head and weigh values
|
117
|
+
- Verifying that the data types are correct (integer for tail and head, integer or float for weight)
|
118
|
+
- Ensuring there are no missing or invalid values (e.g. negative weights)
|
119
|
+
|
120
|
+
If any of these checks fail, an appropriate error is raised.
|
121
|
+
|
122
|
+
```python
|
123
|
+
invalid_edges = pd.DataFrame({
|
124
|
+
'tail': [0, 0, 1, 2, 2, 3],
|
125
|
+
'head': [1, 2, 2, 3, 4, 4],
|
126
|
+
'weight': [1, 4, 2, -1, 3, 1]
|
127
|
+
})
|
128
|
+
dijkstra = Dijkstra(invalid_edges, check_edges=True)
|
129
|
+
```
|
130
|
+
|
131
|
+
ValueError: edges['weight'] should be nonnegative
|
132
|
+
|
133
|
+
#### Permute
|
134
|
+
|
135
|
+
Finally, the `permute` argument (boolean with a default value of `False`) allows to permute the IDs of the nodes. If set to `True`, the node IDs will be reindexed to start from 0 and be contiguous for the inner computations, and the output will be reindexed to the original IDs, loading the same result as if the IDs were not permuted. The permutation may save memory and computation time for large graphs, for example if a significant ratio of the nodes are not actually used in the graph.
|
136
|
+
|
137
|
+
```python
|
138
|
+
SHIFT = 1000
|
139
|
+
|
140
|
+
shifted_edges = pd.DataFrame({
|
141
|
+
'tail': [0, 0, 1, 2, 2, 3],
|
142
|
+
'head': [1, 2, 2, 3, 4, 4],
|
143
|
+
'weight': [1, 4, 2, 1, 3, 1]
|
144
|
+
})
|
145
|
+
shifted_edges["tail"] += SHIFT
|
146
|
+
shifted_edges["head"] += SHIFT
|
147
|
+
shifted_edges.head(3)
|
148
|
+
```
|
149
|
+
|
150
|
+
| | tail | head | weight |
|
151
|
+
|---:|-------:|-------:|---------:|
|
152
|
+
| 0 | 1000 | 1001 | 1 |
|
153
|
+
| 1 | 1000 | 1002 | 4 |
|
154
|
+
| 2 | 1001 | 1002 | 2 |
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
```python
|
159
|
+
dijkstra = Dijkstra(shifted_edges, permute=True)
|
160
|
+
shortest_paths = dijkstra.run(vertex_idx=0 + SHIFT)
|
161
|
+
print("Shortest paths:", shortest_paths)
|
162
|
+
```
|
163
|
+
|
164
|
+
Shortest paths: [inf inf inf ... 3. 4. 5.]
|
165
|
+
|
166
|
+
```python
|
167
|
+
shortest_paths[-5:]
|
168
|
+
```
|
169
|
+
|
170
|
+
array([0., 1., 3., 4., 5.])
|
171
|
+
|
172
|
+
#### Run method options
|
173
|
+
|
174
|
+
The `run` method can take the following arguments besides the source/target vertex index:
|
175
|
+
|
176
|
+
- `path_tracking` : bool, optional (default=False)
|
177
|
+
|
178
|
+
Whether to track the shortest path(s) from/to the source/target vertex to all other vertices in the graph.
|
179
|
+
|
180
|
+
```python
|
181
|
+
dijkstra = Dijkstra(edges)
|
182
|
+
shortest_paths = dijkstra.run(vertex_idx=0, path_tracking=True)
|
183
|
+
dijkstra.get_path(vertex_idx=4)
|
184
|
+
```
|
185
|
+
|
186
|
+
array([4, 3, 2, 1, 0], dtype=uint32)
|
187
|
+
|
188
|
+
```python
|
189
|
+
dijkstra.get_path(vertex_idx=0)
|
190
|
+
```
|
191
|
+
|
192
|
+
array([0], dtype=uint32)
|
193
|
+
|
194
|
+
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.
|
195
|
+
|
196
|
+
- `return_inf` : bool, optional (default=True)
|
197
|
+
|
198
|
+
Whether to return path lengths as infinity (np.inf) when no path exists.
|
199
|
+
|
200
|
+
```python
|
201
|
+
dijkstra = Dijkstra(edges, orientation='in')
|
202
|
+
shortest_paths = dijkstra.run(vertex_idx=0, return_inf=False)
|
203
|
+
shortest_paths
|
204
|
+
```
|
205
|
+
|
206
|
+
array([0.00000000e+000, 1.79769313e+308, 1.79769313e+308, 1.79769313e+308,
|
207
|
+
1.79769313e+308])
|
208
|
+
|
209
|
+
The value 1.79769313e+308 actually used in the code is the largest number that can be represented in the floating point format (`np.float64`).
|
210
|
+
|
211
|
+
- `return_series` : bool, optional (default=False)
|
212
|
+
|
213
|
+
Instead of returning a NumPy array, the `run` method may return a Pandas Series object with the path lengths as values and the vertex indices as the index.
|
214
|
+
|
215
|
+
```python
|
216
|
+
shortest_paths = dijkstra.run(vertex_idx=4, return_series=True)
|
217
|
+
shortest_paths
|
218
|
+
```
|
219
|
+
|
220
|
+
| vertex_idx | path_length |
|
221
|
+
|-------------:|--------------:|
|
222
|
+
| 0 | 5 |
|
223
|
+
| 1 | 4 |
|
224
|
+
| 2 | 2 |
|
225
|
+
| 3 | 1 |
|
226
|
+
| 4 | 0 |
|
227
|
+
|
228
|
+
|
229
|
+
- `heap_length_ratio` : float, optional (default=1.0)
|
230
|
+
|
231
|
+
This is an experimental parameter that controls the size of the heap used in the algorithm. The heap is a static array that is used to store the vertices that may be visited next. A value of 1.0 means that the heap is the same size as the number of vertices, so there is no risk of overflow. Be aware that there is no guarantee that the algorithm will work with a heap length ratio smaller that 1. The lowest ratio that works for a given graph depends on the graph structure and the source vertex. For a rather sparse graph, a small ratio may work, but for a dense graph, a ratio of 1.0 is required.
|
232
|
+
|
233
|
+
## Contributing
|
234
|
+
|
235
|
+
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).
|
236
|
+
|
237
|
+
### License
|
238
|
+
|
239
|
+
Edsger is licensed under the MIT License. See the LICENSE file for more details.
|
240
|
+
|
241
|
+
### Contact
|
242
|
+
|
243
|
+
For any questions or inquiries, please contact François Pacull at [francois.pacull@architecture-performance.fr](mailto:francois.pacull@architecture-performance.fr).
|
@@ -19,7 +19,7 @@ sys.path.insert(0, os.path.abspath("../../src/edsger/"))
|
|
19
19
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
20
20
|
|
21
21
|
project = "Edsger"
|
22
|
-
copyright = "
|
22
|
+
copyright = "2024, Architecture & Performance"
|
23
23
|
author = "Francois Pacull"
|
24
24
|
|
25
25
|
# -- General configuration ---------------------------------------------------
|
@@ -31,8 +31,7 @@ exclude_patterns = ["*.pyx"]
|
|
31
31
|
# -- Options for HTML output -------------------------------------------------
|
32
32
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
33
33
|
|
34
|
-
html_theme = "
|
35
|
-
# html_theme = "furo"
|
34
|
+
html_theme = "furo"
|
36
35
|
html_static_path = ["_static"]
|
37
36
|
|
38
37
|
# Add any Sphinx extension module names here, as strings. They can be
|
@@ -0,0 +1,22 @@
|
|
1
|
+
:github_url: https://github.com/aetperf/Edsger
|
2
|
+
|
3
|
+
======
|
4
|
+
Edsger
|
5
|
+
======
|
6
|
+
|
7
|
+
*Graph algorithms in Cython*
|
8
|
+
|
9
|
+
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.
|
10
|
+
|
11
|
+
Installation
|
12
|
+
------------
|
13
|
+
|
14
|
+
The latest release of Edsger can be installed from
|
15
|
+
`PyPI <https://pypi.org/project/edsger/>`_ using::
|
16
|
+
|
17
|
+
pip install edsger
|
18
|
+
|
19
|
+
You may also install directly from GitHub, using the following command. This
|
20
|
+
can be used to obtain the most recent version of Edsger::
|
21
|
+
|
22
|
+
pip install 'edsger @ git+https://github.com/aetperf/Edsger'
|