diff --git a/.VERSION b/.VERSION index bc80560fa..4cda8f19e 100644 --- a/.VERSION +++ b/.VERSION @@ -1 +1 @@ -1.5.0 +1.5.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index ea6eba437..00b563dad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,22 @@ # CHANGELOG – Digraphs package for GAP -Copyright © 2014-21 by Jan De Beule, Julius Jonušas, James D. Mitchell, +Copyright © 2014-22 by Jan De Beule, Julius Jonušas, James D. Mitchell, Wilf A. Wilson, Michael Young et al. Licensing information can be found in the `LICENSE` file. +## Version 1.5.2 (released 30/03/2022) + +This is a very minor release containing technical changes for maintaining compatibility with other GAP packages. + +## Version 1.5.1 (released 29/03/2022) + +This minor release contains several bugfixes and technical changes. This includes: + +* Bugfix: vertex labels are no longer wrongly retained when using `DigraphEdgeUnion`. This was reported by [Wilf A. Wilson][] in [Issue #496](https://github.com/digraphs/Digraphs/issues/496) and fixed by Joseph Edwards in [PR #507](https://github.com/digraphs/Digraphs/pull/507). +* Bugfix: a segfault could be caused by calling `OutNeighbours` with an inappropriate argument. This was reported by [Wilf A. Wilson][] in [Issue #518](https://github.com/digraphs/Digraphs/issues/518) and fixed by [James D. Mitchell][] in [PR #519](https://github.com/digraphs/Digraphs/pull/519). +* [Wilf A. Wilson][] improved the performance of `DigraphAddEdge` for digraphs without edge labels in [PR #509](https://github.com/digraphs/Digraphs/pull/509). +* [Max Horn][] changed the declaration of the variable `Vertices` to improve compatibility with Grape in [PR #530](https://github.com/digraphs/Digraphs/pull/530). + ## Version 1.5.0 (released 27/10/2021) This is a fairly major release of the Digraphs package, containing some bugfixes and several new features. diff --git a/PackageInfo.g b/PackageInfo.g index aa97bc319..37e49dcc5 100644 --- a/PackageInfo.g +++ b/PackageInfo.g @@ -1,7 +1,7 @@ ############################################################################# ## ## PackageInfo.g -## Copyright (C) 2015-21 James D. Mitchell +## Copyright (C) 2015-22 James D. Mitchell ## ## Licensing information can be found in the README.md file of this package. ## @@ -9,15 +9,15 @@ ## ## <#GAPDoc Label="PKGVERSIONDATA"> -## +## ## ## ## ## ## ## -## -## +## +## ## <#/GAPDoc> _STANDREWSMATHS := Concatenation(["Mathematical Institute, North Haugh, ", @@ -28,8 +28,8 @@ _STANDREWSCS := Concatenation(["Jack Cole Building, North Haugh, ", SetPackageInfo(rec( PackageName := "Digraphs", Subtitle := "Graphs, digraphs, and multidigraphs in GAP", -Version := "1.5.0", -Date := "27/10/2021", # dd/mm/yyyy format +Version := "1.5.2", +Date := "30/03/2022", # dd/mm/yyyy format License := "GPL-3.0-or-later", ArchiveFormats := ".tar.gz", diff --git a/VERSIONS b/VERSIONS index 22cee7c0f..d37734bc2 100644 --- a/VERSIONS +++ b/VERSIONS @@ -1,13 +1,15 @@ ############################################################################# ## #W VERSIONS -#Y Copyright (C) 2015-21 James D. Mitchell +#Y Copyright (C) 2015-22 James D. Mitchell ## ## Licensing information can be found in the README.md file of this package. ## ############################################################################# ## +release 1.5.2 - 30/03/2022 +release 1.5.1 - 29/03/2022 release 1.5.0 - 27/10/2021 release 1.4.1 - 14/05/2021 release 1.4.0 - 27/01/2021 diff --git a/doc/oper.xml b/doc/oper.xml index d8d03e86c..3d2a558ea 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"> @@ -2250,12 +2280,12 @@ true S with respect to embeddings (where the embeddings of S into D1 and D2 can be specified by map1 and map2). The embedding of D1 into D_A is set to always be the - IdentityTransformation.

