From 69a6d9dd5f43afb0425e679c49fa7cb0db507fa2 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 7 Apr 2022 18:21:00 +0100 Subject: [PATCH] Add DigraphRandomWalk with doc and tests --- doc/oper.xml | 30 ++++++++++++++++++++++++++++++ doc/z-chap4.xml | 1 + gap/oper.gd | 1 + gap/oper.gi | 35 +++++++++++++++++++++++++++++++++++ tst/standard/oper.tst | 30 ++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+) diff --git a/doc/oper.xml b/doc/oper.xml index 324951db2..f1c852436 100644 --- a/doc/oper.xml +++ b/doc/oper.xml @@ -1535,6 +1535,36 @@ gap> DigraphShortestPath(D, 1, 1); <#/GAPDoc> +<#GAPDoc Label="DigraphRandomWalk"> + + + A pair of lists. + + Returns a directed path corresponding to a random walk in the digraph + digraph, starting at vertex v and having length no more than + t. +

+ + A random walk is defined as follows. The path begins at v, and at + each step it follows a random edge leaving the current vertex. It continues + through the digraph in this way until it has traversed t edges, or + until it reaches a vertex with no out-edges (a sink) and therefore + cannot continue. +

+ + The output has the same form as that of . +

+ + D := Digraph([[1, 2], [3], [2, 4], [1], [2, 4]]); + +gap> DigraphRandomWalk(D, 1, 4); +[ [ 1, 2, 3, 2, 3 ], [ 2, 1, 1, 1 ] ] +]]> + + +<#/GAPDoc> + <#GAPDoc Label="IteratorOfPaths"> diff --git a/doc/z-chap4.xml b/doc/z-chap4.xml index 1524d671c..3689ed81d 100644 --- a/doc/z-chap4.xml +++ b/doc/z-chap4.xml @@ -68,6 +68,7 @@ <#Include Label="VerticesReachableFrom"> <#Include Label="DigraphPath"> <#Include Label="DigraphShortestPath"> + <#Include Label="DigraphRandomWalk"> <#Include Label="Dominators"> <#Include Label="DominatorTree"> <#Include Label="IteratorOfPaths"> diff --git a/gap/oper.gd b/gap/oper.gd index 45c48ea3c..5c1ea6046 100644 --- a/gap/oper.gd +++ b/gap/oper.gd @@ -125,6 +125,7 @@ DeclareOperation("IteratorOfPaths", [IsList, IsPosInt, IsPosInt]); DeclareOperation("IteratorOfPathsNC", [IsList, IsPosInt, IsPosInt]); DeclareOperation("IsReachable", [IsDigraph, IsPosInt, IsPosInt]); DeclareOperation("DigraphLongestDistanceFromVertex", [IsDigraph, IsPosInt]); +DeclareOperation("DigraphRandomWalk", [IsDigraph, IsPosInt, IsInt]); DeclareOperation("DigraphLayers", [IsDigraph, IsPosInt]); DeclareAttribute("DIGRAPHS_Layers", IsDigraph, "mutable"); diff --git a/gap/oper.gi b/gap/oper.gi index 2af8d27ac..b2426c308 100644 --- a/gap/oper.gi +++ b/gap/oper.gi @@ -1727,6 +1727,41 @@ function(D, v) return dist; end); +InstallMethod(DigraphRandomWalk, +"for a digraph, a pos int and a non-negative int", +[IsDigraph, IsPosInt, IsInt], +function(D, v, t) + local vertices, edge_indices, i, neighbours, index; + + # Check input + if v > DigraphNrVertices(D) then + ErrorNoReturn("the 2nd argument must be ", + "a vertex of the 1st argument ,"); + elif t < 0 then + ErrorNoReturn("the 3rd argument must be a non-negative int,"); + fi; + + # Prepare output lists + vertices := [v]; + edge_indices := []; + + # Iterate to desired length + for i in [1 .. t] do + neighbours := OutNeighboursOfVertex(D, v); + if IsEmpty(neighbours) then + break; # Sink: path ends here + fi; + # Follow a random edge + index := Random(1, Length(neighbours)); + v := neighbours[index]; + vertices[i + 1] := v; + edge_indices[i] := index; + od; + + # Format matches that of DigraphPath + return [vertices, edge_indices]; +end); + InstallMethod(DigraphLayers, "for a digraph, and a positive integer", [IsDigraph, IsPosInt], function(D, v) diff --git a/tst/standard/oper.tst b/tst/standard/oper.tst index 4c4aedf0a..5807a45d0 100644 --- a/tst/standard/oper.tst +++ b/tst/standard/oper.tst @@ -1443,6 +1443,36 @@ infinity gap> DigraphLongestDistanceFromVertex(gr, 16); Error, the 2nd argument must be a vertex of the 1st argument , +# DigraphRandomWalk +gap> gr := CompleteDigraph(5); + +gap> path := DigraphRandomWalk(gr, 1, 100);; +gap> Length(path[1]); +101 +gap> ForAll(path[1], i -> i in [1 .. 5]); +true +gap> Length(path[2]); +100 +gap> ForAll(path[2], i -> i in [1 .. 4]); +true +gap> gr := ChainDigraph(5); + +gap> DigraphRandomWalk(gr, 2, 100); +[ [ 2, 3, 4, 5 ], [ 1, 1, 1 ] ] +gap> DigraphRandomWalk(gr, 2, 2); +[ [ 2, 3, 4 ], [ 1, 1 ] ] +gap> DigraphRandomWalk(gr, 5, 100); +[ [ 5 ], [ ] ] +gap> gr := CompleteBipartiteDigraph(10, 8);; +gap> DigraphRandomWalk(gr, 3, 0); +[ [ 3 ], [ ] ] +gap> DigraphRandomWalk(gr, 19, 5); +Error, the 2nd argument must be a vertex of the 1st argument , +gap> DigraphRandomWalk(gr, 123, 5); +Error, the 2nd argument must be a vertex of the 1st argument , +gap> DigraphRandomWalk(gr, 3, -1); +Error, the 3rd argument must be a non-negative int, + # DigraphLayers gap> gr := CompleteDigraph(4);