From ffdf33daad0ae3be6d02da74ce5ba8734d1b9071 Mon Sep 17 00:00:00 2001 From: Andrea Lee Hui Wen Date: Mon, 6 Dec 2021 19:28:02 +0000 Subject: [PATCH 1/6] doc/attr.xml --- bellman_ford.g | 45 ++++++++++++++++++++++++++++++++++++++++ doc/attr.xml | 30 +++++++++++++++++++++++++++ gap/attr.gd | 1 + gap/attr.gi | 48 +++++++++++++++++++++++++++++++++++++++++++ tst/standard/attr.tst | 18 ++++++++++++++++ 5 files changed, 142 insertions(+) create mode 100644 bellman_ford.g diff --git a/bellman_ford.g b/bellman_ford.g new file mode 100644 index 000000000..f3d980c5f --- /dev/null +++ b/bellman_ford.g @@ -0,0 +1,45 @@ +LoadPackage("Digraphs"); +bellmanford := function(digraph, source) +local distance, n, predecessor, i, inf, u, v, edge, w; +n := DigraphNrVertices(digraph); +#wouldn't work for weighted digraphs +inf := n+1; +distance := List([1..n],x->0); +predecessor := List([1..n],x->0); +for i in DigraphVertices(digraph) do + distance[i] := inf; + predecessor[i] := 0; +od; +distance[source] := 0; +for i in [1..n-1] do + for edge in DigraphEdges(digraph) do + u := edge[1]; + v := edge[2]; + #only works for unweighted graphs, w needs to be changed into a variable + w := 1; + if distance[u] + w < distance[v] then + distance[v] := distance[u] + w; + predecessor[v] := u; + fi; + od; +od; +for edge in DigraphEdges(digraph) do + u := edge[1]; + v := edge[2]; + #only works for unweighted graphs, w needs to be changed into a variable + w := 1; + if distance[u] + w < distance[v] then + Print("Graph contains a negative-weight cycle"); + fi; +od; +for i in DigraphVertices(digraph) do + if distance[i] >= inf then + distance[i] := fail; + fi; + if predecessor[i] = 0 then + predecessor[i] := fail; + fi; +od; +return [distance, predecessor]; +end; + diff --git a/doc/attr.xml b/doc/attr.xml index 9c880fdab..a432c5757 100644 --- a/doc/attr.xml +++ b/doc/attr.xml @@ -902,6 +902,36 @@ gap> DigraphShortestDistances(D); <#/GAPDoc> +<#GAPDoc Label="UnweightedBellmanFord"> + + + A list of integers or fail. + + If digraph is a digraph with n vertices, then this + function returns a list with two sublists of n entries, where each entry is + either a non-negative integer, or fail.

+ + If there is a directed path from source to vertex i, then + the value of the entry i in the first sublist is the length of the shortest such directed + path and the entry i in the second sublist is the vertex preceding the vertex of that entry. If no such directed path exists, then the value of i is fail. We use the convention that the distance from every vertex to + itself is 0 for all vertices i. +

