edsger 0.1.1__cp312-cp312-win_amd64.whl → 0.1.3__cp312-cp312-win_amd64.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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.1"
1
+ __version__ = "0.1.3"
Binary file
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
edsger/path.py CHANGED
@@ -17,8 +17,12 @@ from edsger.commons import (
17
17
  from edsger.dijkstra import (
18
18
  compute_sssp,
19
19
  compute_sssp_w_path,
20
+ compute_sssp_early_termination,
21
+ compute_sssp_w_path_early_termination,
20
22
  compute_stsp,
21
23
  compute_stsp_w_path,
24
+ compute_stsp_early_termination,
25
+ compute_stsp_w_path_early_termination,
22
26
  )
23
27
  from edsger.path_tracking import compute_path
24
28
  from edsger.spiess_florian import compute_SF_in
@@ -285,6 +289,7 @@ class Dijkstra:
285
289
  return_inf=True,
286
290
  return_series=False,
287
291
  heap_length_ratio=1.0,
292
+ termination_nodes=None,
288
293
  ):
289
294
  """
290
295
  Runs shortest path algorithm between a given vertex and all other vertices in the graph.
@@ -303,6 +308,11 @@ class Dijkstra:
303
308
  as values.
304
309
  heap_length_ratio : float, optional (default=1.0)
305
310
  The heap length as a fraction of the number of vertices. Must be in the range (0, 1].
311
+ termination_nodes : array-like, optional (default=None)
312
+ List or array of vertex indices for early termination. For SSSP (orientation='out'),
313
+ these are target nodes to reach. For STSP (orientation='in'), these are source nodes
314
+ to find paths from. When provided, the algorithm stops once all specified nodes have
315
+ been processed, potentially improving performance. If None, runs to completion.
306
316
 
307
317
  Returns
308
318
  -------
@@ -355,49 +365,140 @@ class Dijkstra:
355
365
  )
356
366
  heap_length = int(np.rint(heap_length_ratio * self._n_vertices))
357
367
 
368
+ # validate and process termination_nodes
369
+ termination_nodes_array = None
370
+ if termination_nodes is not None:
371
+ try:
372
+ termination_nodes_array = np.array(termination_nodes, dtype=np.uint32)
373
+ except (ValueError, TypeError):
374
+ raise TypeError(
375
+ "argument 'termination_nodes' must be array-like of integers"
376
+ )
377
+
378
+ if termination_nodes_array.ndim != 1:
379
+ raise ValueError("argument 'termination_nodes' must be 1-dimensional")
380
+
381
+ if len(termination_nodes_array) == 0:
382
+ raise ValueError("argument 'termination_nodes' must not be empty")
383
+
384
+ # handle vertex permutation if needed
385
+ if self._permute:
386
+ termination_nodes_permuted = []
387
+ for termination_node in termination_nodes_array:
388
+ if termination_node not in self._permutation.vert_idx_old.values:
389
+ raise ValueError(
390
+ f"termination node {termination_node} not found in graph"
391
+ )
392
+ termination_node_new = self._permutation.loc[
393
+ self._permutation.vert_idx_old == termination_node,
394
+ "vert_idx_new",
395
+ ].iloc[0]
396
+ termination_nodes_permuted.append(termination_node_new)
397
+ termination_nodes_array = np.array(
398
+ termination_nodes_permuted, dtype=np.uint32
399
+ )
400
+ else:
401
+ # validate that termination nodes exist
402
+ if np.any(termination_nodes_array >= self._n_vertices) or np.any(
403
+ termination_nodes_array < 0
404
+ ):
405
+ raise ValueError(
406
+ "termination_nodes contains invalid vertex indices"
407
+ )
408
+
358
409
  # compute path length
359
410
  if not path_tracking:
360
411
  self._path_links = None
361
- if self._orientation == "in":
362
- path_length_values = compute_stsp(
363
- self.__indptr,
364
- self.__indices,
365
- self.__edge_weights,
366
- vertex_new,
367
- self._n_vertices,
368
- heap_length,
369
- )
412
+ if termination_nodes_array is not None:
413
+ # use early termination algorithms
414
+ if self._orientation == "in":
415
+ path_length_values = compute_stsp_early_termination(
416
+ self.__indptr,
417
+ self.__indices,
418
+ self.__edge_weights,
419
+ termination_nodes_array,
420
+ vertex_new,
421
+ self._n_vertices,
422
+ heap_length,
423
+ )
424
+ else:
425
+ path_length_values = compute_sssp_early_termination(
426
+ self.__indptr,
427
+ self.__indices,
428
+ self.__edge_weights,
429
+ termination_nodes_array,
430
+ vertex_new,
431
+ self._n_vertices,
432
+ heap_length,
433
+ )
370
434
  else:
371
- path_length_values = compute_sssp(
372
- self.__indptr,
373
- self.__indices,
374
- self.__edge_weights,
375
- vertex_new,
376
- self._n_vertices,
377
- heap_length,
378
- )
435
+ # use standard algorithms
436
+ if self._orientation == "in":
437
+ path_length_values = compute_stsp(
438
+ self.__indptr,
439
+ self.__indices,
440
+ self.__edge_weights,
441
+ vertex_new,
442
+ self._n_vertices,
443
+ heap_length,
444
+ )
445
+ else:
446
+ path_length_values = compute_sssp(
447
+ self.__indptr,
448
+ self.__indices,
449
+ self.__edge_weights,
450
+ vertex_new,
451
+ self._n_vertices,
452
+ heap_length,
453
+ )
379
454
  else:
380
455
  self._path_links = np.arange(0, self._n_vertices, dtype=np.uint32)
381
- if self._orientation == "in":
382
- path_length_values = compute_stsp_w_path(
383
- self.__indptr,
384
- self.__indices,
385
- self.__edge_weights,
386
- self._path_links,
387
- vertex_new,
388
- self._n_vertices,
389
- heap_length,
390
- )
456
+ if termination_nodes_array is not None:
457
+ # use early termination algorithms with path tracking
458
+ if self._orientation == "in":
459
+ path_length_values = compute_stsp_w_path_early_termination(
460
+ self.__indptr,
461
+ self.__indices,
462
+ self.__edge_weights,
463
+ self._path_links,
464
+ termination_nodes_array,
465
+ vertex_new,
466
+ self._n_vertices,
467
+ heap_length,
468
+ )
469
+ else:
470
+ path_length_values = compute_sssp_w_path_early_termination(
471
+ self.__indptr,
472
+ self.__indices,
473
+ self.__edge_weights,
474
+ self._path_links,
475
+ termination_nodes_array,
476
+ vertex_new,
477
+ self._n_vertices,
478
+ heap_length,
479
+ )
391
480
  else:
392
- path_length_values = compute_sssp_w_path(
393
- self.__indptr,
394
- self.__indices,
395
- self.__edge_weights,
396
- self._path_links,
397
- vertex_new,
398
- self._n_vertices,
399
- heap_length,
400
- )
481
+ # use standard algorithms with path tracking
482
+ if self._orientation == "in":
483
+ path_length_values = compute_stsp_w_path(
484
+ self.__indptr,
485
+ self.__indices,
486
+ self.__edge_weights,
487
+ self._path_links,
488
+ vertex_new,
489
+ self._n_vertices,
490
+ heap_length,
491
+ )
492
+ else:
493
+ path_length_values = compute_sssp_w_path(
494
+ self.__indptr,
495
+ self.__indices,
496
+ self.__edge_weights,
497
+ self._path_links,
498
+ vertex_new,
499
+ self._n_vertices,
500
+ heap_length,
501
+ )
401
502
 
402
503
  if self._permute:
403
504
  # permute back the path vertex indices
@@ -445,7 +546,7 @@ class Dijkstra:
445
546
 
446
547
  # reorder path lengths
447
548
  if return_series:
448
- if self._permute:
549
+ if self._permute and termination_nodes_array is None:
449
550
  self._permutation["path_length"] = path_length_values
450
551
  path_lengths_df = self._permutation[
451
552
  ["vert_idx_old", "path_length"]
@@ -457,9 +558,16 @@ class Dijkstra:
457
558
  path_lengths_series = pd.Series(path_length_values)
458
559
  path_lengths_series.index.name = "vertex_idx"
459
560
  path_lengths_series.name = "path_length"
561
+ if self._permute and termination_nodes_array is not None:
562
+ # For early termination with permutation, use original termination node indices
563
+ path_lengths_series.index = termination_nodes
460
564
 
461
565
  return path_lengths_series
462
566
 
567
+ # For early termination, return results directly (already in correct order)
568
+ if termination_nodes_array is not None:
569
+ return path_length_values
570
+
463
571
  if self._permute:
464
572
  self._permutation["path_length"] = path_length_values
465
573
  if return_inf:
Binary file
edsger/path_tracking.pyx CHANGED
@@ -43,7 +43,7 @@ cpdef cnp.ndarray compute_path(cnp.uint32_t[::1] path_links, int vertex_idx):
43
43
  cdef int _compute_path_first_pass(
44
44
  cnp.uint32_t[::1] path_links,
45
45
  int vertex_idx
46
- ) nogil noexcept:
46
+ ) noexcept nogil:
47
47
  """Returns the path length.
