edsger 0.1.3__cp312-cp312-win_amd64.whl → 0.1.5__cp312-cp312-win_amd64.whl

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.
Binary file
Binary file
Binary file
Binary file
edsger/utils.py CHANGED
@@ -6,7 +6,15 @@ import numpy as np
6
6
  import pandas as pd
7
7
 
8
8
 
9
- def generate_random_network(n_edges=100, n_verts=20, seed=124, sort=True):
9
+ def generate_random_network(
10
+ n_edges=100,
11
+ n_verts=20,
12
+ seed=124,
13
+ sort=True,
14
+ allow_negative_weights=False,
15
+ negative_weight_ratio=0.3,
16
+ weight_range=(0.1, 1.0),
17
+ ):
10
18
  """
11
19
  Generate a random network with a specified number of edges and vertices.
12
20
 
@@ -20,6 +28,14 @@ def generate_random_network(n_edges=100, n_verts=20, seed=124, sort=True):
20
28
  The seed for the random number generator. Default is 124.
21
29
  sort : bool, optional
22
30
  Whether to sort the edges by tail and head vertices. Default is True.
31
+ allow_negative_weights : bool, optional
32
+ Whether to allow negative edge weights. Default is False (positive weights only).
33
+ negative_weight_ratio : float, optional
34
+ Proportion of edges that should have negative weights when allow_negative_weights=True.
35
+ Must be between 0.0 and 1.0. Default is 0.3 (30% negative).
36
+ weight_range : tuple of float, optional
37
+ Range of absolute values for weights as (min, max). Default is (0.1, 1.0).
38
+ When allow_negative_weights=True, negative weights will be in range (-max, -min).
23
39
 
24
40
  Returns
25
41
  -------
@@ -28,6 +44,8 @@ def generate_random_network(n_edges=100, n_verts=20, seed=124, sort=True):
28
44
 
29
45
  Examples
30
46
  --------
47
+ Generate a graph with positive weights only (default):
48
+
31
49
  >>> generate_random_network(n_edges=5, n_verts=3, seed=42)
32
50
  tail head weight
33
51
  0 0 2 0.975622
@@ -36,20 +54,66 @@ def generate_random_network(n_edges=100, n_verts=20, seed=124, sort=True):
36
54
  3 1 2 0.786064
37
55
  4 2 0 0.761140
38
56
 
57
+ Generate a graph with mixed positive and negative weights:
58
+
59
+ >>> generate_random_network(n_edges=5, n_verts=3, seed=42,
60
+ ... allow_negative_weights=True, negative_weight_ratio=0.4)
61
+ tail head weight
62
+ 0 0 2 0.975622
63
+ 1 1 0 -0.128114
64
+ 2 1 0 0.450386
65
+ 3 1 2 -0.786064
66
+ 4 2 0 0.761140
39
67
 
40
68
  Notes
41
69
  -----
42
70
  The 'tail' and 'head' columns represent the source and destination vertices of each edge,
43
- respectively. The 'weight' column represents the weight of each edge, which is a random
44
- float between 0 and 1.
71
+ respectively. The 'weight' column represents the weight of each edge.
72
+
73
+ When allow_negative_weights=False (default), weights are random floats between
74
+ weight_range[0] and weight_range[1].
75
+
76
+ When allow_negative_weights=True, approximately negative_weight_ratio proportion of edges
77
+ will have negative weights, useful for testing algorithms like Bellman-Ford that support
78
+ negative edge weights.
45
79
 
46
80
  If `sort` is True, the DataFrame is sorted by the 'tail' and 'head' columns and the index
47
81
  is reset.
48
82
  """
83
+ # Validate parameters
84
+ if not 0.0 <= negative_weight_ratio <= 1.0:
85
+ raise ValueError("negative_weight_ratio must be between 0.0 and 1.0")
86
+ if (
87
+ len(weight_range) != 2
88
+ or weight_range[0] <= 0
89
+ or weight_range[1] <= weight_range[0]
90
+ ):
91
+ raise ValueError("weight_range must be (min, max) with 0 < min < max")
92
+
49
93
  rng = np.random.default_rng(seed=seed)
50
94
  tail = rng.integers(low=0, high=n_verts, size=n_edges)
51
95
  head = rng.integers(low=0, high=n_verts, size=n_edges)
