diff --git a/doc/prop.xml b/doc/prop.xml index 5ea727270..11af93010 100644 --- a/doc/prop.xml +++ b/doc/prop.xml @@ -1580,31 +1580,31 @@ false]]> <#/GAPDoc> -<#GAPDoc Label="IsTwoEdgeTransitive"> +<#GAPDoc Label="Is2EdgeTransitive"> - + true or false. - If digraph is a digraph without multiple edges, then IsTwoEdgeTransitive + If digraph is a digraph without multiple edges, then Is2EdgeTransitive returns true if digraph is 2-edge transitive, and false otherwise. A digraph is 2-edge transitive if its automorphism group - acts transitively on its 2-edges via the action - . + acts transitively on 2-edges via the action + . - A 2-edge of a digraph is a pair of its edges, such that the range of the - first edge is equal to the source of the second edge. + A 2-edge in a digraph is a triple (u, v, w) of distinct vertices + such that (u, v) and (v, w) are edges.

&MUTABLE_RECOMPUTED_PROP; IsTwoEdgeTransitive(CompleteDigraph(4)); +gap> Is2EdgeTransitive(CompleteDigraph(4)); true -gap> IsTwoEdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 1], [3, 4]])) +gap> Is2EdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 4]])); false -gap> IsTwoEdgeTransitive(CycleDigraph(5)); +gap> Is2EdgeTransitive(CycleDigraph(5)); true -gap> IsTwoEdgeTransitive(Digraph([[2], [3, 3, 3], []])); +gap> Is2EdgeTransitive(Digraph([[2], [3, 3, 3], []])); Error, the argument must be a digraph with no multiple edges, ]]> diff --git a/doc/z-chap5.xml b/doc/z-chap5.xml index 703832dc5..759876e11 100644 --- a/doc/z-chap5.xml +++ b/doc/z-chap5.xml @@ -78,6 +78,7 @@ <#Include Label="IsDigraphCore"> <#Include Label="IsEdgeTransitive"> <#Include Label="IsVertexTransitive"> + <#Include Label="Is2EdgeTransitive"> diff --git a/gap/prop.gd b/gap/prop.gd index ee3ab4453..a25712a08 100644 --- a/gap/prop.gd +++ b/gap/prop.gd @@ -52,7 +52,7 @@ DeclareProperty("IsMeetSemilatticeDigraph", IsDigraph); DeclareProperty("IsPermutationDigraph", IsDigraph); DeclareProperty("IsDistributiveLatticeDigraph", IsDigraph); DeclareProperty("IsModularLatticeDigraph", IsDigraph); -DeclareProperty("IsTwoEdgeTransitive", IsDigraph); +DeclareProperty("Is2EdgeTransitive", IsDigraph); DeclareSynonymAttr("IsLatticeDigraph", IsMeetSemilatticeDigraph and IsJoinSemilatticeDigraph); DeclareSynonymAttr("IsPreorderDigraph", diff --git a/gap/prop.gi b/gap/prop.gi index e519535b2..22ee67fae 100644 --- a/gap/prop.gi +++ b/gap/prop.gi @@ -687,25 +687,94 @@ function(D) return LatticeDigraphEmbedding(N5, D) = fail; end); -InstallMethod(IsTwoEdgeTransitive, +InstallMethod(Is2EdgeTransitive, "for a digraph without multiple edges", [IsDigraph], function(D) - local twoEdges; - + local Aut, O, I, Centers, Count, In, Out, u; if IsMultiDigraph(D) then ErrorNoReturn("the argument must be a digraph with no multiple", " edges,"); fi; - twoEdges := Filtered(Cartesian(DigraphEdges(D), DigraphEdges(D)), - pair -> pair[1][2] = pair[2][1] - and pair[1][1] <> pair[2][2]); + Aut := AutomorphismGroup(D); + D := DigraphRemoveLoops(D); + O := D!.OutNeighbours; + I := InNeighbours(D); + # The list Centers will store all those vertices which lie at the + # center of a 2-edge. + + Centers := []; + + for u in [1 .. Length(O)] do + if Length(O[u]) > 0 and Length(I[u]) > 0 then + # If u has precisely one in neighbour and out neighbour, + # we must check these are not the same vertex as then there + # would be no 2-edge centered at u. + + if Length(O[u]) = 1 and Length(I[u]) = 1 then + if O[u][1] = I[u][1] then + continue; + fi; + fi; + if not IsBound(Out) then + Out := Length(O[u]); + In := Length(I[u]); + fi; + # For D to be 2-edge transitive, it must be transitive + # on 2-edge centers, so all 2-edge centers must have the + # same in-degree and same out-degree. - if Length(twoEdges) = 0 then + if Out <> Length(O[u]) or In <> Length(I[u]) then + return false; + fi; + Add(Centers, u); + fi; + od; + # If Centers is empty, D has no 2-edges so is vacuously 2-edge + # transtive. + + if Length(Centers) = 0 then return true; - else - return OrbitLength(AutomorphismGroup(D), twoEdges[1], OnTuplesTuples) - = Length(twoEdges); fi; + # Find the number of 2-cycles at any center. We will have to subtract + # these from the total number of 2-edges as 2-cycles are not classed + # as 2-edges. + + Count := 0; + for u in O[Centers[1]] do + if Centers[1] in O[u] then + Count := Count + 1; + fi; + od; + + # Find a 2-edge and check if its orbit length equals the number of 2-edges. + # By this point, we know that D is likely a highly symmetric digraph, + # since all 2-edge centers share a common in and out degree + # (This is by no means a guarantee, see Frucht's graph). From testing, + # calculating the stabilizer and using the orbit-stabilizer + # theorem is usually must faster in this case, so we instead determine + # the stabilizer of a 2-edge. + + for u in I[Centers[1]] do + if Position(O[Centers[1]], u) = 1 then + if Length(O[Centers[1]]) = 1 then + continue; + else + return (In * Out - Count) * Length(Centers) = + Order(Aut) / Order(Stabilizer(Aut, + [u, + Centers[1], + O[Centers[1]][2]], + OnTuples)); + fi; + else + return (In * Out - Count) * Length(Centers) = + Order(Aut) / Order(Stabilizer(Aut, + [u, + Centers[1], + O[Centers[1]][1]], + OnTuples)); + fi; + od; end); diff --git a/tst/standard/prop.tst b/tst/standard/prop.tst index b459e4ee3..503ca8dbb 100644 --- a/tst/standard/prop.tst +++ b/tst/standard/prop.tst @@ -1679,22 +1679,22 @@ true gap> IsEdgeTransitive(Digraph([[2], [3, 3, 3], []])); Error, the argument must be a digraph with no multiple edges, -# IsTwoEdgeTransitive -gap> IsTwoEdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 1]])); +# Is2EdgeTransitive +gap> Is2EdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 1]])); true -gap> IsTwoEdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 1], [3, 4]])); +gap> Is2EdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 1], [3, 4]])); false -gap> IsTwoEdgeTransitive(CompleteDigraph(4)); +gap> Is2EdgeTransitive(CompleteDigraph(4)); true -gap> IsTwoEdgeTransitive(CycleDigraph(100)); +gap> Is2EdgeTransitive(CycleDigraph(100)); true -gap> IsTwoEdgeTransitive(CompleteBipartiteDigraph(11, 23)); +gap> Is2EdgeTransitive(CompleteBipartiteDigraph(11, 23)); false -gap> IsTwoEdgeTransitive(DigraphByEdges([[1, 2]])); +gap> Is2EdgeTransitive(DigraphByEdges([[1, 2]])); true -gap> IsTwoEdgeTransitive(DigraphByEdges([])); +gap> Is2EdgeTransitive(DigraphByEdges([])); true -gap> IsTwoEdgeTransitive(Digraph([[2], [3, 3, 3], []])); +gap> Is2EdgeTransitive(Digraph([[2], [3, 3, 3], []])); Error, the argument must be a digraph with no multiple edges, # DigraphHasNoVertices and DigraphHasAVertex diff --git a/tst/testinstall.tst b/tst/testinstall.tst index 36a7e9c06..0f5f15ba1 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -436,8 +436,8 @@ rec( distances := [ [ 0, 5 ], [ 5, 0 ] ], gap> EdgeWeightedDigraphShortestPath(d, 1, 2); [ [ 1, 2 ], [ 1 ] ] -# IsTwoEdgeTransitive -gap> IsTwoEdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 1]])); +# Is2EdgeTransitive +gap> Is2EdgeTransitive(DigraphByEdges([[1, 2], [2, 3], [3, 1]])); true # Issue 617: bug in DigraphRemoveEdge, wasn't removing edge labels