48
48
  """
49
49
 
@@ -69,7 +69,7 @@ cdef void _compute_path_second_pass(
69
69
  cnp.uint32_t[::1] path_links,
70
70
  cnp.uint32_t[::1] path_vertices,
71
71
  int vertex_idx
72
- ) nogil noexcept:
72
+ ) noexcept nogil:
73
73
  """Compute the sequence of vertices forming a path.
74
74
  """
75
75
  cdef size_t i, j, k
Binary file
edsger/pq_4ary_dec_0b.pxd CHANGED
@@ -17,13 +17,13 @@ cdef struct PriorityQueue:
17
17
  size_t* A # array storing the binary tree
18
18
  Element* Elements # array storing the elements
19
19
 
20
- cdef void init_pqueue(PriorityQueue*, size_t, size_t) nogil
21
- cdef void free_pqueue(PriorityQueue*) nogil
22
- cdef void insert(PriorityQueue*, size_t, DTYPE_t) nogil
23
- cdef DTYPE_t peek(PriorityQueue*) nogil
24
- cdef size_t extract_min(PriorityQueue*) nogil
25
- cdef bint is_empty(PriorityQueue*) nogil
26
- cdef void decrease_key(PriorityQueue*, size_t, DTYPE_t) nogil
20
+ cdef void init_pqueue(PriorityQueue*, size_t, size_t) noexcept nogil
21
+ cdef void free_pqueue(PriorityQueue*) noexcept nogil
22
+ cdef void insert(PriorityQueue*, size_t, DTYPE_t) noexcept nogil
23
+ cdef DTYPE_t peek(PriorityQueue*) noexcept nogil
24
+ cdef size_t extract_min(PriorityQueue*) noexcept nogil
25
+ cdef bint is_empty(PriorityQueue*) noexcept nogil
26
+ cdef void decrease_key(PriorityQueue*, size_t, DTYPE_t) noexcept nogil
27
27
  cdef cnp.ndarray copy_keys_to_numpy(PriorityQueue*, size_t)
28
28
 
29
29
 
edsger/pq_4ary_dec_0b.pyx CHANGED
@@ -47,7 +47,7 @@ from edsger.commons cimport (
47
47
  cdef void init_pqueue(
48
48
  PriorityQueue* pqueue,
49
49
  size_t heap_length,
50
- size_t element_count) nogil noexcept:
50
+ size_t element_count) noexcept nogil:
51
51
  """
