edsger 0.1.4__cp39-cp39-macosx_11_0_arm64.whl → 0.1.5__cp39-cp39-macosx_11_0_arm64.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
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.4
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>
@@ -54,7 +54,7 @@ Dynamic: license-file
54
54
 
55
55
  *Graph algorithms in Cython*
56
56
 
57
- 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.
58
58
 
59
59
  Documentation : [https://edsger.readthedocs.io/en/latest/](https://edsger.readthedocs.io/en/latest/)
60
60
 
@@ -98,6 +98,63 @@ print("Shortest paths:", shortest_paths)
98
98
 
99
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.
100
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
+
101
158
  ## Installation
102
159
 
103
160
  ### Standard Installation
@@ -0,0 +1,36 @@
1
+ edsger-0.1.5.dist-info/RECORD,,
2
+ edsger-0.1.5.dist-info/WHEEL,sha256=XDdnntmiCRXNVLdY5WCtwp2KApIYJ413gXaKlPbYNQ4,107
3
+ edsger-0.1.5.dist-info/top_level.txt,sha256=QvhzFORJIIot6GzSnDrtGa9KQt9iifCbOC5ULlzY5dg,7
4
+ edsger-0.1.5.dist-info/METADATA,sha256=LF7X-0Qgi0OuVqY_I_uNsKr5xfCnPHYbOJ-AuPXN9jY,7111
5
+ edsger-0.1.5.dist-info/licenses/LICENSE,sha256=eNjfz5CInLrVdczJbhazCKtb8-0qB0UaXZ3bXN0zio0,1111
6
+ edsger-0.1.5.dist-info/licenses/AUTHORS.rst,sha256=9lqpqjiC4XukK7jdxXwKJJrddqwCV2DjpYTqhpP6znA,105
7
+ edsger/pq_4ary_dec_0b.cpython-39-darwin.so,sha256=KBapk-RHR0ZbUGsrl6KDS_6U5buNpFplsTtLXJXvwik,214064
8
+ edsger/spiess_florian.c,sha256=wQyfpksgmcZzf6VAdR2Ph6hm8aAOleVcWBg9T16XGuw,1357417
9
+ edsger/star.cpython-39-darwin.so,sha256=lJ3C8e0m_AUUMQh8wB8NrS3jf-zvFOp1VuFxEb2jUto,250632
10
+ edsger/dijkstra.c,sha256=kH_Ju0EImW1luQT8bxqhGqAC7PU097FsY5q_-6z0yvs,1659045
11
+ edsger/bellman_ford.pyx,sha256=swabcGIjYIeFBud5VdlPAQENv3_AXULnSNLXaQSpE3U,16884
12
+ edsger/_version.py,sha256=Nmswip0IUvJenHIhdfSyTYurDcwWTvOQ8mPDREtwE1o,21
13
+ edsger/bellman_ford.c,sha256=XCWqoHn6nRg9P020OzZouoP3fXWm0WiRXQ5G9IM4xV4,1375199
14
+ edsger/bellman_ford.cpython-39-darwin.so,sha256=MAFuGT3-wBBaxDVH9zV2SXKNY82-qvpH7aYTg_ihCnU,250608
15
+ edsger/commons.cpython-39-darwin.so,sha256=wAtI48V0vtN0w_dD7wet3nwRPPhbcex_xE5uirI8YvI,58320
16
+ edsger/commons.c,sha256=BI6WflbINubjwvuP4bfBewI643QqhmhNXeTu99NQ3Lk,344118
17
+ edsger/star.c,sha256=dScC5nFP98RHwAaJolvFHNYh2AxtjeAHkCtH15J8B_A,1320715
18
+ edsger/commons.pyx,sha256=rPWrq1zfN_IuTgLgvvQe8ma7bU1e3V8lWbBEFH5Vaw8,805
19
+ edsger/pq_4ary_dec_0b.pxd,sha256=MbQuP1y1t6_2fJRkpqRPV2lmatwfqOxjsrb1GLfEudA,1065
20
+ edsger/__init__.py,sha256=CoqO_GHq5NC94S2JrYjf6dVGOKnxJ2TRBDOjikyscOY,40
21
+ edsger/prefetch_compat.h,sha256=6HfoyHI0dQE_MDsYHjUiX77Hg_z8eH6kRwN_5RAhbYo,806
22
+ edsger/path_tracking.c,sha256=xx4mSqJzy7STckpsGwCeeglmlkIWtHCkDESecJzMOZw,1145581
23
+ edsger/spiess_florian.pyx,sha256=9CrcMyzwSXaC_EdSl1uiD9yhFH6ACtooZOMfYw6vhvA,9566
24
+ edsger/pq_4ary_dec_0b.c,sha256=scRN-rbG_7k1-s0ZOfkrI_F-ZoPBziIabL5kLNURKwE,1368893
25
+ edsger/networks.py,sha256=o_dKC6fcxsqWr3fE0m5sUQkpZO-0B2x-w51l_1fyzAc,10316
26
+ edsger/spiess_florian.cpython-39-darwin.so,sha256=JlhMXaZOLJQBv1f3PaqyQ_dp91GRgpyYDTELKvukVoM,276208
27
+ edsger/commons.pxd,sha256=P9-n7ChbSIMSBRRY_lY3gBwXYUSvqJG-S_6e0dZTYU8,439
28
+ edsger/.gitignore,sha256=tWvEr3sBkY2ODntRp4_IUz3cidH-xu1efiOQK9DfK04,12
29
+ edsger/utils.py,sha256=C38-cgHUPf5XEjT7359vu5xO8hl-fYAZsYL_mcp8Gts,4601
30
+ edsger/pq_4ary_dec_0b.pyx,sha256=fRJCT79GsCocZgpostfn7t5nkNQALK0A93OJK4fxJR0,17796
31
+ edsger/dijkstra.cpython-39-darwin.so,sha256=bVkXeML0_BzrBTK9eEQtZsJ8_PKuBUBWhxUGElzL9cc,366848
32
+ edsger/path_tracking.cpython-39-darwin.so,sha256=bXfHj4JeBGWMdYej2OOGzoqa6CVGq-IJ311f4FUKgPw,191424
33
+ edsger/path_tracking.pyx,sha256=uO06fzL8V5KeI3w5FPVJJ13ZtdFGTol6oUjVX4Jjxxs,1807
34
+ edsger/path.py,sha256=MJTK4QJKfJnX0FjRC8HnDm0RDOKcoAOdY1a6mQq5LpU,57316
35
+ edsger/dijkstra.pyx,sha256=R8DM9LlHXUOZ38t7qINaB1t_QBP_aeWBOqIHHVN9W10,36513
36
+ edsger/star.pyx,sha256=MVS4kylmMAXOHm8liTfTMLzrr0jhA3cRU3-KCfVjNNM,9246
@@ -1,33 +0,0 @@
1
- edsger-0.1.4.dist-info/RECORD,,
2
- edsger-0.1.4.dist-info/WHEEL,sha256=XDdnntmiCRXNVLdY5WCtwp2KApIYJ413gXaKlPbYNQ4,107
3
- edsger-0.1.4.dist-info/top_level.txt,sha256=QvhzFORJIIot6GzSnDrtGa9KQt9iifCbOC5ULlzY5dg,7
4
- edsger-0.1.4.dist-info/METADATA,sha256=W7VnBFB3jCX77cLnj1naJ-1BGzYatHsQ2DGoBk6Gbkk,5369
5
- edsger-0.1.4.dist-info/licenses/LICENSE,sha256=eNjfz5CInLrVdczJbhazCKtb8-0qB0UaXZ3bXN0zio0,1111
6
- edsger-0.1.4.dist-info/licenses/AUTHORS.rst,sha256=9lqpqjiC4XukK7jdxXwKJJrddqwCV2DjpYTqhpP6znA,105
7
- edsger/pq_4ary_dec_0b.cpython-39-darwin.so,sha256=sx1lpHLz1DORDUoJZyFSAI2A69gx2ukh4-JoJw4rsnw,214288
8
- edsger/spiess_florian.c,sha256=aVvOV6mXXVh1Y1P8SULz6eeDbWEjeZPfixSgqo6rD8s,1356767
9
- edsger/star.cpython-39-darwin.so,sha256=Dm1dGR2IE0_2z8L8F6iGoQH9mwC_eaID8WOtMcE6N38,250856
10
- edsger/dijkstra.c,sha256=Q_UBk6FrP522-dtK9rlMLGn25GN84U9zzJtIir4SbWI,1658395
11
- edsger/_version.py,sha256=JMD28FXYHc_TM03visyUSd3UA9FZAaJMRStnfZoq50Y,21
12
- edsger/commons.cpython-39-darwin.so,sha256=7eEpHoASKIXL9k9vjqacbi5PGZyPxkU3ZYcoe18Oy9g,58320
13
- edsger/commons.c,sha256=yrmGrwzMWR8AhD2H4ok6IOG5Cy5UumX0K2fPs8oOpxs,344001
14
- edsger/star.c,sha256=y9A5NMUSSbzcoU6sgiRMIOesSG6yD41zqbt7vu_btTA,1320065
15
- edsger/commons.pyx,sha256=rPWrq1zfN_IuTgLgvvQe8ma7bU1e3V8lWbBEFH5Vaw8,805
16
- edsger/pq_4ary_dec_0b.pxd,sha256=MbQuP1y1t6_2fJRkpqRPV2lmatwfqOxjsrb1GLfEudA,1065
17
- edsger/__init__.py,sha256=CoqO_GHq5NC94S2JrYjf6dVGOKnxJ2TRBDOjikyscOY,40
18
- edsger/prefetch_compat.h,sha256=6HfoyHI0dQE_MDsYHjUiX77Hg_z8eH6kRwN_5RAhbYo,806
19
- edsger/path_tracking.c,sha256=fM7_EKD8CHaZKhvx1LA5lNAaehGTUQ_3BGuOVaeq-C4,1144931
20
- edsger/spiess_florian.pyx,sha256=9CrcMyzwSXaC_EdSl1uiD9yhFH6ACtooZOMfYw6vhvA,9566
21
- edsger/pq_4ary_dec_0b.c,sha256=Q3o5Ugg-2pE6V2BnHlqSONCHHxhLD8fWQ01UGeN0IpU,1368243
22
- edsger/networks.py,sha256=o_dKC6fcxsqWr3fE0m5sUQkpZO-0B2x-w51l_1fyzAc,10316
23
- edsger/spiess_florian.cpython-39-darwin.so,sha256=Hko2GDbjzGFLpG0HmefcpMfHto1q_gg38drvIdY4Z24,276432
24
- edsger/commons.pxd,sha256=P9-n7ChbSIMSBRRY_lY3gBwXYUSvqJG-S_6e0dZTYU8,439
25
- edsger/.gitignore,sha256=tWvEr3sBkY2ODntRp4_IUz3cidH-xu1efiOQK9DfK04,12
26
- edsger/utils.py,sha256=igl2xDpkjTuQ5fhDdynQ0ekMSva52BstrTsZaTtE2RI,2034
27
- edsger/pq_4ary_dec_0b.pyx,sha256=fRJCT79GsCocZgpostfn7t5nkNQALK0A93OJK4fxJR0,17796
28
- edsger/dijkstra.cpython-39-darwin.so,sha256=axuS7R8dp8-ACY24qhQ_a-X5ua27kdbi89MQdk8aL3o,367072
29
- edsger/path_tracking.cpython-39-darwin.so,sha256=lwZh7ssWqVk3V6GGcDzM0xJS7hrBcfqXWj2kpGjzRj4,191648
30
- edsger/path_tracking.pyx,sha256=uO06fzL8V5KeI3w5FPVJJ13ZtdFGTol6oUjVX4Jjxxs,1807
31
- edsger/path.py,sha256=4hwoXiaepp_twwEQMmlygFsYy1HcxtZmR1HSvNStUC4,34874
32
- edsger/dijkstra.pyx,sha256=R8DM9LlHXUOZ38t7qINaB1t_QBP_aeWBOqIHHVN9W10,36513
33
- edsger/star.pyx,sha256=MVS4kylmMAXOHm8liTfTMLzrr0jhA3cRU3-KCfVjNNM,9246
File without changes