52
- weight = rng.random(size=n_edges)
96
+
97
+ # Generate weights
98
+ if allow_negative_weights:
99
+ # Generate weights in the specified range
100
+ weight = rng.uniform(low=weight_range[0], high=weight_range[1], size=n_edges)
101
+
102
+ # Randomly select edges to have negative weights
103
+ n_negative = int(n_edges * negative_weight_ratio)
104
+ if n_negative > 0:
105
+ negative_indices = rng.choice(n_edges, size=n_negative, replace=False)
106
+ weight[negative_indices] *= -1
107
+ else:
108
+ # Original behavior: positive weights in range [weight_range[0], weight_range[1]]
109
+ if weight_range == (0.1, 1.0):
110
+ # Keep backward compatibility for default case
111
+ weight = rng.random(size=n_edges)
112
+ else:
113
+ weight = rng.uniform(
114
+ low=weight_range[0], high=weight_range[1], size=n_edges
115
+ )
116
+
53
117
  edges = pd.DataFrame(data={"tail": tail, "head": head, "weight": weight})
54
118
  if sort:
55
119
  edges.sort_values(by=["tail", "head"], inplace=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edsger
3
- Version: 0.1.3
3
+ Version: 0.1.5
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/)
@@ -53,7 +54,7 @@ Dynamic: license-file
53
54
 
54
55
  *Graph algorithms in Cython*
55
56
 
56
- 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.
57
+ Welcome to our Python library for graph algorithms. The library includes both Dijkstra's and Bellman-Ford's algorithms, with plans to add more 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.
57
58
 
58
59
  Documentation : [https://edsger.readthedocs.io/en/latest/](https://edsger.readthedocs.io/en/latest/)
59
60
 
@@ -97,14 +98,77 @@ 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
+ ## Bellman-Ford Algorithm: Handling Negative Weights
102
+
103
+ The Bellman-Ford algorithm can handle graphs with negative edge weights and detect negative cycles, making it suitable for more complex scenarios than Dijkstra's algorithm.
104
+
105
+ ```python
106
+ from edsger.path import BellmanFord
107
+
108
+ # Create a graph with negative weights
109
+ edges_negative = pd.DataFrame({
110
+ 'tail': [0, 0, 1, 1, 2, 3],
111
+ 'head': [1, 2, 2, 3, 3, 4],
112
+ 'weight': [1, 4, -2, 5, 1, 3] # Note the negative weight
113
+ })
114
+ edges_negative
115
+ ```
116
+
117
+ | | tail | head | weight |
118
+ |---:|-------:|-------:|---------:|
119
+ | 0 | 0 | 1 | 1.0 |
120
+ | 1 | 0 | 2 | 4.0 |
121
+ | 2 | 1 | 2 | -2.0 |
122
+ | 3 | 1 | 3 | 5.0 |
123
+ | 4 | 2 | 3 | 1.0 |
124
+ | 5 | 3 | 4 | 3.0 |
125
+
126
+ ```python
127
+ # Initialize and run Bellman-Ford
128
+ bf = BellmanFord(edges_negative)
129
+ shortest_paths = bf.run(vertex_idx=0)
130
+ print("Shortest paths:", shortest_paths)
131
+ ```
132
+
133
+ Shortest paths: [ 0. 1. -1. 0. 3.]
134
+
135
+ The Bellman-Ford algorithm finds the optimal path even with negative weights. In this example, the shortest path from node 0 to node 2 has length -1 (going 0→1→2 with weights 1 + (-2) = -1), which is shorter than the direct path 0→2 with weight 4.
136
+
137
+ ### Negative Cycle Detection
138
+
139
+ Bellman-Ford can also detect negative cycles, which indicate that no shortest path exists:
140
+
141
+ ```python
142
+ # Create a graph with a negative cycle
143
+ edges_cycle = pd.DataFrame({
144
+ 'tail': [0, 1, 2],
145
+ 'head': [1, 2, 0],
146
+ 'weight': [1, -2, -1] # Cycle 0→1→2→0 has total weight -2
147
+ })
148
+
149
+ bf_cycle = BellmanFord(edges_cycle)
150
+ try:
151
+ bf_cycle.run(vertex_idx=0)
152
+ except ValueError as e:
153
+ print("Error:", e)
154
+ ```
155
+
156
+ Error: Negative cycle detected in the graph
157
+
158
+ ## Installation
159
+
160
+ ### Standard Installation
161
+
162
+ ```bash
163
+ pip install edsger
164
+ ```
165
+
100
166
  ## Why Use Edsger?
101
167
 
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:
168
+ 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
169
 
104
170
  <img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
105
171
 
106
- *Benchmark performed on Intel i9-12900H Linux laptop.*
107
-
108
172
  ## Contributing
109
173
 
110
174
  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).