52
52
  Initialize the priority queue.
53
53
 
@@ -77,7 +77,7 @@ cdef void init_pqueue(
77
77
 
78
78
  cdef void _initialize_element(
79
79
  PriorityQueue* pqueue,
80
- size_t element_idx) nogil noexcept:
80
+ size_t element_idx) noexcept nogil:
81
81
  """
82
82
  Initialize a single element.
83
83
 
@@ -92,7 +92,7 @@ cdef void _initialize_element(
92
92
 
93
93
 
94
94
  cdef void free_pqueue(
95
- PriorityQueue* pqueue) nogil noexcept:
95
+ PriorityQueue* pqueue) noexcept nogil:
96
96
  """
97
97
  Free the priority queue.
98
98
 
@@ -107,7 +107,7 @@ cdef void free_pqueue(
107
107
  cdef void insert(
108
108
  PriorityQueue* pqueue,
109
109
  size_t element_idx,
110
- DTYPE_t key) nogil noexcept:
110
+ DTYPE_t key) noexcept nogil:
111
111
  """
112
112
  Insert an element into the heap and reorder the heap.
113
113
 
@@ -134,7 +134,7 @@ cdef void insert(
134
134
  cdef void decrease_key(
135
135
  PriorityQueue* pqueue,
136
136
  size_t element_idx,
137
- DTYPE_t key_new) nogil noexcept:
137
+ DTYPE_t key_new) noexcept nogil:
138
138
  """
