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();