From 82f48ab065187c16f75f2c7c8ed0443f6fd319ae Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Fri, 30 Aug 2024 18:00:02 +0100 Subject: [PATCH 01/14] Planar embed works for non-antisymmetric digraphs --- gap/planar.gi | 62 +++++++++++++++++++++++++++++++-------------------- src/planar.c | 53 +++++++++++++++++++++++++------------------ 2 files changed, 69 insertions(+), 46 deletions(-) diff --git a/gap/planar.gi b/gap/planar.gi index 0c9ba14f4..01681f851 100644 --- a/gap/planar.gi +++ b/gap/planar.gi @@ -32,69 +32,83 @@ InstallMethod(PlanarEmbedding, "for a digraph", [IsDigraph], function(D) - if IsEmptyDigraph(D) or DigraphNrVertices(D) < 3 then - return []; - elif HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then + local C; + C := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(C); + if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then + return OutNeighbors(C); + elif HasIsPlanarDigraph(C) and not IsPlanarDigraph(C) then return fail; fi; - D := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); - return PLANAR_EMBEDDING(D); + return PLANAR_EMBEDDING(C); end); InstallMethod(OuterPlanarEmbedding, "for a digraph", [IsDigraph], function(D) - if IsEmptyDigraph(D) or DigraphNrVertices(D) < 3 then - return []; - elif HasIsOuterPlanarDigraph(D) and not IsOuterPlanarDigraph(D) then + local C; + C := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(C); + if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then + return OutNeighbors(C); + elif HasIsOuterPlanarDigraph(C) and not IsOuterPlanarDigraph(C) then return fail; fi; - D := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); - return OUTER_PLANAR_EMBEDDING(D); + return OUTER_PLANAR_EMBEDDING(C); end); InstallMethod(KuratowskiPlanarSubdigraph, "for a digraph", [IsDigraph], function(D) - if IsPlanarDigraph(D) then + local C; + C := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(C); + if IsPlanarDigraph(C) then return fail; fi; - D := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); - return KURATOWSKI_PLANAR_SUBGRAPH(D); + return KURATOWSKI_PLANAR_SUBGRAPH(C); end); InstallMethod(KuratowskiOuterPlanarSubdigraph, "for a digraph", [IsDigraph], function(D) - if IsOuterPlanarDigraph(D) then + local C; + C := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(C); + if IsOuterPlanarDigraph(C) then return fail; fi; - D := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); - return KURATOWSKI_OUTER_PLANAR_SUBGRAPH(D); + return KURATOWSKI_OUTER_PLANAR_SUBGRAPH(C); end); InstallMethod(SubdigraphHomeomorphicToK23, "for a digraph", [IsDigraph], function(D) - if IsOuterPlanarDigraph(D) then + local C; + C := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(C); + if IsOuterPlanarDigraph(C) then return fail; fi; - D := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); - return SUBGRAPH_HOMEOMORPHIC_TO_K23(D); + return SUBGRAPH_HOMEOMORPHIC_TO_K23(C); end); InstallMethod(SubdigraphHomeomorphicToK4, "for a digraph", [IsDigraph], function(D) - if IsOuterPlanarDigraph(D) then + local C; + C := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(C); + if IsOuterPlanarDigraph(C) then return fail; fi; - D := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); return SUBGRAPH_HOMEOMORPHIC_TO_K4(D); end); InstallMethod(SubdigraphHomeomorphicToK33, "for a digraph", [IsDigraph], function(D) - if IsPlanarDigraph(D) then + local C; + C := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(C); + if IsPlanarDigraph(C) then return fail; fi; - D := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); - return SUBGRAPH_HOMEOMORPHIC_TO_K33(D); + return SUBGRAPH_HOMEOMORPHIC_TO_K33(C); end); ######################################################################## diff --git a/src/planar.c b/src/planar.c index b53355416..c36071a79 100644 --- a/src/planar.c +++ b/src/planar.c @@ -113,14 +113,19 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { 0L); } Obj const out = FuncOutNeighbours(0L, digraph); - if (FuncIS_ANTISYMMETRIC_DIGRAPH(0L, out) != True) { - ErrorQuit("Digraphs: boyers_planarity_check (C): the 1st argument must be " - "an antisymmetric digraph", - 0L, - 0L); + Int V = DigraphNrVertices(digraph); + Int E = 0; + for (Int v = 1; v <= LEN_LIST(out); ++v) { + Obj const out_v = ELM_LIST(out, v); + for (Int w = 1; w <= LEN_LIST(out_v); ++w) { + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u + || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), INTOBJ_INT(v)) + == False) { + ++E; + } + } } - Int V = DigraphNrVertices(digraph); - Int E = DigraphNrEdges(digraph); if (V > INT_MAX) { // Cannot currently test this, it might always be true, depending on the // definition of Int. @@ -139,19 +144,7 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { } graphP theGraph = gp_New(); - switch (flags) { - case EMBEDFLAGS_SEARCHFORK33: - gp_AttachK33Search(theGraph); - break; - case EMBEDFLAGS_SEARCHFORK23: - gp_AttachK23Search(theGraph); - break; - case EMBEDFLAGS_SEARCHFORK4: - gp_AttachK4Search(theGraph); - break; - default: - break; - } + if (gp_InitGraph(theGraph, V) != OK) { gp_Free(&theGraph); ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of nodes!", @@ -166,6 +159,20 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { return 0L; } + switch (flags) { + case EMBEDFLAGS_SEARCHFORK33: + gp_AttachK33Search(theGraph); + break; + case EMBEDFLAGS_SEARCHFORK23: + gp_AttachK23Search(theGraph); + break; + case EMBEDFLAGS_SEARCHFORK4: + gp_AttachK4Search(theGraph); + break; + default: + break; + } + int status; for (Int v = 1; v <= LEN_LIST(out); ++v) { @@ -174,8 +181,10 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { Obj const out_v = ELM_LIST(out, v); for (Int w = 1; w <= LEN_LIST(out_v); ++w) { DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, w)); - int u = INT_INTOBJ(ELM_LIST(out_v, w)); - if (v != u) { + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u + || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), INTOBJ_INT(v)) + == False) { status = gp_AddEdge(theGraph, v, 0, u, 0); if (status != OK) { // Cannot currently test this, i.e. it shouldn't happen (and From 01103c0d1e3e5f72eba51c3660f5b18af08f1188 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Fri, 30 Aug 2024 18:23:54 +0100 Subject: [PATCH 02/14] Update tests --- doc/planar.xml | 66 ++++++++++++++-------------- tst/standard/planar.tst | 96 ++++++++++++++++++++++------------------- 2 files changed, 85 insertions(+), 77 deletions(-) diff --git a/doc/planar.xml b/doc/planar.xml index 77e3bde4f..fa6f803bf 100644 --- a/doc/planar.xml +++ b/doc/planar.xml @@ -145,8 +145,8 @@ gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], gap> IsPlanarDigraph(D); false gap> KuratowskiPlanarSubdigraph(D); -[ [ 2, 9, 7 ], [ 3 ], [ 6 ], [ 5, 9 ], [ 6 ], [ ], [ 4 ], - [ 7, 9, 3 ], [ ], [ ] ] +[ [ 2, 9, 7 ], [ 1, 3 ], [ 6 ], [ 5, 9 ], [ 6, 4 ], [ 3, 5 ], [ 4 ], + [ 7, 9, 3 ], [ 8 ], [ ] ] gap> D := Digraph(IsMutableDigraph, [[3, 5, 10], [8, 9, 10], [1, 4], > [3, 6], [1, 7, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); @@ -160,8 +160,8 @@ gap> D := Digraph(IsMutableDigraph, [[2, 4, 7, 9, 10], gap> IsPlanarDigraph(D); false gap> KuratowskiPlanarSubdigraph(D); -[ [ 2, 9, 7 ], [ 3 ], [ 6 ], [ 5, 9 ], [ 6 ], [ ], [ 4 ], - [ 7, 9, 3 ], [ ], [ ] ] +[ [ 2, 9, 7 ], [ 1, 3 ], [ 6 ], [ 5, 9 ], [ 6, 4 ], [ 3, 5 ], [ 4 ], + [ 7, 9, 3 ], [ 8 ], [ ] ] ]]> @@ -201,8 +201,8 @@ gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], > [1, 7, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> KuratowskiOuterPlanarSubdigraph(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], > [2, 5, 8, 9], [1, 2, 3, 4, 6, 7, 9, 10], [3, 4, 5, 7, 9, 10], > [3, 4, 5, 6, 9, 10], [3, 4, 5, 7, 9], [2, 3, 5, 6, 7, 8], [3, 5]]); @@ -210,14 +210,14 @@ gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], gap> IsOuterPlanarDigraph(D); false gap> KuratowskiOuterPlanarSubdigraph(D); -[ [ ], [ ], [ ], [ 8, 9 ], [ ], [ ], [ 9, 4 ], [ 7, 9 ], [ ], - [ ] ] +[ [ ], [ ], [ ], [ 8, 9 ], [ ], [ ], [ 9, 4 ], [ 7, 9, 4 ], + [ 8, 7 ], [ ] ] gap> D := Digraph(IsMutableDigraph, [[3, 5, 10], [8, 9, 10], [1, 4], > [3, 6], [1, 7, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> KuratowskiOuterPlanarSubdigraph(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> D := Digraph(IsMutableDigraph, [[2, 4, 7, 9, 10], > [1, 3, 4, 6, 9, 10], [6, 10], [2, 5, 8, 9], > [1, 2, 3, 4, 6, 7, 9, 10], [3, 4, 5, 7, 9, 10], @@ -226,8 +226,8 @@ gap> D := Digraph(IsMutableDigraph, [[2, 4, 7, 9, 10], gap> IsOuterPlanarDigraph(D); false gap> KuratowskiOuterPlanarSubdigraph(D); -[ [ ], [ ], [ ], [ 8, 9 ], [ ], [ ], [ 9, 4 ], [ 7, 9 ], [ ], - [ ] ]]]> +[ [ ], [ ], [ ], [ 8, 9 ], [ ], [ ], [ 9, 4 ], [ 7, 9, 4 ], + [ 8, 7 ], [ ] ]]]> <#/GAPDoc> @@ -260,8 +260,8 @@ gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], > [1, 7, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> PlanarEmbedding(D); -[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4 ], [ 6 ], [ 11, 7 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11, 7 ], + [ 7, 4 ], [ 8, 6 ], [ 7, 2 ], [ 2, 11 ], [ 1, 2 ], [ 9, 5 ] ] gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], > [2, 5, 8, 9], [1, 2, 3, 4, 6, 7, 9, 10], [3, 4, 5, 7, 9, 10], > [3, 4, 5, 6, 9, 10], [3, 4, 5, 7, 9], [2, 3, 5, 6, 7, 8], [3, 5]]); @@ -272,8 +272,8 @@ gap> D := Digraph(IsMutableDigraph, [[3, 5, 10], [8, 9, 10], [1, 4], > [3, 6], [1, 7, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> PlanarEmbedding(D); -[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4 ], [ 6 ], [ 11, 7 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11, 7 ], + [ 7, 4 ], [ 8, 6 ], [ 7, 2 ], [ 2, 11 ], [ 1, 2 ], [ 9, 5 ] ] gap> D := Digraph(IsMutableDigraph, [[2, 4, 7, 9, 10], > [1, 3, 4, 6, 9, 10], [6, 10], [2, 5, 8, 9], > [1, 2, 3, 4, 6, 7, 9, 10], [3, 4, 5, 7, 9, 10], @@ -321,7 +321,7 @@ gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], gap> OuterPlanarEmbedding(D); fail gap> OuterPlanarEmbedding(CompleteBipartiteDigraph(2, 2)); -[ [ 3, 4 ], [ 4, 3 ], [ ], [ ] ] +[ [ 3, 4 ], [ 4, 3 ], [ 2, 1 ], [ 1, 2 ] ] gap> D := Digraph(IsMutableDigraph, [[3, 5, 10], [8, 9, 10], [1, 4], > [3, 6], [1, 7, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); @@ -335,7 +335,7 @@ gap> D := Digraph(IsMutableDigraph, [[2, 4, 7, 9, 10], gap> OuterPlanarEmbedding(D); fail gap> OuterPlanarEmbedding(CompleteBipartiteDigraph(2, 2)); -[ [ 3, 4 ], [ 4, 3 ], [ ], [ ] ] +[ [ 3, 4 ], [ 4, 3 ], [ 2, 1 ], [ 1, 2 ] ] ]]> @@ -369,19 +369,19 @@ gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], [1, 7, 11], > [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> SubdigraphHomeomorphicToK4(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 7, 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 7, 11 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> SubdigraphHomeomorphicToK23(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], [1, 11], > [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> SubdigraphHomeomorphicToK4(D); fail gap> SubdigraphHomeomorphicToK23(D); -[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4, 1 ], [ 6, 3 ], [ 11, 1 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 2, 11 ], [ 1, 2 ], [ 9, 5 ] ] gap> SubdigraphHomeomorphicToK33(D); fail gap> SubdigraphHomeomorphicToK23(NullDigraph(0)); @@ -389,26 +389,27 @@ fail gap> SubdigraphHomeomorphicToK33(CompleteDigraph(5)); fail gap> SubdigraphHomeomorphicToK33(CompleteBipartiteDigraph(3, 3)); -[ [ 4, 6, 5 ], [ 4, 5, 6 ], [ 6, 5, 4 ], [ ], [ ], [ ] ] +[ [ 4, 6, 5 ], [ 4, 5, 6 ], [ 6, 5, 4 ], [ 1, 2, 3 ], [ 3, 2, 1 ], + [ 2, 3, 1 ] ] gap> SubdigraphHomeomorphicToK4(CompleteDigraph(3)); fail gap> D := Digraph(IsMutableDigraph, [[3, 5, 10], [8, 9, 10], [1, 4], > [3, 6], [1, 7, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> SubdigraphHomeomorphicToK4(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 7, 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 7, 11 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> SubdigraphHomeomorphicToK23(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> D := Digraph(IsMutableDigraph, [[3, 5, 10], [8, 9, 10], [1, 4], > [3, 6], [1, 11], [4, 7], [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> SubdigraphHomeomorphicToK4(D); fail gap> SubdigraphHomeomorphicToK23(D); -[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], - [ ], [ 11 ], [ ], [ ] ] +[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4, 1 ], [ 6, 3 ], [ 11, 1 ], + [ 7, 4 ], [ 8, 6 ], [ 2, 7 ], [ 2, 11 ], [ 1, 2 ], [ 9, 5 ] ] gap> SubdigraphHomeomorphicToK33(D); fail gap> SubdigraphHomeomorphicToK23(NullDigraph(0)); @@ -416,7 +417,8 @@ fail gap> SubdigraphHomeomorphicToK33(CompleteDigraph(5)); fail gap> SubdigraphHomeomorphicToK33(CompleteBipartiteDigraph(3, 3)); -[ [ 4, 6, 5 ], [ 4, 5, 6 ], [ 6, 5, 4 ], [ ], [ ], [ ] ] +[ [ 4, 6, 5 ], [ 4, 5, 6 ], [ 6, 5, 4 ], [ 1, 2, 3 ], [ 3, 2, 1 ], + [ 2, 3, 1 ] ] gap> SubdigraphHomeomorphicToK4(CompleteDigraph(3)); fail ]]> diff --git a/tst/standard/planar.tst b/tst/standard/planar.tst index 869f7397a..80bf205fa 100644 --- a/tst/standard/planar.tst +++ b/tst/standard/planar.tst @@ -79,8 +79,8 @@ gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], [1, 7, 11], [4, 7], > [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> PlanarEmbedding(D); -[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4 ], [ 6 ], [ 11, 7 ], [ 7 ], [ 8 ], [ ], - [ 11 ], [ ], [ ] ] +[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11, 7 ], [ 7, 4 ], + [ 8, 6 ], [ 7, 2 ], [ 2, 11 ], [ 1, 2 ], [ 9, 5 ] ] gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], > [2, 5, 8, 9], [1, 2, 3, 4, 6, 7, 9, 10], [3, 4, 5, 7, 9, 10], > [3, 4, 5, 6, 9, 10], [3, 4, 5, 7, 9], [2, 3, 5, 6, 7, 8], [3, 5]]); @@ -98,34 +98,39 @@ gap> D := List(["D??", "D?_", "D?o", "D?w", "D?{", "DCO", "DCW", "DCc", "DCo", > "DQo", "DQw", "DQ{", "DTk", "DTw", "DT{", "DUW", "DUw", "DU{", "DV{", "D]w", > "D]{", "D^{", "D~{"], DigraphFromGraph6String);; gap> List(D, PlanarEmbedding); -[ [ ], [ [ 5 ], [ ], [ ], [ ], [ ] ], [ [ 5 ], [ 5 ], [ ], [ ], [ ] ], - [ [ 5 ], [ 5 ], [ 5 ], [ ], [ ] ], [ [ 5 ], [ 5 ], [ 5 ], [ 5 ], [ ] ], - [ [ 4 ], [ 5 ], [ ], [ ], [ ] ], [ [ 4 ], [ 5 ], [ 5 ], [ ], [ ] ], - [ [ 4, 5 ], [ ], [ ], [ 5 ], [ ] ], [ [ 4, 5 ], [ 5 ], [ ], [ ], [ ] ] - , [ [ 4, 5 ], [ 5 ], [ ], [ 5 ], [ ] ], - [ [ 4, 5 ], [ 5 ], [ 5 ], [ ], [ ] ], - [ [ 4, 5 ], [ 5 ], [ 5 ], [ 5 ], [ ] ], - [ [ 4, 5 ], [ 4 ], [ 5 ], [ 5 ], [ ] ], - [ [ 4, 5 ], [ 5, 4 ], [ ], [ ], [ ] ], - [ [ 4, 5 ], [ 5, 4 ], [ ], [ 5 ], [ ] ], - [ [ 4, 5 ], [ 5, 4 ], [ 5 ], [ ], [ ] ], - [ [ 4, 5 ], [ 5, 4 ], [ 5 ], [ 5 ], [ ] ], - [ [ 4, 5 ], [ 5, 4 ], [ 4, 5 ], [ ], [ ] ], - [ [ 4, 5 ], [ 5, 4 ], [ 4, 5 ], [ 5 ], [ ] ], - [ [ 3, 5 ], [ 4 ], [ 5 ], [ ], [ ] ], - [ [ 3, 5 ], [ 5, 4 ], [ ], [ ], [ ] ], - [ [ 3, 5 ], [ 5, 4 ], [ 5 ], [ ], [ ] ], - [ [ 3, 5 ], [ 4, 5 ], [ 5 ], [ 5 ], [ ] ], - [ [ 3, 5, 4 ], [ ], [ 4, 5 ], [ 5 ], [ ] ], - [ [ 3, 5, 4 ], [ 5 ], [ 4, 5 ], [ ], [ ] ], - [ [ 3, 5, 4 ], [ 5 ], [ 4, 5 ], [ 5 ], [ ] ], - [ [ 3, 4 ], [ 4, 5 ], [ 5 ], [ ], [ ] ], - [ [ 3, 5, 4 ], [ 4, 5 ], [ 5 ], [ ], [ ] ], - [ [ 3, 5, 4 ], [ 4, 5 ], [ 5 ], [ 5 ], [ ] ], - [ [ 3, 5, 4 ], [ 5, 4 ], [ 4, 5 ], [ 5 ], [ ] ], - [ [ 3, 5, 4 ], [ 4, 5, 3 ], [ 5 ], [ ], [ ] ], - [ [ 3, 5, 4 ], [ 4, 5, 3 ], [ 5 ], [ 5 ], [ ] ], - [ [ 3, 5, 4 ], [ 4, 5, 3 ], [ 4, 5 ], [ 5 ], [ ] ], fail ] +[ [ [ ], [ ], [ ], [ ], [ ] ], [ [ 5 ], [ ], [ ], [ ], [ 1 ] ], + [ [ 5 ], [ 5 ], [ ], [ ], [ 1, 2 ] ], + [ [ 5 ], [ 5 ], [ 5 ], [ ], [ 1, 2, 3 ] ], + [ [ 5 ], [ 5 ], [ 5 ], [ 5 ], [ 1, 2, 3, 4 ] ], + [ [ 4 ], [ 5 ], [ ], [ 1 ], [ 2 ] ], + [ [ 4 ], [ 5 ], [ 5 ], [ 1 ], [ 2, 3 ] ], + [ [ 4, 5 ], [ ], [ ], [ 5, 1 ], [ 1, 4 ] ], + [ [ 4, 5 ], [ 5 ], [ ], [ 1 ], [ 1, 2 ] ], + [ [ 4, 5 ], [ 5 ], [ ], [ 5, 1 ], [ 1, 4, 2 ] ], + [ [ 4, 5 ], [ 5 ], [ 5 ], [ 1 ], [ 1, 2, 3 ] ], + [ [ 4, 5 ], [ 5 ], [ 5 ], [ 5, 1 ], [ 1, 4, 2, 3 ] ], + [ [ 4, 5 ], [ 4 ], [ 5 ], [ 5, 1, 2 ], [ 1, 4, 3 ] ], + [ [ 4, 5 ], [ 5, 4 ], [ ], [ 2, 1 ], [ 1, 2 ] ], + [ [ 4, 5 ], [ 5, 4 ], [ ], [ 2, 5, 1 ], [ 1, 4, 2 ] ], + [ [ 4, 5 ], [ 5, 4 ], [ 5 ], [ 2, 1 ], [ 1, 2, 3 ] ], + [ [ 4, 5 ], [ 5, 4 ], [ 5 ], [ 2, 5, 1 ], [ 1, 4, 2, 3 ] ], + [ [ 4, 5 ], [ 5, 4 ], [ 4, 5 ], [ 2, 3, 1 ], [ 1, 3, 2 ] ], + [ [ 4, 5 ], [ 5, 4 ], [ 4, 5 ], [ 2, 5, 3, 1 ], [ 1, 3, 4, 2 ] ], + [ [ 3, 5 ], [ 4 ], [ 5, 1 ], [ 2 ], [ 1, 3 ] ], + [ [ 3, 5 ], [ 5, 4 ], [ 1 ], [ 2 ], [ 1, 2 ] ], + [ [ 3, 5 ], [ 5, 4 ], [ 5, 1 ], [ 2 ], [ 1, 3, 2 ] ], + [ [ 3, 5 ], [ 4, 5 ], [ 5, 1 ], [ 5, 2 ], [ 1, 3, 2, 4 ] ], + [ [ 4, 5, 3 ], [ ], [ 1, 5, 4 ], [ 3, 5, 1 ], [ 1, 4, 3 ] ], + [ [ 4, 3, 5 ], [ 5 ], [ 5, 1, 4 ], [ 3, 1 ], [ 1, 3, 2 ] ], + [ [ 4, 5, 3 ], [ 5 ], [ 1, 5, 4 ], [ 3, 5, 1 ], [ 1, 4, 3, 2 ] ], + [ [ 4, 3 ], [ 5, 4 ], [ 1, 5 ], [ 2, 1 ], [ 3, 2 ] ], + [ [ 4, 5, 3 ], [ 5, 4 ], [ 1, 5 ], [ 2, 1 ], [ 3, 1, 2 ] ], + [ [ 4, 5, 3 ], [ 5, 4 ], [ 1, 5 ], [ 2, 5, 1 ], [ 3, 1, 4, 2 ] ], + [ [ 4, 3, 5 ], [ 5, 4 ], [ 1, 4, 5 ], [ 2, 5, 3, 1 ], [ 1, 3, 4, 2 ] ], + [ [ 4, 5, 3 ], [ 3, 5, 4 ], [ 1, 5, 2 ], [ 2, 1 ], [ 1, 2, 3 ] ], + [ [ 4, 5, 3 ], [ 3, 5, 4 ], [ 1, 5, 2 ], [ 2, 5, 1 ], [ 1, 4, 2, 3 ] ], + [ [ 4, 5, 3 ], [ 3, 5, 4 ], [ 1, 5, 2, 4 ], [ 3, 2, 5, 1 ], [ 1, 4, 2, 3 ] ] + , fail ] # OuterPlanarEmbedding gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], [1, 7, 11], [4, 7], @@ -148,26 +153,26 @@ gap> OuterPlanarEmbedding(D); gap> D := CompleteDigraph(3); gap> OuterPlanarEmbedding(D); -[ [ 2, 3 ], [ 3 ], [ ] ] +[ [ 2, 3 ], [ 3, 1 ], [ 1, 2 ] ] # SubdigraphHomeomorphicToK23/33/4 gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], [1, 7, 11], [4, 7], > [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> SubdigraphHomeomorphicToK4(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 7, 11 ], [ 7 ], [ 8 ], [ ], - [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 7, 11 ], [ 7, 4 ], + [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> SubdigraphHomeomorphicToK23(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], [ ], - [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11 ], [ 7, 4 ], + [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], [1, 11], [4, 7], > [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> SubdigraphHomeomorphicToK4(D); fail gap> SubdigraphHomeomorphicToK23(D); -[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], [ ], - [ 11 ], [ ], [ ] ] +[ [ 3, 10, 5 ], [ 10, 8, 9 ], [ 4, 1 ], [ 6, 3 ], [ 11, 1 ], [ 7, 4 ], + [ 8, 6 ], [ 2, 7 ], [ 2, 11 ], [ 1, 2 ], [ 9, 5 ] ] gap> SubdigraphHomeomorphicToK33(D); fail gap> SubdigraphHomeomorphicToK23(NullDigraph(0)); @@ -175,7 +180,8 @@ fail gap> SubdigraphHomeomorphicToK33(CompleteDigraph(5)); fail gap> SubdigraphHomeomorphicToK33(CompleteBipartiteDigraph(3, 3)); -[ [ 4, 6, 5 ], [ 4, 5, 6 ], [ 6, 5, 4 ], [ ], [ ], [ ] ] +[ [ 4, 6, 5 ], [ 4, 5, 6 ], [ 6, 5, 4 ], [ 1, 2, 3 ], [ 3, 2, 1 ], + [ 2, 3, 1 ] ] gap> SubdigraphHomeomorphicToK4(CompleteDigraph(3)); fail @@ -192,8 +198,8 @@ gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], gap> IsPlanarDigraph(D); false gap> KuratowskiPlanarSubdigraph(D); -[ [ 2, 9, 7 ], [ 3 ], [ 6 ], [ 5, 9 ], [ 6 ], [ ], [ 4 ], [ 7, 9, 3 ], [ ], - [ ] ] +[ [ 2, 9, 7 ], [ 1, 3 ], [ 6 ], [ 5, 9 ], [ 6, 4 ], [ 3, 5 ], [ 4 ], + [ 7, 9, 3 ], [ 8 ], [ ] ] gap> D := NullDigraph(0); gap> KuratowskiPlanarSubdigraph(D); @@ -204,8 +210,8 @@ gap> D := Digraph([[3, 5, 10], [8, 9, 10], [1, 4], [3, 6], [1, 7, 11], [4, 7], > [6, 8], [2, 7], [2, 11], [1, 2], [5, 9]]); gap> KuratowskiOuterPlanarSubdigraph(D); -[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4 ], [ 6 ], [ 11 ], [ 7 ], [ 8 ], [ ], - [ 11 ], [ ], [ ] ] +[ [ 3, 5, 10 ], [ 9, 8, 10 ], [ 4, 1 ], [ 6, 3 ], [ 1, 11 ], [ 7, 4 ], + [ 8, 6 ], [ 2, 7 ], [ 11, 2 ], [ 2, 1 ], [ 5, 9 ] ] gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], > [2, 5, 8, 9], [1, 2, 3, 4, 6, 7, 9, 10], [3, 4, 5, 7, 9, 10], > [3, 4, 5, 6, 9, 10], [3, 4, 5, 7, 9], [2, 3, 5, 6, 7, 8], [3, 5]]); @@ -213,7 +219,8 @@ gap> D := Digraph([[2, 4, 7, 9, 10], [1, 3, 4, 6, 9, 10], [6, 10], gap> IsOuterPlanarDigraph(D); false gap> KuratowskiOuterPlanarSubdigraph(D); -[ [ ], [ ], [ ], [ 8, 9 ], [ ], [ ], [ 9, 4 ], [ 7, 9 ], [ ], [ ] ] +[ [ ], [ ], [ ], [ 8, 9 ], [ ], [ ], [ 9, 4 ], [ 7, 9, 4 ], [ 8, 7 ], + [ ] ] gap> D := NullDigraph(0); gap> KuratowskiOuterPlanarSubdigraph(D); @@ -234,8 +241,7 @@ Error, Digraphs: boyers_planarity_check (C): invalid number of edges! gap> IsPlanarDigraph(NullDigraph(70000)); true gap> IS_PLANAR(CompleteDigraph(2)); -Error, Digraphs: boyers_planarity_check (C): the 1st argument must be an antis\ -ymmetric digraph +true # DIGRAPHS_UnbindVariables gap> Unbind(D); From 42f6e6900798b51559d6177cf3a99612d6e11d2d Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Sat, 31 Aug 2024 23:43:01 +0100 Subject: [PATCH 03/14] Remove loops --- gap/planar.gi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gap/planar.gi b/gap/planar.gi index 01681f851..8405bd539 100644 --- a/gap/planar.gi +++ b/gap/planar.gi @@ -35,6 +35,7 @@ function(D) local C; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); + DigraphRemoveLoops(C); if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then return OutNeighbors(C); elif HasIsPlanarDigraph(C) and not IsPlanarDigraph(C) then @@ -48,6 +49,7 @@ function(D) local C; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); + DigraphRemoveLoops(C); if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then return OutNeighbors(C); elif HasIsOuterPlanarDigraph(C) and not IsOuterPlanarDigraph(C) then @@ -61,6 +63,7 @@ function(D) local C; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); + DigraphRemoveLoops(C); if IsPlanarDigraph(C) then return fail; fi; @@ -72,6 +75,7 @@ function(D) local C; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); + DigraphRemoveLoops(C); if IsOuterPlanarDigraph(C) then return fail; fi; @@ -83,6 +87,7 @@ function(D) local C; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); + DigraphRemoveLoops(C); if IsOuterPlanarDigraph(C) then return fail; fi; @@ -94,6 +99,7 @@ function(D) local C; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); + DigraphRemoveLoops(C); if IsOuterPlanarDigraph(C) then return fail; fi; @@ -105,6 +111,7 @@ function(D) local C; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); + DigraphRemoveLoops(C); if IsPlanarDigraph(C) then return fail; fi; From 4bb8a6ac4b7e0b92adbae78596920e6ea5edaa7f Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Sat, 31 Aug 2024 23:43:16 +0100 Subject: [PATCH 04/14] Update doc --- doc/planar.xml | 58 ++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/doc/planar.xml b/doc/planar.xml index fa6f803bf..6c9e44d5c 100644 --- a/doc/planar.xml +++ b/doc/planar.xml @@ -112,14 +112,15 @@ true A list or fail. - KuratowskiPlanarSubdigraph returns the immutable list of - lists of out-neighbours of a (not necessarily induced) subdigraph of the - digraph digraph that witnesses the fact that digraph is not - planar, or fail if digraph is planar. In other words, - KuratowskiPlanarSubdigraph returns the out-neighbours of a - subdigraph of digraph that is homeomorphic to the complete graph - with 5 vertices, or to the complete bipartite graph with vertex sets - of sizes 3 and 3.

