Skip to content

Commit

Permalink
Addressed issues with previous pull request. Made description of amal…
Browse files Browse the repository at this point in the history
…gam more rigorous.
  • Loading branch information
finnbuck committed May 11, 2022
1 parent 49e5ef5 commit 781dcb9
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 144 deletions.
115 changes: 49 additions & 66 deletions doc/oper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2231,78 +2231,61 @@ true
<#GAPDoc Label="AmalgamDigraphs">
<ManSection>
<Oper Name="AmalgamDigraphs" Arg="D1, D2,
subdigraphVertices1, subdigraphVertices2"/>
<Returns>An immutable digraph and a record.</Returns>
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 two lists of vertices <A>subdigraphVertices1</A> and
<A>subdigraphVertices2</A>, which correspond to two identical subdigraphs
of <A>D1</A> and <A>D2</A>. It returns a new digraph, the <E>amalgam
digraph</E> <C>AD</C>, which consists of <A>D1</A> and <A>D2</A> joined
together by their common subdigraph in such a way that the edge connectivity
between the vertices of <C>AD</C> matches the edge connectivity of
<A>D1</A> and <A>D2</A>.<P/>

It returns a <E>tuple</E> of size two, with the first element being
<C>AD</C> and the second element being <C>map</C>, which is a record which
maps each vertex's number in <A>D2</A> to the corresponding vertex's number
in <C>AD</C>. The mapping of the vertices of <A>D1</A> can be seen as the
<E>identity mapping</E>.
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> T := Digraph([[2, 3], [1, 3], [1, 2]]);;
gap> A := AmalgamDigraphs(T, T, [1, 2], [1, 2]);
gap> D := CycleGraph(3);;
gap> S := PathGraph(2);;
gap> AmalgamDigraphs(D, D, S);
[ <immutable digraph with 4 vertices, 10 edges>,
rec( 1 := 1, 2 := 2, 3 := 4 ) ]
gap> A := AmalgamDigraphs(A[1], T, [1, 2], [1, 2]);
[ <immutable digraph with 5 vertices, 14 edges>,
rec( 1 := 1, 2 := 2, 3 := 5 ) ]
gap> P := PetersenGraph();;
gap> G := Digraph([[2, 3, 4], [1, 3], [1, 2, 5],
> [1, 6], [3, 6], [4, 5]]);;
gap> A := AmalgamDigraphs(P, G, [1, 2, 7, 10, 5], [1, 3, 5, 6, 4]);
[ <immutable digraph with 11 vertices, 34 edges>,
rec( 1 := 1, 2 := 11, 3 := 2, 4 := 5, 5 := 7, 6 := 10 ) ]
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="AmalgamDigraphsIsomorphic">
<ManSection>
<Oper Name="AmalgamDigraphsIsomorphic" Arg="D1, D2,
subdigraphVertices1, subdigraphVertices2"/>
<Returns>An immutable digraph and a record.</Returns>
<Description>

<C>AmalgamDigraphsIsomorphic</C> is meant to function very similarly to
<C>AmalgamDigraphs</C>. The difference is that in <C>AmalgamDigraphs</C>
<A>subdigraphVertices1</A> and <A>subdigraphVertices2</A> need not
necessarily describe identical subdigraphs of <A>D1</A> and <A>D2</A>,
but need only describe subdigraphs that are isomorphic to one another.
<C>AmalgamDigraphsIsomorphic</C> rearranges the entries of
<A>subdigraphVertices2</A> to obtain <C>newSubdigraphVertices2</C>
in such a way that the induced subdigraph in <A>D2</A> with the
vertices <C>newSubdigraphVertices2</C> is identical to the induced
subdigraph in <A>D1</A> with the vertices <C>subdigraphVertices1</C>.
<C>AmalgamDigraphsIsomorphic</C> then calls <C>AmalgamDigraphs</C>
with <C>newSubdigraphVertices2</C> in the place of
<A>subdigraphVertices2</A>.