+ + D := Digraph([[1, 2], [3], [1, 2], [4]]); + +gap> UnweightedBellmanFord(D,2) +[ [ 2, 0, 1, fail ], [ 3, fail, 2, fail ] ] +gap> D := CycleDigraph(IsMutableDigraph, 3); + +gap> UnweightedBellmanFord(D,3); +[ [ 1, 2, 0 ], [ 3, 1, fail ] ] +]]> + + +<#/GAPDoc> + + <#GAPDoc Label="DigraphDiameter"> diff --git a/gap/attr.gd b/gap/attr.gd index bf22fd30d..b67221290 100644 --- a/gap/attr.gd +++ b/gap/attr.gd @@ -45,6 +45,7 @@ DeclareAttribute("DigraphDegeneracy", IsDigraph); DeclareAttribute("DigraphDegeneracyOrdering", IsDigraph); DeclareAttribute("DIGRAPHS_Degeneracy", IsDigraph); DeclareAttribute("DigraphShortestDistances", IsDigraph); +DeclareOperation("UnweightedBellmanFord", [IsDigraph, IsPosInt]); DeclareAttribute("DigraphDiameter", IsDigraph); DeclareAttribute("DigraphGirth", IsDigraph); DeclareAttribute("DigraphOddGirth", IsDigraph); diff --git a/gap/attr.gi b/gap/attr.gi index 2c721ee9d..ca51e0aac 100644 --- a/gap/attr.gi +++ b/gap/attr.gi @@ -700,6 +700,54 @@ end); # returns the vertices (i.e. numbers) of ordered so that there are no # edges from to for all greater than . +InstallMethod(UnweightedBellmanFord, "for a digraph by out-neighbours", +[IsDigraph,IsPosInt], +function(digraph, source) + local distance, n, predecessor, i, inf, u, v, edge, w; + n := DigraphNrVertices(digraph); + #wouldn't work for weighted digraphs + inf := n+1; + distance := List([1..n],x->0); + predecessor := List([1..n],x->0); + for i in DigraphVertices(digraph) do + distance[i] := inf; + predecessor[i] := 0; + od; + distance[source] := 0; + for i in [1..n-1] do + for edge in DigraphEdges(digraph) do + u := edge[1]; + v := edge[2]; + #only works for unweighted graphs, w needs to be changed into a variable + w := 1; + if distance[u] + w < distance[v] then + distance[v] := distance[u] + w; + predecessor[v] := u; + fi; + od; + od; + for edge in DigraphEdges(digraph) do + u := edge[1]; + v := edge[2]; + #only works for unweighted graphs, w needs to be changed into a variable + w := 1; + if distance[u] + w < distance[v] then + Print("Graph contains a negative-weight cycle"); + fi; + od; + for i in DigraphVertices(digraph) do + if distance[i] >= inf then + distance[i] := fail; + fi; + if predecessor[i] = 0 then + predecessor[i] := fail; + fi; + od; + return [distance, predecessor]; +end); + + + InstallMethod(DigraphTopologicalSort, "for a digraph by out-neighbours", [IsDigraphByOutNeighboursRep], D -> DIGRAPH_TOPO_SORT(OutNeighbours(D))); diff --git a/tst/standard/attr.tst b/tst/standard/attr.tst index ba1ced5d9..5c0558caf 100644 --- a/tst/standard/attr.tst +++ b/tst/standard/attr.tst @@ -486,6 +486,24 @@ gap> DIGRAPH_ConnectivityDataForVertex(gr, 2);; gap> DigraphShortestDistances(gr); [ [ 0, 1, 1 ], [ 1, 0, 1 ], [ 1, 1, 0 ] ] +# UnweightedBellmanFord +gap> gr := Digraph([[1, 2], [3], [1, 2], [4]]); + +gap> UnweightedBellmanFord(gr,2); +[ [ 2, 0, 1, fail ], [ 3, fail, 2, fail ] ] +gap> gr := CycleDigraph(IsMutableDigraph, 3); + +gap> UnweightedBellmanFord(gr,3); +[ [ 1, 2, 0 ], [ 3, 1, fail ] ] +gap> gr := Digraph([[],[]]); + +gap> UnweightedBellmanFord(gr,2); +[ [ fail, 0 ], [ fail, fail ] ] +gap> gr := Digraph([[1],[2],[3],[4]]); + +gap> UnweightedBellmanFord(gr,2); +[ [ fail, 0, fail, fail ], [ fail, fail, fail, fail ] ] + # OutNeighbours and InNeighbours gap> gr := Digraph(rec(DigraphNrVertices := 10, > DigraphSource := [1, 1, 5, 5, 7, 10], From 75795bf9f1adae60dec48ab48326f901a83849d1 Mon Sep 17 00:00:00 2001 From: Andrea Lee Hui Wen Date: Mon, 6 Dec 2021 19:45:38 +0000 Subject: [PATCH 2/6] Implementing an unweighted version of the Bellman-Ford Algorithm --- bellman_ford.g | 45 --------------------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 bellman_ford.g diff --git a/bellman_ford.g b/bellman_ford.g deleted file mode 100644 index f3d980c5f..000000000 --- a/bellman_ford.g +++ /dev/null @@ -1,45 +0,0 @@ -LoadPackage("Digraphs"); -bellmanford := function(digraph, source) -local distance, n, predecessor, i, inf, u, v, edge, w; -n := DigraphNrVertices(digraph); -#wouldn't work for weighted digraphs -inf := n+1; -distance := List([1..n],x->0); -predecessor := List([1..n],x->0); -for i in DigraphVertices(digraph) do - distance[i] := inf; - predecessor[i] := 0; -od; -distance[source] := 0; -for i in [1..n-1] do - for edge in DigraphEdges(digraph) do - u := edge[1]; - v := edge[2]; - #only works for unweighted graphs, w needs to be changed into a variable - w := 1; - if distance[u] + w < distance[v] then - distance[v] := distance[u] + w; - predecessor[v] := u; - fi; - od; -od; -for edge in DigraphEdges(digraph) do - u := edge[1]; - v := edge[2]; - #only works for unweighted graphs, w needs to be changed into a variable - w := 1; - if distance[u] + w < distance[v] then - Print("Graph contains a negative-weight cycle"); - fi; -od; -for i in DigraphVertices(digraph) do - if distance[i] >= inf then - distance[i] := fail; - fi; - if predecessor[i] = 0 then - predecessor[i] := fail; - fi; -od; -return [distance, predecessor]; -end; - From 367e18ec1ff6a3be2e2e9dd383f4f88bc4be9c23 Mon Sep 17 00:00:00 2001 From: Andrea Lee Hui Wen Date: Wed, 19 Jan 2022 16:04:22 +0000 Subject: [PATCH 3/6] Correction to UnweightedBellmanFord description --- doc/attr.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/attr.xml b/doc/attr.xml index a432c5757..e3504ca18 100644 --- a/doc/attr.xml +++ b/doc/attr.xml @@ -911,9 +911,10 @@ gap> DigraphShortestDistances(D); function returns a list with two sublists of n entries, where each entry is either a non-negative integer, or fail.