+ IdentityTransformation.

Note that AmalgamDigraphs does not necessarily return the smallest possible digraph satisfying these properties. For examble, when D1 and D2 are equal, the embedding from D2 - to D_A will not be the IdentityTransformation and so + to D_A will not be the IdentityTransformation and so D_A could have many more vertices than the smallest possible amalgam of D1 and D2 over S. A less formal way to picture the exact form of D_A is to think of it as D1 and D2 @@ -2264,7 +2294,7 @@ true AmalgamDigraphs returns a tuple of size two, with the first element being the digraph A_D and the second element being a transformation object which describes the embedding of D2 into - A_D. + A_D. D := CycleGraph(3);; @@ -2272,14 +2302,9 @@ gap> S := PathGraph(2);; gap> AmalgamDigraphs(D, D, S); [ , Transformation( [ 1, 2, 4, 4 ] ) ] -gap> D := PetersonGraph();; -gap> S := CycleGraph(5);; -gap> AmalgamDigraphs(D, D, S); -[ , - Transformation( [ 1, 2, 3, 4, 5, 11, 12, 13, 14, 15, 11, 12, 13, 14, 15 ] ) - ] gap> D1 := Digraph([[2, 3], [1, 3, 4], [1, 2, 4], [2, 3, 5], [4]]);; -gap> D2 := Digraph([[2, 3], [1, 3, 4], [1, 2, 4, 5], [2, 3, 5], [3, 4]]);; +gap> D2 := Digraph( +> [[2, 3], [1, 3, 4], [1, 2, 4, 5], [2, 3, 5], [3, 4]]);; gap> S := CycleGraph(3);; gap> map1 := Transformation([2, 4, 3, 4]);; gap> map2 := Transformation([2, 3, 4, 4]);; 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/grape.gi b/gap/grape.gi index d4ec6f22d..3bdcd4421 100644 --- a/gap/grape.gi +++ b/gap/grape.gi @@ -132,7 +132,7 @@ function(D) "the Grape graph will have fewer\n#I edges than the original,"); fi; - if not DIGRAPHS_IsGrapeLoaded then + if not DIGRAPHS_IsGrapeLoaded() then Info(InfoWarning, 1, "Grape is not loaded,"); fi; diff --git a/gap/oper.gd b/gap/oper.gd index f4abc08b0..9e34ea03f 100644 --- a/gap/oper.gd +++ b/gap/oper.gd @@ -46,13 +46,13 @@ DeclareOperation("StrongProduct", [IsDigraph, IsDigraph]); DeclareOperation("ConormalProduct", [IsDigraph, IsDigraph]); DeclareOperation("HomomorphicProduct", [IsDigraph, IsDigraph]); DeclareOperation("LexicographicProduct", [IsDigraph, IsDigraph]); -DeclareOperation("AmalgamDigraphs", +DeclareOperation("AmalgamDigraphs", [IsDigraph, IsDigraph, IsDigraph, IsTransformation, IsTransformation]); -DeclareOperation("AmalgamDigraphs", +DeclareOperation("AmalgamDigraphs", [IsDigraph, IsDigraph, IsDigraph, IsTransformation]); -DeclareOperation("AmalgamDigraphs", - [IsDigraph, IsDigraph, IsDigraph,]); +DeclareOperation("AmalgamDigraphs", + [IsDigraph, IsDigraph, IsDigraph]); DeclareSynonym("DigraphModularProduct", ModularProduct); DeclareSynonym("DigraphStrongProduct", StrongProduct); @@ -62,7 +62,7 @@ DeclareSynonym("DigraphLexicographicProduct", LexicographicProduct); DeclareGlobalFunction("DIGRAPHS_CombinationOperProcessArgs"); DeclareOperation("DIGRAPHS_GraphProduct", [IsDigraph, IsDigraph, IsFunction]); -DeclareOperation("NOCHECKS_AmalgamDigraphs", +DeclareOperation("NOCHECKS_AmalgamDigraphs", [IsDigraph, IsDigraph, IsDigraph, IsTransformation, IsTransformation]); @@ -135,6 +135,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 2ff8f6880..b8d671150 100644 --- a/gap/oper.gi +++ b/gap/oper.gi @@ -802,7 +802,7 @@ function(D1, D2, S, map1, map2) n := DigraphNrVertices(D2) + DigraphNrVertices(D1) - DigraphNrVertices(S); - # 'map' is an embedding of D2 into the final output graph. + # 'map' is an embedding of D2 into the final output graph. # The embedding of D1 into the final output graph is the identity mapping. map := [1 .. n]; @@ -856,7 +856,7 @@ function(D1, D2, S, map1) if map2 = fail then ErrorNoReturn( "no embeddings could be found from the 3rd argument ", - "(a digraph) to the 2nd argument (a digraph)"); + "(a digraph) to the 2nd argument (a digraph)"); fi; return NOCHECKS_AmalgamDigraphs(D1, D2, S, map1, map2); @@ -890,7 +890,7 @@ function(D1, D2, S) if map2 = fail then ErrorNoReturn( "no embeddings could be found from the 3rd argument ", - "(a digraph) to the 2nd argument (a digraph)"); + "(a digraph) to the 2nd argument (a digraph)"); fi; return NOCHECKS_AmalgamDigraphs(D1, D2, S, map1, map2); @@ -1891,6 +1891,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/init.g b/init.g index 58acdd12a..8b02db573 100644 --- a/init.g +++ b/init.g @@ -25,14 +25,14 @@ if not IsBound(DIGRAPH_OUT_NBS) and fi; BindGlobal("DIGRAPHS_IsGrapeLoaded", - IsPackageMarkedForLoading("grape", "4.8.1")); + {} -> IsPackageMarkedForLoading("grape", "4.8.1")); # To avoid warnings when GRAPE is not loaded if not IsBound(IsGraph) then IsGraph := ReturnFalse; fi; if not IsBound(Vertices) then - Vertices := IdFunc; + DeclareOperation("Vertices", [IsRecord]); fi; if not IsBound(Adjacency) then Adjacency := IdFunc; diff --git a/read.g b/read.g index c9a71258f..ce3a63a6f 100644 --- a/read.g +++ b/read.g @@ -8,7 +8,7 @@ ############################################################################# ## -if not DIGRAPHS_IsGrapeLoaded then +if not DIGRAPHS_IsGrapeLoaded() then Add(DIGRAPHS_OmitFromTests, "Graph("); fi; diff --git a/tst/standard/digraph.tst b/tst/standard/digraph.tst index d8cbffcb6..7acb51af2 100644 --- a/tst/standard/digraph.tst +++ b/tst/standard/digraph.tst @@ -211,7 +211,7 @@ gap> DigraphRange(gr); [ 2, 3, 2 ] gap> gr; -gap> if DIGRAPHS_IsGrapeLoaded then +gap> if DIGRAPHS_IsGrapeLoaded() then > g := Graph(gr); > if not Digraph(g) = gr then > Print("fail"); @@ -1663,7 +1663,7 @@ gap> MakeImmutable(D); # gap> D := NullDigraph(10); -gap> if DIGRAPHS_IsGrapeLoaded then +gap> if DIGRAPHS_IsGrapeLoaded() then > D := Graph(D); > if D <> rec( > adjacencies := [[]], diff --git a/tst/standard/grape.tst b/tst/standard/grape.tst index a121d7f68..ee7fca465 100644 --- a/tst/standard/grape.tst +++ b/tst/standard/grape.tst @@ -129,7 +129,7 @@ rec( adjacencies := [ [ 2, 4 ] ], group := Group([ (1,3), (1,2)(3,4) ]), schreierVector := [ -1, 2, 1, 2 ] ) # Digraph: copying group from Grape -gap> if DIGRAPHS_IsGrapeLoaded then +gap> if DIGRAPHS_IsGrapeLoaded() then > gr := Digraph(JohnsonGraph(5, 3)); > else > gr := JohnsonDigraph(5, 3); @@ -140,7 +140,7 @@ gap> HasDigraphGroup(gr); true gap> DigraphGroup(gr); Group([ (1,7,10,6,3)(2,8,4,9,5), (4,7)(5,8)(6,9) ]) -gap> if DIGRAPHS_IsGrapeLoaded then +gap> if DIGRAPHS_IsGrapeLoaded() then > gr := Digraph(CompleteGraph(Group((1, 2, 3), (1, 2)))); > else > gr := Digraph([[2, 3], [1, 3], [1, 2]]); @@ -150,7 +150,7 @@ gap> HasDigraphGroup(gr); true gap> DigraphGroup(gr); Group([ (1,2,3), (1,2) ]) -gap> if DIGRAPHS_IsGrapeLoaded then +gap> if DIGRAPHS_IsGrapeLoaded() then > gr := Digraph(Graph(Group([()]), > [1, 2, 3], > OnPoints, @@ -252,7 +252,7 @@ true # Graph gap> gr := Digraph([[2, 2], []]); -gap> if DIGRAPHS_IsGrapeLoaded then +gap> if DIGRAPHS_IsGrapeLoaded() then > Graph(gr); > fi; diff --git a/tst/standard/oper.tst b/tst/standard/oper.tst index 06d14646a..f2f20aa8b 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); diff --git a/tst/testinstall.tst b/tst/testinstall.tst index db8b3d116..abbaad0e7 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -23,11 +23,11 @@ gap> OutNeighbours(gr); [ [ 8 ], [ 4, 5, 6, 8, 9 ], [ 2, 4, 5, 7, 10 ], [ 9 ], [ 1, 4, 6, 7, 9 ], [ 2, 3, 6, 7, 10 ], [ 3, 4, 5, 8, 9 ], [ 3, 4, 9, 10 ], [ 1, 2, 3, 5, 6, 9, 10 ], [ 2, 4, 5, 6, 9 ] ] -gap> not DIGRAPHS_IsGrapeLoaded -> or (DIGRAPHS_IsGrapeLoaded and Digraph(Graph(gr)) = gr); +gap> not DIGRAPHS_IsGrapeLoaded() +> or (DIGRAPHS_IsGrapeLoaded() and Digraph(Graph(gr)) = gr); true -gap> not DIGRAPHS_IsGrapeLoaded -> or (DIGRAPHS_IsGrapeLoaded and Graph(Digraph(Graph(gr))).adjacencies = +gap> not DIGRAPHS_IsGrapeLoaded() +> or (DIGRAPHS_IsGrapeLoaded() and Graph(Digraph(Graph(gr))).adjacencies = > Graph(gr).adjacencies); true gap> adj := [ @@ -38,8 +38,8 @@ gap> adj := [ > [1, 6, 8, 9, 11, 12, 13, 14], [2, 4, 7, 9, 10, 11, 13, 15, 16]];; gap> func := function(x, y) return y in adj[x]; end; function( x, y ) ... end -gap> not DIGRAPHS_IsGrapeLoaded or -> (DIGRAPHS_IsGrapeLoaded and +gap> not DIGRAPHS_IsGrapeLoaded() or +> (DIGRAPHS_IsGrapeLoaded() and > Digraph(Graph(Group(()), [1 .. 20], OnPoints, func, true)) = Digraph(adj)); true