139
139
  Decrease the key of a element in the heap, given its element index.
140
140
 
@@ -154,7 +154,7 @@ cdef void decrease_key(
154
154
  key_new)
155
155
 
156
156
 
157
- cdef DTYPE_t peek(PriorityQueue* pqueue) nogil noexcept:
157
+ cdef DTYPE_t peek(PriorityQueue* pqueue) noexcept nogil:
158
158
  """
159
159
  Find heap min key.
160
160
 
@@ -174,7 +174,7 @@ cdef DTYPE_t peek(PriorityQueue* pqueue) nogil noexcept:
174
174
  return pqueue.Elements[pqueue.A[0]].key
175
175
 
176
176
 
177
- cdef bint is_empty(PriorityQueue* pqueue) nogil noexcept:
177
+ cdef bint is_empty(PriorityQueue* pqueue) noexcept nogil:
178
178
  """
179
179
  Check if the heap is empty.
180
180
 
@@ -190,7 +190,7 @@ cdef bint is_empty(PriorityQueue* pqueue) nogil noexcept:
190
190
  return isempty
191
191
 
192
192
 
193
- cdef size_t extract_min(PriorityQueue* pqueue) nogil noexcept:
193
+ cdef size_t extract_min(PriorityQueue* pqueue) noexcept nogil:
194
194
  """
195
195
  Extract element with min keay from the heap,
196
196
  and return its element index.
@@ -229,7 +229,7 @@ cdef size_t extract_min(PriorityQueue* pqueue) nogil noexcept:
229
229
  cdef cnp.ndarray copy_keys_to_numpy(
230
230
  PriorityQueue* pqueue,
231
231
  size_t vertex_count
232
- ) noexcept:
232
+ ):
233
233
  """
234
234
  Copy the keys into a numpy array.
235
235
 
@@ -261,7 +261,7 @@ cdef cnp.ndarray copy_keys_to_numpy(
261
261
  cdef void _exchange_nodes(
262
262
  PriorityQueue* pqueue,
263
263
  size_t node_i,
264
- size_t node_j) nogil noexcept:
264
+ size_t node_j) noexcept nogil:
265
265
  """
266
266
  Exchange two nodes in the heap.
267
267
 
@@ -286,7 +286,7 @@ cdef void _exchange_nodes(
286
286
 
287
287
  cdef void _min_heapify(
288
288
  PriorityQueue* pqueue,
289
- size_t node_idx) nogil noexcept:
289
+ size_t node_idx) noexcept nogil:
290
290
  """
291
291
  Re-order sub-tree under a given node (given its node index)
292
292
  until it satisfies the heap property.