+ KuratowskiPlanarSubdigraph returns the immutable list of lists of + out-neighbours of an induced subdigraph (excluding multiple edges and loops) + of the digraph digraph that witnesses the fact that digraph is + not planar, or fail if digraph is planar. In other words, + KuratowskiPlanarSubdigraph returns the out-neighbours of a subdigraph + of digraph that is homeomorphic to the complete graph with 5 + vertices, or to the complete bipartite graph with vertex sets of sizes + 3 and 3. +

The directions and multiplicities of any edges in digraph are ignored when considering whether or not digraph is planar.

@@ -173,17 +174,18 @@ gap> KuratowskiPlanarSubdigraph(D); A list or fail. KuratowskiOuterPlanarSubdigraph returns the immutable list of - immutable lists of out-neighbours of a (not necessarily induced) - subdigraph of the digraph digraph that witnesses the fact - that digraph is not outer planar, or fail if - digraph is outer planar. In other words, + immutable lists of out-neighbours of an induced subdigraph (excluding + multiple edges and loops) of the digraph digraph that witnesses the + fact that digraph is not outer planar, or fail if + digraph is outer planar. In other words, KuratowskiOuterPlanarSubdigraph returns the out-neighbours of a - subdigraph of digraph that is homeomorphic to the complete graph - with 4 vertices, or to the complete bipartite graph with vertex sets - of sizes 2 and 3.