@@ -0,0 +1,29 @@
1
+ edsger/.gitignore,sha256=mr9Izcwvjgv215xjRKhWEZ7vsyrKWhMqvWjSLHRYDjk,13
2
+ edsger/__init__.py,sha256=lgtGe3cqdwWdO1DLEOx7fX3i8D4Z_2rXHSq7Xecf-NM,41
3
+ edsger/_version.py,sha256=Nmswip0IUvJenHIhdfSyTYurDcwWTvOQ8mPDREtwE1o,21
4
+ edsger/bellman_ford.cp312-win_amd64.pyd,sha256=5drEeO6adIiCHaN1sJTpc8XWJMU4coAl5W04auzKw1M,190464
5
+ edsger/bellman_ford.pyx,sha256=Wep9yc2G6f14IqwFvedSApZGreQIoBLPEXLHGgXOHk8,17428
6
+ edsger/commons.cp312-win_amd64.pyd,sha256=tZSV5QtdAy9n2V0GasErZFy9EDisSSW9gFotAFuBTJU,23552
7
+ edsger/commons.pxd,sha256=UshKjr5ve3Or9u75xGyGPKRz1RwCCb5N-xgNevXZ4j4,464
8
+ edsger/commons.pyx,sha256=6Ze22eE_zwXPRAe550DEhEvu-b7hvKmwQu73rzzWMUE,839
9
+ edsger/dijkstra.cp312-win_amd64.pyd,sha256=IV7ZeAEw9prZh0HaEfTOP8dx6Ex93oEm1VKzaFnzRPY,257536
10
+ edsger/dijkstra.pyx,sha256=kBXFya0bugjp97xas145sZEUXtb89_Sg9v8IdWiURoE,37542
11
+ edsger/networks.py,sha256=hH9sgT5Ic4TLVCjxPNzMDWNjNDbqpXMxXxLeWxCpdLE,10730
12
+ edsger/path.py,sha256=P9d4uvz-jn9y8I3vncB7HjUluvRGAsdYvgqeQVbT8ew,58809
13
+ edsger/path_tracking.cp312-win_amd64.pyd,sha256=oVp9b8-yD2O_Hjbl4CsE0ILzJydNs12xjUe66U9t11Q,140800
14
+ edsger/path_tracking.pyx,sha256=H24TLmC53I8LjbM1S5E7gS8WEb5uE_PZ8nhG6BteMYA,1900
15
+ edsger/pq_4ary_dec_0b.cp312-win_amd64.pyd,sha256=9_0TXvdcy4RvVmtMiIz7y2yFso4NEz1PlioL6oCx0CY,167936
16
+ edsger/pq_4ary_dec_0b.pxd,sha256=VvXcQzJq3OGBptrbawtemagPimuqSCayGQ91Jrad894,1098
17
+ edsger/pq_4ary_dec_0b.pyx,sha256=IzvzQerf-LYy7weQpgI0f28Q8gUrR4ENaedekXs1Jeg,18486
18
+ edsger/prefetch_compat.h,sha256=AyAYq_ZHKk5ChaJDrZOAOYe6SprL0_2byjRbjcBGrsU,826
19
+ edsger/spiess_florian.cp312-win_amd64.pyd,sha256=86picdlJEqpHZTrMGqP9Q48kp2Ko0veDKFeA28t9eWI,198656
20
+ edsger/spiess_florian.pyx,sha256=tjfF9Iv8nqpp4lnv4KAjF-37ij0_SgQ0fnacVVKx-CE,9934
21
+ edsger/star.cp312-win_amd64.pyd,sha256=U_eWZ_zyyC5LL3-OoAxaQcZLM6e9wfPbg3RZz2iLbDA,185344
22
+ edsger/star.pyx,sha256=9LAIXhlccEeDgT41ico7n57FJ7PKCzhPv4f22Lphn78,9602
23
+ edsger/utils.py,sha256=I1HJtmbnu9XWiY0rstM_D0NCX0bPjdtK-BeIAXwYjO4,4728
24
+ edsger-0.1.5.dist-info/licenses/AUTHORS.rst,sha256=8udN6bgZHdHYcVzV38y6SPnF-x6Ks0uXxxFsic6Aces,110
25
+ edsger-0.1.5.dist-info/licenses/LICENSE,sha256=w7gRlruGxK3_4KTZAyJsOR2tML4UQgB-GNm2LerwJS0,1132
26
+ edsger-0.1.5.dist-info/METADATA,sha256=ZROC5X1uwWzhe_Q68ASsHpKXZKbBX-MPk6ljEk3BeV4,7293
27
+ edsger-0.1.5.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
28
+ edsger-0.1.5.dist-info/top_level.txt,sha256=QvhzFORJIIot6GzSnDrtGa9KQt9iifCbOC5ULlzY5dg,7
29
+ edsger-0.1.5.dist-info/RECORD,,
@@ -1,27 +0,0 @@
1
- edsger/.gitignore,sha256=mr9Izcwvjgv215xjRKhWEZ7vsyrKWhMqvWjSLHRYDjk,13
2
- edsger/__init__.py,sha256=lgtGe3cqdwWdO1DLEOx7fX3i8D4Z_2rXHSq7Xecf-NM,41
3
- edsger/_version.py,sha256=R5TtpJu7Qu6sOarfDpp-5Oyy8Pi2Ir3VewCvsCQiAgo,21
4
- edsger/commons.cp312-win_amd64.pyd,sha256=hvbl0R_hhQFu1mApSWr8-xQZj1U6rHtgUfv8HVK3vMs,23040
5
- edsger/commons.pxd,sha256=UshKjr5ve3Or9u75xGyGPKRz1RwCCb5N-xgNevXZ4j4,464
6
- edsger/commons.pyx,sha256=6Ze22eE_zwXPRAe550DEhEvu-b7hvKmwQu73rzzWMUE,839
7
- edsger/dijkstra.cp312-win_amd64.pyd,sha256=Rl3PRWKmJbEstNfikjYAjMXUdG3sAWTxloppFz7RKps,252928
8
- edsger/dijkstra.pyx,sha256=kBXFya0bugjp97xas145sZEUXtb89_Sg9v8IdWiURoE,37542
9
- edsger/networks.py,sha256=hH9sgT5Ic4TLVCjxPNzMDWNjNDbqpXMxXxLeWxCpdLE,10730
10
- edsger/path.py,sha256=2NtkhwN2HQUsoZn0Sl6UbFKWIcWVTvnE6D8IH-xEG88,35768
11
- edsger/path_tracking.cp312-win_amd64.pyd,sha256=iPjPfWZn-79UB2l-KG9An04EzdOXGt80Z10iPO3JjHw,135168
12
- edsger/path_tracking.pyx,sha256=H24TLmC53I8LjbM1S5E7gS8WEb5uE_PZ8nhG6BteMYA,1900
13
- edsger/pq_4ary_dec_0b.cp312-win_amd64.pyd,sha256=apkpbNPgWBEziUn9TAqEPI3Unqgee-5esj3LHfUBQ4Y,163328
14
- edsger/pq_4ary_dec_0b.pxd,sha256=VvXcQzJq3OGBptrbawtemagPimuqSCayGQ91Jrad894,1098
15
- edsger/pq_4ary_dec_0b.pyx,sha256=IzvzQerf-LYy7weQpgI0f28Q8gUrR4ENaedekXs1Jeg,18486
16
- edsger/prefetch_compat.h,sha256=AyAYq_ZHKk5ChaJDrZOAOYe6SprL0_2byjRbjcBGrsU,826
17
- edsger/spiess_florian.cp312-win_amd64.pyd,sha256=YUe6UD3_VljB2-8sx6mj1BwdK1Bj25Kb9FHxZYnUlhc,192512
18
- edsger/spiess_florian.pyx,sha256=tjfF9Iv8nqpp4lnv4KAjF-37ij0_SgQ0fnacVVKx-CE,9934
19
- edsger/star.cp312-win_amd64.pyd,sha256=eDmVyKxhYkQK7YaY65GP6EjVdzhS55FbAG4hNIb-MXc,181760
20
- edsger/star.pyx,sha256=9LAIXhlccEeDgT41ico7n57FJ7PKCzhPv4f22Lphn78,9602
21
- edsger/utils.py,sha256=xYfOFIbYpAiZljhUOgGWy0TVNvWaMFCwbCLPBkzdVos,2097
22
- edsger-0.1.3.dist-info/licenses/AUTHORS.rst,sha256=8udN6bgZHdHYcVzV38y6SPnF-x6Ks0uXxxFsic6Aces,110
23
- edsger-0.1.3.dist-info/licenses/LICENSE,sha256=w7gRlruGxK3_4KTZAyJsOR2tML4UQgB-GNm2LerwJS0,1132
24
- edsger-0.1.3.dist-info/METADATA,sha256=pd11DVCVJaNAwtzqYHZQu3HffnIgBUmwWwfuzgNTAF0,5314
25
- edsger-0.1.3.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
26
- edsger-0.1.3.dist-info/top_level.txt,sha256=QvhzFORJIIot6GzSnDrtGa9KQt9iifCbOC5ULlzY5dg,7
27
- edsger-0.1.3.dist-info/RECORD,,
File without changes