edsger 0.1.3__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.
- {edsger-0.1.3 → edsger-0.1.4}/.github/workflows/docs.yml +4 -6
- {edsger-0.1.3 → edsger-0.1.4}/.github/workflows/publish.yml +3 -4
- {edsger-0.1.3 → edsger-0.1.4}/.github/workflows/tests.yml +14 -4
- {edsger-0.1.3 → edsger-0.1.4}/PKG-INFO +11 -4
- {edsger-0.1.3 → edsger-0.1.4}/README.md +10 -3
- edsger-0.1.4/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/source/index.md +1 -3
- edsger-0.1.4/scripts/README_benchmark_os.md +72 -0
- edsger-0.1.4/scripts/benchmark_comparison_os.py +409 -0
- edsger-0.1.4/scripts/benchmark_dimacs_USA_linux.json +84 -0
- edsger-0.1.4/scripts/benchmark_dimacs_USA_windows.json +69 -0
- {edsger-0.1.3 → edsger-0.1.4}/scripts/dijkstra_dimacs.py +26 -2
- edsger-0.1.4/scripts/plot_benchmark_comparison.py +414 -0
- edsger-0.1.3/scripts/requirements.txt → edsger-0.1.4/scripts/requirements_linux.txt +0 -1
- edsger-0.1.4/scripts/requirements_windows.txt +9 -0
- {edsger-0.1.3 → edsger-0.1.4}/setup.py +23 -8
- edsger-0.1.4/src/edsger/_version.py +1 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/commons.c +146 -146
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/dijkstra.c +146 -146
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/path_tracking.c +146 -146
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.c +146 -146
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/spiess_florian.c +146 -146
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/star.c +146 -146
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/PKG-INFO +11 -4
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/SOURCES.txt +7 -2
- edsger-0.1.3/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
- edsger-0.1.3/scripts/benchmark_comparison.py +0 -442
- edsger-0.1.3/src/edsger/_version.py +0 -1
- {edsger-0.1.3 → edsger-0.1.4}/.gitignore +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/.pre-commit-config.yaml +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/.readthedocs.yaml +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/AUTHORS.rst +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/CHANGELOG.rst +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/CONTRIBUTING.rst +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/LICENSE +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/MANIFEST.in +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/Makefile +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/requirements.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/source/api.md +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/source/conf.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/source/contributing.md +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/source/installation.md +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/docs/source/quickstart.md +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/pyproject.toml +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/requirements-dev.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/requirements.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/setup.cfg +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/.gitignore +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/__init__.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/commons.pxd +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/commons.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/dijkstra.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/networks.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/path.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/path_tracking.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.pxd +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/prefetch_compat.h +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/spiess_florian.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/star.pyx +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger/utils.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/dependency_links.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/not-zip-safe +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/requires.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/top_level.txt +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/tests/test_dijkstra.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/tests/test_path.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/tests/test_path_tracking.py +0 -0
- {edsger-0.1.3 → edsger-0.1.4}/tests/test_pq_4ary_dec_0b.py +0 -0
- {edsger-0.1.3 → 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
|
-
|
5
|
-
|
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
|
38
|
-
if: github.
|
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,8 @@
|
|
1
1
|
name: ci-publish # Publish Python distribution to PyPI
|
2
2
|
|
3
3
|
on:
|
4
|
-
|
5
|
-
|
6
|
-
- 'v[0-9]+.[0-9]+.[0-9]+'
|
4
|
+
release:
|
5
|
+
types: [created]
|
7
6
|
|
8
7
|
jobs:
|
9
8
|
|
@@ -49,7 +48,7 @@ jobs:
|
|
49
48
|
runs-on: ${{ matrix.os }}
|
50
49
|
strategy:
|
51
50
|
matrix:
|
52
|
-
os: [ubuntu-latest, windows-
|
51
|
+
os: [ubuntu-latest, windows-2022, macos-13, macos-14]
|
53
52
|
|
54
53
|
steps:
|
55
54
|
- uses: actions/checkout@v4
|
@@ -1,9 +1,8 @@
|
|
1
1
|
name: Run tests and upload coverage
|
2
2
|
|
3
3
|
on:
|
4
|
-
|
5
|
-
|
6
|
-
- release
|
4
|
+
release:
|
5
|
+
types: [created]
|
7
6
|
|
8
7
|
jobs:
|
9
8
|
test:
|
@@ -27,16 +26,27 @@ jobs:
|
|
27
26
|
python -m pip install --upgrade pip
|
28
27
|
pip install -r requirements-dev.txt
|
29
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 .
|
30
34
|
- name: Format Python code with black
|
31
35
|
run: |
|
32
36
|
black --check --diff .
|
33
37
|
- name: Lint Cython code
|
34
38
|
run: |
|
35
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
|
36
|
-
- name: Run tests
|
40
|
+
- name: Run tests with coverage
|
41
|
+
if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
|
37
42
|
run: |
|
38
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/
|
39
48
|
- name: Upload results to Codecov
|
49
|
+
if: matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.11'
|
40
50
|
uses: codecov/codecov-action@v5
|
41
51
|
with:
|
42
52
|
token: ${{ secrets.CODECOV_TOKEN }}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: edsger
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.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,6 +42,7 @@ Dynamic: license-file
|
|
42
42
|
|
43
43
|

|
44
44
|
[](https://codecov.io/gh/aetperf/edsger)
|
45
|
+
[](https://edsger.readthedocs.io/en/latest/?badge=latest)
|
45
46
|
[](https://pypi.org/project/edsger/)
|
46
47
|
[](https://pepy.tech/project/edsger)
|
47
48
|
[](https://pypi.org/project/edsger/)
|
@@ -97,14 +98,20 @@ 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
|
+
## Installation
|
102
|
+
|
103
|
+
### Standard Installation
|
104
|
+
|
105
|
+
```bash
|
106
|
+
pip install edsger
|
107
|
+
```
|
108
|
+
|
100
109
|
## Why Use Edsger?
|
101
110
|
|
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:
|
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:
|
103
112
|
|
104
113
|
<img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
|
105
114
|
|
106
|
-
*Benchmark performed on Intel i9-12900H Linux laptop.*
|
107
|
-
|
108
115
|
## Contributing
|
109
116
|
|
110
117
|
We welcome contributions to the Edsger library. If you have any suggestions, bug reports, or feature requests, please open an issue on our [GitHub repository](https://github.com/aetperf/Edsger).
|
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
2
|

|
3
3
|
[](https://codecov.io/gh/aetperf/edsger)
|
4
|
+
[](https://edsger.readthedocs.io/en/latest/?badge=latest)
|
4
5
|
[](https://pypi.org/project/edsger/)
|
5
6
|
[](https://pepy.tech/project/edsger)
|
6
7
|
[](https://pypi.org/project/edsger/)
|
@@ -56,14 +57,20 @@ 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
|
+
## Installation
|
61
|
+
|
62
|
+
### Standard Installation
|
63
|
+
|
64
|
+
```bash
|
65
|
+
pip install edsger
|
66
|
+
```
|
67
|
+
|
59
68
|
## Why Use Edsger?
|
60
69
|
|
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:
|
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:
|
62
71
|
|
63
72
|
<img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
|
64
73
|
|
65
|
-
*Benchmark performed on Intel i9-12900H Linux laptop.*
|
66
|
-
|
67
74
|
## Contributing
|
68
75
|
|
69
76
|
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).
|
Binary file
|
@@ -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 Linux laptop.*
|
18
|
-
|
19
17
|
### Pandas Integration Made Simple
|
20
18
|
|
21
19
|
```python
|
@@ -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
|
@@ -0,0 +1,409 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Comprehensive benchmarking script for Dijkstra implementations with OS-specific output.
|
4
|
+
|
5
|
+
Runs dijkstra_dimacs.py for Edsger, NetworKit, Graph-tool, and SciPy,
|
6
|
+
then saves results to OS-specific JSON files for later comparison.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import sys
|
11
|
+
import subprocess
|
12
|
+
import time
|
13
|
+
import platform
|
14
|
+
import numpy as np
|
15
|
+
from datetime import datetime
|
16
|
+
import json
|
17
|
+
from argparse import ArgumentParser
|
18
|
+
|
19
|
+
|
20
|
+
def get_system_info():
|
21
|
+
"""Collect system and package version information."""
|
22
|
+
info = {}
|
23
|
+
|
24
|
+
# Python version
|
25
|
+
info["python_version"] = platform.python_version()
|
26
|
+
|
27
|
+
# Platform info
|
28
|
+
info["platform"] = platform.platform()
|
29
|
+
info["processor"] = platform.processor()
|
30
|
+
info["os_name"] = platform.system() # Linux, Windows, Darwin
|
31
|
+
|
32
|
+
# Anaconda/Conda version
|
33
|
+
try:
|
34
|
+
result = subprocess.run(["conda", "--version"], capture_output=True, text=True)
|
35
|
+
if result.returncode == 0:
|
36
|
+
info["conda_version"] = result.stdout.strip()
|
37
|
+
else:
|
38
|
+
info["conda_version"] = "Not available"
|
39
|
+
except FileNotFoundError:
|
40
|
+
info["conda_version"] = "Not installed"
|
41
|
+
|
42
|
+
# Package versions - check availability first
|
43
|
+
packages = ["edsger", "networkit", "graph-tool", "scipy", "numpy", "pandas"]
|
44
|
+
available_packages = []
|
45
|
+
|
46
|
+
for package in packages:
|
47
|
+
try:
|
48
|
+
if package == "edsger":
|
49
|
+
import edsger
|
50
|
+
|
51
|
+
info[f"{package}_version"] = getattr(edsger, "__version__", "dev")
|
52
|
+
available_packages.append(package)
|
53
|
+
elif package == "networkit":
|
54
|
+
import networkit as nk
|
55
|
+
|
56
|
+
info[f"{package}_version"] = nk.__version__
|
57
|
+
available_packages.append(package)
|
58
|
+
elif package == "graph-tool":
|
59
|
+
# Graph-tool is not available on Windows
|
60
|
+
if platform.system() == "Windows":
|
61
|
+
info[f"{package}_version"] = "Not available on Windows"
|
62
|
+
else:
|
63
|
+
import graph_tool as gt
|
64
|
+
|
65
|
+
info[f"{package}_version"] = gt.__version__
|
66
|
+
available_packages.append(package)
|
67
|
+
elif package == "scipy":
|
68
|
+
import scipy
|
69
|
+
|
70
|
+
info[f"{package}_version"] = scipy.__version__
|
71
|
+
available_packages.append(package)
|
72
|
+
elif package == "numpy":
|
73
|
+
import numpy as np
|
74
|
+
|
75
|
+
info[f"{package}_version"] = np.__version__
|
76
|
+
available_packages.append(package)
|
77
|
+
elif package == "pandas":
|
78
|
+
import pandas as pd
|
79
|
+
|
80
|
+
info[f"{package}_version"] = pd.__version__
|
81
|
+
available_packages.append(package)
|
82
|
+
except ImportError:
|
83
|
+
info[f"{package}_version"] = "Not installed"
|
84
|
+
except Exception as e:
|
85
|
+
info[f"{package}_version"] = f"Error: {str(e)}"
|
86
|
+
|
87
|
+
info["available_packages"] = available_packages
|
88
|
+
return info
|
89
|
+
|
90
|
+
|
91
|
+
def run_benchmark(library, repeat=5, data_dir=None, network_name="USA"):
|
92
|
+
"""Run dijkstra_dimacs.py for a specific library."""
|
93
|
+
print(f"\n[BENCHMARK] Running {library} benchmark ({repeat} iterations)...")
|
94
|
+
|
95
|
+
# Get the project root directory (parent of scripts)
|
96
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
97
|
+
project_root = os.path.dirname(script_dir)
|
98
|
+
|
99
|
+
# For SciPy, we need to run Edsger with -c flag multiple times to get SciPy timing
|
100
|
+
# (SciPy only runs once per dijkstra_dimacs.py call when using -c)
|
101
|
+
if library == "SciPy":
|
102
|
+
cmd = [
|
103
|
+
sys.executable,
|
104
|
+
"dijkstra_dimacs.py",
|
105
|
+
"-n",
|
106
|
+
network_name,
|
107
|
+
"-l",
|
108
|
+
"E", # Run Edsger but with -c to get SciPy comparison
|
109
|
+
"-r",
|
110
|
+
"1", # Run only 1 iteration of Edsger per call
|
111
|
+
"-c",
|
112
|
+
]
|
113
|
+
else:
|
114
|
+
cmd = [
|
115
|
+
sys.executable,
|
116
|
+
"dijkstra_dimacs.py",
|
117
|
+
"-n",
|
118
|
+
network_name,
|
119
|
+
"-l",
|
120
|
+
library,
|
121
|
+
"-r",
|
122
|
+
str(repeat),
|
123
|
+
]
|
124
|
+
|
125
|
+
# Add data directory argument if provided
|
126
|
+
if data_dir:
|
127
|
+
cmd.extend(["-d", data_dir])
|
128
|
+
|
129
|
+
# Set environment to include project root in Python path
|
130
|
+
env = os.environ.copy()
|
131
|
+
env["PYTHONPATH"] = project_root + ":" + env.get("PYTHONPATH", "")
|
132
|
+
|
133
|
+
try:
|
134
|
+
print(f"[DEBUG] Running command: {' '.join(cmd)}")
|
135
|
+
print(f"[DEBUG] Working directory: {os.getcwd()}")
|
136
|
+
print(f"[DEBUG] Project root: {project_root}")
|
137
|
+
|
138
|
+
times = []
|
139
|
+
total_start_time = time.time()
|
140
|
+
|
141
|
+
# For SciPy, we need to run the command multiple times since SciPy only runs once per call
|
142
|
+
iterations = repeat if library == "SciPy" else 1
|
143
|
+
|
144
|
+
for iteration in range(iterations):
|
145
|
+
if library == "SciPy":
|
146
|
+
print(f"[DEBUG] SciPy iteration {iteration + 1}/{iterations}")
|
147
|
+
|
148
|
+
result = subprocess.run(
|
149
|
+
cmd, capture_output=True, text=True, timeout=600, env=env
|
150
|
+
)
|
151
|
+
|
152
|
+
print(f"[DEBUG] Return code: {result.returncode}")
|
153
|
+
if iteration == 0: # Only print lengths for first iteration to avoid spam
|
154
|
+
print(f"[DEBUG] Stdout length: {len(result.stdout)}")
|
155
|
+
print(f"[DEBUG] Stderr length: {len(result.stderr)}")
|
156
|
+
|
157
|
+
if result.returncode != 0:
|
158
|
+
print(f"[ERROR] {library} benchmark failed!")
|
159
|
+
print(f"[ERROR] stderr: {result.stderr}")
|
160
|
+
print(f"[DEBUG] stdout: {result.stdout}")
|
161
|
+
return None
|
162
|
+
|
163
|
+
# Parse output to extract timing information
|
164
|
+
# The output might be in stderr due to logging configuration
|
165
|
+
output_text = result.stdout + result.stderr
|
166
|
+
lines = output_text.strip().split("\n")
|
167
|
+
|
168
|
+
# Map library codes to full names used in output
|
169
|
+
library_names = {
|
170
|
+
"E": "Edsger",
|
171
|
+
"NK": "NetworKit",
|
172
|
+
"GT": "graph-tool",
|
173
|
+
"SciPy": "SciPy",
|
174
|
+
}
|
175
|
+
|
176
|
+
expected_name = library_names.get(library, library)
|
177
|
+
|
178
|
+
for line in lines:
|
179
|
+
if library == "SciPy":
|
180
|
+
# For SciPy, look for the pattern from the check_result section
|
181
|
+
# "SciPy Dijkstra - Elapsed time: 1.2345 s" (no trial number)
|
182
|
+
if "SciPy Dijkstra - Elapsed time:" in line:
|
183
|
+
parts = line.split("Elapsed time:")
|
184
|
+
if len(parts) > 1:
|
185
|
+
time_str = parts[1].strip().split()[0]
|
186
|
+
try:
|
187
|
+
elapsed_time = float(time_str)
|
188
|
+
times.append(elapsed_time)
|
189
|
+
break # Only one SciPy timing per run
|
190
|
+
except ValueError:
|
191
|
+
continue
|
192
|
+
else:
|
193
|
+
# For other libraries, look for the pattern with trial numbers
|
194
|
+
# "Edsger Dijkstra 1/5 - Elapsed time: 2.5410 s"
|
195
|
+
if (
|
196
|
+
f"{expected_name} Dijkstra" in line
|
197
|
+
and "Elapsed time:" in line
|
198
|
+
and "/" in line
|
199
|
+
):
|
200
|
+
parts = line.split("Elapsed time:")
|
201
|
+
if len(parts) > 1:
|
202
|
+
time_str = parts[1].strip().split()[0]
|
203
|
+
try:
|
204
|
+
elapsed_time = float(time_str)
|
205
|
+
times.append(elapsed_time)
|
206
|
+
except ValueError:
|
207
|
+
continue
|
208
|
+
|
209
|
+
total_end_time = time.time()
|
210
|
+
|
211
|
+
if not times:
|
212
|
+
print(f"[WARNING] Could not parse timing data for {library}")
|
213
|
+
print(f"[DEBUG] Expected pattern: '{expected_name} Dijkstra'")
|
214
|
+
return None
|
215
|
+
|
216
|
+
min_time = min(times)
|
217
|
+
avg_time = np.mean(times)
|
218
|
+
std_time = np.std(times)
|
219
|
+
|
220
|
+
print(
|
221
|
+
f"[BENCHMARK] {library} completed: min={min_time:.4f}s, avg={avg_time:.4f}s, std={std_time:.4f}s"
|
222
|
+
)
|
223
|
+
|
224
|
+
return {
|
225
|
+
"library": library,
|
226
|
+
"times": times,
|
227
|
+
"min_time": min_time,
|
228
|
+
"avg_time": avg_time,
|
229
|
+
"std_time": std_time,
|
230
|
+
"total_duration": total_end_time - total_start_time,
|
231
|
+
}
|
232
|
+
|
233
|
+
except subprocess.TimeoutExpired:
|
234
|
+
print(f"[ERROR] {library} benchmark timed out!")
|
235
|
+
return None
|
236
|
+
except Exception as e:
|
237
|
+
print(f"[ERROR] {library} benchmark failed with exception: {e}")
|
238
|
+
return None
|
239
|
+
|
240
|
+
|
241
|
+
def save_results_json(results, system_info, network_name="USA"):
|
242
|
+
"""Save detailed results to OS-specific JSON file."""
|
243
|
+
os_name = platform.system().lower() # linux, windows, darwin
|
244
|
+
output_file = f"benchmark_dimacs_{network_name}_{os_name}.json"
|
245
|
+
|
246
|
+
data = {
|
247
|
+
"timestamp": datetime.now().isoformat(),
|
248
|
+
"os": os_name,
|
249
|
+
"network": network_name,
|
250
|
+
"system_info": system_info,
|
251
|
+
"benchmark_results": [r for r in results if r is not None],
|
252
|
+
}
|
253
|
+
|
254
|
+
with open(output_file, "w") as f:
|
255
|
+
json.dump(data, f, indent=2)
|
256
|
+
|
257
|
+
print(f"\n[DATA] Saved detailed results to: {output_file}")
|
258
|
+
return output_file
|
259
|
+
|
260
|
+
|
261
|
+
def main():
|
262
|
+
"""Main benchmarking function."""
|
263
|
+
# Determine default data directory based on OS
|
264
|
+
if platform.system() == "Windows":
|
265
|
+
# Check common Windows paths
|
266
|
+
if os.path.exists(
|
267
|
+
r"C:\Users\fpacu\Documents\Workspace\Edsger\data\DIMACS_road_networks"
|
268
|
+
):
|
269
|
+
default_data_dir = (
|
270
|
+
r"C:\Users\fpacu\Documents\Workspace\Edsger\data\DIMACS_road_networks"
|
271
|
+
)
|
272
|
+
else:
|
273
|
+
default_data_dir = os.path.join(
|
274
|
+
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
275
|
+
"data",
|
276
|
+
"DIMACS_road_networks",
|
277
|
+
)
|
278
|
+
else:
|
279
|
+
default_data_dir = "/home/francois/Data/DIMACS_road_networks/"
|
280
|
+
|
281
|
+
# Use environment variable if set, otherwise use OS-specific default
|
282
|
+
default_data_dir = os.getenv("DIMACS_DATA_DIR", default_data_dir)
|
283
|
+
|
284
|
+
# Parse command line arguments
|
285
|
+
parser = ArgumentParser(
|
286
|
+
description="OS-specific benchmark comparison for Dijkstra implementations"
|
287
|
+
)
|
288
|
+
parser.add_argument(
|
289
|
+
"-d",
|
290
|
+
"--dir",
|
291
|
+
dest="data_dir",
|
292
|
+
help="Data folder with network sub-folders",
|
293
|
+
metavar="TXT",
|
294
|
+
type=str,
|
295
|
+
required=False,
|
296
|
+
default=default_data_dir,
|
297
|
+
)
|
298
|
+
parser.add_argument(
|
299
|
+
"-n",
|
300
|
+
"--network",
|
301
|
+
dest="network_name",
|
302
|
+
help="network name, must be 'NY', 'BAY', 'COL', 'FLA', 'NW', "
|
303
|
+
+ "'NE', 'CAL', 'LKS', 'E', 'W', 'CTR', 'USA' (default: USA)",
|
304
|
+
metavar="TXT",
|
305
|
+
type=str,
|
306
|
+
required=False,
|
307
|
+
default="USA",
|
308
|
+
)
|
309
|
+
parser.add_argument(
|
310
|
+
"-r",
|
311
|
+
"--repeat",
|
312
|
+
dest="repeat",
|
313
|
+
help="Number of benchmark iterations per library (default: 5)",
|
314
|
+
metavar="INT",
|
315
|
+
type=int,
|
316
|
+
required=False,
|
317
|
+
default=5,
|
318
|
+
)
|
319
|
+
|
320
|
+
args = parser.parse_args()
|
321
|
+
|
322
|
+
print("=" * 80)
|
323
|
+
print("DIJKSTRA ALGORITHM PERFORMANCE BENCHMARK")
|
324
|
+
print(f"OS: {platform.system()}")
|
325
|
+
print(f"Network: {args.network_name}")
|
326
|
+
print(f"Data directory: {args.data_dir}")
|
327
|
+
print(f"Iterations per library: {args.repeat}")
|
328
|
+
print("=" * 80)
|
329
|
+
|
330
|
+
# Check if we're in the right directory
|
331
|
+
if not os.path.exists("dijkstra_dimacs.py"):
|
332
|
+
print(
|
333
|
+
"[ERROR] dijkstra_dimacs.py not found. Run this script from the scripts/ directory."
|
334
|
+
)
|
335
|
+
sys.exit(1)
|
336
|
+
|
337
|
+
# Collect system information
|
338
|
+
print("\n[INFO] Collecting system and package information...")
|
339
|
+
system_info = get_system_info()
|
340
|
+
|
341
|
+
print(f"[INFO] Python: {system_info['python_version']}")
|
342
|
+
print(f"[INFO] Platform: {system_info['platform']}")
|
343
|
+
print(f"[INFO] OS: {system_info['os_name']}")
|
344
|
+
print(f"[INFO] Conda: {system_info['conda_version']}")
|
345
|
+
|
346
|
+
# Define libraries to benchmark based on what's available
|
347
|
+
all_libraries = ["E", "NK", "GT", "SciPy"]
|
348
|
+
libraries = []
|
349
|
+
|
350
|
+
# Check which libraries are available
|
351
|
+
for lib in all_libraries:
|
352
|
+
lib_name = {
|
353
|
+
"E": "edsger",
|
354
|
+
"NK": "networkit",
|
355
|
+
"GT": "graph-tool",
|
356
|
+
"SciPy": "scipy",
|
357
|
+
}[lib]
|
358
|
+
if lib_name in system_info["available_packages"]:
|
359
|
+
libraries.append(lib)
|
360
|
+
else:
|
361
|
+
print(f"[INFO] Skipping {lib} - {lib_name} not available")
|
362
|
+
|
363
|
+
if not libraries:
|
364
|
+
print("[ERROR] No libraries available for benchmarking!")
|
365
|
+
sys.exit(1)
|
366
|
+
|
367
|
+
print(f"\n[INFO] Running benchmarks with {args.repeat} iterations each...")
|
368
|
+
print(f"[INFO] Libraries to benchmark: {', '.join(libraries)}")
|
369
|
+
|
370
|
+
# Run benchmarks
|
371
|
+
results = []
|
372
|
+
for lib in libraries:
|
373
|
+
result = run_benchmark(lib, args.repeat, args.data_dir, args.network_name)
|
374
|
+
results.append(result)
|
375
|
+
|
376
|
+
# Small delay between benchmarks to let system cool down
|
377
|
+
if result:
|
378
|
+
time.sleep(2)
|
379
|
+
|
380
|
+
# Filter successful results
|
381
|
+
successful_results = [r for r in results if r is not None]
|
382
|
+
|
383
|
+
if not successful_results:
|
384
|
+
print("[ERROR] No benchmarks completed successfully!")
|
385
|
+
sys.exit(1)
|
386
|
+
|
387
|
+
# Print summary
|
388
|
+
print("\n" + "=" * 80)
|
389
|
+
print("BENCHMARK SUMMARY")
|
390
|
+
print("=" * 80)
|
391
|
+
|
392
|
+
for result in successful_results:
|
393
|
+
print(
|
394
|
+
f"{result['library']:>10}: {result['min_time']:7.4f}s (min), {result['avg_time']:7.4f}s (avg) ± {result['std_time']:6.4f}s"
|
395
|
+
)
|
396
|
+
|
397
|
+
# Save detailed results
|
398
|
+
json_file = save_results_json(successful_results, system_info, args.network_name)
|
399
|
+
|
400
|
+
print("\n" + "=" * 80)
|
401
|
+
print("BENCHMARK COMPLETED SUCCESSFULLY")
|
402
|
+
print("=" * 80)
|
403
|
+
print(f"Results saved to: {json_file}")
|
404
|
+
print("\nTo create comparison plots, run:")
|
405
|
+
print(" python plot_benchmark_comparison.py")
|
406
|
+
|
407
|
+
|
408
|
+
if __name__ == "__main__":
|
409
|
+
main()
|