edsger 0.1.1__cp39-cp39-macosx_11_0_arm64.whl → 0.1.3__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/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