From f6e9006fba5855b788135872ae775ef010839e4f Mon Sep 17 00:00:00 2001 From: Markus Pfeiffer Date: Wed, 28 Aug 2024 14:35:29 +0100 Subject: [PATCH] Add iterator for K-Shortest-Paths in a weighted graph --- gap/weights.gd | 1 + gap/weights.gi | 72 ++++++++++++++++++++++++++++++++++++++++ tst/standard/weights.tst | 8 ++++- 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/gap/weights.gd b/gap/weights.gd index 071463c21..6c50101ad 100644 --- a/gap/weights.gd +++ b/gap/weights.gd @@ -33,3 +33,4 @@ DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Johnson"); DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_FloydWarshall"); DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Bellman_Ford"); DeclareGlobalFunction("DIGRAPHS_Edge_Weighted_Dijkstra"); +DeclareGlobalFunction("DIGRAPHS_ShortestPathsIterator"); diff --git a/gap/weights.gi b/gap/weights.gi index 5c5ef640a..988f43043 100644 --- a/gap/weights.gi +++ b/gap/weights.gi @@ -616,3 +616,75 @@ function(digraph, source) return rec(distances := distances, parents := parents, edges := edges); end); + +############################################################################# +# 4. Shortest Paths Iterator +############################################################################# +# +# returns an iterator that generates the (possibly empty) sequence of paths +# between source and dest +# +# the iterator needs to store +# - found paths +# - candidates +# - +# rec( found_paths := [], +InstallGlobalFunction(DIGRAPHS_ShortestPathsIterator, +function(digraph, source, dest) + local currentIterator, findNextPath; + + currentIterator := rec( + candidates := BinaryHeap(), + foundPaths := [ + EdgeWeightedDigraphShortestPath(digraph, source, dest) + ]); + + findNextPath := function(iter) + local currentShortestPath, currentShortestPathLength, spurNode, rootPath, + rootPathNode, modifiedGraph, foundPaths, i, p, spurPath, totalPath, + nextPath; + + currentShortestPath := Last(iter.foundPaths); + currentShortestPathLength := Length(currentShortestPath[1]); + foundPaths := iter.foundPaths; + + for i in [1 .. currentShortestPathLength] do + modifiedGraph := fail; + + spurNode := currentShortestPath[1][i]; + rootPath := [ + currentShortestPath[1]{[1..i]}, + currentShortestPath[2]{[1..i-1]} + ]; + + for p in foundPaths do + if rootPath = p[1]{[1..i]} then + # remove p[2][i] from Graph; + fi; + od; + + for rootPathNode in rootPath[1] do + if rootPathNode <> spurNode then + # remove rootPathNode from Graph; + fi; + od; + + spurPath := EdgeWeightedDigraphShortestPath(modifiedGraph, spurNode, dest); + totalPath := [ Concatenation(rootPath[1], spurPath[1]), + Concatenation(rootPath[2], spurPath[2]) ]; + + Push(iter.candidatePaths, totalPath); + od; + + if IsEmpty(iter.candidatePaths) then + return fail; + fi; + + nextPath := Pop(iter.candidatePaths); + Push(iter.foundPaths, nextPath); + + return nextPath; + end; + + return findNextPath(currentIterator); +end); diff --git a/tst/standard/weights.tst b/tst/standard/weights.tst index 9e5d2abdf..3fd851d34 100644 --- a/tst/standard/weights.tst +++ b/tst/standard/weights.tst @@ -272,10 +272,16 @@ t is the 1st argument, gap> EdgeWeightedDigraphShortestPath(d, 1, 3); [ [ 1, 2, 3 ], [ 1, 1 ] ] +# K Shortest Paths +gap> d := EdgeWeightedDigraph([[2], [3], [4], []], [[1], [1], [1], []]); +gap> shortest_path := EdgeWeightedDigraphShortestPath(d, 1, 4); +[ [ 1, 2, 3, 4 ], [ 1, 1, 1 ] ] +gap> iter := DIGRAPHS_ShortestPathsIterator(d, 1, 4); + # DIGRAPHS_UnbindVariables gap> Unbind(d); gap> Unbind(tree); # gap> DIGRAPHS_StopTest(); -gap> STOP_TEST("Digraphs package: standard/weights.tst", 0); \ No newline at end of file +gap> STOP_TEST("Digraphs package: standard/weights.tst", 0);