From d522900f54499322d84995150ed53c967abafd98 Mon Sep 17 00:00:00 2001 From: Borbala Forizs Date: Mon, 19 Oct 2026 10:34:52 +0200 Subject: [PATCH] Add DigraphMaximalQuotients --- gap/attr.gd | 1 + gap/attr.gi | 43 +++++++++++++++++++++++++++ tst/standard/attr.tst | 68 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/gap/attr.gd b/gap/attr.gd index 09d171f6a..9bc9a8c9c 100644 --- a/gap/attr.gd +++ b/gap/attr.gd @@ -74,6 +74,7 @@ DeclareAttribute("ChromaticNumber", IsDigraph); DeclareAttribute("DigraphBicomponents", IsDigraph); DeclareAttribute("DIGRAPHS_Bipartite", IsDigraph); DeclareAttribute("DigraphCore", IsDigraph); +DeclareAttribute("DigraphMaximalQuotients", IsDigraph); DeclareAttribute("CharacteristicPolynomial", IsDigraph); DeclareAttribute("NrSpanningTrees", IsDigraph); diff --git a/gap/attr.gi b/gap/attr.gi index 5025a453f..5101c9cb0 100644 --- a/gap/attr.gi +++ b/gap/attr.gi @@ -2409,6 +2409,49 @@ function(D) return DigraphVertexLabels(D); end); +InstallMethod(DigraphMaximalQuotients, "for a digraph", [IsDigraph], +function(D) + local n, aut, orbs, pairs_to_merge, candidates, c, distinct_canons, + max_quotients; + + n := DigraphNrVertices(D); + if n < 2 then + return []; + elif IsEmptyDigraph(D) then + return [EmptyDigraph(n - 1)]; + fi; + + # This is to ensure compatibility with AutomorphismGroup and QuotientDigraph. + if IsMultiDigraph(D) then + aut := AutomorphismGroup(DigraphRemoveAllMultipleEdges(D)); + else + aut := AutomorphismGroup(D); + fi; + + # Creating orbit representatives. + orbs := Orbits(aut, Combinations(DigraphVertices(D), 2), OnSets); + pairs_to_merge := List(orbs, Representative); + + # Creating list of candidate maximal quotients. + candidates := List(pairs_to_merge, + pair -> QuotientDigraph(D, Concatenation([pair], + List(Difference(DigraphVertices(D), pair), v -> [v])))); + + # Filtering candidate list based on isomorphism. + distinct_canons := Set([]); + max_quotients := []; + + for c in candidates do + AddSet(distinct_canons, BlissCanonicalDigraph(c)); + + if Length(distinct_canons) > Length(max_quotients) then + Add(max_quotients, c); + fi; + od; + + return max_quotients; +end); + InstallMethod(CharacteristicPolynomial, "for a digraph", [IsDigraph], D -> CharacteristicPolynomial(AdjacencyMatrix(D))); diff --git a/tst/standard/attr.tst b/tst/standard/attr.tst index d1108e071..f1b44247b 100644 --- a/tst/standard/attr.tst +++ b/tst/standard/attr.tst @@ -2522,6 +2522,74 @@ gap> D := Digraph([[2], [1, 3], []]);; gap> DigraphCore(D); [ 1, 2 ] +# DigraphMaximalQuotients +gap> DigraphMaximalQuotients(EmptyDigraph(0)); +[ ] +gap> DigraphMaximalQuotients(EmptyDigraph(1)); +[ ] +gap> DigraphMaximalQuotients(EmptyDigraph(5)); +[ ] +gap> DigraphMaximalQuotients(ChainDigraph(2)) = [Digraph([[1]])]; +true +gap> DigraphMaximalQuotients(CompleteDigraph(2)) = [Digraph([[1]])]; +true +gap> mq := DigraphMaximalQuotients(CompleteDigraph(5));; +gap> Length(mq) = 1; +true +gap> DigraphNrVertices(mq[1]) = 4; +true +gap> DigraphNrEdges(mq[1]) = 13; +true +gap> DigraphNrLoops(mq[1]) = 1; +true +gap> mq := DigraphMaximalQuotients(ChainDigraph(3));; +gap> Length(mq) = 3; +true +gap> ForAll([Digraph([[1, 2], []]), Digraph([[2], [2]]), Digraph([[2], [1]])], +> d -> ForAny(mq, q -> IsIsomorphicDigraph(d, q))); +true +gap> mq := DigraphMaximalQuotients(Digraph([[2], [], []]));; +gap> Length(mq) = 2; +true +gap> ForAll([Digraph([[1], []]), Digraph([[2], []])], +> d -> ForAny(mq, q -> IsIsomorphicDigraph(d, q))); +true +gap> mq := DigraphMaximalQuotients(CycleDigraph(4));; +gap> Length(mq) = 2; +true +gap> ForAll([Digraph([[2], [2, 3], [1]]), Digraph([[2], [1, 3], [2]])], +> d -> ForAny(mq, q -> IsIsomorphicDigraph(d, q))); +true +gap> D := Digraph([[2, 2], [3], [3]]); + +gap> mq := DigraphMaximalQuotients(D);; +gap> Length(mq) = 3; +true +gap> ForAny(mq, q -> IsMultiDigraph(q)); +false +gap> ForAll([Digraph([[1, 2], [2]]), Digraph([[2], [2]]), Digraph([[2], [1, 2]])], +> d -> ForAny(mq, q -> IsIsomorphicDigraph(d, q))); +true +gap> mq := DigraphMaximalQuotients(BinaryTree(3));; +gap> Length(mq) = 7; +true +gap> ForAll(Combinations(mq, 2), p -> not IsIsomorphicDigraph(p[1], p[2])); +true +gap> ForAll(mq, q -> DigraphNrVertices(q) = 6); +true +gap> D := RandomDigraph(6, 0.6);; +gap> mq := DigraphMaximalQuotients(D);; +gap> ForAll(mq, q -> DigraphNrVertices(q) = 5); +true +gap> ForAll(Combinations(mq, 2), p -> not IsIsomorphicDigraph(p[1], p[2])); +true +gap> mq2 := DigraphMaximalQuotients(OnDigraphs(D, (1, 2, 3, 4, 5, 6)));; +gap> Length(mq) = Length(mq2); +true +gap> ForAll(mq, q -> ForAny(mq2, r -> IsIsomorphicDigraph(q, r))) +> and ForAll(mq2, q -> ForAny(mq, r -> IsIsomorphicDigraph(q, r))); +true + # MaximalAntiSymmetricSubdigraph gap> MaximalAntiSymmetricSubdigraph(Digraph([[2, 2], [1]]));