diff --git a/doc/oper.xml b/doc/oper.xml index 0930e8f3c..d19ca3973 100644 --- a/doc/oper.xml +++ b/doc/oper.xml @@ -2076,6 +2076,84 @@ gap> DigraphVertexLabels(D); <#/GAPDoc> +<#GAPDoc Label="DigraphInsertEdge"> + + + A digraph. + + If edge1 and edge2 are distinct pairs of vertices of digraph, + then then this operation inserts a new edge between edge1 and + edge2. + edge1 and edge2 are split up into two new edges each, which are + all adjacent to the inserted edge. Hence, the vertices of the inserted edge both + have a degree of 3. + + The opposite operation is . + + A new digraph constructed from digraph is returned, + unless digraph belongs to ; + in this case changes are made directly to digraph, which is then returned. + The digraph must not belong to .

+ + D := DigraphByEdges([[1, 2], [2, 1], [3, 4], [4, 3]]); + +gap> D2 := DigraphInsertEdge(D, [1, 2], [3, 4]); + +gap> DigraphEdges(D2); +[ [ 1, 5 ], [ 2, 5 ], [ 3, 6 ], [ 4, 6 ], [ 5, 1 ], [ 5, 2 ], [ 5, 6 ], + [ 6, 3 ], [ 6, 4 ], [ 6, 5 ] ] +gap> D := DigraphByEdges([[1, 2], [2, 1], [2, 3], [3, 2]]); + +gap> D2 := DigraphInsertEdge(D, [1, 2], [2, 3]); + +gap> DigraphEdges(D2); +[ [ 1, 4 ], [ 2, 4 ], [ 2, 5 ], [ 3, 5 ], [ 4, 1 ], [ 4, 2 ], [ 4, 5 ], + [ 5, 2 ], [ 5, 3 ], [ 5, 4 ] ] +]]> + + +<#/GAPDoc> + +<#GAPDoc Label="DigraphReduceEdge"> + + + A digraph. + + If edge is an edge of digraph and adjacent to four edges, then this + operation reduces this edge: edge will be removed, and for both + vertices of edge the two adjacent edges are combined to a single edge. + The vertices of edge must both have a degree of 3. + + The opposite operation is . + + A new digraph constructed from digraph is returned, + unless digraph belongs to ; + in this case changes are made directly to digraph, which is then returned. + The digraph must not belong to .