<Example><![CDATA[
gap> P := PetersenGraph();;
gap> G := Digraph([[2, 3, 4], [1, 3],
> [1, 2, 5], [1, 6], [3, 6], [4, 5]]);;
gap> A := AmalgamDigraphs(P, G, [1, 2, 7, 10, 5], [1, 3, 5, 6, 4]);
[ <immutable digraph with 11 vertices, 34 edges>,
rec( 1 := 1, 2 := 11, 3 := 2, 4 := 5, 5 := 7, 6 := 10 ) ]
gap> A := AmalgamDigraphsIsomorphic(P, G,
> [1, 2, 7, 10, 5], [1, 4, 6, 3, 5]);
[ <immutable digraph with 11 vertices, 34 edges>,
rec( 1 := 1, 2 := 11, 3 := 5, 4 := 2, 5 := 10, 6 := 7 ) ]
gap> A := AmalgamDigraphs(P, G, [1, 2, 7, 10, 5], [1, 4, 6, 3, 5]);
Error, the two subdigraphs must be equal.
Transformation( [ 1, 2, 4, 4 ] ) ]
gap> D := PetersonGraph();;
gap> S := CycleGraph(5);;
gap> AmalgamDigraphs(D, D, S);
[ <immutable digraph with 15 vertices, 50 edges>,
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> 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>
Expand Down
1 change: 0 additions & 1 deletion doc/z-chap2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
<#Include Label="DigraphClosure">
<#Include Label="DigraphMycielskian">
<#Include Label="AmalgamDigraphs">
<#Include Label="AmalgamDigraphsIsomorphic">
</Section>

<Section><Heading>Random digraphs</Heading>
Expand Down
13 changes: 10 additions & 3 deletions gap/oper.gd
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ DeclareOperation("StrongProduct", [IsDigraph, IsDigraph]);
DeclareOperation("ConormalProduct", [IsDigraph, IsDigraph]);
DeclareOperation("HomomorphicProduct", [IsDigraph, IsDigraph]);
DeclareOperation("LexicographicProduct", [IsDigraph, IsDigraph]);
DeclareOperation("AmalgamDigraphs", [IsDigraph, IsDigraph, IsList, IsList]);
DeclareOperation("AmalgamDigraphsIsomorphic",
[IsDigraph, IsDigraph, IsList, IsList]);
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 @@ -58,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
177 changes: 130 additions & 47 deletions gap/oper.gi
Original file line number Diff line number Diff line change
Expand Up @@ -765,80 +765,163 @@ function(D1, D2, edge_function)
return Digraph(edges);
end);

InstallMethod(AmalgamDigraphsIsomorphic,
InstallMethod(AmalgamDigraphs,
"for a digraph, a digraph, a list, and a list",
[IsDigraph, IsDigraph, IsList, IsList],
function(D1, D2, subdigraphVertices1, subdigraphVertices2)
local subdigraph1, subdigraph2, newSubdigraphVertices2, transformation, vertex;
[IsDigraph, IsDigraph, IsDigraph, IsTransformation, IsTransformation],
function(D1, D2, S, map1, map2)
local D, n, imageList1, imageList2, map, edge, T;

subdigraph1 := InducedSubdigraph(DigraphImmutableCopyIfMutable(D1),
subdigraphVertices1);
subdigraph2 := InducedSubdigraph(DigraphImmutableCopyIfMutable(D2),
subdigraphVertices2);
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 IsIsomorphicDigraph(subdigraph1, subdigraph2) then
if not IsDigraphEmbedding(S, D1, map1) then
ErrorNoReturn(
"the subdigraph induced by the 3rd argument (a list) in the 1st ",
"argument (a digraph) is not ismorphic to the subdigraph induced ",
"by the 4th argument (a list) in the 2nd argument (a digraph)");
"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.

newSubdigraphVertices2 := [];
transformation := DigraphEmbedding(subdigraph2, subdigraph1);
for vertex in subdigraphVertices2 do
newSubdigraphVertices2[
Position(subdigraphVertices2, vertex) ^ transformation] := vertex;
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 AmalgamDigraphs(D1, D2, subdigraphVertices1, newSubdigraphVertices2);
return [MakeImmutable(D), T];
end);

InstallMethod(AmalgamDigraphs,
"for a digraph, a digraph, a list, and a list",
[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, a list, and a list",
[IsDigraph, IsDigraph, IsList, IsList],
function(D1, D2, subdigraphVertices1, subdigraphVertices2)
local D, n, map, T, vertex, edge;

if not InducedSubdigraph(DigraphImmutableCopyIfMutable(D1),
subdigraphVertices1) =
InducedSubdigraph(DigraphImmutableCopyIfMutable(D2),
subdigraphVertices2) then
[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 subdigraph induced by the 3rd argument (a list) in the 1st ",
"argument (a digraph) does not equal the subdigraph induced by the ",
"4th argument (a list) in the 2nd argument (a digraph)");
"the 3rd argument (a digraph) must not satisfy IsMultiDigraph");
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);
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;

n := DigraphNrVertices(D2) + DigraphNrVertices(D1);
n := n - Length(subdigraphVertices1);
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;

# 'map' is a mapping from the vertices of D2 to the vertices of the
# final output graph. The idea is to map the subdigraph vertices of D2
# onto the subdigraph vertices of D1 and then map the rest of the vertices
# of D2 to other (higher) values. The mapping from D1 to the output graph
# can be understood as the identity mapping.
return NOCHECKS_AmalgamDigraphs(D1, D2, S, map1, map2);
end);

InstallMethod(NOCHECKS_AmalgamDigraphs,
"for a digraph, a digraph, a list, and a list",
[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];

for vertex in [1 .. Length(subdigraphVertices1)] do
map[subdigraphVertices2[vertex]] := subdigraphVertices1[vertex];
od;
imageList1 := OnTuples([1 .. DigraphNrVertices(S)], map1);
imageList2 := OnTuples([1 .. DigraphNrVertices(S)], map2);

map{Difference(DigraphVertices(D2), subdigraphVertices2)} :=
map{imageList2} := imageList1;
map{Difference(DigraphVertices(D2), imageList2)} :=
[DigraphNrVertices(D1) + 1 .. n];

T := Transformation(map);

DigraphAddVertices(D, DigraphNrVertices(D2) - Length(subdigraphVertices1));
DigraphAddVertices(D, DigraphNrVertices(D2) - DigraphNrVertices(S));

for edge in DigraphEdges(D2) do
if not (edge[1] in subdigraphVertices2
and edge[2] in subdigraphVertices2) then
if not (edge[1] in imageList2
and edge[2] in imageList2) then
DigraphAddEdge(D, [edge[1] ^ T, edge[2] ^ T]);
fi;
od;
Expand Down
Loading

0 comments on commit 781dcb9

Please sign in to comment.