From 3bd82a7c12d8c0ba16e0242e40713803280040b0 Mon Sep 17 00:00:00 2001 From: Joseph Edwards Date: Wed, 4 Sep 2024 04:11:51 +0100 Subject: [PATCH] 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 3a6769da8..42649c41b 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 0f2ebe1ee..79113ce92 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 68fdd903c..3078cdd72 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 167efe8be..a51a6bed2 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 e3307afbd..938397521 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