- If there is a directed path from source to vertex i, then - the value of the entry i in the first sublist is the length of the shortest such directed - path and the entry i in the second sublist is the vertex preceding the vertex of that entry. If no such directed path exists, then the value of i is fail. We use the convention that the distance from every vertex to + If there is a directed path from source to vertex i, then for each i-th entry the first sublist contains + the length of the shortest directed path to that i-th vertex and second sublist contains the vertex preceding that i-th + vertex. If no such directed path exists, then the value of i is fail. + We use the convention that the distance from every vertex to itself is 0 for all vertices i.

From f22c4632c9a4578f38cb961b84d6ace574a9b4c6 Mon Sep 17 00:00:00 2001 From: Andrea Lee Hui Wen Date: Wed, 19 Jan 2022 16:04:52 +0000 Subject: [PATCH 4/6] Grammar corrections --- gap/attr.gi | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/gap/attr.gi b/gap/attr.gi index ca51e0aac..045e842b8 100644 --- a/gap/attr.gi +++ b/gap/attr.gi @@ -701,24 +701,24 @@ end); # edges from to for all greater than . InstallMethod(UnweightedBellmanFord, "for a digraph by out-neighbours", -[IsDigraph,IsPosInt], +[IsDigraph, IsPosInt], function(digraph, source) local distance, n, predecessor, i, inf, u, v, edge, w; n := DigraphNrVertices(digraph); - #wouldn't work for weighted digraphs - inf := n+1; - distance := List([1..n],x->0); - predecessor := List([1..n],x->0); + # wouldn't work for weighted digraphs + inf := n + 1; + distance := List([1 .. n], x -> 0); + predecessor := List([1 .. n], x -> 0); for i in DigraphVertices(digraph) do distance[i] := inf; predecessor[i] := 0; od; distance[source] := 0; - for i in [1..n-1] do + for i in [1 .. n - 1] do for edge in DigraphEdges(digraph) do u := edge[1]; v := edge[2]; - #only works for unweighted graphs, w needs to be changed into a variable + # only works for unweighted graphs, w needs to be changed into a variable w := 1; if distance[u] + w < distance[v] then distance[v] := distance[u] + w; @@ -729,7 +729,7 @@ function(digraph, source) for edge in DigraphEdges(digraph) do u := edge[1]; v := edge[2]; - #only works for unweighted graphs, w needs to be changed into a variable + # only works for unweighted graphs, w needs to be changed into a variable w := 1; if distance[u] + w < distance[v] then Print("Graph contains a negative-weight cycle"); @@ -746,8 +746,6 @@ function(digraph, source) return [distance, predecessor]; end); - - InstallMethod(DigraphTopologicalSort, "for a digraph by out-neighbours", [IsDigraphByOutNeighboursRep], D -> DIGRAPH_TOPO_SORT(OutNeighbours(D))); From 620d5f3d9f7e919a45e2e5e48a18efa1dba9effa Mon Sep 17 00:00:00 2001 From: Andrea Lee Hui Wen Date: Wed, 19 Jan 2022 16:41:04 +0000 Subject: [PATCH 5/6] Grammar corrections --- doc/attr.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/attr.xml b/doc/attr.xml index e3504ca18..f2862fc47 100644 --- a/doc/attr.xml +++ b/doc/attr.xml @@ -921,11 +921,11 @@ gap> DigraphShortestDistances(D); D := Digraph([[1, 2], [3], [1, 2], [4]]); -gap> UnweightedBellmanFord(D,2) +gap> UnweightedBellmanFord(D, 2) [ [ 2, 0, 1, fail ], [ 3, fail, 2, fail ] ] gap> D := CycleDigraph(IsMutableDigraph, 3); -gap> UnweightedBellmanFord(D,3); +gap> UnweightedBellmanFord(D, 3); [ [ 1, 2, 0 ], [ 3, 1, fail ] ] ]]> From bb363ede066a913b7bf44f5d83e05b56271a1132 Mon Sep 17 00:00:00 2001 From: Andrea Lee Hui Wen Date: Wed, 19 Jan 2022 17:11:53 +0000 Subject: [PATCH 6/6] Grammar corrections --- tst/standard/attr.tst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tst/standard/attr.tst b/tst/standard/attr.tst index 5c0558caf..919543dfd 100644 --- a/tst/standard/attr.tst +++ b/tst/standard/attr.tst @@ -489,19 +489,19 @@ gap> DigraphShortestDistances(gr); # UnweightedBellmanFord gap> gr := Digraph([[1, 2], [3], [1, 2], [4]]); -gap> UnweightedBellmanFord(gr,2); +gap> UnweightedBellmanFord(gr, 2); [ [ 2, 0, 1, fail ], [ 3, fail, 2, fail ] ] gap> gr := CycleDigraph(IsMutableDigraph, 3); -gap> UnweightedBellmanFord(gr,3); +gap> UnweightedBellmanFord(gr, 3); [ [ 1, 2, 0 ], [ 3, 1, fail ] ] -gap> gr := Digraph([[],[]]); +gap> gr := Digraph([[], []]); -gap> UnweightedBellmanFord(gr,2); +gap> UnweightedBellmanFord(gr, 2); [ [ fail, 0 ], [ fail, fail ] ] -gap> gr := Digraph([[1],[2],[3],[4]]); +gap> gr := Digraph([[1], [2], [3], [4]]); -gap> UnweightedBellmanFord(gr,2); +gap> UnweightedBellmanFord(gr, 2); [ [ fail, 0, fail, fail ], [ fail, fail, fail, fail ] ] # OutNeighbours and InNeighbours