@@ -1432,10 +1432,9 @@ def pnc_k_shortest_simple_paths(self, source, target, weight_function=None,
1432
1432
returned.
1433
1433
1434
1434
OUTPUT: iterator
1435
-
1436
1435
ALGORITHM:
1437
1436
1438
- This algorithm is based on the `feng_k_shortest_simple_paths` algorithm
1437
+ This algorithm is based on the `` feng_k_shortest_simple_paths` ` algorithm
1439
1438
in [Feng2014 ]_, but postpones the shortest path tree computation when non-simple
1440
1439
deviations occur. See Postponed Node Classification algorithm in [ACN2023 ]_
1441
1440
for the algorithm description.
@@ -1469,8 +1468,8 @@ def pnc_k_shortest_simple_paths(self, source, target, weight_function=None,
1469
1468
( 22. 0, [(5, 2, 5), (2, 0, 5), (0, 4, 2), (4, 3, 3), (3, 1, 7) ]) ,
1470
1469
( 23. 0, [(5, 2, 5), (2, 0, 5), (0, 3, 1), (3, 4, 2), (4, 1, 10) ]) ,
1471
1470
( 25. 0, [(5, 4, 9), (4, 0, 8), (0, 3, 1), (3, 1, 7) ]) ,
1472
- ( 26. 0, [(5, 4, 9), (4, 0, 8), (0, 3, 1), (3, 2, 4), (2, 1, 4) ]) ,
1473
1471
( 26. 0, [(5, 4, 9), (4, 0, 8), (0, 1, 9) ]) ,
1472
+ ( 26. 0, [(5, 4, 9), (4, 0, 8), (0, 3, 1), (3, 2, 4), (2, 1, 4) ]) ,
1474
1473
( 30. 0, [(5, 4, 9), (4, 3, 3), (3, 2, 4), (2, 0, 5), (0, 1, 9) ]) ]
1475
1474
sage: g = DiGraph( graphs. Grid2dGraph( 2, 6) . relabel( inplace=False))
1476
1475
sage: for u, v in g. edge_iterator( labels=False) :
@@ -1479,15 +1478,22 @@ def pnc_k_shortest_simple_paths(self, source, target, weight_function=None,
1479
1478
[4.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 8.0, 8.0,
1480
1479
8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 10.0, 10.0, 10.0, 10.0 ]
1481
1480
1482
- Same tests as yen_k_shortest_simple_paths::
1481
+ Same tests as `` yen_k_shortest_simple_paths`` ::
1483
1482
1484
- sage: g = DiGraph( [(1, 2, 20), (1, 3, 10), (1, 4, 30), (2, 5, 20), (3, 5, 10), (4, 5, 30) ])
1485
1483
sage: g = DiGraph( [(1, 2, 1), (2, 3, 1), (3, 4, 1), (4, 5, 1),
1486
1484
....: (1, 7, 1), (7, 8, 1), (8, 5, 1), (1, 6, 1),
1487
1485
....: (6, 9, 1), (9, 5, 1), (4, 2, 1), (9, 3, 1),
1488
1486
....: (9, 10, 1), (10, 5, 1), (9, 11, 1), (11, 10, 1) ])
1489
1487
sage: [w for w, P in pnc_k_shortest_simple_paths(g, 1, 5, by_weight=True, report_weight=True) ]
1490
1488
[3.0, 3.0, 4.0, 4.0, 5.0, 5.0 ]
1489
+
1490
+ More tests::
1491
+
1492
+ sage: D = graphs. Grid2dGraph( 5, 5) . relabel( inplace=False) . to_directed( )
1493
+ sage: A = [w for w, P in pnc_k_shortest_simple_paths(D, 0, 24, report_weight=True) ]
1494
+ sage: assert len( A) == 8512
1495
+ sage: for i in range( len( A) - 1) :
1496
+ .... : assert A[i ] <= A[i + 1 ]
1491
1497
"""
1492
1498
if not self .is_directed():
1493
1499
raise ValueError (" this algorithm works only for directed graphs" )
@@ -1574,17 +1580,19 @@ def pnc_k_shortest_simple_paths(self, source, target, weight_function=None,
1574
1580
path = idx_to_path[path_idx]
1575
1581
del idx_to_path[path_idx]
1576
1582
1577
- # ancestor_idx_dict[v] := the first vertex reachable by edges of
1578
- # first shortest path tree from v
1583
+ # ancestor_idx_dict[v] := the first vertex of ``path[:t+1]`` or ``path[-1]`` reachable by
1584
+ # edges of first shortest path tree from v when enumerating deviating edges
1585
+ # from ``path[t]``.
1579
1586
ancestor_idx_dict = {v: i for i, v in enumerate (path)}
1580
1587
1581
- def ancestor_idx_func (v ):
1582
- if v in ancestor_idx_dict:
1583
- return ancestor_idx_dict[v]
1588
+ def ancestor_idx_func (v , t ):
1584
1589
if v not in successor:
1585
1590
# target vertex is not reachable from v
1586
1591
return - 1
1587
- ancestor_idx_dict[v] = ancestor_idx_func(successor[v])
1592
+ if v in ancestor_idx_dict:
1593
+ if ancestor_idx_dict[v] <= t or ancestor_idx_dict[v] == len (path) - 1 :
1594
+ return ancestor_idx_dict[v]
1595
+ ancestor_idx_dict[v] = ancestor_idx_func(successor[v], t)
1588
1596
return ancestor_idx_dict[v]
1589
1597
1590
1598
if is_simple:
@@ -1601,12 +1609,12 @@ def pnc_k_shortest_simple_paths(self, source, target, weight_function=None,
1601
1609
yield P
1602
1610
1603
1611
# GET DEVIATION PATHS
1604
- original_cost = sidetrack_length(path[:dev_idx + 1 ])
1605
- for deviation_i in range (dev_idx, len (path) - 1 ):
1612
+ original_cost = cost
1613
+ for deviation_i in range (len (path) - 1 , dev_idx - 1 , - 1 ):
1606
1614
for e in G.outgoing_edge_iterator(path[deviation_i]):
1607
1615
if e[1 ] in path[:deviation_i + 2 ]: # e[1] is red or e in path
1608
1616
continue
1609
- ancestor_idx = ancestor_idx_func(e[1 ])
1617
+ ancestor_idx = ancestor_idx_func(e[1 ], deviation_i )
1610
1618
if ancestor_idx == - 1 :
1611
1619
continue
1612
1620
new_path = path[:deviation_i + 1 ] + tree_path(e[1 ])
@@ -1616,7 +1624,9 @@ def pnc_k_shortest_simple_paths(self, source, target, weight_function=None,
1616
1624
new_cost = original_cost + sidetrack_cost[(e[0 ], e[1 ])]
1617
1625
new_is_simple = ancestor_idx > deviation_i
1618
1626
candidate_paths.push(((- new_cost, new_is_simple), (new_path_idx, deviation_i + 1 )))
1619
- original_cost += sidetrack_cost[(path[deviation_i], path[deviation_i + 1 ])]
1627
+ if deviation_i == dev_idx:
1628
+ continue
1629
+ original_cost -= sidetrack_cost[(path[deviation_i - 1 ], path[deviation_i])]
1620
1630
else :
1621
1631
# get a path to target in G \ path[:dev_idx]
1622
1632
deviation = shortest_path_func(path[dev_idx], target,
0 commit comments