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.
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: