diff --git a/doc/oper.xml b/doc/oper.xml index f1c852436..3d2a558ea 100644 --- a/doc/oper.xml +++ b/doc/oper.xml @@ -2257,3 +2257,61 @@ true <#/GAPDoc> + +<#GAPDoc Label="AmalgamDigraphs"> + + + An immutable digraph and a transformation. + + + AmalgamDigraphs takes as input two digraphs D1 and D2 + and a digraph S for which there is an embedding into both + D1 and D2. Additional optional arguments map1 and + map2 are transformation objects which can force specific + embeddings of S into D1 and D2 respectively. If + map1 and map2 are not given then arbitrary embeddings + will be found using DigraphEmbedding. If no embeddings can be found + the function will throw an error.

+ + If D1, D2 and S are not multidigraphs then + AmalgamDigraphs calculates a new digraph, the amalgam digraph + D_A. D_A is an amalgam of D1 and D2 over + 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.

+ + 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 + 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 + 'joined together' by the common subdigraph S.

+ + 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. + + D := CycleGraph(3);; +gap> S := PathGraph(2);; +gap> AmalgamDigraphs(D, D, S); +[ , + Transformation( [ 1, 2, 4, 4 ] ) ] +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> S := CycleGraph(3);; +gap> map1 := Transformation([2, 4, 3, 4]);; +gap> map2 := Transformation([2, 3, 4, 4]);; +gap> AmalgamDigraphs(D1, D2, S, map1, map2); +[ , + Transformation( [ 6, 2, 4, 3, 7, 6, 7 ] ) ] +]]> + + +<#/GAPDoc> diff --git a/doc/z-chap2.xml b/doc/z-chap2.xml index 9de59949e..8272edbf7 100644 --- a/doc/z-chap2.xml +++ b/doc/z-chap2.xml @@ -74,6 +74,7 @@ <#Include Label="DistanceDigraph"> <#Include Label="DigraphClosure"> <#Include Label="DigraphMycielskian"> + <#Include Label="AmalgamDigraphs">