@@ -363,7 +363,7 @@ cdef void _min_heapify(
363
363
  cdef void _decrease_key_from_node_index(
364
364
  PriorityQueue* pqueue,
365
365
  size_t node_idx,
366
- DTYPE_t key_new) nogil noexcept:
366
+ DTYPE_t key_new) noexcept nogil:
367
367
  """
368
368
  Decrease the key of an element in the heap, given its tree index.
369
369
 
@@ -594,7 +594,7 @@ cpdef is_empty_01():
594
594
  assert is_empty(&pqueue) == 1
595
595
  insert(&pqueue, 1, 3.0)
596
596
  assert is_empty(&pqueue) == 0
597
- idx = extract_min(&pqueue)
597
+ _ = extract_min(&pqueue)
598
598
  assert is_empty(&pqueue) == 1
599
599
 
600
600
  free_pqueue(&pqueue)
@@ -652,7 +652,7 @@ cpdef decrease_key_01():
652
652
  free_pqueue(&pqueue)
653
653
 
654
654
 
655
- cdef void heapsort(DTYPE_t[::1] values_in, DTYPE_t[::1] values_out) nogil:
655
+ cdef void heapsort(DTYPE_t[::1] values_in, DTYPE_t[::1] values_out) noexcept nogil:
656
656
  """
657
657
  Heap sort by inerting all the values into the priority queue,
658
658
  and extracting them.
@@ -676,8 +676,6 @@ cpdef sort_01(int n, random_seed=124):
676
676
  and with the numpy default sort function, compare the results.
