Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AmalgamDigraphs #532

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
58 changes: 58 additions & 0 deletions doc/oper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2257,3 +2257,61 @@ true
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="AmalgamDigraphs">
<ManSection>
<Oper Name="AmalgamDigraphs" Arg="D1, D2,
S[, map1[, map2]]"/>
<Returns>An immutable digraph and a transformation.</Returns>
<Description>

<C>AmalgamDigraphs</C> takes as input two digraphs <A>D1</A> and <A>D2</A>
and a digraph <A>S</A> for which there is an embedding into both
<A>D1</A> and <A>D2</A>. Additional optional arguments <A>map1</A> and
<A>map2</A> are transformation objects which can force specific
embeddings of <A>S</A> into <A>D1</A> and <A>D2</A> respectively. If
<A>map1</A> and <A>map2</A> are not given then arbitrary embeddings
will be found using <C>DigraphEmbedding</C>. If no embeddings can be found
the function will throw an error.<P/>

If <A>D1</A>, <A>D2</A> and <A>S</A> are not multidigraphs then
<C>AmalgamDigraphs</C> calculates a new digraph, the <E>amalgam digraph</E>
<M>D_A</M>. <M>D_A</M> is an amalgam of <A>D1</A> and <A>D2</A> over
<A>S</A> with respect to embeddings (where the embeddings of <A>S</A> into
<A>D1</A> and <A>D2</A> can be specified by <A>map1</A> and <A>map2</A>).
The embedding of <A>D1</A> into <M>D_A</M> is set to always be the
<C>IdentityTransformation</C>.<P/>

Note that <C>AmalgamDigraphs</C> does not necessarily return the smallest
possible digraph satisfying these properties. For examble, when
<A>D1</A> and <A>D2</A> are equal, the embedding from <A>D2</A>
to <M>D_A</M> will not be the <C>IdentityTransformation</C> and so
<M>D_A</M> could have many more vertices than the smallest possible amalgam
of <A>D1</A> and <A>D2</A> over <A>S</A>. A less formal way to picture
the exact form of <M>D_A</M> is to think of it as <A>D1</A> and <A>D2</A>
'joined together' by the common subdigraph <A>S</A>.<P/>

<C>AmalgamDigraphs</C> returns a <E>tuple</E> of size two, with the first
element being the digraph <M>A_D</M> and the second element being a
transformation object which describes the embedding of <A>D2</A> into
<M>A_D</M>.

<Example><![CDATA[
gap> D := CycleGraph(3);;
gap> S := PathGraph(2);;
gap> AmalgamDigraphs(D, D, S);
[ <immutable digraph with 4 vertices, 10 edges>,
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);
[ <immutable digraph with 7 vertices, 20 edges>,
Transformation( [ 6, 2, 4, 3, 7, 6, 7 ] ) ]
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>
1 change: 1 addition & 0 deletions doc/z-chap2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<#Include Label="DistanceDigraph">
<#Include Label="DigraphClosure">
<#Include Label="DigraphMycielskian">
<#Include Label="AmalgamDigraphs">
</Section>

<Section><Heading>Random digraphs</Heading>
Expand Down
10 changes: 10 additions & 0 deletions gap/oper.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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]);
Expand Down
164 changes: 164 additions & 0 deletions gap/oper.gi
Original file line number Diff line number Diff line change
Expand Up @@ -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(
finnbuck marked this conversation as resolved.
Show resolved Hide resolved
"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
###############################################################################
Expand Down
119 changes: 119 additions & 0 deletions tst/standard/oper.tst
Original file line number Diff line number Diff line change
Expand Up @@ -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);
[ <immutable digraph with 7 vertices, 15 edges>,
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);
[ <immutable digraph with 6 vertices, 13 edges>,
Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ]
gap> D1 := DigraphImmutableCopy(D1);
<immutable digraph with 5 vertices, 9 edges>
gap> U := AmalgamDigraphs(D1, D2, S, map1, map2);
[ <immutable digraph with 6 vertices, 13 edges>,
Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ]
gap> D1 := DigraphMutableCopy(D1);
<mutable digraph with 5 vertices, 9 edges>
gap> U := AmalgamDigraphs(D1, D2, S, map1, map2);
[ <immutable digraph with 6 vertices, 13 edges>,
Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ]
gap> D1;
<immutable digraph with 6 vertices, 13 edges>
gap> D1 := Digraph([[2, 5], [1, 3], [4], [2, 5], [1, 4]]);;
gap> D2 := DigraphMutableCopy(D2);
<mutable digraph with 4 vertices, 7 edges>
gap> U := AmalgamDigraphs(D1, D2, S, map1, map2);
[ <immutable digraph with 6 vertices, 13 edges>,
Transformation( [ 6, 2, 4, 3, 5, 6 ] ) ]
gap> D2;
<mutable digraph with 4 vertices, 7 edges>
gap> D1 := DigraphImmutableCopy(D1);
<immutable digraph with 5 vertices, 9 edges>
gap> U := AmalgamDigraphs(D1, D2, S, map1, map2);
[ <immutable digraph with 6 vertices, 13 edges>,
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);
[ <immutable digraph with 11 vertices, 32 edges>,
Transformation( [ 11, 1, 2, 5, 3, 4, 7, 8, 9, 10, 11 ] ) ]
gap> U := AmalgamDigraphs(D1, D2, S);
[ <immutable digraph with 11 vertices, 32 edges>,
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);
[ <immutable digraph with 6 vertices, 7 edges>,
Transformation( [ 2, 1, 4, 5, 6, 6 ] ) ]
gap> U := AmalgamDigraphs(D1, D2, S, map1);
[ <immutable digraph with 6 vertices, 7 edges>,
Transformation( [ 1, 4, 2, 5, 6, 6 ] ) ]
gap> U := AmalgamDigraphs(D1, D2, S);
[ <immutable digraph with 6 vertices, 7 edges>,
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);
Expand Down Expand Up @@ -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();
Expand Down