diff --git a/doc/cong.xml b/doc/cong.xml index 3705b4c21..a3b480654 100644 --- a/doc/cong.xml +++ b/doc/cong.xml @@ -204,47 +204,49 @@ gap> RightSemigroupCongruence(S, pair1, pair2); <#/GAPDoc> -<#GAPDoc Label="GeneratingPairsOfSemigroupCongruence"> - + <#GAPDoc Label="CongruencesOfSemigroup"> @@ -331,7 +333,7 @@ gap> congs[pos]; gap> S := Semigroup(Transformation([1, 3, 2]), > Transformation([3, 1, 3]));; gap> min := MinimalCongruencesOfSemigroup(S); -[ with 1 generating pairs> ] gap> minl := MinimalLeftCongruencesOfSemigroup(S); [ minl := MinimalLeftCongruencesOfSemigroup(S); gap> S := Semigroup(Transformation([1, 3, 2]), > Transformation([3, 1, 3]));; gap> congs := PrincipalCongruencesOfSemigroup(S); -[ with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs> ] ]]> @@ -552,11 +554,11 @@ gap> cong := SemigroupCongruence(S, [Transformation([1, 2, 1]), > Transformation([2, 1, 2])]);; gap> classes := NonTrivialEquivalenceClasses(cong);; gap> Set(classes); -[ , - , - , - , - ] +[ <2-sided congruence class of Transformation( [ 1, 2, 2 ] )>, + <2-sided congruence class of Transformation( [ 3, 1, 3 ] )>, + <2-sided congruence class of Transformation( [ 3, 1, 1 ] )>, + <2-sided congruence class of Transformation( [ 2, 1, 2 ] )>, + <2-sided congruence class of Transformation( [ 3, 3, 3 ] )> ] gap> cong := RightSemigroupCongruence(S, [Transformation([1, 2, 1]), > Transformation([2, 1, 2])]);; gap> classes := NonTrivialEquivalenceClasses(cong);; @@ -583,7 +585,7 @@ gap> cong := SemigroupCongruence(S, [Transformation([1, 2, 1]), > Transformation([2, 1, 2])]);; gap> class := EquivalenceClassOfElement(cong, > Transformation([3, 1, 1])); - +<2-sided congruence class of Transformation( [ 3, 1, 1 ] )> gap> IsCongruenceClass(class); true]]> diff --git a/doc/conginv.xml b/doc/conginv.xml index b29eb7645..e3e3ecf3b 100644 --- a/doc/conginv.xml +++ b/doc/conginv.xml @@ -25,7 +25,7 @@ gap> I := InverseSemigroup([ gap> cong := SemigroupCongruence(I, > [[PartialPerm([0, 1, 3]), PartialPerm([0, 1])], > [PartialPerm([]), PartialPerm([1, 2])]]); - with 2 generating pairs> gap> TraceOfSemigroupCongruence(cong); [ [ , , @@ -55,7 +55,7 @@ gap> I := InverseSemigroup([ gap> cong := SemigroupCongruence(I, > [[PartialPerm([0, 1, 3]), PartialPerm([0, 1])], > [PartialPerm([]), PartialPerm([1, 2])]]); - with 2 generating pairs> gap> KernelOfSemigroupCongruence(cong); @@ -84,8 +84,8 @@ gap> I := InverseSemigroup([ gap> cong := SemigroupCongruenceByGeneratingPairs(I, > [[PartialPerm([0, 1, 3]), PartialPerm([0, 1])], > [PartialPerm([]), PartialPerm([1, 2])]]); - with 2 generating pairs> +<2-sided semigroup congruence over with 2 generating pairs> gap> cong2 := AsInverseSemigroupCongruenceByKernelTrace(cong); with congruence pair (19,1)>]]> diff --git a/doc/conglatt.xml b/doc/conglatt.xml index 39fcbf01f..4afd2fcf0 100644 --- a/doc/conglatt.xml +++ b/doc/conglatt.xml @@ -36,21 +36,23 @@ S := SymmetricInverseMonoid(2);; gap> poset := LatticeOfCongruences(S); -> +> gap> IsCongruencePoset(poset); true gap> IsDigraph(poset); true gap> OutNeighbours(poset); -[ [ 1 .. 4 ], [ 2, 3, 4 ], [ 3 ], [ 3, 4 ] ] +[ [ 1, 2, 3, 4 ], [ 2 ], [ 2, 3 ], [ 2, 3, 4 ] ] gap> T := FullTransformationMonoid(3);; gap> congs := PrincipalCongruencesOfSemigroup(T);; -gap> poset := JoinSemilatticeOfCongruences(congs, -> JoinSemigroupCongruences); -> +gap> poset := JoinSemilatticeOfCongruences(PosetOfCongruences(congs), +> WrappedTwoSidedCongruence); +> gap> IsCongruencePoset(poset); true -gap> Size(poset); +gap> DigraphNrVertices(poset); 6 ]]> @@ -91,22 +93,23 @@ gap> Size(poset); S := OrderEndomorphisms(2);; gap> LatticeOfCongruences(S); -> +> gap> LatticeOfLeftCongruences(S); -> gap> LatticeOfRightCongruences(S); -> gap> OutNeighbours(LatticeOfRightCongruences(S)); -[ [ 1 .. 5 ], [ 2, 5 ], [ 3, 5 ], [ 4, 5 ], [ 5 ] ] +[ [ 1, 2, 3, 4, 5 ], [ 2 ], [ 2, 3 ], [ 2, 4 ], [ 2, 5 ] ] gap> S := FullTransformationMonoid(4);; gap> restriction := [Transformation([1, 1, 1, 1]), > Transformation([1, 1, 1, 2]), > Transformation([1, 1, 1, 3])];; -gap> latt := LatticeOfCongruences(S, restriction); -> +gap> latt := LatticeOfCongruences(S, Combinations(restriction, 2)); +> ]]> @@ -148,20 +151,21 @@ gap> latt := LatticeOfCongruences(S, restriction); Label = "for a semigroup"/>.

S := Semigroup([Transformation([1, 3, 1]), -> Transformation([2, 3, 3])]);; +gap> S := Semigroup(Transformation([1, 3, 1]), +> Transformation([2, 3, 3]));; gap> PosetOfPrincipalLeftCongruences(S); -> +> gap> PosetOfPrincipalCongruences(S); -> +> gap> restriction := [Transformation([3, 2, 3]), > Transformation([3, 1, 3]), > Transformation([2, 2, 2])];; -gap> poset := PosetOfPrincipalRightCongruences(S, restriction); -> +gap> poset := PosetOfPrincipalRightCongruences(S, +> Combinations(restriction, 2)); +> ]]> @@ -183,7 +187,7 @@ gap> poset := PosetOfPrincipalRightCongruences(S, restriction); S := OrderEndomorphisms(2);; gap> latt := LatticeOfRightCongruences(S); -> gap> CongruencesOfPoset(latt); [ CongruencesOfPoset(latt); gap> S := OrderEndomorphisms(2); gap> latt := LatticeOfRightCongruences(S); -> gap> UnderlyingSemigroupOfCongruencePoset(latt) = S; true @@ -246,8 +250,8 @@ gap> coll := [RightSemigroupCongruence(S, pair1), > RightSemigroupCongruence(S, pair2), > RightSemigroupCongruence(S, [])];; gap> poset := PosetOfCongruences(coll); -> +> gap> OutNeighbours(poset); [ [ 1 ], [ 2 ], [ 1, 2, 3 ] ] ]]> @@ -282,13 +286,15 @@ gap> OutNeighbours(poset); gap> S := SymmetricInverseMonoid(2);; gap> pair1 := [PartialPerm([1], [1]), PartialPerm([2], [1])];; gap> pair2 := [PartialPerm([1], [1]), PartialPerm([1, 2], [1, 2])];; -gap> pair3 := [PartialPerm([1, 2], [1, 2]), +gap> pair3 := [PartialPerm([1, 2], [1, 2]), > PartialPerm([1, 2], [2, 1])];; gap> coll := [RightSemigroupCongruence(S, pair1), > RightSemigroupCongruence(S, pair2), > RightSemigroupCongruence(S, pair3)];; -gap> JoinSemilatticeOfCongruences(coll, JoinRightSemigroupCongruences); -> +gap> JoinSemilatticeOfCongruences(PosetOfCongruences(coll), +> WrappedRightCongruence); +> ]]> @@ -326,16 +332,17 @@ gap> pair3 := [PartialPerm([1, 2], [1, 2]), gap> coll := [RightSemigroupCongruence(S, pair1), > RightSemigroupCongruence(S, pair2), > RightSemigroupCongruence(S, pair3)];; -gap> MinimalCongruences(coll); +gap> MinimalCongruences(PosetOfCongruences(coll)); [ with 1 generating pairs>, with 1 generating pairs> ] gap> poset := LatticeOfCongruences(S); -> +> gap> MinimalCongruences(poset); -[ wi\ -th 0 generating pairs> ] +[ <2-sided semigroup congruence over with 0 generating pairs> ] ]]> diff --git a/doc/congrms.xml b/doc/congrms.xml index 35f4652c0..b32368409 100644 --- a/doc/congrms.xml +++ b/doc/congrms.xml @@ -156,7 +156,7 @@ gap> rowBlocks := [[1], [2], [3]];; gap> cong := RZMSCongruenceByLinkedTriple(S, N, colBlocks, rowBlocks);; gap> class := RZMSCongruenceClassByLinkedTriple(cong, > RightCoset(N, (1, 5)), 2, 3); -]]> +<2-sided congruence class of (2,(3,4),3)>]]> <#/GAPDoc> diff --git a/doc/congsemigraph.xml b/doc/congsemigraph.xml index e375ddca3..8767d97ca 100644 --- a/doc/congsemigraph.xml +++ b/doc/congsemigraph.xml @@ -38,8 +38,8 @@ e_1 gap> e_3 := S.3; e_3 gap> cong := SemigroupCongruence(S, [[e_1, e_3]]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> IsCongruenceByWangPair(cong); false ]]> @@ -98,15 +98,15 @@ gap> S := GraphInverseSemigroup(D); gap> CongruenceByWangPair(S, [4], [2]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 2 generating pairs> +<2-sided semigroup congruence over with 2 generating pairs> gap> AsCongruenceByWangPair(cong); gap> CongruenceByWangPair(S, [3, 4], [1]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 3 generating pairs> +<2-sided semigroup congruence over with 3 generating pairs> gap> AsCongruenceByWangPair(cong); ]]> @@ -133,15 +133,15 @@ gap> S := GraphInverseSemigroup(D); gap> CongruenceByWangPair(S, [4], [2]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 2 generating pairs> +<2-sided semigroup congruence over with 2 generating pairs> gap> AsCongruenceByWangPair(cong); gap> CongruenceByWangPair(S, [3, 4], [1]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 3 generating pairs> +<2-sided semigroup congruence over with 3 generating pairs> gap> AsCongruenceByWangPair(cong); ]]> diff --git a/doc/z-chap13.xml b/doc/z-chap13.xml index 3b0d23a02..b0785bd24 100644 --- a/doc/z-chap13.xml +++ b/doc/z-chap13.xml @@ -58,7 +58,6 @@ <#Include Label = "SemigroupCongruence"> <#Include Label = "LeftSemigroupCongruence"> <#Include Label = "RightSemigroupCongruence"> - <#Include Label = "GeneratingPairsOfSemigroupCongruence"> diff --git a/gap/congruences/cong.gd b/gap/congruences/cong.gd index e80f31ec6..269dadae2 100644 --- a/gap/congruences/cong.gd +++ b/gap/congruences/cong.gd @@ -1,16 +1,19 @@ ############################################################################ ## ## congruences/cong.gd -## Copyright (C) 2015-2021 Michael C. Young +## Copyright (C) 2015-2022 Michael C. Young ## ## Licensing information can be found in the README file of this package. ## ############################################################################ ## ## This file contains declarations for functions, operations and attributes of -## semigroup congruences. Methods for most of these are implemented for -## specific types of congruence in the following files: +## semigroup congruences that do not depend on any particular representation. +## Methods for most of these are implemented for specific types of congruence +## in the following files: ## +## congpart.gi - for congruences that can compute +## EquivalenceRelationPartition ## conginv.gi - Inverse semigroups ## congpairs.gi - Congruences with generating pairs ## congrees.gi - Rees congruences @@ -18,42 +21,75 @@ ## congsimple.gi - (0-)simple semigroups ## conguniv.gi - Universal congruences ## -## Some general functions are also implemented in cong.gi -## +## Some general functions are also implemented in cong.gi, when these do not +## depend on the particular representation of the congruences, or that anything +## other than what is implemented in the GAP library is required. Most of the +## operations etc declared in this file are not implemented in cong.gi, but are +## declared here for congruences in general. -######################################################################## -# Categories +############################################################################### +# IsLeft/Right/SemigroupCongruence is a property, but +# IsLeft/Right/MagmaCongruence is a category so we use them in conjunction to +# mean i.e. a left congruence on a semigroup that was created in the category +# of left congruences. We introduce synonyms for these to simplify their use. # -# IsLeft/Right/SemigroupCongruence is a property, and so we introduce a -# category for each type of congruence. Many operations are agnostic to the -# "handedness" of the congruence, and so we also introduce the category -# IsAnyCongruenceCategory (meaning a left, right or 2-sided congruence). -# -######################################################################## +# Note that IsMagmaCongruence implies IsLeftMagmaCongruence and +# IsRightMagmaCongruence, and so IsLeftMagmaCongruence and +# IsLeftSemigroupCongruence returns true when applied to a 2-sided congruence. +# In other words, we cannot use IsLeftMagmaCongruence to determine whether or +# not a congruence was created as a left congruence or not (we can use +# IsLeftMagmaCongruence and not IsRightMagmaCongruence). +############################################################################### + +DeclareProperty("IsLeftRightOrTwoSidedCongruence", + IsLeftMagmaCongruence and IsLeftSemigroupCongruence); +DeclareProperty("IsLeftRightOrTwoSidedCongruence", + IsRightMagmaCongruence and IsRightSemigroupCongruence); +DeclareProperty("IsLeftRightOrTwoSidedCongruence", + IsMagmaCongruence and IsSemigroupCongruence); + +InstallTrueMethod(IsLeftRightOrTwoSidedCongruence, + IsLeftMagmaCongruence and IsLeftSemigroupCongruence); +InstallTrueMethod(IsLeftRightOrTwoSidedCongruence, + IsRightMagmaCongruence and IsRightSemigroupCongruence); +InstallTrueMethod(IsLeftRightOrTwoSidedCongruence, + IsMagmaCongruence and IsSemigroupCongruence); + +# The next attributes allows us to recover the category/string from a +# left/right/2-sided congruence object +DeclareAttribute("CongruenceHandednessString", + IsLeftRightOrTwoSidedCongruence); + +############################################################################ +# We introduce the property IsLeftRightOrTwoSidedCongruenceClass in a similar +# vein to IsLeftRightOrTwoSidedCongruence. Since we declare +# IsLeftCongruenceClass and IsRightCongruenceClass we could define them to +# satisfy IsLeftRightOrTwoSidedCongruenceClass, but then we have to declare +# IsLeftRightOrTwoSidedCongruenceClass as a being a property of +# IsEquivalenceClass, which means we then have to fiddle more with ranks of +# methods. +############################################################################ + +# IsCongruenceClass is declared in gap/lib/mgmcong.gd:140 +# but it does not include IsAttributeStoringRep +DeclareCategory("IsLeftCongruenceClass", + IsEquivalenceClass and IsAttributeStoringRep); +DeclareCategory("IsRightCongruenceClass", + IsEquivalenceClass and IsAttributeStoringRep); + +DeclareProperty("IsLeftRightOrTwoSidedCongruenceClass", + IsLeftCongruenceClass); +DeclareProperty("IsLeftRightOrTwoSidedCongruenceClass", + IsRightCongruenceClass); +DeclareProperty("IsLeftRightOrTwoSidedCongruenceClass", + IsCongruenceClass); -DeclareCategory("IsAnyCongruenceCategory", - IsEquivalenceRelation, - Maximum(RankFilter(IsSemigroupCongruence), - RankFilter(IsLeftMagmaCongruence), - RankFilter(IsRightMagmaCongruence)) - - RankFilter(IsEquivalenceRelation) + 1); -DeclareCategory("IsCongruenceCategory", - IsAnyCongruenceCategory and IsSemigroupCongruence and - IsMagmaCongruence); -DeclareCategory("IsLeftCongruenceCategory", - IsAnyCongruenceCategory and IsLeftSemigroupCongruence and - IsLeftMagmaCongruence); -DeclareCategory("IsRightCongruenceCategory", - IsAnyCongruenceCategory and IsRightSemigroupCongruence and - IsRightMagmaCongruence); - -DeclareAttribute("AnyCongruenceCategory", IsAnyCongruenceCategory); -DeclareAttribute("AnyCongruenceString", IsAnyCongruenceCategory); -DeclareCategory("IsAnyCongruenceClass", - IsEquivalenceClass and IsAttributeStoringRep, - 3); # to beat IsCongruenceClass - -Assert(1, RankFilter(IsAnyCongruenceClass) > RankFilter(IsCongruenceClass)); +InstallTrueMethod(IsLeftRightOrTwoSidedCongruenceClass, + IsLeftCongruenceClass); +InstallTrueMethod(IsLeftRightOrTwoSidedCongruenceClass, + IsRightCongruenceClass); +InstallTrueMethod(IsLeftRightOrTwoSidedCongruenceClass, + IsCongruenceClass); ######################################################################## # Congruences @@ -66,31 +102,36 @@ DeclareGlobalFunction("RightSemigroupCongruence"); # Properties of congruences DeclareProperty("IsRightSemigroupCongruence", IsLeftSemigroupCongruence); -DeclareProperty("IsLeftSemigroupCongruence", IsRightSemigroupCongruence); DeclareProperty("IsSemigroupCongruence", IsLeftSemigroupCongruence); -DeclareProperty("IsSemigroupCongruence", IsRightSemigroupCongruence); - -# Attributes of congruences -# EquivalenceRelationPartition is implemented in libsemigroups/cong.gi -DeclareAttribute("NonTrivialEquivalenceClasses", IsEquivalenceRelation); -DeclareAttribute("EquivalenceRelationLookup", IsEquivalenceRelation); -DeclareAttribute("EquivalenceRelationCanonicalLookup", IsEquivalenceRelation); -DeclareAttribute("NrEquivalenceClasses", IsEquivalenceRelation); + +DeclareAttribute("NrEquivalenceClasses", + IsEquivalenceRelation); +DeclareAttribute("NonTrivialEquivalenceClasses", + IsEquivalenceRelation); +DeclareAttribute("EquivalenceRelationLookup", + IsEquivalenceRelation); +DeclareAttribute("EquivalenceRelationCanonicalLookup", + IsEquivalenceRelation); DeclareAttribute("EquivalenceRelationCanonicalPartition", IsEquivalenceRelation); DeclareAttribute("EquivalenceRelationPartitionWithSingletons", IsEquivalenceRelation); # No-checks version of the "\in" operation -DeclareOperation("CongruenceTestMembershipNC", [IsEquivalenceRelation, - IsMultiplicativeElement, - IsMultiplicativeElement]); +DeclareOperation("CongruenceTestMembershipNC", + [IsLeftRightOrTwoSidedCongruence, + IsMultiplicativeElement, + IsMultiplicativeElement]); # Algebraic operators DeclareOperation("JoinLeftSemigroupCongruences", [IsLeftSemigroupCongruence, IsLeftSemigroupCongruence]); DeclareOperation("JoinRightSemigroupCongruences", [IsRightSemigroupCongruence, IsRightSemigroupCongruence]); +DeclareOperation("MeetLeftSemigroupCongruences", + [IsLeftSemigroupCongruence, IsLeftSemigroupCongruence]); +DeclareOperation("MeetRightSemigroupCongruences", + [IsRightSemigroupCongruence, IsRightSemigroupCongruence]); # Comparison operators DeclareOperation("IsSubrelation", @@ -102,14 +143,10 @@ DeclareOperation("IsSuperrelation", # Congruence classes ######################################################################## -# IsCongruenceClass is declared in gap/lib/mgmcong.gd:140 -DeclareCategory("IsLeftCongruenceClass", - IsEquivalenceClass and IsAttributeStoringRep); -DeclareCategory("IsRightCongruenceClass", - IsEquivalenceClass and IsAttributeStoringRep); - # Actions DeclareOperation("OnLeftCongruenceClasses", - [IsLeftCongruenceClass, IsMultiplicativeElement]); + [IsLeftRightOrTwoSidedCongruenceClass, + IsMultiplicativeElement]); DeclareOperation("OnRightCongruenceClasses", - [IsRightCongruenceClass, IsMultiplicativeElement]); + [IsLeftRightOrTwoSidedCongruenceClass, + IsMultiplicativeElement]); diff --git a/gap/congruences/cong.gi b/gap/congruences/cong.gi index 34d829b4a..6ff7c005b 100644 --- a/gap/congruences/cong.gi +++ b/gap/congruences/cong.gi @@ -21,31 +21,21 @@ ## cong.gd contains declarations for many of these. ## -# Where possible, we only provide methods in this file for -# IsAnyCongruenceCategory and IsAnyCongruenceClass, and not arbitrary -# congruences. +# This file contains implementations of methods for congruences that don't +# assume any particular representation. ######################################################################## # 0. Categories ######################################################################## -InstallMethod(AnyCongruenceCategory, "for a right congruence category", -[IsRightCongruenceCategory], C -> IsRightCongruenceCategory); +InstallMethod(CongruenceHandednessString, "for a right congruence", +[IsRightMagmaCongruence and IsRightSemigroupCongruence], C -> "right"); -InstallMethod(AnyCongruenceCategory, "for a left congruence category", -[IsLeftCongruenceCategory], C -> IsLeftCongruenceCategory); +InstallMethod(CongruenceHandednessString, "for a left congruence", +[IsLeftMagmaCongruence and IsLeftSemigroupCongruence], C -> "left"); -InstallMethod(AnyCongruenceCategory, "for a 2-sided congruence category", -[IsCongruenceCategory], C -> IsCongruenceCategory); - -InstallMethod(AnyCongruenceString, "for a right congruence category", -[IsRightCongruenceCategory], C -> "right"); - -InstallMethod(AnyCongruenceString, "for a left congruence category", -[IsLeftCongruenceCategory], C -> "left"); - -InstallMethod(AnyCongruenceString, "for a 2-sided congruence category", -[IsCongruenceCategory], C -> "2-sided"); +InstallMethod(CongruenceHandednessString, "for a 2-sided congruence", +[IsMagmaCongruence and IsSemigroupCongruence], C -> "2-sided"); ######################################################################## # Flexible functions for creating congruences @@ -188,204 +178,47 @@ function(arg) return _LeftOrRightCong(RightSemigroupCongruenceByGeneratingPairs, arg); end); -######################################################################## -# Congruence attributes -######################################################################## - -InstallMethod(NonTrivialEquivalenceClasses, "for IsAnyCongruenceCategory", -[IsAnyCongruenceCategory], -function(C) - local part, nr_classes, classes, i; - part := EquivalenceRelationPartition(C); - nr_classes := Length(part); - classes := EmptyPlist(nr_classes); - for i in [1 .. nr_classes] do - classes[i] := EquivalenceClassOfElementNC(C, part[i][1]); - SetAsList(classes[i], part[i]); - od; - return classes; -end); - -InstallMethod(EquivalenceRelationLookup, "for IsAnyCongruenceCategory", -[IsAnyCongruenceCategory], -function(C) - local S, lookup, class, nr, elm; - S := Range(C); - if not (IsSemigroup(S) and IsFinite(S)) then - ErrorNoReturn("the range of the argument (an equivalence relation) ", - "is not a finite semigroup"); - fi; - Assert(1, CanUseFroidurePin(S)); - - lookup := [1 .. Size(S)]; - for class in NonTrivialEquivalenceClasses(C) do - nr := PositionCanonical(S, Representative(class)); - for elm in class do - lookup[PositionCanonical(S, elm)] := nr; - od; - od; - return lookup; -end); - -InstallMethod(EquivalenceRelationCanonicalLookup, -"for IsAnyCongruenceCategory", [IsAnyCongruenceCategory], -function(C) - local S, lookup, max, dictionary, next, out, new_nr, i; - S := Range(C); - if not (IsSemigroup(S) and IsFinite(S)) then - ErrorNoReturn("the range of the argument (an equivalence relation) ", - "is not a finite semigroup"); - fi; - Assert(1, CanUseFroidurePin(S)); - - lookup := EquivalenceRelationLookup(C); - max := Maximum(lookup); - # We do not know whether the maximum is NrEquivalenceClasses(C) - dictionary := ListWithIdenticalEntries(max, 0); - next := 1; - out := EmptyPlist(max); - for i in [1 .. Length(lookup)] do - new_nr := dictionary[lookup[i]]; - if new_nr = 0 then - dictionary[lookup[i]] := next; - new_nr := next; - next := next + 1; - fi; - out[i] := new_nr; - od; - return out; -end); - -InstallMethod(EquivalenceRelationCanonicalPartition, -"for IsAnyCongruenceCategory", [IsAnyCongruenceCategory], -function(C) - return Filtered(EquivalenceRelationPartitionWithSingletons(C), - x -> Size(x) <> 1); -end); - -InstallMethod(EquivalenceRelationPartitionWithSingletons, -"for IsAnyCongruenceCategory", [IsAnyCongruenceCategory], -function(C) - local en, partition, lookup, i; - if not IsFinite(Range(C)) then - Error("the argument (a congruence) must have finite range"); - fi; - # CanUseFroidurePin is required because EnumeratorCanonical is not a - # thing for other types of congruences. There isn't a way of constructing an - # object in IsAnyCongruenceCategory where the range does not have - # CanUseFroidurePin, so we just assert this for safety. - Assert(1, CanUseFroidurePin(Range(C))); - en := EnumeratorCanonical(Range(C)); - partition := List([1 .. NrEquivalenceClasses(C)], x -> []); - lookup := EquivalenceRelationCanonicalLookup(C); - for i in [1 .. Length(lookup)] do - Add(partition[lookup[i]], en[i]); - od; - - return partition; -end); - ######################################################################## # Congruence operators ######################################################################## -InstallMethod(\in, -"for homog. list and IsAnyCongruenceCategory", -[IsHomogeneousList, IsAnyCongruenceCategory], -function(pair, C) - local S, string; - - if Size(pair) <> 2 then - ErrorNoReturn("the 1st argument (a list) does not have length 2"); - fi; - - S := Range(C); - string := AnyCongruenceString(C); - if not (pair[1] in S and pair[2] in S) then - ErrorNoReturn("the items in the 1st argument (a list) do not all belong to ", - "the range of the 2nd argument (a ", string, " semigroup ", - "congruence)"); - elif CanEasilyCompareElements(pair[1]) and pair[1] = pair[2] then - return true; - fi; - return CongruenceTestMembershipNC(C, pair[1], pair[2]); -end); - -InstallMethod(\=, "for IsAnyCongruenceCategory", -[IsAnyCongruenceCategory, IsAnyCongruenceCategory], -function(lhop, rhop) - local S; - S := Range(lhop); - if S <> Range(rhop) then - return false; - elif HasIsFinite(S) and IsFinite(S) then - return EquivalenceRelationCanonicalLookup(lhop) = - EquivalenceRelationCanonicalLookup(rhop); - fi; - return EquivalenceRelationCanonicalPartition(lhop) = - EquivalenceRelationCanonicalPartition(rhop); -end); - InstallMethod(IsSuperrelation, "for semigroup congruences", -[IsAnyCongruenceCategory, IsAnyCongruenceCategory], +[IsLeftRightOrTwoSidedCongruence, IsLeftRightOrTwoSidedCongruence], {lhop, rhop} -> IsSubrelation(rhop, lhop)); -# TODO what about MeetLeftSemigroupCongruences, MeetRightSemigroupCongruences? -InstallMethod(MeetSemigroupCongruences, "for semigroup congruences", -[IsSemigroupCongruence, IsSemigroupCongruence], -function(lhop, rhop) - if Range(lhop) <> Range(rhop) then - Error("cannot form the meet of congruences over different semigroups"); - elif lhop = rhop then - return lhop; - elif IsSubrelation(lhop, rhop) then - return rhop; - elif IsSubrelation(rhop, lhop) then - return lhop; - fi; - # lhop_lookup := EquivalenceRelationCanonicalLookup(lhop); - # rhop_lookup := EquivalenceRelationCanonicalLookup(rhop); - # N := Length(lhop); - - # hash := HashMap(); - # next := 1; - # for i in [1 .. N] do - # The problem is how to represent the meet of the congruences - # od; - # TODO actually implement a method - TryNextMethod(); -end); - ######################################################################## # Congruence classes ######################################################################## InstallMethod(EquivalenceClassOfElement, -"for IsAnyCongruenceCategory and multiplicative element", -[IsAnyCongruenceCategory, IsMultiplicativeElement], +"for a left, right, or 2-sided semigroup congruence and mult. elt.", +[IsLeftRightOrTwoSidedCongruence, IsMultiplicativeElement], function(C, x) if not x in Range(C) then - Error("the 2nd argument (a mult. elt.) does not belong to the range ", - "of the 1st argument (a congruence)"); + ErrorNoReturn("the 2nd argument (a mult. elt.) does not belong ", + "to the range of the 1st argument (a ", + CongruenceHandednessString(C), + " congruence)"); fi; return EquivalenceClassOfElementNC(C, x); end); InstallMethod(EquivalenceClassOfElementNC, -"for IsAnyCongruenceCategory and multiplicative element", -[IsAnyCongruenceCategory, IsMultiplicativeElement], +"for a left, right, or 2-sided congruence and mult. elt.", +[IsLeftRightOrTwoSidedCongruence, IsMultiplicativeElement], function(C, x) local filt, class; - - filt := IsAnyCongruenceClass; - - if IsCongruenceCategory(C) then - filt := filt and IsCongruenceClass; - elif IsLeftCongruenceCategory(C) then - filt := filt and IsLeftCongruenceClass; + if IsMagmaCongruence(C) then + # IsCongruenceClass is declared in the GAP library and it does not belong + # to IsAttributeStoringRep + filt := IsCongruenceClass and IsAttributeStoringRep; + elif IsLeftMagmaCongruence(C) then + # IsLeftCongruenceClass is declared in the Semigroups pkg and does belong + # to IsAttributeStoringRep + filt := IsLeftCongruenceClass; else - Assert(1, IsRightCongruenceCategory(C)); - filt := filt and IsRightCongruenceClass; + Assert(1, IsRightMagmaCongruence(C)); + filt := IsRightCongruenceClass; fi; class := Objectify(NewType(FamilyObj(Range(C)), filt), rec()); @@ -403,13 +236,22 @@ end); # Congruence class attributes ######################################################################## -InstallMethod(AsList, "for IsAnyCongruenceClass", [IsAnyCongruenceClass], +# Maybe these should only be for CanComputeEquivalenceRelationPartition? + +InstallMethod(AsList, +"for a class of a left, right, or 2-sided semigroup congruence", +[IsLeftRightOrTwoSidedCongruenceClass], C -> ImagesElm(EquivalenceClassRelation(C), Representative(C))); -InstallMethod(Enumerator, "for IsAnyCongruenceClass", [IsAnyCongruenceClass], +InstallMethod(Enumerator, +"for a class of a left, right, or 2-sided semigroup congruence", +[IsLeftRightOrTwoSidedCongruenceClass], +6, # To beat the library method for magma congruence classes AsList); -InstallMethod(Size, "for IsAnyCongruenceClass", [IsAnyCongruenceClass], +InstallMethod(Size, +"for a class of a left, right, or 2-sided semigroup congruence", +[IsLeftRightOrTwoSidedCongruenceClass], C -> Size(AsList(C))); ######################################################################## @@ -430,9 +272,9 @@ function(lhop, rhop) end); InstallMethod(\=, -"for IsAnyCongruenceClass and IsAnyCongruenceClass", +"for classes of a left, right, or 2-sided semigroup congruence", IsIdenticalObj, -[IsAnyCongruenceClass, IsAnyCongruenceClass], +[IsLeftRightOrTwoSidedCongruenceClass, IsLeftRightOrTwoSidedCongruenceClass], function(lhop, rhop) return EquivalenceClassRelation(lhop) = EquivalenceClassRelation(rhop) and [Representative(lhop), Representative(rhop)] @@ -440,24 +282,22 @@ function(lhop, rhop) end); InstallMethod(\in, -"for a mult. elt. and IsAnyCongruenceClass", -[IsMultiplicativeElement, IsAnyCongruenceClass], +"for a mult. elt. and a class of a left, right or 2-sided congruence", +[IsMultiplicativeElement, IsLeftRightOrTwoSidedCongruenceClass], +3, # to beat the library method function(x, class) local C; C := EquivalenceClassRelation(class); return x in Range(C) and [x, Representative(class)] in C; end); -InstallMethod(ViewObj, "for IsAnyCongruenceClass", [IsAnyCongruenceClass], +InstallMethod(ViewObj, +"for a left, right, or 2-sided congruence class", +[IsLeftRightOrTwoSidedCongruenceClass], function(C) local string; - if IsCongruenceCategory(EquivalenceClassRelation(C)) then - string := ""; - else - string := Concatenation(AnyCongruenceString(EquivalenceClassRelation(C)), - " "); - fi; - Print("<", string, "congruence class of "); + string := CongruenceHandednessString(EquivalenceClassRelation(C)); + Print("<", string, " congruence class of "); ViewObj(Representative(C)); Print(">"); end); @@ -468,18 +308,28 @@ end); InstallMethod(OnLeftCongruenceClasses, "for a left congruence class and a multiplicative element", -[IsLeftCongruenceClass, IsMultiplicativeElement], +[IsLeftRightOrTwoSidedCongruenceClass, IsMultiplicativeElement], function(class, x) local C; + # This is necessary because IsCongruenceClass is not a sub-category of + # IsLeftCongruenceClass or IsRightCongruenceClass + if IsRightCongruenceClass(class) then + TryNextMethod(); + fi; C := EquivalenceClassRelation(class); return EquivalenceClassOfElementNC(C, x * Representative(class)); end); InstallMethod(OnRightCongruenceClasses, "for a right congruence class and a multiplicative element", -[IsRightCongruenceClass, IsMultiplicativeElement], +[IsLeftRightOrTwoSidedCongruenceClass, IsMultiplicativeElement], function(class, x) local C; + # This is necessary because IsCongruenceClass is not a sub-category of + # IsLeftCongruenceClass or IsRightCongruenceClass + if IsLeftCongruenceClass(class) then + TryNextMethod(); + fi; C := EquivalenceClassRelation(class); return EquivalenceClassOfElementNC(C, Representative(class) * x); end); diff --git a/gap/congruences/conginv.gd b/gap/congruences/conginv.gd index a2b76ee5a..91aba23a4 100644 --- a/gap/congruences/conginv.gd +++ b/gap/congruences/conginv.gd @@ -13,7 +13,11 @@ # Inverse Congruences By Kernel and Trace DeclareCategory("IsInverseSemigroupCongruenceByKernelTrace", - IsCongruenceCategory and IsAttributeStoringRep and IsFinite); + IsSemigroupCongruence + and IsMagmaCongruence + and CanComputeEquivalenceRelationPartition + and IsAttributeStoringRep + and IsFinite); DeclareGlobalFunction("InverseSemigroupCongruenceByKernelTrace"); DeclareGlobalFunction("InverseSemigroupCongruenceByKernelTraceNC"); @@ -26,8 +30,8 @@ DeclareAttribute("InverseSemigroupCongruenceClassByKernelTraceType", # Congruence Classes DeclareCategory("IsInverseSemigroupCongruenceClassByKernelTrace", - IsAnyCongruenceClass and IsCongruenceClass and - IsAttributeStoringRep and IsMultiplicativeElement); + IsCongruenceClass and IsAttributeStoringRep and + IsMultiplicativeElement); # Special congruences DeclareAttribute("MinimumGroupCongruence", diff --git a/gap/congruences/conginv.gi b/gap/congruences/conginv.gi index 7d55928da..c2a0aa2e1 100644 --- a/gap/congruences/conginv.gi +++ b/gap/congruences/conginv.gi @@ -160,29 +160,27 @@ function(C, elm) return images; end); -InstallMethod(EquivalenceClasses, +InstallMethod(EquivalenceRelationPartitionWithSingletons, "for inverse semigroup congruence by kernel and trace", [IsInverseSemigroupCongruenceByKernelTrace], function(C) - local S, reps, elmlists, kernel, traceBlock, blockreps, blockelmlists, id, - elm, pos, classes, i; + local S, elmlists, kernel, blockelmlists, pos, traceBlock, id, elm; + S := Range(C); - reps := []; elmlists := []; kernel := Elements(C!.kernel); # Consider each trace-class in turn for traceBlock in C!.traceBlocks do # Consider all the congruence classes corresponding to this trace-class - blockreps := []; # List of class reps blockelmlists := []; # List of lists of elms in class for id in traceBlock do for elm in LClass(S, id) do # Find the congruence class that this element lies in - pos := PositionProperty(blockreps, rep -> elm * rep ^ -1 in kernel); + pos := PositionProperty(blockelmlists, + class -> elm * class[1] ^ -1 in kernel); if pos = fail then # New class - Add(blockreps, elm); Add(blockelmlists, [elm]); else # Add to the old class @@ -190,49 +188,9 @@ function(C) fi; od; od; - Append(reps, blockreps); Append(elmlists, blockelmlists); od; - - # Create the class objects - classes := []; - for i in [1 .. Length(reps)] do - classes[i] := EquivalenceClassOfElementNC(C, reps[i]); - SetAsList(classes[i], elmlists[i]); - od; - return classes; -end); - -InstallMethod(NrEquivalenceClasses, -"for inverse semigroup congruence by kernel and trace", -[IsInverseSemigroupCongruenceByKernelTrace], -function(C) - return Length(EquivalenceClasses(C)); -end); - -# Although the next method doesn't use the representation, it also doesn't -# require additional computation of the uncongruence, since it calls -# EquivalenceClasses. -InstallMethod(EquivalenceRelationCanonicalLookup, -"for inverse semigroup congruence by kernel and trace", -[IsInverseSemigroupCongruenceByKernelTrace], -function(C) - local S, n, classes, elms, table, next, i, x; - S := Range(C); - n := Size(S); - classes := EquivalenceClasses(C); - elms := AsListCanonical(S); - table := EmptyPlist(n); - next := 1; - for i in [1 .. n] do - if not IsBound(table[i]) then - for x in First(classes, class -> elms[i] in class) do - table[Position(S, x)] := next; - od; - next := next + 1; - fi; - od; - return table; + return elmlists; end); InstallMethod(CongruenceTestMembershipNC, diff --git a/gap/congruences/conglatt.gd b/gap/congruences/conglatt.gd index 411cae7ef..68acc1c65 100644 --- a/gap/congruences/conglatt.gd +++ b/gap/congruences/conglatt.gd @@ -18,40 +18,49 @@ DeclareCategory("IsCongruencePoset", IsDigraph); -DeclareAttribute("CongruencesOfPoset", IsCongruencePoset); DeclareAttribute("UnderlyingSemigroupOfCongruencePoset", IsCongruencePoset); +DeclareAttribute("PosetOfPrincipalCongruences", IsCongruencePoset); +DeclareOperation("JoinSemilatticeOfCongruences", + [IsCongruencePoset, IsFunction]); +DeclareAttribute("MinimalCongruences", IsCongruencePoset); -DeclareAttribute("LatticeOfCongruences", IsSemigroup); -DeclareAttribute("LatticeOfLeftCongruences", IsSemigroup); -DeclareAttribute("LatticeOfRightCongruences", IsSemigroup); -DeclareOperation("LatticeOfCongruences", - [IsSemigroup, IsMultiplicativeElementCollection]); -DeclareOperation("LatticeOfLeftCongruences", - [IsSemigroup, IsMultiplicativeElementCollection]); -DeclareOperation("LatticeOfRightCongruences", - [IsSemigroup, IsMultiplicativeElementCollection]); +DeclareAttribute("CongruencesOfPoset", IsCongruencePoset); + +# Constructs the poset object consisting of the congruences given in the +# argument. +DeclareOperation("PosetOfCongruences", [IsListOrCollection]); + +DeclareAttribute("GeneratingPairsOfPrincipalCongruences", IsSemigroup); +DeclareAttribute("GeneratingPairsOfPrincipalLeftCongruences", IsSemigroup); +DeclareAttribute("GeneratingPairsOfPrincipalRightCongruences", IsSemigroup); DeclareAttribute("PosetOfPrincipalCongruences", IsSemigroup); DeclareAttribute("PosetOfPrincipalLeftCongruences", IsSemigroup); DeclareAttribute("PosetOfPrincipalRightCongruences", IsSemigroup); + DeclareOperation("PosetOfPrincipalCongruences", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); DeclareOperation("PosetOfPrincipalLeftCongruences", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); DeclareOperation("PosetOfPrincipalRightCongruences", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); -DeclareOperation("PosetOfCongruences", [IsListOrCollection]); - -DeclareOperation("JoinSemilatticeOfCongruences", - [IsListOrCollection, IsFunction]); -DeclareOperation("JoinSemilatticeOfCongruences", - [IsCongruencePoset, IsFunction]); - -DeclareAttribute("MinimalCongruences", IsListOrCollection); -DeclareAttribute("MinimalCongruences", IsCongruencePoset); +DeclareAttribute("LatticeOfCongruences", IsSemigroup); +DeclareAttribute("LatticeOfLeftCongruences", IsSemigroup); +DeclareAttribute("LatticeOfRightCongruences", IsSemigroup); -DeclareAttribute("Size", IsCongruencePoset); +DeclareOperation("LatticeOfCongruences", + [IsSemigroup, IsListOrCollection]); +DeclareOperation("LatticeOfCongruencesNC", + [IsSemigroup, IsListOrCollection]); +DeclareOperation("LatticeOfLeftCongruences", + [IsSemigroup, IsListOrCollection]); +DeclareOperation("LatticeOfLeftCongruencesNC", + [IsSemigroup, IsListOrCollection]); +DeclareOperation("LatticeOfRightCongruences", + [IsSemigroup, IsListOrCollection]); +DeclareOperation("LatticeOfRightCongruencesNC", + [IsSemigroup, IsListOrCollection]); DeclareAttribute("CongruencesOfSemigroup", IsSemigroup); DeclareAttribute("LeftCongruencesOfSemigroup", IsSemigroup); @@ -62,19 +71,22 @@ DeclareAttribute("MinimalLeftCongruencesOfSemigroup", IsSemigroup); DeclareAttribute("MinimalRightCongruencesOfSemigroup", IsSemigroup); DeclareOperation("MinimalCongruencesOfSemigroup", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); +DeclareOperation("MinimalCongruencesOfSemigroup", + [IsSemigroup, IsIterator]); + DeclareOperation("MinimalLeftCongruencesOfSemigroup", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); DeclareOperation("MinimalRightCongruencesOfSemigroup", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); DeclareAttribute("PrincipalCongruencesOfSemigroup", IsSemigroup); DeclareAttribute("PrincipalLeftCongruencesOfSemigroup", IsSemigroup); DeclareAttribute("PrincipalRightCongruencesOfSemigroup", IsSemigroup); DeclareOperation("PrincipalCongruencesOfSemigroup", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); DeclareOperation("PrincipalLeftCongruencesOfSemigroup", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); DeclareOperation("PrincipalRightCongruencesOfSemigroup", - [IsSemigroup, IsMultiplicativeElementCollection]); + [IsSemigroup, IsListOrCollection]); diff --git a/gap/congruences/conglatt.gi b/gap/congruences/conglatt.gi index e37d0366d..e93c33fee 100644 --- a/gap/congruences/conglatt.gi +++ b/gap/congruences/conglatt.gi @@ -22,39 +22,21 @@ ## user as the list of out-neighbours of that digraph. ## -InstallMethod(ViewObj, "for a congruence poset", [IsCongruencePoset], -function(poset) - if DigraphNrVertices(poset) = 0 then - Print(""); - else - Print(""); - fi; -end); - -InstallMethod(PrintObj, "for a congruence poset", [IsCongruencePoset], -function(poset) - Print("PosetOfCongruences( ", CongruencesOfPoset(poset), " )"); -end); - -InstallMethod(Size, "for a congruence poset", [IsCongruencePoset], -DigraphNrVertices); +############################################################################# +## The main three functions +############################################################################# SEMIGROUPS.PrincipalXCongruencePosetNC := - function(S, - restriction, - SemigroupXCongruence, - GeneratingPairsOfXSemigroupCongruence) - local pairs, total, congs, nrcongs, children, parents, last_collected, - nr, pair, badcong, newchildren, newparents, newcong, i, pair1, c, p, - poset; - - # Only try pairs from - pairs := IteratorOfCombinations(AsList(restriction), 2); - total := Binomial(Size(restriction), 2); + function(S, pairs, SemigroupXCongruence) + local total, congs, nrcongs, children, parents, last_collected, nr, + badcong, newchildren, newparents, newcong, pair1, poset, pair, i, c, p; # Get all the unique principal congs + if IsList(pairs) then + total := Length(pairs); + else + total := Binomial(Size(S), 2); + fi; Info(InfoSemigroups, 1, "Finding principal congruences . . ."); congs := []; # List of all congs found so far nrcongs := 0; # Number of congs found so far @@ -63,28 +45,38 @@ SEMIGROUPS.PrincipalXCongruencePosetNC := last_collected := 0; nr := 0; for pair in pairs do + nr := nr + 1; + if pair[1] = pair[2] then + continue; + fi; badcong := false; newchildren := []; # Children of newcong newparents := []; # Parents of newcong newcong := SemigroupXCongruence(S, pair); for i in [1 .. Length(congs)] do - pair1 := GeneratingPairsOfXSemigroupCongruence(congs[i])[1]; - if CongruenceTestMembershipNC(congs[i], pair[1], pair[2]) then - if CongruenceTestMembershipNC(newcong, pair1[1], pair1[2]) then - # This is not a new cong - drop it! - badcong := true; - break; - else - Add(newparents, i); + pairs := GeneratingPairsOfLeftRightOrTwoSidedCongruence(congs[i]); + if not IsEmpty(pairs) then + pair1 := pairs[1]; + if CongruenceTestMembershipNC(congs[i], pair[1], pair[2]) then + if CongruenceTestMembershipNC(newcong, pair1[1], pair1[2]) then + # This is not a new cong - drop it! + badcong := true; + break; + else + Add(newparents, i); + fi; + elif CongruenceTestMembershipNC(newcong, pair1[1], pair1[2]) then + Add(newchildren, i); fi; - elif CongruenceTestMembershipNC(newcong, pair1[1], pair1[2]) then - Add(newchildren, i); fi; od; - nr := nr + 1; if nr > last_collected + 1999 then - Info(InfoSemigroups, 1, "Pair ", nr, " of ", total, ": ", nrcongs, - " congruences found so far"); + Info(InfoSemigroups, + 1, + StringFormatted("Pair {} of {}: {} congruences so far", + nr, + total, + nrcongs)); last_collected := nr; GASMAN("collect"); fi; @@ -103,6 +95,10 @@ SEMIGROUPS.PrincipalXCongruencePosetNC := parents[nrcongs] := newparents; fi; od; + Info(InfoSemigroups, + 1, + StringFormatted("Found {} principal congruences in total!", + Length(parents))); # We are done: make the object and return poset := Digraph(parents); @@ -114,87 +110,30 @@ SEMIGROUPS.PrincipalXCongruencePosetNC := return poset; end; -InstallMethod(MinimalCongruences, -"for a congruence poset", -[IsCongruencePoset], -poset -> CongruencesOfPoset(poset){Positions(InDegrees(poset), 1)}); - -InstallMethod(MinimalCongruences, -"for a list or collection", +InstallMethod(PosetOfCongruences, "for a list or collection", [IsListOrCollection], -coll -> MinimalCongruences(PosetOfCongruences(coll))); - -InstallMethod(JoinSemilatticeOfCongruences, -"for a congruence poset and a function", -[IsCongruencePoset, IsFunction], -function(poset, join_func) - local children, parents, congs, princ_congs, nrcongs, S, length, - found, ignore, start, i, j, newcong, badcong, newchildren, newparents, - k, c, p; - - # Trivial case - if Size(poset) = 0 then - return poset; - fi; - - # Extract the info - children := InNeighboursMutableCopy(poset); - parents := OutNeighboursMutableCopy(poset); - congs := ShallowCopy(CongruencesOfPoset(poset)); - princ_congs := ShallowCopy(congs); +function(coll) + local congs, nrcongs, children, parents, i, ignore, j, poset; + congs := AsList(coll); nrcongs := Length(congs); - S := UnderlyingSemigroupOfCongruencePoset(poset); - # Take all the joins - Info(InfoSemigroups, 1, "Finding joins of congruences . . ."); - length := 0; - found := true; - # 'ignore' is a list of congs that we don't try joining - ignore := BlistList([1 .. nrcongs], []); - while found do - # There are new congs to try joining - start := length + 1; # New congs start here - found := false; # Have we found any more congs on this sweep? - length := Length(congs); # Remember starting position for next sweep - - for i in [start .. Length(congs)] do # for each new cong - for j in [1 .. Length(princ_congs)] do # for each 1-generated cong - newcong := join_func(congs[i], princ_congs[j]); - badcong := false; # Is newcong the same as another cong? - newchildren := []; # Children of newcong - newparents := []; # Parents of newcong - for k in [1 .. Length(congs)] do - if IsSubrelation(congs[k], newcong) then - if IsSubrelation(newcong, congs[k]) then - # This is the same as an old cong - discard it! - badcong := true; - break; - else - Add(newparents, k); - fi; - elif IsSubrelation(newcong, congs[k]) then - Add(newchildren, k); - fi; - od; - if not badcong then - nrcongs := nrcongs + 1; - congs[nrcongs] := newcong; - for c in newchildren do - Add(parents[c], nrcongs); - od; - for p in newparents do - Add(children[p], nrcongs); - od; - Add(newchildren, nrcongs); # Include loops (reflexive) - Add(newparents, nrcongs); - children[nrcongs] := newchildren; - parents[nrcongs] := newparents; - ignore[nrcongs] := false; - found := true; - fi; - od; - Info(InfoSemigroups, 2, "Processed cong ", i, " of ", Length(congs), - " (", Length(congs) - i, " remaining)"); + # Setup children and parents lists + children := []; + parents := []; + for i in [1 .. nrcongs] do + children[i] := Set([i]); + parents[i] := Set([i]); + od; + + # Find children of each cong in turn + for i in [1 .. nrcongs] do + # Ignore known parents + ignore := BlistList([1 .. nrcongs], [parents[i]]); + for j in [1 .. nrcongs] do + if not ignore[j] and IsSubrelation(congs[i], congs[j]) then + AddSet(children[i], j); + AddSet(parents[j], i); + fi; od; od; @@ -203,21 +142,14 @@ function(poset, join_func) SetInNeighbours(poset, children); SetCongruencesOfPoset(poset, congs); SetDigraphVertexLabels(poset, congs); - SetUnderlyingSemigroupOfCongruencePoset(poset, S); + if nrcongs > 0 then + SetUnderlyingSemigroupOfCongruencePoset(poset, Range(congs[1])); + fi; SetFilterObj(poset, IsCongruencePoset); return poset; end); -InstallMethod(JoinSemilatticeOfCongruences, -"for a list or collection, and a function", -[IsListOrCollection, IsFunction], -function(coll, join_func) - local poset; - poset := PosetOfCongruences(coll); - return JoinSemilatticeOfCongruences(poset, join_func); -end); - -SEMIGROUPS.AddTrivialCongruence := function(poset, cong_func) +SEMIGROUPS.AddTrivialCongruence := function(poset, SemigroupXCongruence) local S, children, parents, congs, nrcongs, i; # Extract the info S := UnderlyingSemigroupOfCongruencePoset(poset); @@ -227,7 +159,7 @@ SEMIGROUPS.AddTrivialCongruence := function(poset, cong_func) # Add the trivial congruence nrcongs := Length(congs) + 1; - Add(congs, cong_func(S, []), 1); + Add(congs, SemigroupXCongruence(S, []), 1); children := Concatenation([[]], children + 1); parents := Concatenation([[1 .. nrcongs]], parents + 1); for i in [1 .. nrcongs] do @@ -244,235 +176,537 @@ SEMIGROUPS.AddTrivialCongruence := function(poset, cong_func) return poset; end; -InstallMethod(PosetOfCongruences, -"for a list or collection", -[IsListOrCollection], -function(coll) - local congs, nrcongs, children, parents, i, ignore, j, poset; - congs := AsList(coll); - nrcongs := Length(congs); +# We declare the following for the sole purpose of being able to use the +# Froidure-Pin (GAP implementation) algorithm for computing the join +# semilattice of congruences. We could not just implement multiplication of +# left, right, 2-sided congruences (which would have been less code) for family +# reasons (i.e. if we declare DeclareCategoryCollections for +# IsLeftMagmaCongruence, it turns out that [LeftSemigroupCongruence(*)] does +# not belong to IsLeftMagmaCongruenceCollection, because the family of these +# objects is GeneralMappingsFamily, and it is not the case that +# IsLeftMagmaCongruence is true for every elements of the +# GeneralMappingsFamily. This is a requirement according to the GAP reference +# manual entry for CategoryCollections. +DeclareCategory("IsWrappedLeftRightOrTwoSidedCongruence", + IsAssociativeElement); +DeclareCategory("IsWrappedRightCongruence", + IsWrappedLeftRightOrTwoSidedCongruence); +DeclareCategory("IsWrappedLeftCongruence", + IsWrappedLeftRightOrTwoSidedCongruence); +DeclareCategory("IsWrappedTwoSidedCongruence", + IsWrappedLeftRightOrTwoSidedCongruence); + +DeclareCategoryCollections("IsWrappedLeftRightOrTwoSidedCongruence"); + +InstallTrueMethod(CanUseGapFroidurePin, + IsWrappedLeftRightOrTwoSidedCongruenceCollection and + IsSemigroup); + +InstallTrueMethod(IsFinite, + IsWrappedLeftRightOrTwoSidedCongruenceCollection and + IsSemigroup); + +BindGlobal("WrappedLeftCongruenceFamily", + NewFamily("WrappedLeftCongruenceFamily", + IsWrappedLeftCongruence)); +BindGlobal("WrappedRightCongruenceFamily", + NewFamily("WrappedRightCongruenceFamily", + IsWrappedRightCongruence)); +BindGlobal("WrappedTwoSidedCongruenceFamily", + NewFamily("WrappedTwoSidedCongruenceFamily", + IsWrappedTwoSidedCongruence)); + +BindGlobal("WrappedLeftCongruenceType", + NewType(WrappedLeftCongruenceFamily, + IsWrappedLeftCongruence and IsPositionalObjectRep)); +BindGlobal("WrappedRightCongruenceType", + NewType(WrappedRightCongruenceFamily, + IsWrappedRightCongruence and IsPositionalObjectRep)); +BindGlobal("WrappedTwoSidedCongruenceType", + NewType(WrappedTwoSidedCongruenceFamily, + IsWrappedTwoSidedCongruence and IsPositionalObjectRep)); + +BindGlobal("WrappedLeftCongruence", +function(x) + return Objectify(WrappedLeftCongruenceType, [x]); +end); - # Setup children and parents lists - children := []; - parents := []; - for i in [1 .. nrcongs] do - children[i] := Set([i]); - parents[i] := Set([i]); - od; +BindGlobal("WrappedRightCongruence", +function(x) + return Objectify(WrappedRightCongruenceType, [x]); +end); - # Find children of each cong in turn - for i in [1 .. nrcongs] do - # Ignore known parents - ignore := BlistList([1 .. nrcongs], [parents[i]]); - for j in [1 .. nrcongs] do - if not ignore[j] and IsSubrelation(congs[i], congs[j]) then - AddSet(children[i], j); - AddSet(parents[j], i); - fi; - od; - od; +BindGlobal("WrappedTwoSidedCongruence", +function(x) + return Objectify(WrappedTwoSidedCongruenceType, [x]); +end); - # We are done: make the object and return - poset := Digraph(parents); - SetInNeighbours(poset, children); - SetCongruencesOfPoset(poset, congs); - SetDigraphVertexLabels(poset, congs); - if nrcongs > 0 then - SetUnderlyingSemigroupOfCongruencePoset(poset, Range(congs[1])); +InstallMethod(\=, "for wrapped left, right, or 2-sided congruences", +[IsWrappedLeftRightOrTwoSidedCongruence, + IsWrappedLeftRightOrTwoSidedCongruence], +function(x, y) + return x![1] = y![1]; +end); + +InstallMethod(\<, "for wrapped left, right, or 2-sided congruences", +[IsWrappedLeftRightOrTwoSidedCongruence, + IsWrappedLeftRightOrTwoSidedCongruence], +function(x, y) + return EquivalenceRelationCanonicalLookup(x![1]) + < EquivalenceRelationCanonicalLookup(y![1]); +end); + +InstallMethod(ChooseHashFunction, +"for a wrapped left, right, or 2-sided congruence and integer", +[IsLeftRightOrTwoSidedCongruence, IsInt], +function(cong, data) + local HashFunc; + HashFunc := function(cong, data) + local x; + x := EquivalenceRelationCanonicalLookup(cong![1]); + return ORB_HashFunctionForPlainFlatList(x, data); + end; + return rec(func := HashFunc, data := data); +end); + +InstallMethod(\*, "for wrapped left semigroup congruences", +[IsWrappedLeftCongruence, IsWrappedLeftCongruence], +{x, y} -> WrappedLeftCongruence(JoinLeftSemigroupCongruences(x![1], y![1]))); + +InstallMethod(\*, "for wrapped right semigroup congruences", +[IsWrappedRightCongruence, IsWrappedRightCongruence], +{x, y} -> WrappedRightCongruence(JoinRightSemigroupCongruences(x![1], y![1]))); + +InstallMethod(\*, "for wrapped 2-sided semigroup congruences", +[IsWrappedTwoSidedCongruence, IsWrappedTwoSidedCongruence], +{x, y} -> WrappedTwoSidedCongruence(JoinSemigroupCongruences(x![1], y![1]))); + +InstallMethod(JoinSemilatticeOfCongruences, +"for a congruence poset and a function", +[IsCongruencePoset, IsFunction], +function(gen_congs, WrappedXCongruence) + local gens, U, S, poset, all_congs; + + # Trivial case + if DigraphNrVertices(gen_congs) = 0 then + return gen_congs; fi; + + gens := List(CongruencesOfPoset(gen_congs), WrappedXCongruence); + U := Range(gens[1]![1]); + # Add(gens, WrappedXCongruence(RightSemigroupCongruence(U, []))); + + S := Semigroup(List(CongruencesOfPoset(gen_congs), WrappedXCongruence)); + poset := DigraphReflexiveTransitiveClosure(PartialOrderOfDClasses(S)); + Info(InfoSemigroups, 1, StringFormatted("Found {} congruences in total!", + Size(S))); + all_congs := List(AsList(S), x -> x![1]); + SetCongruencesOfPoset(poset, all_congs); + SetDigraphVertexLabels(poset, all_congs); + SetUnderlyingSemigroupOfCongruencePoset(poset, U); SetFilterObj(poset, IsCongruencePoset); + SetPosetOfPrincipalCongruences(poset, + Filtered(CongruencesOfPoset(gen_congs), + x -> Size(GeneratingPairsOfLeftRightOrTwoSidedCongruence(x)) = 1)); return poset; end); -InstallMethod(LatticeOfLeftCongruences, -"for a semigroup", [IsSemigroup], -S -> LatticeOfLeftCongruences(S, S)); - -InstallMethod(LatticeOfRightCongruences, -"for a semigroup", [IsSemigroup], -S -> LatticeOfRightCongruences(S, S)); +BindGlobal("_CheckCongruenceLatticeArgs", +function(S, obj) + if not (IsFinite(S) and CanUseFroidurePin(S)) then + ErrorNoReturn("the 1st argument (a semigroup) must be finite and have ", + "CanUseFroidurePin"); + elif IsIterator(obj) then + return; + elif not (IsEmpty(obj) or IsMultiplicativeElementCollColl(obj)) then + ErrorNoReturn("the 2nd argument (a list or collection) must be ", + "empty or a mult. elt. coll. coll."); + elif not ForAll(obj, x -> Size(x) = 2) + or not ForAll(obj, x -> x[1] in S and x[2] in S) then + ErrorNoReturn("the 2nd argument (a list) must consist of ", + "pairs of the 1st argument (a semigroup)"); + fi; +end); -InstallMethod(LatticeOfCongruences, -"for a semigroup", [IsSemigroup], -S -> LatticeOfCongruences(S, S)); +######################################################################## +# GeneratingPairsOfPrincipalCongruences +######################################################################## -InstallMethod(LatticeOfLeftCongruences, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - local poset; - poset := PosetOfPrincipalLeftCongruences(S, restriction); - poset := JoinSemilatticeOfCongruences(poset, JoinLeftSemigroupCongruences); - return SEMIGROUPS.AddTrivialCongruence(poset, LeftSemigroupCongruence); +InstallMethod(GeneratingPairsOfPrincipalCongruences, "for a semigroup", +[IsSemigroup], +function(S) + if not (IsFinite(S) and CanUseFroidurePin(S)) then + ErrorNoReturn("the argument (a semigroup) must be finite and have ", + "CanUseFroidurePin"); + fi; + return Combinations(AsList(S), 2); + # return IteratorOfCombinations(AsList(S), 2); end); -InstallMethod(LatticeOfRightCongruences, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - local poset; - poset := PosetOfPrincipalRightCongruences(S, restriction); - poset := JoinSemilatticeOfCongruences(poset, JoinRightSemigroupCongruences); - return SEMIGROUPS.AddTrivialCongruence(poset, RightSemigroupCongruence); +InstallMethod(GeneratingPairsOfPrincipalLeftCongruences, +"for an acting semigroup", [IsSemigroup], +function(S) + if not (IsFinite(S) and CanUseFroidurePin(S)) then + ErrorNoReturn("the argument (a semigroup) must be finite and have ", + "CanUseFroidurePin"); + fi; + return Combinations(AsList(S), 2); + # return IteratorOfCombinations(AsList(S), 2); end); -InstallMethod(LatticeOfCongruences, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - local poset; - poset := PosetOfPrincipalCongruences(S, restriction); - poset := JoinSemilatticeOfCongruences(poset, JoinSemigroupCongruences); - return SEMIGROUPS.AddTrivialCongruence(poset, SemigroupCongruence); +InstallMethod(GeneratingPairsOfPrincipalRightCongruences, +"for an acting semigroup", [IsSemigroup], +GeneratingPairsOfPrincipalCongruences); + +InstallMethod(GeneratingPairsOfPrincipalCongruences, "for an acting semigroup", +[IsActingSemigroup], +function(S) + local M, MxM, map1, map2, Delta, pairs; + if not (IsFinite(S) and CanUseFroidurePin(S)) then + ErrorNoReturn("the argument (a semigroup) must be finite and have ", + "CanUseFroidurePin"); + elif not IsMonoid(S) and not IsMonoidAsSemigroup(S) then + M := Monoid(S, rec(acting := true)); + else + M := S; + fi; + + MxM := DirectProduct(M, M); + SetFilterObj(MxM, IsActingSemigroup); + map1 := Embedding(MxM, 1); + map2 := Embedding(MxM, 2); + + Delta := Set(GeneratorsOfSemigroup(S), x -> x ^ map1 * x ^ map2); + pairs := RelativeDClassReps(MxM, Semigroup(Delta, rec(acting := true))); + map1 := Projection(MxM, 1); + map2 := Projection(MxM, 2); + pairs := Set(pairs, x -> AsSortedList([x ^ map1, x ^ map2])); + return Filtered(pairs, x -> x[1] in S and x[2] in S); end); -InstallMethod(LeftCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(LatticeOfLeftCongruences(S))); +InstallMethod(GeneratingPairsOfPrincipalRightCongruences, +"for an acting semigroup", +[IsActingSemigroup], +function(S) + local M, MxM, map1, map2, Delta, pairs; -InstallMethod(RightCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(LatticeOfRightCongruences(S))); + if not (IsFinite(S) and CanUseFroidurePin(S)) then + ErrorNoReturn("the argument (a semigroup) must be finite and have ", + "CanUseFroidurePin"); + elif not IsMonoid(S) and not IsMonoidAsSemigroup(S) then + M := Monoid(S); + else + M := S; + fi; -InstallMethod(CongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(LatticeOfCongruences(S))); + MxM := DirectProduct(M, M); + map1 := Embedding(MxM, 1); + map2 := Embedding(MxM, 2); -InstallMethod(PosetOfPrincipalLeftCongruences, -"for a semigroup", [IsSemigroup], -S -> PosetOfPrincipalLeftCongruences(S, S)); + Delta := Set(GeneratorsOfSemigroup(S), x -> x ^ map1 * x ^ map2); + pairs := RelativeRClassReps(MxM, Semigroup(Delta)); + map1 := Projection(MxM, 1); + map2 := Projection(MxM, 2); + pairs := Set(pairs, x -> AsSortedList([x ^ map1, x ^ map2])); + return Filtered(pairs, x -> x[1] in S and x[2] in S); +end); -InstallMethod(PosetOfPrincipalRightCongruences, -"for a semigroup", [IsSemigroup], -S -> PosetOfPrincipalRightCongruences(S, S)); +######################################################################## +# PosetOfPrincipalRight/LeftCongruences +######################################################################## + +InstallMethod(PosetOfPrincipalCongruences, "for a semigroup", [IsSemigroup], +function(S) + local pairs; + if HasLatticeOfCongruences(S) then + return PosetOfPrincipalCongruences(LatticeOfCongruences(S)); + fi; + pairs := GeneratingPairsOfPrincipalCongruences(S); + return SEMIGROUPS.PrincipalXCongruencePosetNC(S, + pairs, + SemigroupCongruence); +end); InstallMethod(PosetOfPrincipalCongruences, -"for a semigroup", [IsSemigroup], -S -> PosetOfPrincipalCongruences(S, S)); +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return SEMIGROUPS.PrincipalXCongruencePosetNC(S, + pairs, + SemigroupCongruence); +end); -InstallMethod(PosetOfPrincipalLeftCongruences, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - local genpairs; - if not (IsFinite(S) and CanUseFroidurePin(S)) then - ErrorNoReturn("the 1st argument (a semigroup) must be finite and have ", - "CanUseFroidurePin"); - elif not IsSubset(S, restriction) then - ErrorNoReturn("the 2nd argument (a set) must be a subset of the 1st ", - "argument (a semigroup)"); +InstallMethod(PosetOfPrincipalRightCongruences, "for a semigroup", +[IsSemigroup], +function(S) + local pairs; + if HasLatticeOfRightCongruences(S) then + return PosetOfPrincipalRightCongruences(LatticeOfRightCongruences(S)); fi; - genpairs := GeneratingPairsOfLeftSemigroupCongruence; + pairs := GeneratingPairsOfPrincipalRightCongruences(S); return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - restriction, - LeftSemigroupCongruence, - genpairs); + pairs, + RightSemigroupCongruence); end); InstallMethod(PosetOfPrincipalRightCongruences, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - local genpairs; - if not (IsFinite(S) and CanUseFroidurePin(S)) then - ErrorNoReturn("the 1st argument (a semigroup) must be finite and have ", - "CanUseFroidurePin"); - elif not IsSubset(S, restriction) then - ErrorNoReturn("the 2nd argument (a set) must be a subset of the 1st ", - "argument (a semigroup)"); - fi; - genpairs := GeneratingPairsOfRightSemigroupCongruence; +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - restriction, - RightSemigroupCongruence, - genpairs); + pairs, + RightSemigroupCongruence); end); -InstallMethod(PosetOfPrincipalCongruences, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - local genpairs; - if not (IsFinite(S) and CanUseFroidurePin(S)) then - ErrorNoReturn("the 1st argument (a semigroup) must be finite and have ", - "CanUseFroidurePin"); - elif not IsSubset(S, restriction) then - ErrorNoReturn("the 2nd argument (a set) must be a subset of the 1st ", - "argument (a semigroup)"); +InstallMethod(PosetOfPrincipalLeftCongruences, "for a semigroup", +[IsSemigroup], +function(S) + local pairs; + if HasLatticeOfLeftCongruences(S) then + return PosetOfPrincipalLeftCongruences(LatticeOfLeftCongruences(S)); fi; - genpairs := GeneratingPairsOfSemigroupCongruence; + pairs := GeneratingPairsOfPrincipalLeftCongruences(S); return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - restriction, - SemigroupCongruence, - genpairs); + pairs, + LeftSemigroupCongruence); end); -InstallMethod(MinimalLeftCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> MinimalLeftCongruencesOfSemigroup(S, S)); +InstallMethod(PosetOfPrincipalLeftCongruences, +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return SEMIGROUPS.PrincipalXCongruencePosetNC(S, + pairs, + LeftSemigroupCongruence); +end); -InstallMethod(MinimalRightCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> MinimalRightCongruencesOfSemigroup(S, S)); +############################################################################# +## LatticeOfCongruences +############################################################################# -InstallMethod(MinimalCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> MinimalCongruencesOfSemigroup(S, S)); +InstallMethod(LatticeOfCongruencesNC, +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + local poset; + poset := PosetOfPrincipalCongruences(S, pairs); + poset := JoinSemilatticeOfCongruences(poset, WrappedTwoSidedCongruence); + return SEMIGROUPS.AddTrivialCongruence(poset, SemigroupCongruence); +end); -InstallMethod(MinimalLeftCongruencesOfSemigroup, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - return MinimalCongruences(PosetOfPrincipalLeftCongruences(S, restriction)); +InstallMethod(LatticeOfCongruences, +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return LatticeOfCongruencesNC(S, pairs); end); -InstallMethod(MinimalRightCongruencesOfSemigroup, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - return MinimalCongruences(PosetOfPrincipalRightCongruences(S, restriction)); +InstallMethod(LatticeOfCongruences, "for a semigroup", [IsSemigroup], +function(S) + return LatticeOfCongruencesNC(S, GeneratingPairsOfPrincipalCongruences(S)); end); -InstallMethod(MinimalCongruencesOfSemigroup, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], -function(S, restriction) - return MinimalCongruences(PosetOfPrincipalCongruences(S, restriction)); +InstallMethod(LatticeOfRightCongruencesNC, +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + local poset; + poset := PosetOfPrincipalRightCongruences(S, pairs); + poset := JoinSemilatticeOfCongruences(poset, WrappedRightCongruence); + return SEMIGROUPS.AddTrivialCongruence(poset, RightSemigroupCongruence); +end); + +InstallMethod(LatticeOfRightCongruences, +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return LatticeOfRightCongruencesNC(S, pairs); +end); + +InstallMethod(LatticeOfRightCongruences, "for a semigroup", [IsSemigroup], +function(S) + return LatticeOfRightCongruencesNC(S, + GeneratingPairsOfPrincipalRightCongruences(S)); +end); + +InstallMethod(LatticeOfLeftCongruencesNC, +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + local poset; + poset := PosetOfPrincipalLeftCongruences(S, pairs); + poset := JoinSemilatticeOfCongruences(poset, WrappedLeftCongruence); + return SEMIGROUPS.AddTrivialCongruence(poset, LeftSemigroupCongruence); end); +InstallMethod(LatticeOfLeftCongruences, +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return LatticeOfLeftCongruencesNC(S, pairs); +end); + +InstallMethod(LatticeOfLeftCongruences, "for a semigroup", [IsSemigroup], +function(S) + return LatticeOfLeftCongruencesNC(S, + GeneratingPairsOfPrincipalLeftCongruences(S)); +end); + +######################################################################## +# Left/Right/CongruencesOfSemigroup +######################################################################## + +InstallMethod(LeftCongruencesOfSemigroup, +"for a semigroup", [IsSemigroup], +S -> CongruencesOfPoset(LatticeOfLeftCongruences(S))); + +InstallMethod(RightCongruencesOfSemigroup, +"for a semigroup", [IsSemigroup], +S -> CongruencesOfPoset(LatticeOfRightCongruences(S))); + +InstallMethod(CongruencesOfSemigroup, +"for a semigroup", [IsSemigroup], +S -> CongruencesOfPoset(LatticeOfCongruences(S))); + +######################################################################## +# Principal congruences +######################################################################## + InstallMethod(PrincipalLeftCongruencesOfSemigroup, "for a semigroup", [IsSemigroup], -S -> PrincipalLeftCongruencesOfSemigroup(S, S)); +S -> CongruencesOfPoset(PosetOfPrincipalLeftCongruences(S))); InstallMethod(PrincipalRightCongruencesOfSemigroup, "for a semigroup", [IsSemigroup], -S -> PrincipalRightCongruencesOfSemigroup(S, S)); +S -> CongruencesOfPoset(PosetOfPrincipalRightCongruences(S))); InstallMethod(PrincipalCongruencesOfSemigroup, "for a semigroup", [IsSemigroup], -S -> PrincipalCongruencesOfSemigroup(S, S)); +S -> CongruencesOfPoset(PosetOfPrincipalCongruences(S))); InstallMethod(PrincipalLeftCongruencesOfSemigroup, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], function(S, restriction) return CongruencesOfPoset(PosetOfPrincipalLeftCongruences(S, restriction)); end); InstallMethod(PrincipalRightCongruencesOfSemigroup, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], function(S, restriction) return CongruencesOfPoset(PosetOfPrincipalRightCongruences(S, restriction)); end); InstallMethod(PrincipalCongruencesOfSemigroup, -"for a semigroup and a multiplicative element collection", -[IsSemigroup, IsMultiplicativeElementCollection], +"for a semigroup and a list or collection", +[IsSemigroup, IsListOrCollection], function(S, restriction) return CongruencesOfPoset(PosetOfPrincipalCongruences(S, restriction)); end); +######################################################################## +## MinimalCongruences +######################################################################## + +InstallMethod(MinimalCongruences, "for a congruence poset", +[IsCongruencePoset], +poset -> CongruencesOfPoset(poset){Positions(InDegrees(poset), 1)}); + +InstallMethod(MinimalCongruencesOfSemigroup, "for a semigroup", [IsSemigroup], +function(S) + if HasLatticeOfCongruences(S) then + return MinimalCongruences(LatticeOfCongruences(S)); + fi; + return MinimalCongruences(PosetOfPrincipalCongruences(S)); +end); + +InstallMethod(MinimalLeftCongruencesOfSemigroup, "for a semigroup", +[IsSemigroup], +function(S) + if HasLatticeOfLeftCongruences(S) then + return MinimalCongruences(LatticeOfLeftCongruences(S)); + fi; + return MinimalCongruences(PosetOfPrincipalLeftCongruences(S)); +end); + +InstallMethod(MinimalRightCongruencesOfSemigroup, "for a semigroup", +[IsSemigroup], +function(S) + if HasLatticeOfRightCongruences(S) then + return MinimalCongruences(LatticeOfRightCongruences(S)); + fi; + return MinimalCongruences(PosetOfPrincipalRightCongruences(S)); +end); + +InstallMethod(MinimalCongruencesOfSemigroup, +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + return MinimalCongruences(PosetOfPrincipalCongruences(S, pairs)); +end); + +InstallMethod(MinimalRightCongruencesOfSemigroup, +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + return MinimalCongruences(PosetOfPrincipalRightCongruences(S, pairs)); +end); + +InstallMethod(MinimalLeftCongruencesOfSemigroup, +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + return MinimalCongruences(PosetOfPrincipalLeftCongruences(S, pairs)); +end); + +######################################################################## +# Printing, viewing, dot strings etc +######################################################################## + +InstallMethod(ViewObj, "for a congruence poset", [IsCongruencePoset], +function(poset) + local prefix, S, C, hand; + if DigraphNrVertices(poset) = 0 then + Print(""); + else + if IsLatticeDigraph(poset) then + prefix := "lattice"; + else + prefix := "poset"; + fi; + S := UnderlyingSemigroupOfCongruencePoset(poset); + # Find a non-trivial non-universal congruence if it exists + C := First(CongruencesOfPoset(poset), + x -> not NrEquivalenceClasses(x) in [1, Size(S)]); + if C = fail or IsMagmaCongruence(C) then + hand := "two-sided"; + else + hand := CongruenceHandednessString(C); + fi; + Print("<\>", + prefix, + " of ", + DigraphNrVertices(poset), + " ", + hand, + " congruences over \<"); + ViewObj(UnderlyingSemigroupOfCongruencePoset(poset)); + Print(">"); + fi; +end); + +InstallMethod(PrintObj, "for a congruence poset", [IsCongruencePoset], +function(poset) + Print("PosetOfCongruences( ", CongruencesOfPoset(poset), " )"); +end); + InstallMethod(DotString, "for a congruence poset", [IsCongruencePoset], @@ -486,7 +720,7 @@ InstallMethod(DotString, [IsCongruencePoset, IsRecord], function(poset, opts) local nrcongs, congs, S, symbols, i, nr, in_nbs, rel, str, j, k; - nrcongs := Size(poset); + nrcongs := DigraphNrVertices(poset); # Setup unbound options if not IsBound(opts.info) then opts.info := false; diff --git a/gap/congruences/congpairs.gd b/gap/congruences/congpairs.gd index 55bf1feee..8a018629b 100644 --- a/gap/congruences/congpairs.gd +++ b/gap/congruences/congpairs.gd @@ -8,14 +8,10 @@ ############################################################################# ## ## This file contains functions for any semigroup congruence with generating -## pairs. +## pairs; regardless of representation. -DeclareAttribute("GeneratingPairsOfAnyCongruence", IsAnyCongruenceCategory); - -DeclareSynonym("GeneratingPairsOfLeftSemigroupCongruence", - GeneratingPairsOfLeftMagmaCongruence); -DeclareSynonym("GeneratingPairsOfRightSemigroupCongruence", - GeneratingPairsOfRightMagmaCongruence); +DeclareAttribute("GeneratingPairsOfLeftRightOrTwoSidedCongruence", + IsLeftRightOrTwoSidedCongruence); DeclareOperation("AsSemigroupCongruenceByGeneratingPairs", [IsSemigroupCongruence]); @@ -23,8 +19,3 @@ DeclareOperation("AsRightSemigroupCongruenceByGeneratingPairs", [IsRightSemigroupCongruence]); DeclareOperation("AsLeftSemigroupCongruenceByGeneratingPairs", [IsLeftSemigroupCongruence]); - -# Internal detail - -DeclareOperation("_AnyCongruenceByGeneratingPairs", - [IsSemigroup, IsHomogeneousList, IsFunction]); diff --git a/gap/congruences/congpairs.gi b/gap/congruences/congpairs.gi index 26b7613da..3c7fdcd2a 100644 --- a/gap/congruences/congpairs.gi +++ b/gap/congruences/congpairs.gi @@ -6,67 +6,30 @@ ## Licensing information can be found in the README file of this package. ## ############################################################################# -## + +############################################################################# ## This file contains functions for any congruence of a semigroup with -## generating pairs. +## generating pairs, regardless of representation. ############################################################################# -InstallImmediateMethod(GeneratingPairsOfAnyCongruence, - IsCongruenceCategory +InstallImmediateMethod(GeneratingPairsOfLeftRightOrTwoSidedCongruence, + IsMagmaCongruence and IsSemigroupCongruence and HasGeneratingPairsOfMagmaCongruence, 0, GeneratingPairsOfMagmaCongruence); -InstallImmediateMethod(GeneratingPairsOfAnyCongruence, - IsLeftCongruenceCategory +InstallImmediateMethod(GeneratingPairsOfLeftRightOrTwoSidedCongruence, + IsLeftMagmaCongruence and IsLeftSemigroupCongruence and HasGeneratingPairsOfLeftMagmaCongruence, 0, GeneratingPairsOfLeftMagmaCongruence); -InstallImmediateMethod(GeneratingPairsOfAnyCongruence, - IsRightCongruenceCategory +InstallImmediateMethod(GeneratingPairsOfLeftRightOrTwoSidedCongruence, + IsRightMagmaCongruence and IsRightSemigroupCongruence and HasGeneratingPairsOfRightMagmaCongruence, 0, GeneratingPairsOfRightMagmaCongruence); -############################################################################# -# Constructor -############################################################################# - -InstallMethod(_AnyCongruenceByGeneratingPairs, -[IsSemigroup, IsHomogeneousList, IsFunction], -function(S, pairs, filt) - local fam, C, pair; - - for pair in pairs do - if not IsList(pair) or Length(pair) <> 2 then - Error("the 2nd argument must consist of lists of ", - "length 2"); - elif not pair[1] in S or not pair[2] in S then - Error("the 2nd argument must consist of lists of ", - "elements of the 1st argument (a semigroup)"); - fi; - od; - - # Create the Object - fam := GeneralMappingsFamily(ElementsFamily(FamilyObj(S)), - ElementsFamily(FamilyObj(S))); - - C := Objectify(NewType(fam, filt and IsAttributeStoringRep), - rec()); - SetSource(C, S); - SetRange(C, S); - if IsCongruenceCategory(C) then - SetGeneratingPairsOfMagmaCongruence(C, pairs); - elif IsLeftCongruenceCategory(C) then - SetGeneratingPairsOfLeftMagmaCongruence(C, pairs); - else - Assert(0, IsRightCongruenceCategory(C)); - SetGeneratingPairsOfRightMagmaCongruence(C, pairs); - fi; - return C; -end); - InstallMethod(AsSemigroupCongruenceByGeneratingPairs, "for semigroup congruence", [IsSemigroupCongruence], @@ -108,7 +71,7 @@ function(congl) local pairs, cong2; # Is this left congruence right-compatible? # First, create the 2-sided congruence generated by these pairs. - pairs := GeneratingPairsOfLeftSemigroupCongruence(congl); + pairs := GeneratingPairsOfLeftMagmaCongruence(congl); cong2 := SemigroupCongruence(Range(congl), pairs); # congl is right-compatible iff these describe the same relation @@ -129,7 +92,7 @@ function(congr) local pairs, cong2; # Is this right congruence left-compatible? # First, create the 2-sided congruence generated by these pairs. - pairs := GeneratingPairsOfRightSemigroupCongruence(congr); + pairs := GeneratingPairsOfRightMagmaCongruence(congr); cong2 := SemigroupCongruence(Range(congr), pairs); # congr is left-compatible iff these describe the same relation @@ -148,7 +111,9 @@ InstallMethod(IsSemigroupCongruence, [IsLeftSemigroupCongruence and HasGeneratingPairsOfLeftMagmaCongruence], IsRightSemigroupCongruence); -InstallMethod(IsSemigroupCongruence, +# The method below matches more than one declaration of IsSemigroupCongruence, +# hence the Other +InstallOtherMethod(IsSemigroupCongruence, "for a right semigroup congruence with known generating pairs", [IsRightSemigroupCongruence and HasGeneratingPairsOfRightMagmaCongruence], IsLeftSemigroupCongruence); @@ -158,12 +123,16 @@ IsLeftSemigroupCongruence); ############################################################################# InstallMethod(PrintObj, -"for IsAnyCongruenceCategory with known generating pairs", -[IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence], +"for left, right, or 2-sided congruences with known generating pairs", +[IsLeftRightOrTwoSidedCongruence + and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], +10, function(C) local string; - if not IsCongruenceCategory(C) then - string := ShallowCopy(AnyCongruenceString(C)); + if not IsMagmaCongruence(C) then + # Don't need to check IsSemigroupCongruence because we check + # IsLeftRightOrTwoSidedCongruence above. + string := ShallowCopy(CongruenceHandednessString(C)); string[1] := UppercaseChar(string[1]); else string := ""; @@ -172,25 +141,20 @@ function(C) Print(string, "SemigroupCongruence( "); PrintObj(Range(C)); Print(", "); - Print(GeneratingPairsOfAnyCongruence(C)); + Print(GeneratingPairsOfLeftRightOrTwoSidedCongruence(C)); Print(" )"); end); InstallMethod(ViewObj, -"for IsAnyCongruenceCategory with known generating pairs", -[IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence], +"for left, right, or 2-sided congruences with known generating pairs", +[IsLeftRightOrTwoSidedCongruence + and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], +9, # to beat the library method function(C) - local string; - if not IsCongruenceCategory(C) then - string := ShallowCopy(AnyCongruenceString(C)); - Append(string, " "); - else - string := ""; - fi; - Print("<", string, "semigroup congruence over "); + Print("<", CongruenceHandednessString(C), " semigroup congruence over "); ViewObj(Range(C)); Print(" with ", - Size(GeneratingPairsOfAnyCongruence(C)), + Size(GeneratingPairsOfLeftRightOrTwoSidedCongruence(C)), " generating pairs>"); end); @@ -199,78 +163,71 @@ end); ######################################################################## InstallMethod(\=, -"for IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence", -[IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence, - IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence], -function(lhop, rhop) - if AnyCongruenceCategory(lhop) = AnyCongruenceCategory(rhop) then - return Range(lhop) = Range(rhop) - and ForAll(GeneratingPairsOfAnyCongruence(lhop), x -> x in rhop) - and ForAll(GeneratingPairsOfAnyCongruence(rhop), x -> x in lhop); - fi; - TryNextMethod(); -end); - -InstallMethod(IsSubrelation, -"for IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence", -[IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence, - IsAnyCongruenceCategory and HasGeneratingPairsOfAnyCongruence], +"for left, right, or 2-sided congruences with known generating pairs", +[IsLeftRightOrTwoSidedCongruence and + HasGeneratingPairsOfLeftRightOrTwoSidedCongruence, + IsLeftRightOrTwoSidedCongruence and + HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], function(lhop, rhop) - # Only valid for certain combinations of types - if AnyCongruenceCategory(lhop) <> AnyCongruenceCategory(rhop) - and AnyCongruenceCategory(lhop) <> IsCongruenceCategory then + local lpairs, rpairs; + if CongruenceHandednessString(lhop) <> CongruenceHandednessString(rhop) then TryNextMethod(); - elif Range(lhop) <> Range(rhop) then - Error("the 1st and 2nd arguments are congruences over different", - " semigroups"); fi; - # Test whether lhop contains all the pairs in rhop - return ForAll(GeneratingPairsOfAnyCongruence(rhop), - x -> CongruenceTestMembershipNC(lhop, x[1], x[2])); + lpairs := GeneratingPairsOfLeftRightOrTwoSidedCongruence(lhop); + rpairs := GeneratingPairsOfLeftRightOrTwoSidedCongruence(rhop); + return Range(lhop) = Range(rhop) + and ForAll(lpairs, x -> x in rhop) + and ForAll(rpairs, x -> x in lhop); end); ######################################################################## # Algebraic operators ######################################################################## -BindGlobal("_JoinAnyCongruences", -function(lhop, rhop) - local Constructor, pairs; +BindGlobal("_JoinLeftRightOrTwoSidedCongruences", +function(XSemigroupCongruence, GeneratingPairsOfXCongruence, lhop, rhop) + local pairs; if Range(lhop) <> Range(rhop) then - Error("cannot form the join of congruences over different semigroups"); + ErrorNoReturn("cannot form the join of congruences over ", + "different semigroups"); elif lhop = rhop then return lhop; - elif IsCongruenceCategory(lhop) then - Constructor := SemigroupCongruenceByGeneratingPairs; - elif IsLeftCongruenceCategory(lhop) then - Constructor := LeftSemigroupCongruenceByGeneratingPairs; - else - Assert(1, IsRightCongruenceCategory(lhop)); - Constructor := RightSemigroupCongruenceByGeneratingPairs; fi; - pairs := Concatenation(GeneratingPairsOfAnyCongruence(lhop), - GeneratingPairsOfAnyCongruence(rhop)); - return Constructor(Range(lhop), pairs); + pairs := Concatenation(GeneratingPairsOfXCongruence(lhop), + GeneratingPairsOfXCongruence(rhop)); + return XSemigroupCongruence(Range(lhop), pairs); end); InstallMethod(JoinSemigroupCongruences, "for a semigroup congruence with known generating pairs", -[IsCongruenceCategory and HasGeneratingPairsOfMagmaCongruence, - IsCongruenceCategory and HasGeneratingPairsOfMagmaCongruence], -_JoinAnyCongruences); +[IsSemigroupCongruence and HasGeneratingPairsOfMagmaCongruence, + IsSemigroupCongruence and HasGeneratingPairsOfMagmaCongruence], +function(lhop, rhop) + return _JoinLeftRightOrTwoSidedCongruences(SemigroupCongruence, + GeneratingPairsOfMagmaCongruence, + lhop, + rhop); +end); InstallMethod(JoinLeftSemigroupCongruences, -"for a semigroup left congruence with known generating pairs", -[IsLeftCongruenceCategory and HasGeneratingPairsOfLeftMagmaCongruence, - IsLeftCongruenceCategory and HasGeneratingPairsOfLeftMagmaCongruence], -_JoinAnyCongruences); +"for a left semigroup congruence with known generating pairs", +[IsLeftSemigroupCongruence and HasGeneratingPairsOfLeftMagmaCongruence, + IsLeftSemigroupCongruence and HasGeneratingPairsOfLeftMagmaCongruence], +function(lhop, rhop) + return _JoinLeftRightOrTwoSidedCongruences(LeftSemigroupCongruence, + GeneratingPairsOfLeftMagmaCongruence, + lhop, + rhop); +end); InstallMethod(JoinRightSemigroupCongruences, -"for a semigroup right congruence with known generating pairs", -[IsRightCongruenceCategory and HasGeneratingPairsOfRightMagmaCongruence, - IsRightCongruenceCategory and HasGeneratingPairsOfRightMagmaCongruence], -_JoinAnyCongruences); - -MakeReadWriteGlobal("_JoinAnyCongruences"); -Unbind(_JoinAnyCongruences); +"for a right semigroup congruence with known generating pairs", +[IsRightSemigroupCongruence and HasGeneratingPairsOfRightMagmaCongruence, + IsRightSemigroupCongruence and HasGeneratingPairsOfRightMagmaCongruence], +function(lhop, rhop) + return _JoinLeftRightOrTwoSidedCongruences(RightSemigroupCongruence, + GeneratingPairsOfRightMagmaCongruence, + lhop, + rhop); +end); diff --git a/gap/congruences/congpart.gd b/gap/congruences/congpart.gd new file mode 100644 index 000000000..12de789cb --- /dev/null +++ b/gap/congruences/congpart.gd @@ -0,0 +1,90 @@ +############################################################################ +## +## congruences/congpart.gd +## Copyright (C) 2022 James D. Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################ +## +## This file contains declarations for left, right, and two-sided congruences +## that can compute EquivalenceRelationPartition. + +############################################################################### +############################################################################### +# The following are the fundamental attributes for a congruence in +# CanComputeEquivalenceRelationPartition. If defining a new type of congruence +# in this representation it is necessary to provide methods for the following: +# +# * one or both of EquivalenceRelationPartitionWithSingletons or +# EquivalenceRelationPartition (for congruences whose source/range is infinite, +# or otherwise when it is more convenient). +# * ImagesElm (for the calculation of the elements of an equivalence class) +# * CongruenceTestMembershipNC +# +# If the equivalence classes of a congruence have their own representation +# (because they too have better methods than the default ones), then it is also +# necessary to implement a method for: +# +# * EquivalenceClassOfElementNC. +# +# If the congruence is not natively implemented in terms of generating pairs, +# then it is also useful to provide methods for: +# +# * GeneratingPairsOfLeft/Right/MagmaCongruence +# * Left/Right/SemigroupCongruenceByGeneratingPairs +# +# to allow for conversion of the representation. +# +# The following attributes can be computed (by default, if no better +# method is known) from the EquivalenceRelationPartitionWithSingletons: +# +# * EquivalenceRelationPartition (if not implemented above) +# * EquivalenceRelationPartitionWithSingletons (if not implemented above) +# * EquivalenceRelationLookup +# * EquivalenceRelationCanonicalLookup +# * EquivalenceRelationCanonicalPartition +# * NonTrivialEquivalenceClasses +# * EquivalenceClasses +# +# If a congruence knows its generating pairs, knows its source/range, +# CanUseFroidurePin(Range(C)) is true, and HasGeneratorsOfSemigroup(Range(C)) +# is true, then automatically satisfies CanUseLibsemigroupsCongruence +# (which is implies CanComputeEquivalenceRelationPartition). In this case the +# generating pairs are used to construct a libsemigroups Congruence object +# representing , and this object will be used to perform many computations +# for . If you never want to construct such a libsemigroups Congruence +# object representing for , then an immediate method should be installed in +# libsemigroups/cong.gi explicitly excluding this type of congruence, and the +# following mandatory methods listed above for +# CanComputeEquivalenceRelationPartition should be implemented. +# +# To define a new type of congruence that does not implement any of the above +# (i.e. that uses libsemigroups Congruence objects to compute +# EquivalenceRelationPartition), it's sufficient to ensure that the +# congruence's generating pairs are computed in the function where it is +# Objectified. +# +# There are some types of congruences in the Semigroups package which use both +# a specialised representation for some things, and use a libsemigroups +# Congruence object for others: +# +# * IsCongruenceByWangPair; +# * IsReesCongruence (when *not* constructed directly from an ideal). +# +# there are others that never use libsemigroups Congruence objects: +# +# * IsInverseSemigroupCongruenceByKernelTrace; +# * IsSimpleSemigroupCongruence; +# * IsRMSCongruenceByLinkedTriple; +# * IsUniversalSemigroupCongruence; +# * IsReesCongruence (when constructed directly from an ideal). +# +# and some that always do (congruences defined by generating pairs, not +# belonging to the previous types). +# +############################################################################### +############################################################################### + +DeclareProperty("CanComputeEquivalenceRelationPartition", + IsLeftRightOrTwoSidedCongruence); diff --git a/gap/congruences/congpart.gi b/gap/congruences/congpart.gi new file mode 100644 index 000000000..e4c768b8c --- /dev/null +++ b/gap/congruences/congpart.gi @@ -0,0 +1,405 @@ +############################################################################ +## +## congruences/congpart.gi +## Copyright (C) 2022 James D. Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################ +## +## This file contains implementations for left, right, and two-sided +## congruences that can compute EquivalenceRelationPartition. Please read the +## comments in congruences/congpart.gd. + +############################################################################# +# Constructor by generating pairs +############################################################################# + +BindGlobal("_AnyCongruenceByGeneratingPairs", +function(S, pairs, filt) + local fam, C, pair; + + for pair in pairs do + if not IsList(pair) or Length(pair) <> 2 then + Error("the 2nd argument must consist of lists of ", + "length 2"); + elif not pair[1] in S or not pair[2] in S then + Error("the 2nd argument must consist of lists of ", + "elements of the 1st argument (a semigroup)"); + fi; + od; + + # Create the Object + fam := GeneralMappingsFamily(ElementsFamily(FamilyObj(S)), + ElementsFamily(FamilyObj(S))); + + C := Objectify(NewType(fam, filt and IsAttributeStoringRep), + rec()); + SetSource(C, S); + SetRange(C, S); + return C; +end); + +InstallMethod(SemigroupCongruenceByGeneratingPairs, +"for a semigroup and a list", +[IsSemigroup, IsList], +19, # to beat the library method for IsList and IsEmpty +function(S, pairs) + local filt, C; + if not (CanUseFroidurePin(S) or IsFpSemigroup(S) or IsFpMonoid(S) + or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) + or (HasIsFreeMonoid(S) and IsFreeMonoid(S))) then + TryNextMethod(); + fi; + filt := IsSemigroupCongruence + and IsMagmaCongruence + and CanComputeEquivalenceRelationPartition; + C := _AnyCongruenceByGeneratingPairs(S, pairs, filt); + SetGeneratingPairsOfMagmaCongruence(C, pairs); + return C; +end); + +InstallMethod(LeftSemigroupCongruenceByGeneratingPairs, +"for a semigroup and a list", +[IsSemigroup, IsList], +19, # to beat the library method for IsList and IsEmpty +function(S, pairs) + local filt, C; + if not (CanUseFroidurePin(S) or IsFpSemigroup(S) or IsFpMonoid(S) + or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) + or (HasIsFreeMonoid(S) and IsFreeMonoid(S))) then + TryNextMethod(); + fi; + filt := IsLeftSemigroupCongruence + and IsLeftMagmaCongruence + and CanComputeEquivalenceRelationPartition; + C := _AnyCongruenceByGeneratingPairs(S, pairs, filt); + SetGeneratingPairsOfLeftMagmaCongruence(C, pairs); + return C; +end); + +InstallMethod(RightSemigroupCongruenceByGeneratingPairs, +"for a semigroup and a list", +[IsSemigroup, IsList], +19, # to beat the library method for IsList and IsEmpty +function(S, pairs) + local filt, C; + if not (CanUseFroidurePin(S) or IsFpSemigroup(S) or IsFpMonoid(S) + or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) + or (HasIsFreeMonoid(S) and IsFreeMonoid(S))) then + TryNextMethod(); + fi; + filt := IsRightSemigroupCongruence + and IsRightMagmaCongruence + and CanComputeEquivalenceRelationPartition; + C := _AnyCongruenceByGeneratingPairs(S, pairs, filt); + SetGeneratingPairsOfRightMagmaCongruence(C, pairs); + return C; +end); + +############################################################################ +# Congruence attributes that do use FroidurePin directly +############################################################################ + +InstallMethod(EquivalenceRelationPartitionWithSingletons, +"for left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + local S, lookup, result, enum, i; + + S := Range(C); + if not IsFinite(S) then + ErrorNoReturn("the argument (a ", + CongruenceHandednessString(C), + " congruence) must have finite range"); + elif not CanUseFroidurePin(S) then + # CanUseFroidurePin is required because PositionCanonical is not a thing + # for other types of semigroups. + TryNextMethod(); + fi; + lookup := EquivalenceRelationCanonicalLookup(C); + result := List([1 .. NrEquivalenceClasses(C)], x -> []); + enum := EnumeratorCanonical(S); + for i in [1 .. Size(S)] do + Add(result[lookup[i]], enum[i]); + od; + + return result; +end); + +InstallMethod(EquivalenceRelationLookup, +"for a left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + local S, lookup, class, nr, elm; + S := Range(C); + if not IsFinite(S) then + ErrorNoReturn("the argument (a ", + CongruenceHandednessString(C), + " congruence) must have finite range"); + elif not CanUseFroidurePin(S) then + # CanUseFroidurePin is required because PositionCanonical is not a thing + # for other types of semigroups. + TryNextMethod(); + fi; + + lookup := [1 .. Size(S)]; + for class in EquivalenceRelationPartition(C) do + nr := PositionCanonical(S, Representative(class)); + for elm in class do + lookup[PositionCanonical(S, elm)] := nr; + od; + od; + return lookup; +end); + +InstallMethod(EquivalenceRelationCanonicalPartition, +"for a left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + local S, result, cmp, i; + S := Range(C); + if not IsFinite(S) then + ErrorNoReturn("the argument (a ", + CongruenceHandednessString(C), + " congruence) must have finite range"); + elif not CanUseFroidurePin(S) then + # CanUseFroidurePin is required because PositionCanonical is not a thing + # for other types of semigroups. + TryNextMethod(); + fi; + result := ShallowCopy(EquivalenceRelationPartition(C)); + cmp := {x, y} -> PositionCanonical(S, x) < PositionCanonical(S, y); + for i in [1 .. Length(result)] do + result[i] := ShallowCopy(result[i]); + Sort(result[i], cmp); + od; + Sort(result, {x, y} -> cmp(Representative(x), Representative(y))); + return result; +end); +############################################################################ +# Congruence attributes/operations/etc that don't require CanUseFroidurePin +############################################################################ + +InstallMethod(EquivalenceRelationPartition, +"for left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + return Filtered(EquivalenceRelationPartitionWithSingletons(C), + x -> Size(x) > 1); +end); + +InstallMethod(EquivalenceRelationCanonicalLookup, +"for a left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + local S, lookup; + S := Range(C); + if not IsFinite(S) then + ErrorNoReturn("the argument (a ", + CongruenceHandednessString(C), + " congruence) must have finite range"); + fi; + lookup := EquivalenceRelationLookup(C); + return FlatKernelOfTransformation(Transformation(lookup), Length(lookup)); +end); + +InstallMethod(NonTrivialEquivalenceClasses, +"for a left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + local part, nr_classes, classes, i; + part := EquivalenceRelationPartition(C); + nr_classes := Length(part); + classes := EmptyPlist(nr_classes); + for i in [1 .. nr_classes] do + classes[i] := EquivalenceClassOfElementNC(C, part[i][1]); + SetAsList(classes[i], part[i]); + od; + return classes; +end); + +InstallMethod(EquivalenceClasses, +"for left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + local part, classes, i; + part := EquivalenceRelationPartitionWithSingletons(C); + classes := []; + for i in [1 .. Length(part)] do + classes[i] := EquivalenceClassOfElementNC(C, part[i][1]); + SetAsList(classes[i], part[i]); + od; + return classes; +end); + +InstallMethod(NrEquivalenceClasses, +"for a left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +function(C) + return Length(EquivalenceClasses(C)); +end); + +######################################################################## +# Comparison operators +######################################################################## + +InstallMethod(\=, +"for left, right, or 2-sided congruences with known generating pairs", +[CanComputeEquivalenceRelationPartition and + HasGeneratingPairsOfLeftRightOrTwoSidedCongruence, + CanComputeEquivalenceRelationPartition and + HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], +function(lhop, rhop) + local lpairs, rpairs; + if CongruenceHandednessString(lhop) <> CongruenceHandednessString(rhop) then + TryNextMethod(); + fi; + + lpairs := GeneratingPairsOfLeftRightOrTwoSidedCongruence(lhop); + rpairs := GeneratingPairsOfLeftRightOrTwoSidedCongruence(rhop); + return Range(lhop) = Range(rhop) + and ForAll(lpairs, x -> CongruenceTestMembershipNC(rhop, x[1], x[2])) + and ForAll(rpairs, x -> CongruenceTestMembershipNC(lhop, x[1], x[2])); +end); + +InstallMethod(\=, "for a left, right, or 2-sided semigroup congruence", +[IsLeftRightOrTwoSidedCongruence, IsLeftRightOrTwoSidedCongruence], +function(lhop, rhop) + local S; + S := Range(lhop); + if S <> Range(rhop) then + return false; + elif HasIsFinite(S) and IsFinite(S) then + return EquivalenceRelationCanonicalLookup(lhop) = + EquivalenceRelationCanonicalLookup(rhop); + fi; + return EquivalenceRelationCanonicalPartition(lhop) = + EquivalenceRelationCanonicalPartition(rhop); +end); + +# This is declared only for CanComputeEquivalenceRelationPartition because no +# other types of congruence have CongruenceTestMembershipNC implemented. +InstallMethod(\in, +"for pair of mult. elt. and left, right, or 2-sided congruence", +[IsMultiplicativeElementCollection, CanComputeEquivalenceRelationPartition], +function(pair, C) + local S, string; + + if Size(pair) <> 2 then + ErrorNoReturn("the 1st argument (a list) does not have length 2"); + fi; + + S := Range(C); + string := CongruenceHandednessString(C); + if not (pair[1] in S and pair[2] in S) then + ErrorNoReturn("the items in the 1st argument (a list) do not all belong to ", + "the range of the 2nd argument (a ", string, " semigroup ", + "congruence)"); + elif CanEasilyCompareElements(pair[1]) and pair[1] = pair[2] then + return true; + fi; + return CongruenceTestMembershipNC(C, pair[1], pair[2]); +end); + +# This is implemented only for CanComputeEquivalenceRelationPartition because +# no other types of congruence have CongruenceTestMembershipNC implemented. +InstallMethod(IsSubrelation, +"for left, right, or 2-sided congruences with known generating pairs", +[CanComputeEquivalenceRelationPartition + and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence, + CanComputeEquivalenceRelationPartition + and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence], +function(lhop, rhop) + # Only valid for certain combinations of types + if CongruenceHandednessString(lhop) <> CongruenceHandednessString(rhop) + and not IsMagmaCongruence(lhop) then + TryNextMethod(); + fi; + + # We use CongruenceTestMembershipNC instead of \in because using \in causes a + # 33% slow down in tst/standard/congruences/conglatt.tst + return Range(lhop) = Range(rhop) + and ForAll(GeneratingPairsOfLeftRightOrTwoSidedCongruence(rhop), + x -> CongruenceTestMembershipNC(lhop, x[1], x[2])); +end); + +############################################################################ +# Operators +############################################################################ + +BindGlobal("_MeetXCongruences", +function(XCongruenceByGeneratingPairs, GeneratingPairsOfXCongruence, lhop, rhop) + local result, lhop_nr_pairs, rhop_nr_pairs, cong1, cong2, pairs, class, i, j; + + if Range(lhop) <> Range(rhop) then + Error("cannot form the meet of congruences over different semigroups"); + elif lhop = rhop then + return lhop; + elif IsSubrelation(lhop, rhop) then + return rhop; + elif IsSubrelation(rhop, lhop) then + return lhop; + fi; + result := XCongruenceByGeneratingPairs(Source(lhop), []); + + lhop_nr_pairs := Sum(EquivalenceRelationPartition(lhop), + x -> Binomial(Size(x), 2)); + rhop_nr_pairs := Sum(EquivalenceRelationPartition(rhop), + x -> Binomial(Size(x), 2)); + + if lhop_nr_pairs < rhop_nr_pairs then + cong1 := lhop; + cong2 := rhop; + else + cong1 := rhop; + cong2 := lhop; + fi; + + for class in EquivalenceRelationPartition(cong1) do + for i in [1 .. Length(class) - 1] do + for j in [i + 1 .. Length(class)] do + if [class[i], class[j]] in cong2 + and not [class[i], class[j]] in result then + pairs := GeneratingPairsOfXCongruence(result); + pairs := Concatenation(pairs, [[class[i], class[j]]]); + result := XCongruenceByGeneratingPairs(Source(cong1), pairs); + fi; + od; + od; + od; + return result; +end); + +InstallMethod(MeetLeftSemigroupCongruences, +"for semigroup congruences that can compute partition", +[CanComputeEquivalenceRelationPartition and IsLeftSemigroupCongruence, + CanComputeEquivalenceRelationPartition and IsLeftSemigroupCongruence], +function(lhop, rhop) + return _MeetXCongruences(LeftSemigroupCongruenceByGeneratingPairs, + GeneratingPairsOfLeftMagmaCongruence, + lhop, + rhop); +end); + +InstallMethod(MeetRightSemigroupCongruences, +"for semigroup congruences that can compute partition", +[CanComputeEquivalenceRelationPartition and IsRightSemigroupCongruence, + CanComputeEquivalenceRelationPartition and IsRightSemigroupCongruence], +function(lhop, rhop) + return _MeetXCongruences(RightSemigroupCongruenceByGeneratingPairs, + GeneratingPairsOfRightMagmaCongruence, + lhop, + rhop); +end); + +InstallMethod(MeetSemigroupCongruences, +"for semigroup congruences that can compute partition", +[CanComputeEquivalenceRelationPartition and IsSemigroupCongruence, + CanComputeEquivalenceRelationPartition and IsSemigroupCongruence], +function(lhop, rhop) + return _MeetXCongruences(SemigroupCongruenceByGeneratingPairs, + GeneratingPairsOfSemigroupCongruence, + lhop, + rhop); +end); diff --git a/gap/congruences/congrees.gd b/gap/congruences/congrees.gd index b00f8cf5b..64c6ceae8 100644 --- a/gap/congruences/congrees.gd +++ b/gap/congruences/congrees.gd @@ -17,4 +17,4 @@ DeclareCategory("IsReesCongruenceClass", IsCongruenceClass and IsAttributeStoringRep and IsMultiplicativeElement); -DeclareProperty("IsReesCongruence", IsAnyCongruenceCategory); +DeclareProperty("IsReesCongruence", IsLeftRightOrTwoSidedCongruence); diff --git a/gap/congruences/congrees.gi b/gap/congruences/congrees.gi index ae6fcc7f2..92d0e756f 100644 --- a/gap/congruences/congrees.gi +++ b/gap/congruences/congrees.gi @@ -11,8 +11,17 @@ ## defined by a two-sided ideal. See Howie 1.7 ## +############################################################################# +# Testers + fundamental representation specific attributes +############################################################################# + +# The following requires CanComputeEquivalenceRelationPartition, because +# otherwise, it applies to other congruences, which don't have a method for +# NrEquivalenceClasses. + InstallMethod(IsReesCongruence, "for a semigroup congruence", -[IsAnyCongruenceCategory], +[CanComputeEquivalenceRelationPartition], +9, # to beat the library method function(C) local S, classes, nontrivial, i, class, I; if not IsSemigroupCongruence(C) then @@ -63,7 +72,11 @@ function(I) # Construct the object fam := GeneralMappingsFamily(ElementsFamily(FamilyObj(S)), ElementsFamily(FamilyObj(S))); - type := NewType(fam, IsCongruenceCategory and IsAttributeStoringRep); + type := NewType(fam, + IsSemigroupCongruence + and IsMagmaCongruence + and IsAttributeStoringRep + and CanComputeEquivalenceRelationPartition); C := Objectify(type, rec()); # Set some attributes SetSource(C, S); @@ -73,6 +86,33 @@ function(I) return C; end); +InstallMethod(AsSemigroupCongruenceByGeneratingPairs, "for a Rees congruence", +[IsReesCongruence], +function(C) + local S, gens, min, pairs, y, x; + S := Range(C); + gens := MinimalIdealGeneratingSet(SemigroupIdealOfReesCongruence(C)); + min := MinimalIdeal(S); + pairs := []; + C := SemigroupCongruence(S, pairs); + for y in min do + for x in gens do + if not [x, y] in C then + Add(pairs, [x, y]); + C := SemigroupCongruence(S, pairs); + fi; + od; + od; + return C; +end); + +InstallMethod(GeneratingPairsOfMagmaCongruence, "for a Rees congruence", +[IsReesCongruence], +function(C) + C := AsSemigroupCongruenceByGeneratingPairs(C); + return GeneratingPairsOfSemigroupCongruence(C); +end); + InstallMethod(ViewObj, "for a Rees congruence", [IsReesCongruence], function(C) @@ -91,6 +131,14 @@ function(C) Print(" )"); end); +############################################################################# +# The mandatory things that must be implemented for +# congruences belonging to CanComputeEquivalenceRelationPartition +############################################################################# + +InstallMethod(EquivalenceRelationPartition, "for a Rees congruence", +[IsReesCongruence], C -> [AsList(SemigroupIdealOfReesCongruence(C))]); + InstallMethod(ImagesElm, "for a Rees congruence and a multiplicative element", [IsReesCongruence, IsMultiplicativeElement], @@ -105,38 +153,22 @@ function(C, x) fi; end); -InstallMethod(AsSemigroupCongruenceByGeneratingPairs, "for a Rees congruence", -[IsReesCongruence], -function(C) - local S, gens, min, pairs, y, x; - S := Range(C); - gens := MinimalIdealGeneratingSet(SemigroupIdealOfReesCongruence(C)); - min := MinimalIdeal(S); - pairs := []; - C := SemigroupCongruence(S, pairs); - for y in min do - for x in gens do - if not [x, y] in C then - Add(pairs, [x, y]); - C := SemigroupCongruence(S, pairs); - fi; - od; - od; - return C; -end); - -InstallMethod(GeneratingPairsOfMagmaCongruence, "for a Rees congruence", -[IsReesCongruence], -function(C) - C := AsSemigroupCongruenceByGeneratingPairs(C); - return GeneratingPairsOfSemigroupCongruence(C); +InstallMethod(CongruenceTestMembershipNC, +"for Rees congruence and two multiplicative elements", +[IsReesCongruence, IsMultiplicativeElement, IsMultiplicativeElement], +function(C, lhop, rhop) + local I; + I := SemigroupIdealOfReesCongruence(C); + return (lhop = rhop) or (lhop in I and rhop in I); end); -InstallMethod(EquivalenceRelationPartition, "for a Rees congruence", -[IsReesCongruence], C -> [AsList(SemigroupIdealOfReesCongruence(C))]); +######################################################################## +# The non-mandatory things where we have a superior method for this particular +# representation. +######################################################################## ######################################################################## -# Operators for congruences +# Operators ######################################################################## InstallMethod(\=, "for two Rees congruences", @@ -160,15 +192,6 @@ function(lhop, rhop) return ForAll(GeneratorsOfSemigroupIdeal(I2), gen -> gen in I1); end); -InstallMethod(CongruenceTestMembershipNC, -"for Rees congruence and two multiplicative elements", -[IsReesCongruence, IsMultiplicativeElement, IsMultiplicativeElement], -function(C, lhop, rhop) - local I; - I := SemigroupIdealOfReesCongruence(C); - return (lhop = rhop) or (lhop in I and rhop in I); -end); - InstallMethod(JoinSemigroupCongruences, "for two Rees congruences", [IsReesCongruence, IsReesCongruence], function(lhop, rhop) @@ -235,7 +258,7 @@ function(C, x) fam := CollectionsFamily(FamilyObj(x)); class := Objectify(NewType(fam, IsReesCongruenceClass - and IsAnyCongruenceClass), + and IsLeftRightOrTwoSidedCongruenceClass), rec()); if is_ideal_class then SetSize(class, Size(SemigroupIdealOfReesCongruence(C))); @@ -248,17 +271,6 @@ function(C, x) return class; end); -InstallMethod(Enumerator, "for a Rees congruence class", -[IsReesCongruenceClass], -function(class) - local C; - if Size(class) > 1 then - C := EquivalenceClassRelation(class); - return Enumerator(SemigroupIdealOfReesCongruence(C)); - fi; - return [Representative(class)]; -end); - ######################################################################## # Operators for classes ######################################################################## @@ -267,8 +279,8 @@ InstallMethod(\*, "for two Rees congruence classes", [IsReesCongruenceClass, IsReesCongruenceClass], function(lhop, rhop) if not EquivalenceClassRelation(lhop) = EquivalenceClassRelation(rhop) then - ErrorNoReturn("the arguments (classes of Rees congruences) do not ", - "belong to the same congruence"); + ErrorNoReturn("the arguments (cong. classes) are not classes of the same ", + "congruence"); elif Size(lhop) > 1 then return lhop; elif Size(rhop) > 1 then @@ -282,7 +294,8 @@ end); InstallMethod(\=, "for two Rees congruence classes", [IsReesCongruenceClass, IsReesCongruenceClass], function(lhop, rhop) - return (Representative(lhop) = Representative(rhop)) + return EquivalenceClassRelation(lhop) = EquivalenceClassRelation(rhop) + and (Representative(lhop) = Representative(rhop)) or (Size(lhop) > 1 and Size(rhop) > 1); end); diff --git a/gap/congruences/congrms.gd b/gap/congruences/congrms.gd index dea40939f..e943004f2 100644 --- a/gap/congruences/congrms.gd +++ b/gap/congruences/congrms.gd @@ -15,9 +15,18 @@ # Congruences by linked triple DeclareCategory("IsRMSCongruenceByLinkedTriple", - IsCongruenceCategory and IsAttributeStoringRep and IsFinite); + IsSemigroupCongruence + and IsMagmaCongruence + and CanComputeEquivalenceRelationPartition + and IsAttributeStoringRep + and IsFinite); DeclareCategory("IsRZMSCongruenceByLinkedTriple", - IsCongruenceCategory and IsAttributeStoringRep and IsFinite); + IsSemigroupCongruence + and IsMagmaCongruence + and CanComputeEquivalenceRelationPartition + and IsAttributeStoringRep + and IsFinite); + DeclareOperation("IsLinkedTriple", [IsSemigroup, IsGroup, IsDenseList, IsDenseList]); DeclareGlobalFunction("RMSCongruenceByLinkedTriple"); @@ -27,10 +36,10 @@ DeclareGlobalFunction("RZMSCongruenceByLinkedTripleNC"); # Congruence Classes DeclareCategory("IsRMSCongruenceClassByLinkedTriple", - IsAnyCongruenceClass and IsCongruenceClass and + IsLeftRightOrTwoSidedCongruenceClass and IsCongruenceClass and IsAttributeStoringRep and IsAssociativeElement); DeclareCategory("IsRZMSCongruenceClassByLinkedTriple", - IsAnyCongruenceClass and IsCongruenceClass and + IsLeftRightOrTwoSidedCongruenceClass and IsCongruenceClass and IsAttributeStoringRep and IsAssociativeElement); DeclareOperation("RMSCongruenceClassByLinkedTriple", [IsRMSCongruenceByLinkedTriple, diff --git a/gap/congruences/congrms.gi b/gap/congruences/congrms.gi index 9aa3408f6..afdace00d 100644 --- a/gap/congruences/congrms.gi +++ b/gap/congruences/congrms.gi @@ -13,17 +13,193 @@ ## and MSc thesis "Computing with Semigroup Congruences" chapter 3. ## -InstallMethod(IsUniversalSemigroupCongruence, -"for a semigroup congruence", -[IsRMSCongruenceByLinkedTriple], -function(C) - return Length(C!.colBlocks) = 1 and - Length(C!.rowBlocks) = 1 and - C!.n = UnderlyingSemigroup(Range(C)); +# This file is organised as follows: +# 1. Congruences: representation specific operations/functions +# 2. Congruences: representation specific constructors +# 3. Congruences: changing representation, generating pairs +# 4. Congruences: printing and viewing +# 5. Congruences: mandatory methods for CanComputeEquivalenceRelationPartition +# 6. Congruences: non-mandatory methods for +# CanComputeEquivalenceRelationPartition, where we have a superior method +# for this particular representation. +# 7. Equivalence classes: representation specific constructors +# 8. Equivalence classes: mandatory methods for +# CanComputeEquivalenceRelationPartition +# 9. Congruence lattice + +############################################################################### +# 1. Congruences: representation specific operations/functions +############################################################################### + +InstallMethod(IsLinkedTriple, +"for Rees matrix semigroup, group, and two lists", +[IsReesMatrixSemigroup, + IsGroup, IsDenseList, IsDenseList], +function(S, n, colBlocks, rowBlocks) + local mat, block, bi, bj, i, j, u, v, bu, bv; + # Check the semigroup is valid + if not IsFinite(S) then + ErrorNoReturn("the 1st argument (a Rees matrix semigroup) is not finite"); + elif not IsSimpleSemigroup(S) then + ErrorNoReturn("the 1st argument (a Rees matrix semigroup) is not simple"); + fi; + mat := Matrix(S); + # Check axiom (L2) from Howie p.86, then call NC function + # Go through the column blocks + for block in colBlocks do + # Check q-condition for all pairs of columns in this block (L2) + for bi in [1 .. Size(block)] do + for bj in [bi + 1 .. Size(block)] do + i := block[bi]; + j := block[bj]; + # Check all pairs of rows (u,v) + for u in [1 .. Size(mat)] do + for v in [u + 1 .. Size(mat)] do + if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) + in n then + return false; + fi; + od; + od; + od; + od; + od; + + # Go through the row blocks + for block in rowBlocks do + # Check q-condition for all pairs of rows in this block (L2) + for bu in [1 .. Size(block)] do + for bv in [bu + 1 .. Size(block)] do + u := block[bu]; + v := block[bv]; + # Check all pairs of columns (i,j) + for i in [1 .. Size(mat[1])] do + for j in [i + 1 .. Size(mat[1])] do + if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) + in n then + return false; + fi; + od; + od; + od; + od; + od; + return true; +end); + +InstallMethod(IsLinkedTriple, +"for Rees 0-matrix semigroup, group, and two lists", +[IsReesZeroMatrixSemigroup, + IsGroup, IsDenseList, IsDenseList], +function(S, n, colBlocks, rowBlocks) + local mat, block, i, j, u, v, bi, bj, bu, bv; + # Check the semigroup is valid + if not IsFinite(S) then + ErrorNoReturn("the 1st argument (a Rees 0-matrix semigroup) is not finite"); + elif not IsZeroSimpleSemigroup(S) then + ErrorNoReturn("the 1st argument (a Rees 0-matrix semigroup) is ", + "not 0-simple"); + fi; + mat := Matrix(S); + # Check axioms (L1) and (L2) from Howie p.86, then call NC function + # Go through the column blocks + for block in colBlocks do + for bj in [2 .. Size(block)] do + # Check columns have zeroes in all the same rows (L1) + for u in [1 .. Size(mat)] do + if (mat[u][block[1]] = 0) <> (mat[u][block[bj]] = 0) then + return false; + fi; + od; + od; + # Check q-condition for all pairs of columns in this block (L2) + for bi in [1 .. Size(block)] do + for bj in [bi + 1 .. Size(block)] do + i := block[bi]; + j := block[bj]; + # Check all pairs of rows (u,v) + for u in [1 .. Size(mat)] do + if mat[u][i] = 0 then + continue; + fi; + for v in [u + 1 .. Size(mat)] do + if mat[v][i] = 0 then + continue; + fi; + if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) + in n then + return false; + fi; + od; + od; + od; + od; + od; + + # Go through the row blocks + for block in rowBlocks do + for bv in [2 .. Size(block)] do + # Check rows have zeroes in all the same columns (L1) + for i in [1 .. Size(mat[1])] do + if (mat[block[1]][i] = 0) <> (mat[block[bv]][i] = 0) then + return false; + fi; + od; + od; + # Check q-condition for all pairs of rows in this block (L2) + for bu in [1 .. Size(block)] do + for bv in [bu + 1 .. Size(block)] do + u := block[bu]; + v := block[bv]; + # Check all pairs of columns (i,j) + for i in [1 .. Size(mat[1])] do + if mat[u][i] = 0 then + continue; + fi; + for j in [i + 1 .. Size(mat[1])] do + if mat[u][j] = 0 then + continue; + fi; + if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) + in n then + return false; + fi; + od; + od; + od; + od; + od; + return true; +end); + +BindGlobal("LinkedElement", +function(x) + local mat, i, u, v, j; + mat := Matrix(ReesMatrixSemigroupOfFamily(FamilyObj(x))); + i := x[1]; # Column no + u := x[3]; # Row no + if IsReesMatrixSemigroupElement(x) then + # RMS case + return mat[1][i] * x[2] * mat[u][1]; + else + # RZMS case + for v in [1 .. Size(mat)] do + if mat[v][i] <> 0 then + break; + fi; + od; + for j in [1 .. Size(mat[1])] do + if mat[u][j] <> 0 then + break; + fi; + od; + return mat[v][i] * x[2] * mat[u][j]; + fi; end); -InstallImmediateMethod(IsUniversalSemigroupCongruence, -IsRZMSCongruenceByLinkedTriple, 0, ReturnFalse); +############################################################################# +# 2. Congruences: representation specific constructors +############################################################################# InstallGlobalFunction(RMSCongruenceByLinkedTriple, function(S, n, colBlocks, rowBlocks) @@ -158,371 +334,424 @@ function(S, n, colBlocks, rowBlocks) return C; end); -InstallMethod(ViewObj, +############################################################################### +# 3. Congruences: changing representation, generating pairs +############################################################################### + +InstallMethod(GeneratingPairsOfMagmaCongruence, "for Rees matrix semigroup congruence by linked triple", [IsRMSCongruenceByLinkedTriple], function(C) - Print(""); -end); - -InstallMethod(ViewObj, -"for Rees zero-matrix semigroup congruence by linked triple", -[IsRZMSCongruenceByLinkedTriple], -function(C) - Print(""); -end); - -InstallMethod(CongruencesOfSemigroup, -"for finite simple Rees matrix semigroup", -[IsReesMatrixSemigroup and IsSimpleSemigroup and IsFinite], -function(S) - local subpartitions, congs, mat, g, colBlocksList, - rowBlocksList, n, colBlocks, rowBlocks; - - # Function to compute all subsets of a relation given by partitions - subpartitions := function(part) - local l; - # Replace each class with a list of all partitions of that class - l := List(part, PartitionsSet); - # Produce all the combinations of partitions of classes - l := Cartesian(l); - # Concatenate these lists to produce complete partitions of the set - l := List(l, Concatenation); - # Finally sort each of these into the canonical order of its new classes - return List(l, SSortedList); - end; - - congs := []; - mat := Matrix(S); - g := UnderlyingSemigroup(S); - - # No need to add the universal congruence + local S, G, m, pairs, n_gens, col_pairs, bl, j, row_pairs, max, n, cp, rp, + pair_no, r, c; + S := Range(C); + G := UnderlyingSemigroup(S); + m := Matrix(S); + pairs := []; - # Compute all column and row relations which are subsets of the max relations - colBlocksList := subpartitions([[1 .. Size(mat[1])]]); - rowBlocksList := subpartitions([[1 .. Size(mat)]]); + # Normal subgroup elements to identify with id + n_gens := GeneratorsOfSemigroup(C!.n); - # Go through all triples and check - for n in NormalSubgroups(g) do - for colBlocks in colBlocksList do - for rowBlocks in rowBlocksList do - if IsLinkedTriple(S, n, colBlocks, rowBlocks) then - Add(congs, - RMSCongruenceByLinkedTripleNC(S, n, colBlocks, rowBlocks)); - fi; - od; + # Pairs that generate the columns relation + col_pairs := []; + for bl in C!.colBlocks do + for j in [2 .. Size(bl)] do + Add(col_pairs, [bl[1], bl[j]]); od; od; - return congs; -end); + # Pairs that generate the rows relation + row_pairs := []; + for bl in C!.rowBlocks do + for j in [2 .. Size(bl)] do + Add(row_pairs, [bl[1], bl[j]]); + od; + od; -InstallMethod(CongruencesOfSemigroup, -"for finite 0-simple Rees 0-matrix semigroup", -[IsReesZeroMatrixSemigroup and IsZeroSimpleSemigroup and IsFinite], -function(S) - local congs, mat, g, AddRelation, maxColBlocks, maxRowBlocks, - i, j, u, v, n, colBlocks, rowBlocks, colBlocksList, rowBlocksList, - subpartitions; - - # Function to compute all subsets of a relation given by partitions - subpartitions := function(part) - local l; - # Replace each class with a list of all partitions of that class - l := List(part, PartitionsSet); - # Produce all the combinations of partitions of classes - l := Cartesian(l); - # Concatenate these lists to produce complete partitions of the set - l := List(l, Concatenation); - # Finally sort each of these into the canonical order of its new classes - return List(l, SSortedList); - end; - - congs := []; - mat := Matrix(S); - g := UnderlyingSemigroup(S); - - # This function combines two congruence classes - AddRelation := function(R, x, y) - local xClass, yClass; - xClass := PositionProperty(R, class -> x in class); - yClass := PositionProperty(R, class -> y in class); - if xClass <> yClass then - Append(R[xClass], R[yClass]); - Remove(R, yClass); + # Collate into RMS element pairs + max := Maximum(Length(n_gens), Length(col_pairs), Length(row_pairs)); + pairs := EmptyPlist(max); + # Set default values in case a list has length 0 + n := One(G); + cp := [1, 1]; + rp := [1, 1]; + # Iterate through all 3 lists together + for pair_no in [1 .. max] do + # If any list has run out, just keep using the last entry or default value + if pair_no <= Length(n_gens) then + n := n_gens[pair_no]; fi; - end; - - # Construct maximum column relation - maxColBlocks := List([1 .. Size(mat[1])], i -> [i]); - for i in [1 .. Size(mat[1])] do - for j in [i + 1 .. Size(mat[1])] do - if ForAll([1 .. Size(mat)], - u -> ((mat[u][i] = 0) = (mat[u][j] = 0))) then - AddRelation(maxColBlocks, i, j); - fi; - od; - od; - - # Construct maximum row relation - maxRowBlocks := List([1 .. Size(mat)], u -> [u]); - for u in [1 .. Size(mat)] do - for v in [u + 1 .. Size(mat)] do - if ForAll([1 .. Size(mat[1])], - i -> ((mat[u][i] = 0) = (mat[v][i] = 0))) then - AddRelation(maxRowBlocks, u, v); - fi; - od; + if pair_no <= Length(col_pairs) then + cp := col_pairs[pair_no]; + fi; + if pair_no <= Length(row_pairs) then + rp := row_pairs[pair_no]; + fi; + # Choose r and c s.t. m[r][cp[1]] and m[rp[1]][c] are both non-zero + # (since there are no zeroes, we can just use 1 for both) + r := 1; + c := 1; + # Make the pair and add it + pairs[pair_no] := + [RMSElement(S, cp[1], m[r][cp[1]] ^ -1 * n * m[rp[1]][c] ^ -1, rp[1]), + RMSElement(S, cp[2], m[r][cp[2]] ^ -1 * m[rp[2]][c] ^ -1, rp[2])]; od; + return pairs; +end); - # Add the universal congruence - Add(congs, UniversalSemigroupCongruence(S)); +InstallMethod(GeneratingPairsOfMagmaCongruence, +"for Rees 0-matrix semigroup congruence by linked triple", +[IsRZMSCongruenceByLinkedTriple], +function(C) + local S, G, m, pairs, n_gens, col_pairs, bl, j, row_pairs, max, n, cp, rp, + pair_no, r, c; + S := Range(C); + G := UnderlyingSemigroup(S); + m := Matrix(S); + pairs := []; - # Compute all column and row relations which are subsets of the max relations - colBlocksList := subpartitions(maxColBlocks); - rowBlocksList := subpartitions(maxRowBlocks); + # Normal subgroup elements to identify with id + n_gens := GeneratorsOfSemigroup(C!.n); - # Go through all triples and check - for n in NormalSubgroups(g) do - for colBlocks in colBlocksList do - for rowBlocks in rowBlocksList do - if IsLinkedTriple(S, n, colBlocks, rowBlocks) then - Add(congs, - RZMSCongruenceByLinkedTripleNC(S, n, colBlocks, rowBlocks)); - fi; - od; + # Pairs that generate the columns relation + col_pairs := []; + for bl in C!.colBlocks do + for j in [2 .. Size(bl)] do + Add(col_pairs, [bl[1], bl[j]]); od; od; - return congs; -end); - -InstallMethod(IsLinkedTriple, -"for Rees matrix semigroup, group, and two lists", -[IsReesMatrixSemigroup, - IsGroup, IsDenseList, IsDenseList], -function(S, n, colBlocks, rowBlocks) - local mat, block, bi, bj, i, j, u, v, bu, bv; - # Check the semigroup is valid - if not IsFinite(S) then - ErrorNoReturn("the 1st argument (a Rees matrix semigroup) is not finite"); - elif not IsSimpleSemigroup(S) then - ErrorNoReturn("the 1st argument (a Rees matrix semigroup) is not simple"); - fi; - mat := Matrix(S); - # Check axiom (L2) from Howie p.86, then call NC function - # Go through the column blocks - for block in colBlocks do - # Check q-condition for all pairs of columns in this block (L2) - for bi in [1 .. Size(block)] do - for bj in [bi + 1 .. Size(block)] do - i := block[bi]; - j := block[bj]; - # Check all pairs of rows (u,v) - for u in [1 .. Size(mat)] do - for v in [u + 1 .. Size(mat)] do - if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) - in n then - return false; - fi; - od; - od; - od; + # Pairs that generate the rows relation + row_pairs := []; + for bl in C!.rowBlocks do + for j in [2 .. Size(bl)] do + Add(row_pairs, [bl[1], bl[j]]); od; od; - # Go through the row blocks - for block in rowBlocks do - # Check q-condition for all pairs of rows in this block (L2) - for bu in [1 .. Size(block)] do - for bv in [bu + 1 .. Size(block)] do - u := block[bu]; - v := block[bv]; - # Check all pairs of columns (i,j) - for i in [1 .. Size(mat[1])] do - for j in [i + 1 .. Size(mat[1])] do - if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) - in n then - return false; - fi; - od; - od; - od; - od; + # Collate into RMS element pairs + max := Maximum(Length(n_gens), Length(col_pairs), Length(row_pairs)); + pairs := EmptyPlist(max); + # Set default values in case a list has length 0 + n := One(G); + cp := [1, 1]; + rp := [1, 1]; + # Iterate through all 3 lists together + for pair_no in [1 .. max] do + # If any list has run out, just keep using the last entry or default value + if pair_no <= Length(n_gens) then + n := n_gens[pair_no]; + fi; + if pair_no <= Length(col_pairs) then + cp := col_pairs[pair_no]; + fi; + if pair_no <= Length(row_pairs) then + rp := row_pairs[pair_no]; + fi; + # Choose r and c s.t. m[r][cp[1]] and m[rp[1]][c] are both non-zero + r := First([1 .. Length(m)], r -> m[r][cp[1]] <> 0); + c := First([1 .. Length(m[1])], c -> m[rp[1]][c] <> 0); + # Make the pair and add it + pairs[pair_no] := + [RMSElement(S, cp[1], m[r][cp[1]] ^ -1 * n * m[rp[1]][c] ^ -1, rp[1]), + RMSElement(S, cp[2], m[r][cp[2]] ^ -1 * m[rp[2]][c] ^ -1, rp[2])]; od; - return true; + return pairs; end); -InstallMethod(IsLinkedTriple, -"for Rees 0-matrix semigroup, group, and two lists", -[IsReesZeroMatrixSemigroup, - IsGroup, IsDenseList, IsDenseList], -function(S, n, colBlocks, rowBlocks) - local mat, block, i, j, u, v, bi, bj, bu, bv; - # Check the semigroup is valid - if not IsFinite(S) then - ErrorNoReturn("the 1st argument (a Rees 0-matrix semigroup) is not finite"); - elif not IsZeroSimpleSemigroup(S) then - ErrorNoReturn("the 1st argument (a Rees 0-matrix semigroup) is ", - "not 0-simple"); - fi; +InstallMethod(SemigroupCongruenceByGeneratingPairs, +"for Rees matrix semigroup and list of pairs", +[IsReesMatrixSemigroup, IsHomogeneousList], +ToBeat([IsReesMatrixSemigroup, IsHomogeneousList], + [IsSemigroup and CanUseLibsemigroupsCongruences, IsList and IsEmpty], + [IsSimpleSemigroup, IsHomogeneousList]) ++ +ToBeat([IsSimpleSemigroup, IsHomogeneousList], + [IsSemigroup and CanUseLibsemigroupsCongruences, + IsList and IsEmpty]), +function(S, pairs) + local g, mat, colLookup, rowLookup, n, C, pair, v, j; + + g := UnderlyingSemigroup(S); mat := Matrix(S); - # Check axioms (L1) and (L2) from Howie p.86, then call NC function - # Go through the column blocks - for block in colBlocks do - for bj in [2 .. Size(block)] do - # Check columns have zeroes in all the same rows (L1) - for u in [1 .. Size(mat)] do - if (mat[u][block[1]] = 0) <> (mat[u][block[bj]] = 0) then - return false; - fi; - od; - od; - # Check q-condition for all pairs of columns in this block (L2) - for bi in [1 .. Size(block)] do - for bj in [bi + 1 .. Size(block)] do - i := block[bi]; - j := block[bj]; - # Check all pairs of rows (u,v) - for u in [1 .. Size(mat)] do - if mat[u][i] = 0 then - continue; - fi; - for v in [u + 1 .. Size(mat)] do - if mat[v][i] = 0 then - continue; - fi; - if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) - in n then - return false; - fi; - od; - od; - od; - od; - od; - # Go through the row blocks - for block in rowBlocks do - for bv in [2 .. Size(block)] do - # Check rows have zeroes in all the same columns (L1) - for i in [1 .. Size(mat[1])] do - if (mat[block[1]][i] = 0) <> (mat[block[bv]][i] = 0) then - return false; - fi; - od; + # Union-Find data structure for the column and row equivalences + colLookup := PartitionDS(IsPartitionDS, Size(mat[1])); + rowLookup := PartitionDS(IsPartitionDS, Size(mat)); + + # Normal subgroup + n := Subgroup(g, []); + + for pair in pairs do + # If this pair adds no information, ignore it + if pair[1] = pair[2] then + continue; + fi; + + # Associate the columns and rows + Unite(colLookup, pair[1][1], pair[2][1]); + Unite(rowLookup, pair[1][3], pair[2][3]); + + # Associate group entries in the normal subgroup + n := ClosureGroup(n, LinkedElement(pair[1]) * LinkedElement(pair[2]) ^ -1); + + # Ensure linkedness + for v in [2 .. Size(mat)] do + n := ClosureGroup(n, mat[1][pair[1][1]] + * mat[v][pair[1][1]] ^ -1 + * mat[v][pair[2][1]] + * mat[1][pair[2][1]] ^ -1); od; - # Check q-condition for all pairs of rows in this block (L2) - for bu in [1 .. Size(block)] do - for bv in [bu + 1 .. Size(block)] do - u := block[bu]; - v := block[bv]; - # Check all pairs of columns (i,j) - for i in [1 .. Size(mat[1])] do - if mat[u][i] = 0 then - continue; - fi; - for j in [i + 1 .. Size(mat[1])] do - if mat[u][j] = 0 then - continue; - fi; - if not (mat[u][i] * mat[v][i] ^ -1 * mat[v][j] * mat[u][j] ^ -1) - in n then - return false; - fi; - od; - od; - od; + for j in [2 .. Size(mat[1])] do + n := ClosureGroup(n, mat[pair[1][3]][1] + * mat[pair[2][3]][1] ^ -1 + * mat[pair[2][3]][j] + * mat[pair[1][3]][j] ^ -1); od; od; - return true; + + # Make n normal + n := NormalClosure(g, n); + C := RMSCongruenceByLinkedTriple(S, + n, + PartsOfPartitionDS(colLookup), + PartsOfPartitionDS(rowLookup)); + SetGeneratingPairsOfMagmaCongruence(C, pairs); + return C; end); -BindGlobal("LinkedElement", -function(x) - local mat, i, u, v, j; - mat := Matrix(ReesMatrixSemigroupOfFamily(FamilyObj(x))); - i := x[1]; # Column no - u := x[3]; # Row no - if IsReesMatrixSemigroupElement(x) then - # RMS case - return mat[1][i] * x[2] * mat[u][1]; - else - # RZMS case - for v in [1 .. Size(mat)] do - if mat[v][i] <> 0 then - break; +InstallMethod(SemigroupCongruenceByGeneratingPairs, +"for a Rees 0-matrix semigroup and list of pairs", +[IsReesZeroMatrixSemigroup, IsHomogeneousList], +ToBeat([IsReesZeroMatrixSemigroup, IsHomogeneousList], + [IsSemigroup and CanUseLibsemigroupsCongruences, IsList and IsEmpty], + [IsZeroSimpleSemigroup, IsHomogeneousList]) ++ +ToBeat([IsZeroSimpleSemigroup, IsHomogeneousList], + [IsSemigroup and CanUseLibsemigroupsCongruences, + IsList and IsEmpty]), +function(S, pairs) + local g, mat, colLookup, rowLookup, n, u, i, C, pair, v, j; + + g := UnderlyingSemigroup(S); + mat := Matrix(S); + + # Union-Find data structure for the column and row equivalences + colLookup := PartitionDS(IsPartitionDS, Size(mat[1])); + rowLookup := PartitionDS(IsPartitionDS, Size(mat)); + + # Normal subgroup + n := Subgroup(g, []); + + for pair in pairs do + # If this pair adds no information, ignore it + if pair[1] = pair[2] then + continue; + fi; + + # Does this relate any non-zero elements to zero? + if pair[1] = MultiplicativeZero(S) + or pair[2] = MultiplicativeZero(S) + or ForAny([1 .. Size(mat)], + u -> (mat[u][pair[1][1]] = 0) + <> (mat[u][pair[2][1]] = 0)) + or ForAny([1 .. Size(mat[1])], + i -> (mat[pair[1][3]][i] = 0) + <> (mat[pair[2][3]][i] = 0)) then + return UniversalSemigroupCongruence(S); + fi; + + # Associate the columns and rows + Unite(colLookup, pair[1][1], pair[2][1]); + Unite(rowLookup, pair[1][3], pair[2][3]); + + # Associate group entries in the normal subgroup + n := ClosureGroup(n, LinkedElement(pair[1]) * LinkedElement(pair[2]) ^ -1); + + # Ensure linkedness + u := PositionProperty([1 .. Size(mat)], u -> mat[u][pair[1][1]] <> 0); + for v in [u + 1 .. Size(mat)] do + if mat[v][pair[1][1]] = 0 then + continue; fi; + n := ClosureGroup(n, mat[u][pair[1][1]] + * mat[v][pair[1][1]] ^ -1 + * mat[v][pair[2][1]] + * mat[u][pair[2][1]] ^ -1); od; - for j in [1 .. Size(mat[1])] do - if mat[u][j] <> 0 then - break; + i := PositionProperty([1 .. Size(mat[1])], k -> mat[pair[1][3]][k] <> 0); + for j in [i + 1 .. Size(mat[1])] do + if mat[pair[1][3]][j] = 0 then + continue; fi; + n := ClosureGroup(n, mat[pair[1][3]][i] + * mat[pair[2][3]][i] ^ -1 + * mat[pair[2][3]][j] + * mat[pair[1][3]][j] ^ -1); od; - return mat[v][i] * x[2] * mat[u][j]; - fi; + od; + + n := NormalClosure(g, n); + C := RZMSCongruenceByLinkedTriple(S, + n, + PartsOfPartitionDS(colLookup), + PartsOfPartitionDS(rowLookup)); + SetGeneratingPairsOfMagmaCongruence(C, pairs); + return C; end); -InstallMethod(\=, -"for two Rees matrix semigroup congruences by linked triple", -[IsRMSCongruenceByLinkedTriple, IsRMSCongruenceByLinkedTriple], -function(lhop, rhop) - return(Range(lhop) = Range(rhop) and - lhop!.n = rhop!.n and - lhop!.colBlocks = rhop!.colBlocks and - lhop!.rowBlocks = rhop!.rowBlocks); +############################################################################# +# 4. Congruences: printing and viewing +############################################################################# + +InstallMethod(ViewObj, +"for Rees matrix semigroup congruence by linked triple", +[IsRMSCongruenceByLinkedTriple], +function(C) + Print(""); end); -InstallMethod(\=, -"for two Rees 0-matrix semigroup congruences by linked triple", -[IsRZMSCongruenceByLinkedTriple, IsRZMSCongruenceByLinkedTriple], -function(lhop, rhop) - return(Range(lhop) = Range(rhop) and - lhop!.n = rhop!.n and - lhop!.colBlocks = rhop!.colBlocks and - lhop!.rowBlocks = rhop!.rowBlocks); +InstallMethod(ViewObj, +"for Rees zero-matrix semigroup congruence by linked triple", +[IsRZMSCongruenceByLinkedTriple], +function(C) + Print(""); end); -InstallMethod(IsSubrelation, -"for two Rees matrix semigroup congruences by linked triple", -[IsRMSCongruenceByLinkedTriple, IsRMSCongruenceByLinkedTriple], -function(lhop, rhop) - # Tests whether rhop is a subcongruence of lhop - if Range(lhop) <> Range(rhop) then - Error("the 1st and 2nd arguments are congruences over different", - " semigroups"); +############################################################################# +# 5. Congruences: mandatory methods for CanComputeEquivalenceRelationPartition +############################################################################# + +BindGlobal("_EquivalenceRelationPartition", +function(C) + local S, n, elms, table, next, part, class, i, x; + + S := Range(C); + n := Size(S); + elms := EnumeratorCanonical(S); + table := EmptyPlist(n); + next := 1; + part := []; + for i in [1 .. n] do + if not IsBound(table[i]) then + class := ImagesElm(C, elms[i]); + for x in class do + table[PositionCanonical(S, x)] := next; + od; + Add(part, class); + next := next + 1; + fi; + od; + SetEquivalenceRelationCanonicalLookup(C, table); + return part; +end); + +InstallMethod(EquivalenceRelationPartitionWithSingletons, +"for Rees matrix semigroup congruence by linked triple", +[IsRMSCongruenceByLinkedTriple], +_EquivalenceRelationPartition); + +InstallMethod(EquivalenceRelationPartitionWithSingletons, +"for Rees 0-matrix semigroup congruence by linked triple", +[IsRZMSCongruenceByLinkedTriple], +_EquivalenceRelationPartition); + +MakeReadWriteGlobal("_EquivalenceRelationPartition"); +UnbindGlobal("_EquivalenceRelationPartition"); + +InstallMethod(ImagesElm, +"for Rees matrix semigroup congruence by linked triple and element", +[IsRMSCongruenceByLinkedTriple, IsReesMatrixSemigroupElement], +function(C, x) + local S, mat, images, i, a, u, j, v, nElm, b; + S := Range(C); + mat := Matrix(S); + if not x in S then + ErrorNoReturn("the 2nd argument (an element of a Rees matrix ", + "semigroup) does not belong to the range of the ", + "1st argument (a congruence)"); fi; - return IsSubgroup(lhop!.n, rhop!.n) - and ForAll(rhop!.colBlocks, - b2 -> ForAny(lhop!.colBlocks, b1 -> IsSubset(b1, b2))) - and ForAll(rhop!.rowBlocks, - b2 -> ForAny(lhop!.rowBlocks, b1 -> IsSubset(b1, b2))); + # List of all elements congruent to x under C + images := []; + # Read the element as (i,a,u) + i := x[1]; + a := x[2]; + u := x[3]; + # Construct congruent elements as (j,b,v) + for j in C!.colBlocks[C!.colLookup[i]] do + for v in C!.rowBlocks[C!.rowLookup[u]] do + for nElm in C!.n do + # Might be better to use congruence classes after all + b := mat[1][j] ^ -1 * nElm * mat[1][i] * a * mat[u][1] + * mat[v][1] ^ -1; + Add(images, RMSElement(S, j, b, v)); + od; + od; + od; + return images; end); -InstallMethod(IsSubrelation, -"for two Rees 0-matrix semigroup congruences by linked triple", -[IsRZMSCongruenceByLinkedTriple, IsRZMSCongruenceByLinkedTriple], -function(lhop, rhop) - # Tests whether rhop is a subcongruence of lhop - if Range(lhop) <> Range(rhop) then - Error("the 1st and 2nd arguments are congruences over different", - " semigroups"); +InstallMethod(ImagesElm, +"for Rees 0-matrix semigroup congruence by linked triple and element", +[IsRZMSCongruenceByLinkedTriple, IsReesZeroMatrixSemigroupElement], +function(C, x) + local S, mat, images, i, a, u, row, col, j, b, v, nElm; + S := Range(C); + mat := Matrix(S); + if not x in S then + ErrorNoReturn("the 2nd argument (an element of a Rees 0-matrix ", + "semigroup) does not belong to the range of the ", + "1st argument (a congruence)"); fi; - return IsSubgroup(lhop!.n, rhop!.n) - and ForAll(rhop!.colBlocks, - b2 -> ForAny(lhop!.colBlocks, b1 -> IsSubset(b1, b2))) - and ForAll(rhop!.rowBlocks, - b2 -> ForAny(lhop!.rowBlocks, b1 -> IsSubset(b1, b2))); + # Special case for 0 + if x = MultiplicativeZero(S) then + return [x]; + fi; + # List of all elements congruent to x under C + images := []; + # Read the element as (i,a,u) + i := x[1]; + a := x[2]; + u := x[3]; + # Find a non-zero row for this class of columns + for row in [1 .. Size(mat)] do + if mat[row][i] <> 0 then + break; + fi; + od; + # Find a non-zero column for this class of rows + for col in [1 .. Size(mat[1])] do + if mat[u][col] <> 0 then + break; + fi; + od; + + # Construct congruent elements as (j,b,v) + for j in C!.colBlocks[C!.colLookup[i]] do + for v in C!.rowBlocks[C!.rowLookup[u]] do + for nElm in C!.n do + # Might be better to use congruence classes after all + b := mat[row][j] ^ -1 + * nElm + * mat[row][i] + * a + * mat[u][col] + * mat[v][col] ^ -1; + Add(images, RMSElement(S, j, b, v)); + od; + od; + od; + return images; end); InstallMethod(CongruenceTestMembershipNC, @@ -592,91 +821,68 @@ function(C, lhop, rhop) return gpElm in C!.n; end); -InstallMethod(ImagesElm, -"for Rees matrix semigroup congruence by linked triple and element", -[IsRMSCongruenceByLinkedTriple, IsReesMatrixSemigroupElement], -function(C, x) - local S, mat, images, i, a, u, j, v, nElm, b; - S := Range(C); - mat := Matrix(S); - if not x in S then - ErrorNoReturn("the 2nd argument (an element of a Rees matrix ", - "semigroup) does not belong to the range of the ", - "1st argument (a congruence)"); - fi; - # List of all elements congruent to x under C - images := []; - # Read the element as (i,a,u) - i := x[1]; - a := x[2]; - u := x[3]; - # Construct congruent elements as (j,b,v) - for j in C!.colBlocks[C!.colLookup[i]] do - for v in C!.rowBlocks[C!.rowLookup[u]] do - for nElm in C!.n do - # Might be better to use congruence classes after all - b := mat[1][j] ^ -1 * nElm * mat[1][i] * a * mat[u][1] - * mat[v][1] ^ -1; - Add(images, RMSElement(S, j, b, v)); - od; - od; - od; - return images; +######################################################################## +# 6. Congruences: non-mandatory methods for +# CanComputeEquivalenceRelationPartition, where we have a superior method +# for this particular representation. +######################################################################## + +# Comparison operators + +InstallMethod(\=, +"for two Rees matrix semigroup congruences by linked triple", +[IsRMSCongruenceByLinkedTriple, IsRMSCongruenceByLinkedTriple], +function(lhop, rhop) + return(Range(lhop) = Range(rhop) and + lhop!.n = rhop!.n and + lhop!.colBlocks = rhop!.colBlocks and + lhop!.rowBlocks = rhop!.rowBlocks); +end); + +InstallMethod(\=, +"for two Rees 0-matrix semigroup congruences by linked triple", +[IsRZMSCongruenceByLinkedTriple, IsRZMSCongruenceByLinkedTriple], +function(lhop, rhop) + return(Range(lhop) = Range(rhop) and + lhop!.n = rhop!.n and + lhop!.colBlocks = rhop!.colBlocks and + lhop!.rowBlocks = rhop!.rowBlocks); end); -InstallMethod(ImagesElm, -"for Rees 0-matrix semigroup congruence by linked triple and element", -[IsRZMSCongruenceByLinkedTriple, IsReesZeroMatrixSemigroupElement], -function(C, x) - local S, mat, images, i, a, u, row, col, j, b, v, nElm; - S := Range(C); - mat := Matrix(S); - if not x in S then - ErrorNoReturn("the 2nd argument (an element of a Rees 0-matrix ", - "semigroup) does not belong to the range of the ", - "1st argument (a congruence)"); - fi; - # Special case for 0 - if x = MultiplicativeZero(S) then - return [x]; +InstallMethod(IsSubrelation, +"for two Rees matrix semigroup congruences by linked triple", +[IsRMSCongruenceByLinkedTriple, IsRMSCongruenceByLinkedTriple], +function(lhop, rhop) + # Tests whether rhop is a subcongruence of lhop + if Range(lhop) <> Range(rhop) then + Error("the 1st and 2nd arguments are congruences over different", + " semigroups"); fi; - # List of all elements congruent to x under C - images := []; - # Read the element as (i,a,u) - i := x[1]; - a := x[2]; - u := x[3]; - # Find a non-zero row for this class of columns - for row in [1 .. Size(mat)] do - if mat[row][i] <> 0 then - break; - fi; - od; - # Find a non-zero column for this class of rows - for col in [1 .. Size(mat[1])] do - if mat[u][col] <> 0 then - break; - fi; - od; + return IsSubgroup(lhop!.n, rhop!.n) + and ForAll(rhop!.colBlocks, + b2 -> ForAny(lhop!.colBlocks, b1 -> IsSubset(b1, b2))) + and ForAll(rhop!.rowBlocks, + b2 -> ForAny(lhop!.rowBlocks, b1 -> IsSubset(b1, b2))); +end); - # Construct congruent elements as (j,b,v) - for j in C!.colBlocks[C!.colLookup[i]] do - for v in C!.rowBlocks[C!.rowLookup[u]] do - for nElm in C!.n do - # Might be better to use congruence classes after all - b := mat[row][j] ^ -1 - * nElm - * mat[row][i] - * a - * mat[u][col] - * mat[v][col] ^ -1; - Add(images, RMSElement(S, j, b, v)); - od; - od; - od; - return images; +InstallMethod(IsSubrelation, +"for two Rees 0-matrix semigroup congruences by linked triple", +[IsRZMSCongruenceByLinkedTriple, IsRZMSCongruenceByLinkedTriple], +function(lhop, rhop) + # Tests whether rhop is a subcongruence of lhop + if Range(lhop) <> Range(rhop) then + Error("the 1st and 2nd arguments are congruences over different", + " semigroups"); + fi; + return IsSubgroup(lhop!.n, rhop!.n) + and ForAll(rhop!.colBlocks, + b2 -> ForAny(lhop!.colBlocks, b1 -> IsSubset(b1, b2))) + and ForAll(rhop!.rowBlocks, + b2 -> ForAny(lhop!.rowBlocks, b1 -> IsSubset(b1, b2))); end); +# EquivalenceClasses + InstallMethod(EquivalenceClasses, "for Rees matrix semigroup congruence by linked triple", [IsRMSCongruenceByLinkedTriple], @@ -754,19 +960,7 @@ function(C) + 1); # Class containing zero end); -InstallMethod(Enumerator, -"for RMS congruence class by linked triple", -[IsRMSCongruenceClassByLinkedTriple], -function(class) - return ImagesElm(EquivalenceClassRelation(class), Representative(class)); -end); - -InstallMethod(Enumerator, -"for RZMS congruence class by linked triple", -[IsRZMSCongruenceClassByLinkedTriple], -function(class) - return ImagesElm(EquivalenceClassRelation(class), Representative(class)); -end); +# Algebraic operators InstallMethod(JoinSemigroupCongruences, "for two Rees matrix semigroup congruences by linked triple", @@ -942,6 +1136,11 @@ function(lhop, rhop) return RZMSCongruenceByLinkedTripleNC(Range(lhop), n, colBlocks, rowBlocks); end); +############################################################################### +# 8. Equivalence classes: mandatory methods for +# CanComputeEquivalenceRelationPartition +############################################################################### + InstallMethod(RMSCongruenceClassByLinkedTriple, "for semigroup congruence by linked triple, a coset and two positive integers", [IsRMSCongruenceByLinkedTriple, IsRightCoset, IsPosInt, IsPosInt], @@ -1008,6 +1207,11 @@ function(C, nCoset, colClass, rowClass) return class; end); +############################################################################### +# 7. Equivalence classes - mandatory methods for +# CanComputeEquivalenceRelationPartition +############################################################################### + InstallMethod(EquivalenceClassOfElementNC, "for Rees matrix semigroup congruence by linked triple and element", [IsRMSCongruenceByLinkedTriple, IsReesMatrixSemigroupElement], @@ -1051,6 +1255,10 @@ function(C, x) return class; end); +############################################################################### +# 7. Equivalence classes - superior representation specific methods +############################################################################### + InstallMethod(\in, "for Rees matrix semigroup element and a congruence class by linked triple", [IsReesMatrixSemigroupElement, IsRMSCongruenceClassByLinkedTriple], @@ -1074,12 +1282,14 @@ function(x, class) # Special case for 0 and {0} if class!.nCoset = 0 then return x = MultiplicativeZero(S); + elif IsMultiplicativeZero(S, x) then + return false; fi; # Otherwise - return(x in S and - C!.colLookup[x[1]] = class!.colClass and - C!.rowLookup[x[3]] = class!.rowClass and - LinkedElement(x) in class!.nCoset); + return x in S + and C!.colLookup[x[1]] = class!.colClass + and C!.rowLookup[x[3]] = class!.rowClass + and LinkedElement(x) in class!.nCoset; end); InstallMethod(Size, @@ -1174,310 +1384,143 @@ function(class) if mat[v][i] <> 0 then break; fi; - od; - for j in [1 .. Size(mat[1])] do - if mat[u][j] <> 0 then - break; - fi; - od; - a := mat[v][i] ^ -1 - * CanonicalRightCosetElement(C!.n, Representative(class!.nCoset)) - * mat[u][j] ^ -1; - return RMSElement(S, i, a, u); -end); - -InstallMethod(GeneratingPairsOfMagmaCongruence, -"for Rees matrix semigroup congruence by linked triple", -[IsRMSCongruenceByLinkedTriple], -function(C) - local S, G, m, pairs, n_gens, col_pairs, bl, j, row_pairs, max, n, cp, rp, - pair_no, r, c; - S := Range(C); - G := UnderlyingSemigroup(S); - m := Matrix(S); - pairs := []; - - # Normal subgroup elements to identify with id - n_gens := GeneratorsOfSemigroup(C!.n); - - # Pairs that generate the columns relation - col_pairs := []; - for bl in C!.colBlocks do - for j in [2 .. Size(bl)] do - Add(col_pairs, [bl[1], bl[j]]); - od; - od; - - # Pairs that generate the rows relation - row_pairs := []; - for bl in C!.rowBlocks do - for j in [2 .. Size(bl)] do - Add(row_pairs, [bl[1], bl[j]]); - od; - od; - - # Collate into RMS element pairs - max := Maximum(Length(n_gens), Length(col_pairs), Length(row_pairs)); - pairs := EmptyPlist(max); - # Set default values in case a list has length 0 - n := One(G); - cp := [1, 1]; - rp := [1, 1]; - # Iterate through all 3 lists together - for pair_no in [1 .. max] do - # If any list has run out, just keep using the last entry or default value - if pair_no <= Length(n_gens) then - n := n_gens[pair_no]; - fi; - if pair_no <= Length(col_pairs) then - cp := col_pairs[pair_no]; - fi; - if pair_no <= Length(row_pairs) then - rp := row_pairs[pair_no]; - fi; - # Choose r and c s.t. m[r][cp[1]] and m[rp[1]][c] are both non-zero - # (since there are no zeroes, we can just use 1 for both) - r := 1; - c := 1; - # Make the pair and add it - pairs[pair_no] := - [RMSElement(S, cp[1], m[r][cp[1]] ^ -1 * n * m[rp[1]][c] ^ -1, rp[1]), - RMSElement(S, cp[2], m[r][cp[2]] ^ -1 * m[rp[2]][c] ^ -1, rp[2])]; - od; - return pairs; -end); - -InstallMethod(GeneratingPairsOfMagmaCongruence, -"for Rees 0-matrix semigroup congruence by linked triple", -[IsRZMSCongruenceByLinkedTriple], -function(C) - local S, G, m, pairs, n_gens, col_pairs, bl, j, row_pairs, max, n, cp, rp, - pair_no, r, c; - S := Range(C); - G := UnderlyingSemigroup(S); - m := Matrix(S); - pairs := []; - - # Normal subgroup elements to identify with id - n_gens := GeneratorsOfSemigroup(C!.n); - - # Pairs that generate the columns relation - col_pairs := []; - for bl in C!.colBlocks do - for j in [2 .. Size(bl)] do - Add(col_pairs, [bl[1], bl[j]]); - od; - od; - - # Pairs that generate the rows relation - row_pairs := []; - for bl in C!.rowBlocks do - for j in [2 .. Size(bl)] do - Add(row_pairs, [bl[1], bl[j]]); - od; - od; - - # Collate into RMS element pairs - max := Maximum(Length(n_gens), Length(col_pairs), Length(row_pairs)); - pairs := EmptyPlist(max); - # Set default values in case a list has length 0 - n := One(G); - cp := [1, 1]; - rp := [1, 1]; - # Iterate through all 3 lists together - for pair_no in [1 .. max] do - # If any list has run out, just keep using the last entry or default value - if pair_no <= Length(n_gens) then - n := n_gens[pair_no]; - fi; - if pair_no <= Length(col_pairs) then - cp := col_pairs[pair_no]; - fi; - if pair_no <= Length(row_pairs) then - rp := row_pairs[pair_no]; + od; + for j in [1 .. Size(mat[1])] do + if mat[u][j] <> 0 then + break; fi; - # Choose r and c s.t. m[r][cp[1]] and m[rp[1]][c] are both non-zero - r := First([1 .. Length(m)], r -> m[r][cp[1]] <> 0); - c := First([1 .. Length(m[1])], c -> m[rp[1]][c] <> 0); - # Make the pair and add it - pairs[pair_no] := - [RMSElement(S, cp[1], m[r][cp[1]] ^ -1 * n * m[rp[1]][c] ^ -1, rp[1]), - RMSElement(S, cp[2], m[r][cp[2]] ^ -1 * m[rp[2]][c] ^ -1, rp[2])]; od; - return pairs; + a := mat[v][i] ^ -1 + * CanonicalRightCosetElement(C!.n, Representative(class!.nCoset)) + * mat[u][j] ^ -1; + return RMSElement(S, i, a, u); end); -InstallMethod(SemigroupCongruenceByGeneratingPairs, -"for Rees matrix semigroup and list of pairs", -[IsReesMatrixSemigroup, IsHomogeneousList], -ToBeat([IsReesMatrixSemigroup, IsHomogeneousList], - [IsSemigroup and CanUseLibsemigroupsCongruences, IsList and IsEmpty], - [IsSimpleSemigroup, IsHomogeneousList]) -+ -ToBeat([IsSimpleSemigroup, IsHomogeneousList], - [IsSemigroup and CanUseLibsemigroupsCongruences, - IsList and IsEmpty]), -function(S, pairs) - local g, mat, colLookup, rowLookup, n, C, pair, v, j; - - g := UnderlyingSemigroup(S); - mat := Matrix(S); +############################################################################### +# 9. Congruence lattice +############################################################################### - # Union-Find data structure for the column and row equivalences - colLookup := PartitionDS(IsPartitionDS, Size(mat[1])); - rowLookup := PartitionDS(IsPartitionDS, Size(mat)); +InstallMethod(CongruencesOfSemigroup, +"for finite simple Rees matrix semigroup", +[IsReesMatrixSemigroup and IsSimpleSemigroup and IsFinite], +function(S) + local subpartitions, congs, mat, g, colBlocksList, + rowBlocksList, n, colBlocks, rowBlocks; - # Normal subgroup - n := Subgroup(g, []); + # Function to compute all subsets of a relation given by partitions + subpartitions := function(part) + local l; + # Replace each class with a list of all partitions of that class + l := List(part, PartitionsSet); + # Produce all the combinations of partitions of classes + l := Cartesian(l); + # Concatenate these lists to produce complete partitions of the set + l := List(l, Concatenation); + # Finally sort each of these into the canonical order of its new classes + return List(l, SSortedList); + end; - for pair in pairs do - # If this pair adds no information, ignore it - if pair[1] = pair[2] then - continue; - fi; + congs := []; + mat := Matrix(S); + g := UnderlyingSemigroup(S); - # Associate the columns and rows - Unite(colLookup, pair[1][1], pair[2][1]); - Unite(rowLookup, pair[1][3], pair[2][3]); + # No need to add the universal congruence - # Associate group entries in the normal subgroup - n := ClosureGroup(n, LinkedElement(pair[1]) * LinkedElement(pair[2]) ^ -1); + # Compute all column and row relations which are subsets of the max relations + colBlocksList := subpartitions([[1 .. Size(mat[1])]]); + rowBlocksList := subpartitions([[1 .. Size(mat)]]); - # Ensure linkedness - for v in [2 .. Size(mat)] do - n := ClosureGroup(n, mat[1][pair[1][1]] - * mat[v][pair[1][1]] ^ -1 - * mat[v][pair[2][1]] - * mat[1][pair[2][1]] ^ -1); - od; - for j in [2 .. Size(mat[1])] do - n := ClosureGroup(n, mat[pair[1][3]][1] - * mat[pair[2][3]][1] ^ -1 - * mat[pair[2][3]][j] - * mat[pair[1][3]][j] ^ -1); + # Go through all triples and check + for n in NormalSubgroups(g) do + for colBlocks in colBlocksList do + for rowBlocks in rowBlocksList do + if IsLinkedTriple(S, n, colBlocks, rowBlocks) then + Add(congs, + RMSCongruenceByLinkedTripleNC(S, n, colBlocks, rowBlocks)); + fi; + od; od; od; - # Make n normal - n := NormalClosure(g, n); - C := RMSCongruenceByLinkedTriple(S, - n, - PartsOfPartitionDS(colLookup), - PartsOfPartitionDS(rowLookup)); - SetGeneratingPairsOfMagmaCongruence(C, pairs); - return C; + return congs; end); -InstallMethod(SemigroupCongruenceByGeneratingPairs, -"for a Rees 0-matrix semigroup and list of pairs", -[IsReesZeroMatrixSemigroup, IsHomogeneousList], -ToBeat([IsReesZeroMatrixSemigroup, IsHomogeneousList], - [IsSemigroup and CanUseLibsemigroupsCongruences, IsList and IsEmpty], - [IsZeroSimpleSemigroup, IsHomogeneousList]) -+ -ToBeat([IsZeroSimpleSemigroup, IsHomogeneousList], - [IsSemigroup and CanUseLibsemigroupsCongruences, - IsList and IsEmpty]), -function(S, pairs) - local g, mat, colLookup, rowLookup, n, u, i, C, pair, v, j; - - g := UnderlyingSemigroup(S); - mat := Matrix(S); - - # Union-Find data structure for the column and row equivalences - colLookup := PartitionDS(IsPartitionDS, Size(mat[1])); - rowLookup := PartitionDS(IsPartitionDS, Size(mat)); +InstallMethod(CongruencesOfSemigroup, +"for finite 0-simple Rees 0-matrix semigroup", +[IsReesZeroMatrixSemigroup and IsZeroSimpleSemigroup and IsFinite], +function(S) + local congs, mat, g, AddRelation, maxColBlocks, maxRowBlocks, + i, j, u, v, n, colBlocks, rowBlocks, colBlocksList, rowBlocksList, + subpartitions; - # Normal subgroup - n := Subgroup(g, []); + # Function to compute all subsets of a relation given by partitions + subpartitions := function(part) + local l; + # Replace each class with a list of all partitions of that class + l := List(part, PartitionsSet); + # Produce all the combinations of partitions of classes + l := Cartesian(l); + # Concatenate these lists to produce complete partitions of the set + l := List(l, Concatenation); + # Finally sort each of these into the canonical order of its new classes + return List(l, SSortedList); + end; - for pair in pairs do - # If this pair adds no information, ignore it - if pair[1] = pair[2] then - continue; - fi; + congs := []; + mat := Matrix(S); + g := UnderlyingSemigroup(S); - # Does this relate any non-zero elements to zero? - if pair[1] = MultiplicativeZero(S) - or pair[2] = MultiplicativeZero(S) - or ForAny([1 .. Size(mat)], - u -> (mat[u][pair[1][1]] = 0) - <> (mat[u][pair[2][1]] = 0)) - or ForAny([1 .. Size(mat[1])], - i -> (mat[pair[1][3]][i] = 0) - <> (mat[pair[2][3]][i] = 0)) then - return UniversalSemigroupCongruence(S); + # This function combines two congruence classes + AddRelation := function(R, x, y) + local xClass, yClass; + xClass := PositionProperty(R, class -> x in class); + yClass := PositionProperty(R, class -> y in class); + if xClass <> yClass then + Append(R[xClass], R[yClass]); + Remove(R, yClass); fi; + end; - # Associate the columns and rows - Unite(colLookup, pair[1][1], pair[2][1]); - Unite(rowLookup, pair[1][3], pair[2][3]); - - # Associate group entries in the normal subgroup - n := ClosureGroup(n, LinkedElement(pair[1]) * LinkedElement(pair[2]) ^ -1); - - # Ensure linkedness - u := PositionProperty([1 .. Size(mat)], u -> mat[u][pair[1][1]] <> 0); - for v in [u + 1 .. Size(mat)] do - if mat[v][pair[1][1]] = 0 then - continue; + # Construct maximum column relation + maxColBlocks := List([1 .. Size(mat[1])], i -> [i]); + for i in [1 .. Size(mat[1])] do + for j in [i + 1 .. Size(mat[1])] do + if ForAll([1 .. Size(mat)], + u -> ((mat[u][i] = 0) = (mat[u][j] = 0))) then + AddRelation(maxColBlocks, i, j); fi; - n := ClosureGroup(n, mat[u][pair[1][1]] - * mat[v][pair[1][1]] ^ -1 - * mat[v][pair[2][1]] - * mat[u][pair[2][1]] ^ -1); od; - i := PositionProperty([1 .. Size(mat[1])], k -> mat[pair[1][3]][k] <> 0); - for j in [i + 1 .. Size(mat[1])] do - if mat[pair[1][3]][j] = 0 then - continue; + od; + + # Construct maximum row relation + maxRowBlocks := List([1 .. Size(mat)], u -> [u]); + for u in [1 .. Size(mat)] do + for v in [u + 1 .. Size(mat)] do + if ForAll([1 .. Size(mat[1])], + i -> ((mat[u][i] = 0) = (mat[v][i] = 0))) then + AddRelation(maxRowBlocks, u, v); fi; - n := ClosureGroup(n, mat[pair[1][3]][i] - * mat[pair[2][3]][i] ^ -1 - * mat[pair[2][3]][j] - * mat[pair[1][3]][j] ^ -1); od; od; - n := NormalClosure(g, n); - C := RZMSCongruenceByLinkedTriple(S, - n, - PartsOfPartitionDS(colLookup), - PartsOfPartitionDS(rowLookup)); - SetGeneratingPairsOfMagmaCongruence(C, pairs); - return C; -end); + # Add the universal congruence + Add(congs, UniversalSemigroupCongruence(S)); -_EquivalenceRelationPartition := function(C) - local S, n, elms, table, next, part, i, x; - S := Range(C); - n := Size(S); - elms := EnumeratorCanonical(S); - table := EmptyPlist(n); - next := 1; - part := []; - for i in [1 .. n] do - if not IsBound(table[i]) then - Add(part, ImagesElm(C, elms[i])); - for x in part[Length(part)] do - table[PositionCanonical(S, x)] := next; + # Compute all column and row relations which are subsets of the max relations + colBlocksList := subpartitions(maxColBlocks); + rowBlocksList := subpartitions(maxRowBlocks); + + # Go through all triples and check + for n in NormalSubgroups(g) do + for colBlocks in colBlocksList do + for rowBlocks in rowBlocksList do + if IsLinkedTriple(S, n, colBlocks, rowBlocks) then + Add(congs, + RZMSCongruenceByLinkedTripleNC(S, n, colBlocks, rowBlocks)); + fi; od; - next := next + 1; - fi; + od; od; - SetEquivalenceRelationCanonicalLookup(C, table); - SetEquivalenceRelationCanonicalPartition(C, part); - return part; -end; - -InstallMethod(EquivalenceRelationPartition, -"for Rees matrix semigroup congruence by linked triple", -[IsRMSCongruenceByLinkedTriple], -_EquivalenceRelationPartition); - -InstallMethod(EquivalenceRelationPartition, -"for Rees 0-matrix semigroup congruence by linked triple", -[IsRZMSCongruenceByLinkedTriple], -_EquivalenceRelationPartition); -Unbind(_EquivalenceRelationPartition); + return congs; +end); diff --git a/gap/congruences/congsemigraph.gd b/gap/congruences/congsemigraph.gd index 304b00601..3510e79fd 100644 --- a/gap/congruences/congsemigraph.gd +++ b/gap/congruences/congsemigraph.gd @@ -9,7 +9,11 @@ ############################################################################ DeclareCategory("IsCongruenceByWangPair", - IsSemigroupCongruence and IsAttributeStoringRep and IsFinite); + IsSemigroupCongruence + and CanComputeEquivalenceRelationPartition + and IsMagmaCongruence + and IsAttributeStoringRep + and IsFinite); DeclareOperation("CongruenceByWangPair", [IsGraphInverseSemigroup, diff --git a/gap/congruences/congsemigraph.gi b/gap/congruences/congsemigraph.gi index 319466f33..1b4c1e94b 100644 --- a/gap/congruences/congsemigraph.gi +++ b/gap/congruences/congsemigraph.gi @@ -8,6 +8,10 @@ ## ############################################################################ +# TODO: +# * MeetOfSemigroupCongruences? +# * fix the error messsage + BindGlobal("SEMIGROUPS_IsHereditarySubset", function(S, H) local out, h, v, D, BlistH; @@ -68,10 +72,10 @@ function(S, H, W) SEMIGROUPS_ValidateWangPair(S, H, W); fam := GeneralMappingsFamily(ElementsFamily(FamilyObj(S)), ElementsFamily(FamilyObj(S))); - cong := Objectify(NewType(fam, IsCongruenceByWangPair), - rec(H := H, W := W)); + cong := Objectify(NewType(fam, IsCongruenceByWangPair), rec(H := H, W := W)); SetSource(cong, S); SetRange(cong, S); + GeneratingPairsOfSemigroupCongruence(cong); return cong; end); @@ -90,7 +94,7 @@ function(C) ViewString(C!.W)); end); -InstallMethod(AsSemigroupCongruenceByGeneratingPairs, +InstallMethod(GeneratingPairsOfSemigroupCongruence, "for a congruence by Wang pair", [IsCongruenceByWangPair], function(cong) @@ -118,7 +122,7 @@ function(cong) fi; od; od; - return SemigroupCongruence(S, pairs); + return pairs; end); BindGlobal("SEMIGROUPS_MinimalHereditarySubsetsVertex", @@ -177,7 +181,8 @@ end); InstallMethod(AsCongruenceByWangPair, "for a semigroup congruence", [IsSemigroupCongruence], function(C) - local H, W, eq, j; + local H, W, eq, result, pairs, j; + if not IsGraphInverseSemigroup(Source(C)) then ErrorNoReturn(Source(C), " is not a graph inverse semigroup"); fi; @@ -194,7 +199,12 @@ function(C) IndexOfVertexOfGraphInverseSemigroup)); fi; od; - return CongruenceByWangPair(Source(C), H, W); + result := CongruenceByWangPair(Source(C), H, W); + if HasGeneratingPairsOfMagmaCongruence(C) then + pairs := GeneratingPairsOfMagmaCongruence(C); + SetGeneratingPairsOfMagmaCongruence(result, pairs); + fi; + return result; end); InstallMethod(JoinSemigroupCongruences, @@ -244,5 +254,8 @@ end); InstallMethod(LatticeOfCongruences, "for a graph inverse semigroup", [IsGraphInverseSemigroup], -S -> JoinSemilatticeOfCongruences(GeneratingCongruencesOfLattice(S), - JoinSemigroupCongruences)); +function(S) + local D; + D := PosetOfCongruences(GeneratingCongruencesOfLattice(S)); + return JoinSemilatticeOfCongruences(D, WrappedTwoSidedCongruence); +end); diff --git a/gap/congruences/congsimple.gd b/gap/congruences/congsimple.gd index afeddfbc4..51d9925c7 100644 --- a/gap/congruences/congsimple.gd +++ b/gap/congruences/congsimple.gd @@ -12,11 +12,16 @@ ## congruences/reesmat.gd/gi. DeclareCategory("IsSimpleSemigroupCongruence", - IsCongruenceCategory and IsAttributeStoringRep and IsFinite); + IsSemigroupCongruence + and IsMagmaCongruence + and CanComputeEquivalenceRelationPartition + and IsAttributeStoringRep + and IsFinite); DeclareCategory("IsSimpleSemigroupCongruenceClass", - IsAnyCongruenceClass and IsCongruenceClass and - IsAttributeStoringRep and IsAssociativeElement); + IsCongruenceClass + and IsAttributeStoringRep + and IsAssociativeElement); DeclareOperation("CongruenceByIsomorphism", [IsGeneralMapping, IsSemigroupCongruence]); diff --git a/gap/congruences/conguniv.gi b/gap/congruences/conguniv.gi index e8a0a3c2a..e0f403317 100644 --- a/gap/congruences/conguniv.gi +++ b/gap/congruences/conguniv.gi @@ -18,7 +18,10 @@ function(S) fam := GeneralMappingsFamily(ElementsFamily(FamilyObj(S)), ElementsFamily(FamilyObj(S))); C := Objectify(NewType(fam, - IsCongruenceCategory and IsAttributeStoringRep), + IsSemigroupCongruence + and IsMagmaCongruence + and CanComputeEquivalenceRelationPartition + and IsAttributeStoringRep), rec()); SetSource(C, S); SetRange(C, S); @@ -187,7 +190,7 @@ function(C, x) fam := CollectionsFamily(FamilyObj(x)); class := Objectify(NewType(fam, IsUniversalSemigroupCongruenceClass - and IsAnyCongruenceClass), + and IsLeftRightOrTwoSidedCongruenceClass), rec()); SetParentAttr(class, Range(C)); @@ -204,7 +207,7 @@ function(x, class) return x in Parent(class); end); -# TODO more \* methods for universal and non-universal congruences?? +# TODO(later) more \* methods for universal and non-universal congruences?? InstallMethod(\*, "for two universal semigroup congruence classes", [IsUniversalSemigroupCongruenceClass, IsUniversalSemigroupCongruenceClass], diff --git a/gap/greens/acting.gd b/gap/greens/acting.gd index 3a48aafc6..d8ad29862 100644 --- a/gap/greens/acting.gd +++ b/gap/greens/acting.gd @@ -75,3 +75,7 @@ DeclareOperation("IteratorOfRClasses", [IsSemigroup]); DeclareOperation("IteratorOfDClassReps", [IsSemigroup]); DeclareOperation("IteratorOfRClassReps", [IsSemigroup]); + +DeclareOperation("RelativeDClassReps", [IsActingSemigroup, IsActingSemigroup]); +DeclareOperation("RelativeLClassReps", [IsActingSemigroup, IsActingSemigroup]); +DeclareOperation("RelativeRClassReps", [IsActingSemigroup, IsActingSemigroup]); diff --git a/gap/greens/acting.gi b/gap/greens/acting.gi index eba6ce2d5..7d2d5615d 100644 --- a/gap/greens/acting.gi +++ b/gap/greens/acting.gi @@ -1253,6 +1253,44 @@ function(S) return out; end); +InstallMethod(RelativeDClassReps, "for an acting semigroup and subsemigroup", +[IsActingSemigroup, IsActingSemigroup], +function(S, T) + local data, D, gens, genstoapply, x, i, j; + + if not IsSubsemigroup(S, T) then + ErrorNoReturn("the 2nd argument (an acting semigroup) must be ", + "a subsemigroup of the 1st argument (an acting semigroup)"); + fi; + + data := Enumerate(SemigroupData(S, RelativeLambdaOrb(S, T))); + D := List([1 .. Length(data)], x -> []); + gens := GeneratorsOfSemigroup(T); + genstoapply := [1 .. Length(gens)]; + for i in [2 .. Length(data)] do + x := data[i][4]; + for j in genstoapply do + Add(D[i], Position(data, gens[j] * x)); + od; + od; + D := DigraphStronglyConnectedComponents(Digraph(D)).comps; + D := List(D, x -> x[1]){[2 .. Length(D)]}; + return List(data{D}, x -> x[4]); +end); + +InstallMethod(RelativeRClassReps, "for an acting semigroup and subsemigroup", +[IsActingSemigroup, IsActingSemigroup], +function(S, T) + local data; + if not IsSubsemigroup(S, T) then + ErrorNoReturn("the 2nd argument (an acting semigroup) must be ", + "a subsemigroup of the 1st argument (an acting semigroup)"); + fi; + + data := Enumerate(SemigroupData(S, RelativeLambdaOrb(S, T))); + return List(data, x -> x[4]); +end); + # different method for regular/inverse/ideals InstallMethod(GreensDClasses, "for an acting semigroup", diff --git a/gap/greens/generic.gi b/gap/greens/generic.gi index 328d4ae95..3b839ee9b 100644 --- a/gap/greens/generic.gi +++ b/gap/greens/generic.gi @@ -493,7 +493,7 @@ function(C) end); InstallMethod(ViewString, "for a Green's relation", -[IsGreensRelation], 2, # to beat the method for congruences +[IsGreensRelation], 11, # to beat the method for congruences function(rel) local str; @@ -517,7 +517,7 @@ function(rel) end); InstallMethod(ViewObj, "for a Green's relation", -[IsGreensRelation], 2, # to beat the method for congruences +[IsGreensRelation], 11, # to beat the method for congruences function(rel) Print(ViewString(rel)); return; diff --git a/gap/libsemigroups/cong.gd b/gap/libsemigroups/cong.gd index 8481d0564..8b4a4cd0f 100644 --- a/gap/libsemigroups/cong.gd +++ b/gap/libsemigroups/cong.gd @@ -7,9 +7,13 @@ ## ############################################################################ -# A congruence belongs to this category if it can use libsemigroups to compute -# things about itself. -DeclareCategory("CanUseLibsemigroupsCongruence", IsAnyCongruenceCategory); +# A congruence satisfies CanUseLibsemigroupsCongruence if it should use a +# libsemigroups Congruence object to compute things about itself. +DeclareProperty("CanUseLibsemigroupsCongruence", + CanComputeEquivalenceRelationPartition); + +InstallTrueMethod(CanComputeEquivalenceRelationPartition, + CanUseLibsemigroupsCongruence); # The next operation is the only one supplied by libsemigroups/cong.gd/i that # is exported. diff --git a/gap/libsemigroups/cong.gi b/gap/libsemigroups/cong.gi index 4a3fe1b4b..2965b33c5 100644 --- a/gap/libsemigroups/cong.gi +++ b/gap/libsemigroups/cong.gi @@ -8,10 +8,7 @@ ########################################################################### ## -## This file contains the interface to libsemigroups Congruence objects. There -## is no declaration/implementation file because no other part of the -## Semigroups package should directly use any of the functionality in -## libsemigroups, only via the functions specified in this file. +## This file contains the interface to libsemigroups Congruence objects. # TODO: A method for MeetXSemigroupCongruences @@ -19,6 +16,60 @@ # Categories + properties + true methods ########################################################################### +# Unless explicitly excluded below, any left, right, or 2-sided congruence +# satisfies CanUseLibsemigroupsCongruence if its range has generators and +# CanUseFroidurePin, and the congruence has GeneratingPairs. The main reason to +# exclude some types of congruences from having CanUseLibsemigroupsCongruence +# is because there are better methods specifically for that type of congruence, +# and to avoid inadvertently computing a libsemigroups Congruence object. +# Note that any congruence with generating pairs whose range/source has +# CanUseFroidurePin, can use libsemigroups Congruence objects in theory (and +# they could also in practice), but we exclude this, for the reasons above. +# +# The fundamental operation for Congruences that satisfy +# CanUseLibsemigroupsCongruence is EquivalenceRelationPartition, and any type +# of congruence not satisfying CanUseLibsemigroupsCongruence should implement +# EquivalenceRelationPartition, and then the other methods will be available. + +InstallImmediateMethod(CanUseLibsemigroupsCongruence, + IsLeftRightOrTwoSidedCongruence + and HasGeneratingPairsOfLeftRightOrTwoSidedCongruence + and HasRange, + 0, + C -> CanUseFroidurePin(Range(C)) + and HasGeneratorsOfSemigroup(Range(C)) + or (HasIsFreeSemigroup(Range(C)) + and IsFreeSemigroup(Range(C))) + or (HasIsFreeMonoid(Range(C)) + and IsFreeMonoid(Range(C)))); + +InstallImmediateMethod(CanUseLibsemigroupsCongruence, + IsRMSCongruenceByLinkedTriple, + 0, + ReturnFalse); + +InstallImmediateMethod(CanUseLibsemigroupsCongruence, + IsRZMSCongruenceByLinkedTriple, + 0, + ReturnFalse); + +InstallImmediateMethod(CanUseLibsemigroupsCongruence, + IsSimpleSemigroupCongruence, + 0, + ReturnFalse); + +InstallImmediateMethod(CanUseLibsemigroupsCongruence, + IsInverseSemigroupCongruenceByKernelTrace, + 0, + ReturnFalse); + +InstallMethod(CanUseLibsemigroupsCongruence, +"for a left, right, or 2-sided congruence that can compute partition", +[CanComputeEquivalenceRelationPartition], +ReturnFalse); + +# TODO(now) remove CanUseLibsemigroupsCongruences? + # A semigroup satisfies this property if its congruences should belong to # CanUseLibsemigroupsCongruence. DeclareProperty("CanUseLibsemigroupsCongruences", IsSemigroup); @@ -132,33 +183,33 @@ function(C) if IsFpSemigroup(S) or (HasIsFreeSemigroup(S) and IsFreeSemigroup(S)) or IsFpMonoid(S) or (HasIsFreeMonoid(S) and IsFreeMonoid(S)) then make := libsemigroups.Congruence.make_from_fpsemigroup; - CC := make([AnyCongruenceString(C), LibsemigroupsFpSemigroup(S)]); + CC := make([CongruenceHandednessString(C), LibsemigroupsFpSemigroup(S)]); factor := Factorization; elif CanUseLibsemigroupsFroidurePin(S) then - CC := LibsemigroupsCongruenceConstructor(S)([AnyCongruenceString(C), - LibsemigroupsFroidurePin(S)]); + CC := LibsemigroupsCongruenceConstructor(S)([CongruenceHandednessString(C), + LibsemigroupsFroidurePin(S)]); factor := MinimalFactorization; elif CanUseGapFroidurePin(S) then N := Length(GeneratorsOfSemigroup(Range(C))); - tc := libsemigroups.ToddCoxeter.make([AnyCongruenceString(C)]); + tc := libsemigroups.ToddCoxeter.make([CongruenceHandednessString(C)]); libsemigroups.ToddCoxeter.set_number_of_generators(tc, N); - if IsLeftCongruenceCategory(C) then - table := LeftCayleyGraphSemigroup(Range(C)) - 1; - else + if IsRightMagmaCongruence(C) then table := RightCayleyGraphSemigroup(Range(C)) - 1; + else + table := LeftCayleyGraphSemigroup(Range(C)) - 1; fi; libsemigroups.ToddCoxeter.prefill(tc, table); - CC := libsemigroups.Congruence.make_from_table([AnyCongruenceString(C), - "none"]); + CC := libsemigroups.Congruence.make_from_table( + [CongruenceHandednessString(C), "none"]); libsemigroups.Congruence.set_number_of_generators(CC, N); libsemigroups.Congruence.add_runner(CC, tc); factor := MinimalFactorization; else # Shouldn't be possible to reach the next line, and can't currently test it - Assert(0, false); + TryNextMethod(); fi; add_pair := libsemigroups.Congruence.add_pair; - for pair in GeneratingPairsOfAnyCongruence(C) do + for pair in GeneratingPairsOfLeftRightOrTwoSidedCongruence(C) do add_pair(CC, factor(S, pair[1]) - 1, factor(S, pair[2]) - 1); od; C!.LibsemigroupsCongruence := CC; @@ -202,8 +253,8 @@ function(C, elm1, elm2) if CanUseFroidurePin(S) then pos1 := PositionCanonical(S, elm1); pos2 := PositionCanonical(S, elm2); - if HasEquivalenceRelationLookup(C) then - lookup := EquivalenceRelationLookup(C); + if HasEquivalenceRelationCanonicalLookup(C) then + lookup := EquivalenceRelationCanonicalLookup(C); return lookup[pos1] < lookup[pos2]; else word1 := MinimalFactorization(S, pos1); @@ -227,7 +278,7 @@ end); ########################################################################### InstallMethod(NrEquivalenceClasses, "for CanUseLibsemigroupsCongruence", -[CanUseLibsemigroupsCongruence], 100, +[CanUseLibsemigroupsCongruence], function(C) local number_of_classes, result; number_of_classes := libsemigroups.Congruence.number_of_classes; @@ -271,7 +322,7 @@ function(C, elm1, elm2) end); InstallMethod(EquivalenceRelationPartition, "for CanUseLibsemigroupsCongruence", -[CanUseLibsemigroupsCongruence], 100, +[CanUseLibsemigroupsCongruence], function(C) local S, CC, ntc, gens, class, i, j; S := Range(C); @@ -287,20 +338,21 @@ function(C) od; return ntc; elif CanUseGapFroidurePin(S) then - # in this case libsemigroups.Congruence.ntc doesn't work + # in this case libsemigroups.Congruence.ntc doesn't work, because S is not + # represented in the libsemigroups object return Filtered(EquivalenceRelationPartitionWithSingletons(C), x -> Size(x) > 1); fi; - # Cannot currently test the next line - Assert(0, false); + TryNextMethod(); end); # Methods for congruence classes InstallMethod(\<, "for congruence classes of CanUseLibsemigroupsCongruence", IsIdenticalObj, -[IsAnyCongruenceClass, IsAnyCongruenceClass], -1, # to beat the method in congruences/cong.gi for IsAnyCongruenceClass +[IsLeftRightOrTwoSidedCongruenceClass, IsLeftRightOrTwoSidedCongruenceClass], +1, # to beat the method in congruences/cong.gi for + # IsLeftRightOrTwoSidedCongruenceClass function(class1, class2) local C, word1, word2, CC; @@ -318,12 +370,12 @@ function(class1, class2) end); InstallMethod(EquivalenceClasses, "for CanUseLibsemigroupsCongruence", -[CanUseLibsemigroupsCongruence], 100, +[CanUseLibsemigroupsCongruence], function(C) local result, CC, gens, class_index_to_word, rep, i; if NrEquivalenceClasses(C) = infinity then - Error("the argument (a congruence) must have a finite ", + ErrorNoReturn("the argument (a congruence) must have a finite ", "number of classes"); fi; @@ -343,20 +395,24 @@ end); # operation or function that only applies to CanUseLibsemigroupsCongruence ########################################################################### -InstallMethod(EquivalenceRelationLookup, "for CanUseLibsemigroupsCongruence", -[CanUseLibsemigroupsCongruence], 100, +InstallMethod(EquivalenceRelationPartitionWithSingletons, +"for CanUseLibsemigroupsCongruence", [CanUseLibsemigroupsCongruence], function(C) - local S, N, lookup, i; - S := Range(C); - if not IsFinite(S) then - Error("the argument (a congruence) must have finite range"); + local part, i, x; + if not IsFinite(Range(C)) then + ErrorNoReturn("the argument (a congruence) must have finite range"); fi; - N := Size(S); - lookup := EmptyPlist(N); - for i in [1 .. N] do - lookup[i] := CongruenceWordToClassIndex(C, Factorization(S, i)); + + part := []; + for x in Range(C) do + i := CongruenceWordToClassIndex(C, x); + if not IsBound(part[i]) then + part[i] := []; + fi; + Add(part[i], x); od; - return lookup; + + return part; end); InstallMethod(ImagesElm, @@ -369,8 +425,7 @@ function(cong, elm) and CanUseFroidurePin(Range(cong)) then lookup := EquivalenceRelationCanonicalLookup(cong); id := lookup[PositionCanonical(Range(cong), elm)]; - part := EquivalenceRelationPartitionWithSingletons(cong); - return part[id]; + return EnumeratorCanonical(Range(cong)){Positions(lookup, id)}; elif IsFpSemigroup(Range(cong)) or (HasIsFreeSemigroup(Range(cong)) and IsFreeSemigroup(Range(cong))) or IsFpMonoid(Range(cong)) @@ -385,39 +440,3 @@ function(cong, elm) # Shouldn't be able to reach here Assert(0, false); end); - -# These constructors exist so that the resulting congruences belong to the -# correct categories, namely CanUseLibsemigroupsCongruence. - -InstallMethod(SemigroupCongruenceByGeneratingPairs, -"for a semigroup with CanUseLibsemigroupsCongruences and a list", -[IsSemigroup and CanUseLibsemigroupsCongruences, IsList], -ToBeat([IsSemigroup and CanUseLibsemigroupsCongruences, IsList], - [IsSemigroup and CanUseLibsemigroupsCongruences, IsList and IsEmpty]), -function(S, pairs) - local filt; - filt := IsCongruenceCategory and CanUseLibsemigroupsCongruence; - return _AnyCongruenceByGeneratingPairs(S, pairs, filt); -end); - -InstallMethod(LeftSemigroupCongruenceByGeneratingPairs, -"for a semigroup with CanUseLibsemigroupsCongruences and a list", -[IsSemigroup and CanUseLibsemigroupsCongruences, IsList], -ToBeat([IsSemigroup and CanUseLibsemigroupsCongruences, IsList], - [IsSemigroup and CanUseLibsemigroupsCongruences, IsList and IsEmpty]), -function(S, pairs) - local filt; - filt := IsLeftCongruenceCategory and CanUseLibsemigroupsCongruence; - return _AnyCongruenceByGeneratingPairs(S, pairs, filt); -end); - -InstallMethod(RightSemigroupCongruenceByGeneratingPairs, -"for a semigroup with CanUseLibsemigroupsCongruences and a list", -[IsSemigroup and CanUseLibsemigroupsCongruences, IsList], -ToBeat([IsSemigroup and CanUseLibsemigroupsCongruences, IsList], - [IsSemigroup and CanUseLibsemigroupsCongruences, IsList and IsEmpty]), -function(S, pairs) - local filt; - filt := IsRightCongruenceCategory and CanUseLibsemigroupsCongruence; - return _AnyCongruenceByGeneratingPairs(S, pairs, filt); -end); diff --git a/gap/libsemigroups/fpsemi.gi b/gap/libsemigroups/fpsemi.gi index a1c96ce2b..81efa5340 100644 --- a/gap/libsemigroups/fpsemi.gi +++ b/gap/libsemigroups/fpsemi.gi @@ -1,7 +1,7 @@ ############################################################################# ## ## libsemigroups/fpsemi.gi -## Copyright (C) 2022 James D. Mitchell +## Copyright (C) 2022 James D. Mitchell ## ## Licensing information can be found in the README file of this package. ## diff --git a/gap/main/acting.gd b/gap/main/acting.gd index 495cfce2c..dcaead396 100644 --- a/gap/main/acting.gd +++ b/gap/main/acting.gd @@ -12,6 +12,7 @@ DeclareCategory("IsSemigroupData", IsList and IsComponentObjectRep); DeclareFilter("IsClosedData", IsSemigroupData); DeclareAttribute("SemigroupData", IsActingSemigroup, "mutable"); +DeclareOperation("SemigroupData", [IsActingSemigroup, IsLambdaOrb]); DeclareOperation("Enumerate", [IsSemigroupData]); DeclareOperation("Enumerate", [IsSemigroupData, IsCyclotomic]); diff --git a/gap/main/acting.gi b/gap/main/acting.gi index 01bd29e28..cccd9f2f4 100644 --- a/gap/main/acting.gi +++ b/gap/main/acting.gi @@ -19,9 +19,9 @@ InstallMethod(SemigroupData, "for an acting inverse semigroup rep", # different method for ideals -InstallMethod(SemigroupData, "for an acting semigroup", -[IsActingSemigroup], -function(S) +InstallMethod(SemigroupData, "for an acting semigroup and lambda orb", +[IsActingSemigroup, IsLambdaOrb], +function(S, lambda_orb) local gens, data; gens := GeneratorsOfSemigroup(S); @@ -32,6 +32,7 @@ function(S) ht := HTCreate(gens[1], rec(treehashsize := SEMIGROUPS.OptionsRec(S).hashlen)), init := false, + lambda_orb := lambda_orb, lambdarhoht := [], lenreps := [0], orbit := [[,,, FakeOne(gens)]], @@ -53,6 +54,12 @@ function(S) return data; end); +InstallMethod(SemigroupData, "for an acting semigroup", +[IsActingSemigroup], +function(S) + return SemigroupData(S, LambdaOrb(S)); +end); + # different method for regular ideals, regular/inverse semigroups, same method # for non-regular ideals @@ -382,7 +389,7 @@ function(data, limit, lookfunc) lambda := LambdaFunc(s); lambdaperm := LambdaPerm(s); - o := LambdaOrb(s); + o := data!.lambda_orb; oht := o!.ht; scc := OrbSCC(o); lookup := o!.scc_lookup; @@ -651,7 +658,7 @@ function(data, x, n) membership, lambdaperm; S := data!.parent; - o := LambdaOrb(S); + o := data!.lambda_orb; l := Position(o, LambdaFunc(S)(x)); if l = fail then diff --git a/gap/main/froidure-pin.gi b/gap/main/froidure-pin.gi index 3cc9b6947..a8bc5c2a3 100644 --- a/gap/main/froidure-pin.gi +++ b/gap/main/froidure-pin.gi @@ -170,7 +170,8 @@ function(S) if not IsFinite(S) then ErrorNoReturn("the argument (a semigroup) is not finite"); fi; - return SortedList(RUN_FROIDURE_PIN(GapFroidurePin(S), -1).elts); + return SortedList(RUN_FROIDURE_PIN(GapFroidurePin(S), -1, + InfoLevel(InfoSemigroups) > 0).elts); end); InstallMethod(AsList, @@ -185,7 +186,8 @@ function(S) if not IsFinite(S) then ErrorNoReturn("the argument (a semigroup) is not finite"); fi; - return RUN_FROIDURE_PIN(GapFroidurePin(S), -1).elts; + return RUN_FROIDURE_PIN(GapFroidurePin(S), -1, + InfoLevel(InfoSemigroups) > 0).elts; end); # For ideals and other generatorless semigroups @@ -239,7 +241,7 @@ function(S) fp := GapFroidurePin(S); if not (IsBound(fp.elts) and nr < Length(fp.elts) and IsBound(fp.elts[nr])) then - fp := RUN_FROIDURE_PIN(fp, nr); + fp := RUN_FROIDURE_PIN(fp, nr, InfoLevel(InfoSemigroups) > 0); fi; if nr <= Length(fp.elts) and IsBound(fp.elts[nr]) then @@ -288,7 +290,8 @@ InstallMethod(Size, "for a semigroup with CanUseGapFroidurePin and known generators", [CanUseGapFroidurePin and HasGeneratorsOfSemigroup], function(S) - return Length(RUN_FROIDURE_PIN(GapFroidurePin(S), -1).elts); + return Length(RUN_FROIDURE_PIN(GapFroidurePin(S), -1, + InfoLevel(InfoSemigroups) > 0).elts); end); # different method for ideals @@ -333,7 +336,7 @@ function(S, x) return val; fi; limit := nr + 1; - fp := RUN_FROIDURE_PIN(fp, limit); + fp := RUN_FROIDURE_PIN(fp, limit, InfoLevel(InfoSemigroups) > 0); pos := fp.pos; nr := fp.nr; until pos > nr; @@ -393,7 +396,7 @@ InstallMethod(Enumerate, "for a semigroup with CanUseGapFroidurePin and known generators and pos int", [CanUseGapFroidurePin and HasGeneratorsOfSemigroup, IsInt], function(S, limit) - RUN_FROIDURE_PIN(GapFroidurePin(S), limit); + RUN_FROIDURE_PIN(GapFroidurePin(S), limit, InfoLevel(InfoSemigroups) > 0); return S; end); @@ -413,7 +416,8 @@ function(S) if not IsFinite(S) then ErrorNoReturn("the argument (a semigroup) is not finite"); fi; - return RUN_FROIDURE_PIN(GapFroidurePin(S), -1).right; + return RUN_FROIDURE_PIN(GapFroidurePin(S), -1, + InfoLevel(InfoSemigroups) > 0).right; end); InstallMethod(RightCayleyDigraph, @@ -441,7 +445,8 @@ function(S) if not IsFinite(S) then ErrorNoReturn("the argument (a semigroup) is not finite"); fi; - return RUN_FROIDURE_PIN(GapFroidurePin(S), -1).left; + return RUN_FROIDURE_PIN(GapFroidurePin(S), -1, + InfoLevel(InfoSemigroups) > 0).left; end); InstallMethod(LeftCayleyDigraph, @@ -483,7 +488,9 @@ function(S, i) "than the size of the 1st argument (a semigroup)"); fi; - words := RUN_FROIDURE_PIN(GapFroidurePin(S), i + 1).words; + words := RUN_FROIDURE_PIN(GapFroidurePin(S), + i + 1, + InfoLevel(InfoSemigroups) > 0).words; return ShallowCopy(words[i]); end); @@ -509,7 +516,8 @@ function(S) if not IsFinite(S) then Error("the argument (a semigroup) is not finite"); fi; - return RUN_FROIDURE_PIN(GapFroidurePin(S), -1).rules; + return RUN_FROIDURE_PIN(GapFroidurePin(S), -1, + InfoLevel(InfoSemigroups) > 0).rules; end); InstallMethod(IdempotentsSubset, @@ -519,7 +527,9 @@ InstallMethod(IdempotentsSubset, function(S, list) local fp, left, final, prefix, elts, out, i, j, pos; - fp := RUN_FROIDURE_PIN(GapFroidurePin(S), Maximum(list) + 1); + fp := RUN_FROIDURE_PIN(GapFroidurePin(S), + Maximum(list) + 1, + InfoLevel(InfoSemigroups) > 0); left := fp.left; final := fp.final; prefix := fp.prefix; diff --git a/gap/main/lambda-rho.gd b/gap/main/lambda-rho.gd index 12c5a2c60..c94479230 100644 --- a/gap/main/lambda-rho.gd +++ b/gap/main/lambda-rho.gd @@ -24,3 +24,6 @@ DeclareGlobalFunction("RhoOrbRep"); DeclareGlobalFunction("RhoOrbSchutzGp"); DeclareGlobalFunction("LambdaOrbStabChain"); DeclareOperation("RhoOrbStabChain", [IsOrbit, IsPosInt]); + +DeclareOperation("RelativeLambdaOrb", + [IsActingSemigroup, IsActingSemigroup]); diff --git a/gap/main/lambda-rho.gi b/gap/main/lambda-rho.gi index 2ed6f9c07..d0c6797ca 100644 --- a/gap/main/lambda-rho.gi +++ b/gap/main/lambda-rho.gi @@ -460,3 +460,64 @@ function(o, m) fi; return G; end); + +InstallMethod(RelativeLambdaOrb, +"for acting semigroup and subsemigroup", +[IsActingSemigroup, IsActingSemigroup], +function(S, T) + local o, D, act, schreiergen, schreierpos, genstoapply, gens, x, pos, i, j, + m; + + if not IsSubsemigroup(S, T) then + ErrorNoReturn("the 2nd argument (an acting semigroup) must be ", + "a subsemigroup of the 1st argument (an acting semigroup)"); + fi; + Info(InfoSemigroups, 1, "Computing relative lambda orb . . "); + + o := StructuralCopy(Enumerate(LambdaOrb(S))); + o!.scc_reps := [FakeOne(GeneratorsOfSemigroup(S))]; + + Unbind(o!.scc); + Unbind(o!.trees); + Unbind(o!.scc_lookup); + Unbind(o!.mults); + Unbind(o!.schutz); + Unbind(o!.reverse); + Unbind(o!.rev); + Unbind(o!.schutzstab); + Unbind(o!.factorgroups); + Unbind(o!.factors); + + D := List([1 .. Length(o)], x -> []); + act := LambdaAct(S); + schreiergen := ListWithIdenticalEntries(Length(o), fail); + schreierpos := ListWithIdenticalEntries(Length(o), fail); + genstoapply := [1 .. Length(GeneratorsOfSemigroup(T))]; + gens := GeneratorsOfSemigroup(T); + + for i in [1 .. Length(o)] do + x := o[i]; + for j in genstoapply do + pos := Position(o, act(x, gens[j])); + Add(D[i], pos); + if i < pos and schreiergen[pos] = fail then + schreiergen[pos] := j; + schreierpos[pos] := i; + fi; + od; + od; + o!.orbitgraph := D; + # Compute scc wrt to the new orbit graph, but scc reps using the old Schreier + # tree. + for m in [2 .. Length(OrbSCC(o))] do + LambdaOrbRep(o, m); + od; + + o!.gens := GeneratorsOfSemigroup(T); + o!.schreierpos := schreierpos; + o!.schreiergen := schreiergen; + Info(InfoSemigroups, + 1, + StringFormatted("found {} lambda values!", Length(o))); + return o; +end); diff --git a/gap/tools/utils.gi b/gap/tools/utils.gi index 0c69f4e25..a1c42ebc4 100644 --- a/gap/tools/utils.gi +++ b/gap/tools/utils.gi @@ -41,16 +41,16 @@ end); SEMIGROUPS.TestRec := rec(); SEMIGROUPS.TestRec.reportDiff := function(inp, expout, found, fnam, line, time) - Print("\033[31m######## > Diff in:\n"); + Print("######## > Diff in:\n"); if IsStream(fnam) then Print("test stream, line ", line, "\n"); else Print(fnam, ":", line, "\n"); fi; Print("# Input is:\n", inp); - Print("# Expected output:\n", expout); - Print("# But found:\n", found); - Print("########\033[0m\n"); + Print("# Expected output:\n\033[30;42m", Chomp(expout), "\033[0m\n"); + Print("# But found:\n\033[30;41m", Chomp(found), "\033[0m\n"); + Print("########\n"); return; end; @@ -294,18 +294,18 @@ SEMIGROUPS.ManualExamples := function() "Single"); end; -SEMIGROUPS.RunExamples := function(exlists, excluded) - local oldscr, passed, pad, total, l, sp, bad, s, start_time, test, end_time, - elapsed, pex, j, ex, i; +SEMIGROUPS.RunExamples := function(exlists, nums, excluded) + local oldscr, pad, total, num_fails, l, sp, bad, s, start_time, test, + end_time, elapsed, pex, j, ex, i; oldscr := SizeScreen(); SizeScreen([72, oldscr[2]]); - passed := true; pad := function(nr) - nr := Length(String(Length(exlists))) - Length(String(nr)) + 1; + nr := Length(String(Maximum(nums))) - Length(String(nr)) + 1; return List([1 .. nr], x -> ' '); end; total := 0; + num_fails := 0; for j in [1 .. Length(exlists)] do if j in excluded then Print("\033[44m# Skipping example ", @@ -314,7 +314,7 @@ SEMIGROUPS.RunExamples := function(exlists, excluded) " . . .\033[0m\n"); else l := exlists[j]; - Print("# Running example ", j, pad(j), " . . ."); + Print("# Running example ", nums[j], pad(nums[j]), " . . ."); START_TEST(""); for ex in l do sp := SplitString(ex[1], "\n", ""); @@ -342,22 +342,22 @@ SEMIGROUPS.RunExamples := function(exlists, excluded) " in ", ex[2]{[1 .. 3]}, "\033[0m\n"); - passed := false; + num_fails := num_fails + 1; fi; if test = false then for i in [1 .. Length(pex[1])] do if pex[2][i] <> pex[4][i] then - Print("\033[31m########> Diff in:\n", + Print("########> Diff in:\n", "# ", ex[2][1], ":", ex[2][2], "\n# Input is:\n"); PrintFormattedString(pex[1][i]); - Print("# Expected output:\n"); - PrintFormattedString(pex[2][i]); - Print("# But found:\n"); - PrintFormattedString(pex[4][i]); - Print("########\033[0m\n"); - passed := false; + Print("# Expected output:\n\033[30;42m"); + PrintFormattedString(Chomp(pex[2][i])); + Print("\033[0m\n# But found:\n\033[30;41m"); + PrintFormattedString(Chomp(pex[4][i])); + Print("\033[0m\n########\n"); + num_fails := num_fails + 1; fi; od; fi; @@ -366,20 +366,29 @@ SEMIGROUPS.RunExamples := function(exlists, excluded) od; SizeScreen(oldscr); if Length(exlists) > 1 then + PrintFormatted("{} failures in {} examples\n", + num_fails, + Sum(exlists, Length)); Print("Total: ", total, " msecs\n"); fi; - return passed; + return num_fails = 0; end; SEMIGROUPS.TestManualExamples := function(arg) - local ex, doc, tree, tester, omit, acting, passed, str; + local ex, nums, doc, tree, mansect, tester, actual, omit, acting, passed, + str; + ex := SEMIGROUPS.ManualExamples(); - if Length(arg) = 1 then + if Length(arg) = 0 then + nums := [1 .. Length(ex)]; + elif Length(arg) = 1 then if IsPosInt(arg[1]) and arg[1] <= Length(ex) then ex := [ex[arg[1]]]; + nums := [arg[1]]; elif IsHomogeneousList(arg[1]) and ForAll(arg[1], x -> IsPosInt(x) and x <= Length(ex)) then - ex := SEMIGROUPS.ManualExamples(){arg}; + ex := SEMIGROUPS.ManualExamples(){arg[1]}; + nums := arg[1]; elif IsString(arg[1]) then doc := ComposedXMLString(Concatenation(SEMIGROUPS.PackageDir, "/doc"), "main.xml", @@ -387,16 +396,18 @@ SEMIGROUPS.TestManualExamples := function(arg) true); tree := ParseTreeXMLString(doc[1]); CheckAndCleanGapDocTree(tree); - ex := XMLElements(tree, "ManSection"); + mansect := XMLElements(tree, "ManSection"); tester := function(record) return IsBound(record.content[1].attributes.Name) and record.content[1].attributes.Name = arg[1]; end; - ex := First(ex, tester); - if ex = fail then + mansect := First(mansect, tester); + if mansect = fail then ErrorNoReturn("did not find a man section named ", arg[1]); fi; - ex := ExtractExamplesXMLTree(ex, "Single"); + actual := ExtractExamplesXMLTree(mansect, "Single"); + nums := [PositionProperty(ex, x -> x[1][1] = actual[1][1][1])]; + ex := actual; else ErrorNoReturn("the argument must be a pos int or list of pos ints"); fi; @@ -422,7 +433,7 @@ SEMIGROUPS.TestManualExamples := function(arg) SEMIGROUPS.DefaultOptionsRec.acting := true; SEMIGROUPS.StartTest(); - passed := SEMIGROUPS.RunExamples(ex, []); + passed := SEMIGROUPS.RunExamples(ex, nums, []); SEMIGROUPS.StopTest(); SEMIGROUPS.DefaultOptionsRec.acting := acting; diff --git a/init.g b/init.g index 46661840d..9f612e946 100644 --- a/init.g +++ b/init.g @@ -61,8 +61,8 @@ ReadPackage("semigroups", "gap/libsemigroups/froidure-pin.gd"); ReadPackage("semigroups", "gap/main/froidure-pin.gd"); ReadPackage("semigroups", "gap/main/semiact.gd"); ReadPackage("semigroups", "gap/main/setup.gd"); -ReadPackage("semigroups", "gap/main/acting.gd"); ReadPackage("semigroups", "gap/main/lambda-rho.gd"); +ReadPackage("semigroups", "gap/main/acting.gd"); ReadPackage("semigroups", "gap/main/graded.gd"); ReadPackage("semigroups", "gap/main/orbits.gd"); @@ -112,6 +112,7 @@ ReadPackage("semigroups", "gap/attributes/maximal.gd"); ReadPackage("semigroups", "gap/attributes/properties.gd"); ReadPackage("semigroups", "gap/congruences/cong.gd"); +ReadPackage("semigroups", "gap/congruences/congpart.gd"); ReadPackage("semigroups", "gap/congruences/conginv.gd"); ReadPackage("semigroups", "gap/congruences/conglatt.gd"); ReadPackage("semigroups", "gap/congruences/congpairs.gd"); diff --git a/read.g b/read.g index 4503d1dbd..621fa841d 100644 --- a/read.g +++ b/read.g @@ -80,15 +80,16 @@ ReadPackage("semigroups", "gap/attributes/maximal.gi"); ReadPackage("semigroups", "gap/attributes/properties.gi"); ReadPackage("semigroups", "gap/attributes/semifp.gi"); +ReadPackage("semigroups", "gap/congruences/cong.gi"); +ReadPackage("semigroups", "gap/congruences/congpart.gi"); ReadPackage("semigroups", "gap/congruences/congpairs.gi"); -ReadPackage("semigroups", "gap/congruences/congrms.gi"); -ReadPackage("semigroups", "gap/congruences/conguniv.gi"); ReadPackage("semigroups", "gap/congruences/conginv.gi"); +ReadPackage("semigroups", "gap/congruences/conglatt.gi"); +ReadPackage("semigroups", "gap/congruences/congrees.gi"); +ReadPackage("semigroups", "gap/congruences/congrms.gi"); ReadPackage("semigroups", "gap/congruences/congsemigraph.gi"); ReadPackage("semigroups", "gap/congruences/congsimple.gi"); -ReadPackage("semigroups", "gap/congruences/congrees.gi"); -ReadPackage("semigroups", "gap/congruences/cong.gi"); -ReadPackage("semigroups", "gap/congruences/conglatt.gi"); +ReadPackage("semigroups", "gap/congruences/conguniv.gi"); ReadPackage("semigroups", "gap/fp/freeinverse.gi"); ReadPackage("semigroups", "gap/fp/freeband.gi"); diff --git a/src/froidure-pin-fallback.cpp b/src/froidure-pin-fallback.cpp index 4bb29009d..ae961f0e3 100644 --- a/src/froidure-pin-fallback.cpp +++ b/src/froidure-pin-fallback.cpp @@ -43,16 +43,6 @@ using libsemigroups::detail::Timer; #define INT_PLIST2(plist, i, j) INT_INTOBJ(ELM_PLIST2(plist, i, j)) #define ELM_PLIST2(plist, i, j) ELM_PLIST(ELM_PLIST(plist, i), j) -#define SEMIGROUPS_REPORT(...) \ - (libsemigroups::REPORTER.report() ? libsemigroups::REPORTER(__VA_ARGS__) \ - : libsemigroups::REPORTER) - -#define SEMIGROUPS_REPORT_DEFAULT(...) SEMIGROUPS_REPORT(__VA_ARGS__).flush(); - -#define SEMIGROUPS_REPORT_TIME(var) \ - SEMIGROUPS_REPORT_DEFAULT( \ - "elapsed time (%s): %s\n", __func__, var.string().c_str()); - static Int RNam_batch_size = 0; static Int RNam_DefaultOptionsRec = 0; static Int RNam_opts = 0; @@ -95,7 +85,7 @@ static inline size_t get_batch_size(Obj so) { // // Assumes the length of data!.elts is at most 2 ^ 28. -Obj RUN_FROIDURE_PIN(Obj self, Obj obj, Obj limit) { +Obj RUN_FROIDURE_PIN(Obj self, Obj obj, Obj limit, Obj report) { Obj found, elts, gens, genslookup, right, left, first, final, prefix, suffix, reduced, words, ht, rules, lenindex, newElt, newword, objval, newrule, empty, oldword, x, data, parent, stopper; @@ -123,7 +113,9 @@ Obj RUN_FROIDURE_PIN(Obj self, Obj obj, Obj limit) { return data; } int_limit = std::max(static_cast(INT_INTOBJ(limit)), nr + batch_size); - SEMIGROUPS_REPORT_DEFAULT("limit = %llu\n", uint64_t(int_limit)); + if (report == True) { + std::cout << "#I limit = " << int_limit << std::endl; + } Timer timer; @@ -333,24 +325,21 @@ Obj RUN_FROIDURE_PIN(Obj self, Obj obj, Obj limit) { len++; AssPlist(lenindex, len, INTOBJ_INT(i)); } - if (i <= nr) { - SEMIGROUPS_REPORT_DEFAULT("found %llu elements, %llu " - "rules, max word length %llu, so " - "far\n", - uint64_t(nr), - uint64_t(nrrules), - uint64_t(len - 1)); - } else { - SEMIGROUPS_REPORT_DEFAULT("found %llu elements, %llu " - "rules, max word length %llu, " - "finished!\n", - uint64_t(nr), - uint64_t(nrrules), - uint64_t(len - 1)); + if (report == True) { + std::cout << "#I found " << nr << " elements, " << nrrules + << " rules, max word length " << len - 1 << ", "; + if (i <= nr) { + std::cout << "so far"; + } else { + std::cout << "finished!"; + } + std::cout << std::endl; } } - SEMIGROUPS_REPORT_TIME(timer); + if (report == True) { + std::cout << "#I elapsed time: " << timer << std::endl; + } AssPRec(data, RNamName("nr"), INTOBJ_INT(nr)); AssPRec(data, RNamName("nrrules"), INTOBJ_INT(nrrules)); AssPRec(data, RNamName("one"), ((one != 0) ? INTOBJ_INT(one) : False)); @@ -441,13 +430,10 @@ Obj SCC_UNION_LEFT_RIGHT_CAYLEY_GRAPHS(Obj self, Obj scc1, Obj scc2) { return out; } -// and should be scc data -// structures for the right and left Cayley -// graphs of a semigroup, as produced by -// DigraphStronglyConnectedComponents. This -// function find the H-classes of the -// semigroup from and . The -// method used is that described in: +// and should be scc data structures for the right and left +// Cayley graphs of a semigroup, as produced by +// DigraphStronglyConnectedComponents. This function find the H-classes of the +// semigroup from and . The method used is that described in: // https://www.irif.fr/~jep//PDF/Exposes/StAndrews.pdf Obj FIND_HCLASSES(Obj self, Obj right, Obj left) { diff --git a/src/froidure-pin-fallback.hpp b/src/froidure-pin-fallback.hpp index 65a2862a8..9699a314f 100644 --- a/src/froidure-pin-fallback.hpp +++ b/src/froidure-pin-fallback.hpp @@ -21,7 +21,7 @@ #include "compiled.h" // for Obj -Obj RUN_FROIDURE_PIN(Obj self, Obj obj, Obj limit); +Obj RUN_FROIDURE_PIN(Obj self, Obj obj, Obj limit, Obj report); Obj SCC_UNION_LEFT_RIGHT_CAYLEY_GRAPHS(Obj, Obj, Obj); Obj FIND_HCLASSES(Obj, Obj, Obj); diff --git a/src/pkg.cpp b/src/pkg.cpp index 534c64990..7e6fccda0 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -335,7 +335,10 @@ static StructGVarFunc GVarFuncs[] = { 2, "scc1, scc2"), GVAR_ENTRY("froidure-pin-fallback.cpp", FIND_HCLASSES, 2, "left, right"), - GVAR_ENTRY("froidure-pin-fallback.cpp", RUN_FROIDURE_PIN, 2, "obj, limit"), + GVAR_ENTRY("froidure-pin-fallback.cpp", + RUN_FROIDURE_PIN, + 3, + "obj, limit, report"), GVAR_ENTRY("bipart.cpp", BIPART_NC, 1, "list"), GVAR_ENTRY("bipart.cpp", BIPART_EXT_REP, 1, "x"), diff --git a/tst/extreme/cong.tst b/tst/extreme/cong.tst index 0c6f1a5ab..488cd994f 100644 --- a/tst/extreme/cong.tst +++ b/tst/extreme/cong.tst @@ -27,8 +27,8 @@ gap> gens := [ > [Transformation([3, 3, 3, 6, 3, 3]), > Transformation([1, 6, 6, 6, 6, 1])]];; gap> cong := SemigroupCongruence(s, gens); - with 2 generating pairs> +<2-sided semigroup congruence over with 2 generating pairs> gap> gens[2] in cong; true gap> x := Transformation([6, 5, 4, 4, 4, 6]);; @@ -49,7 +49,7 @@ true gap> classx := EquivalenceClassOfElement(cong, x);; gap> classy := EquivalenceClassOfElement(cong, y);; gap> classz := EquivalenceClassOfElement(cong, z); - +<2-sided congruence class of Transformation( [ 2, 4, 6, 1, 6, 5 ] )> gap> classx = classy; true gap> classz = classx; @@ -110,8 +110,8 @@ gap> u = v; false gap> gens := List(t, x -> [gens[1], x]);; gap> v := SemigroupCongruence(t, gens); - with 5 generating pairs> +<2-sided semigroup congruence over with 5 generating pairs> gap> u = v; true gap> NrEquivalenceClasses(u); @@ -126,19 +126,21 @@ gap> IsRegularSemigroup(s); false gap> gens := List(s, x -> [gens[1], x]);; gap> u := SemigroupCongruence(s, gens); # universal congruence - with 4 generating pairs> +<2-sided semigroup congruence over with 4 generating pairs> gap> u = UniversalSemigroupCongruence(s); true gap> v := SemigroupCongruence(s, [gens[1], gens[1]]); # trivial congruence - with 0 generating pairs> +<2-sided semigroup congruence over with 0 generating pairs> gap> classes := Set(EquivalenceClasses(v)); -[ , - , - , - , - ] +[ <2-sided congruence class of Transformation( [ 2, 6, 7, 2, 6, 9, 9, 1, 1, + 5 ] )>, <2-sided congruence class of Transformation( [ 6, 9, 9, 6, 9, 1, + 1, 2, 2, 6 ] )>, <2-sided congruence class of Transformation( [ 9, 1, 1, + 9, 1, 2, 2, 6, 6, 9 ] )>, + <2-sided congruence class of Transformation( [ 1, 2, 2, 1, 2, 6, 6, 9, 9, + 1 ] )>, <2-sided congruence class of Transformation( [ 2, 6, 6, 2, 6, 9, + 9, 1, 1, 2 ] )> ] gap> ForAny(EquivalenceClasses(u), x -> x in classes); false gap> classes[1] * EquivalenceClasses(u)[1]; @@ -146,9 +148,11 @@ Error, the arguments (cong. classes) are not classes of the same congruence gap> EquivalenceClasses(u)[1] * classes[1]; Error, the arguments (cong. classes) are not classes of the same congruence gap> classes[3] * classes[4]; - +<2-sided congruence class of Transformation( [ 9, 1, 1, 9, 1, 2, 2, 6, 6, + 9 ] )> gap> classes[4] * classes[3]; - +<2-sided congruence class of Transformation( [ 9, 1, 1, 9, 1, 2, 2, 6, 6, + 9 ] )> gap> Representative(classes[5] * classes[2]) = > Representative(classes[5]) * Representative(classes[2]); true @@ -157,259 +161,255 @@ true gap> S := Semigroup([ > Transformation([1, 3, 4, 1]), Transformation([3, 1, 1, 3])]);; gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 7, 28, 26 ], [ 23, 10 ], [ 6 ], [ 17 ], [ 4, 33 ], [ ], [ 39, 27 ], - [ 9, 19, 5 ], [ 15, 4 ], [ 45 ], [ 34 ], [ 47, 44 ], [ 35, 48 ], - [ 17, 32 ], [ 42, 17 ], [ 18, 25, 50 ], [ 31 ], [ 38 ], [ 41, 33 ], - [ 4, 46 ], [ 36, 49 ], [ 37, 25 ], [ 30, 12, 45 ], [ 37, 21, 50 ], [ 38 ], - [ 27, 22, 24, 16 ], [ 37, 18 ], [ 39, 22 ], [ 42, 43 ], [ 9, 47, 20 ], - [ 6 ], [ 31 ], [ 14 ], [ 29, 40 ], [ 12, 52 ], [ 30, 8, 51 ], - [ 23, 36, 38 ], [ 35, 51 ], [ 2, 37 ], [ 42, 32 ], [ 15, 40, 14 ], - [ 3, 31 ], [ 31 ], [ 46 ], [ 20, 44 ], [ 17, 43 ], [ 15, 29, 46 ], - [ 52, 11 ], [ 51, 48 ], [ 38, 13, 49 ], [ 52, 19 ], [ 47, 41, 34 ] ] +> +gap> IsIsomorphicDigraph(l, +> DigraphReflexiveTransitiveClosure(DigraphFromDiSparse6String( +> Concatenation(".sa?wwQFR@t`\\QqNIdyLUyGHpZsuxJbfYqND@QVxgxNANVl`LVO", +> "TRbauTneHUDJAgV\\GgI@GBoXoa_BEbh{]k@kBqkAKX_VQtAZCHQ", +> "hdQiVLSRAQKZoshppN[dqQtLVJMrIc{nbQkKQ|d")))); +true gap> S := Semigroup([ > Transformation([1, 4, 3, 1, 4, 2]), Transformation([1, 6, 6, 3, 6, 6])]);; gap> IsRegularSemigroup(S); false gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 2 ], [ 4 ], [ 5 ], [ 3 ], [ ] ] +> +gap> IsIsomorphicDigraph(l, DigraphFromDigraph6String("&D}{ho_")); +true gap> S := Semigroup([ > Transformation([4, 3, 1, 1, 6, 4]), Transformation([4, 3, 6, 4, 2, 3])]);; gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 2, 50 ], [ 8, 52, 40 ], [ 16, 72, 66 ], [ 74, 11 ], [ 77, 83, 36 ], - [ 89, 87 ], [ 94, 63, 86 ], [ 10, 99, 15 ], [ 3, 101, 108, 105 ], - [ 4, 116, 110 ], [ 43, 120, 73 ], [ 100, 129, 125 ], [ 109, 132 ], - [ 57, 37 ], [ 110, 13, 95, 144 ], [ 67, 149, 118 ], [ 58, 14 ], - [ 156, 154 ], [ 102, 159 ], [ 111, 130, 135, 80 ], [ 168, 119, 170, 167 ], - [ 21, 171, 172 ], [ 61, 175 ], [ 62, 22, 178 ], [ 112, 81, 20 ], [ 183 ], - [ 186, 187 ], [ 153 ], [ 27, 188 ], [ 153 ], [ 32, 189 ], [ 191, 190, 27 ], - [ 64 ], [ 65, 31 ], [ 153 ], [ 96, 195 ], [ 59 ], [ 107, 143, 194 ], - [ 98, 82, 136 ], [ 58, 210 ], [ 40, 17 ], [ 151, 83 ], [ 51, 199 ], - [ 16, 200, 216 ], [ 7, 218 ], [ 114, 131, 139, 78, 212 ], [ 3, 44, 220 ], - [ 115, 77, 46, 222 ], [ 48, 5, 42 ], [ 41 ], [ 16, 202, 214 ], - [ 99, 49, 210 ], [ 69, 145, 180 ], [ 75, 221, 146 ], [ 71, 185 ], - [ 117, 222, 147 ], [ 95, 140, 59 ], [ 15, 57, 152 ], [ 97, 203 ], - [ 18, 225, 223 ], [ 18, 226, 173 ], [ 21, 68, 227, 176 ], [ 71, 182 ], - [ 35, 28, 30 ], [ 32, 192, 29 ], [ 118, 231 ], [ 7, 234, 229 ], - [ 119, 236, 230 ], [ 121, 177 ], [ 148, 227, 69 ], [ 18, 228, 196 ], - [ 149, 62, 70, 231 ], [ 199, 237, 6 ], [ 47, 120, 76 ], [ 219, 122 ], - [ 220, 123, 75 ], [ 9, 78, 85, 96 ], [ 101, 12, 98, 242, 142 ], - [ 78, 39, 150 ], [ 103, 124, 164, 143, 162 ], [ 104, 80, 38, 179 ], - [ 240, 193 ], [ 79, 195 ], [ 70, 241, 245, 53 ], [ 108, 242, 81, 197 ], - [ 248, 182 ], [ 88, 26 ], [ 7, 250, 183 ], [ 45, 88, 92 ], [ 174, 184 ], - [ 217, 249 ], [ 218, 250, 91 ], [ 226, 228, 90 ], [ 61, 71, 93, 248 ], - [ 109, 252, 97 ], [ 105, 142, 197 ], [ 113, 254 ], [ 100, 240, 209, 251 ], - [ 116, 48, 144, 56 ], [ 16, 106, 258, 256 ], [ 100, 261, 259 ], - [ 60, 161, 262 ], [ 255, 260, 244, 263 ], [ 62, 103, 107, 245, 265 ], - [ 66, 259, 268 ], [ 67, 269, 266 ], [ 68, 260, 208, 267 ], - [ 72, 261, 104, 84, 268 ], [ 11, 272, 113 ], [ 109, 274 ], - [ 270, 103, 166 ], [ 24, 104, 111, 181 ], [ 73, 278 ], [ 271, 101, 280 ], - [ 47, 9, 114, 281 ], [ 74, 115, 274, 117 ], [ 76, 281, 275, 54 ], - [ 229, 283 ], [ 60, 285, 282 ], [ 51, 44, 237, 123 ], [ 232, 169 ], - [ 213, 215, 238 ], [ 214, 216, 239, 122 ], [ 255, 126, 286 ], [ 256, 290 ], - [ 257, 289 ], [ 121, 204, 287 ], [ 148, 246, 288, 127 ], [ 258, 124, 290 ], - [ 270, 124 ], [ 271, 12, 295 ], [ 272, 131, 134 ], [ 122, 294, 205 ], - [ 273, 295 ], [ 270, 164, 163 ], [ 251, 193 ], [ 257, 19, 297 ], - [ 139, 39, 198 ], [ 271, 98, 201, 299 ], [ 252, 138, 203 ], - [ 273, 299, 206 ], [ 259, 125, 251, 301 ], [ 260, 126, 137, 300 ], - [ 274, 132, 252, 46, 147 ], [ 127, 165 ], [ 133, 211 ], - [ 275, 134, 141, 212 ], [ 233, 170, 121 ], [ 234, 21, 148, 283 ], - [ 142, 136 ], [ 46, 138, 79 ], [ 144, 140, 151 ], [ ], [ 155 ], [ 153 ], - [ 35, 155 ], [ 153 ], [ 35, 157 ], [ 262 ], [ 224 ], [ 225, 160 ], - [ 263, 286, 296, 300 ], [ 276, 243 ], [ 255, 243, 137, 296 ], [ 287, 302 ], - [ 293, 244 ], [ 307, 282, 169 ], [ 61, 60, 309, 307 ], [ 308, 284, 27 ], - [ 309, 285, 32, 169 ], [ 168, 23, 310 ], [ 170, 310, 31 ], [ 154, 174 ], - [ 155, 28 ], [ 226, 33 ], [ 167, 230, 177 ], [ 169, 235, 29 ], - [ 227, 172, 34 ], [ 265, 162, 194 ], [ 177, 165, 207 ], [ 178, 245, 166 ], - [ 196 ], [ 63, 55 ], [ 155 ], [ 228 ], [ 28, 157 ], [ 157 ], [ 187 ], - [ 191, 33 ], [ 158, 187 ], [ 64, 158, 186 ], [ 190, 188 ], [ 253 ], - [ 267, 300 ], [ 150 ], [ 154, 184 ], [ 268, 301, 179 ], [ 201, 82 ], - [ 202, 87 ], [ 67, 45, 317 ], [ 277, 240, 313 ], [ 67, 88, 315 ], - [ 254, 198 ], [ 232, 305 ], [ 238, 312 ], [ 279, 313 ], [ 235, 302 ], - [ 236, 303, 207 ], [ 258, 247, 164, 298 ], [ 152, 42 ], [ 294, 241, 166 ], - [ 280, 295, 299, 242, 20 ], [ 148, 314 ], [ 149, 315, 213 ], - [ 148, 316, 172 ], [ 149, 317, 22, 215 ], [ 93, 175 ], [ 94, 23, 217 ], - [ 70, 215, 178 ], [ 72, 216, 24, 219 ], [ 219, 84, 211, 181 ], - [ 281, 85, 212, 25 ], [ 154, 224 ], [ 155, 157 ], [ 156, 158, 224 ], - [ 156, 64, 174 ], [ 170, 236, 65, 177 ], [ 156, 184 ], [ 86, 318 ], - [ 282, 235 ], [ 283, 176, 69 ], [ 90, 308 ], [ 93, 309, 232 ], - [ 94, 168, 233, 318 ], [ 284, 188 ], [ 285, 192, 235 ], - [ 202, 200, 89, 239 ], [ 314, 316, 91 ], [ 315, 317, 92, 238 ], - [ 106, 247, 253 ], [ 128, 244, 145 ], [ 261, 129, 209, 80, 301 ], - [ 264, 19, 304 ], [ 288, 303, 165 ], [ 227, 244, 208, 180 ], - [ 233, 319, 204 ], [ 269, 243, 311 ], [ 173, 196, 90 ], [ 93, 185 ], - [ 94, 55, 249 ], [ 256, 253, 298 ], [ 272, 139, 254, 141 ], [ 266, 311 ], - [ 278, 201, 206 ], [ 21, 264, 257, 288, 320 ], [ 118, 266, 322 ], - [ 119, 102, 292, 321 ], [ 149, 269, 255, 128, 322 ], [ 256, 324 ], - [ 257, 303, 323 ], [ 258, 103, 241, 324 ], [ 223, 160 ], [ 320, 323, 165 ], - [ 168, 102, 319, 325 ], [ 176, 263, 267, 180 ], [ 229, 326 ], - [ 230, 323, 207 ], [ 231, 324, 265, 53 ], [ 234, 264, 246, 326 ], - [ 22, 255, 276, 293 ], [ 44, 100, 277, 327 ], [ 120, 271, 278, 273 ], - [ 123, 327, 279, 133 ], [ 272, 114, 275 ], [ 273, 280, 146 ], - [ 171, 264, 306 ], [ 200, 106, 328 ], [ 237, 277, 279 ], [ 239, 328, 205 ], - [ 327, 261, 111, 211 ], [ 220, 108, 280, 112, 221 ], [ 223, 284 ], - [ 318, 167, 121 ], [ 224, 187 ], [ 225, 190, 284 ], [ 320, 289 ], - [ 169, 305, 291 ], [ 170, 319, 292, 287 ], [ 321 ], [ 322, 286 ], - [ 284, 160 ], [ 285, 161, 291 ], [ 172, 288, 306 ], [ 215, 128, 312, 293 ], - [ 327, 129, 130 ], [ 320, 304, 297 ], [ 321, 159 ], [ 322, 311, 296 ], - [ 327, 209, 313, 135 ], [ 323, 289, 297 ], [ 324, 290, 298, 162 ], [ 291 ], - [ 292, 302 ], [ 325, 159 ], [ 308, 160 ], [ 310, 319 ], [ 173, 223, 308 ], - [ 174, 224, 186 ], [ 226, 225, 191, 308 ], [ 309, 175, 189 ], [ 326, 304 ], - [ 316, 246, 306 ], [ 328, 247, 163 ], [ 233, 249 ], [ 234, 250, 314 ], - [ 233, 217, 310 ], [ 234, 218, 171, 316 ], [ 248, 307, 232 ], - [ 309, 161, 305 ], [ 167, 325, 321, 287 ], [ 282, 262, 291 ], - [ 283, 326, 320, 127 ], [ 321, 302 ], [ 322, 263, 145 ], [ 307, 262, 305 ], - [ 318, 325, 204 ], [ 216, 258, 328, 270, 294 ], [ 317, 269, 276, 312 ] ] +> +gap> IsIsomorphicDigraph(l, DigraphReflexiveTransitiveClosure(Digraph( +> [[2, 50], [8, 52, 40], [16, 72, 66], [74, 11], [77, 83, 36], +> [89, 87], [94, 63, 86], [10, 99, 15], [3, 101, 108, 105], +> [4, 116, 110], [43, 120, 73], [100, 129, 125], [109, 132], +> [57, 37], [110, 13, 95, 144], [67, 149, 118], [58, 14], +> [156, 154], [102, 159], [111, 130, 135, 80], [168, 119, 170, 167], +> [21, 171, 172], [61, 175], [62, 22, 178], [112, 81, 20], [183], +> [186, 187], [153], [27, 188], [153], [32, 189], [191, 190, 27], +> [64], [65, 31], [153], [96, 195], [59], [107, 143, 194], +> [98, 82, 136], [58, 210], [40, 17], [151, 83], [51, 199], +> [16, 200, 216], [7, 218], [114, 131, 139, 78, 212], [3, 44, 220], +> [115, 77, 46, 222], [48, 5, 42], [41], [16, 202, 214], +> [99, 49, 210], [69, 145, 180], [75, 221, 146], [71, 185], +> [117, 222, 147], [95, 140, 59], [15, 57, 152], [97, 203], +> [18, 225, 223], [18, 226, 173], [21, 68, 227, 176], [71, 182], +> [35, 28, 30], [32, 192, 29], [118, 231], [7, 234, 229], +> [119, 236, 230], [121, 177], [148, 227, 69], [18, 228, 196], +> [149, 62, 70, 231], [199, 237, 6], [47, 120, 76], [219, 122], +> [220, 123, 75], [9, 78, 85, 96], [101, 12, 98, 242, 142], +> [78, 39, 150], [103, 124, 164, 143, 162], [104, 80, 38, 179], +> [240, 193], [79, 195], [70, 241, 245, 53], [108, 242, 81, 197], +> [248, 182], [88, 26], [7, 250, 183], [45, 88, 92], [174, 184], +> [217, 249], [218, 250, 91], [226, 228, 90], [61, 71, 93, 248], +> [109, 252, 97], [105, 142, 197], [113, 254], [100, 240, 209, 251], +> [116, 48, 144, 56], [16, 106, 258, 256], [100, 261, 259], +> [60, 161, 262], [255, 260, 244, 263], [62, 103, 107, 245, 265], +> [66, 259, 268], [67, 269, 266], [68, 260, 208, 267], +> [72, 261, 104, 84, 268], [11, 272, 113], [109, 274], +> [270, 103, 166], [24, 104, 111, 181], [73, 278], [271, 101, 280], +> [47, 9, 114, 281], [74, 115, 274, 117], [76, 281, 275, 54], +> [229, 283], [60, 285, 282], [51, 44, 237, 123], [232, 169], +> [213, 215, 238], [214, 216, 239, 122], [255, 126, 286], [256, 290], +> [257, 289], [121, 204, 287], [148, 246, 288, 127], [258, 124, 290], +> [270, 124], [271, 12, 295], [272, 131, 134], [122, 294, 205], +> [273, 295], [270, 164, 163], [251, 193], [257, 19, 297], +> [139, 39, 198], [271, 98, 201, 299], [252, 138, 203], +> [273, 299, 206], [259, 125, 251, 301], [260, 126, 137, 300], +> [274, 132, 252, 46, 147], [127, 165], [133, 211], +> [275, 134, 141, 212], [233, 170, 121], [234, 21, 148, 283], +> [142, 136], [46, 138, 79], [144, 140, 151], [], [155], [153], +> [35, 155], [153], [35, 157], [262], [224], [225, 160], +> [263, 286, 296, 300], [276, 243], [255, 243, 137, 296], [287, 302], +> [293, 244], [307, 282, 169], [61, 60, 309, 307], [308, 284, 27], +> [309, 285, 32, 169], [168, 23, 310], [170, 310, 31], [154, 174], +> [155, 28], [226, 33], [167, 230, 177], [169, 235, 29], +> [227, 172, 34], [265, 162, 194], [177, 165, 207], [178, 245, 166], +> [196], [63, 55], [155], [228], [28, 157], [157], [187], +> [191, 33], [158, 187], [64, 158, 186], [190, 188], [253], +> [267, 300], [150], [154, 184], [268, 301, 179], [201, 82], +> [202, 87], [67, 45, 317], [277, 240, 313], [67, 88, 315], +> [254, 198], [232, 305], [238, 312], [279, 313], [235, 302], +> [236, 303, 207], [258, 247, 164, 298], [152, 42], [294, 241, 166], +> [280, 295, 299, 242, 20], [148, 314], [149, 315, 213], +> [148, 316, 172], [149, 317, 22, 215], [93, 175], [94, 23, 217], +> [70, 215, 178], [72, 216, 24, 219], [219, 84, 211, 181], +> [281, 85, 212, 25], [154, 224], [155, 157], [156, 158, 224], +> [156, 64, 174], [170, 236, 65, 177], [156, 184], [86, 318], +> [282, 235], [283, 176, 69], [90, 308], [93, 309, 232], +> [94, 168, 233, 318], [284, 188], [285, 192, 235], +> [202, 200, 89, 239], [314, 316, 91], [315, 317, 92, 238], +> [106, 247, 253], [128, 244, 145], [261, 129, 209, 80, 301], +> [264, 19, 304], [288, 303, 165], [227, 244, 208, 180], +> [233, 319, 204], [269, 243, 311], [173, 196, 90], [93, 185], +> [94, 55, 249], [256, 253, 298], [272, 139, 254, 141], [266, 311], +> [278, 201, 206], [21, 264, 257, 288, 320], [118, 266, 322], +> [119, 102, 292, 321], [149, 269, 255, 128, 322], [256, 324], +> [257, 303, 323], [258, 103, 241, 324], [223, 160], [320, 323, 165], +> [168, 102, 319, 325], [176, 263, 267, 180], [229, 326], +> [230, 323, 207], [231, 324, 265, 53], [234, 264, 246, 326], +> [22, 255, 276, 293], [44, 100, 277, 327], [120, 271, 278, 273], +> [123, 327, 279, 133], [272, 114, 275], [273, 280, 146], +> [171, 264, 306], [200, 106, 328], [237, 277, 279], [239, 328, 205], +> [327, 261, 111, 211], [220, 108, 280, 112, 221], [223, 284], +> [318, 167, 121], [224, 187], [225, 190, 284], [320, 289], +> [169, 305, 291], [170, 319, 292, 287], [321], [322, 286], +> [284, 160], [285, 161, 291], [172, 288, 306], [215, 128, 312, 293], +> [327, 129, 130], [320, 304, 297], [321, 159], [322, 311, 296], +> [327, 209, 313, 135], [323, 289, 297], [324, 290, 298, 162], [291], +> [292, 302], [325, 159], [308, 160], [310, 319], [173, 223, 308], +> [174, 224, 186], [226, 225, 191, 308], [309, 175, 189], [326, 304], +> [316, 246, 306], [328, 247, 163], [233, 249], [234, 250, 314], +> [233, 217, 310], [234, 218, 171, 316], [248, 307, 232], +> [309, 161, 305], [167, 325, 321, 287], [282, 262, 291], +> [283, 326, 320, 127], [321, 302], [322, 263, 145], [307, 262, 305], +> [318, 325, 204], [216, 258, 328, 270, 294], [317, 269, 276, 312]]))); +true gap> S := Semigroup([ > Transformation([1, 5, 4, 5, 2]), Transformation([4, 5, 1, 3, 5])]);; gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 32, 55 ], [ 48, 11 ], [ 28, 60, 62 ], [ 12, 66, 21 ], [ 70, 73 ], - [ 85, 78, 19 ], [ 83, 57 ], [ 87, 17 ], [ 93 ], [ 114, 109, 58 ], - [ 113, 56 ], [ 65, 116, 107 ], [ 118, 9 ], [ 88, 127 ], [ 22 ], - [ 14, 128 ], [ 129 ], [ 131 ], [ 140, 136 ], [ 17, 142 ], [ 107, 143 ], - [ ], [ 144, 148, 89 ], [ 150, 90 ], [ 152 ], [ 160 ], [ 40, 41, 31 ], - [ 61, 30 ], [ 79, 145, 133 ], [ 144, 110, 92 ], [ 117, 42, 156, 157 ], - [ 3, 27, 39, 43 ], [ 59, 161, 154 ], [ 166, 38 ], [ 162, 70, 37 ], - [ 147, 163, 71, 135 ], [ 146, 164, 72, 96 ], [ 120, 167, 170 ], - [ 60, 34, 171 ], [ 28, 172, 156 ], [ 172, 34, 157 ], [ 122, 173 ], - [ 62, 153, 52, 171 ], [ 66, 111 ], [ 4, 112 ], [ 51 ], [ 168, 102 ], - [ 4, 113, 44 ], [ 17, 138 ], [ 12, 104 ], [ 17 ], [ 63, 158, 53 ], - [ 64, 159 ], [ 43, 160 ], [ 54, 26 ], [ 103, 7 ], [ 49 ], [ 177, 175, 6 ], - [ 162, 108 ], [ 35, 5, 179 ], [ 35, 59, 110 ], [ 10, 63, 179 ], - [ 114, 64 ], [ 115, 74 ], [ 8, 180, 20 ], [ 116, 16, 143 ], [ 181, 18 ], - [ 183, 67 ], [ 184, 68 ], [ 45, 72 ], [ 151, 81, 130 ], [ 150, 112, 182 ], - [ 69, 74 ], [ 184 ], [ 145, 185, 132 ], [ 151, 25 ], [ 163, 82, 134 ], - [ 86, 67, 136 ], [ 36, 186, 77, 137 ], [ 87, 138 ], [ 84, 46 ], - [ 169, 139 ], [ 8, 80, 49 ], [ 8, 51 ], [ 29, 86, 75, 140 ], - [ 36, 185, 181, 141 ], [ 15, 129 ], [ 15, 126 ], [ 187, 189, 75 ], - [ 191, 76 ], [ 98, 99 ], [ 187, 176, 29 ], [ 58, 105, 97 ], [ 174, 192 ], - [ 194 ], [ 188, 193, 182, 36 ], [ 175, 106 ], [ 92, 196 ], [ 196, 95 ], - [ 180, 80 ], [ 104, 81 ], [ 195, 82 ], [ 65, 83, 100 ], [ 65, 84 ], - [ 177, 91, 106 ], [ 178, 95 ], [ 20, 197 ], [ 47, 164, 174 ], - [ 115, 69, 175 ], [ 149, 108, 37, 176 ], [ 116, 100 ], [ 50, 101 ], - [ 12, 103, 111 ], [ 23, 30, 115, 177 ], [ 148, 37, 184, 178 ], - [ 180, 14, 197 ], [ 122, 123, 91 ], [ 10, 124, 121, 93 ], [ 108, 198, 94 ], - [ 199, 95 ], [ 109, 125, 97 ], [ 30, 200, 98 ], [ 200, 120, 99 ], - [ 114, 117, 125, 105 ], [ 115, 120, 106 ], [ 22 ], [ 126 ], [ 127 ], - [ 22 ], [ 152, 46 ], [ 25, 130 ], [ 201, 203 ], [ 201, 137 ], [ 139, 205 ], - [ 202, 205, 130 ], [ 141, 18 ], [ 204, 134, 135 ], [ 129 ], [ 206 ], - [ 132, 133, 141 ], [ 203, 135, 131 ], [ 129, 126 ], [ 197, 128 ], - [ 149, 187 ], [ 186, 201 ], [ 168, 150, 188 ], [ 169, 151, 202 ], - [ 146, 24, 189 ], [ 47, 146, 190 ], [ 50, 191 ], [ 84, 152 ], [ 51 ], - [ 118, 158, 155 ], [ 119, 165 ], [ 121, 159 ], [ 122, 207 ], - [ 123, 173, 207, 38 ], [ 124, 31, 159 ], [ 125, 38 ], [ 13, 153 ], - [ 162, 165 ], [ 2, 45, 164 ], [ 169, 81, 205 ], [ 168, 112, 193 ], [ 198 ], - [ 35, 161, 170 ], [ 199 ], [ 11, 50, 195 ], [ 7, 84, 206 ], [ 199, 165 ], - [ 179, 155, 53 ], [ 61, 166, 33, 207 ], [ 200, 167 ], [ 102, 193, 77 ], - [ 178, 68, 78 ], [ 190, 174, 96, 79 ], [ 89, 92, 178, 85 ], - [ 189, 96, 183, 86 ], [ 109, 64, 73 ], [ 87, 88, 142 ], [ 76, 71, 131 ], - [ 191, 101, 71 ], [ 90, 182, 181 ], [ 24, 72, 183 ], [ 147, 76, 203 ], - [ 147, 82, 204 ], [ 190, 145 ], [ 195, 191, 147 ], [ 188, 90, 185 ], - [ 102, 188, 186 ], [ 104, 151 ], [ 193 ], [ 195, 101, 163 ], [ 96, 192 ], - [ 56, 104, 169 ], [ 176, 94, 194 ], [ 142, 127 ], [ 164, 192 ], - [ 37, 198, 194 ], [ 110, 119, 199, 196 ], [ 204 ], [ 206, 152 ], - [ 202, 25 ], [ 139, 202 ], [ 206, 46 ], [ 57, 51 ], [ 200, 154, 170 ] ] +> +gap> IsIsomorphicDigraph(l, DigraphReflexiveTransitiveClosure(Digraph( +> [[32, 55], [48, 11], [28, 60, 62], [12, 66, 21], [70, 73], +> [85, 78, 19], [83, 57], [87, 17], [93], [114, 109, 58], +> [113, 56], [65, 116, 107], [118, 9], [88, 127], [22], +> [14, 128], [129], [131], [140, 136], [17, 142], [107, 143], +> [], [144, 148, 89], [150, 90], [152], [160], [40, 41, 31], +> [61, 30], [79, 145, 133], [144, 110, 92], [117, 42, 156, 157], +> [3, 27, 39, 43], [59, 161, 154], [166, 38], [162, 70, 37], +> [147, 163, 71, 135], [146, 164, 72, 96], [120, 167, 170], +> [60, 34, 171], [28, 172, 156], [172, 34, 157], [122, 173], +> [62, 153, 52, 171], [66, 111], [4, 112], [51], [168, 102], +> [4, 113, 44], [17, 138], [12, 104], [17], [63, 158, 53], +> [64, 159], [43, 160], [54, 26], [103, 7], [49], [177, 175, 6], +> [162, 108], [35, 5, 179], [35, 59, 110], [10, 63, 179], +> [114, 64], [115, 74], [8, 180, 20], [116, 16, 143], [181, 18], +> [183, 67], [184, 68], [45, 72], [151, 81, 130], [150, 112, 182], +> [69, 74], [184], [145, 185, 132], [151, 25], [163, 82, 134], +> [86, 67, 136], [36, 186, 77, 137], [87, 138], [84, 46], +> [169, 139], [8, 80, 49], [8, 51], [29, 86, 75, 140], +> [36, 185, 181, 141], [15, 129], [15, 126], [187, 189, 75], +> [191, 76], [98, 99], [187, 176, 29], [58, 105, 97], [174, 192], +> [194], [188, 193, 182, 36], [175, 106], [92, 196], [196, 95], +> [180, 80], [104, 81], [195, 82], [65, 83, 100], [65, 84], +> [177, 91, 106], [178, 95], [20, 197], [47, 164, 174], +> [115, 69, 175], [149, 108, 37, 176], [116, 100], [50, 101], +> [12, 103, 111], [23, 30, 115, 177], [148, 37, 184, 178], +> [180, 14, 197], [122, 123, 91], [10, 124, 121, 93], [108, 198, 94], +> [199, 95], [109, 125, 97], [30, 200, 98], [200, 120, 99], +> [114, 117, 125, 105], [115, 120, 106], [22], [126], [127], +> [22], [152, 46], [25, 130], [201, 203], [201, 137], [139, 205], +> [202, 205, 130], [141, 18], [204, 134, 135], [129], [206], +> [132, 133, 141], [203, 135, 131], [129, 126], [197, 128], +> [149, 187], [186, 201], [168, 150, 188], [169, 151, 202], +> [146, 24, 189], [47, 146, 190], [50, 191], [84, 152], [51], +> [118, 158, 155], [119, 165], [121, 159], [122, 207], +> [123, 173, 207, 38], [124, 31, 159], [125, 38], [13, 153], +> [162, 165], [2, 45, 164], [169, 81, 205], [168, 112, 193], [198], +> [35, 161, 170], [199], [11, 50, 195], [7, 84, 206], [199, 165], +> [179, 155, 53], [61, 166, 33, 207], [200, 167], [102, 193, 77], +> [178, 68, 78], [190, 174, 96, 79], [89, 92, 178, 85], +> [189, 96, 183, 86], [109, 64, 73], [87, 88, 142], [76, 71, 131], +> [191, 101, 71], [90, 182, 181], [24, 72, 183], [147, 76, 203], +> [147, 82, 204], [190, 145], [195, 191, 147], [188, 90, 185], +> [102, 188, 186], [104, 151], [193], [195, 101, 163], [96, 192], +> [56, 104, 169], [176, 94, 194], [142, 127], [164, 192], +> [37, 198, 194], [110, 119, 199, 196], [204], [206, 152], +> [202, 25], [139, 202], [206, 46], [57, 51], [200, 154, 170]]))); +true gap> S := Semigroup([ > Transformation([1, 3, 5, 5, 3]), Transformation([4, 5, 5, 5, 2])]);; gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 45, 32, 36 ], [ 48, 62 ], [ 65, 64 ], [ 10, 67, 14, 69 ], - [ 24, 73, 72, 80 ], [ 25 ], [ 11, 85, 15, 30 ], [ 72, 12, 92, 93, 13 ], - [ 104, 107 ], [ 71, 111, 113 ], [ 86, 116, 102 ], [ 74, 95, 119, 120 ], - [ 75, 17, 125, 126, 88 ], [ 40, 127, 128 ], [ 41, 129, 122 ], - [ 13, 130, 19 ], [ 76, 135, 136, 96 ], [ 22, 137, 68 ], [ 88, 38, 27 ], - [ 97, 81, 77 ], [ 83, 26 ], [ 52, 140, 112 ], [ 98, 18, 141 ], - [ 99, 89, 143 ], [ ], [ 28 ], [ 90, 147 ], [ 56 ], [ 101, 26 ], - [ 102, 31, 122 ], [ 103, 123, 29 ], [ 8, 151, 16 ], [ 5, 104, 8, 37, 152 ], - [ 78, 105, 91 ], [ 79, 106 ], [ 157, 151 ], [ 80, 108, 93, 34 ], - [ 94, 147 ], [ 81, 110, 138, 148 ], [ 144, 153 ], [ 145, 132 ], - [ 82, 95, 156, 158 ], [ 55, 22, 155 ], [ 58, 28 ], [ 33, 9, 157 ], - [ 50, 55, 160 ], [ 51, 162, 59 ], [ 164, 49 ], [ 167, 53 ], [ 52, 168 ], - [ 54, 165 ], [ 3, 169, 159 ], [ 63, 57 ], [ 6, 56 ], [ 52, 171 ], [ 25 ], - [ 172 ], [ 56 ], [ 165, 58 ], [ 163 ], [ 161 ], [ 164, 60 ], - [ 52, 170, 172 ], [ 66 ], [ 6, 66 ], [ 25 ], [ 111, 127, 23, 173 ], - [ 112, 70 ], [ 113, 173, 128, 7 ], [ 114, 26 ], [ 174, 40, 175 ], - [ 89, 74, 177, 75 ], [ 99, 82, 74, 182 ], [ 117, 178, 185 ], - [ 121, 76, 187 ], [ 131, 189 ], [ 179, 190 ], [ 142, 180, 176 ], - [ 181, 70 ], [ 143, 182, 177, 78 ], [ 183, 18, 139, 190 ], - [ 146, 178, 192 ], [ 54, 28 ], [ 115, 21, 29 ], [ 116, 129, 84, 31 ], - [ 193, 41, 149 ], [ 162, 83, 44 ], [ 20, 96, 39, 94, 90 ], - [ 117, 195, 121 ], [ 77, 100, 148, 197 ], [ 176, 118, 124 ], - [ 177, 119, 125 ], [ 177, 120, 126, 91 ], [ 81, 109, 197 ], - [ 178, 199, 200, 17 ], [ 97, 109, 110, 100 ], [ 53, 183, 179 ], - [ 43, 203 ], [ 146, 117, 205 ], [ 179, 206, 207 ], [ 44 ], [ 149, 103 ], - [ 150, 101 ], [ 73, 42, 12, 108, 208 ], [ 180, 154, 118 ], [ 181 ], - [ 208 ], [ 182, 158, 120, 105 ], [ 183, 206 ], [ 183, 201, 207 ], - [ 174, 98, 209 ], [ 159, 114 ], [ 175, 209, 11 ], [ 161, 28 ], [ 87, 101 ], - [ 193, 115, 103 ], [ 196, 211 ], [ 184, 198 ], [ 185, 199 ], - [ 185, 200, 118 ], [ 131, 213, 20 ], [ 132, 123 ], [ 133, 26 ], - [ 186, 134, 138 ], [ 187, 135, 94 ], [ 187, 136, 39, 124 ], - [ 144, 18, 214 ], [ 153, 214, 15 ], [ 145, 21, 123 ], [ 125, 38 ], - [ 49, 216, 97 ], [ 133 ], [ 165, 28 ], [ 188, 201 ], [ 189, 109 ], - [ 189, 110, 134 ], [ 140, 21, 70 ], [ 139, 201, 35 ], [ 202, 137, 79 ], - [ 169, 83, 114 ], [ 203, 137, 84 ], [ 204, 194 ], [ 205, 195, 142 ], - [ 50, 22, 217 ], [ 51, 83, 133 ], [ 2, 196, 219 ], [ 197 ], - [ 190, 207, 35 ], [ 150, 132 ], [ 59, 133, 44 ], [ 92, 130 ], - [ 80, 208, 92 ], [ 217, 41 ], [ 191, 198 ], [ 171, 140, 87 ], [ 192, 199 ], - [ 152, 107 ], [ 192, 200, 154 ], [ 64, 161 ], [ 168, 171, 47 ], [ 66, 56 ], - [ 54, 58 ], [ 160, 166 ], [ 46, 167, 163 ], [ 56 ], [ 168, 170 ], - [ 50, 63, 166 ], [ 169, 51 ], [ 65, 54, 161 ], [ 169, 61 ], [ 169, 162 ], - [ 159, 61 ], [ 209, 214, 141, 85 ], [ 46, 144, 43, 220 ], [ 220, 153, 86 ], - [ 194, 184, 69, 186 ], [ 195, 185, 4, 187, 176 ], [ 196, 222, 76 ], - [ 57, 223 ], [ 204, 191, 184 ], [ 61, 114 ], [ 205, 192, 185, 180 ], - [ 63, 22, 202, 223 ], [ 210, 221, 113 ], [ 211, 222, 10, 184 ], - [ 212, 188, 128 ], [ 213, 189, 14, 186 ], [ 215, 153 ], [ 216, 40, 188 ], - [ 223, 68, 79 ], [ 218, 221 ], [ 219, 222, 191 ], [ 47, 145, 87, 150 ], - [ 210, 173, 212 ], [ 211, 67, 213, 194 ], [ 48, 225, 131 ], [ 190, 206 ], - [ 221, 134 ], [ 222, 135 ], [ 222, 136, 198 ], [ 202, 106 ], - [ 170, 140, 181 ], [ 155, 115 ], [ 218, 210 ], [ 219, 211, 204 ], [ 223 ], - [ 223, 106 ], [ 182, 156, 119 ], [ 220, 203, 116 ], [ 224, 209 ], - [ 225, 111, 210 ], [ 215, 214, 139 ], [ 216, 127, 81, 212 ], - [ 217, 137, 129 ], [ 166, 217, 202 ], [ 167, 144, 183, 215 ], - [ 168, 140, 145 ], [ 60, 224 ], [ 62, 225, 218 ], [ 160, 217, 155, 193 ], - [ 224, 175, 188 ], [ 225, 71, 189, 221 ], [ 172, 112, 181 ], - [ 163, 220, 215 ], [ 164, 174, 216, 224 ] ] +> +gap> IsIsomorphicDigraph(l, DigraphReflexiveTransitiveClosure(Digraph( +> [[45, 32, 36], [48, 62], [65, 64], [10, 67, 14, 69], +> [24, 73, 72, 80], [25], [11, 85, 15, 30], [72, 12, 92, 93, 13], +> [104, 107], [71, 111, 113], [86, 116, 102], [74, 95, 119, 120], +> [75, 17, 125, 126, 88], [40, 127, 128], [41, 129, 122], +> [13, 130, 19], [76, 135, 136, 96], [22, 137, 68], [88, 38, 27], +> [97, 81, 77], [83, 26], [52, 140, 112], [98, 18, 141], +> [99, 89, 143], [], [28], [90, 147], [56], [101, 26], +> [102, 31, 122], [103, 123, 29], [8, 151, 16], [5, 104, 8, 37, 152], +> [78, 105, 91], [79, 106], [157, 151], [80, 108, 93, 34], +> [94, 147], [81, 110, 138, 148], [144, 153], [145, 132], +> [82, 95, 156, 158], [55, 22, 155], [58, 28], [33, 9, 157], +> [50, 55, 160], [51, 162, 59], [164, 49], [167, 53], [52, 168], +> [54, 165], [3, 169, 159], [63, 57], [6, 56], [52, 171], [25], +> [172], [56], [165, 58], [163], [161], [164, 60], +> [52, 170, 172], [66], [6, 66], [25], [111, 127, 23, 173], +> [112, 70], [113, 173, 128, 7], [114, 26], [174, 40, 175], +> [89, 74, 177, 75], [99, 82, 74, 182], [117, 178, 185], +> [121, 76, 187], [131, 189], [179, 190], [142, 180, 176], +> [181, 70], [143, 182, 177, 78], [183, 18, 139, 190], +> [146, 178, 192], [54, 28], [115, 21, 29], [116, 129, 84, 31], +> [193, 41, 149], [162, 83, 44], [20, 96, 39, 94, 90], +> [117, 195, 121], [77, 100, 148, 197], [176, 118, 124], +> [177, 119, 125], [177, 120, 126, 91], [81, 109, 197], +> [178, 199, 200, 17], [97, 109, 110, 100], [53, 183, 179], +> [43, 203], [146, 117, 205], [179, 206, 207], [44], [149, 103], +> [150, 101], [73, 42, 12, 108, 208], [180, 154, 118], [181], +> [208], [182, 158, 120, 105], [183, 206], [183, 201, 207], +> [174, 98, 209], [159, 114], [175, 209, 11], [161, 28], [87, 101], +> [193, 115, 103], [196, 211], [184, 198], [185, 199], +> [185, 200, 118], [131, 213, 20], [132, 123], [133, 26], +> [186, 134, 138], [187, 135, 94], [187, 136, 39, 124], +> [144, 18, 214], [153, 214, 15], [145, 21, 123], [125, 38], +> [49, 216, 97], [133], [165, 28], [188, 201], [189, 109], +> [189, 110, 134], [140, 21, 70], [139, 201, 35], [202, 137, 79], +> [169, 83, 114], [203, 137, 84], [204, 194], [205, 195, 142], +> [50, 22, 217], [51, 83, 133], [2, 196, 219], [197], +> [190, 207, 35], [150, 132], [59, 133, 44], [92, 130], +> [80, 208, 92], [217, 41], [191, 198], [171, 140, 87], [192, 199], +> [152, 107], [192, 200, 154], [64, 161], [168, 171, 47], [66, 56], +> [54, 58], [160, 166], [46, 167, 163], [56], [168, 170], +> [50, 63, 166], [169, 51], [65, 54, 161], [169, 61], [169, 162], +> [159, 61], [209, 214, 141, 85], [46, 144, 43, 220], [220, 153, 86], +> [194, 184, 69, 186], [195, 185, 4, 187, 176], [196, 222, 76], +> [57, 223], [204, 191, 184], [61, 114], [205, 192, 185, 180], +> [63, 22, 202, 223], [210, 221, 113], [211, 222, 10, 184], +> [212, 188, 128], [213, 189, 14, 186], [215, 153], [216, 40, 188], +> [223, 68, 79], [218, 221], [219, 222, 191], [47, 145, 87, 150], +> [210, 173, 212], [211, 67, 213, 194], [48, 225, 131], [190, 206], +> [221, 134], [222, 135], [222, 136, 198], [202, 106], +> [170, 140, 181], [155, 115], [218, 210], [219, 211, 204], [223], +> [223, 106], [182, 156, 119], [220, 203, 116], [224, 209], +> [225, 111, 210], [215, 214, 139], [216, 127, 81, 212], +> [217, 137, 129], [166, 217, 202], [167, 144, 183, 215], +> [168, 140, 145], [60, 224], [62, 225, 218], [160, 217, 155, 193], +> [224, 175, 188], [225, 71, 189, 221], [172, 112, 181], +> [163, 220, 215], [164, 174, 216, 224]]))); +true gap> S := OrderEndomorphisms(2);; gap> l := LatticeOfCongruences(S); -> +> gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); [ [ 3 ], [ ], [ 2 ] ] gap> S := OrderEndomorphisms(3);; gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 4 ], [ 3 ], [ ], [ 2 ] ] +> +gap> IsIsomorphicDigraph(DigraphFromDigraph6String("&C|a["), l); +true gap> S := OrderEndomorphisms(4);; gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 5 ], [ 3 ], [ 4 ], [ ], [ 2 ] ] +> +gap> IsIsomorphicDigraph(DigraphFromDigraph6String("&D}wof_"), l); +true gap> S := PartitionMonoid(2);; gap> l := LatticeOfCongruences(S); -> -gap> OutNeighbours(DigraphReflexiveTransitiveReduction(l)); -[ [ 3, 4, 9 ], [ 6, 11 ], [ 2, 5, 8 ], [ 2, 10 ], [ 6, 12 ], [ 13 ], [ ], - [ 11, 12 ], [ 5, 10 ], [ 6 ], [ 13 ], [ 13 ], [ 7 ] ] - -# Check robustness against non-free infinite semigroups -# TODO(later)! +> +gap> IsIsomorphicDigraph(l, +> DigraphFromDigraph6String("&L~~gpU{yksMEB@?_?XozWKcAI@B?__")); +true # SEMIGROUPS_UnbindVariables gap> Unbind(P); diff --git a/tst/extreme/inverse.tst b/tst/extreme/inverse.tst index dcf1ca7c6..d65ca486b 100644 --- a/tst/extreme/inverse.tst +++ b/tst/extreme/inverse.tst @@ -898,8 +898,8 @@ gap> s := InverseSemigroup([PartialPerm([1, 2, 3, 5], [2, 7, 3, 4]), > PartialPerm([1, 2, 3, 6], [7, 3, 4, 2])]);; gap> cong := SemigroupCongruence(s, > [PartialPerm([4], [7]), PartialPerm([2], [1])]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> # InverseCongTest3: Try some methods gap> x := PartialPerm([4], [5]);; @@ -914,7 +914,7 @@ false # InverseCongTest4: Congruence classes gap> classx := EquivalenceClassOfElement(cong, x); - +<2-sided congruence class of [4,5]> gap> classy := EquivalenceClassOfElement(cong, y);; gap> classz := EquivalenceClassOfElement(cong, z);; gap> classx = classy; @@ -944,8 +944,8 @@ gap> ccong := SemigroupCongruence(s, pairs);; gap> ccong = cong; true gap> ccong := AsSemigroupCongruenceByGeneratingPairs(cong); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> [x, y] in ccong; false gap> [x, z] in ccong; diff --git a/tst/standard/attributes/properties.tst b/tst/standard/attributes/properties.tst index 31f6d8ffd..6b0912ffc 100644 --- a/tst/standard/attributes/properties.tst +++ b/tst/standard/attributes/properties.tst @@ -1980,8 +1980,8 @@ arguments gap> S := SymmetricInverseMonoid(3);; gap> S := InverseSemigroup(S, rec(acting := true));; gap> C := SemigroupCongruence(S, [[S.1, S.2]]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> K := KernelOfSemigroupCongruence(C); gap> K := InverseSemigroup(K, rec(acting := true)); diff --git a/tst/standard/congruences/cong.tst b/tst/standard/congruences/cong.tst index c6331d79e..66ef6917b 100644 --- a/tst/standard/congruences/cong.tst +++ b/tst/standard/congruences/cong.tst @@ -61,8 +61,8 @@ f the 2nd argument (a 2-sided semigroup congruence) # SemigroupCongruence: Infinite semigroup gap> S := FreeSemigroup(2);; gap> SemigroupCongruence(S, [S.1, S.2]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> # SemigroupCongruence: Simple semigroup gap> S := Semigroup(MinimalIdeal(FullTransformationMonoid(5)));; @@ -89,11 +89,11 @@ gap> S := InverseSemigroup([PartialPerm([1, 2, 3], [1, 4, 2]), > PartialPerm([1, 2, 3], [2, 3, 4]), > PartialPerm([1, 2, 4], [2, 1, 3])]);; gap> SemigroupCongruence(S, [S.1, S.2]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> SemigroupCongruence(S, [S.1, S.2], rec(cong_by_ker_trace_threshold := 1024)); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> SemigroupCongruence(S, [S.1, S.2], rec(cong_by_ker_trace_threshold := 0)); with congruence pair (116,1)> @@ -114,8 +114,8 @@ gap> S := InverseSemigroup([PartialPerm([1, 2, 3], [1, 4, 2]), > rec(cong_by_ker_trace_threshold := 100));; gap> SemigroupCongruence(S, [S.1, S.2], > rec(cong_by_ker_trace_threshold := infinity)); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> # SemigroupCongruence: Pairs gap> S := Semigroup([Transformation([3, 3, 3]), @@ -225,7 +225,7 @@ Error, the arguments are not valid for this function # LeftSemigroupCongruence: Pairs gap> S := Semigroup([Transformation([3, 3, 3]), -> Transformation([3, 4, 3, 3])]);; +> Transformation([3, 4, 3, 3])]);; gap> pairs := [Transformation([3, 4, 3, 3]), Transformation([3, 3, 3, 3])];; gap> cong := LeftSemigroupCongruence(S, pairs); C < C; false gap> EquivalenceClassOfElement(cong, Transformation([3, 4, 3, 3, 6, 6, 6])); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a left congruence) gap> Transformation([3, 4, 3, 3, 6, 6, 6]) in C; false @@ -316,11 +316,11 @@ gap> pair := [Transformation([2, 3, 4, 2]), Transformation([4, 4, 4, 4])];; gap> cong := SemigroupCongruence(S, pair);; gap> class := EquivalenceClassOfElement(cong, Transformation([4, 4, 4, 4]));; gap> class * EquivalenceClasses(cong); -[ , - ] +[ <2-sided congruence class of Transformation( [ 4, 4, 4, 4 ] )>, + <2-sided congruence class of Transformation( [ 2, 2, 2, 2 ] )> ] gap> EquivalenceClasses(cong) * class; -[ , - ] +[ <2-sided congruence class of Transformation( [ 4, 4, 4, 4 ] )>, + <2-sided congruence class of Transformation( [ 4, 4, 4, 4 ] )> ] # Equivalence classes gap> S := Semigroup( @@ -333,9 +333,9 @@ gap> class1b := EquivalenceClassOfElement(cong1, Transformation([2, 3, 4, 2]));; gap> class1c := EquivalenceClassOfElement(cong1, Transformation([1, 4, 3, 4]));; gap> class2 := EquivalenceClassOfElement(cong2, Transformation([4, 4, 4, 4]));; gap> class1a * class1b; - +<2-sided congruence class of Transformation( [ 2, 2, 2, 2 ] )> gap> class1b * class1a; - +<2-sided congruence class of Transformation( [ 4, 4, 4, 4 ] )> gap> class1a * class2; Error, the arguments (cong. classes) are not classes of the same congruence gap> class1a = class1b; @@ -368,9 +368,9 @@ gap> reescong := ReesCongruenceOfSemigroupIdeal(I);; gap> pair := [Transformation([1, 1, 2, 2]), Transformation([1, 1, 1, 1])];; gap> cong := SemigroupCongruence(S, pair);; gap> reesclass := EquivalenceClassOfElement(reescong, pair[1]); - +<2-sided congruence class of Transformation( [ 1, 1, 2, 2 ] )> gap> class := EquivalenceClassOfElement(cong, Transformation([1, 1, 2, 2])); - +<2-sided congruence class of Transformation( [ 1, 1, 2, 2 ] )> gap> class = reesclass; true gap> cong := SemigroupCongruence(S, []);; @@ -450,17 +450,15 @@ true gap> F := FreeSemigroup(2);; gap> cong := ReesCongruenceOfSemigroupIdeal(SemigroupIdeal(F, [F.1]));; gap> EquivalenceRelationLookup(cong); -Error, the range of the argument (an equivalence relation) is not a finite sem\ -igroup +Error, the argument (a 2-sided congruence) must have finite range gap> EquivalenceRelationCanonicalLookup(cong); -Error, the range of the argument (an equivalence relation) is not a finite sem\ -igroup +Error, the argument (a 2-sided congruence) must have finite range gap> cong := LeftSemigroupCongruence(F, [F.1, F.2]);; gap> EquivalenceRelationLookup(cong); -Error, the argument (a congruence) must have finite range +Error, the argument (a left congruence) must have finite range gap> cong := RightSemigroupCongruence(F, [F.1, F.2]);; gap> EquivalenceRelationLookup(cong); -Error, the argument (a congruence) must have finite range +Error, the argument (a right congruence) must have finite range # Equality for congruences over different semigroups (false) gap> S := Semigroup([Transformation([3, 2, 3]), Transformation([3, 1, 1])]);; @@ -571,8 +569,8 @@ true gap> S := FreeSemigroup(2); gap> C := SemigroupCongruence(S, [S.1, S.2]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> EquivalenceRelationPartitionWithSingletons(C); Error, the argument (a congruence) must have finite range @@ -605,7 +603,7 @@ gap> pairs2 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), gap> cong1 := SemigroupCongruence(S, pairs1);; gap> cong2 := SemigroupCongruence(S, pairs2);; gap> cong3 := JoinSemigroupCongruences(cong1, cong2); - with 3 generating pairs> gap> MeetSemigroupCongruences(cong1, cong3) = cong1; true @@ -616,10 +614,419 @@ true gap> MeetSemigroupCongruences(cong3, cong2) = cong2; true gap> MeetSemigroupCongruences(cong1, cong2); - +<2-sided semigroup congruence over with 1 generating pairs> gap> MeetSemigroupCongruences(cong3, cong3) = cong3; true +# MeetRightSemigroupCongruences +gap> S := PartitionMonoid(3);; +gap> pairs1 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), +> Bipartition([[1, 2, -1, -2, -3], [3]])]];; +gap> pairs2 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), +> Bipartition([[1, 2, 3, -1, -2], [-3]])], +> [Bipartition([[1, 2, -1, -2], [3, -3]]), +> Bipartition([[1, 2, -3], [3, -1, -2]])]];; +gap> cong1 := RightSemigroupCongruence(S, pairs1);; +gap> cong2 := RightSemigroupCongruence(S, pairs2);; +gap> cong3 := JoinRightSemigroupCongruences(cong1, cong2); + with 3 generating pairs> +gap> MeetRightSemigroupCongruences(cong1, cong3) = cong1; +true +gap> MeetRightSemigroupCongruences(cong2, cong3) = cong2; +true +gap> MeetRightSemigroupCongruences(cong3, cong1) = cong1; +true +gap> MeetRightSemigroupCongruences(cong3, cong2) = cong2; +true +gap> MeetRightSemigroupCongruences(cong1, cong2); + with 0 generating pairs> +gap> MeetRightSemigroupCongruences(cong3, cong3) = cong3; +true + +# MeetLeftSemigroupCongruences +gap> S := PartitionMonoid(3);; +gap> pairs1 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), +> Bipartition([[1, 2, -1, -2, -3], [3]])]];; +gap> pairs2 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), +> Bipartition([[1, 2, 3, -1, -2], [-3]])], +> [Bipartition([[1, 2, -1, -2], [3, -3]]), +> Bipartition([[1, 2, -3], [3, -1, -2]])]];; +gap> cong1 := LeftSemigroupCongruence(S, pairs1);; +gap> cong2 := LeftSemigroupCongruence(S, pairs2);; +gap> cong3 := JoinLeftSemigroupCongruences(cong1, cong2); + with 3 generating pairs> +gap> MeetLeftSemigroupCongruences(cong1, cong3) = cong1; +true +gap> MeetLeftSemigroupCongruences(cong2, cong3) = cong2; +true +gap> MeetLeftSemigroupCongruences(cong3, cong1) = cong1; +true +gap> MeetLeftSemigroupCongruences(cong3, cong2) = cong2; +true +gap> MeetLeftSemigroupCongruences(cong1, cong2); + with 0 generating pairs> +gap> MeetLeftSemigroupCongruences(cong3, cong3) = cong3; +true + +# +gap> S := PartitionMonoid(3);; +gap> pairs1 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), +> Bipartition([[1, 2, -1, -2, -3], [3]])]];; +gap> pairs2 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), +> Bipartition([[1, 2, 3, -1, -2], [-3]])], +> [Bipartition([[1, 2, -1, -2], [3, -3]]), +> Bipartition([[1, 2, -3], [3, -1, -2]])]];; +gap> C := SemigroupCongruence(S, pairs1); +<2-sided semigroup congruence over with 1 generating pairs> +gap> EquivalenceRelationLookup(C); +[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 30, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 30, 45, 46, 47, 48, 36, 50, 51, 52, 53, 54, 30, 56, 30, 56, 59, + 30, 30, 56, 63, 36, 37, 66, 67, 37, 36, 70, 30, 72, 73, 30, 75, 76, 36, 78, + 79, 56, 30, 56, 83, 30, 85, 36, 87, 67, 37, 90, 30, 92, 93, 30, 56, 96, 56, + 30, 30, 56, 36, 102, 36, 36, 67, 37, 67, 30, 56, 30, 56, 112, 30, 30, 56, + 116, 117, 67, 119, 30, 121, 122, 30, 56, 36, 126, 67, 37, 30, 30, 56, 132, + 30, 30, 56, 30, 56, 67, 36, 67, 37, 67, 30, 56, 30, 30, 36, 148, 36, 36, + 30, 56, 153, 30, 56, 67, 56, 30, 36, 160, 36, 36, 30, 67, 37, 30, 56, 56, + 67, 36, 67, 37, 67, 56, 36, 176, 36, 36, 56, 67, 36, 67, 37, 67, 56, 30, + 36, 67, 37, 67, 36, 67, 37, 67, 67, 37, 56, 67, 36, 67, 37, 67, 37 ] +gap> EquivalenceRelationPartition(C); +[ [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ] ] +gap> EquivalenceRelationPartitionWithSingletons(C); +[ [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ ], + [ ], + [ ], + [ ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ ], + [ ], + [ ], + [ , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ], + [ ] ] + # SEMIGROUPS_UnbindVariables gap> Unbind(F); gap> Unbind(I); diff --git a/tst/standard/congruences/conginv.tst b/tst/standard/congruences/conginv.tst index c2aff9d26..ced9c88ee 100644 --- a/tst/standard/congruences/conginv.tst +++ b/tst/standard/congruences/conginv.tst @@ -27,8 +27,8 @@ gap> cong := SemigroupCongruence(S, rank 5 with 4 generators> with congruence pair (41,16)> gap> ccong := SemigroupCongruenceByGeneratingPairs(S, > [[PartialPerm([4], [4]), PartialPerm([2], [1])]]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> KernelOfSemigroupCongruence(ccong) = cong!.kernel; true gap> ccong := SemigroupCongruenceByGeneratingPairs(S, @@ -72,7 +72,7 @@ gap> AsSortedList(List(TraceOfSemigroupCongruence(cong), AsSortedList)); # Congruence classes gap> classx := EquivalenceClassOfElement(cong, x); - +<2-sided congruence class of [1,2]> gap> classy := EquivalenceClassOfElement(cong, y);; gap> classz := EquivalenceClassOfElement(cong, z);; gap> classx = classy; @@ -103,8 +103,8 @@ gap> ccong := SemigroupCongruence(S, pairs);; gap> ccong = cong; true gap> ccong := AsSemigroupCongruenceByGeneratingPairs(cong); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> [x, y] in ccong; false gap> [x, z] in ccong; @@ -171,7 +171,7 @@ Error, no method found! For debugging hints type ?Recovery from NoMethodFound Error, no 1st choice method found for `in' on 2 arguments gap> EquivalenceClassOfElement(cong, (2, 5, 4)); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) # Congruence Class Multiplication: Bad Input gap> S := InverseSemigroup([PartialPerm([1, 2, 3], [2, 5, 3]), diff --git a/tst/standard/congruences/conglatt.tst b/tst/standard/congruences/conglatt.tst index c3083c1d4..f3d73547f 100644 --- a/tst/standard/congruences/conglatt.tst +++ b/tst/standard/congruences/conglatt.tst @@ -17,40 +17,35 @@ gap> SEMIGROUPS.StartTest(); # Robustness against infinite semigroups gap> S := FreeSemigroup(2);; gap> congs := CongruencesOfSemigroup(S); -Error, the 1st argument (a semigroup) must be finite and have CanUseFroidurePi\ -n +Error, the argument (a semigroup) must be finite and have CanUseFroidurePin gap> poset := PosetOfPrincipalLeftCongruences(S); -Error, the 1st argument (a semigroup) must be finite and have CanUseFroidurePi\ -n +Error, the argument (a semigroup) must be finite and have CanUseFroidurePin gap> poset := PosetOfPrincipalRightCongruences(S); -Error, the 1st argument (a semigroup) must be finite and have CanUseFroidurePi\ -n +Error, the argument (a semigroup) must be finite and have CanUseFroidurePin # LatticeOfCongruences gap> S := PartitionMonoid(2);; gap> l := LatticeOfCongruences(S); -> -gap> InNeighbours(l); -[ [ 1 ], [ 1, 2, 3, 4 ], [ 1, 3 ], [ 1, 4 ], [ 1, 3, 5, 9 ], - [ 1, 2, 3, 4, 5, 6, 9, 10 ], [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ], - [ 1, 3, 8 ], [ 1, 9 ], [ 1, 4, 9, 10 ], [ 1, 2, 3, 4, 8, 11 ], - [ 1, 3, 5, 8, 9, 12 ], [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13 ] ] +> +gap> IsIsomorphicDigraph(l, +> DigraphFromDigraph6String("&L~~gpU{yksMEB@?_?XozWKcAI@B?__")); +true +gap> IsLatticeDigraph(l); +true gap> S := OrderEndomorphisms(2);; gap> CongruencesOfSemigroup(S); -[ with 0 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs> ] +[ <2-sided semigroup congruence over with 0 generating pairs>, + <2-sided semigroup congruence over with 1 generating pairs>, + <2-sided semigroup congruence over with 1 generating pairs> ] gap> l := LatticeOfCongruences(S); -> -gap> InNeighbours(l); -[ [ 1 ], [ 1, 2, 3 ], [ 1, 3 ] ] -gap> OutNeighbours(l); -[ [ 1 .. 3 ], [ 2 ], [ 2, 3 ] ] +> +gap> IsIsomorphicDigraph(l, DigraphFromDigraph6String("&ByW")); +true gap> Print(l, "\n"); PosetOfCongruences( [ @@ -69,44 +64,24 @@ gap> DotString(l); gap> S := Semigroup([Transformation([1, 4, 3, 1, 4, 2]), > Transformation([1, 6, 6, 3, 6, 6])]);; gap> l := LatticeOfCongruences(S);; -gap> InNeighbours(l); -[ [ 1 ], [ 1, 2 ], [ 1, 2, 3, 4 ], [ 1, 2, 4 ], [ 1, 2, 3, 4, 5 ] ] -gap> OutNeighbours(l); -[ [ 1 .. 5 ], [ 2, 3, 4, 5 ], [ 3, 5 ], [ 3, 4, 5 ], [ 5 ] ] -gap> DotString(l, rec(info := true)) = Concatenation("//dot\ngraph graphname", -> " {\n node [shape=circle]\nR2 -- T\nR3 -- 4\n4 -- R2\nU -- R3\n }"); +gap> IsIsomorphicDigraph(l, DigraphFromDigraph6String("&D}{ho_")); true +gap> DotString(l, rec(info := true));; gap> S := Semigroup([Transformation([1, 1, 2, 1]), > Transformation([3, 3, 1, 2])]);; gap> l := LatticeOfCongruences(S);; -gap> DotString(l) = Concatenation( -> "//dot\ngraph graphname {\n node [shape=point]\n2 -- 3\n2 -- 7\n3 -- 8\n", -> "4 -- 1\n5 -- 22\n6 -- 5\n6 -- 18\n7 -- 8\n7 -- 25\n8 -- 9\n9 -- 1\n10 -- 33\n", -> "11 -- 5\n11 -- 23\n12 -- 5\n13 -- 10\n13 -- 41\n14 -- 1\n15 -- 9\n16 -- 2\n16", -> " -- 17\n16 -- 31\n17 -- 3\n17 -- 33\n18 -- 16\n18 -- 22\n18 -- 26\n19 -- 6\n1", -> "9 -- 11\n19 -- 12\n19 -- 20\n20 -- 18\n20 -- 23\n20 -- 27\n20 -- 36\n21 -- 2", -> "\n21 -- 24\n21 -- 30\n22 -- 10\n22 -- 17\n23 -- 13\n23 -- 22\n23 -- 37\n24 --", -> " 3\n24 -- 32\n25 -- 4\n25 -- 9\n26 -- 10\n26 -- 31\n27 -- 13\n27 -- 26\n27 --", -> " 40\n28 -- 4\n28 -- 14\n29 -- 15\n29 -- 25\n30 -- 7\n30 -- 32\n30 -- 38\n31 -", -> "- 7\n31 -- 29\n31 -- 33\n32 -- 8\n32 -- 34\n33 -- 8\n33 -- 15\n34 -- 9\n34 --", -> " 14\n35 -- 15\n35 -- 34\n36 -- 16\n36 -- 21\n36 -- 37\n36 -- 40\n37 -- 17\n37", -> " -- 24\n37 -- 41\n38 -- 25\n38 -- 28\n38 -- 34\n39 -- 29\n39 -- 35\n39 -- 38", -> "\n40 -- 30\n40 -- 31\n40 -- 39\n40 -- 41\n41 -- 32\n41 -- 33\n41 -- 35\n }"); -true -gap> DotString(l, rec(numbers := true)) = Concatenation( -> "//dot\ngraph graphname {\n node [shape=circle]\n2 -- 3\n2 -- 7\n3 -- 8\n", -> "4 -- 1\n5 -- 22\n6 -- 5\n6 -- 18\n7 -- 8\n7 -- 25\n8 -- 9\n9 -- 1\n10 -- 33\n", -> "11 -- 5\n11 -- 23\n12 -- 5\n13 -- 10\n13 -- 41\n14 -- 1\n15 -- 9\n16 -- 2\n16", -> " -- 17\n16 -- 31\n17 -- 3\n17 -- 33\n18 -- 16\n18 -- 22\n18 -- 26\n19 -- 6\n1", -> "9 -- 11\n19 -- 12\n19 -- 20\n20 -- 18\n20 -- 23\n20 -- 27\n20 -- 36\n21 -- 2", -> "\n21 -- 24\n21 -- 30\n22 -- 10\n22 -- 17\n23 -- 13\n23 -- 22\n23 -- 37\n24 --", -> " 3\n24 -- 32\n25 -- 4\n25 -- 9\n26 -- 10\n26 -- 31\n27 -- 13\n27 -- 26\n27 --", -> " 40\n28 -- 4\n28 -- 14\n29 -- 15\n29 -- 25\n30 -- 7\n30 -- 32\n30 -- 38\n31 -", -> "- 7\n31 -- 29\n31 -- 33\n32 -- 8\n32 -- 34\n33 -- 8\n33 -- 15\n34 -- 9\n34 --", -> " 14\n35 -- 15\n35 -- 34\n36 -- 16\n36 -- 21\n36 -- 37\n36 -- 40\n37 -- 17\n37", -> " -- 24\n37 -- 41\n38 -- 25\n38 -- 28\n38 -- 34\n39 -- 29\n39 -- 35\n39 -- 38", -> "\n40 -- 30\n40 -- 31\n40 -- 39\n40 -- 41\n41 -- 32\n41 -- 33\n41 -- 35\n }"); +gap> IsIsomorphicDigraph(l, DigraphFromDigraph6String( +> Concatenation( +> "&h~~~~~~}a?Jo?A@kK^{?EAk?nF{J_ooG????_?O???P_DwX`CvnN}rrLn}~~n~wW{Mr??", +> "??_G?????_O????A_qG???D`uYn{K]~XimwG?m??G?op|_?W?_?w??????_?????@_?????", +> "B_?C?WWMo????_K_???@?\\_?oP_Dwz`[A?B_o????B?_????F@gJ_O@[EgR???[C_a@?Do", +> "W`C?D@uQbKK]^XaeW?SFXI~o?gKa?z_??W??_?A?q?@_??@oQAw??B?_Co??E@?G_?gKa?X", +> "_"))); true + +# the string depends on the representation of the semigroup +gap> DotString(l);; +gap> DotString(l, rec(numbers := true));; gap> IsCongruencePoset(l); true gap> IsDigraph(l); @@ -124,77 +99,63 @@ gap> Size(RightCongruencesOfSemigroup(S)); # LatticeOfLeft/RightCongruences gap> S := Semigroup([Transformation([1, 3, 1]), Transformation([2, 3, 3])]);; gap> l := LatticeOfLeftCongruences(S); -> -gap> InNeighbours(l) = -> [[1], [1, 2, 9, 12], [1, 3], [1, 2, 3, 4, 9, 12, 13, 15, 17], -> [1, 3, 5, 8, 11, 12, 13, 16, 17], [1, 3, 6], [1 .. 21], -> [1, 8, 11, 13], [1, 9, 12], [1, 3, 10, 12, 13, 17], -> [1, 11, 13], [1, 12], [1, 13], -> [1, 2, 3, 4, 9, 11, 12, 13, 14, 15, 16, 17, 21], [1, 3, 9, 12, 13, 15, 17], -> [1, 3, 11, 12, 13, 16, 17], [1, 3, 12, 13, 17], -> [1, 3, 5, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21], -> [1, 3, 6, 9, 10, 11, 12, 13, 15, 16, 17, 19, 20, 21], -> [1, 3, 6, 12, 13, 17, 20], -> [1, 3, 9, 11, 12, 13, 15, 16, 17, 21]]; +> +gap> IsIsomorphicDigraph(l, DigraphFromDigraph6String( +> "&T~~~ycA?Nc^wcA?A_@?K?E?_??U?GSgXgC_CAqTitj~Eu~wCA?C_XgSAlEc^wC?G?_?_C?E?_Pg")); true gap> l := LatticeOfRightCongruences(S); -> -gap> InNeighbours(l) = -> [[1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 2, 5, 6, 8, 14, 24], -> [1, 3, 5, 7, 10, 12, 23], [1, 8], [1, 4, 8, 9, 10], [1, 10], [1, 11], -> [1, 12], [1, 3, 8, 11, 13], [1, 14], [1, 2, 10, 11, 15], [1, 2, 3, 4, 16], -> [1 .. 31], [1, 2, 12, 18], [1, 3, 14, 19], -> [1, 4, 5, 20], [1, 4, 11, 12, 14, 21, 29], [1, 5, 11, 22], [1, 5, 12, 23], -> [1, 5, 14, 24], [1, 2, 5, 6, 8, 12, 14, 18, 23, 24, 25, 27, 29, 31], -> [1, 3, 5, 7, 10, 12, 14, 19, 23, 24, 26, 28, 29, 31], [1, 8, 12, 27], -> [1, 10, 14, 28], [1, 12, 14, 29], -> [1, 4, 5, 11, 12, 14, 20, 21, 22, 23, 24, 29, 30, 31], -> [1, 5, 12, 14, 23, 24, 29, 31]]; +> +gap> IsIsomorphicDigraph(l, DigraphFromDigraph6String( +> Concatenation( +> "&^~~~~~g_F_OAGHgC?`?r?GM?H^EA?C@??_A?O?kP?S?A?_??D`OA_?IgoC@", +> "AETv??a???_HSzo?A_????o????G????E@???A_O??@G?_??a?O??O_GA?GJEA?CBb??A?", +> "_??@?G???_I???OA_??GbM??C?A??A?p_"))); +true +gap> IsIsomorphicDigraph(DigraphFromDigraph6String("&C|FS"), +> LatticeOfCongruences(S)); true -gap> InNeighbours(LatticeOfCongruences(S)); -[ [ 1 ], [ 1, 2, 3, 4 ], [ 1, 3 ], [ 1, 3, 4 ] ] gap> Size(CongruencesOfSemigroup(S)); 4 gap> IsPartialOrderDigraph(l); true +gap> IsLatticeDigraph(l); +true # LatticeOfLeft/RightCongruences with restriction gap> S := Semigroup([Transformation([1, 3, 1]), Transformation([2, 3, 3])]);; gap> restriction := Subsemigroup(S, [Transformation([1, 1, 1]), > Transformation([2, 2, 2]), > Transformation([3, 3, 3])]);; -gap> latt := LatticeOfLeftCongruences(S, restriction); -> -gap> InNeighbours(latt); -[ [ 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 1, 2, 3, 4, 5 ] ] -gap> OutNeighbours(latt); -[ [ 1 .. 5 ], [ 2, 5 ], [ 3, 5 ], [ 4, 5 ], [ 5 ] ] +gap> latt := LatticeOfLeftCongruences(S, Combinations(AsList(restriction), 2)); +> +gap> IsIsomorphicDigraph(latt, DigraphFromDigraph6String("&D}cgo_")); +true gap> restriction := [Transformation([3, 2, 3]), > Transformation([3, 1, 3]), > Transformation([2, 2, 2])];; -gap> latt := LatticeOfRightCongruences(S, restriction); -> -gap> InNeighbours(latt); -[ [ 1 ], [ 1, 2, 3, 4 ], [ 1, 3 ], [ 1, 4 ] ] +gap> latt := LatticeOfRightCongruences(S, Combinations(restriction, 2)); +> +gap> IsIsomorphicDigraph(latt, DigraphFromDigraph6String("&C|ES")); +true gap> congs := CongruencesOfPoset(latt);; gap> Length(congs); 4 gap> IsDuplicateFreeList(congs); true gap> restriction := [Transformation([3, 1, 3]), Transformation([3, 2, 3])];; -gap> latt := LatticeOfCongruences(S, restriction); -> +gap> latt := LatticeOfCongruences(S, Combinations(restriction, 2)); +> gap> InNeighbours(latt); [ [ 1 ], [ 1, 2 ] ] gap> restriction := [Transformation([3, 3, 3])];; -gap> latt := LatticeOfCongruences(S, restriction); -> +gap> latt := LatticeOfCongruences(S, Combinations(restriction, 2)); +> gap> InNeighbours(latt); [ [ 1 ] ] @@ -202,14 +163,14 @@ gap> InNeighbours(latt); gap> S := Semigroup([Transformation([1, 3, 1]), Transformation([2, 3, 3])]);; gap> restriction := [Transformation([1, 1, 1]), Transformation([2, 2, 2, 2])];; gap> LatticeOfCongruences(S, restriction); -Error, the 2nd argument (a set) must be a subset of the 1st argument (a semigr\ -oup) +Error, the 2nd argument (a list or collection) must be empty or a mult. elt. c\ +oll. coll. gap> LatticeOfLeftCongruences(S, restriction); -Error, the 2nd argument (a set) must be a subset of the 1st argument (a semigr\ -oup) +Error, the 2nd argument (a list or collection) must be empty or a mult. elt. c\ +oll. coll. gap> LatticeOfRightCongruences(S, restriction); -Error, the 2nd argument (a set) must be a subset of the 1st argument (a semigr\ -oup) +Error, the 2nd argument (a list or collection) must be empty or a mult. elt. c\ +oll. coll. # Left/RightCongruences (as a list) gap> S := Semigroup([Transformation([1, 3, 1]), Transformation([2, 3, 3])]);; @@ -221,26 +182,23 @@ gap> Size(RightCongruencesOfSemigroup(S)); # PosetOfPrincipalLeft/RightCongruences gap> S := Semigroup([Transformation([1, 3, 1]), Transformation([2, 3, 3])]);; gap> poset := PosetOfPrincipalLeftCongruences(S); -> -gap> InNeighbours(poset) = -> [[1, 8, 11], [2], [1, 2, 3, 8, 11, 12], [2, 4, 7, 10, 11, 12], [2, 5], -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [7, 10, 12], [8, 11], -> [2, 9, 11, 12], [10, 12], [11], [12]]; +> +gap> IsIsomorphicDigraph(poset, +> DigraphFromDigraph6String("&Kh?^GH?D?B?@?D_hO@GDclYLl")); true gap> poset := PosetOfPrincipalRightCongruences(S); -> -gap> InNeighbours(poset) = -> [[1], [2], [3], [4], [1, 4, 5, 7, 13], [2, 4, 6, 9, 11], [7], -> [3, 7, 8, 9], [9], [10], [11], [2, 7, 10, 12], [13], [1, 9, 10, 14], -> [1, 2, 3, 15]]; +> +gap> IsIsomorphicDigraph(poset, +> DigraphFromDiSparse6String(".NkR@RyJofoPdM?qPEUsbFpfSRHVqACl_CRn")); true gap> poset := PosetOfPrincipalCongruences(S); -> -gap> InNeighbours(poset); -[ [ 1, 2, 3 ], [ 2 ], [ 2, 3 ] ] +> +gap> IsIsomorphicDigraph(poset, DigraphByInNeighbours( +> [[1, 2, 3], [2], [2, 3]])); +true gap> Print(poset, "\n"); PosetOfCongruences( [ SemigroupCongruence( Semigroup( [ Transformation( [ 1, 3, 1 ] ), @@ -251,7 +209,7 @@ PosetOfCongruences( [ [ Transformation( [ 1, 1, 1 ] ), Transformation( [ 2, 2, 2 ] ) ] ] ), SemigroupCongruence( Semigroup( [ Transformation( [ 1, 3, 1 ] ), Transformation( [ 2, 3, 3 ] ) ] ), - [ [ Transformation( [ 1, 3, 3 ] ), Transformation( [ 3, 1, 1 ] ) ] ] ) ] ) + [ [ Transformation( [ 1, 3, 1 ] ), Transformation( [ 3, 1, 3 ] ) ] ] ) ] ) gap> Size(PrincipalCongruencesOfSemigroup(S)); 3 @@ -260,31 +218,33 @@ gap> S := Semigroup([Transformation([1, 3, 1]), Transformation([2, 3, 3])]);; gap> restriction := Subsemigroup(S, [Transformation([1, 1, 1]), > Transformation([2, 2, 2]), > Transformation([3, 3, 3])]);; -gap> latt := PosetOfPrincipalLeftCongruences(S, restriction); -> +gap> latt := PosetOfPrincipalLeftCongruences(S, +> Combinations(AsList(restriction), 2)); +> gap> InNeighbours(latt); [ [ 1 ], [ 2 ], [ 3 ] ] gap> restriction := [Transformation([3, 2, 3]), > Transformation([3, 1, 3]), > Transformation([2, 2, 2])];; -gap> latt := PosetOfPrincipalRightCongruences(S, restriction); -> +gap> latt := PosetOfPrincipalRightCongruences(S, Combinations(restriction, 2)); +> gap> InNeighbours(latt); [ [ 1, 2, 3 ], [ 2 ], [ 3 ] ] gap> CongruencesOfPoset(latt); -[ with 1 generating pairs>, with - 1 generating pairs>, with 1 generating pairs> ] +[ with 1 generating pairs>, + with 1 generating pairs>, + with 1 generating pairs> ] gap> restriction := [Transformation([3, 1, 3]), Transformation([3, 2, 3])];; -gap> latt := PosetOfPrincipalCongruences(S, restriction);; +gap> latt := PosetOfPrincipalCongruences(S, Combinations(restriction, 2));; gap> InNeighbours(latt); [ [ 1 ] ] gap> restriction := [Transformation([3, 3, 3])];; -gap> latt := PosetOfPrincipalCongruences(S, restriction); +gap> latt := PosetOfPrincipalCongruences(S, Combinations(restriction, 2)); gap> InNeighbours(latt); [ ] @@ -294,30 +254,21 @@ true # PosetOfPrincipal(Left/Right)Congruences with invalid restriction gap> S := Semigroup([Transformation([1, 3, 1]), Transformation([2, 3, 3])]);; gap> restriction := [Transformation([1, 1, 1]), Transformation([2, 2, 2, 2])];; -gap> PosetOfPrincipalCongruences(S, restriction); -Error, the 2nd argument (a set) must be a subset of the 1st argument (a semigr\ -oup) +gap> PosetOfPrincipalCongruences(S, Combinations(restriction, 2)); +Error, the 2nd argument (a list) must consist of pairs of the 1st argument (a \ +semigroup) gap> PosetOfPrincipalLeftCongruences(S, restriction); -Error, the 2nd argument (a set) must be a subset of the 1st argument (a semigr\ -oup) +Error, the 2nd argument (a list or collection) must be empty or a mult. elt. c\ +oll. coll. gap> PosetOfPrincipalRightCongruences(S, restriction); -Error, the 2nd argument (a set) must be a subset of the 1st argument (a semigr\ -oup) +Error, the 2nd argument (a list or collection) must be empty or a mult. elt. c\ +oll. coll. # PrincipalCongruencesOfSemigroup gap> S := Semigroup(Transformation([1, 3, 2]), > Transformation([3, 1, 3]));; -gap> congs := PrincipalCongruencesOfSemigroup(S); -[ with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs> ] +gap> Length(PrincipalCongruencesOfSemigroup(S)); +5 # PrincipalLeft/RightCongruencesOfSemigroup gap> S := Semigroup([Transformation([1, 1]), Transformation([2, 1])]);; @@ -334,28 +285,17 @@ gap> PrincipalLeftCongruencesOfSemigroup(S)[2]; # MinimalCongruencesOfSemigroup gap> S := Semigroup([Transformation([1, 3, 2]), Transformation([3, 1, 3])]);; -gap> min := MinimalCongruencesOfSemigroup(S); -[ with 1 generating pairs> ] -gap> congs := CongruencesOfSemigroup(S); -[ with 0 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs> ] -gap> l := LatticeOfCongruences(S); -> -gap> InNeighbours(l); -[ [ 1 ], [ 1, 2, 5, 6 ], [ 1, 2, 3, 4, 5, 6 ], [ 1, 2, 4, 5, 6 ], - [ 1, 5, 6 ], [ 1, 6 ] ] +gap> min := MinimalCongruencesOfSemigroup(S);; +gap> Length(min); +1 +gap> Length(CongruencesOfSemigroup(S)); +6 +gap> l := LatticeOfCongruences(S);; +gap> IsIsomorphicDigraph(l, +> DigraphByInNeighbours( +> [[1], [1, 2, 5, 6], [1, 2, 3, 4, 5, 6], [1, 2, 4, 5, 6], +> [1, 5, 6], [1, 6]])); +true gap> minl := MinimalLeftCongruencesOfSemigroup(S);; gap> Size(minl); 3 @@ -364,20 +304,15 @@ gap> Size(minr); 9 gap> PositionsProperty(minl, c -> IsSubrelation(min[1], c)); [ 1, 2, 3 ] -gap> PositionsProperty(minr, c -> IsSubrelation(min[1], c)); -[ 9 ] +gap> PositionsProperty(minr, c -> IsSubrelation(min[1], c)) in [[1], [5]]; +true # Biggish example which forces garbage collection gap> S := Semigroup([Transformation([4, 2, 4, 4, 1]), > Transformation([4, 4, 1, 2, 2]), > Transformation([3, 3, 1, 2, 5])]);; -gap> MinimalCongruencesOfSemigroup(S); -[ with 1 generating pairs>, - with 1 generating pairs>, - with 1 generating pairs> ] +gap> Length(MinimalCongruencesOfSemigroup(S)); +3 # JoinSemilatticeOfCongruences gap> S := SymmetricInverseMonoid(2);; @@ -387,13 +322,14 @@ gap> pair3 := [PartialPerm([1, 2], [1, 2]), PartialPerm([1, 2], [2, 1])];; gap> coll := [RightSemigroupCongruence(S, pair1), > RightSemigroupCongruence(S, pair2), > RightSemigroupCongruence(S, pair3)];; -gap> l := JoinSemilatticeOfCongruences(coll, JoinRightSemigroupCongruences); -> -gap> InNeighbours(l); -[ [ 1 ], [ 2 ], [ 1, 3 ], [ 1, 2, 3, 4 ] ] -gap> JoinSemilatticeOfCongruences(coll, JoinLeftSemigroupCongruences); +gap> l := JoinSemilatticeOfCongruences(PosetOfCongruences(coll), +> WrappedRightCongruence); +> +gap> IsIsomorphicDigraph(l, DigraphFromDigraph6String("&ClRC")); +true +gap> JoinSemilatticeOfCongruences(coll); Error, no method found! For debugging hints type ?Recovery from NoMethodFound -Error, no 1st choice method found for `JoinLeftSemigroupCongruences' on 2 argu\ +Error, no 1st choice method found for `JoinSemilatticeOfCongruences' on 1 argu\ ments # MinimalCongruences @@ -404,14 +340,15 @@ gap> pair3 := [PartialPerm([1, 2], [1, 2]), PartialPerm([1, 2], [2, 1])];; gap> coll := [RightSemigroupCongruence(S, pair1), > RightSemigroupCongruence(S, pair2), > RightSemigroupCongruence(S, pair3)];; -gap> MinimalCongruences(coll) = coll{[1, 2]}; +gap> MinimalCongruences(PosetOfCongruences(coll)) = coll{[1, 2]}; true gap> MinimalCongruences(PosetOfCongruences(coll)) = coll{[1, 2]}; true gap> poset := LatticeOfCongruences(S); -> -gap> InNeighbours(poset); -[ [ 1 ], [ 1, 2 ], [ 1, 2, 3, 4 ], [ 1, 2, 4 ] ] +> +gap> IsIsomorphicDigraph(poset, DigraphFromDigraph6String("&C|qK")); +true gap> Print(l, "\n"); PosetOfCongruences( [ RightSemigroupCongruence( InverseMonoid( @@ -429,9 +366,9 @@ PosetOfCongruences( [ PartialPerm( [ 1 ], [ 1 ] ), PartialPerm( [ 1, 2 ], [ 1, 2 ] ) ] ] ) ] ) gap> MinimalCongruences(poset); -[ with - 0 generating pairs> ] -gap> MinimalCongruences([]); +[ <2-sided semigroup congruence over wi\ +th 0 generating pairs> ] +gap> MinimalCongruences(PosetOfCongruences([])); [ ] # PosetOfCongruences @@ -442,8 +379,8 @@ gap> coll := [RightSemigroupCongruence(S, pair1), > RightSemigroupCongruence(S, pair2), > RightSemigroupCongruence(S, [])];; gap> poset := PosetOfCongruences(coll); -> +> gap> InNeighbours(poset); [ [ 1, 3 ], [ 2, 3 ], [ 3 ] ] @@ -452,7 +389,7 @@ gap> poset := PosetOfCongruences([]); gap> CongruencesOfPoset(poset); [ ] -gap> Size(poset); +gap> DigraphNrVertices(poset); 0 gap> JoinSemilatticeOfCongruences(poset, JoinSemigroupCongruences); @@ -464,9 +401,10 @@ gap> S := Semigroup(Transformation([2, 1, 4, 3, 5, 2]), > Transformation([3, 4, 1, 2, 5, 3]), > Transformation([5, 5, 5, 5, 5, 5]));; gap> l := LatticeOfCongruences(S);; -gap> InNeighbours(l); -[ [ 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 1, 2, 3, 4, 5, 6 ], - [ 1, 2, 3, 4, 6 ] ] +gap> IsIsomorphicDigraph(l, DigraphByInNeighbours( +> [[1], [1, 2], [1, 3], [1, 4], [1, 2, 3, 4, 5, 6], +> [1, 2, 3, 4, 6]])); +true # SEMIGROUPS_UnbindVariables gap> Unbind(S); diff --git a/tst/standard/congruences/congpairs.tst b/tst/standard/congruences/congpairs.tst index 8cd4fbca8..a94db24c4 100644 --- a/tst/standard/congruences/congpairs.tst +++ b/tst/standard/congruences/congpairs.tst @@ -22,8 +22,8 @@ s1 gap> gens := [x ^ 2, x ^ 4]; [ s1^2, s1^4 ] gap> cong := SemigroupCongruence(S, gens); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> # The next test is now valid (but would run forever) #gap> NonTrivialEquivalenceClasses(cong); @@ -33,7 +33,7 @@ gap> cong := SemigroupCongruence(S, gens); gap> gens in cong; true gap> EquivalenceRelationLookup(cong); -Error, the argument (a congruence) must have finite range +Error, the argument (a 2-sided congruence) must have finite range gap> NrEquivalenceClasses(cong); 3 gap> class := EquivalenceClassOfElement(cong, x);; @@ -71,8 +71,8 @@ gap> u = v; false gap> gens := List(T, x -> [gens[1], x]);; gap> v := SemigroupCongruence(T, gens); - with 5 generating pairs> +<2-sided semigroup congruence over with 5 generating pairs> gap> u = v; true gap> NrEquivalenceClasses(u); @@ -95,11 +95,13 @@ true gap> Size(GeneratingPairsOfSemigroupCongruence(v)); 0 gap> classes := Set(EquivalenceClasses(v)); -[ , - , - , - , - ] +[ <2-sided congruence class of Transformation( [ 2, 6, 7, 2, 6, 9, 9, 1, 1, + 5 ] )>, <2-sided congruence class of Transformation( [ 6, 9, 9, 6, 9, 1, + 1, 2, 2, 6 ] )>, <2-sided congruence class of Transformation( [ 9, 1, 1, + 9, 1, 2, 2, 6, 6, 9 ] )>, + <2-sided congruence class of Transformation( [ 1, 2, 2, 1, 2, 6, 6, 9, 9, + 1 ] )>, <2-sided congruence class of Transformation( [ 2, 6, 6, 2, 6, 9, + 9, 1, 1, 2 ] )> ] gap> EquivalenceClasses(u)[1] in classes; false gap> classes[1] * EquivalenceClasses(u)[1]; @@ -107,9 +109,11 @@ Error, the arguments (cong. classes) are not classes of the same congruence gap> EquivalenceClasses(u)[1] * classes[1]; Error, the arguments (cong. classes) are not classes of the same congruence gap> classes[3] * classes[4]; - +<2-sided congruence class of Transformation( [ 9, 1, 1, 9, 1, 2, 2, 6, 6, + 9 ] )> gap> classes[4] * classes[3]; - +<2-sided congruence class of Transformation( [ 9, 1, 1, 9, 1, 2, 2, 6, 6, + 9 ] )> gap> EquivalenceClassOfElement(v, Representative(classes[5] * classes[2])) = > EquivalenceClassOfElement(v, > Representative(classes[5]) * @@ -154,10 +158,10 @@ gap> EquivalenceRelationCanonicalLookup(cong); [ 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] gap> NonTrivialEquivalenceClasses(cong); -[ ] +[ <2-sided congruence class of Transformation( [ 2, 1, 1, 2, 1 ] )> ] gap> classes := EquivalenceClasses(cong); -[ , - ] +[ <2-sided congruence class of Transformation( [ 2, 1, 1, 2, 1 ] )>, + <2-sided congruence class of Transformation( [ 1, 3, 4, 1, 3 ] )> ] gap> ImagesElm(cong, Transformation([1, 3, 4, 1, 3])); [ Transformation( [ 1, 3, 4, 1, 3 ] ) ] gap> cong = JoinSemigroupCongruences(cong, cong); @@ -170,17 +174,17 @@ gap> T := Semigroup([Transformation([2, 1, 1, 2, 1]), gap> cong2 := SemigroupCongruence(T, pair1, pair2);; gap> EquivalenceClassOfElement(cong, Transformation([2, 3, 2, 2, 3, 1])); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) gap> JoinSemigroupCongruences(cong, cong2); Error, cannot form the join of congruences over different semigroups gap> IsSubrelation(cong, cong2); -Error, the 1st and 2nd arguments are congruences over different semigroups +false gap> cong := LeftSemigroupCongruence(S, pair1, pair2);; gap> IsSubrelation(cong2, cong); -Error, the 1st and 2nd arguments are congruences over different semigroups +false gap> cong := RightSemigroupCongruence(S, pair1, pair2);; gap> IsSubrelation(cong2, cong); -Error, the 1st and 2nd arguments are congruences over different semigroups +false # A left semigroup congruence example that is also right gap> S := Semigroup(Transformation([2, 1, 1, 2, 1]), @@ -430,7 +434,7 @@ gap> pairs2 := [[Bipartition([[1, 2, 3, -1, -2, -3]]), gap> cong1 := SemigroupCongruence(S, pairs1);; gap> cong2 := SemigroupCongruence(S, pairs2);; gap> cong3 := JoinSemigroupCongruences(cong1, cong2); - with 3 generating pairs> gap> pairs1[1] in cong3; true @@ -495,8 +499,8 @@ false gap> S := FreeBand(3); gap> cong := SemigroupCongruence(S, [S.1, S.1 * S.2]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> [S.1, S.1] in cong; true @@ -546,7 +550,7 @@ gap> S := Semigroup( > Matrix(IsMaxPlusMatrix, [[-infinity, 0, 0], [0, 1, 0], [1, -1, 0]])]);; gap> pairs := [[S.1, S.2]];; gap> SemigroupCongruenceByGeneratingPairs(S, pairs); - with 1 generating pairs> gap> LeftSemigroupCongruenceByGeneratingPairs(S, pairs); F := FreeMonoid(2);; gap> S := F / [[F.2 ^ 2, F.2], [F.1 ^ 3, F.1 ^ 2]];; gap> SemigroupCongruenceByGeneratingPairs(S, [[S.1, S.2]]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> LeftSemigroupCongruenceByGeneratingPairs(F, [[F.1, F.2]]); with 1 generating pairs> @@ -689,7 +693,7 @@ Error, the items in the 1st argument (a list) do not all belong to the range o\ f the 2nd argument (a 2-sided semigroup congruence) gap> EquivalenceClassOfElement(cong, Transformation([1, 2, 1])); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) # A 2-sided example gap> F := FreeMonoid(2);; @@ -706,8 +710,8 @@ gap> (M.2 * M.1) ^ 2 * M.2 * M.1 ^ 2 = M.1 ^ 3; true gap> pair := [M.1 ^ 2 * M.2 * M.1, M.1 * M.2 * M.1];; gap> cong := SemigroupCongruence(M, pair); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> NrEquivalenceClasses(cong); 3 gap> [M.2, M.2 * M.1] in cong; @@ -980,11 +984,11 @@ gap> S := InverseSemigroup([PartialPerm([1, 2], [1, 2]), > PartialPerm([1, 2], [2, 3])]);; gap> pairs := [PartialPerm([], []), PartialPerm([1], [1])];; gap> C := SemigroupCongruence(S, pairs); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> AsSemigroupCongruenceByGeneratingPairs(C); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> C := RightSemigroupCongruence(S, pairs); with 1 generating pairs> diff --git a/tst/standard/congruences/congrees.tst b/tst/standard/congruences/congrees.tst index 052b42140..72d2811a2 100644 --- a/tst/standard/congruences/congrees.tst +++ b/tst/standard/congruences/congrees.tst @@ -61,11 +61,11 @@ gap> Size(cc); gap> List(cc, Size); [ 1095, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] gap> cc[1] * cc[1]; - +<2-sided congruence class of Transformation( [ 3, 1, 1, 4, 4 ] )> gap> cc[7] * cc[1]; - +<2-sided congruence class of Transformation( [ 3, 1, 1, 4, 4 ] )> gap> cc[2] * cc[5]; - +<2-sided congruence class of Transformation( [ 2, 4, 1, 3, 1 ] )> gap> cc[9] * cc[7] = cc[11]; true gap> Print(cong, "\n"); @@ -140,9 +140,9 @@ gap> y in tclass; true gap> EquivalenceClassOfElement(cong, z); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) gap> xclass := EquivalenceClassOfElement(cong, x); - +<2-sided congruence class of Transformation( [ 3, 4, 2, 4 ] )> gap> x in xclass; true gap> xclass * yclass = tclass; @@ -165,8 +165,7 @@ gap> cj := ReesCongruenceOfSemigroupIdeal(J);; gap> class1 := EquivalenceClassOfElement(ci, Transformation([1, 1, 3, 1, 3]));; gap> class2 := EquivalenceClassOfElement(cj, Transformation([1, 1, 3, 1, 3]));; gap> class1 * class2; -Error, the arguments (classes of Rees congruences) do not belong to the same c\ -ongruence +Error, the arguments (cong. classes) are not classes of the same congruence gap> cc := JoinSemigroupCongruences(ci, cj);; gap> NrEquivalenceClasses(ci); NrEquivalenceClasses(cj); NrEquivalenceClasses(cc); 16 diff --git a/tst/standard/congruences/congrms.tst b/tst/standard/congruences/congrms.tst index 10ffc70a4..9fc3a913e 100644 --- a/tst/standard/congruences/congrms.tst +++ b/tst/standard/congruences/congrms.tst @@ -132,7 +132,7 @@ true gap> class1 := EquivalenceClassOfElement(cong, x);; gap> class2 := EquivalenceClassOfElement(cong, y);; gap> class3 := EquivalenceClassOfElement(cong, z); - +<2-sided congruence class of (1,(2,3,4),3)> gap> Representative(class1); (1,(2,3),2) gap> Representative(class2); @@ -141,7 +141,7 @@ gap> Representative(class3); (1,(2,3,4),3) gap> EquivalenceClassOfElement(cong, t); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) gap> congs12 := SemigroupCongruence(S, [[RMSElement(S, 1, (1, 2, 3, 4), 2), > RMSElement(S, 2, (), 2)], > [RMSElement(S, 1, (1, 3), 1), @@ -410,7 +410,7 @@ gap> ims := ImagesElm(cong, zero); # ReesZeroMatCongTest5: Equivalence classes gap> class0 := EquivalenceClassOfElement(cong, zero); - +<2-sided congruence class of 0> gap> class0!.nCoset; 0 gap> HasSize(class0); @@ -425,10 +425,10 @@ true gap> class1 := EquivalenceClassOfElement(cong, x);; gap> class2 := EquivalenceClassOfElement(cong, y);; gap> class3 := EquivalenceClassOfElement(cong, z); - +<2-sided congruence class of (1,(1,3,5),2)> gap> EquivalenceClassOfElement(cong, t); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) gap> congs12 := SemigroupCongruence(S, [[RMSElement(S, 2, (), 2), > RMSElement(S, 5, (3, 5), 2)], > [RMSElement(S, 3, (1, 3)(4, 5), 1), @@ -541,7 +541,7 @@ gap> Length(EquivalenceClasses(uni)) = 1 and > RMSElement(S, 1, (), 1)); # the first is after 4.7.7 the latter before true gap> eq := EquivalenceClassOfElement(uni, y); - +<2-sided congruence class of (6,(1,3,5),1)> gap> eq := EquivalenceClassOfElement(uni, y);; gap> z in eq; true diff --git a/tst/standard/congruences/congsemigraph.tst b/tst/standard/congruences/congsemigraph.tst index e606156b7..39efac1ef 100644 --- a/tst/standard/congruences/congsemigraph.tst +++ b/tst/standard/congruences/congsemigraph.tst @@ -32,8 +32,8 @@ e_1 gap> e_3 := S.3; e_3 gap> cong := SemigroupCongruence(S, [[e_1, e_3]]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> IsCongruenceByWangPair(cong); false @@ -90,12 +90,12 @@ gap> D := Digraph([[2, 3], [3], [4], []]); gap> S := GraphInverseSemigroup(D); -gap> CongruenceByWangPair(S, [4], [2]); +gap> C := CongruenceByWangPair(S, [4], [2]); -gap> AsSemigroupCongruenceByGeneratingPairs(last); - with 2 generating pairs> -gap> EquivalenceRelationPartition(last); +gap> AsSemigroupCongruenceByGeneratingPairs(C); +<2-sided semigroup congruence over with 2 generating pairs> +gap> EquivalenceRelationPartition(C); [ [ e_1, e_1e_3e_3^-1 ], [ e_4, v_4, e_4^-1, 0, e_2e_4, e_3e_4, e_4e_4^-1, e_4^-1e_2^-1, e_4^-1e_3^-1, e_1e_3e_4, e_2e_4e_4^-1, e_3e_4e_4^-1, e_4e_4^-1e_2^-1, @@ -113,8 +113,8 @@ gap> S := GraphInverseSemigroup(D); gap> CongruenceByWangPair(S, [4], [1, 2]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 3 generating pairs> +<2-sided semigroup congruence over with 3 generating pairs> gap> EquivalenceRelationPartition(cong); [ [ e_1, e_1e_2e_2^-1 ], [ e_3, v_4, e_3^-1, 0, e_2e_3, e_3e_3^-1, e_3^-1e_2^-1, e_1e_2e_3, @@ -126,8 +126,8 @@ gap> EquivalenceRelationPartition(cong); gap> CongruenceByWangPair(S, [2, 3, 4], []); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 3 generating pairs> +<2-sided semigroup congruence over with 3 generating pairs> gap> EquivalenceRelationPartition(cong); [ [ e_1, e_2, e_3, v_2, v_3, v_4, e_1^-1, e_2^-1, e_3^-1, 0, e_1e_2, e_1e_1^-1, e_2e_3, e_2e_2^-1, e_3e_3^-1, e_2^-1e_1^-1, e_3^-1e_2^-1, @@ -138,8 +138,8 @@ gap> EquivalenceRelationPartition(cong); gap> CongruenceByWangPair(S, [], [1]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> EquivalenceRelationPartition(cong); [ [ v_1, e_1e_1^-1 ] ] @@ -203,15 +203,15 @@ gap> S := GraphInverseSemigroup(D); gap> CongruenceByWangPair(S, [4], [2]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 2 generating pairs> +<2-sided semigroup congruence over with 2 generating pairs> gap> AsCongruenceByWangPair(cong); gap> CongruenceByWangPair(S, [3, 4], [1]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 3 generating pairs> +<2-sided semigroup congruence over with 3 generating pairs> gap> AsCongruenceByWangPair(cong); gap> D := Digraph([[2], [3, 4], [4], []]); @@ -221,8 +221,8 @@ gap> S := GraphInverseSemigroup(D); gap> CongruenceByWangPair(S, [4], [1, 2]); gap> cong := AsSemigroupCongruenceByGeneratingPairs(last); - with 3 generating pairs> +<2-sided semigroup congruence over with 3 generating pairs> gap> AsCongruenceByWangPair(cong); @@ -300,22 +300,22 @@ gap> D := ChainDigraph(4); gap> S := GraphInverseSemigroup(D); gap> LatticeOfCongruences(S); -> +> gap> D := Digraph([[2, 3], [3], [4], []]); gap> S := GraphInverseSemigroup(D); gap> LatticeOfCongruences(S); -> +> gap> D := Digraph([[2], [3, 4], [4], []]); gap> S := GraphInverseSemigroup(D); gap> LatticeOfCongruences(S); -> +> # gap> SEMIGROUPS.StopTest(); diff --git a/tst/standard/congruences/congsimple.tst b/tst/standard/congruences/congsimple.tst index 67b155dab..8937cdbbe 100644 --- a/tst/standard/congruences/congsimple.tst +++ b/tst/standard/congruences/congsimple.tst @@ -121,11 +121,11 @@ gap> Size(classes) = NrEquivalenceClasses(cong); true gap> EquivalenceClassOfElement(cong, PartialPerm([2], [3])); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) gap> classx := EquivalenceClassOfElement(cong, x);; gap> classy := EquivalenceClassOfElement(cong, y);; gap> classz := EquivalenceClassOfElement(cong, z); - +<2-sided congruence class of Transformation( [ 2, 1, 2, 1, 1 ] )> gap> elms := ImagesElm(cong, x); [ Transformation( [ 1, 2, 2, 1, 2 ] ), Transformation( [ 2, 1, 1, 2, 1 ] ), Transformation( [ 1, 2, 2, 1, 1 ] ), Transformation( [ 2, 1, 1, 2, 2 ] ) ] @@ -289,7 +289,8 @@ gap> C := SemigroupCongruence(S, [S.1, S.1 ^ 2]); # SemigroupCongruenceByGeneratingPairs for free group gap> SemigroupCongruenceByGeneratingPairs(FreeGroup(1), []); - +<2-sided semigroup congruence over with +0 generating pairs> # CongruenceByIsomorphism, error gap> S := Semigroup([Transformation([3, 3, 3]), Transformation([4, 1, 1, 4])], diff --git a/tst/standard/congruences/conguniv.tst b/tst/standard/congruences/conguniv.tst index 1e40c8c02..0dd1f9e94 100644 --- a/tst/standard/congruences/conguniv.tst +++ b/tst/standard/congruences/conguniv.tst @@ -138,12 +138,12 @@ gap> ImagesElm(uni, Transformation([1, 3, 2])); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ argument (a congruence) gap> classes := EquivalenceClasses(uni); -[ ] +[ <2-sided congruence class of [2,1,3]> ] gap> EquivalenceClassOfElement(uni, Transformation([1, 3, 2])); Error, the 2nd argument (a mult. elt.) does not belong to the range of the 1st\ - argument (a congruence) + argument (a 2-sided congruence) gap> class := EquivalenceClassOfElement(uni, PartialPerm([1, 2, 3], [1, 3, 4])); - +<2-sided congruence class of [2,3,4](1)> gap> PartialPerm([2], [3]) in class; true gap> PartialPerm([1, 2, 4], [3, 2, 1]) in class; diff --git a/tst/standard/libsemigroups/cong.tst b/tst/standard/libsemigroups/cong.tst index b899c85ad..0ddc3a105 100644 --- a/tst/standard/libsemigroups/cong.tst +++ b/tst/standard/libsemigroups/cong.tst @@ -84,24 +84,24 @@ function( arg1 ) ... end gap> S := FullBooleanMatMonoid(2); gap> C := SemigroupCongruence(S, [[S.1, S.3]]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> LibsemigroupsCongruence(C);; # Can't test output because it contains the memory address # LibsemigroupsCongruence for a congruence on a fp semigroup gap> S := FreeSemigroup(2); gap> C := SemigroupCongruence(S, [[S.1, S.2]]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> LibsemigroupsCongruence(C);; # LibsemigroupsCongruence for a congruence on a semigroup with CanUseGapFroidurePin gap> S := FreeBand(2); gap> C := SemigroupCongruence(S, [[S.1, S.2]]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> LibsemigroupsCongruence(C);; gap> C := LeftSemigroupCongruence(S, [[S.1, S.2]]); with @@ -146,15 +146,15 @@ false gap> S := FreeSemigroup(2); gap> C := SemigroupCongruence(S, [[S.1, S.2]]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> NrEquivalenceClasses(C); infinity gap> CongruenceLessNC(C, S.2, S.1); false gap> C := SemigroupCongruence(S, [[S.1, S.2], [S.1 * S.2, S.2 * S.1], [S.1 ^ 10, S.1]]); - with -3 generating pairs> +<2-sided semigroup congruence over with 3 generating pairs> gap> CongruenceLessNC(C, S.2, S.1); false gap> NrEquivalenceClasses(C); @@ -175,8 +175,8 @@ true gap> S := FreeSemigroup(2); gap> C := SemigroupCongruence(S, [[S.1, S.2]]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> CongruenceTestMembershipNC(C, S.1 ^ 4, S.1 * S.2 ^ 3 * S.1 ^ 3 * S.2 ^ 2 * S.1 ^ 2); false gap> CongruenceTestMembershipNC(C, S.1 ^ 4, S.1 * S.2 ^ 3); @@ -248,9 +248,9 @@ true gap> Size(T); 169 gap> u := Image(hom, Transformation([1, 1, 1, 1])); - +<2-sided congruence class of Transformation( [ 1, 2, 2, 2 ] )> gap> t := Image(hom, Transformation([2, 1, 2, 3])); - +<2-sided congruence class of Transformation( [ 2, 1, 2, 3 ] )> gap> u < t; true @@ -258,12 +258,12 @@ true gap> S := FreeSemigroup(2); gap> C := SemigroupCongruence(S, [[S.1, S.2]]); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> EquivalenceClasses(C); Error, the argument (a congruence) must have a finite number of classes gap> EquivalenceRelationLookup(C); -Error, the argument (a congruence) must have finite range +Error, the argument (a 2-sided congruence) must have finite range # ImagesElm gap> S := FreeBand(2); @@ -296,8 +296,8 @@ gap> R := [[F.1 * F.2, F.2 * F.1], [F.1 * F.3, F.3 * F.1], [F.1 ^ 2, F.1], gap> S := F / R; gap> C := SemigroupCongruence(S, [[S.1, S.2]]); - wi\ -th 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> ImagesElm(C, S.1); [ s1, s2, s1*s2, s2^2, s1*s2^2 ] gap> ImagesElm(C, S.3); diff --git a/tst/standard/semigroups/semiquo.tst b/tst/standard/semigroups/semiquo.tst index 116ec4706..b592758cd 100644 --- a/tst/standard/semigroups/semiquo.tst +++ b/tst/standard/semigroups/semiquo.tst @@ -17,14 +17,14 @@ gap> SEMIGROUPS.StartTest(); gap> S := PartitionMonoid(4); gap> cong := SemigroupCongruence(S, [S.3, S.4]); - with 1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> T := S / cong;; gap> Size(T); 25 gap> One(T); -> +<2-sided congruence class of > # quotients, GeneratorsOfSemigroup gap> S := JonesMonoid(5); @@ -38,7 +38,7 @@ gap> T := I / J;; gap> HasGeneratorsOfMagma(T); false gap> GeneratorsOfSemigroup(T); -[ > ] # quotients, Rees quotient @@ -56,18 +56,24 @@ gap> S := Semigroup([Matrix(IsTropicalMaxPlusMatrix, [[0, 0], [1, 1]], 2), > Matrix(IsTropicalMaxPlusMatrix, [[2, 2], [1, 0]], 2)]); gap> cong := SemigroupCongruence(S, [S.3, S.1]); - with 1 generating pairs> gap> T := S / cong;; gap> AsList(T) * T.1; -[ , - , - ] +[ <2-sided congruence class of Matrix(IsTropicalMaxPlusMatrix, + [[1, 1], [2, 2]], 2)>, + <2-sided congruence class of Matrix(IsTropicalMaxPlusMatrix, + [[2, 2], [0, 0]], 2)>, + <2-sided congruence class of Matrix(IsTropicalMaxPlusMatrix, + [[2, 2], [2, 2]], 2)> ] gap> T.1 * AsList(T); -[ , - , - ] +[ <2-sided congruence class of Matrix(IsTropicalMaxPlusMatrix, + [[1, 1], [2, 2]], 2)>, + <2-sided congruence class of Matrix(IsTropicalMaxPlusMatrix, + [[1, 2], [2, 2]], 2)>, + <2-sided congruence class of Matrix(IsTropicalMaxPlusMatrix, + [[2, 2], [2, 2]], 2)> ] gap> GreensRClasses(S) * T.1; Error, no method found! For debugging hints type ?Recovery from NoMethodFound Error, no 3rd choice method found for `*' on 2 arguments @@ -80,8 +86,8 @@ gap> S := Semigroup([Transformation([2, 3, 2]), Transformation([3, 1, 3])]);; gap> pair := [Transformation([3, 2, 3]), Transformation([1, 1, 1])];; gap> cong := SemigroupCongruence(S, [pair]);; gap> Q := S / cong; - with 1 generating pairs>> + with 1 generating pairs>> gap> Size(Q); 1 gap> I := MinimalIdeal(S); diff --git a/tst/testinstall.tst b/tst/testinstall.tst index fc265f936..2baf69f8c 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -627,13 +627,13 @@ true gap> Size(T); 169 gap> u := Image(hom, Transformation([1, 1, 1, 1])); - +<2-sided congruence class of Transformation( [ 1, 2, 2, 2 ] )> gap> t := Image(hom, Transformation([2, 1, 2, 3])); - +<2-sided congruence class of Transformation( [ 2, 1, 2, 3 ] )> gap> u * t; - +<2-sided congruence class of Transformation( [ 1, 2, 2, 2 ] )> gap> t * u; - +<2-sided congruence class of Transformation( [ 1, 2, 2, 2 ] )> gap> S := Semigroup(u, t); gap> Size(S); @@ -886,9 +886,12 @@ gap> R := PrincipalFactor(MinimalDClass(S)); gap> cong := SemigroupCongruenceByGeneratingPairs(R, []);; gap> EquivalenceClasses(cong); -[ , , - , , - , ] +[ <2-sided congruence class of (1,(),1)>, + <2-sided congruence class of (1,(),2)>, + <2-sided congruence class of (1,(),3)>, + <2-sided congruence class of (1,(),4)>, + <2-sided congruence class of (1,(),5)>, + <2-sided congruence class of (1,(),6)> ] # TestInstall61: Issue 95: # No zero class in semigroup congruence EquivalenceClasses (generating pairs) @@ -901,28 +904,48 @@ gap> x := ReesZeroMatrixSemigroupElement(R, 1, (1, 3), 1);; gap> y := ReesZeroMatrixSemigroupElement(R, 1, (), 1);; gap> cong := SemigroupCongruenceByGeneratingPairs(R, [[x, y]]);; gap> c := Set(EquivalenceClasses(cong)); -[ , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - , , - ] +[ <2-sided congruence class of 0>, <2-sided congruence class of (1,(),1)>, + <2-sided congruence class of (1,(),2)>, + <2-sided congruence class of (1,(),3)>, + <2-sided congruence class of (1,(),4)>, + <2-sided congruence class of (1,(),5)>, + <2-sided congruence class of (1,(),6)>, + <2-sided congruence class of (2,(),1)>, + <2-sided congruence class of (2,(),2)>, + <2-sided congruence class of (2,(),3)>, + <2-sided congruence class of (2,(),4)>, + <2-sided congruence class of (2,(),5)>, + <2-sided congruence class of (2,(),6)>, + <2-sided congruence class of (3,(),1)>, + <2-sided congruence class of (3,(),2)>, + <2-sided congruence class of (3,(),3)>, + <2-sided congruence class of (3,(),4)>, + <2-sided congruence class of (3,(),5)>, + <2-sided congruence class of (3,(),6)>, + <2-sided congruence class of (4,(),1)>, + <2-sided congruence class of (4,(),2)>, + <2-sided congruence class of (4,(),3)>, + <2-sided congruence class of (4,(),4)>, + <2-sided congruence class of (4,(),5)>, + <2-sided congruence class of (4,(),6)>, + <2-sided congruence class of (5,(),1)>, + <2-sided congruence class of (5,(),2)>, + <2-sided congruence class of (5,(),3)>, + <2-sided congruence class of (5,(),4)>, + <2-sided congruence class of (5,(),5)>, + <2-sided congruence class of (5,(),6)>, + <2-sided congruence class of (6,(),1)>, + <2-sided congruence class of (6,(),2)>, + <2-sided congruence class of (6,(),3)>, + <2-sided congruence class of (6,(),4)>, + <2-sided congruence class of (6,(),5)>, + <2-sided congruence class of (6,(),6)>, + <2-sided congruence class of (7,(),1)>, + <2-sided congruence class of (7,(),2)>, + <2-sided congruence class of (7,(),3)>, + <2-sided congruence class of (7,(),4)>, + <2-sided congruence class of (7,(),5)>, + <2-sided congruence class of (7,(),6)> ] gap> ForAny(c, x -> MultiplicativeZero(R) in x); true @@ -1034,7 +1057,7 @@ gap> I := SemigroupIdeal(FullTransformationSemigroup(3), > Transformation([1, 1, 2]));; gap> T := S / I;; gap> One(T); - +<2-sided congruence class of IdentityTransformation> # TestInstall66: Second bug in Issue #131 gap> I := SemigroupIdeal(FullTransformationSemigroup(3), @@ -1356,8 +1379,8 @@ gap> AsList(JonesMonoid(1)); # Kernel-trace methods should only be selected for semigroups with inverse op gap> S := HallMonoid(2);; gap> latt := LatticeOfCongruences(S);; -gap> InNeighbours(latt); -[ [ 1 ], [ 1, 2, 3, 4 ], [ 1, 3, 4 ], [ 1, 4 ] ] +gap> IsIsomorphicDigraph(latt, DigraphFromDigraph6String("&C|E[")); +true gap> IsPartialOrderDigraph(latt); true @@ -1593,15 +1616,15 @@ gap> F := FreeSemigroup(1);; gap> x := GeneratorsOfSemigroup(F)[1];; gap> pair := [x ^ 2, x ^ 4];; gap> cong := SemigroupCongruence(F, pair); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> pair in cong; true gap> EquivalenceRelationLookup(cong); -Error, the argument (a congruence) must have finite range +Error, the argument (a 2-sided congruence) must have finite range gap> EquivalenceClasses(cong); -[ , , - ] +[ <2-sided congruence class of s1>, <2-sided congruence class of s1^2>, + <2-sided congruence class of s1^3> ] gap> NrEquivalenceClasses(cong); 3 @@ -1745,14 +1768,14 @@ gap> s1 := F.1;; s2 := F.2;; gap> rels := [[s2 * s1 * s2, s2 * s1], [s1, s1], [s2, s2], > [s1 * s2, s1 * s2], [s2 * s1, s2 * s1]];; gap> cong := SemigroupCongruence(F, rels); - with -1 generating pairs> +<2-sided semigroup congruence over with 1 generating pairs> gap> NrEquivalenceClasses(cong); infinity gap> EquivalenceRelationPartitionWithSingletons(cong); Error, the argument (a congruence) must have finite range gap> EquivalenceRelationLookup(cong); -Error, the argument (a congruence) must have finite range +Error, the argument (a 2-sided congruence) must have finite range # Issue 788 gap> S := GLM(2, 2);;