edsger 0.1.2__cp310-cp310-macosx_11_0_arm64.whl → 0.1.4__cp310-cp310-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 +146 -146
- edsger/dijkstra.c +7058 -2696
- edsger/dijkstra.cpython-310-darwin.so +0 -0
- edsger/dijkstra.pyx +507 -5
- edsger/path.py +145 -37
- edsger/path_tracking.c +146 -146
- edsger/pq_4ary_dec_0b.c +146 -146
- edsger/spiess_florian.c +146 -146
- edsger/star.c +146 -146
- {edsger-0.1.2.dist-info → edsger-0.1.4.dist-info}/METADATA +21 -14
- {edsger-0.1.2.dist-info → edsger-0.1.4.dist-info}/RECORD +16 -16
- {edsger-0.1.2.dist-info → edsger-0.1.4.dist-info}/WHEEL +0 -0
- {edsger-0.1.2.dist-info → edsger-0.1.4.dist-info}/licenses/AUTHORS.rst +0 -0
- {edsger-0.1.2.dist-info → edsger-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {edsger-0.1.2.dist-info → edsger-0.1.4.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,7 +35,7 @@ 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
|
|
26
|
-
#
|
38
|
+
# memory prefetching support (x86/x64 only)
|
27
39
|
cdef extern from "prefetch_compat.h":
|
28
40
|
void prefetch_hint(char*, int) nogil
|
29
41
|
int PREFETCH_T0
|
@@ -89,14 +101,14 @@ cpdef cnp.ndarray compute_sssp(
|
|
89
101
|
|
90
102
|
head_vert_idx = <size_t>csr_indices[idx]
|
91
103
|
|
92
|
-
#
|
104
|
+
# prefetch next iteration data to improve cache performance
|
93
105
|
if idx + 1 < <size_t>csr_indptr[tail_vert_idx + 1]:
|
94
106
|
prefetch_hint(<char*>&csr_indices[idx + 1], PREFETCH_T0)
|
95
107
|
prefetch_hint(<char*>&csr_data[idx + 1], PREFETCH_T0)
|
96
108
|
|
97
109
|
vert_state = pqueue.Elements[head_vert_idx].state
|
98
110
|
if vert_state != SCANNED:
|
99
|
-
#
|
111
|
+
# prefetch priority queue element data for the vertex
|
100
112
|
prefetch_hint(<char*>&pqueue.Elements[head_vert_idx], PREFETCH_T0)
|
101
113
|
|
102
114
|
head_vert_val = tail_vert_val + csr_data[idx]
|
@@ -114,6 +126,109 @@ cpdef cnp.ndarray compute_sssp(
|
|
114
126
|
return path_lengths
|
115
127
|
|
116
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
|
+
|
117
232
|
cpdef cnp.ndarray compute_sssp_w_path(
|
118
233
|
cnp.uint32_t[::1] csr_indptr,
|
119
234
|
cnp.uint32_t[::1] csr_indices,
|
@@ -179,14 +294,14 @@ cpdef cnp.ndarray compute_sssp_w_path(
|
|
179
294
|
|
180
295
|
head_vert_idx = <size_t>csr_indices[idx]
|
181
296
|
|
182
|
-
#
|
297
|
+
# prefetch next iteration data to improve cache performance
|
183
298
|
if idx + 1 < <size_t>csr_indptr[tail_vert_idx + 1]:
|
184
299
|
prefetch_hint(<char*>&csr_indices[idx + 1], PREFETCH_T0)
|
185
300
|
prefetch_hint(<char*>&csr_data[idx + 1], PREFETCH_T0)
|
186
301
|
|
187
302
|
vert_state = pqueue.Elements[head_vert_idx].state
|
188
303
|
if vert_state != SCANNED:
|
189
|
-
#
|
304
|
+
# prefetch priority queue element data for the vertex
|
190
305
|
prefetch_hint(<char*>&pqueue.Elements[head_vert_idx], PREFETCH_T0)
|
191
306
|
|
192
307
|
head_vert_val = tail_vert_val + csr_data[idx]
|
@@ -206,6 +321,115 @@ cpdef cnp.ndarray compute_sssp_w_path(
|
|
206
321
|
return path_lengths
|
207
322
|
|
208
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
|
+
|
209
433
|
cpdef cnp.ndarray compute_stsp(
|
210
434
|
cnp.uint32_t[::1] csc_indptr,
|
211
435
|
cnp.uint32_t[::1] csc_indices,
|
@@ -265,8 +489,17 @@ cpdef cnp.ndarray compute_stsp(
|
|
265
489
|
<size_t>csc_indptr[head_vert_idx + 1]):
|
266
490
|
|
267
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
|
+
|
268
498
|
vert_state = pqueue.Elements[tail_vert_idx].state
|
269
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
|
+
|
270
503
|
tail_vert_val = head_vert_val + csc_data[idx]
|
271
504
|
if vert_state == UNLABELED:
|
272
505
|
pq.insert(&pqueue, tail_vert_idx, tail_vert_val)
|
@@ -342,8 +575,17 @@ cpdef cnp.ndarray compute_stsp_w_path(
|
|
342
575
|
<size_t>csc_indptr[head_vert_idx + 1]):
|
343
576
|
|
344
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
|
+
|
345
584
|
vert_state = pqueue.Elements[tail_vert_idx].state
|
346
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
|
+
|
347
589
|
tail_vert_val = head_vert_val + csc_data[idx]
|
348
590
|
if vert_state == UNLABELED:
|
349
591
|
pq.insert(&pqueue, tail_vert_idx, tail_vert_val)
|
@@ -361,6 +603,216 @@ cpdef cnp.ndarray compute_stsp_w_path(
|
|
361
603
|
return path_lengths
|
362
604
|
|
363
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
|
+
|
364
816
|
# ============================================================================ #
|
365
817
|
# tests #
|
366
818
|
# ============================================================================ #
|
@@ -521,6 +973,56 @@ cpdef compute_stsp_02():
|
|
521
973
|
assert np.allclose(path_lengths_ref, path_lengths)
|
522
974
|
|
523
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
|
+
|
524
1026
|
# author : Francois Pacull
|
525
1027
|
# copyright : Architecture & Performance
|
526
1028
|
# email: francois.pacull@architecture-performance.fr
|