edsger 0.1.1__cp312-cp312-macosx_11_0_arm64.whl → 0.1.3__cp312-cp312-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.
- edsger/_version.py +1 -1
- edsger/commons.c +151 -147
- edsger/commons.cpython-312-darwin.so +0 -0
- edsger/dijkstra.c +7197 -2762
- edsger/dijkstra.cpython-312-darwin.so +0 -0
- edsger/dijkstra.pyx +525 -0
- edsger/path.py +145 -37
- edsger/path_tracking.c +151 -147
- edsger/path_tracking.cpython-312-darwin.so +0 -0
- edsger/path_tracking.pyx +2 -2
- edsger/pq_4ary_dec_0b.c +203 -199
- edsger/pq_4ary_dec_0b.cpython-312-darwin.so +0 -0
- edsger/pq_4ary_dec_0b.pxd +7 -7
- edsger/pq_4ary_dec_0b.pyx +14 -16
- edsger/prefetch_compat.h +21 -0
- edsger/spiess_florian.c +157 -176
- edsger/spiess_florian.cpython-312-darwin.so +0 -0
- edsger/star.c +151 -147
- edsger/star.cpython-312-darwin.so +0 -0
- edsger/star.pyx +3 -3
- {edsger-0.1.1.dist-info → edsger-0.1.3.dist-info}/METADATA +18 -11
- edsger-0.1.3.dist-info/RECORD +33 -0
- edsger-0.1.1.dist-info/RECORD +0 -32
- {edsger-0.1.1.dist-info → edsger-0.1.3.dist-info}/WHEEL +0 -0
- {edsger-0.1.1.dist-info → edsger-0.1.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {edsger-0.1.1.dist-info → edsger-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {edsger-0.1.1.dist-info → edsger-0.1.3.dist-info}/top_level.txt +0 -0
Binary file
|
edsger/dijkstra.pyx
CHANGED
@@ -9,12 +9,24 @@ cpdef functions:
|
|
9
9
|
- compute_sssp_w_path
|
10
10
|
Compute single-source shortest path (from one vertex to all vertices).
|
11
11
|
Compute predecessors.
|
12
|
+
- compute_sssp_early_termination
|
13
|
+
Compute single-source shortest path with early termination when target nodes
|
14
|
+
are reached. Does not return predecessors.
|
15
|
+
- compute_sssp_w_path_early_termination
|
16
|
+
Compute single-source shortest path with early termination when target nodes
|
17
|
+
are reached. Compute predecessors.
|
12
18
|
- compute_stsp
|
13
19
|
Compute single-target shortest path (from all vertices to one vertex). Does
|
14
20
|
not return successors.
|
15
21
|
- compute_stsp_w_path
|
16
22
|
Compute single-target shortest path (from all vertices to one vertex).
|
17
23
|
Compute successors.
|
24
|
+
- compute_stsp_early_termination
|
25
|
+
Compute single-target shortest path with early termination when target nodes
|
26
|
+
are reached. Does not return successors.
|
27
|
+
- compute_stsp_w_path_early_termination
|
28
|
+
Compute single-target shortest path with early termination when target nodes
|
29
|
+
are reached. Compute successors.
|
18
30
|
"""
|
19
31
|
|
20
32
|
cimport numpy as cnp
|
@@ -23,6 +35,11 @@ from edsger.commons cimport (
|
|
23
35
|
DTYPE_INF, UNLABELED, SCANNED, DTYPE_t, ElementState)
|
24
36
|
cimport edsger.pq_4ary_dec_0b as pq # priority queue
|
25
37
|
|
38
|
+
# memory prefetching support (x86/x64 only)
|
39
|
+
cdef extern from "prefetch_compat.h":
|
40
|
+
void prefetch_hint(char*, int) nogil
|
41
|
+
int PREFETCH_T0
|
42
|
+
|
26
43
|
|
27
44
|
cpdef cnp.ndarray compute_sssp(
|
28
45
|
cnp.uint32_t[::1] csr_indptr,
|
@@ -83,8 +100,17 @@ cpdef cnp.ndarray compute_sssp(
|
|
83
100
|
<size_t>csr_indptr[tail_vert_idx + 1]):
|
84
101
|
|
85
102
|
head_vert_idx = <size_t>csr_indices[idx]
|
103
|
+
|
104
|
+
# prefetch next iteration data to improve cache performance
|
105
|
+
if idx + 1 < <size_t>csr_indptr[tail_vert_idx + 1]:
|
106
|
+
prefetch_hint(<char*>&csr_indices[idx + 1], PREFETCH_T0)
|
107
|
+
prefetch_hint(<char*>&csr_data[idx + 1], PREFETCH_T0)
|
108
|
+
|
86
109
|
vert_state = pqueue.Elements[head_vert_idx].state
|
87
110
|
if vert_state != SCANNED:
|
111
|
+
# prefetch priority queue element data for the vertex
|
112
|
+
prefetch_hint(<char*>&pqueue.Elements[head_vert_idx], PREFETCH_T0)
|
113
|
+
|
88
114
|
head_vert_val = tail_vert_val + csr_data[idx]
|
89
115
|
if vert_state == UNLABELED:
|
90
116
|
pq.insert(&pqueue, head_vert_idx, head_vert_val)
|
@@ -100,6 +126,109 @@ cpdef cnp.ndarray compute_sssp(
|
|
100
126
|
return path_lengths
|
101
127
|
|
102
128
|
|
129
|
+
cpdef cnp.ndarray compute_sssp_early_termination(
|
130
|
+
cnp.uint32_t[::1] csr_indptr,
|
131
|
+
cnp.uint32_t[::1] csr_indices,
|
132
|
+
DTYPE_t[::1] csr_data,
|
133
|
+
cnp.uint32_t[::1] termination_nodes,
|
134
|
+
int source_vert_idx,
|
135
|
+
int vertex_count,
|
136
|
+
int heap_length):
|
137
|
+
"""
|
138
|
+
Compute single-source shortest path with early termination when target
|
139
|
+
nodes are reached.
|
140
|
+
Parameters
|
141
|
+
----------
|
142
|
+
csr_indices : cnp.uint32_t[::1]
|
143
|
+
indices in the CSR format
|
144
|
+
csr_indptr : cnp.uint32_t[::1]
|
145
|
+
pointers in the CSR format
|
146
|
+
csr_data DTYPE_t[::1]
|
147
|
+
data (edge weights) in the CSR format
|
148
|
+
termination_nodes : cnp.uint32_t[::1]
|
149
|
+
target node indices for early termination
|
150
|
+
source_vert_idx : int
|
151
|
+
source vertex index
|
152
|
+
vertex_count : int
|
153
|
+
vertex count
|
154
|
+
heap_length : int
|
155
|
+
heap length
|
156
|
+
|
157
|
+
Returns
|
158
|
+
-------
|
159
|
+
path_lengths : cnp.ndarray
|
160
|
+
shortest path length for each termination node
|
161
|
+
"""
|
162
|
+
|
163
|
+
cdef:
|
164
|
+
size_t tail_vert_idx, head_vert_idx, idx, i
|
165
|
+
DTYPE_t tail_vert_val, head_vert_val
|
166
|
+
pq.PriorityQueue pqueue
|
167
|
+
ElementState vert_state
|
168
|
+
size_t source = <size_t>source_vert_idx
|
169
|
+
size_t target_count = termination_nodes.shape[0]
|
170
|
+
size_t visited_targets = 0
|
171
|
+
size_t iteration_count = 0
|
172
|
+
size_t check_frequency = 16
|
173
|
+
|
174
|
+
with nogil:
|
175
|
+
|
176
|
+
# initialization of the heap elements
|
177
|
+
# all nodes have INFINITY key and UNLABELED state
|
178
|
+
pq.init_pqueue(&pqueue, <size_t>heap_length, <size_t>vertex_count)
|
179
|
+
|
180
|
+
# the key is set to zero for the source vertex,
|
181
|
+
# which is inserted into the heap
|
182
|
+
pq.insert(&pqueue, source, 0.0)
|
183
|
+
|
184
|
+
# main loop
|
185
|
+
while pqueue.size > 0:
|
186
|
+
tail_vert_idx = pq.extract_min(&pqueue)
|
187
|
+
tail_vert_val = pqueue.Elements[tail_vert_idx].key
|
188
|
+
|
189
|
+
# check for early termination every check_frequency iterations
|
190
|
+
iteration_count += 1
|
191
|
+
if iteration_count % check_frequency == 0:
|
192
|
+
visited_targets = 0
|
193
|
+
for i in range(target_count):
|
194
|
+
if pqueue.Elements[termination_nodes[i]].state == SCANNED:
|
195
|
+
visited_targets += 1
|
196
|
+
if visited_targets == target_count:
|
197
|
+
break
|
198
|
+
|
199
|
+
# loop on outgoing edges
|
200
|
+
for idx in range(<size_t>csr_indptr[tail_vert_idx],
|
201
|
+
<size_t>csr_indptr[tail_vert_idx + 1]):
|
202
|
+
|
203
|
+
head_vert_idx = <size_t>csr_indices[idx]
|
204
|
+
|
205
|
+
# prefetch next iteration data to improve cache performance
|
206
|
+
if idx + 1 < <size_t>csr_indptr[tail_vert_idx + 1]:
|
207
|
+
prefetch_hint(<char*>&csr_indices[idx + 1], PREFETCH_T0)
|
208
|
+
prefetch_hint(<char*>&csr_data[idx + 1], PREFETCH_T0)
|
209
|
+
|
210
|
+
vert_state = pqueue.Elements[head_vert_idx].state
|
211
|
+
if vert_state != SCANNED:
|
212
|
+
# prefetch priority queue element data for the vertex
|
213
|
+
prefetch_hint(<char*>&pqueue.Elements[head_vert_idx], PREFETCH_T0)
|
214
|
+
|
215
|
+
head_vert_val = tail_vert_val + csr_data[idx]
|
216
|
+
if vert_state == UNLABELED:
|
217
|
+
pq.insert(&pqueue, head_vert_idx, head_vert_val)
|
218
|
+
elif pqueue.Elements[head_vert_idx].key > head_vert_val:
|
219
|
+
pq.decrease_key(&pqueue, head_vert_idx, head_vert_val)
|
220
|
+
|
221
|
+
# copy only the termination nodes' results into a numpy array
|
222
|
+
cdef cnp.ndarray path_lengths = np.empty(target_count, dtype=DTYPE_PY)
|
223
|
+
for i in range(target_count):
|
224
|
+
path_lengths[i] = pqueue.Elements[termination_nodes[i]].key
|
225
|
+
|
226
|
+
# cleanup
|
227
|
+
pq.free_pqueue(&pqueue)
|
228
|
+
|
229
|
+
return path_lengths
|
230
|
+
|
231
|
+
|
103
232
|
cpdef cnp.ndarray compute_sssp_w_path(
|
104
233
|
cnp.uint32_t[::1] csr_indptr,
|
105
234
|
cnp.uint32_t[::1] csr_indices,
|
@@ -164,8 +293,17 @@ cpdef cnp.ndarray compute_sssp_w_path(
|
|
164
293
|
<size_t>csr_indptr[tail_vert_idx + 1]):
|
165
294
|
|
166
295
|
head_vert_idx = <size_t>csr_indices[idx]
|
296
|
+
|
297
|
+
# prefetch next iteration data to improve cache performance
|
298
|
+
if idx + 1 < <size_t>csr_indptr[tail_vert_idx + 1]:
|
299
|
+
prefetch_hint(<char*>&csr_indices[idx + 1], PREFETCH_T0)
|
300
|
+
prefetch_hint(<char*>&csr_data[idx + 1], PREFETCH_T0)
|
301
|
+
|
167
302
|
vert_state = pqueue.Elements[head_vert_idx].state
|
168
303
|
if vert_state != SCANNED:
|
304
|
+
# prefetch priority queue element data for the vertex
|
305
|
+
prefetch_hint(<char*>&pqueue.Elements[head_vert_idx], PREFETCH_T0)
|
306
|
+
|
169
307
|
head_vert_val = tail_vert_val + csr_data[idx]
|
170
308
|
if vert_state == UNLABELED:
|
171
309
|
pq.insert(&pqueue, head_vert_idx, head_vert_val)
|
@@ -183,6 +321,115 @@ cpdef cnp.ndarray compute_sssp_w_path(
|
|
183
321
|
return path_lengths
|
184
322
|
|
185
323
|
|
324
|
+
cpdef cnp.ndarray compute_sssp_w_path_early_termination(
|
325
|
+
cnp.uint32_t[::1] csr_indptr,
|
326
|
+
cnp.uint32_t[::1] csr_indices,
|
327
|
+
DTYPE_t[::1] csr_data,
|
328
|
+
cnp.uint32_t[::1] predecessor,
|
329
|
+
cnp.uint32_t[::1] termination_nodes,
|
330
|
+
int source_vert_idx,
|
331
|
+
int vertex_count,
|
332
|
+
int heap_length):
|
333
|
+
"""
|
334
|
+
Compute single-source shortest path with path tracking and early termination.
|
335
|
+
Parameters
|
336
|
+
----------
|
337
|
+
csr_indices : cnp.uint32_t[::1]
|
338
|
+
indices in the CSR format
|
339
|
+
csr_indptr : cnp.uint32_t[::1]
|
340
|
+
pointers in the CSR format
|
341
|
+
csr_data : DTYPE_t[::1]
|
342
|
+
data (edge weights) in the CSR format
|
343
|
+
predecessor : cnp.uint32_t[::1]
|
344
|
+
array of indices, one for each vertex of the graph. Each vertex'
|
345
|
+
entry contains the index of its predecessor in a path from the
|
346
|
+
source, through the graph.
|
347
|
+
termination_nodes : cnp.uint32_t[::1]
|
348
|
+
target node indices for early termination
|
349
|
+
source_vert_idx : int
|
350
|
+
source vertex index
|
351
|
+
vertex_count : int
|
352
|
+
vertex count
|
353
|
+
heap_length : int
|
354
|
+
heap length
|
355
|
+
|
356
|
+
Returns
|
357
|
+
-------
|
358
|
+
path_lengths : cnp.ndarray
|
359
|
+
shortest path length for each termination node
|
360
|
+
"""
|
361
|
+
|
362
|
+
cdef:
|
363
|
+
size_t tail_vert_idx, head_vert_idx, idx, i
|
364
|
+
DTYPE_t tail_vert_val, head_vert_val
|
365
|
+
pq.PriorityQueue pqueue
|
366
|
+
ElementState vert_state
|
367
|
+
size_t source = <size_t>source_vert_idx
|
368
|
+
size_t target_count = termination_nodes.shape[0]
|
369
|
+
size_t visited_targets = 0
|
370
|
+
size_t iteration_count = 0
|
371
|
+
size_t check_frequency = 16
|
372
|
+
|
373
|
+
with nogil:
|
374
|
+
|
375
|
+
# initialization of the heap elements
|
376
|
+
# all nodes have INFINITY key and UNLABELED state
|
377
|
+
pq.init_pqueue(&pqueue, <size_t>heap_length, <size_t>vertex_count)
|
378
|
+
|
379
|
+
# the key is set to zero for the source vertex,
|
380
|
+
# which is inserted into the heap
|
381
|
+
pq.insert(&pqueue, source, 0.0)
|
382
|
+
|
383
|
+
# main loop
|
384
|
+
while pqueue.size > 0:
|
385
|
+
tail_vert_idx = pq.extract_min(&pqueue)
|
386
|
+
tail_vert_val = pqueue.Elements[tail_vert_idx].key
|
387
|
+
|
388
|
+
# check for early termination every check_frequency iterations
|
389
|
+
iteration_count += 1
|
390
|
+
if iteration_count % check_frequency == 0:
|
391
|
+
visited_targets = 0
|
392
|
+
for i in range(target_count):
|
393
|
+
if pqueue.Elements[termination_nodes[i]].state == SCANNED:
|
394
|
+
visited_targets += 1
|
395
|
+
if visited_targets == target_count:
|
396
|
+
break
|
397
|
+
|
398
|
+
# loop on outgoing edges
|
399
|
+
for idx in range(<size_t>csr_indptr[tail_vert_idx],
|
400
|
+
<size_t>csr_indptr[tail_vert_idx + 1]):
|
401
|
+
|
402
|
+
head_vert_idx = <size_t>csr_indices[idx]
|
403
|
+
|
404
|
+
# prefetch next iteration data to improve cache performance
|
405
|
+
if idx + 1 < <size_t>csr_indptr[tail_vert_idx + 1]:
|
406
|
+
prefetch_hint(<char*>&csr_indices[idx + 1], PREFETCH_T0)
|
407
|
+
prefetch_hint(<char*>&csr_data[idx + 1], PREFETCH_T0)
|
408
|
+
|
409
|
+
vert_state = pqueue.Elements[head_vert_idx].state
|
410
|
+
if vert_state != SCANNED:
|
411
|
+
# prefetch priority queue element data for the vertex
|
412
|
+
prefetch_hint(<char*>&pqueue.Elements[head_vert_idx], PREFETCH_T0)
|
413
|
+
|
414
|
+
head_vert_val = tail_vert_val + csr_data[idx]
|
415
|
+
if vert_state == UNLABELED:
|
416
|
+
pq.insert(&pqueue, head_vert_idx, head_vert_val)
|
417
|
+
predecessor[head_vert_idx] = tail_vert_idx
|
418
|
+
elif pqueue.Elements[head_vert_idx].key > head_vert_val:
|
419
|
+
pq.decrease_key(&pqueue, head_vert_idx, head_vert_val)
|
420
|
+
predecessor[head_vert_idx] = tail_vert_idx
|
421
|
+
|
422
|
+
# copy only the termination nodes' results into a numpy array
|
423
|
+
cdef cnp.ndarray path_lengths = np.empty(target_count, dtype=DTYPE_PY)
|
424
|
+
for i in range(target_count):
|
425
|
+
path_lengths[i] = pqueue.Elements[termination_nodes[i]].key
|
426
|
+
|
427
|
+
# cleanup
|
428
|
+
pq.free_pqueue(&pqueue)
|
429
|
+
|
430
|
+
return path_lengths
|
431
|
+
|
432
|
+
|
186
433
|
cpdef cnp.ndarray compute_stsp(
|
187
434
|
cnp.uint32_t[::1] csc_indptr,
|
188
435
|
cnp.uint32_t[::1] csc_indices,
|
@@ -242,8 +489,17 @@ cpdef cnp.ndarray compute_stsp(
|
|
242
489
|
<size_t>csc_indptr[head_vert_idx + 1]):
|
243
490
|
|
244
491
|
tail_vert_idx = <size_t>csc_indices[idx]
|
492
|
+
|
493
|
+
# prefetch next iteration data to improve cache performance
|
494
|
+
if idx + 1 < <size_t>csc_indptr[head_vert_idx + 1]:
|
495
|
+
prefetch_hint(<char*>&csc_indices[idx + 1], PREFETCH_T0)
|
496
|
+
prefetch_hint(<char*>&csc_data[idx + 1], PREFETCH_T0)
|
497
|
+
|
245
498
|
vert_state = pqueue.Elements[tail_vert_idx].state
|
246
499
|
if vert_state != SCANNED:
|
500
|
+
# prefetch priority queue element data for the vertex
|
501
|
+
prefetch_hint(<char*>&pqueue.Elements[tail_vert_idx], PREFETCH_T0)
|
502
|
+
|
247
503
|
tail_vert_val = head_vert_val + csc_data[idx]
|
248
504
|
if vert_state == UNLABELED:
|
249
505
|
pq.insert(&pqueue, tail_vert_idx, tail_vert_val)
|
@@ -319,8 +575,17 @@ cpdef cnp.ndarray compute_stsp_w_path(
|
|
319
575
|
<size_t>csc_indptr[head_vert_idx + 1]):
|
320
576
|
|
321
577
|
tail_vert_idx = <size_t>csc_indices[idx]
|
578
|
+
|
579
|
+
# prefetch next iteration data to improve cache performance
|
580
|
+
if idx + 1 < <size_t>csc_indptr[head_vert_idx + 1]:
|
581
|
+
prefetch_hint(<char*>&csc_indices[idx + 1], PREFETCH_T0)
|
582
|
+
prefetch_hint(<char*>&csc_data[idx + 1], PREFETCH_T0)
|
583
|
+
|
322
584
|
vert_state = pqueue.Elements[tail_vert_idx].state
|
323
585
|
if vert_state != SCANNED:
|
586
|
+
# prefetch priority queue element data for the vertex
|
587
|
+
prefetch_hint(<char*>&pqueue.Elements[tail_vert_idx], PREFETCH_T0)
|
588
|
+
|
324
589
|
tail_vert_val = head_vert_val + csc_data[idx]
|
325
590
|
if vert_state == UNLABELED:
|
326
591
|
pq.insert(&pqueue, tail_vert_idx, tail_vert_val)
|
@@ -338,6 +603,216 @@ cpdef cnp.ndarray compute_stsp_w_path(
|
|
338
603
|
return path_lengths
|
339
604
|
|
340
605
|
|
606
|
+
cpdef cnp.ndarray compute_stsp_early_termination(
|
607
|
+
cnp.uint32_t[::1] csc_indptr,
|
608
|
+
cnp.uint32_t[::1] csc_indices,
|
609
|
+
DTYPE_t[::1] csc_data,
|
610
|
+
cnp.uint32_t[::1] termination_nodes,
|
611
|
+
int target_vert_idx,
|
612
|
+
int vertex_count,
|
613
|
+
int heap_length):
|
614
|
+
"""
|
615
|
+
Compute single-target shortest path with early termination when target
|
616
|
+
nodes are reached.
|
617
|
+
Parameters
|
618
|
+
----------
|
619
|
+
csc_indices : cnp.uint32_t[::1]
|
620
|
+
indices in the CSC format
|
621
|
+
csc_indptr : cnp.uint32_t[::1]
|
622
|
+
pointers in the CSC format
|
623
|
+
csc_data : DTYPE_t[::1]
|
624
|
+
data (edge weights) in the CSC format
|
625
|
+
termination_nodes : cnp.uint32_t[::1]
|
626
|
+
target node indices for early termination
|
627
|
+
target_vert_idx : int
|
628
|
+
target vertex index
|
629
|
+
vertex_count : int
|
630
|
+
vertex count
|
631
|
+
heap_length : int
|
632
|
+
heap length
|
633
|
+
|
634
|
+
Returns
|
635
|
+
-------
|
636
|
+
path_lengths : cnp.ndarray
|
637
|
+
shortest path length for each termination node
|
638
|
+
"""
|
639
|
+
|
640
|
+
cdef:
|
641
|
+
size_t tail_vert_idx, head_vert_idx, idx, i
|
642
|
+
DTYPE_t tail_vert_val, head_vert_val
|
643
|
+
pq.PriorityQueue pqueue
|
644
|
+
ElementState vert_state
|
645
|
+
size_t target = <size_t>target_vert_idx
|
646
|
+
size_t target_count = termination_nodes.shape[0]
|
647
|
+
size_t visited_targets = 0
|
648
|
+
size_t iteration_count = 0
|
649
|
+
size_t check_frequency = 16
|
650
|
+
|
651
|
+
with nogil:
|
652
|
+
|
653
|
+
# initialization of the heap elements
|
654
|
+
# all nodes have INFINITY key and UNLABELED state
|
655
|
+
pq.init_pqueue(&pqueue, <size_t>heap_length, <size_t>vertex_count)
|
656
|
+
|
657
|
+
# the key is set to zero for the target vertex,
|
658
|
+
# which is inserted into the heap
|
659
|
+
pq.insert(&pqueue, target, 0.0)
|
660
|
+
|
661
|
+
# main loop
|
662
|
+
while pqueue.size > 0:
|
663
|
+
head_vert_idx = pq.extract_min(&pqueue)
|
664
|
+
head_vert_val = pqueue.Elements[head_vert_idx].key
|
665
|
+
|
666
|
+
# check for early termination every check_frequency iterations
|
667
|
+
iteration_count += 1
|
668
|
+
if iteration_count % check_frequency == 0:
|
669
|
+
visited_targets = 0
|
670
|
+
for i in range(target_count):
|
671
|
+
if pqueue.Elements[termination_nodes[i]].state == SCANNED:
|
672
|
+
visited_targets += 1
|
673
|
+
if visited_targets == target_count:
|
674
|
+
break
|
675
|
+
|
676
|
+
# loop on incoming edges
|
677
|
+
for idx in range(<size_t>csc_indptr[head_vert_idx],
|
678
|
+
<size_t>csc_indptr[head_vert_idx + 1]):
|
679
|
+
|
680
|
+
tail_vert_idx = <size_t>csc_indices[idx]
|
681
|
+
|
682
|
+
# prefetch next iteration data to improve cache performance
|
683
|
+
if idx + 1 < <size_t>csc_indptr[head_vert_idx + 1]:
|
684
|
+
prefetch_hint(<char*>&csc_indices[idx + 1], PREFETCH_T0)
|
685
|
+
prefetch_hint(<char*>&csc_data[idx + 1], PREFETCH_T0)
|
686
|
+
|
687
|
+
vert_state = pqueue.Elements[tail_vert_idx].state
|
688
|
+
if vert_state != SCANNED:
|
689
|
+
# prefetch priority queue element data for the vertex
|
690
|
+
prefetch_hint(<char*>&pqueue.Elements[tail_vert_idx], PREFETCH_T0)
|
691
|
+
|
692
|
+
tail_vert_val = head_vert_val + csc_data[idx]
|
693
|
+
if vert_state == UNLABELED:
|
694
|
+
pq.insert(&pqueue, tail_vert_idx, tail_vert_val)
|
695
|
+
elif pqueue.Elements[tail_vert_idx].key > tail_vert_val:
|
696
|
+
pq.decrease_key(&pqueue, tail_vert_idx, tail_vert_val)
|
697
|
+
|
698
|
+
# copy only the termination nodes' results into a numpy array
|
699
|
+
cdef cnp.ndarray path_lengths = np.empty(target_count, dtype=DTYPE_PY)
|
700
|
+
for i in range(target_count):
|
701
|
+
path_lengths[i] = pqueue.Elements[termination_nodes[i]].key
|
702
|
+
|
703
|
+
# cleanup
|
704
|
+
pq.free_pqueue(&pqueue)
|
705
|
+
|
706
|
+
return path_lengths
|
707
|
+
|
708
|
+
|
709
|
+
cpdef cnp.ndarray compute_stsp_w_path_early_termination(
|
710
|
+
cnp.uint32_t[::1] csc_indptr,
|
711
|
+
cnp.uint32_t[::1] csc_indices,
|
712
|
+
DTYPE_t[::1] csc_data,
|
713
|
+
cnp.uint32_t[::1] successor,
|
714
|
+
cnp.uint32_t[::1] termination_nodes,
|
715
|
+
int target_vert_idx,
|
716
|
+
int vertex_count,
|
717
|
+
int heap_length):
|
718
|
+
"""
|
719
|
+
Compute single-target shortest path with path tracking and early termination.
|
720
|
+
Parameters
|
721
|
+
----------
|
722
|
+
csc_indices : cnp.uint32_t[::1]
|
723
|
+
Indices in the CSC format.
|
724
|
+
csc_indptr : cnp.uint32_t[::1]
|
725
|
+
Pointers in the CSC format.
|
726
|
+
csc_data : DTYPE_t[::1]
|
727
|
+
Data (edge weights) in the CSC format.
|
728
|
+
successor : cnp.uint32_t[::1]
|
729
|
+
Array of successor indices for path reconstruction.
|
730
|
+
termination_nodes : cnp.uint32_t[::1]
|
731
|
+
target node indices for early termination
|
732
|
+
target_vert_idx : int
|
733
|
+
Target vertex index.
|
734
|
+
vertex_count : int
|
735
|
+
Vertex count.
|
736
|
+
heap_length : int
|
737
|
+
heap_length.
|
738
|
+
|
739
|
+
Returns
|
740
|
+
-------
|
741
|
+
path_lengths : cnp.ndarray
|
742
|
+
shortest path length for each termination node
|
743
|
+
"""
|
744
|
+
|
745
|
+
cdef:
|
746
|
+
size_t tail_vert_idx, head_vert_idx, idx, i
|
747
|
+
DTYPE_t tail_vert_val, head_vert_val
|
748
|
+
pq.PriorityQueue pqueue
|
749
|
+
ElementState vert_state
|
750
|
+
size_t target = <size_t>target_vert_idx
|
751
|
+
size_t target_count = termination_nodes.shape[0]
|
752
|
+
size_t visited_targets = 0
|
753
|
+
size_t iteration_count = 0
|
754
|
+
size_t check_frequency = 16
|
755
|
+
|
756
|
+
with nogil:
|
757
|
+
|
758
|
+
# initialization of the heap elements
|
759
|
+
# all nodes have INFINITY key and UNLABELED state
|
760
|
+
pq.init_pqueue(&pqueue, <size_t>heap_length, <size_t>vertex_count)
|
761
|
+
|
762
|
+
# the key is set to zero for the target vertex,
|
763
|
+
# which is inserted into the heap
|
764
|
+
pq.insert(&pqueue, target, 0.0)
|
765
|
+
|
766
|
+
# main loop
|
767
|
+
while pqueue.size > 0:
|
768
|
+
head_vert_idx = pq.extract_min(&pqueue)
|
769
|
+
head_vert_val = pqueue.Elements[head_vert_idx].key
|
770
|
+
|
771
|
+
# check for early termination every check_frequency iterations
|
772
|
+
iteration_count += 1
|
773
|
+
if iteration_count % check_frequency == 0:
|
774
|
+
visited_targets = 0
|
775
|
+
for i in range(target_count):
|
776
|
+
if pqueue.Elements[termination_nodes[i]].state == SCANNED:
|
777
|
+
visited_targets += 1
|
778
|
+
if visited_targets == target_count:
|
779
|
+
break
|
780
|
+
|
781
|
+
# loop on incoming edges
|
782
|
+
for idx in range(<size_t>csc_indptr[head_vert_idx],
|
783
|
+
<size_t>csc_indptr[head_vert_idx + 1]):
|
784
|
+
|
785
|
+
tail_vert_idx = <size_t>csc_indices[idx]
|
786
|
+
|
787
|
+
# prefetch next iteration data to improve cache performance
|
788
|
+
if idx + 1 < <size_t>csc_indptr[head_vert_idx + 1]:
|
789
|
+
prefetch_hint(<char*>&csc_indices[idx + 1], PREFETCH_T0)
|
790
|
+
prefetch_hint(<char*>&csc_data[idx + 1], PREFETCH_T0)
|
791
|
+
|
792
|
+
vert_state = pqueue.Elements[tail_vert_idx].state
|
793
|
+
if vert_state != SCANNED:
|
794
|
+
# prefetch priority queue element data for the vertex
|
795
|
+
prefetch_hint(<char*>&pqueue.Elements[tail_vert_idx], PREFETCH_T0)
|
796
|
+
|
797
|
+
tail_vert_val = head_vert_val + csc_data[idx]
|
798
|
+
if vert_state == UNLABELED:
|
799
|
+
pq.insert(&pqueue, tail_vert_idx, tail_vert_val)
|
800
|
+
successor[tail_vert_idx] = head_vert_idx
|
801
|
+
elif pqueue.Elements[tail_vert_idx].key > tail_vert_val:
|
802
|
+
pq.decrease_key(&pqueue, tail_vert_idx, tail_vert_val)
|
803
|
+
successor[tail_vert_idx] = head_vert_idx
|
804
|
+
|
805
|
+
# copy only the termination nodes' results into a numpy array
|
806
|
+
cdef cnp.ndarray path_lengths = np.empty(target_count, dtype=DTYPE_PY)
|
807
|
+
for i in range(target_count):
|
808
|
+
path_lengths[i] = pqueue.Elements[termination_nodes[i]].key
|
809
|
+
|
810
|
+
# cleanup
|
811
|
+
pq.free_pqueue(&pqueue)
|
812
|
+
|
813
|
+
return path_lengths
|
814
|
+
|
815
|
+
|
341
816
|
# ============================================================================ #
|
342
817
|
# tests #
|
343
818
|
# ============================================================================ #
|
@@ -498,6 +973,56 @@ cpdef compute_stsp_02():
|
|
498
973
|
assert np.allclose(path_lengths_ref, path_lengths)
|
499
974
|
|
500
975
|
|
976
|
+
cpdef compute_sssp_early_termination_01():
|
977
|
+
"""
|
978
|
+
Test SSSP early termination on Braess-like network.
|
979
|
+
"""
|
980
|
+
|
981
|
+
csr_indptr, csr_indices, csr_data = generate_braess_network_csr()
|
982
|
+
|
983
|
+
# from vertex 0, stop when vertices 1 and 3 are reached
|
984
|
+
target_nodes = np.array([1, 3], dtype=np.uint32)
|
985
|
+
path_lengths = compute_sssp_early_termination(
|
986
|
+
csr_indptr, csr_indices, csr_data, target_nodes, 0, 4, 4
|
987
|
+
)
|
988
|
+
|
989
|
+
# should return path lengths only for termination nodes [1, 3]
|
990
|
+
path_lengths_ref = np.array([1., 2.], dtype=DTYPE_PY) # lengths to nodes 1 and 3
|
991
|
+
assert np.allclose(path_lengths_ref, path_lengths)
|
992
|
+
|
993
|
+
# test with path tracking
|
994
|
+
predecessor = np.arange(0, 4, dtype=np.uint32)
|
995
|
+
path_lengths_w_path = compute_sssp_w_path_early_termination(
|
996
|
+
csr_indptr, csr_indices, csr_data, predecessor, target_nodes, 0, 4, 4
|
997
|
+
)
|
998
|
+
assert np.allclose(path_lengths_ref, path_lengths_w_path)
|
999
|
+
|
1000
|
+
|
1001
|
+
cpdef compute_stsp_early_termination_01():
|
1002
|
+
"""
|
1003
|
+
Test STSP early termination on Braess-like network.
|
1004
|
+
"""
|
1005
|
+
|
1006
|
+
csc_indptr, csc_indices, csc_data = generate_braess_network_csc()
|
1007
|
+
|
1008
|
+
# to vertex 3, stop when vertices 0 and 2 are reached
|
1009
|
+
target_nodes = np.array([0, 2], dtype=np.uint32)
|
1010
|
+
path_lengths = compute_stsp_early_termination(
|
1011
|
+
csc_indptr, csc_indices, csc_data, target_nodes, 3, 4, 4
|
1012
|
+
)
|
1013
|
+
|
1014
|
+
# should return path lengths only for termination nodes [0, 2]
|
1015
|
+
path_lengths_ref = np.array([2., 1.], dtype=DTYPE_PY) # lengths to nodes 0 and 2
|
1016
|
+
assert np.allclose(path_lengths_ref, path_lengths)
|
1017
|
+
|
1018
|
+
# test with path tracking
|
1019
|
+
successor = np.arange(0, 4, dtype=np.uint32)
|
1020
|
+
path_lengths_w_path = compute_stsp_w_path_early_termination(
|
1021
|
+
csc_indptr, csc_indices, csc_data, successor, target_nodes, 3, 4, 4
|
1022
|
+
)
|
1023
|
+
assert np.allclose(path_lengths_ref, path_lengths_w_path)
|
1024
|
+
|
1025
|
+
|
501
1026
|
# author : Francois Pacull
|
502
1027
|
# copyright : Architecture & Performance
|
503
1028
|
# email: francois.pacull@architecture-performance.fr
|