+ + D := DigraphByEdges([[1, 5], [5, 1], [2, 5], [5, 2], [3, 6], + [6, 3], [4, 6], [6, 4], [5, 6], [6, 5]]); + +gap> D2 := DigraphReduceEdge(D, [5, 6]); + +gap> DigraphEdges(D2); +[ [ 1, 2 ], [ 2, 1 ], [ 3, 4 ], [ 4, 3 ] ] +gap> D := DigraphByEdges([[1, 4], [4, 1], [2, 4], [4, 2], [2, 5], + [5, 2], [3, 5], [5, 3], [4, 5], [5, 4]]); + +gap> D2 := DigraphReduceEdge(D, [4, 5]); + +gap> DigraphEdges(D2); +[ [ 1, 2 ], [ 2, 1 ], [ 2, 3 ], [ 3, 2 ] ] +]]> + + +<#/GAPDoc> + <#GAPDoc Label="IsMatching"> diff --git a/gap/oper.gd b/gap/oper.gd index f6a37e0f8..722439753 100644 --- a/gap/oper.gd +++ b/gap/oper.gd @@ -38,6 +38,10 @@ DeclareOperation("DigraphClosure", [IsDigraph, IsPosInt]); DeclareOperation("DigraphContractEdge", [IsDigraph, IsPosInt, IsPosInt]); DeclareOperation("DigraphContractEdge", [IsDigraph, IsDenseList]); +DeclareOperation("DigraphInsertEdge", [IsDigraph, IsDenseList, IsDenseList]); + +DeclareOperation("DigraphReduceEdge", [IsDigraph, IsDenseList]); + # 3. Ways of combining digraphs . . . DeclareGlobalFunction("DigraphDisjointUnion"); DeclareGlobalFunction("DigraphJoin"); diff --git a/gap/oper.gi b/gap/oper.gi index 3876db50e..6e26bcbc6 100644 --- a/gap/oper.gi +++ b/gap/oper.gi @@ -603,6 +603,187 @@ function(D, edge) return DigraphContractEdge(D, edge[1], edge[2]); end); +DIGRAPHS_CheckInsertEdgeDigraph := function(D, edge1, edge2) + if IsEmptyDigraph(D) then + ErrorNoReturn("the 1st argument must not be an empty digraph"); + fi; + if not IsSymmetricDigraph(D) then + ErrorNoReturn("the 1st argument must be a symmetric digraph"); + fi; + + if Length(edge1) <> 2 then + ErrorNoReturn("the 2nd argument must be a list of length 2"); + fi; + if not IsDigraphEdge(D, edge1) then + ErrorNoReturn("the 2nd argument must be an edge of the digraph "); + fi; + + if Length(edge2) <> 2 then + ErrorNoReturn("the 3rd argument must be a list of length 2"); + fi; + if not IsDigraphEdge(D, edge2) then + ErrorNoReturn("the 3rd argument must be an edge of the digraph "); + fi; + + if Length(Union(edge1, edge2)) < 3 then + ErrorNoReturn("the 2nd and 3rd argument and must be two different edges"); + fi; + +end; + +InstallMethod(DigraphInsertEdge, +"for a symmetric digraph and two dense lists", +[IsMutableDigraph, IsDenseList, IsDenseList], +function(D, edge1, edge2) + local numVertices; + + DIGRAPHS_CheckInsertEdgeDigraph(D, edge1, edge2); + + numVertices := Maximum(DigraphVertices(D)); + + D := DigraphRemoveEdge(D, edge1); + D := DigraphRemoveEdge(D, Reversed(edge1)); + D := DigraphRemoveEdge(D, edge2); + D := DigraphRemoveEdge(D, Reversed(edge2)); + + DigraphAddVertex(D, numVertices + 1); # vertex A + DigraphAddVertex(D, numVertices + 2); # vertex B + + # Add two connected edges between each former edge + # + # Add edges intersecting former edge1 with vertex A + DigraphAddEdge(D, edge1[1], numVertices + 1); + DigraphAddEdge(D, numVertices + 1, edge1[1]); + # + DigraphAddEdge(D, edge1[2], numVertices + 1); + DigraphAddEdge(D, numVertices + 1, edge1[2]); + # + # Add edges intersecting former edge2 with vertex B + DigraphAddEdge(D, edge2[1], numVertices + 2); + DigraphAddEdge(D, numVertices + 2, edge2[1]); + # + DigraphAddEdge(D, edge2[2], numVertices + 2); + DigraphAddEdge(D, numVertices + 2, edge2[2]); + # + # Add edges connecting vertex A and vertex B + DigraphAddEdge(D, numVertices + 1, numVertices + 2); + DigraphAddEdge(D, numVertices + 2, numVertices + 1); + + # TODO: Call SetIsSymmetricDigraph and/or + # SetDigraphSymmetricClosureAttr here? + + return D; +end); + +InstallMethod(DigraphInsertEdge, +"for a symmetric digraph and two dense lists", +[IsImmutableDigraph, IsDenseList, IsDenseList], +function(D, edge1, edge2) + D := DigraphInsertEdge(DigraphMutableCopy(D), edge1, edge2); + + return MakeImmutable(D); +end); + +InstallMethod(DigraphInsertEdge, +"for a symmetric digraph and two dense lists", +[IsDigraph, IsDenseList, IsDenseList], +function(D, edge1, edge2) + return DigraphInsertEdge(D, edge1, edge2); +end); + +DIGRAPHS_CheckReduceEdgeDigraph := function(D, edge) + local leftVertexOutNeighbours, rightVertexOutNeighbours; + + if IsEmptyDigraph(D) then + ErrorNoReturn("the 1st argument must not be an empty digraph"); + fi; + if not IsSymmetricDigraph(D) then + ErrorNoReturn("the 1st argument must be a symmetric digraph"); + fi; + + if Length(edge) <> 2 then + ErrorNoReturn("the 2nd argument must be a list of length 2"); + fi; + if not IsDigraphEdge(D, edge) then + ErrorNoReturn("the 2nd argument must be an edge of the digraph "); + fi; + + leftVertexOutNeighbours := OutNeighboursOfVertex(D, edge[1]); + rightVertexOutNeighbours := OutNeighboursOfVertex(D, edge[2]); + + # Check if edge vertices have degree three + if Length(leftVertexOutNeighbours) <> 3 or Length(rightVertexOutNeighbours) <> 3 then + ErrorNoReturn("the 2nd argument must be an edge where the incident vertices have degree three"); + fi; + if not Length(Union(leftVertexOutNeighbours, rightVertexOutNeighbours)) in [5, 6] then + ErrorNoReturn("the 2nd argument must be an edge that is adjacent to four edges"); + fi; + +end; + +InstallMethod(DigraphReduceEdge, +"for a symmetric digraph and a dense list", +[IsMutableDigraph, IsDenseList], +function(D, edge) + local neighbours, neighbour, newEdge; + + DIGRAPHS_CheckReduceEdgeDigraph(D, edge); + + # Add new edges + # + # left side of edge + newEdge := []; + neighbours := OutNeighboursOfVertex(D, edge[1]); + for neighbour in neighbours do + if neighbour <> edge[2] then + Add(newEdge, neighbour); + fi; + od; + for neighbour in neighbours do + DigraphRemoveEdge(D, [edge[1], neighbour]); + od; + DigraphAddEdge(D, newEdge[1], newEdge[2]); + DigraphAddEdge(D, newEdge[2], newEdge[1]); + # + # right side of intersecting edge + newEdge := []; + neighbours := OutNeighboursOfVertex(D, edge[2]); + for neighbour in neighbours do + if neighbour <> edge[1] then + Add(newEdge, neighbour); + fi; + od; + for neighbour in neighbours do + DigraphRemoveEdge(D, [edge[2], neighbour]); + od; + DigraphAddEdge(D, newEdge[1], newEdge[2]); + DigraphAddEdge(D, newEdge[2], newEdge[1]); + + # Remove old vertices + DigraphRemoveVertices(D, edge); + + # TODO: Call SetIsSymmetricDigraph and/or + # SetDigraphSymmetricClosureAttr here? + + return D; +end); + +InstallMethod(DigraphReduceEdge, +"for a symmetric digraph and a dense list", +[IsImmutableDigraph, IsDenseList], +function(D, edge) + D := DigraphReduceEdge(DigraphMutableCopy(D), edge); + + return MakeImmutable(D); +end); + +InstallMethod(DigraphReduceEdge, +"for a symmetric digraph and a dense list", +[IsDigraph, IsDenseList], +function(D, edge) + return DigraphReduceEdge(D, edge); +end); + ############################################################################# # 3. Ways of combining digraphs ############################################################################# diff --git a/tst/standard/oper.tst b/tst/standard/oper.tst index 1c813fe21..401573fda 100644 --- a/tst/standard/oper.tst +++ b/tst/standard/oper.tst @@ -3324,6 +3324,177 @@ gap> DigraphEdges(D); gap> DigraphVertexLabels(D); [ 1, 2, 3, 6, [ 4, 5 ] ] +# DigraphInsertEdge + +# DigraphInsertEdge: D is empty digraph test +gap> D := NullDigraph(0); + +gap> DigraphInsertEdge(D, [1,2], [3,4]); +Error, the 1st argument must not be an empty digraph + +# DigraphInsertEdge: D is not a symmetric digraph +gap> D := DigraphByEdges([[1,2], [3,4], [1,3], [2,4]]); + +gap> DigraphInsertEdge(D, [1,2], [3,4]); +Error, the 1st argument must be a symmetric digraph + +# DigraphInsertEdge: edge1 is not a list of length 2 +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [], [3,4]); +Error, the 2nd argument must be a list of length 2 +gap> DigraphInsertEdge(D, [1], [3,4]); +Error, the 2nd argument must be a list of length 2 +gap> DigraphInsertEdge(D, [1,2,3], [3,4]); +Error, the 2nd argument must be a list of length 2 + +# DigraphInsertEdge: edge2 is not a list of length 2 +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [1,2], []); +Error, the 3rd argument must be a list of length 2 +gap> DigraphInsertEdge(D, [1,2], [3]); +Error, the 3rd argument must be a list of length 2 +gap> DigraphInsertEdge(D, [1,2], [2,3,4]); +Error, the 3rd argument must be a list of length 2 + +# DigraphInsertEdge: edge1 is not an edge in D +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [1,4], [3,4]); +Error, the 2nd argument must be an edge of the digraph +gap> DigraphInsertEdge(D, [5,6], [3,4]); +Error, the 2nd argument must be an edge of the digraph + +# DigraphInsertEdge: edge2 is not an edge in D +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [1,2], [3,2]); +Error, the 3rd argument must be an edge of the digraph +gap> DigraphInsertEdge(D, [1,2], [5,6]); +Error, the 3rd argument must be an edge of the digraph + +# DigraphInsertEdge: edge1 and edge2 are identical +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [1,2], [1,2]); +Error, the 2nd and 3rd argument and must be two different edge\ +s + +# DigraphInsertEdge: standard insertion (mutable digraph D) +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [1,2], [3,4]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 5 ], [ 2, 4 ], [ 2, 5 ], [ 3, 1 ], [ 3, 6 ], [ 4, 2 ], + [ 4, 6 ], [ 5, 1 ], [ 5, 2 ], [ 5, 6 ], [ 6, 3 ], [ 6, 4 ], [ 6, 5 ] ] + +# DigraphInsertEdge: standard insertion (immutable digraph D) +gap> D := DigraphByEdges(IsImmutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> D := DigraphInsertEdge(D, [1,2], [3,4]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 5 ], [ 2, 4 ], [ 2, 5 ], [ 3, 1 ], [ 3, 6 ], [ 4, 2 ], + [ 4, 6 ], [ 5, 1 ], [ 5, 2 ], [ 5, 6 ], [ 6, 3 ], [ 6, 4 ], [ 6, 5 ] ] + +# DigraphInsertEdge: triangle insertion (mutable digraph D) +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [1,2], [2,4]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 5 ], [ 2, 5 ], [ 2, 6 ], [ 3, 4 ], [ 3, 1 ], [ 4, 3 ], + [ 4, 6 ], [ 5, 1 ], [ 5, 2 ], [ 5, 6 ], [ 6, 2 ], [ 6, 4 ], [ 6, 5 ] ] + +# DigraphInsertEdge: triangle insertion (immutable digraph D) +gap> D := DigraphByEdges(IsImmutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> D := DigraphInsertEdge(D, [1,2], [2,4]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 5 ], [ 2, 5 ], [ 2, 6 ], [ 3, 4 ], [ 3, 1 ], [ 4, 3 ], + [ 4, 6 ], [ 5, 1 ], [ 5, 2 ], [ 5, 6 ], [ 6, 2 ], [ 6, 4 ], [ 6, 5 ] ] + +# DigraphReduceEdge + +# DigraphReduceEdge: D is empty digraph test +gap> D := NullDigraph(0); + +gap> DigraphReduceEdge(D, [1,2]); +Error, the 1st argument must not be an empty digraph + +# DigraphReduceEdge: D is not a symmetric digraph +gap> D := DigraphByEdges([[1,2], [3,4], [1,3], [2,4]]); + +gap> DigraphReduceEdge(D, [1,2]); +Error, the 1st argument must be a symmetric digraph + +# DigraphReduceEdge: edge is not a list of length 2 +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphReduceEdge(D, []); +Error, the 2nd argument must be a list of length 2 +gap> DigraphReduceEdge(D, [1]); +Error, the 2nd argument must be a list of length 2 +gap> DigraphReduceEdge(D, [1,2,2]); +Error, the 2nd argument must be a list of length 2 + +# DigraphReduceEdge: edge is not an edge in D +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphReduceEdge(D, [1,4]); +Error, the 2nd argument must be an edge of the digraph +gap> DigraphReduceEdge(D, [5,6]); +Error, the 2nd argument must be an edge of the digraph + +# DigraphReduceEdge: vertex at edge[1] or edge[2] does not have a degree of 3 +gap> D := DigraphByEdges(IsMutableDigraph, [[1,3], [3,1], [2,4], [4,2], [1,5], [5,1], [2,5], [5,2], [3,6], [6,3], [4,6], [6,4], [5,6], [6,5]]); + +gap> DigraphReduceEdge(D, [4,6]); +Error, the 2nd argument must be an edge where the incident vertices hav\ +e degree three +gap> DigraphReduceEdge(D, [6,4]); +Error, the 2nd argument must be an edge where the incident vertices hav\ +e degree three + +# DigraphReduceEdge: standard reduction (mutable digraph D) +gap> D := DigraphByEdges(IsMutableDigraph, [[1,3], [3,1], [2,4], [4,2], [1,5], [5,1], [2,5], [5,2], [3,6], [6,3], [4,6], [6,4], [5,6], [6,5]]); + +gap> DigraphReduceEdge(D, [5,6]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 2 ], [ 2, 4 ], [ 2, 1 ], [ 3, 1 ], [ 3, 4 ], [ 4, 2 ], + [ 4, 3 ] ] + +# DigraphReduceEdge: standard reduction (immutable digraph D) +gap> D := DigraphByEdges(IsImmutableDigraph, [[1,3], [3,1], [2,4], [4,2], [1,5], [5,1], [2,5], [5,2], [3,6], [6,3], [4,6], [6,4], [5,6], [6,5]]); + +gap> D := DigraphReduceEdge(D, [5,6]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 2 ], [ 2, 4 ], [ 2, 1 ], [ 3, 1 ], [ 3, 4 ], [ 4, 2 ], + [ 4, 3 ] ] + +# DigraphReduceEdge: triangle reduction (mutable digraph D) +gap> D := DigraphByEdges(IsMutableDigraph, [[1,3], [3,1], [1,5], [5,1], [2,5], [5,2], [2,6], [6,2], [3,4], [4,3], [4,6], [6,4], [5,6], [6,5]]); + +gap> DigraphReduceEdge(D, [5,6]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 2 ], [ 2, 1 ], [ 2, 4 ], [ 3, 1 ], [ 3, 4 ], [ 4, 3 ], + [ 4, 2 ] ] + +# DigraphReduceEdge: triangle reduction (immutable digraph D) +gap> D := DigraphByEdges(IsImmutableDigraph, [[1,3], [3,1], [1,5], [5,1], [2,5], [5,2], [2,6], [6,2], [3,4], [4,3], [4,6], [6,4], [5,6], [6,5]]); + +gap> D := DigraphReduceEdge(D, [5,6]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 2 ], [ 2, 1 ], [ 2, 4 ], [ 3, 1 ], [ 3, 4 ], [ 4, 3 ], + [ 4, 2 ] ] + # gap> DIGRAPHS_StopTest(); gap> STOP_TEST("Digraphs package: standard/oper.tst", 0); diff --git a/tst/testinstall.tst b/tst/testinstall.tst index 291b09b7a..6afa8a2da 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -475,6 +475,24 @@ gap> C := DigraphContractEdge(D, 2, 1); gap> DigraphEdges(C); [ [ 2, 1 ] ] +# DigraphInsertEdge +gap> D := DigraphByEdges(IsMutableDigraph, [[1,2], [2,1], [3,4], [4,3], [1,3], [3,1], [2,4], [4,2]]); + +gap> DigraphInsertEdge(D, [1,2], [3,4]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 5 ], [ 2, 4 ], [ 2, 5 ], [ 3, 1 ], [ 3, 6 ], [ 4, 2 ], + [ 4, 6 ], [ 5, 1 ], [ 5, 2 ], [ 5, 6 ], [ 6, 3 ], [ 6, 4 ], [ 6, 5 ] ] + +# DigraphReduceEdge +gap> D := DigraphByEdges(IsMutableDigraph, [[1,3], [3,1], [2,4], [4,2], [1,5], [5,1], [2,5], [5,2], [3,6], [6,3], [4,6], [6,4], [5,6], [6,5]]); + +gap> DigraphReduceEdge(D, [5,6]); + +gap> DigraphEdges(D); +[ [ 1, 3 ], [ 1, 2 ], [ 2, 4 ], [ 2, 1 ], [ 3, 1 ], [ 3, 4 ], [ 4, 2 ], + [ 4, 3 ] ] + # Issue #704 SubdigraphsMonomorphisms bug gap> d := Digraph([[2, 3, 4, 5], [1, 3, 4], [1, 2, 4, 5], [1, 2, 3, 5], > [1, 3, 4]]);;