+ subdigraph of digraph that is homeomorphic to the complete graph with + 4 vertices, or to the complete bipartite graph with vertex sets of + sizes 2 and 3. +

- The directions and multiplicities of any edges in digraph are - ignored when considering whether or not digraph is outer planar. + The directions and multiplicities of any edges in digraph are ignored + when considering whether or not digraph is outer planar.

See also @@ -237,14 +239,14 @@ gap> KuratowskiOuterPlanarSubdigraph(D); A list or fail. - If digraph is a planar digraph, then - PlanarEmbedding returns the immutable list of lists of - out-neighbours of a subdigraph of digraph such that each vertex's - neighbours are given in clockwise order. If digraph is not planar, - then fail is returned.

+ If digraph is a planar digraph, then PlanarEmbedding returns + the immutable list of lists of out-neighbours of digraph (excluding + multiple edges and loops) such that each vertex's neighbours are given in + clockwise order. If digraph is not planar, then fail is + returned.

- The directions and multiplicities of any edges in digraph are - ignored by PlanarEmbedding. + The directions and multiplicities of any edges in digraph are ignored + by PlanarEmbedding.

See also @@ -292,10 +294,10 @@ fail A list or fail. If digraph is an outer planar digraph, then - OuterPlanarEmbedding returns the immutable list of lists - of out-neighbours of a subdigraph of digraph such that each - vertex's neighbours are given in clockwise order. If digraph is not - outer planar, then fail is returned.

