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.
Files changed (70) hide show
  1. {edsger-0.1.3 → edsger-0.1.4}/.github/workflows/docs.yml +4 -6
  2. {edsger-0.1.3 → edsger-0.1.4}/.github/workflows/publish.yml +3 -4
  3. {edsger-0.1.3 → edsger-0.1.4}/.github/workflows/tests.yml +14 -4
  4. {edsger-0.1.3 → edsger-0.1.4}/PKG-INFO +11 -4
  5. {edsger-0.1.3 → edsger-0.1.4}/README.md +10 -3
  6. edsger-0.1.4/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
  7. {edsger-0.1.3 → edsger-0.1.4}/docs/source/index.md +1 -3
  8. edsger-0.1.4/scripts/README_benchmark_os.md +72 -0
  9. edsger-0.1.4/scripts/benchmark_comparison_os.py +409 -0
  10. edsger-0.1.4/scripts/benchmark_dimacs_USA_linux.json +84 -0
  11. edsger-0.1.4/scripts/benchmark_dimacs_USA_windows.json +69 -0
  12. {edsger-0.1.3 → edsger-0.1.4}/scripts/dijkstra_dimacs.py +26 -2
  13. edsger-0.1.4/scripts/plot_benchmark_comparison.py +414 -0
  14. edsger-0.1.3/scripts/requirements.txt → edsger-0.1.4/scripts/requirements_linux.txt +0 -1
  15. edsger-0.1.4/scripts/requirements_windows.txt +9 -0
  16. {edsger-0.1.3 → edsger-0.1.4}/setup.py +23 -8
  17. edsger-0.1.4/src/edsger/_version.py +1 -0
  18. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/commons.c +146 -146
  19. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/dijkstra.c +146 -146
  20. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/path_tracking.c +146 -146
  21. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.c +146 -146
  22. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/spiess_florian.c +146 -146
  23. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/star.c +146 -146
  24. {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/PKG-INFO +11 -4
  25. {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/SOURCES.txt +7 -2
  26. edsger-0.1.3/docs/source/assets/dijkstra_benchmark_comparison.png +0 -0
  27. edsger-0.1.3/scripts/benchmark_comparison.py +0 -442
  28. edsger-0.1.3/src/edsger/_version.py +0 -1
  29. {edsger-0.1.3 → edsger-0.1.4}/.gitignore +0 -0
  30. {edsger-0.1.3 → edsger-0.1.4}/.pre-commit-config.yaml +0 -0
  31. {edsger-0.1.3 → edsger-0.1.4}/.readthedocs.yaml +0 -0
  32. {edsger-0.1.3 → edsger-0.1.4}/AUTHORS.rst +0 -0
  33. {edsger-0.1.3 → edsger-0.1.4}/CHANGELOG.rst +0 -0
  34. {edsger-0.1.3 → edsger-0.1.4}/CONTRIBUTING.rst +0 -0
  35. {edsger-0.1.3 → edsger-0.1.4}/LICENSE +0 -0
  36. {edsger-0.1.3 → edsger-0.1.4}/MANIFEST.in +0 -0
  37. {edsger-0.1.3 → edsger-0.1.4}/docs/Makefile +0 -0
  38. {edsger-0.1.3 → edsger-0.1.4}/docs/requirements.txt +0 -0
  39. {edsger-0.1.3 → edsger-0.1.4}/docs/source/api.md +0 -0
  40. {edsger-0.1.3 → edsger-0.1.4}/docs/source/conf.py +0 -0
  41. {edsger-0.1.3 → edsger-0.1.4}/docs/source/contributing.md +0 -0
  42. {edsger-0.1.3 → edsger-0.1.4}/docs/source/installation.md +0 -0
  43. {edsger-0.1.3 → edsger-0.1.4}/docs/source/quickstart.md +0 -0
  44. {edsger-0.1.3 → edsger-0.1.4}/pyproject.toml +0 -0
  45. {edsger-0.1.3 → edsger-0.1.4}/requirements-dev.txt +0 -0
  46. {edsger-0.1.3 → edsger-0.1.4}/requirements.txt +0 -0
  47. {edsger-0.1.3 → edsger-0.1.4}/setup.cfg +0 -0
  48. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/.gitignore +0 -0
  49. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/__init__.py +0 -0
  50. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/commons.pxd +0 -0
  51. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/commons.pyx +0 -0
  52. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/dijkstra.pyx +0 -0
  53. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/networks.py +0 -0
  54. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/path.py +0 -0
  55. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/path_tracking.pyx +0 -0
  56. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.pxd +0 -0
  57. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/pq_4ary_dec_0b.pyx +0 -0
  58. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/prefetch_compat.h +0 -0
  59. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/spiess_florian.pyx +0 -0
  60. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/star.pyx +0 -0
  61. {edsger-0.1.3 → edsger-0.1.4}/src/edsger/utils.py +0 -0
  62. {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/dependency_links.txt +0 -0
  63. {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/not-zip-safe +0 -0
  64. {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/requires.txt +0 -0
  65. {edsger-0.1.3 → edsger-0.1.4}/src/edsger.egg-info/top_level.txt +0 -0
  66. {edsger-0.1.3 → edsger-0.1.4}/tests/test_dijkstra.py +0 -0
  67. {edsger-0.1.3 → edsger-0.1.4}/tests/test_path.py +0 -0
  68. {edsger-0.1.3 → edsger-0.1.4}/tests/test_path_tracking.py +0 -0
  69. {edsger-0.1.3 → edsger-0.1.4}/tests/test_pq_4ary_dec_0b.py +0 -0
  70. {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
- 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,8 @@
1
1
  name: ci-publish # Publish Python distribution to PyPI
2
2
 
3
3
  on:
4
- push:
5
- tags:
6
- - 'v[0-9]+.[0-9]+.[0-9]+'
4
+ release:
5
+ types: [created]
7
6
 
8
7
  jobs:
9
8
 
@@ -49,7 +48,7 @@ jobs:
49
48
  runs-on: ${{ matrix.os }}
50
49
  strategy:
51
50
  matrix:
52
- os: [ubuntu-latest, windows-2019, macos-13, macos-14]
51
+ os: [ubuntu-latest, windows-2022, macos-13, macos-14]
53
52
 
54
53
  steps:
55
54
  - uses: actions/checkout@v4
@@ -1,9 +1,8 @@
1
1
  name: Run tests and upload coverage
2
2
 
3
3
  on:
4
- push:
5
- branches:
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
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
  ![Tests Status](https://github.com/aetperf/edsger/actions/workflows/tests.yml/badge.svg?branch=release)
44
44
  [![codecov](https://codecov.io/gh/aetperf/edsger/branch/release/graph/badge.svg)](https://codecov.io/gh/aetperf/edsger)
45
+ [![Documentation Status](https://readthedocs.org/projects/edsger/badge/?version=latest)](https://edsger.readthedocs.io/en/latest/?badge=latest)
45
46
  [![PyPI version](https://img.shields.io/pypi/v/edsger.svg?refresh=1)](https://pypi.org/project/edsger/)
46
47
  [![Downloads](https://static.pepy.tech/badge/edsger)](https://pepy.tech/project/edsger)
47
48
  [![Python 3.9 | 3.10 | 3.11 | 3.12 | 3.13](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/edsger/)
@@ -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
  ![Tests Status](https://github.com/aetperf/edsger/actions/workflows/tests.yml/badge.svg?branch=release)
3
3
  [![codecov](https://codecov.io/gh/aetperf/edsger/branch/release/graph/badge.svg)](https://codecov.io/gh/aetperf/edsger)
4
+ [![Documentation Status](https://readthedocs.org/projects/edsger/badge/?version=latest)](https://edsger.readthedocs.io/en/latest/?badge=latest)
4
5
  [![PyPI version](https://img.shields.io/pypi/v/edsger.svg?refresh=1)](https://pypi.org/project/edsger/)
5
6
  [![Downloads](https://static.pepy.tech/badge/edsger)](https://pepy.tech/project/edsger)
6
7
  [![Python 3.9 | 3.10 | 3.11 | 3.12 | 3.13](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/edsger/)
@@ -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).
@@ -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()