677
677
  """
678
678
 
679
- cdef PriorityQueue pqueue
680
-
681
679
  rng = np.random.default_rng(random_seed)
682
680
  values_in = rng.random(size=n)
683
681
  values_out = np.empty_like(values_in, dtype=DTYPE_PY)
@@ -0,0 +1,21 @@
1
+ #ifndef PREFETCH_COMPAT_H
2
+ #define PREFETCH_COMPAT_H
3
+
4
+ // Cross-platform memory prefetching compatibility header
5
+
6
+ #if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm64__)
7
+ // ARM64 platforms - use ARM-specific prefetch
8
+ #define prefetch_hint(addr, hint) __builtin_prefetch((const void*)(addr), 0, 3)
9
+ #define PREFETCH_T0 0
10
+ #elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
11
+ // x86/x64 platforms - use SSE intrinsics
12
+ #include <xmmintrin.h>
13
+ #define prefetch_hint(addr, hint) _mm_prefetch((const char*)(addr), hint)
14
+ #define PREFETCH_T0 _MM_HINT_T0
15
+ #else
16
+ // Other platforms - no-op (compile time optimization will remove calls)
17
+ #define prefetch_hint(addr, hint) ((void)0)
18
+ #define PREFETCH_T0 0
19
+ #endif
20
+
21
+ #endif // PREFETCH_COMPAT_H
Binary file
Binary file
edsger/star.pyx CHANGED
@@ -204,7 +204,7 @@ cdef void _coo_to_csr_uint32(
204
204
  cnp.uint32_t [::1] Ax,
205
205
  cnp.uint32_t [::1] Bp,
206
206
  cnp.uint32_t [::1] Bj,
207
- cnp.uint32_t [::1] Bx) nogil noexcept:
207
+ cnp.uint32_t [::1] Bx) noexcept nogil:
208
208
 
209
209
  cdef:
210
210
  size_t i, row, dest
@@ -280,7 +280,7 @@ cpdef void _coo_to_csr_float64(
280
280
  cnp.float64_t [::1] Ax,
281
281
  cnp.uint32_t [::1] Bp,
282
282
  cnp.uint32_t [::1] Bj,
283
- cnp.float64_t [::1] Bx) nogil noexcept:
283
+ cnp.float64_t [::1] Bx) noexcept nogil:
284
284
 
285
285
  cdef:
286
286
  size_t i, row, dest
@@ -318,7 +318,7 @@ cpdef void _coo_to_csc_float64(
318
318
  cnp.float64_t [::1] Ax,
319
319
  cnp.uint32_t [::1] Bp,
320
320
  cnp.uint32_t [::1] Bi,
321
- cnp.float64_t [::1] Bx) nogil noexcept:
321
+ cnp.float64_t [::1] Bx) noexcept nogil:
322
322
 
323
323
  cdef:
324
324
  size_t i, col, dest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edsger
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Graph algorithms in Cython.
5
5
  Author-email: François Pacull <francois.pacull@architecture-performance.fr>
6
6
  Maintainer-email: François Pacull <francois.pacull@architecture-performance.fr>
@@ -42,7 +42,7 @@ Dynamic: license-file
42
42
 
43
43
  ![Tests Status](https://github.com/aetperf/edsger/actions/workflows/tests.yml/badge.svg?branch=release)
44
44
  [![codecov](https://codecov.io/gh/aetperf/edsger/branch/release/graph/badge.svg)](https://codecov.io/gh/aetperf/edsger)
45
- [![PyPI version](https://img.shields.io/pypi/v/edsger.svg)](https://pypi.org/project/edsger/)
45
+ [![PyPI version](https://img.shields.io/pypi/v/edsger.svg?refresh=1)](https://pypi.org/project/edsger/)
46
46
  [![Downloads](https://static.pepy.tech/badge/edsger)](https://pepy.tech/project/edsger)
47
47
  [![Python 3.9 | 3.10 | 3.11 | 3.12 | 3.13](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://pypi.org/project/edsger/)
48
48
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
@@ -70,20 +70,19 @@ from edsger.path import Dijkstra
70
70
  edges = pd.DataFrame({
71
71
  'tail': [0, 0, 1, 2, 2, 3],
72
72
  'head': [1, 2, 2, 3, 4, 4],
73
- 'weight': [1, 4, 2, 1, 3, 1]
73
+ 'weight': [1, 4, 2, 1.5, 3, 1]
74
74
  })
75
75
  edges
76
76
  ```
77
77
 
78
78
  | | tail | head | weight |
79
79
  |---:|-------:|-------:|---------:|
80
- | 0 | 0 | 1 | 1 |
81
- | 1 | 0 | 2 | 4 |
82
- | 2 | 1 | 2 | 2 |
83
- | 3 | 2 | 3 | 1 |
84
- | 4 | 2 | 4 | 3 |
85
- | 5 | 3 | 4 | 1 |
86
-
80
+ | 0 | 0 | 1 | 1.0 |
81
+ | 1 | 0 | 2 | 4.0 |
82
+ | 2 | 1 | 2 | 2.0 |
83
+ | 3 | 2 | 3 | 1.5 |
84
+ | 4 | 2 | 4 | 3.0 |
85
+ | 5 | 3 | 4 | 1.0 |
87
86
 
88
87
  ```python
89
88
  # Initialize the Dijkstra object
@@ -94,10 +93,18 @@ shortest_paths = dijkstra.run(vertex_idx=0)
94
93
  print("Shortest paths:", shortest_paths)
95
94
  ```
96
95
 
97
- Shortest paths: [0. 1. 3. 4. 5.]
96
+ Shortest paths: [0. 1. 3. 4.5 5.5]
98
97
 
99
98
  We get the shortest paths from the source node 0 to all other nodes in the graph. The output is an array with the shortest path length to each node. A path length is the sum of the weights of the edges in the path.
100
99
 
100
+ ## Why Use Edsger?
101
+
102
+ Edsger is designed to be **dataframe-friendly**, providing seamless integration with pandas workflows for graph algorithms. Also it is rather efficient. Our benchmarks on the USA road network (23.9M vertices, 57.7M edges) demonstrate nice performance:
103
+
104
+ <img src="https://raw.githubusercontent.com/aetperf/edsger/release/docs/source/assets/dijkstra_benchmark_comparison.png" alt="Dijkstra Performance Comparison" width="700">
105
+
106
+ *Benchmark performed on Intel i9-12900H Linux laptop.*
107
+
101
108
  ## Contributing
102
109
 
103
110
  We welcome contributions to the Edsger library. If you have any suggestions, bug reports, or feature requests, please open an issue on our [GitHub repository](https://github.com/aetperf/Edsger).
@@ -0,0 +1,27 @@
1
+ edsger/.gitignore,sha256=mr9Izcwvjgv215xjRKhWEZ7vsyrKWhMqvWjSLHRYDjk,13
2
+ edsger/__init__.py,sha256=lgtGe3cqdwWdO1DLEOx7fX3i8D4Z_2rXHSq7Xecf-NM,41
3
+ edsger/_version.py,sha256=R5TtpJu7Qu6sOarfDpp-5Oyy8Pi2Ir3VewCvsCQiAgo,21
4
+ edsger/commons.cp312-win_amd64.pyd,sha256=hvbl0R_hhQFu1mApSWr8-xQZj1U6rHtgUfv8HVK3vMs,23040
5
+ edsger/commons.pxd,sha256=UshKjr5ve3Or9u75xGyGPKRz1RwCCb5N-xgNevXZ4j4,464
6
+ edsger/commons.pyx,sha256=6Ze22eE_zwXPRAe550DEhEvu-b7hvKmwQu73rzzWMUE,839
7
+ edsger/dijkstra.cp312-win_amd64.pyd,sha256=Rl3PRWKmJbEstNfikjYAjMXUdG3sAWTxloppFz7RKps,252928
8
+ edsger/dijkstra.pyx,sha256=kBXFya0bugjp97xas145sZEUXtb89_Sg9v8IdWiURoE,37542
9
+ edsger/networks.py,sha256=hH9sgT5Ic4TLVCjxPNzMDWNjNDbqpXMxXxLeWxCpdLE,10730
10
+ edsger/path.py,sha256=2NtkhwN2HQUsoZn0Sl6UbFKWIcWVTvnE6D8IH-xEG88,35768
11
+ edsger/path_tracking.cp312-win_amd64.pyd,sha256=iPjPfWZn-79UB2l-KG9An04EzdOXGt80Z10iPO3JjHw,135168
12
+ edsger/path_tracking.pyx,sha256=H24TLmC53I8LjbM1S5E7gS8WEb5uE_PZ8nhG6BteMYA,1900
13
+ edsger/pq_4ary_dec_0b.cp312-win_amd64.pyd,sha256=apkpbNPgWBEziUn9TAqEPI3Unqgee-5esj3LHfUBQ4Y,163328
14
+ edsger/pq_4ary_dec_0b.pxd,sha256=VvXcQzJq3OGBptrbawtemagPimuqSCayGQ91Jrad894,1098
15
+ edsger/pq_4ary_dec_0b.pyx,sha256=IzvzQerf-LYy7weQpgI0f28Q8gUrR4ENaedekXs1Jeg,18486
16
+ edsger/prefetch_compat.h,sha256=AyAYq_ZHKk5ChaJDrZOAOYe6SprL0_2byjRbjcBGrsU,826
17
+ edsger/spiess_florian.cp312-win_amd64.pyd,sha256=YUe6UD3_VljB2-8sx6mj1BwdK1Bj25Kb9FHxZYnUlhc,192512
18
+ edsger/spiess_florian.pyx,sha256=tjfF9Iv8nqpp4lnv4KAjF-37ij0_SgQ0fnacVVKx-CE,9934
19
+ edsger/star.cp312-win_amd64.pyd,sha256=eDmVyKxhYkQK7YaY65GP6EjVdzhS55FbAG4hNIb-MXc,181760
20
+ edsger/star.pyx,sha256=9LAIXhlccEeDgT41ico7n57FJ7PKCzhPv4f22Lphn78,9602
21
+ edsger/utils.py,sha256=xYfOFIbYpAiZljhUOgGWy0TVNvWaMFCwbCLPBkzdVos,2097
22
+ edsger-0.1.3.dist-info/licenses/AUTHORS.rst,sha256=8udN6bgZHdHYcVzV38y6SPnF-x6Ks0uXxxFsic6Aces,110
23
+ edsger-0.1.3.dist-info/licenses/LICENSE,sha256=w7gRlruGxK3_4KTZAyJsOR2tML4UQgB-GNm2LerwJS0,1132
24
+ edsger-0.1.3.dist-info/METADATA,sha256=pd11DVCVJaNAwtzqYHZQu3HffnIgBUmwWwfuzgNTAF0,5314
25
+ edsger-0.1.3.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
26
+ edsger-0.1.3.dist-info/top_level.txt,sha256=QvhzFORJIIot6GzSnDrtGa9KQt9iifCbOC5ULlzY5dg,7
27
+ edsger-0.1.3.dist-info/RECORD,,
@@ -1,26 +0,0 @@
1
- edsger/.gitignore,sha256=mr9Izcwvjgv215xjRKhWEZ7vsyrKWhMqvWjSLHRYDjk,13
2
- edsger/__init__.py,sha256=lgtGe3cqdwWdO1DLEOx7fX3i8D4Z_2rXHSq7Xecf-NM,41
3
- edsger/_version.py,sha256=8oAxKUG747GUokmxjkrWejyJa5yPNEsoJDlXxoedxTw,21
4
- edsger/commons.cp312-win_amd64.pyd,sha256=crxW78SAffVvTahJTLiTFf3bdb8wl5hiZ4Fa5CD3K2M,23040
5
- edsger/commons.pxd,sha256=UshKjr5ve3Or9u75xGyGPKRz1RwCCb5N-xgNevXZ4j4,464
6
- edsger/commons.pyx,sha256=6Ze22eE_zwXPRAe550DEhEvu-b7hvKmwQu73rzzWMUE,839
7
- edsger/dijkstra.cp312-win_amd64.pyd,sha256=KACyaiEcqK9RF2ZT_cAXPExjqH24ulPzCynlAocvB3M,205824
8
- edsger/dijkstra.pyx,sha256=0Hs3ccSGWpKx53md3kHye344u63KaunHj_gmG3_T8EE,16830
9
- edsger/networks.py,sha256=hH9sgT5Ic4TLVCjxPNzMDWNjNDbqpXMxXxLeWxCpdLE,10730
10
- edsger/path.py,sha256=OnXbP8Mf1rcO_9m6XsDrdXCRcLMeOb2vOAb1eHcZfM8,30395
11
- edsger/path_tracking.cp312-win_amd64.pyd,sha256=qIxmq2NUDBqSOIrvoNb66b2CxPLnjVIZc0Q_Gu2Ib2Q,135168
12
- edsger/path_tracking.pyx,sha256=ZyIJQucG4-pyltgjsfGuZ7B6d0bFeovCuNh34LrBSvM,1900
13
- edsger/pq_4ary_dec_0b.cp312-win_amd64.pyd,sha256=blY1e7cGrBlNyqVIMvthKrmTzNSXGzEIO5CcJDsxls0,163328
14
- edsger/pq_4ary_dec_0b.pxd,sha256=s0IsejKiG95SPgS3EX8rG86w1gzEyAkXJMaq5zj3HzE,1035
15
- edsger/pq_4ary_dec_0b.pyx,sha256=x9rDOYMAjS6uCyp_VQYrI1yFBY4va4sTLXn05be4rTo,18521
16
- edsger/spiess_florian.cp312-win_amd64.pyd,sha256=9k9dTLuDHX97sfADkyiKI-JSJgFXH8qMtF3mpKHZRGs,193536
17
- edsger/spiess_florian.pyx,sha256=tjfF9Iv8nqpp4lnv4KAjF-37ij0_SgQ0fnacVVKx-CE,9934
18
- edsger/star.cp312-win_amd64.pyd,sha256=5KaOeohFtvkjcVH6bzjXkls_K5zYmOyDAEom9KlmU7I,181760
19
- edsger/star.pyx,sha256=50sopL56DnCQokHORsJ4IydQv_Zqs39W3WZLnadwgfk,9602
20
- edsger/utils.py,sha256=xYfOFIbYpAiZljhUOgGWy0TVNvWaMFCwbCLPBkzdVos,2097
21
- edsger-0.1.1.dist-info/licenses/AUTHORS.rst,sha256=8udN6bgZHdHYcVzV38y6SPnF-x6Ks0uXxxFsic6Aces,110
22
- edsger-0.1.1.dist-info/licenses/LICENSE,sha256=w7gRlruGxK3_4KTZAyJsOR2tML4UQgB-GNm2LerwJS0,1132
23
- edsger-0.1.1.dist-info/METADATA,sha256=I6IBbA-l70HNaSOT9dlFffY90wm0eeXqbukb_-no2qo,4789
24
- edsger-0.1.1.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
25
- edsger-0.1.1.dist-info/top_level.txt,sha256=QvhzFORJIIot6GzSnDrtGa9KQt9iifCbOC5ULlzY5dg,7
26
- edsger-0.1.1.dist-info/RECORD,,
File without changes