Random digraphs diff --git a/gap/oper.gd b/gap/oper.gd index 5c1ea6046..9e34ea03f 100644 --- a/gap/oper.gd +++ b/gap/oper.gd @@ -46,6 +46,13 @@ DeclareOperation("StrongProduct", [IsDigraph, IsDigraph]); DeclareOperation("ConormalProduct", [IsDigraph, IsDigraph]); DeclareOperation("HomomorphicProduct", [IsDigraph, IsDigraph]); DeclareOperation("LexicographicProduct", [IsDigraph, IsDigraph]); +DeclareOperation("AmalgamDigraphs", + [IsDigraph, IsDigraph, IsDigraph, + IsTransformation, IsTransformation]); +DeclareOperation("AmalgamDigraphs", + [IsDigraph, IsDigraph, IsDigraph, IsTransformation]); +DeclareOperation("AmalgamDigraphs", + [IsDigraph, IsDigraph, IsDigraph]); DeclareSynonym("DigraphModularProduct", ModularProduct); DeclareSynonym("DigraphStrongProduct", StrongProduct); @@ -55,6 +62,9 @@ DeclareSynonym("DigraphLexicographicProduct", LexicographicProduct); DeclareGlobalFunction("DIGRAPHS_CombinationOperProcessArgs"); DeclareOperation("DIGRAPHS_GraphProduct", [IsDigraph, IsDigraph, IsFunction]); +DeclareOperation("NOCHECKS_AmalgamDigraphs", + [IsDigraph, IsDigraph, IsDigraph, + IsTransformation, IsTransformation]); # 4. Actions . . . DeclareOperation("OnDigraphs", [IsDigraph, IsPerm]); diff --git a/gap/oper.gi b/gap/oper.gi index b2426c308..e1e04dd8d 100644 --- a/gap/oper.gi +++ b/gap/oper.gi @@ -765,6 +765,170 @@ function(D1, D2, edge_function) return Digraph(edges); end); +InstallMethod(AmalgamDigraphs, +"for a digraph, a digraph, a digraph, a transformation, and a transformation", +[IsDigraph, IsDigraph, IsDigraph, IsTransformation, IsTransformation], +function(D1, D2, S, map1, map2) + local D, n, imageList1, imageList2, map, edge, T; + + if IsMultiDigraph(D1) then + ErrorNoReturn( + "the 1st argument (a digraph) must not satisfy IsMultiDigraph"); + elif IsMultiDigraph(D2) then + ErrorNoReturn( + "the 2nd argument (a digraph) must not satisfy IsMultiDigraph"); + elif IsMultiDigraph(S) then + ErrorNoReturn( + "the 3rd argument (a digraph) must not satisfy IsMultiDigraph"); + fi; + + if not IsDigraphEmbedding(S, D1, map1) then + ErrorNoReturn( + "the 4th argument (a transformation) is not ", + "a digraph embedding from the 3rd argument (a digraph) into ", + "the 1st argument (a digraph)"); + fi; + if not IsDigraphEmbedding(S, D2, map2) then + ErrorNoReturn( + "the 5th argument (a transformation) is not ", + "a digraph embedding from the 3rd argument (a digraph) into ", + "the 2nd argument (a digraph)"); + fi; + + # Create a mutable copy so that the function also works if + # D1 is immutable. If D1 is a mutable digraph then + # D1 will be changed in place. + D := DigraphMutableCopyIfImmutable(D1); + + n := DigraphNrVertices(D2) + DigraphNrVertices(D1) - DigraphNrVertices(S); + + # '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]; + + imageList1 := OnTuples([1 .. DigraphNrVertices(S)], map1); + imageList2 := OnTuples([1 .. DigraphNrVertices(S)], map2); + + map{imageList2} := imageList1; + map{Difference(DigraphVertices(D2), imageList2)} := + [DigraphNrVertices(D1) + 1 .. n]; + + T := Transformation(map); + + DigraphAddVertices(D, DigraphNrVertices(D2) - DigraphNrVertices(S)); + + for edge in DigraphEdges(D2) do + if not (edge[1] in imageList2 + and edge[2] in imageList2) then + DigraphAddEdge(D, [edge[1] ^ T, edge[2] ^ T]); + fi; + od; + + return [MakeImmutable(D), T]; +end); + +InstallMethod(AmalgamDigraphs, +"for a digraph, a digraph, a digraph, and a transformation", +[IsDigraph, IsDigraph, IsDigraph, IsTransformation], +function(D1, D2, S, map1) + local map2; + + if IsMultiDigraph(D1) then + ErrorNoReturn( + "the 1st argument (a digraph) must not satisfy IsMultiDigraph"); + elif IsMultiDigraph(D2) then + ErrorNoReturn( + "the 2nd argument (a digraph) must not satisfy IsMultiDigraph"); + elif IsMultiDigraph(S) then + ErrorNoReturn( + "the 3rd argument (a digraph) must not satisfy IsMultiDigraph"); + fi; + + if not IsDigraphEmbedding(S, D1, map1) then + ErrorNoReturn( + "the 4th argument (a transformation) is not ", + "a digraph embedding from the 3rd argument (a digraph) into ", + "the 1st argument (a digraph)"); + fi; + + map2 := DigraphEmbedding(S, D2); + if map2 = fail then + ErrorNoReturn( + "no embeddings could be found from the 3rd argument ", + "(a digraph) to the 2nd argument (a digraph)"); + fi; + + return NOCHECKS_AmalgamDigraphs(D1, D2, S, map1, map2); +end); + +InstallMethod(AmalgamDigraphs, +"for a digraph, a digraph, and a digraph", +[IsDigraph, IsDigraph, IsDigraph], +function(D1, D2, S) + local map1, map2; + + if IsMultiDigraph(D1) then + ErrorNoReturn( + "the 1st argument (a digraph) must not satisfy IsMultiDigraph"); + elif IsMultiDigraph(D2) then + ErrorNoReturn( + "the 2nd argument (a digraph) must not satisfy IsMultiDigraph"); + elif IsMultiDigraph(S) then + ErrorNoReturn( + "the 3rd argument (a digraph) must not satisfy IsMultiDigraph"); + fi; + + map1 := DigraphEmbedding(S, D1); + if map1 = fail then + ErrorNoReturn( + "no embeddings could be found from the 3rd argument ", + "(a digraph) to the 1st argument (a digraph)"); + fi; + + map2 := DigraphEmbedding(S, D2); + if map2 = fail then + ErrorNoReturn( + "no embeddings could be found from the 3rd argument ", + "(a digraph) to the 2nd argument (a digraph)"); + fi; + + return NOCHECKS_AmalgamDigraphs(D1, D2, S, map1, map2); +end); + +InstallMethod(NOCHECKS_AmalgamDigraphs, +"for a digraph, a digraph, a digraph, a transformation, and a transformation", +[IsDigraph, IsDigraph, IsDigraph, IsTransformation, IsTransformation], +function(D1, D2, S, map1, map2) + local D, n, imageList1, imageList2, map, edge, T; + + D := DigraphMutableCopyIfImmutable(D1); + + n := DigraphNrVertices(D2) + DigraphNrVertices(D1) - DigraphNrVertices(S); + + map := [1 .. n]; + + imageList1 := OnTuples([1 .. DigraphNrVertices(S)], map1); + imageList2 := OnTuples([1 .. DigraphNrVertices(S)], map2); + + map{imageList2} := imageList1; + map{Difference(DigraphVertices(D2), imageList2)} := + [DigraphNrVertices(D1) + 1 .. n]; + + T := Transformation(map); + + DigraphAddVertices(D, DigraphNrVertices(D2) - DigraphNrVertices(S)); + + for edge in DigraphEdges(D2) do + if not (edge[1] in imageList2 + and edge[2] in imageList2) then + DigraphAddEdge(D, [edge[1] ^ T, edge[2] ^ T]); + fi; + od; + + return [MakeImmutable(D), T]; +end); + ############################################################################### # 4. Actions ############################################################################### diff --git a/tst/standard/oper.tst b/tst/standard/oper.tst index 5807a45d0..f2f20aa8b 100644 --- a/tst/standard/oper.tst +++ b/tst/standard/oper.tst @@ -2787,6 +2787,122 @@ gap> path := DigraphPath(D, 5, 5);; gap> IsDigraphPath(D, path); true +# AmalgamDigraphs +gap> D1 := Digraph([[2, 3], [1, 3], [1, 2], [2], [3, 4]]);; +gap> D2 := Digraph([[2, 6], [1, 3, 5], [4], [3], [4, 6], [1, 5]]);; +gap> S := InducedSubdigraph(D1, [2, 3, 4, 5]);; +gap> T := DigraphEmbedding(S, D2);; +gap> U := AmalgamDigraphs(D2, D1, S, T); +[ , + Transformation( [ 7, 4, 3, 5, 2, 6, 7 ] ) ] +gap> U := AmalgamDigraphs(D1, D2, S, IdentityTransformation, T); +Error, the 4th argument (a transformation) is not a digraph embedding from the\ + 3rd argument (a digraph) into the 1st argument (a digraph) +gap> D1 := Digraph([ +> [2, 3], [1, 3, 4, 6], [1, 2, 5, 7], [2, 6], [3, 7], [2, 4, 7, 8], +> [3, 5, 6, 8], [6, 7]]);; +gap> D2 := Digraph([ +> [2, 3], [1, 4], [1, 5], [2, 5, 6], [3, 4, 7], [4, 7], [5, 6]]);; +gap> S := InducedSubdigraph(D1, [2, 3, 6, 7]);; +gap> T := DigraphEmbedding(S, D1);; +gap> U := AmalgamDigraphs(D1, D2, S, T, IdentityTransformation); +Error, the 5th argument (a transformation) is not a digraph embedding from the\ + 3rd argument (a digraph) into the 2nd argument (a digraph) +gap> D1 := Digraph([[2, 5], [1, 3], [4], [2, 5], [1, 4]]);; +gap> D2 := Digraph([[2, 3], [1, 4], [1, 2], [3]]);; +gap> S := Digraph([[2], [3], [1]]);; +gap> map1 := DigraphEmbedding(S, D1);; +gap> map2 := DigraphEmbedding(S, D2);; +gap> U := AmalgamDigraphs(D1, D2, S, map1, map2); +[ , + Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ] +gap> D1 := DigraphImmutableCopy(D1); + +gap> U := AmalgamDigraphs(D1, D2, S, map1, map2); +[ , + Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ] +gap> D1 := DigraphMutableCopy(D1); + +gap> U := AmalgamDigraphs(D1, D2, S, map1, map2); +[ , + Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ] +gap> D1; + +gap> D1 := Digraph([[2, 5], [1, 3], [4], [2, 5], [1, 4]]);; +gap> D2 := DigraphMutableCopy(D2); + +gap> U := AmalgamDigraphs(D1, D2, S, map1, map2); +[ , + Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ] +gap> D2; + +gap> D1 := DigraphImmutableCopy(D1); + +gap> U := AmalgamDigraphs(D1, D2, S, map1, map2); +[ , + Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ] +gap> D1 := PetersenGraph();; +gap> D2 := Digraph([[], [1, 3, 4], [1, 2, 5], [2, 6], [3, 6], [4, 5]]);; +gap> S := CycleGraph(5);; +gap> U := AmalgamDigraphs(D1, D2, S, IdentityTransformation); +[ , + Transformation( [ 11, 1, 2, 5, 3, 4, 7, 8, 9, 10, 11 ] ) ] +gap> U := AmalgamDigraphs(D1, D2, S); +[ , + Transformation( [ 11, 1, 2, 5, 3, 4, 7, 8, 9, 10, 11 ] ) ] +gap> D1 := Digraph([[2], [3, 4], [1], [1]]);; +gap> D2 := Digraph([[3], [1], [2, 4, 5], [], []]);; +gap> S := Digraph([[2], [3], [1]]);; +gap> map1 := Transformation([1, 2, 4, 4]);; +gap> map2 := Transformation([2, 1]);; +gap> U := AmalgamDigraphs(D1, D2, S, map1, map2); +[ , + Transformation( [ 2, 1, 4, 5, 6, 6 ] ) ] +gap> U := AmalgamDigraphs(D1, D2, S, map1); +[ , + Transformation( [ 1, 4, 2, 5, 6, 6 ] ) ] +gap> U := AmalgamDigraphs(D1, D2, S); +[ , + Transformation( [ 1, 3, 2, 5, 6, 6 ] ) ] +gap> U := AmalgamDigraphs(D1, D2, S, Transformation([3, 2, 1])); +Error, the 4th argument (a transformation) is not a digraph embedding from the\ + 3rd argument (a digraph) into the 1st argument (a digraph) +gap> U := AmalgamDigraphs(D1, D2, D1, IdentityTransformation); +Error, no embeddings could be found from the 3rd argument (a digraph) to the 2\ +nd argument (a digraph) +gap> U := AmalgamDigraphs(D1, D2, D1); +Error, no embeddings could be found from the 3rd argument (a digraph) to the 2\ +nd argument (a digraph) +gap> U := AmalgamDigraphs(D1, D2, D2); +Error, no embeddings could be found from the 3rd argument (a digraph) to the 1\ +st argument (a digraph) +gap> D1 := Digraph([[2, 3, 3], [3], []]);; +gap> D2 := Digraph([[2, 3], [3, 4], [4], []]);; +gap> S := Digraph([[2, 3], [3], []]);; +gap> AmalgamDigraphs(D1, D2, S); +Error, the 1st argument (a digraph) must not satisfy IsMultiDigraph +gap> AmalgamDigraphs(D1, D2, S, IdentityTransformation); +Error, the 1st argument (a digraph) must not satisfy IsMultiDigraph +gap> AmalgamDigraphs( +> D1, D2, S, IdentityTransformation, IdentityTransformation); +Error, the 1st argument (a digraph) must not satisfy IsMultiDigraph +gap> AmalgamDigraphs(D2, D1, S); +Error, the 2nd argument (a digraph) must not satisfy IsMultiDigraph +gap> AmalgamDigraphs(D2, D1, S, IdentityTransformation); +Error, the 2nd argument (a digraph) must not satisfy IsMultiDigraph +gap> AmalgamDigraphs( +> D2, D1, S, IdentityTransformation, IdentityTransformation); +Error, the 2nd argument (a digraph) must not satisfy IsMultiDigraph +gap> D1 := PetersenGraph();; +gap> S := Digraph([[2], [3, 3], [1]]);; +gap> AmalgamDigraphs(D1, D1, S); +Error, the 3rd argument (a digraph) must not satisfy IsMultiDigraph +gap> AmalgamDigraphs(D1, D1, S, IdentityTransformation); +Error, the 3rd argument (a digraph) must not satisfy IsMultiDigraph +gap> AmalgamDigraphs( +> D1, D1, S, IdentityTransformation, IdentityTransformation); +Error, the 3rd argument (a digraph) must not satisfy IsMultiDigraph + #DIGRAPHS_UnbindVariables gap> Unbind(a); gap> Unbind(adj); @@ -2849,6 +2965,9 @@ gap> Unbind(temp); gap> Unbind(U); gap> Unbind(u1); gap> Unbind(u2); +gap> Unbind(S); +gap> Unbind(map1); +gap> Unbind(map2); # gap> DIGRAPHS_StopTest();