Skip to content

Commit

Permalink
Fix edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
Joseph-Edwards committed Sep 4, 2024
1 parent 14d930c commit bea14d0
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 39 deletions.
28 changes: 24 additions & 4 deletions doc/attr.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,10 @@ gap> DigraphNrEdges(D);
<ManSection>
<Attr Name="DigraphNrAdjacencies" Arg="digraph" />
<Returns>An integer.</Returns>
<Description> Returns the number of pairs of adjacent vertices of the digraph <A>digraph</A>. This
function is agnostic to the direction of an edge, so if <A>digraph</A> contains both the edges <M>(a,
b)</M> and <M>(b, a)</M>, this only counts as one adjacency. The following equality holds for
<Description> Returns the number of sets <M>\{u, v\}</M> of vertices of the digraph <A>digraph</A>, such that
either <M>(u, v)</M> or <M>(v, u)</M> is an edge. The following equality holds for
any digraph <C>D</C> with no multiple edges: <C>DigraphNrAdjacencies(D) * 2 - DigraphNrLoops(D)
= DigraphNrEdges(DigraphSymmetricClosure(D))</C>
= DigraphNrEdges(DigraphSymmetricClosure(D))</C>.
<Example><![CDATA[
gap> gr := Digraph([
> [1, 3, 4, 5], [1, 2, 3, 5], [2, 4, 5], [2, 4, 5], [1]]);;
Expand All @@ -160,6 +159,27 @@ true
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphNrAdjacenciesWithoutLoops">
<ManSection>
<Attr Name="DigraphNrAdjacenciesWithoutLoops" Arg="digraph" />
<Returns>An integer.</Returns>
<Description> Returns the number of sets <M>\{u, v\}</M> of vertices of the digraph <A>digraph</A>, such that
<M>u \neq v</M> and either <M>(u, v)</M> or <M>(v, u)</M> is an edge. The following equality holds for
any digraph <C>D</C> with no multiple edges: <C>DigraphNrAdjacencies(D) * 2 + DigraphNrLoops(D)
= DigraphNrEdges(DigraphSymmetricClosure(D))</C>.
<Example><![CDATA[
gap> 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
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphNrLoops">
<ManSection>
<Attr Name="DigraphNrLoops" Arg="digraph"/>
Expand Down
1 change: 1 addition & 0 deletions doc/z-chap4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
1 change: 1 addition & 0 deletions gap/attr.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions gap/attr.gi
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
31 changes: 14 additions & 17 deletions gap/planar.gi
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,29 @@
# 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);
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);
Expand All @@ -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;
Expand All @@ -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
Expand Down
29 changes: 29 additions & 0 deletions src/digraphs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"),
Expand Down
1 change: 1 addition & 0 deletions src/digraphs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
61 changes: 45 additions & 16 deletions src/planar.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -196,13 +222,16 @@ 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
// currently there is no example where it does happen)
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
Expand Down
4 changes: 2 additions & 2 deletions tst/standard/planar.tst
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down

0 comments on commit bea14d0

Please sign in to comment.