+ OuterPlanarEmbedding returns the immutable list of lists of + out-neighbours of digraph (excluding multiple edges and loops) such + that each vertex's neighbours are given in clockwise order. If + digraph is not outer planar, then fail is returned.

The directions and multiplicities of any edges in digraph are ignored by OuterPlanarEmbedding. From 97a9674678b16daa15e28390322e8dc18e05160b Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Mon, 2 Sep 2024 12:07:13 +0100 Subject: [PATCH 05/14] Check attributes before making mutable copy --- gap/planar.gi | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gap/planar.gi b/gap/planar.gi index 8405bd539..386e5c180 100644 --- a/gap/planar.gi +++ b/gap/planar.gi @@ -33,13 +33,14 @@ InstallMethod(PlanarEmbedding, "for a digraph", [IsDigraph], function(D) local C; + if HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then + return fail; + fi; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); DigraphRemoveLoops(C); if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then return OutNeighbors(C); - elif HasIsPlanarDigraph(C) and not IsPlanarDigraph(C) then - return fail; fi; return PLANAR_EMBEDDING(C); end); @@ -47,13 +48,14 @@ end); InstallMethod(OuterPlanarEmbedding, "for a digraph", [IsDigraph], function(D) local C; + if HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then + return fail; + fi; C := DigraphMutableCopy(D); DigraphRemoveAllMultipleEdges(C); DigraphRemoveLoops(C); if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then return OutNeighbors(C); - elif HasIsOuterPlanarDigraph(C) and not IsOuterPlanarDigraph(C) then - return fail; fi; return OUTER_PLANAR_EMBEDDING(C); end); From 8929b8d8584525f0b3829c371fdfcdff7ab7403e Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Tue, 3 Sep 2024 00:25:36 +0100 Subject: [PATCH 06/14] Add NrAdjacencies --- gap/attr.gd | 1 + gap/attr.gi | 7 +++++++ src/digraphs.c | 29 +++++++++++++++++++++++++++++ src/digraphs.h | 1 + 4 files changed, 38 insertions(+) diff --git a/gap/attr.gd b/gap/attr.gd index 020bf0899..a8b99b33a 100644 --- a/gap/attr.gd +++ b/gap/attr.gd @@ -14,6 +14,7 @@ DeclareAttribute("DigraphVertices", IsDigraph); DeclareAttribute("DigraphNrVertices", IsDigraph); DeclareAttribute("DigraphEdges", IsDigraph); DeclareAttribute("DigraphNrEdges", IsDigraph); +DeclareAttribute("DigraphNrAdjacencies", IsDigraph); DeclareAttribute("DigraphNrLoops", IsDigraph); DeclareAttribute("DigraphHash", IsDigraph); diff --git a/gap/attr.gi b/gap/attr.gi index 8864416bc..4dadac3c5 100644 --- a/gap/attr.gi +++ b/gap/attr.gi @@ -701,6 +701,13 @@ function(D) return m; end); +InstallMethod(DigraphNrAdjacencies, "for a digraph", [IsDigraphByOutNeighboursRep], +function(D) + local m; + m := DIGRAPH_NRADJACENCIES(D); + return m; +end); + InstallMethod(DigraphNrLoops, "for a digraph by out-neighbours", [IsDigraphByOutNeighboursRep], diff --git a/src/digraphs.c b/src/digraphs.c index 53ea3f390..2a397b7d5 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -138,6 +138,34 @@ static Obj FuncDIGRAPH_NREDGES(Obj self, Obj D) { return INTOBJ_INT(DigraphNrEdges(D)); } +Int DigraphNrAdjacencies(Obj D) { + Int nr = 0; + if (IsbPRec(D, RNamName("DigraphNrAdjacencies"))) { + return INT_INTOBJ(ElmPRec(D, RNamName("DigraphNrAdjacencies"))); + } else { + Obj const out = FuncOutNeighbours(0L, D); + for (Int v = 1; v <= LEN_LIST(out); ++v) { + Obj const out_v = ELM_LIST(out, v); + for (Int w = 1; w <= LEN_LIST(out_v); ++w) { + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u + || CALL_3ARGS(IsDigraphEdge, D, INTOBJ_INT(u), INTOBJ_INT(v)) + == False) { + ++nr; + } + } + } + } + if (IsAttributeStoringRep(D)) { + AssPRec(D, RNamName("DigraphNrAdjacencies"), INTOBJ_INT(nr)); + } + return nr; +} + +static Obj FuncDIGRAPH_NRADJACENCIES(Obj self, Obj D) { + return INTOBJ_INT(DigraphNrAdjacencies(D)); +} + /**************************************************************************** ** *F FuncGABOW_SCC @@ -2113,6 +2141,7 @@ FuncMULTIDIGRAPH_CANONICAL_LABELLING(Obj self, Obj digraph, Obj colours) { static StructGVarFunc GVarFuncs[] = { GVAR_FUNC(DIGRAPH_NREDGES, 1, "digraph"), + GVAR_FUNC(DIGRAPH_NRADJACENCIES, 1, "digraph"), GVAR_FUNC(GABOW_SCC, 1, "adj"), GVAR_FUNC(DIGRAPH_CONNECTED_COMPONENTS, 1, "digraph"), GVAR_FUNC(IS_ACYCLIC_DIGRAPH, 1, "adj"), diff --git a/src/digraphs.h b/src/digraphs.h index f7b1f1ff8..4017a16a3 100644 --- a/src/digraphs.h +++ b/src/digraphs.h @@ -24,6 +24,7 @@ Obj FuncIS_ANTISYMMETRIC_DIGRAPH(Obj self, Obj D); Obj FuncADJACENCY_MATRIX(Obj self, Obj D); Int DigraphNrEdges(Obj digraph); +Int DigraphNrAdjacencies(Obj digraph); Obj DigraphSource(Obj digraph); Obj DigraphRange(Obj digraph); From 43cfd86b332edf09546115568948f581dd71ec50 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Tue, 3 Sep 2024 08:56:51 +0100 Subject: [PATCH 07/14] Update gap/attr.gi --- gap/attr.gi | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/gap/attr.gi b/gap/attr.gi index 4dadac3c5..aaf3d1df0 100644 --- a/gap/attr.gi +++ b/gap/attr.gi @@ -702,11 +702,7 @@ function(D) end); InstallMethod(DigraphNrAdjacencies, "for a digraph", [IsDigraphByOutNeighboursRep], -function(D) - local m; - m := DIGRAPH_NRADJACENCIES(D); - return m; -end); +DIGRAPH_NRADJACENCIES); InstallMethod(DigraphNrLoops, "for a digraph by out-neighbours", From b0357a6e5a15652ee115da81bedfcf81971dc7ae Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 4 Sep 2024 04:11:51 +0100 Subject: [PATCH 08/14] Reduce the amount we copy digraphs --- doc/attr.xml | 22 ++++++++ doc/z-chap4.xml | 1 + gap/attr.gi | 4 +- gap/planar.gi | 114 ++++++++++++-------------------------- src/digraphs.c | 2 +- src/planar.c | 10 +--- tst/standard/attr.tst | 6 ++ tst/standard/examples.tst | 2 + tst/standard/planar.tst | 4 +- tst/testinstall.tst | 4 ++ 10 files changed, 76 insertions(+), 93 deletions(-) diff --git a/doc/attr.xml b/doc/attr.xml index 91e995611..3c30d08ce 100644 --- a/doc/attr.xml +++ b/doc/attr.xml @@ -138,6 +138,28 @@ gap> DigraphNrEdges(D); <#/GAPDoc> +<#GAPDoc Label="DigraphNrAdjacencies"> + + + An integer. + Returns the number of pairs of adjacent vertices of the digraph digraph. This + function is agnostic to the direction of an edge, so if digraph constains both the edges (a, + b) and (b, a), this only counts as one adjacency. The following equality holds for + any digraph D with no multiple edges: DigraphNrAdjacencies(D) * 2 - DigraphNrLoops(D) + = DigraphNrEdges(DigraphSymmetricClosure(D)) + gr := Digraph([ +> [1, 3, 4, 5], [1, 2, 3, 5], [2, 4, 5], [2, 4, 5], [1]]);; +gap> DigraphNrAdjacencies(gr); +13 +gap> DigraphNrAdjacencies(gr) * 2 - DigraphNrLoops(gr) = +> DigraphNrEdges(DigraphSymmetricClosure(gr)); +true +]]> + + +<#/GAPDoc> + <#GAPDoc Label="DigraphNrLoops"> diff --git a/doc/z-chap4.xml b/doc/z-chap4.xml index 4af3cab3e..c5e51aae1 100644 --- a/doc/z-chap4.xml +++ b/doc/z-chap4.xml @@ -4,6 +4,7 @@ <#Include Label="DigraphNrVertices"> <#Include Label="DigraphEdges"> <#Include Label="DigraphNrEdges"> + <#Include Label="DigraphNrAdjacencies"> <#Include Label="DigraphNrLoops"> <#Include Label="DigraphSinks"> <#Include Label="DigraphSources"> diff --git a/gap/attr.gi b/gap/attr.gi index aaf3d1df0..7307320fa 100644 --- a/gap/attr.gi +++ b/gap/attr.gi @@ -701,8 +701,8 @@ function(D) return m; end); -InstallMethod(DigraphNrAdjacencies, "for a digraph", [IsDigraphByOutNeighboursRep], -DIGRAPH_NRADJACENCIES); +InstallMethod(DigraphNrAdjacencies, "for a digraph", +[IsDigraphByOutNeighboursRep], DIGRAPH_NRADJACENCIES); InstallMethod(DigraphNrLoops, "for a digraph by out-neighbours", diff --git a/gap/planar.gi b/gap/planar.gi index 386e5c180..0d7f85cd0 100644 --- a/gap/planar.gi +++ b/gap/planar.gi @@ -30,95 +30,50 @@ # 1. Attributes ######################################################################## -InstallMethod(PlanarEmbedding, "for a digraph", [IsDigraph], +HasTrivialRotaionSystem := function(D) - local C; + if IsMultiDigraph(D) then + ErrorNoReturn("expected a digraph with no multiple edges"); + fi; if HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then - return fail; + return false; fi; - C := DigraphMutableCopy(D); - DigraphRemoveAllMultipleEdges(C); - DigraphRemoveLoops(C); - if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then - return OutNeighbors(C); + if DigraphNrVertices(D) < 3 then + return true; fi; - return PLANAR_EMBEDDING(C); -end); + return DigraphNrAdjacencies(D) = DigraphNrLoops(D); +end; -InstallMethod(OuterPlanarEmbedding, "for a digraph", [IsDigraph], +InstallMethod(PlanarEmbedding, "for a digraph", [IsDigraph], function(D) - local C; - if HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then - return fail; - fi; - C := DigraphMutableCopy(D); - DigraphRemoveAllMultipleEdges(C); - DigraphRemoveLoops(C); - if IsEmptyDigraph(C) or DigraphNrVertices(C) < 3 then - return OutNeighbors(C); + if HasTrivialRotaionSystem(D) then; + return OutNeighbors(D); fi; - return OUTER_PLANAR_EMBEDDING(C); + return PLANAR_EMBEDDING(D); end); -InstallMethod(KuratowskiPlanarSubdigraph, "for a digraph", [IsDigraph], +InstallMethod(OuterPlanarEmbedding, "for a digraph", [IsDigraph], function(D) - local C; - C := DigraphMutableCopy(D); - DigraphRemoveAllMultipleEdges(C); - DigraphRemoveLoops(C); - if IsPlanarDigraph(C) then - return fail; + if HasTrivialRotaionSystem(D) then; + return OutNeighbors(D); fi; - return KURATOWSKI_PLANAR_SUBGRAPH(C); + return OUTER_PLANAR_EMBEDDING(D); end); +InstallMethod(KuratowskiPlanarSubdigraph, "for a digraph", [IsDigraph], +KURATOWSKI_PLANAR_SUBGRAPH); + InstallMethod(KuratowskiOuterPlanarSubdigraph, "for a digraph", [IsDigraph], -function(D) - local C; - C := DigraphMutableCopy(D); - DigraphRemoveAllMultipleEdges(C); - DigraphRemoveLoops(C); - if IsOuterPlanarDigraph(C) then - return fail; - fi; - return KURATOWSKI_OUTER_PLANAR_SUBGRAPH(C); -end); +KURATOWSKI_OUTER_PLANAR_SUBGRAPH); InstallMethod(SubdigraphHomeomorphicToK23, "for a digraph", [IsDigraph], -function(D) - local C; - C := DigraphMutableCopy(D); - DigraphRemoveAllMultipleEdges(C); - DigraphRemoveLoops(C); - if IsOuterPlanarDigraph(C) then - return fail; - fi; - return SUBGRAPH_HOMEOMORPHIC_TO_K23(C); -end); +SUBGRAPH_HOMEOMORPHIC_TO_K23); InstallMethod(SubdigraphHomeomorphicToK4, "for a digraph", [IsDigraph], -function(D) - local C; - C := DigraphMutableCopy(D); - DigraphRemoveAllMultipleEdges(C); - DigraphRemoveLoops(C); - if IsOuterPlanarDigraph(C) then - return fail; - fi; - return SUBGRAPH_HOMEOMORPHIC_TO_K4(D); -end); +SUBGRAPH_HOMEOMORPHIC_TO_K4); InstallMethod(SubdigraphHomeomorphicToK33, "for a digraph", [IsDigraph], -function(D) - local C; - C := DigraphMutableCopy(D); - DigraphRemoveAllMultipleEdges(C); - DigraphRemoveLoops(C); - if IsPlanarDigraph(C) then - return fail; - fi; - return SUBGRAPH_HOMEOMORPHIC_TO_K33(C); -end); +SUBGRAPH_HOMEOMORPHIC_TO_K33); ######################################################################## # 2. Properties @@ -126,33 +81,32 @@ end); InstallMethod(IsPlanarDigraph, "for a digraph", [IsDigraph], function(D) - local C, v, e; - C := MaximalAntiSymmetricSubdigraph(DigraphMutableCopyIfMutable(D)); + local v, n_antisymmetric_edges; v := DigraphNrVertices(D); - e := DigraphNrEdges(C); - if v < 5 or e < 9 then + n_antisymmetric_edges := DigraphNrAdjacencies(D) - DigraphNrLoops(D); + if v < 5 or n_antisymmetric_edges < 9 then return true; - elif (IsConnectedDigraph(D) and e > 3 * v - 6) + elif (IsConnectedDigraph(D) and n_antisymmetric_edges > 3 * v - 6) or (HasChromaticNumber(D) and ChromaticNumber(D) > 4) then return false; fi; - return IS_PLANAR(C); + return IS_PLANAR(D); end); InstallMethod(IsOuterPlanarDigraph, "for a digraph", [IsDigraph], function(D) - local C, v, e; + local v, n_antisymmetric_edges; if HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then return false; fi; v := DigraphNrVertices(D); - e := DigraphNrEdges(D); - if v < 4 or e < 6 then + n_antisymmetric_edges := DigraphNrAdjacencies(D) - DigraphNrLoops(D); + + if v < 4 or n_antisymmetric_edges < 6 then return true; elif HasChromaticNumber(D) and ChromaticNumber(D) > 3 then # Outer planar graphs are 3-colourable return false; fi; - C := DigraphMutableCopyIfMutable(D); - return IS_OUTER_PLANAR(MaximalAntiSymmetricSubdigraph(C)); + return IS_OUTER_PLANAR(D); end); diff --git a/src/digraphs.c b/src/digraphs.c index 2a397b7d5..90183ccef 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -148,7 +148,7 @@ Int DigraphNrAdjacencies(Obj D) { Obj const out_v = ELM_LIST(out, v); for (Int w = 1; w <= LEN_LIST(out_v); ++w) { Int u = INT_INTOBJ(ELM_LIST(out_v, w)); - if (v < u + if (v <= u || CALL_3ARGS(IsDigraphEdge, D, INTOBJ_INT(u), INTOBJ_INT(v)) == False) { ++nr; diff --git a/src/planar.c b/src/planar.c index c36071a79..57bc95cd0 100644 --- a/src/planar.c +++ b/src/planar.c @@ -147,16 +147,10 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { if (gp_InitGraph(theGraph, V) != OK) { gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of nodes!", - 0L, - 0L); - return 0L; + return Fail; } else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { gp_Free(&theGraph); - ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of edges!", - 0L, - 0L); - return 0L; + return Fail; } switch (flags) { diff --git a/tst/standard/attr.tst b/tst/standard/attr.tst index a1bf70123..ee41d837c 100644 --- a/tst/standard/attr.tst +++ b/tst/standard/attr.tst @@ -192,6 +192,12 @@ gap> AdjacencyMatrix(Digraph(rec(DigraphNrVertices := 0, > DigraphRange := []))); [ ] +# DigraphNrAdjacencies +gap> G := RandomDigraph(50);; +gap> DigraphNrAdjacencies(G) * 2 - DigraphNrLoops(G) = +> DigraphNrEdges(DigraphSymmetricClosure(G)); +true + # DigraphTopologicalSort gap> r := rec(DigraphNrVertices := 20000, > DigraphSource := [], diff --git a/tst/standard/examples.tst b/tst/standard/examples.tst index d52bf344e..56d1359d5 100644 --- a/tst/standard/examples.tst +++ b/tst/standard/examples.tst @@ -249,6 +249,8 @@ gap> DigraphNrVertices(D); 8 gap> DigraphNrEdges(D); 26 +gap> DigraphNrAdjacencies(D); +13 gap> DigraphUndirectedGirth(D); 3 gap> LollipopGraph(IsMutableDigraph, 5, 3); diff --git a/tst/standard/planar.tst b/tst/standard/planar.tst index 80bf205fa..c8f4c8ecf 100644 --- a/tst/standard/planar.tst +++ b/tst/standard/planar.tst @@ -235,9 +235,9 @@ gap> IS_PLANAR(2); Error, Digraphs: boyers_planarity_check (C): the 1st argument must be a digrap\ h, not integer gap> IS_PLANAR(NullDigraph(0)); -Error, Digraphs: boyers_planarity_check (C): invalid number of nodes! +fail gap> IS_PLANAR(NullDigraph(70000)); -Error, Digraphs: boyers_planarity_check (C): invalid number of edges! +fail gap> IsPlanarDigraph(NullDigraph(70000)); true gap> IS_PLANAR(CompleteDigraph(2)); diff --git a/tst/testinstall.tst b/tst/testinstall.tst index e165c78c2..0012bcd8e 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -246,9 +246,13 @@ gap> gr := DigraphRemoveEdge(gr, [1, 2]);; gap> gr := DigraphRemoveEdges(gr, [[1, 2], [2, 1]]);; gap> DigraphNrEdges(gr); 40 +gap> DigraphNrAdjacencies(gr); +20 gap> gr2 := DigraphClosure(gr, 7);; gap> DigraphNrEdges(gr2); 42 +gap> DigraphNrAdjacencies(gr2); +21 # Fix seg fault cause by wrong handling of no edges in # FuncDIGRAPH_SOURCE_RANGE From 58f9289a5152ae63244bbf92a86b7ba00d89dbe6 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 4 Sep 2024 04:17:40 +0100 Subject: [PATCH 09/14] fix spelling --- doc/attr.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/attr.xml b/doc/attr.xml index 3c30d08ce..f7a9b3a17 100644 --- a/doc/attr.xml +++ b/doc/attr.xml @@ -143,7 +143,7 @@ gap> DigraphNrEdges(D); An integer. Returns the number of pairs of adjacent vertices of the digraph digraph. This - function is agnostic to the direction of an edge, so if digraph constains both the edges (a, + function is agnostic to the direction of an edge, so if digraph contains both the edges (a, b) and (b, a), this only counts as one adjacency. The following equality holds for any digraph D with no multiple edges: DigraphNrAdjacencies(D) * 2 - DigraphNrLoops(D) = DigraphNrEdges(DigraphSymmetricClosure(D)) From b9d5dc83979a0027410735eea29ecc0916c95e4b Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 4 Sep 2024 04:22:37 +0100 Subject: [PATCH 10/14] Add mult-digraph check --- src/planar.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/planar.c b/src/planar.c index 57bc95cd0..13559f753 100644 --- a/src/planar.c +++ b/src/planar.c @@ -112,6 +112,9 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { (Int) TNAM_OBJ(digraph), 0L); } + if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { + ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); + } Obj const out = FuncOutNeighbours(0L, digraph); Int V = DigraphNrVertices(digraph); Int E = 0; From 32f8e54c056d70264047a51b15915fd9d52a8f34 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 4 Sep 2024 16:58:03 +0100 Subject: [PATCH 11/14] Fix edge cases --- doc/attr.xml | 28 ++++++++++++++++--- doc/z-chap4.xml | 1 + gap/attr.gd | 1 + gap/attr.gi | 3 ++ gap/planar.gi | 31 ++++++++++----------- src/digraphs.c | 29 ++++++++++++++++++++ src/digraphs.h | 1 + src/planar.c | 61 ++++++++++++++++++++++++++++++----------- tst/standard/planar.tst | 4 +-- 9 files changed, 120 insertions(+), 39 deletions(-) diff --git a/doc/attr.xml b/doc/attr.xml index f7a9b3a17..341f979d6 100644 --- a/doc/attr.xml +++ b/doc/attr.xml @@ -142,11 +142,10 @@ gap> DigraphNrEdges(D); An integer. - Returns the number of pairs of adjacent vertices of the digraph digraph. This - function is agnostic to the direction of an edge, so if digraph contains both the edges (a, - b) and (b, a), this only counts as one adjacency. The following equality holds for + Returns the number of sets \{u, v\} of vertices of the digraph digraph, such that + either (u, v) or (v, u) is an edge. The following equality holds for any digraph D with no multiple edges: DigraphNrAdjacencies(D) * 2 - DigraphNrLoops(D) - = DigraphNrEdges(DigraphSymmetricClosure(D)) + = DigraphNrEdges(DigraphSymmetricClosure(D)). gr := Digraph([ > [1, 3, 4, 5], [1, 2, 3, 5], [2, 4, 5], [2, 4, 5], [1]]);; @@ -160,6 +159,27 @@ true <#/GAPDoc> +<#GAPDoc Label="DigraphNrAdjacenciesWithoutLoops"> + + + An integer. + Returns the number of sets \{u, v\} of vertices of the digraph digraph, such that + u \neq v and either (u, v) or (v, u) is an edge. The following equality holds for + any digraph D with no multiple edges: DigraphNrAdjacencies(D) * 2 + DigraphNrLoops(D) + = DigraphNrEdges(DigraphSymmetricClosure(D)). + gr := Digraph([ +> [1, 3, 4, 5], [1, 2, 3, 5], [2, 4, 5], [2, 4, 5], [1]]);; +gap> DigraphNrAdjacencies(gr); +10 +gap> DigraphNrAdjacencies(gr) * 2 + DigraphNrLoops(gr) = +> DigraphNrEdges(DigraphSymmetricClosure(gr)); +true +]]> + + +<#/GAPDoc> + <#GAPDoc Label="DigraphNrLoops"> diff --git a/doc/z-chap4.xml b/doc/z-chap4.xml index c5e51aae1..4e87e055b 100644 --- a/doc/z-chap4.xml +++ b/doc/z-chap4.xml @@ -5,6 +5,7 @@ <#Include Label="DigraphEdges"> <#Include Label="DigraphNrEdges"> <#Include Label="DigraphNrAdjacencies"> + <#Include Label="DigraphNrAdjacenciesWithoutLoops"> <#Include Label="DigraphNrLoops"> <#Include Label="DigraphSinks"> <#Include Label="DigraphSources"> diff --git a/gap/attr.gd b/gap/attr.gd index a8b99b33a..7814a5f37 100644 --- a/gap/attr.gd +++ b/gap/attr.gd @@ -15,6 +15,7 @@ DeclareAttribute("DigraphNrVertices", IsDigraph); DeclareAttribute("DigraphEdges", IsDigraph); DeclareAttribute("DigraphNrEdges", IsDigraph); DeclareAttribute("DigraphNrAdjacencies", IsDigraph); +DeclareAttribute("DigraphNrAdjacenciesWithoutLoops", IsDigraph); DeclareAttribute("DigraphNrLoops", IsDigraph); DeclareAttribute("DigraphHash", IsDigraph); diff --git a/gap/attr.gi b/gap/attr.gi index 7307320fa..67bb792ac 100644 --- a/gap/attr.gi +++ b/gap/attr.gi @@ -704,6 +704,9 @@ end); InstallMethod(DigraphNrAdjacencies, "for a digraph", [IsDigraphByOutNeighboursRep], DIGRAPH_NRADJACENCIES); +InstallMethod(DigraphNrAdjacenciesWithoutLoops, "for a digraph", +[IsDigraphByOutNeighboursRep], DIGRAPH_NRADJACENCIESWITHOUTLOOPS); + InstallMethod(DigraphNrLoops, "for a digraph by out-neighbours", [IsDigraphByOutNeighboursRep], diff --git a/gap/planar.gi b/gap/planar.gi index 0d7f85cd0..9a8fbbfb2 100644 --- a/gap/planar.gi +++ b/gap/planar.gi @@ -30,23 +30,21 @@ # 1. Attributes ######################################################################## -HasTrivialRotaionSystem := +BindGlobal("DIGRAPHS_HasTrivialRotationSystem", function(D) if IsMultiDigraph(D) then ErrorNoReturn("expected a digraph with no multiple edges"); - fi; - if HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then + elif HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then return false; - fi; - if DigraphNrVertices(D) < 3 then + elif DigraphNrVertices(D) < 3 then return true; fi; - return DigraphNrAdjacencies(D) = DigraphNrLoops(D); -end; + return DigraphNrAdjacenciesWithoutLoops(D) = 0; +end); InstallMethod(PlanarEmbedding, "for a digraph", [IsDigraph], function(D) - if HasTrivialRotaionSystem(D) then; + if DIGRAPHS_HasTrivialRotationSystem(D) then; return OutNeighbors(D); fi; return PLANAR_EMBEDDING(D); @@ -54,7 +52,7 @@ end); InstallMethod(OuterPlanarEmbedding, "for a digraph", [IsDigraph], function(D) - if HasTrivialRotaionSystem(D) then; + if DIGRAPHS_HasTrivialRotationSystem(D) then; return OutNeighbors(D); fi; return OUTER_PLANAR_EMBEDDING(D); @@ -81,12 +79,12 @@ SUBGRAPH_HOMEOMORPHIC_TO_K33); InstallMethod(IsPlanarDigraph, "for a digraph", [IsDigraph], function(D) - local v, n_antisymmetric_edges; + local v, e; v := DigraphNrVertices(D); - n_antisymmetric_edges := DigraphNrAdjacencies(D) - DigraphNrLoops(D); - if v < 5 or n_antisymmetric_edges < 9 then + e := DigraphNrAdjacenciesWithoutLoops(D); + if v < 5 or e < 9 then return true; - elif (IsConnectedDigraph(D) and n_antisymmetric_edges > 3 * v - 6) + elif (IsConnectedDigraph(D) and e > 3 * v - 6) or (HasChromaticNumber(D) and ChromaticNumber(D) > 4) then return false; fi; @@ -95,14 +93,13 @@ end); InstallMethod(IsOuterPlanarDigraph, "for a digraph", [IsDigraph], function(D) - local v, n_antisymmetric_edges; + local v, e; if HasIsPlanarDigraph(D) and not IsPlanarDigraph(D) then return false; fi; v := DigraphNrVertices(D); - n_antisymmetric_edges := DigraphNrAdjacencies(D) - DigraphNrLoops(D); - - if v < 4 or n_antisymmetric_edges < 6 then + e := DigraphNrAdjacenciesWithoutLoops(D); + if v < 4 or e < 6 then return true; elif HasChromaticNumber(D) and ChromaticNumber(D) > 3 then # Outer planar graphs are 3-colourable diff --git a/src/digraphs.c b/src/digraphs.c index 90183ccef..6697ad613 100644 --- a/src/digraphs.c +++ b/src/digraphs.c @@ -166,6 +166,34 @@ static Obj FuncDIGRAPH_NRADJACENCIES(Obj self, Obj D) { return INTOBJ_INT(DigraphNrAdjacencies(D)); } +Int DigraphNrAdjacenciesWithoutLoops(Obj D) { + Int nr = 0; + if (IsbPRec(D, RNamName("DigraphNrAdjacenciesWithoutLoops"))) { + return INT_INTOBJ(ElmPRec(D, RNamName("DigraphNrAdjacenciesWithoutLoops"))); + } else { + Obj const out = FuncOutNeighbours(0L, D); + for (Int v = 1; v <= LEN_LIST(out); ++v) { + Obj const out_v = ELM_LIST(out, v); + for (Int w = 1; w <= LEN_LIST(out_v); ++w) { + Int u = INT_INTOBJ(ELM_LIST(out_v, w)); + if (v < u + || CALL_3ARGS(IsDigraphEdge, D, INTOBJ_INT(u), INTOBJ_INT(v)) + == False) { + ++nr; + } + } + } + } + if (IsAttributeStoringRep(D)) { + AssPRec(D, RNamName("DigraphNrAdjacenciesWithoutLoops"), INTOBJ_INT(nr)); + } + return nr; +} + +static Obj FuncDIGRAPH_NRADJACENCIESWITHOUTLOOPS(Obj self, Obj D) { + return INTOBJ_INT(DigraphNrAdjacenciesWithoutLoops(D)); +} + /**************************************************************************** ** *F FuncGABOW_SCC @@ -2142,6 +2170,7 @@ FuncMULTIDIGRAPH_CANONICAL_LABELLING(Obj self, Obj digraph, Obj colours) { static StructGVarFunc GVarFuncs[] = { GVAR_FUNC(DIGRAPH_NREDGES, 1, "digraph"), GVAR_FUNC(DIGRAPH_NRADJACENCIES, 1, "digraph"), + GVAR_FUNC(DIGRAPH_NRADJACENCIESWITHOUTLOOPS, 1, "digraph"), GVAR_FUNC(GABOW_SCC, 1, "adj"), GVAR_FUNC(DIGRAPH_CONNECTED_COMPONENTS, 1, "digraph"), GVAR_FUNC(IS_ACYCLIC_DIGRAPH, 1, "adj"), diff --git a/src/digraphs.h b/src/digraphs.h index 4017a16a3..a5a2708e3 100644 --- a/src/digraphs.h +++ b/src/digraphs.h @@ -25,6 +25,7 @@ Obj FuncADJACENCY_MATRIX(Obj self, Obj D); Int DigraphNrEdges(Obj digraph); Int DigraphNrAdjacencies(Obj digraph); +Int DigraphNrAdjacenciesWithoutLoops(Obj digraph); Obj DigraphSource(Obj digraph); Obj DigraphRange(Obj digraph); diff --git a/src/planar.c b/src/planar.c index 13559f753..7d3233c5f 100644 --- a/src/planar.c +++ b/src/planar.c @@ -98,6 +98,28 @@ Obj FuncSUBGRAPH_HOMEOMORPHIC_TO_K4(Obj self, Obj digraph) { // The implementation of the main functions in this file. +Obj trivial_planarity_output(Int V, bool krtwsk) { + Obj res; + if (krtwsk) { + Obj subgraph = NEW_PLIST_IMM(T_PLIST, V); + SET_LEN_PLIST(subgraph, V); + for (int i = 1; i <= V; ++i) { + Obj list = NEW_PLIST_IMM(T_PLIST, 0); + SET_LEN_PLIST(list, 0); + SET_ELM_PLIST(subgraph, i, list); + CHANGED_BAG(subgraph); + } + res = NEW_PLIST_IMM(T_PLIST, 2); + SET_LEN_PLIST(res, 2); + SET_ELM_PLIST(res, 1, True); + SET_ELM_PLIST(res, 2, subgraph); + CHANGED_BAG(res); + } else { + res = True; + } + return res; +} + // This function only accepts digraphs without multiple edges Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { @@ -115,19 +137,15 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { if (CALL_1ARGS(IsMultiDigraph, digraph) == True) { ErrorQuit("expected a digraph without multiple edges!", 0L, 0L); } - Obj const out = FuncOutNeighbours(0L, digraph); - Int V = DigraphNrVertices(digraph); - Int E = 0; - for (Int v = 1; v <= LEN_LIST(out); ++v) { - Obj const out_v = ELM_LIST(out, v); - for (Int w = 1; w <= LEN_LIST(out_v); ++w) { - Int u = INT_INTOBJ(ELM_LIST(out_v, w)); - if (v < u - || CALL_3ARGS(IsDigraphEdge, digraph, INTOBJ_INT(u), INTOBJ_INT(v)) - == False) { - ++E; - } - } + + Int V = DigraphNrVertices(digraph); + if (V == 0) { + return trivial_planarity_output(0, krtwsk); + } + + Int E = DigraphNrAdjacenciesWithoutLoops(digraph); + if (E == 0) { + return trivial_planarity_output(V, krtwsk); } if (V > INT_MAX) { // Cannot currently test this, it might always be true, depending on the @@ -150,10 +168,16 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { if (gp_InitGraph(theGraph, V) != OK) { gp_Free(&theGraph); - return Fail; + ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of nodes!", + 0L, + 0L); + return 0L; } else if (gp_EnsureArcCapacity(theGraph, 2 * E) != OK) { gp_Free(&theGraph); - return Fail; + ErrorQuit("Digraphs: boyers_planarity_check (C): invalid number of edges!", + 0L, + 0L); + return 0L; } switch (flags) { @@ -170,8 +194,10 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { break; } - int status; + int status; + Obj const out = FuncOutNeighbours(0L, digraph); + // Construct the antisymmetric digraph with no loops for (Int v = 1; v <= LEN_LIST(out); ++v) { DIGRAPHS_ASSERT(gp_VertexInRange(theGraph, v)); gp_SetVertexIndex(theGraph, v, v); @@ -196,6 +222,7 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { } } } + status = gp_Embed(theGraph, flags); if (status == NOTOK) { // Cannot currently test this, i.e. it shouldn't happen (and @@ -203,6 +230,8 @@ Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk) { gp_Free(&theGraph); ErrorQuit("Digraphs: boyers_planarity_check (C): status is not ok", 0L, 0L); } + + // Construct the return value Obj res; if (krtwsk) { // Kuratowski subgraph isolator diff --git a/tst/standard/planar.tst b/tst/standard/planar.tst index c8f4c8ecf..b0d08f622 100644 --- a/tst/standard/planar.tst +++ b/tst/standard/planar.tst @@ -235,9 +235,9 @@ gap> IS_PLANAR(2); Error, Digraphs: boyers_planarity_check (C): the 1st argument must be a digrap\ h, not integer gap> IS_PLANAR(NullDigraph(0)); -fail +true gap> IS_PLANAR(NullDigraph(70000)); -fail +true gap> IsPlanarDigraph(NullDigraph(70000)); true gap> IS_PLANAR(CompleteDigraph(2)); From 73ba5ea9dccc04c6b1ee4328fae86a3a939dc3a5 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 4 Sep 2024 17:04:09 +0100 Subject: [PATCH 12/14] Correct function call in manual example --- doc/attr.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/attr.xml b/doc/attr.xml index 341f979d6..7eecc5721 100644 --- a/doc/attr.xml +++ b/doc/attr.xml @@ -165,14 +165,14 @@ true An integer. Returns the number of sets \{u, v\} of vertices of the digraph digraph, such that u \neq v and either (u, v) or (v, u) is an edge. The following equality holds for - any digraph D with no multiple edges: DigraphNrAdjacencies(D) * 2 + DigraphNrLoops(D) + any digraph D with no multiple edges: DigraphNrAdjacenciesWithoutLoops(D) * 2 + DigraphNrLoops(D) = DigraphNrEdges(DigraphSymmetricClosure(D)). gr := Digraph([ > [1, 3, 4, 5], [1, 2, 3, 5], [2, 4, 5], [2, 4, 5], [1]]);; -gap> DigraphNrAdjacencies(gr); +gap> DigraphNrAdjacenciesWithoutLoops(gr); 10 -gap> DigraphNrAdjacencies(gr) * 2 + DigraphNrLoops(gr) = +gap> DigraphNrAdjacenciesWithoutLoops(gr) * 2 + DigraphNrLoops(gr) = > DigraphNrEdges(DigraphSymmetricClosure(gr)); true ]]> From 497d955f1c81bf49187c5fcd83e27dea4ae34ce0 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 4 Sep 2024 17:37:17 +0100 Subject: [PATCH 13/14] Add forward decl --- gap/planar.gi | 3 +++ src/planar.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/gap/planar.gi b/gap/planar.gi index 9a8fbbfb2..8a36385ca 100644 --- a/gap/planar.gi +++ b/gap/planar.gi @@ -82,6 +82,9 @@ function(D) local v, e; v := DigraphNrVertices(D); e := DigraphNrAdjacenciesWithoutLoops(D); + if HasIsPlanarDigraph(D) then + return IsPlanarDigraph(D); + fi; if v < 5 or e < 9 then return true; elif (IsConnectedDigraph(D) and e > 3 * v - 6) diff --git a/src/planar.c b/src/planar.c index 7d3233c5f..1cdf94a76 100644 --- a/src/planar.c +++ b/src/planar.c @@ -49,6 +49,9 @@ #pragma GCC diagnostic pop #endif +// Forward declaration of the main function in this file. +Obj trivial_planarity_output(Int V, bool krtwsk); + // Forward declaration of the main function in this file. Obj boyers_planarity_check(Obj digraph, int flags, bool krtwsk); From de6266696b44bdbd5aee88a1100e6e6fbf9591e1 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Thu, 5 Sep 2024 13:05:29 +0100 Subject: [PATCH 14/14] Remove random graph from test --- tst/standard/attr.tst | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tst/standard/attr.tst b/tst/standard/attr.tst index ee41d837c..3d79a8c5f 100644 --- a/tst/standard/attr.tst +++ b/tst/standard/attr.tst @@ -193,10 +193,42 @@ gap> AdjacencyMatrix(Digraph(rec(DigraphNrVertices := 0, [ ] # DigraphNrAdjacencies -gap> G := RandomDigraph(50);; +gap> G := Digraph([[1, 3, 4, 5, 6, 7, 10, 12, 14, 15, 16, 17, 19, 20, 21, 22, 23, 26, 28, 29, 30], +> [2, 3, 4, 6, 7, 8, 11, 13, 14, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30], +> [1, 2, 4, 5, 6, 9, 10, 12, 14, 15, 17, 20, 22, 24, 25, 26, 27, 28, 30], +> [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29], +> [1, 4, 6, 7, 9, 10, 11, 12, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30], +> [1, 5, 6, 7, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 28, 29, 30], +> [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 13, 15, 16, 18, 19, 21, 22, 23, 25, 26, 27, 28, 30], +> [2, 5, 6, 8, 9, 10, 12, 13, 14, 15, 19, 20, 21, 22, 23, 24, 25, 26, 29], +> [1, 5, 6, 9, 12, 13, 14, 16, 18, 20, 21, 22, 23, 24, 25, 26, 29, 30], +> [1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 15, 16, 18, 19, 20, 21, 22, 25, 28], +> [1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29], +> [1, 2, 5, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30], +> [1, 3, 4, 5, 8, 9, 10, 14, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29, 30], +> [2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30], +> [1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 13, 14, 16, 17, 20, 22, 23, 24, 25, 26, 27, 28], +> [1, 3, 6, 8, 10, 11, 13, 14, 15, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], +> [1, 2, 3, 6, 7, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 22, 23, 26, 27, 30], +> [1, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 30], +> [1, 3, 4, 5, 6, 10, 11, 13, 14, 15, 18, 19, 20, 21, 22, 24, 25, 27, 28, 29], +> [1, 2, 4, 7, 8, 9, 10, 11, 18, 20, 21, 22, 23, 24, 25, 26, 28, 30], +> [1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 18, 19, 20, 21, 22, 24, 25, 26, 28, 29, 30], +> [1, 3, 5, 7, 8, 9, 10, 12, 13, 14, 17, 18, 19, 21, 24, 26, 29, 30], +> [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 16, 17, 18, 19, 20, 21, 23, 24, 25, 27, 28, 29], +> [1, 2, 3, 6, 8, 9, 11, 12, 14, 16, 17, 18, 20, 22, 25, 26, 27, 28, 29, 30], +> [1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 24, 25, 27, 28, 29, 30], +> [1, 2, 5, 6, 7, 10, 12, 13, 14, 15, 16, 19, 20, 21, 24, 25, 26, 27, 28, 29, 30], +> [1, 2, 4, 5, 6, 7, 9, 10, 13, 17, 18, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30], +> [1, 3, 4, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19, 20, 21, 23, 24, 26, 27, 29], +> [1, 4, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18, 20, 21, 23, 24, 25, 27, 28, 30], +> [1, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 18, 19, 20, 21, 22, 24, 27, 28, 29]]);; gap> DigraphNrAdjacencies(G) * 2 - DigraphNrLoops(G) = > DigraphNrEdges(DigraphSymmetricClosure(G)); true +gap> DigraphNrAdjacenciesWithoutLoops(G) * 2 + DigraphNrLoops(G) = +> DigraphNrEdges(DigraphSymmetricClosure(G)); +true # DigraphTopologicalSort gap> r := rec(DigraphNrVertices := 20000,