edsger 0.1.4__cp311-cp311-win32.whl → 0.1.5__cp311-cp311-win32.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.
- edsger/_version.py +1 -1
- edsger/bellman_ford.cp311-win32.pyd +0 -0
- edsger/bellman_ford.pyx +544 -0
- edsger/commons.cp311-win32.pyd +0 -0
- edsger/dijkstra.cp311-win32.pyd +0 -0
- edsger/path.py +622 -23
- edsger/path_tracking.cp311-win32.pyd +0 -0
- edsger/pq_4ary_dec_0b.cp311-win32.pyd +0 -0
- edsger/spiess_florian.cp311-win32.pyd +0 -0
- edsger/star.cp311-win32.pyd +0 -0
- edsger/utils.py +68 -4
- {edsger-0.1.4.dist-info → edsger-0.1.5.dist-info}/METADATA +59 -2
- edsger-0.1.5.dist-info/RECORD +29 -0
- edsger-0.1.4.dist-info/RECORD +0 -27
- {edsger-0.1.4.dist-info → edsger-0.1.5.dist-info}/WHEEL +0 -0
- {edsger-0.1.4.dist-info → edsger-0.1.5.dist-info}/licenses/AUTHORS.rst +0 -0
- {edsger-0.1.4.dist-info → edsger-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {edsger-0.1.4.dist-info → edsger-0.1.5.dist-info}/top_level.txt +0 -0
edsger/_version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.
|
1
|
+
__version__ = "0.1.5"
|
Binary file
|
edsger/bellman_ford.pyx
ADDED
@@ -0,0 +1,544 @@
|
|
1
|
+
"""
|
2
|
+
An implementation of the Bellman-Ford algorithm.
|
3
|
+
|
4
|
+
cpdef functions:
|
5
|
+
|
6
|
+
- compute_bf_sssp
|
7
|
+
Compute single-source shortest path (from one vertex to all vertices). Does
|
8
|
+
not return predecessors. Supports negative weights.
|
9
|
+
- compute_bf_sssp_w_path
|
10
|
+
Compute single-source shortest path (from one vertex to all vertices).
|
11
|
+
Compute predecessors. Supports negative weights.
|
12
|
+
- compute_bf_stsp
|
13
|
+
Compute single-target shortest path (from all vertices to one vertex). Does
|
14
|
+
not return successors. Supports negative weights.
|
15
|
+
- compute_bf_stsp_w_path
|
16
|
+
Compute single-target shortest path (from all vertices to one vertex).
|
17
|
+
Compute successors. Supports negative weights.
|
18
|
+
- detect_negative_cycle
|
19
|
+
Detect negative cycles in the graph.
|
20
|
+
"""
|
21
|
+
|
22
|
+
cimport numpy as cnp
|
23
|
+
import numpy as np
|
24
|
+
|
25
|
+
from edsger.commons cimport DTYPE_INF, DTYPE_t
|
26
|
+
from edsger.commons import DTYPE_PY
|
27
|
+
|
28
|
+
|
29
|
+
cpdef cnp.ndarray compute_bf_sssp(
|
30
|
+
cnp.uint32_t[::1] csr_indptr,
|
31
|
+
cnp.uint32_t[::1] csr_indices,
|
32
|
+
DTYPE_t[::1] csr_data,
|
33
|
+
int source_vert_idx,
|
34
|
+
int vertex_count):
|
35
|
+
"""
|
36
|
+
Compute single-source shortest path using Bellman-Ford algorithm.
|
37
|
+
|
38
|
+
From one vertex to all vertices. Does not return predecessors.
|
39
|
+
Supports negative edge weights.
|
40
|
+
|
41
|
+
Parameters
|
42
|
+
----------
|
43
|
+
csr_indices : cnp.uint32_t[::1]
|
44
|
+
indices in the CSR format
|
45
|
+
csr_indptr : cnp.uint32_t[::1]
|
46
|
+
pointers in the CSR format
|
47
|
+
csr_data : DTYPE_t[::1]
|
48
|
+
data (edge weights) in the CSR format
|
49
|
+
source_vert_idx : int
|
50
|
+
source vertex index
|
51
|
+
vertex_count : int
|
52
|
+
vertex count
|
53
|
+
|
54
|
+
Returns
|
55
|
+
-------
|
56
|
+
path_lengths : cnp.ndarray
|
57
|
+
shortest path length for each vertex
|
58
|
+
"""
|
59
|
+
|
60
|
+
cdef:
|
61
|
+
size_t tail_vert_idx, head_vert_idx, idx, v
|
62
|
+
DTYPE_t tail_vert_val, new_dist
|
63
|
+
cnp.ndarray[DTYPE_t, ndim=1] dist = np.full(
|
64
|
+
vertex_count, DTYPE_INF, dtype=DTYPE_PY)
|
65
|
+
size_t source = <size_t>source_vert_idx
|
66
|
+
bint changed
|
67
|
+
|
68
|
+
# Initialize source distance
|
69
|
+
dist[source] = 0.0
|
70
|
+
|
71
|
+
with nogil:
|
72
|
+
# Relax edges V-1 times
|
73
|
+
for v in range(<size_t>(vertex_count - 1)):
|
74
|
+
changed = False
|
75
|
+
|
76
|
+
# Iterate through all vertices
|
77
|
+
for tail_vert_idx in range(<size_t>vertex_count):
|
78
|
+
tail_vert_val = dist[tail_vert_idx]
|
79
|
+
|
80
|
+
# Skip if vertex is unreachable
|
81
|
+
if tail_vert_val == DTYPE_INF:
|
82
|
+
continue
|
83
|
+
|
84
|
+
# Relax edges from this vertex
|
85
|
+
for idx in range(<size_t>csr_indptr[tail_vert_idx],
|
86
|
+
<size_t>csr_indptr[tail_vert_idx + 1]):
|
87
|
+
|
88
|
+
head_vert_idx = <size_t>csr_indices[idx]
|
89
|
+
new_dist = tail_vert_val + csr_data[idx]
|
90
|
+
|
91
|
+
if dist[head_vert_idx] > new_dist:
|
92
|
+
dist[head_vert_idx] = new_dist
|
93
|
+
changed = True
|
94
|
+
|
95
|
+
# Early termination if no changes
|
96
|
+
if not changed:
|
97
|
+
break
|
98
|
+
|
99
|
+
return dist
|
100
|
+
|
101
|
+
|
102
|
+
cpdef cnp.ndarray compute_bf_sssp_w_path(
|
103
|
+
cnp.uint32_t[::1] csr_indptr,
|
104
|
+
cnp.uint32_t[::1] csr_indices,
|
105
|
+
DTYPE_t[::1] csr_data,
|
106
|
+
cnp.uint32_t[::1] predecessor,
|
107
|
+
int source_vert_idx,
|
108
|
+
int vertex_count):
|
109
|
+
"""
|
110
|
+
Compute single-source shortest path using Bellman-Ford algorithm.
|
111
|
+
|
112
|
+
From one vertex to all vertices. Compute predecessors.
|
113
|
+
Supports negative edge weights.
|
114
|
+
|
115
|
+
Parameters
|
116
|
+
----------
|
117
|
+
csr_indices : cnp.uint32_t[::1]
|
118
|
+
indices in the CSR format
|
119
|
+
csr_indptr : cnp.uint32_t[::1]
|
120
|
+
pointers in the CSR format
|
121
|
+
csr_data : DTYPE_t[::1]
|
122
|
+
data (edge weights) in the CSR format
|
123
|
+
predecessor : cnp.uint32_t[::1]
|
124
|
+
array of indices, one for each vertex of the graph. Each vertex'
|
125
|
+
entry contains the index of its predecessor in a path from the
|
126
|
+
source, through the graph.
|
127
|
+
source_vert_idx : int
|
128
|
+
source vertex index
|
129
|
+
vertex_count : int
|
130
|
+
vertex count
|
131
|
+
|
132
|
+
Returns
|
133
|
+
-------
|
134
|
+
path_lengths : cnp.ndarray
|
135
|
+
shortest path length for each vertex
|
136
|
+
"""
|
137
|
+
|
138
|
+
cdef:
|
139
|
+
size_t tail_vert_idx, head_vert_idx, idx, v
|
140
|
+
DTYPE_t tail_vert_val, new_dist
|
141
|
+
cnp.ndarray[DTYPE_t, ndim=1] dist = np.full(
|
142
|
+
vertex_count, DTYPE_INF, dtype=DTYPE_PY)
|
143
|
+
size_t source = <size_t>source_vert_idx
|
144
|
+
bint changed
|
145
|
+
|
146
|
+
# Initialize source distance
|
147
|
+
dist[source] = 0.0
|
148
|
+
|
149
|
+
with nogil:
|
150
|
+
# Relax edges V-1 times
|
151
|
+
for v in range(<size_t>(vertex_count - 1)):
|
152
|
+
changed = False
|
153
|
+
|
154
|
+
# Iterate through all vertices
|
155
|
+
for tail_vert_idx in range(<size_t>vertex_count):
|
156
|
+
tail_vert_val = dist[tail_vert_idx]
|
157
|
+
|
158
|
+
# Skip if vertex is unreachable
|
159
|
+
if tail_vert_val == DTYPE_INF:
|
160
|
+
continue
|
161
|
+
|
162
|
+
# Relax edges from this vertex
|
163
|
+
for idx in range(<size_t>csr_indptr[tail_vert_idx],
|
164
|
+
<size_t>csr_indptr[tail_vert_idx + 1]):
|
165
|
+
|
166
|
+
head_vert_idx = <size_t>csr_indices[idx]
|
167
|
+
new_dist = tail_vert_val + csr_data[idx]
|
168
|
+
|
169
|
+
if dist[head_vert_idx] > new_dist:
|
170
|
+
dist[head_vert_idx] = new_dist
|
171
|
+
predecessor[head_vert_idx] = tail_vert_idx
|
172
|
+
changed = True
|
173
|
+
|
174
|
+
# Early termination if no changes
|
175
|
+
if not changed:
|
176
|
+
break
|
177
|
+
|
178
|
+
return dist
|
179
|
+
|
180
|
+
|
181
|
+
cpdef cnp.ndarray compute_bf_stsp(
|
182
|
+
cnp.uint32_t[::1] csc_indptr,
|
183
|
+
cnp.uint32_t[::1] csc_indices,
|
184
|
+
DTYPE_t[::1] csc_data,
|
185
|
+
int target_vert_idx,
|
186
|
+
int vertex_count):
|
187
|
+
"""
|
188
|
+
Compute single-target shortest path using Bellman-Ford algorithm.
|
189
|
+
|
190
|
+
From all vertices to one vertex. Does not return successors.
|
191
|
+
Supports negative edge weights.
|
192
|
+
|
193
|
+
Parameters
|
194
|
+
----------
|
195
|
+
csc_indices : cnp.uint32_t[::1]
|
196
|
+
indices in the CSC format
|
197
|
+
csc_indptr : cnp.uint32_t[::1]
|
198
|
+
pointers in the CSC format
|
199
|
+
csc_data : DTYPE_t[::1]
|
200
|
+
data (edge weights) in the CSC format
|
201
|
+
target_vert_idx : int
|
202
|
+
target vertex index
|
203
|
+
vertex_count : int
|
204
|
+
vertex count
|
205
|
+
|
206
|
+
Returns
|
207
|
+
-------
|
208
|
+
path_lengths : cnp.ndarray
|
209
|
+
shortest path length for each vertex
|
210
|
+
"""
|
211
|
+
|
212
|
+
cdef:
|
213
|
+
size_t tail_vert_idx, head_vert_idx, idx, v
|
214
|
+
DTYPE_t head_vert_val, new_dist
|
215
|
+
cnp.ndarray[DTYPE_t, ndim=1] dist = np.full(
|
216
|
+
vertex_count, DTYPE_INF, dtype=DTYPE_PY)
|
217
|
+
size_t target = <size_t>target_vert_idx
|
218
|
+
bint changed
|
219
|
+
|
220
|
+
# Initialize target distance
|
221
|
+
dist[target] = 0.0
|
222
|
+
|
223
|
+
with nogil:
|
224
|
+
# Relax edges V-1 times
|
225
|
+
for v in range(<size_t>(vertex_count - 1)):
|
226
|
+
changed = False
|
227
|
+
|
228
|
+
# Iterate through all vertices (reversed for incoming edges)
|
229
|
+
for head_vert_idx in range(<size_t>vertex_count):
|
230
|
+
head_vert_val = dist[head_vert_idx]
|
231
|
+
|
232
|
+
# Skip if vertex is unreachable
|
233
|
+
if head_vert_val == DTYPE_INF:
|
234
|
+
continue
|
235
|
+
|
236
|
+
# Relax incoming edges to this vertex
|
237
|
+
for idx in range(<size_t>csc_indptr[head_vert_idx],
|
238
|
+
<size_t>csc_indptr[head_vert_idx + 1]):
|
239
|
+
|
240
|
+
tail_vert_idx = <size_t>csc_indices[idx]
|
241
|
+
new_dist = head_vert_val + csc_data[idx]
|
242
|
+
|
243
|
+
if dist[tail_vert_idx] > new_dist:
|
244
|
+
dist[tail_vert_idx] = new_dist
|
245
|
+
changed = True
|
246
|
+
|
247
|
+
# Early termination if no changes
|
248
|
+
if not changed:
|
249
|
+
break
|
250
|
+
|
251
|
+
return dist
|
252
|
+
|
253
|
+
|
254
|
+
cpdef cnp.ndarray compute_bf_stsp_w_path(
|
255
|
+
cnp.uint32_t[::1] csc_indptr,
|
256
|
+
cnp.uint32_t[::1] csc_indices,
|
257
|
+
DTYPE_t[::1] csc_data,
|
258
|
+
cnp.uint32_t[::1] successor,
|
259
|
+
int target_vert_idx,
|
260
|
+
int vertex_count):
|
261
|
+
"""
|
262
|
+
Compute single-target shortest path using Bellman-Ford algorithm.
|
263
|
+
|
264
|
+
From all vertices to one vertex. Compute successors.
|
265
|
+
Supports negative edge weights.
|
266
|
+
|
267
|
+
Parameters
|
268
|
+
----------
|
269
|
+
csc_indices : cnp.uint32_t[::1]
|
270
|
+
indices in the CSC format
|
271
|
+
csc_indptr : cnp.uint32_t[::1]
|
272
|
+
pointers in the CSC format
|
273
|
+
csc_data : DTYPE_t[::1]
|
274
|
+
data (edge weights) in the CSC format
|
275
|
+
successor : cnp.uint32_t[::1]
|
276
|
+
array of indices, one for each vertex of the graph. Each vertex'
|
277
|
+
entry contains the index of its successor in a path to the
|
278
|
+
target, through the graph.
|
279
|
+
target_vert_idx : int
|
280
|
+
target vertex index
|
281
|
+
vertex_count : int
|
282
|
+
vertex count
|
283
|
+
|
284
|
+
Returns
|
285
|
+
-------
|
286
|
+
path_lengths : cnp.ndarray
|
287
|
+
shortest path length for each vertex
|
288
|
+
"""
|
289
|
+
|
290
|
+
cdef:
|
291
|
+
size_t tail_vert_idx, head_vert_idx, idx, v
|
292
|
+
DTYPE_t head_vert_val, new_dist
|
293
|
+
cnp.ndarray[DTYPE_t, ndim=1] dist = np.full(
|
294
|
+
vertex_count, DTYPE_INF, dtype=DTYPE_PY)
|
295
|
+
size_t target = <size_t>target_vert_idx
|
296
|
+
bint changed
|
297
|
+
|
298
|
+
# Initialize target distance
|
299
|
+
dist[target] = 0.0
|
300
|
+
|
301
|
+
with nogil:
|
302
|
+
# Relax edges V-1 times
|
303
|
+
for v in range(<size_t>(vertex_count - 1)):
|
304
|
+
changed = False
|
305
|
+
|
306
|
+
# Iterate through all vertices (reversed for incoming edges)
|
307
|
+
for head_vert_idx in range(<size_t>vertex_count):
|
308
|
+
head_vert_val = dist[head_vert_idx]
|
309
|
+
|
310
|
+
# Skip if vertex is unreachable
|
311
|
+
if head_vert_val == DTYPE_INF:
|
312
|
+
continue
|
313
|
+
|
314
|
+
# Relax incoming edges to this vertex
|
315
|
+
for idx in range(<size_t>csc_indptr[head_vert_idx],
|
316
|
+
<size_t>csc_indptr[head_vert_idx + 1]):
|
317
|
+
|
318
|
+
tail_vert_idx = <size_t>csc_indices[idx]
|
319
|
+
new_dist = head_vert_val + csc_data[idx]
|
320
|
+
|
321
|
+
if dist[tail_vert_idx] > new_dist:
|
322
|
+
dist[tail_vert_idx] = new_dist
|
323
|
+
successor[tail_vert_idx] = head_vert_idx
|
324
|
+
changed = True
|
325
|
+
|
326
|
+
# Early termination if no changes
|
327
|
+
if not changed:
|
328
|
+
break
|
329
|
+
|
330
|
+
return dist
|
331
|
+
|
332
|
+
|
333
|
+
cpdef bint detect_negative_cycle(
|
334
|
+
cnp.uint32_t[::1] csr_indptr,
|
335
|
+
cnp.uint32_t[::1] csr_indices,
|
336
|
+
DTYPE_t[::1] csr_data,
|
337
|
+
DTYPE_t[::1] dist_matrix,
|
338
|
+
int vertex_count):
|
339
|
+
"""
|
340
|
+
Detect negative cycles using one more iteration of edge relaxation.
|
341
|
+
|
342
|
+
Parameters
|
343
|
+
----------
|
344
|
+
csr_indices : cnp.uint32_t[::1]
|
345
|
+
indices in the CSR format
|
346
|
+
csr_indptr : cnp.uint32_t[::1]
|
347
|
+
pointers in the CSR format
|
348
|
+
csr_data : DTYPE_t[::1]
|
349
|
+
data (edge weights) in the CSR format
|
350
|
+
dist_matrix : DTYPE_t[::1]
|
351
|
+
current distance matrix from Bellman-Ford algorithm
|
352
|
+
vertex_count : int
|
353
|
+
vertex count
|
354
|
+
|
355
|
+
Returns
|
356
|
+
-------
|
357
|
+
has_negative_cycle : bool
|
358
|
+
True if negative cycle detected, False otherwise
|
359
|
+
"""
|
360
|
+
|
361
|
+
cdef:
|
362
|
+
size_t tail_vert_idx, head_vert_idx, idx
|
363
|
+
DTYPE_t tail_vert_val, new_dist
|
364
|
+
bint has_negative_cycle = False
|
365
|
+
|
366
|
+
with nogil:
|
367
|
+
# One more iteration to detect negative cycles
|
368
|
+
for tail_vert_idx in range(<size_t>vertex_count):
|
369
|
+
tail_vert_val = dist_matrix[tail_vert_idx]
|
370
|
+
|
371
|
+
# Skip if vertex is unreachable
|
372
|
+
if tail_vert_val == DTYPE_INF:
|
373
|
+
continue
|
374
|
+
|
375
|
+
# Check edges from this vertex
|
376
|
+
for idx in range(<size_t>csr_indptr[tail_vert_idx],
|
377
|
+
<size_t>csr_indptr[tail_vert_idx + 1]):
|
378
|
+
|
379
|
+
head_vert_idx = <size_t>csr_indices[idx]
|
380
|
+
new_dist = tail_vert_val + csr_data[idx]
|
381
|
+
|
382
|
+
# If we can still relax, there's a negative cycle
|
383
|
+
if dist_matrix[head_vert_idx] > new_dist:
|
384
|
+
has_negative_cycle = True
|
385
|
+
break
|
386
|
+
|
387
|
+
if has_negative_cycle:
|
388
|
+
break
|
389
|
+
|
390
|
+
return has_negative_cycle
|
391
|
+
|
392
|
+
|
393
|
+
cpdef bint detect_negative_cycle_csc(
|
394
|
+
cnp.uint32_t[::1] csc_indptr,
|
395
|
+
cnp.uint32_t[::1] csc_indices,
|
396
|
+
DTYPE_t[::1] csc_data,
|
397
|
+
DTYPE_t[::1] stsp_dist,
|
398
|
+
int vertex_count):
|
399
|
+
"""
|
400
|
+
Detect negative cycles using CSC format and STSP distances.
|
401
|
+
|
402
|
+
For STSP (Single-Target Shortest Path):
|
403
|
+
- stsp_dist[u] = distance FROM vertex u TO target vertex
|
404
|
+
- Edge (u→v) can be relaxed if: stsp_dist[u] > stsp_dist[v] + weight(u→v)
|
405
|
+
|
406
|
+
This function performs one additional iteration to check if any edge
|
407
|
+
can still be relaxed, which indicates the presence of a negative cycle.
|
408
|
+
|
409
|
+
Parameters
|
410
|
+
----------
|
411
|
+
csc_indptr : cnp.uint32_t[::1]
|
412
|
+
Pointers in the CSC format (incoming edges by destination)
|
413
|
+
csc_indices : cnp.uint32_t[::1]
|
414
|
+
Indices in the CSC format (source vertices)
|
415
|
+
csc_data : DTYPE_t[::1]
|
416
|
+
Data (edge weights) in the CSC format
|
417
|
+
stsp_dist : DTYPE_t[::1]
|
418
|
+
Current distance array from STSP algorithm (distances TO target)
|
419
|
+
vertex_count : int
|
420
|
+
Total number of vertices in the graph
|
421
|
+
|
422
|
+
Returns
|
423
|
+
-------
|
424
|
+
has_negative_cycle : bool
|
425
|
+
True if negative cycle detected, False otherwise
|
426
|
+
"""
|
427
|
+
|
428
|
+
cdef:
|
429
|
+
size_t head_vert_idx, tail_vert_idx, idx
|
430
|
+
DTYPE_t new_dist
|
431
|
+
bint has_negative_cycle = False
|
432
|
+
|
433
|
+
with nogil:
|
434
|
+
# Iterate over destination vertices (CSC organization)
|
435
|
+
for head_vert_idx in range(<size_t>vertex_count):
|
436
|
+
# Skip unreachable vertices
|
437
|
+
if stsp_dist[head_vert_idx] == DTYPE_INF:
|
438
|
+
continue
|
439
|
+
|
440
|
+
# Check all incoming edges to this vertex
|
441
|
+
for idx in range(<size_t>csc_indptr[head_vert_idx],
|
442
|
+
<size_t>csc_indptr[head_vert_idx + 1]):
|
443
|
+
|
444
|
+
tail_vert_idx = <size_t>csc_indices[idx] # Source of edge
|
445
|
+
|
446
|
+
# STSP relaxation: can we improve distance FROM tail TO target?
|
447
|
+
new_dist = stsp_dist[head_vert_idx] + csc_data[idx]
|
448
|
+
|
449
|
+
# If we can still relax this edge, negative cycle exists
|
450
|
+
if stsp_dist[tail_vert_idx] > new_dist:
|
451
|
+
has_negative_cycle = True
|
452
|
+
break
|
453
|
+
|
454
|
+
if has_negative_cycle:
|
455
|
+
break
|
456
|
+
|
457
|
+
return has_negative_cycle
|
458
|
+
|
459
|
+
|
460
|
+
# ============================================================================ #
|
461
|
+
# tests #
|
462
|
+
# ============================================================================ #
|
463
|
+
|
464
|
+
cdef generate_negative_edge_network_csr():
|
465
|
+
"""
|
466
|
+
Generate a network with negative edges (no negative cycle) in CSR format.
|
467
|
+
|
468
|
+
Graph structure:
|
469
|
+
0 -> 1 (weight: 1)
|
470
|
+
0 -> 2 (weight: 4)
|
471
|
+
1 -> 2 (weight: -2)
|
472
|
+
1 -> 3 (weight: 5)
|
473
|
+
2 -> 3 (weight: 1)
|
474
|
+
3 -> 4 (weight: 3)
|
475
|
+
|
476
|
+
This network has 6 edges and 5 vertices.
|
477
|
+
"""
|
478
|
+
|
479
|
+
csr_indptr = np.array([0, 2, 4, 5, 6, 6], dtype=np.uint32)
|
480
|
+
csr_indices = np.array([1, 2, 2, 3, 3, 4], dtype=np.uint32)
|
481
|
+
csr_data = np.array([1., 4., -2., 5., 1., 3.], dtype=DTYPE_PY)
|
482
|
+
|
483
|
+
return csr_indptr, csr_indices, csr_data
|
484
|
+
|
485
|
+
|
486
|
+
cdef generate_negative_cycle_network_csr():
|
487
|
+
"""
|
488
|
+
Generate a network with a negative cycle in CSR format.
|
489
|
+
|
490
|
+
Graph structure:
|
491
|
+
0 -> 1 (weight: 1)
|
492
|
+
1 -> 2 (weight: -2)
|
493
|
+
2 -> 0 (weight: -1)
|
494
|
+
2 -> 3 (weight: 1)
|
495
|
+
|
496
|
+
Cycle 0->1->2->0 has total weight -2 (negative cycle)
|
497
|
+
This network has 4 edges and 4 vertices.
|
498
|
+
"""
|
499
|
+
|
500
|
+
csr_indptr = np.array([0, 1, 2, 4, 4], dtype=np.uint32)
|
501
|
+
csr_indices = np.array([1, 2, 0, 3], dtype=np.uint32)
|
502
|
+
csr_data = np.array([1., -2., -1., 1.], dtype=DTYPE_PY)
|
503
|
+
|
504
|
+
return csr_indptr, csr_indices, csr_data
|
505
|
+
|
506
|
+
|
507
|
+
cpdef test_bf_negative_edges():
|
508
|
+
"""
|
509
|
+
Test Bellman-Ford with negative edges (no negative cycle).
|
510
|
+
"""
|
511
|
+
|
512
|
+
csr_indptr, csr_indices, csr_data = generate_negative_edge_network_csr()
|
513
|
+
|
514
|
+
# from vertex 0
|
515
|
+
path_lengths = compute_bf_sssp(csr_indptr, csr_indices, csr_data, 0, 5)
|
516
|
+
path_lengths_ref = np.array([0., 1., -1., 0., 3.], dtype=DTYPE_PY)
|
517
|
+
assert np.allclose(path_lengths_ref, path_lengths)
|
518
|
+
|
519
|
+
# Test negative cycle detection (should return False)
|
520
|
+
has_cycle = detect_negative_cycle(
|
521
|
+
csr_indptr, csr_indices, csr_data, path_lengths, 5)
|
522
|
+
assert not has_cycle
|
523
|
+
|
524
|
+
|
525
|
+
cpdef test_bf_negative_cycle():
|
526
|
+
"""
|
527
|
+
Test Bellman-Ford negative cycle detection.
|
528
|
+
"""
|
529
|
+
|
530
|
+
csr_indptr, csr_indices, csr_data = generate_negative_cycle_network_csr()
|
531
|
+
|
532
|
+
# from vertex 0
|
533
|
+
path_lengths = compute_bf_sssp(csr_indptr, csr_indices, csr_data, 0, 4)
|
534
|
+
|
535
|
+
# Test negative cycle detection (should return True)
|
536
|
+
has_cycle = detect_negative_cycle(
|
537
|
+
csr_indptr, csr_indices, csr_data, path_lengths, 4)
|
538
|
+
assert has_cycle
|
539
|
+
|
540
|
+
|
541
|
+
# author : Francois Pacull
|
542
|
+
# copyright : Architecture & Performance
|
543
|
+
# email: francois.pacull@architecture-performance.fr
|
544
|
+
# license : MIT
|
edsger/commons.cp311-win32.pyd
CHANGED
Binary file
|
edsger/dijkstra.cp311-win32.pyd
CHANGED
Binary file
|