diff --git a/Makefile.am b/Makefile.am index 135baef35..83fb2cc2b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,7 @@ GAPINSTALLLIB = $(abs_top_srcdir)/$(BINARCHDIR)/semigroups.so lib_LTLIBRARIES = semigroups.la semigroups_la_SOURCES = src/bipart.cpp +semigroups_la_SOURCES += src/conglatt.cpp semigroups_la_SOURCES += src/cong.cpp semigroups_la_SOURCES += src/froidure-pin-base.cpp semigroups_la_SOURCES += src/froidure-pin-bipart.cpp diff --git a/doc/cong.xml b/doc/cong.xml index 5c30da33e..47f8cd1f0 100644 --- a/doc/cong.xml +++ b/doc/cong.xml @@ -333,8 +333,9 @@ gap> congs[pos]; gap> S := Semigroup(Transformation([1, 3, 2]), > Transformation([3, 1, 3]));; gap> min := MinimalCongruencesOfSemigroup(S); -[ <2-sided semigroup congruence over with 1 generating pairs> ] +[ <2-sided semigroup congruence over with 1 generating pairs> + ] gap> minl := MinimalLeftCongruencesOfSemigroup(S); [ with 1 generating pairs>, @@ -391,16 +392,17 @@ gap> minl := MinimalLeftCongruencesOfSemigroup(S); gap> S := Semigroup(Transformation([1, 3, 2]), > Transformation([3, 1, 3]));; gap> congs := PrincipalCongruencesOfSemigroup(S); -[ <2-sided semigroup congruence over with 1 generating pairs>, - <2-sided semigroup congruence over with 1 generating pairs>, - <2-sided semigroup congruence over with 1 generating pairs>, - <2-sided semigroup congruence over with 1 generating pairs>, - <2-sided semigroup congruence over with 1 generating pairs> ] +[ >, + <2-sided semigroup congruence over with 1 generating pairs>, + <2-sided semigroup congruence over with 1 generating pairs>, + <2-sided semigroup congruence over with 1 generating pairs>, + <2-sided semigroup congruence over with 1 generating pairs> + ] ]]> diff --git a/doc/conglatt.xml b/doc/conglatt.xml index fbb71c2c8..b60eebf43 100644 --- a/doc/conglatt.xml +++ b/doc/conglatt.xml @@ -1,7 +1,7 @@ ############################################################################# ## #W conglatt.xml -#Y Copyright (C) 2016 Michael Young +#Y Copyright (C) 2016-2022 Michael Young ## ## Licensing information can be found in the README file of this package. ## @@ -11,6 +11,7 @@ <#GAPDoc Label="IsCongruencePoset"> + true or false. This category contains all congruence posets. A congruence poset @@ -21,17 +22,49 @@ cong1 is a subrelation (a refinement) of cong2. The congruences in a congruence poset can be left, right, or two-sided.

- A congruence poset is a digraph - (see ) with a vertex for - each congruence, and an edge from vertex i to vertex j if - and only if the congruence numbered i is a subrelation of the - congruence numbered j. The list of congruences can be obtained - using .

+ A congruence poset is a digraph (see ) with a vertex for each congruence, and an edge from + vertex i to vertex j only if the congruence numbered + i is a subrelation of the congruence numbered j. To avoid + using an unnecessarily large amount of memory in some cases, + a congruence poset does not necessarily belong to + . In other words, + although every congruence poset represents a partial order it is not + necessarily the case that there is an edge from vertex i to vertex + j if and only if the congruence numbered i is a subrelation + of the congruence numbered j.

- Congruence posets can be created using , - , and - .

+ The list of congruences can be obtained using ; and the underlying semigroup of the poset can + be obtained using .

+ + Congruence posets can be created using any of: + + + , + + + + + + , + , or + + + + , + , or + . + + + + IsCayleyDigraphOfCongruences only applies to the output of + , + , + , and + . + The congruences used as the generating set for these operations can be obtained using + . S := SymmetricInverseMonoid(2);; @@ -42,18 +75,25 @@ gap> IsCongruencePoset(poset); true gap> IsDigraph(poset); true -gap> OutNeighbours(poset); -[ [ 1, 2, 3, 4 ], [ 2 ], [ 2, 3 ], [ 2, 3, 4 ] ] +gap> IsIsomorphicDigraph(poset, +> Digraph([[1, 2, 3, 4], [2], [2, 3], [2, 3, 4]])); +true gap> T := FullTransformationMonoid(3);; gap> congs := PrincipalCongruencesOfSemigroup(T);; -gap> poset := JoinSemilatticeOfCongruences(PosetOfCongruences(congs), -> WrappedTwoSidedCongruence); +gap> poset := JoinSemilatticeOfCongruences(PosetOfCongruences(congs)); > +gap> IsCayleyDigraphOfCongruences(poset); +false gap> IsCongruencePoset(poset); true gap> DigraphNrVertices(poset); 6 +gap> poset := CayleyDigraphOfCongruences(T); +> +gap> IsCayleyDigraphOfCongruences(poset); +true ]]> @@ -68,20 +108,20 @@ gap> DigraphNrVertices(poset); + Label = "for a semigroup and a list or collection"/> + Label = "for a semigroup and a list or collection"/> - A list of lists. + Label = "for a semigroup and a list or collection"/> + A lattice digraph. - If S is a semigroup, then LatticeOfCongruences gives a + If S is a semigroup, then LatticeOfCongruences returns a congruence poset object containing all the congruences of S and information about how they are contained in each other. See for more details.

LatticeOfLeftCongruences and LatticeOfRightCongruences do - the same thing for left and right congruences respectively.

+ the same thing for left and right congruences, respectively.

If restriction is specified and is a collection of elements from S, then the result will only include congruences generated by pairs @@ -101,8 +141,9 @@ gap> LatticeOfLeftCongruences(S); gap> LatticeOfRightCongruences(S); > -gap> OutNeighbours(LatticeOfRightCongruences(S)); -[ [ 1, 2, 3, 4, 5 ], [ 2 ], [ 2, 3 ], [ 2, 4 ], [ 2, 5 ] ] +gap> IsIsomorphicDigraph(LatticeOfRightCongruences(S), +> Digraph([[1, 2, 3, 4, 5], [2], [2, 3], [2, 4], [2, 5]])); +true gap> S := FullTransformationMonoid(4);; gap> restriction := [Transformation([1, 1, 1, 1]), > Transformation([1, 1, 1, 2]), @@ -114,6 +155,79 @@ gap> latt := LatticeOfCongruences(S, Combinations(restriction, 2)); <#/GAPDoc> + +<#GAPDoc Label="CayleyDigraphOfCongruences"> + + + + + + + + A digraph. + + If S is a semigroup, then CayleyDigraphOfCongruences + returns the right Cayley graph of the semilattice of congruences of + S with respect to the generating set consisting of the principal + congruences congruence poset. + See for more details.

+ + CayleyDigraphOfLeftCongruences and + CayleyDigraphOfRightCongruences do + the same thing for left and right congruences, respectively.

+ + If restriction is specified and is a collection of elements from + S, then the result will only include congruences generated by pairs + of elements from restriction. Otherwise, all congruences will be + calculated.

+ + Note that , and + its analogues for right and left congruences, return the reflexive + transitive closure of the digraph returned by this function (with any + multiple edges removed). If there are a large number of congruences, then + it might be the case that forming the reflexive transitive closure takes + a significant amount of time, and so it might be desirable to use this + function instead.

+ + See .

+ + S := OrderEndomorphisms(2);; +gap> CayleyDigraphOfCongruences(S); +> +gap> CayleyDigraphOfLeftCongruences(S); +> +gap> CayleyDigraphOfRightCongruences(S); +> +gap> IsIsomorphicDigraph(CayleyDigraphOfRightCongruences(S), +> Digraph([[2, 3, 4], [2, 5, 5], [5, 3, 5], [5, 5, 4], [5, 5, 5]])); +true +gap> S := FullTransformationMonoid(4);; +gap> restriction := [Transformation([1, 1, 1, 1]), +> Transformation([1, 1, 1, 2]), +> Transformation([1, 1, 1, 3])];; +gap> CayleyDigraphOfCongruences(S, Combinations(restriction, 2)); +> +]]> + + +<#/GAPDoc> <#GAPDoc Label="PosetOfPrincipalCongruences"> @@ -190,16 +304,16 @@ gap> latt := LatticeOfRightCongruences(S); > gap> CongruencesOfPoset(latt); -[ with 0 generating pairs>, with 2 generating pairs>, + of size 3, degree 2 with 2 generators> with 1 generating pairs>, with 1 generating pairs>, with 1 generating pairs>, with 1 generating pairs> ] + of size 3, degree 2 with 2 generators> with 2 generating pairs> ] ]]> @@ -236,10 +350,8 @@ true poset formed by these congruences partially ordered by containment.

This operation does not create any new congruences or take any joins. - However, see - . - See also + See also , + , and .

OutNeighbours(poset); <#GAPDoc Label="JoinSemilatticeOfCongruences"> - - + A congruence poset. - If coll is a list or collection of semigroup congruences (which may - be left, right, or two-sided) and join_func is a function for - taking the join of two of the congruences (such as - ) then this operation returns the - congruence poset formed by these congruences partially ordered by - containment, along with all their joins.

+ If poset is a congruence poset (i.e. it satisfies + ), then + this function returns the congruence poset formed by these congruences + partially ordered by containment, along with all their joins. + This includes the empty join which equals the trivial congruence. +

- Alternatively, a congruence poset poset can be specified; in this - case, the congruences contained in poset will be used in place of - coll, and information already known about their containments will - be used.

+ The digraph returned by this function represents the Cayley graph of + the semilattice generated by with + identity adjoined. The reflexive transitive closure of this digraph is a + join semillatice in the sense of + . +

See also - and .

+ and . S := SymmetricInverseMonoid(2);; @@ -291,11 +402,11 @@ gap> pair3 := [PartialPerm([1, 2], [1, 2]), gap> coll := [RightSemigroupCongruence(S, pair1), > RightSemigroupCongruence(S, pair2), > RightSemigroupCongruence(S, pair3)];; -gap> JoinSemilatticeOfCongruences(PosetOfCongruences(coll), -> WrappedRightCongruence); +gap> D := JoinSemilatticeOfCongruences(PosetOfCongruences(coll)); > -]]> +gap> IsJoinSemilatticeDigraph(DigraphReflexiveTransitiveClosure(D)); +true]]> <#/GAPDoc> @@ -347,3 +458,30 @@ ee 2> with 0 generating pairs> ] <#/GAPDoc> + +<#GAPDoc Label="GeneratingCongruencesOfJoinSemilattice"> + + + A list of congruences. + + If poset satisfies , + then this attribute holds the generating set for the semilattice of + congruences (where the operation is join). + + S := OrderEndomorphisms(3);; +gap> D := CayleyDigraphOfCongruences(S); +> +gap> GeneratingCongruencesOfJoinSemilattice(D); +[ >, + <2-sided semigroup congruence over with 1 generating pairs>, + <2-sided semigroup congruence over with 1 generating pairs> + ] +]]> + + + <#/GAPDoc> diff --git a/doc/congsemigraph.xml b/doc/congsemigraph.xml index 8767d97ca..0781ead44 100644 --- a/doc/congsemigraph.xml +++ b/doc/congsemigraph.xml @@ -116,10 +116,10 @@ gap> AsCongruenceByWangPair(cong); <#GAPDoc Label="GeneratingCongruencesOfLattice"> - + A semigroup. - This operation takes a finite graph inverse semigroup S and returns a + This attribute takes a finite graph inverse semigroup S and returns a minimal generating set for the lattice of congruences of S, as described in . This operation works only if the corresponding digraph of the graph inverse semigroup is simple. If there diff --git a/doc/conguniv.xml b/doc/conguniv.xml index 7aa5d4844..423121c01 100644 --- a/doc/conguniv.xml +++ b/doc/conguniv.xml @@ -72,3 +72,26 @@ gap> UniversalSemigroupCongruence(S); <#/GAPDoc> + +<#GAPDoc Label="TrivialCongruence"> + + + A trivial semigroup congruence. + + This operation returns the trivial semigroup congruence for the + semigroup S. It can be used in the same way as any other + semigroup congruence object. + S := ReesZeroMatrixSemigroup(SymmetricGroup(3), +> [[(), (1, 3, 2)], [(1, 2), 0]]);; +gap> TrivialCongruence(S); + with linked triple (1,2,2)> +gap> S := PartitionMonoid(2); + +gap> TrivialCongruence(S); +<2-sided semigroup congruence over with 0 generating pairs>]]> + + +<#/GAPDoc> diff --git a/doc/z-chap13.xml b/doc/z-chap13.xml index 928eb0cb4..7cd62472f 100644 --- a/doc/z-chap13.xml +++ b/doc/z-chap13.xml @@ -109,11 +109,13 @@ <#Include Label = "PrincipalCongruencesOfSemigroup"> <#Include Label = "IsCongruencePoset"> <#Include Label = "LatticeOfCongruences"> + <#Include Label = "CayleyDigraphOfCongruences"> <#Include Label = "PosetOfPrincipalCongruences"> <#Include Label = "CongruencesOfPoset"> <#Include Label = "UnderlyingSemigroupOfCongruencePoset"> <#Include Label = "PosetOfCongruences"> <#Include Label = "JoinSemilatticeOfCongruences"> + <#Include Label = "GeneratingCongruencesOfJoinSemilattice"> <#Include Label = "MinimalCongruences"> <#Include Label = "NumberOfRightCongruences"> <#Include Label = "IteratorOfRightCongruences"> @@ -310,7 +312,7 @@

- Universal congruences + Universal and trivial congruences The linked triples of a completely 0-simple Rees 0-matrix semigroup describe only its non-universal congruences. In any one of these, the zero element @@ -329,5 +331,6 @@ <#Include Label = "IsUniversalSemigroupCongruence"> <#Include Label = "IsUniversalSemigroupCongruenceClass"> <#Include Label = "UniversalSemigroupCongruence"> + <#Include Label = "TrivialCongruence">
diff --git a/gap/congruences/cong.gd b/gap/congruences/cong.gd index 269dadae2..34eb27279 100644 --- a/gap/congruences/cong.gd +++ b/gap/congruences/cong.gd @@ -100,6 +100,8 @@ DeclareGlobalFunction("SemigroupCongruence"); DeclareGlobalFunction("LeftSemigroupCongruence"); DeclareGlobalFunction("RightSemigroupCongruence"); +DeclareAttribute("TrivialCongruence", IsSemigroup); + # Properties of congruences DeclareProperty("IsRightSemigroupCongruence", IsLeftSemigroupCongruence); DeclareProperty("IsSemigroupCongruence", IsLeftSemigroupCongruence); diff --git a/gap/congruences/cong.gi b/gap/congruences/cong.gi index e5ea48f8a..fe4ec70a6 100644 --- a/gap/congruences/cong.gi +++ b/gap/congruences/cong.gi @@ -187,6 +187,16 @@ function(arg) return _LeftOrRightCong(RightSemigroupCongruenceByGeneratingPairs, arg); end); +######################################################################## +# Trivial congruence +######################################################################## + +InstallMethod(TrivialCongruence, "for a semigroup", +[IsSemigroup], +function(S) + return SemigroupCongruence(S, []); +end); + ######################################################################## # Congruence operators ######################################################################## diff --git a/gap/congruences/conglatt.gd b/gap/congruences/conglatt.gd index 68acc1c65..3dfc9f432 100644 --- a/gap/congruences/conglatt.gd +++ b/gap/congruences/conglatt.gd @@ -20,8 +20,7 @@ DeclareCategory("IsCongruencePoset", IsDigraph); DeclareAttribute("UnderlyingSemigroupOfCongruencePoset", IsCongruencePoset); DeclareAttribute("PosetOfPrincipalCongruences", IsCongruencePoset); -DeclareOperation("JoinSemilatticeOfCongruences", - [IsCongruencePoset, IsFunction]); +DeclareAttribute("JoinSemilatticeOfCongruences", IsCongruencePoset); DeclareAttribute("MinimalCongruences", IsCongruencePoset); DeclareAttribute("CongruencesOfPoset", IsCongruencePoset); @@ -29,6 +28,7 @@ DeclareAttribute("CongruencesOfPoset", IsCongruencePoset); # Constructs the poset object consisting of the congruences given in the # argument. DeclareOperation("PosetOfCongruences", [IsListOrCollection]); +DeclareAttribute("JoinSemilatticeOfCongruences", IsListOrCollection); DeclareAttribute("GeneratingPairsOfPrincipalCongruences", IsSemigroup); DeclareAttribute("GeneratingPairsOfPrincipalLeftCongruences", IsSemigroup); @@ -49,18 +49,25 @@ DeclareAttribute("LatticeOfCongruences", IsSemigroup); DeclareAttribute("LatticeOfLeftCongruences", IsSemigroup); DeclareAttribute("LatticeOfRightCongruences", IsSemigroup); -DeclareOperation("LatticeOfCongruences", +DeclareAttribute("CayleyDigraphOfCongruences", IsSemigroup); +DeclareAttribute("CayleyDigraphOfLeftCongruences", IsSemigroup); +DeclareAttribute("CayleyDigraphOfRightCongruences", IsSemigroup); + +DeclareOperation("CayleyDigraphOfCongruences", [IsSemigroup, IsListOrCollection]); -DeclareOperation("LatticeOfCongruencesNC", +DeclareOperation("CayleyDigraphOfLeftCongruences", [IsSemigroup, IsListOrCollection]); -DeclareOperation("LatticeOfLeftCongruences", +DeclareOperation("CayleyDigraphOfRightCongruences", + [IsSemigroup, IsListOrCollection]); + +DeclareCategory("IsCayleyDigraphOfCongruences", IsCongruencePoset); + +DeclareOperation("LatticeOfCongruences", [IsSemigroup, IsListOrCollection]); -DeclareOperation("LatticeOfLeftCongruencesNC", +DeclareOperation("LatticeOfLeftCongruences", [IsSemigroup, IsListOrCollection]); DeclareOperation("LatticeOfRightCongruences", [IsSemigroup, IsListOrCollection]); -DeclareOperation("LatticeOfRightCongruencesNC", - [IsSemigroup, IsListOrCollection]); DeclareAttribute("CongruencesOfSemigroup", IsSemigroup); DeclareAttribute("LeftCongruencesOfSemigroup", IsSemigroup); @@ -90,3 +97,5 @@ DeclareOperation("PrincipalLeftCongruencesOfSemigroup", [IsSemigroup, IsListOrCollection]); DeclareOperation("PrincipalRightCongruencesOfSemigroup", [IsSemigroup, IsListOrCollection]); + +DeclareAttribute("GeneratingCongruencesOfJoinSemilattice", IsCongruencePoset); diff --git a/gap/congruences/conglatt.gi b/gap/congruences/conglatt.gi index fafc64499..2742e4b2b 100644 --- a/gap/congruences/conglatt.gi +++ b/gap/congruences/conglatt.gi @@ -22,54 +22,75 @@ ## user as the list of out-neighbours of that digraph. ## +############################################################################# +## Helper function for creating CongruencePosets +############################################################################# + +SEMIGROUPS.MakeCongruencePoset := function(poset, congs) + if congs <> fail then + SetCongruencesOfPoset(poset, congs); + SetDigraphVertexLabels(poset, congs); + if not IsEmpty(congs) then + SetUnderlyingSemigroupOfCongruencePoset(poset, Range(congs[1])); + fi; + fi; + SetFilterObj(poset, IsCongruencePoset); + return poset; +end; + ############################################################################# ## The main three functions ############################################################################# -SEMIGROUPS.PrincipalXCongruencePosetNC := +SEMIGROUPS.PrincipalXCongruencesNC := function(S, pairs, SemigroupXCongruence) - local total, congs, nrcongs, children, parents, last_collected, nr, - badcong, newchildren, newparents, newcong, pair1, poset, pair, i, c, p; + local total, words, congs, congs_discrim, nrcongs, last_collected, nr, + keep, newcong, m, newcongdiscrim, i, old_pair, new_pair; + + Assert(1, IsListOrCollection(pairs)); + total := Size(pairs); - # 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 + words := List([1 .. Int(Log2(Float(Size(S))))], x -> Random(S)); + words := List(words, x -> MinimalFactorization(S, x)); + + congs := []; # List of all congs found so far, partitioned by nr classes + congs_discrim := []; + nrcongs := 0; # Number of congs found so far - children := []; # List of lists of children - parents := []; # List of lists of parents last_collected := 0; nr := 0; - for pair in pairs do + for new_pair in pairs do nr := nr + 1; - if pair[1] = pair[2] then + if new_pair[1] = new_pair[2] then + continue; + fi; + keep := true; + newcong := SemigroupXCongruence(S, [new_pair]); + m := NrEquivalenceClasses(newcong); + newcongdiscrim := List(words, w -> CongruenceWordToClassIndex(newcong, w)); + if not IsBound(congs[m]) then + congs[m] := [newcong]; + congs_discrim[m] := [newcongdiscrim]; + nrcongs := nrcongs + 1; continue; fi; - badcong := false; - newchildren := []; # Children of newcong - newparents := []; # Parents of newcong - newcong := SemigroupXCongruence(S, pair); - for i in [1 .. Length(congs)] do - 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); + i := PositionSorted(congs_discrim[m], newcongdiscrim); + while i <= Length(congs_discrim[m]) + and congs_discrim[m][i] = newcongdiscrim do + old_pair := GeneratingPairsOfLeftRightOrTwoSidedCongruence(congs[m][i]); + if not IsEmpty(old_pair) then + old_pair := old_pair[1]; + if CongruenceTestMembershipNC(congs[m][i], new_pair[1], new_pair[2]) + and CongruenceTestMembershipNC(newcong, old_pair[1], old_pair[2]) + then + keep := false; + break; fi; fi; + i := i + 1; od; + if nr > last_collected + 1999 then Info(InfoSemigroups, 1, @@ -80,101 +101,22 @@ SEMIGROUPS.PrincipalXCongruencePosetNC := last_collected := nr; GASMAN("collect"); fi; - if not badcong then + if keep 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; + InsertElmList(congs[m], i, newcong); + InsertElmList(congs_discrim[m], i, newcongdiscrim); fi; od; Info(InfoSemigroups, 1, StringFormatted("Found {} principal congruences in total!", - Length(parents))); + nrcongs)); - # We are done: make the object and return - poset := Digraph(parents); - SetInNeighbours(poset, children); - SetCongruencesOfPoset(poset, congs); - SetDigraphVertexLabels(poset, congs); - SetUnderlyingSemigroupOfCongruencePoset(poset, S); - SetFilterObj(poset, IsCongruencePoset); - return poset; + return Flat(congs); 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); - - # 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; - - # 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])); - fi; - SetFilterObj(poset, IsCongruencePoset); - return poset; -end); - -SEMIGROUPS.AddTrivialCongruence := function(poset, SemigroupXCongruence) - local S, children, parents, congs, nrcongs, i; - # Extract the info - S := UnderlyingSemigroupOfCongruencePoset(poset); - children := InNeighboursMutableCopy(poset); - parents := OutNeighboursMutableCopy(poset); - congs := ShallowCopy(CongruencesOfPoset(poset)); - - # Add the trivial congruence - nrcongs := Length(congs) + 1; - Add(congs, SemigroupXCongruence(S, []), 1); - children := Concatenation([[]], children + 1); - parents := Concatenation([[1 .. nrcongs]], parents + 1); - for i in [1 .. nrcongs] do - Add(children[i], 1, 1); - od; - - # Make the object and return - poset := Digraph(parents); - SetInNeighbours(poset, children); - SetCongruencesOfPoset(poset, congs); - SetDigraphVertexLabels(poset, congs); - SetUnderlyingSemigroupOfCongruencePoset(poset, S); - SetFilterObj(poset, IsCongruencePoset); - return poset; -end; +######################################################################## +######################################################################## # We declare the following for the sole purpose of being able to use the # Froidure-Pin (GAP implementation) algorithm for computing the join @@ -188,7 +130,7 @@ end; # GeneralMappingsFamily. This is a requirement according to the GAP reference # manual entry for CategoryCollections. DeclareCategory("IsWrappedLeftRightOrTwoSidedCongruence", - IsAssociativeElement); + IsAssociativeElement and IsMultiplicativeElementWithOne); DeclareCategory("IsWrappedRightCongruence", IsWrappedLeftRightOrTwoSidedCongruence); DeclareCategory("IsWrappedLeftCongruence", @@ -281,33 +223,55 @@ 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; +InstallMethod(One, "for wrapped left semigroup congruence", +[IsWrappedLeftCongruence], +x -> WrappedLeftCongruence(TrivialCongruence(Source(x![1])))); + +InstallMethod(One, "for wrapped right semigroup congruence", +[IsWrappedRightCongruence], +x -> WrappedRightCongruence(TrivialCongruence(Source(x![1])))); + +InstallMethod(One, "for wrapped 2-sided semigroup congruence", +[IsWrappedTwoSidedCongruence], +x -> WrappedTwoSidedCongruence(TrivialCongruence(Source(x![1])))); + +BindGlobal("_ClosureLattice", +function(S, gen_congs, WrappedXCongruence) + local gens, poset, all_congs, old_value, U; # Trivial case - if DigraphNrVertices(gen_congs) = 0 then - return gen_congs; + if Length(gen_congs) = 0 then + return SEMIGROUPS.MakeCongruencePoset(Digraph([[1]]), + [TrivialCongruence(S)]); fi; - gens := List(CongruencesOfPoset(gen_congs), WrappedXCongruence); - U := Range(gens[1]![1]); - - S := Semigroup(List(CongruencesOfPoset(gen_congs), WrappedXCongruence)); - poset := DigraphReflexiveTransitiveClosure(PartialOrderOfDClasses(S)); + if ValueOption("FroidurePin") <> fail then + gens := List(gen_congs, WrappedXCongruence); + S := Monoid(gens); + poset := RightCayleyDigraph(S); + all_congs := List(AsListCanonical(S), x -> x![1]); + else # The default + S := List(gen_congs, EquivalenceRelationLookup); + old_value := libsemigroups.should_report(); + if InfoLevel(InfoSemigroups) = 4 then + libsemigroups.set_report(true); + fi; + poset := DigraphNC(libsemigroups.LATTICE_OF_CONGRUENCES(S)); + libsemigroups.set_report(old_value); + all_congs := fail; + fi; Info(InfoSemigroups, 1, StringFormatted("Found {} congruences in total!", - Size(S))); - all_congs := List(DClasses(S), x -> Representative(x)![1]); + DigraphNrVertices(poset))); - SetCongruencesOfPoset(poset, all_congs); - SetDigraphVertexLabels(poset, all_congs); + U := Source(Representative(gen_congs)); + + poset := SEMIGROUPS.MakeCongruencePoset(poset, all_congs); SetUnderlyingSemigroupOfCongruencePoset(poset, U); - SetFilterObj(poset, IsCongruencePoset); SetPosetOfPrincipalCongruences(poset, - Filtered(CongruencesOfPoset(gen_congs), + Filtered(gen_congs, x -> Size(GeneratingPairsOfLeftRightOrTwoSidedCongruence(x)) = 1)); + SetGeneratingCongruencesOfJoinSemilattice(poset, gen_congs); + SetFilterObj(poset, IsCayleyDigraphOfCongruences); return poset; end); @@ -328,6 +292,143 @@ function(S, obj) fi; end); +# Creates a poset object from a list of congruences, without generating any +# congruences. + +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); + + # 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; + + # We are done: make the object and return + poset := Digraph(parents); + SetInNeighbours(poset, children); + return SEMIGROUPS.MakeCongruencePoset(poset, congs); +end); + +InstallMethod(JoinSemilatticeOfCongruences, "for a congruence poset", +[IsCongruencePoset], +function(C) + local S; + if IsEmpty(CongruencesOfPoset(C)) then + if not HasUnderlyingSemigroupOfCongruencePoset(C) then + ErrorNoReturn("cannot form the join semilattice of an empty congruence ", + "poset without the underlying semigroup being set"); + fi; + + S := UnderlyingSemigroupOfCongruencePoset(C); + return SEMIGROUPS.MakeCongruencePoset(Digraph([[1]]), + [TrivialCongruence(S)]); + fi; + return JoinSemilatticeOfCongruences(CongruencesOfPoset(C)); +end); + +InstallMethod(JoinSemilatticeOfCongruences, "for a congruence poset", +[IsListOrCollection], +function(gen_congs) + local S, D, all_congs, trivial; + # TODO(FasterJoins) arg checks + if IsEmpty(gen_congs) then + ErrorNoReturn("the argument must not be empty"); + fi; + S := Source(gen_congs[1]); + if ForAll(gen_congs, IsMagmaCongruence) then + D := _ClosureLattice(S, gen_congs, WrappedTwoSidedCongruence); + elif ForAll(gen_congs, IsLeftMagmaCongruence) then + D := _ClosureLattice(S, gen_congs, WrappedLeftCongruence); + else + Assert(1, ForAll(gen_congs, IsRightMagmaCongruence)); + D := _ClosureLattice(S, gen_congs, WrappedRightCongruence); + fi; + all_congs := CongruencesOfPoset(D); + D := DigraphMutableCopy(D); + DigraphRemoveAllMultipleEdges(D); + if not TrivialCongruence(S) in gen_congs then + all_congs := ShallowCopy(all_congs); + DigraphRemoveLoops(D); + trivial := DigraphSources(D)[1]; + DigraphRemoveVertex(D, trivial); + Remove(all_congs, trivial); + fi; + DigraphReflexiveTransitiveClosure(D); + MakeImmutable(D); + return SEMIGROUPS.MakeCongruencePoset(D, all_congs); +end); + +# This method exists because when we use the "Simple" option with +# LatticeOfCongruences etc the congruences themselves are not present (only the +# CayleyDigraphOfCongruences), so we use this method to reconstruct the +# congruences themselves. +InstallMethod(CongruencesOfPoset, "for a congruence poset", +[IsCayleyDigraphOfCongruences], +function(D) + local S, result, gen_congs, Q, q, genstoapply, seen, Join, current, n, i; + + S := UnderlyingSemigroupOfCongruencePoset(D); + result := [TrivialCongruence(S)]; + gen_congs := GeneratingCongruencesOfJoinSemilattice(D); + if IsEmpty(gen_congs) then + return result; + fi; + Append(result, gen_congs); + + # TODO(later): replace this with a Queue from the datastructures + # We do a simple BFS from the bottom of the lattice. + Q := [1]; + q := 1; + # We prepended the TrivialCongruence and this is not one of the generators + genstoapply := [1 .. Length(result) - 1]; + seen := BlistList([1 .. DigraphNrVertices(D)], []); + + if IsMagmaCongruence(gen_congs[1]) then + Join := JoinSemigroupCongruences; + elif IsRightMagmaCongruence(gen_congs[1]) then + Join := JoinRightSemigroupCongruences; + else + Assert(1, IsLeftMagmaCongruence(gen_congs[1])); + Join := JoinLeftSemigroupCongruences; + fi; + + while q <= Size(Q) do + current := Q[q]; + for i in genstoapply do + n := OutNeighbours(D)[current][i]; + if not seen[n] then + seen[n] := true; + result[n] := Join(result[current], result[i + 1]); + if n <> 1 then + Add(Q, n); + fi; + fi; + od; + q := q + 1; + od; + SetDigraphVertexLabels(D, result); + return result; +end); + ######################################################################## # GeneratingPairsOfPrincipalCongruences ######################################################################## @@ -340,23 +441,19 @@ function(S) "CanUseFroidurePin"); fi; return Combinations(AsList(S), 2); + # It'd be better to return an iterator here, but given that + # GeneratingPairsOfPrincipalCongruences is an attribute, the iterator can't + # be used when it's returned. # return IteratorOfCombinations(AsList(S), 2); end); +# Use the method just above 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); +"for a semigroup", [IsSemigroup], GeneratingPairsOfPrincipalCongruences); +# Use the method just above InstallMethod(GeneratingPairsOfPrincipalRightCongruences, -"for an acting semigroup", [IsSemigroup], -GeneratingPairsOfPrincipalCongruences); +"for a semigroup", [IsSemigroup], GeneratingPairsOfPrincipalCongruences); InstallMethod(GeneratingPairsOfPrincipalCongruences, "for an acting semigroup", [IsActingSemigroup], @@ -412,166 +509,120 @@ function(S) return Filtered(pairs, x -> x[1] in S and x[2] in S); end); -######################################################################## -# PosetOfPrincipalRight/LeftCongruences -######################################################################## - -InstallMethod(PosetOfPrincipalCongruences, "for a semigroup", [IsSemigroup], +InstallMethod(GeneratingPairsOfPrincipalLeftCongruences, +"for an acting semigroup", [IsActingSemigroup], function(S) - local pairs; - if HasLatticeOfCongruences(S) then - return PosetOfPrincipalCongruences(LatticeOfCongruences(S)); - fi; - pairs := GeneratingPairsOfPrincipalCongruences(S); - return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - pairs, - SemigroupCongruence); + local map, T; + map := AntiIsomorphismTransformationSemigroup(S); + T := Range(map); + map := InverseGeneralMapping(map); + return List(GeneratingPairsOfPrincipalRightCongruences(T), + x -> List(x, y -> y ^ map)); end); -InstallMethod(PosetOfPrincipalCongruences, -"for a semigroup and list or collection", +############################################################################# +## CayleyDigraphOfCongruences +############################################################################# + +InstallMethod(CayleyDigraphOfCongruences, +"for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], function(S, pairs) - _CheckCongruenceLatticeArgs(S, pairs); - return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - pairs, - SemigroupCongruence); + # pairs are checked in PrincipalCongruencesOfSemigroup + return _ClosureLattice(S, + PrincipalCongruencesOfSemigroup(S, pairs), + WrappedTwoSidedCongruence); end); -InstallMethod(PosetOfPrincipalRightCongruences, "for a semigroup", -[IsSemigroup], +InstallMethod(CayleyDigraphOfCongruences, "for a semigroup", [IsSemigroup], function(S) - local pairs; - if HasLatticeOfRightCongruences(S) then - return PosetOfPrincipalRightCongruences(LatticeOfRightCongruences(S)); - fi; - pairs := GeneratingPairsOfPrincipalRightCongruences(S); - return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - pairs, - RightSemigroupCongruence); + return _ClosureLattice(S, + PrincipalCongruencesOfSemigroup(S), + WrappedTwoSidedCongruence); end); -InstallMethod(PosetOfPrincipalRightCongruences, -"for a semigroup and list or collection", +InstallMethod(CayleyDigraphOfRightCongruences, +"for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], function(S, pairs) - _CheckCongruenceLatticeArgs(S, pairs); - return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - pairs, - RightSemigroupCongruence); + # pairs are checked in PrincipalCongruencesOfSemigroup + return _ClosureLattice(S, + PrincipalRightCongruencesOfSemigroup(S, pairs), + WrappedRightCongruence); end); -InstallMethod(PosetOfPrincipalLeftCongruences, "for a semigroup", +InstallMethod(CayleyDigraphOfRightCongruences, "for a semigroup", [IsSemigroup], function(S) - local pairs; - if HasLatticeOfLeftCongruences(S) then - return PosetOfPrincipalLeftCongruences(LatticeOfLeftCongruences(S)); - fi; - pairs := GeneratingPairsOfPrincipalLeftCongruences(S); - return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - pairs, - LeftSemigroupCongruence); + return _ClosureLattice(S, + PrincipalRightCongruencesOfSemigroup(S), + WrappedRightCongruence); end); -InstallMethod(PosetOfPrincipalLeftCongruences, -"for a semigroup and list or collection", +InstallMethod(CayleyDigraphOfLeftCongruences, +"for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], function(S, pairs) - _CheckCongruenceLatticeArgs(S, pairs); - return SEMIGROUPS.PrincipalXCongruencePosetNC(S, - pairs, - LeftSemigroupCongruence); + # pairs are checked in PrincipalCongruencesOfSemigroup + return _ClosureLattice(S, + PrincipalLeftCongruencesOfSemigroup(S, pairs), + WrappedLeftCongruence); +end); + +InstallMethod(CayleyDigraphOfLeftCongruences, "for a semigroup", [IsSemigroup], +function(S) + return _ClosureLattice(S, + PrincipalLeftCongruencesOfSemigroup(S), + WrappedLeftCongruence); end); ############################################################################# ## LatticeOfCongruences ############################################################################# -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); +SEMIGROUPS.MakeLattice := function(C) + local D; + D := DigraphMutableCopy(C); + DigraphRemoveAllMultipleEdges(D); + DigraphReflexiveTransitiveClosure(D); + MakeImmutable(D); + return SEMIGROUPS.MakeCongruencePoset(D, CongruencesOfPoset(C)); +end; InstallMethod(LatticeOfCongruences, "for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], function(S, pairs) - _CheckCongruenceLatticeArgs(S, pairs); - return LatticeOfCongruencesNC(S, pairs); + return SEMIGROUPS.MakeLattice(CayleyDigraphOfCongruences(S, pairs)); end); InstallMethod(LatticeOfCongruences, "for a semigroup", [IsSemigroup], function(S) - local poset; - # Although this duplicates code from LatticeOfCongruencesNC above, it avoids - # recomputation of the PosetOfPrincipalCongruences if it's already known. - poset := PosetOfPrincipalCongruences(S); - poset := JoinSemilatticeOfCongruences(poset, WrappedTwoSidedCongruence); - return SEMIGROUPS.AddTrivialCongruence(poset, SemigroupCongruence); -end); - -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); + return SEMIGROUPS.MakeLattice(CayleyDigraphOfCongruences(S)); end); InstallMethod(LatticeOfRightCongruences, "for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], function(S, pairs) - _CheckCongruenceLatticeArgs(S, pairs); - return LatticeOfRightCongruencesNC(S, pairs); + return SEMIGROUPS.MakeLattice(CayleyDigraphOfRightCongruences(S, pairs)); end); InstallMethod(LatticeOfRightCongruences, "for a semigroup", [IsSemigroup], function(S) - local poset; - # Although this duplicates code from LatticeOfRightCongruencesNC above, it - # avoids recomputation of the PosetOfPrincipalCongruences if it's already - # known. - poset := PosetOfPrincipalRightCongruences(S); - poset := JoinSemilatticeOfCongruences(poset, WrappedRightCongruence); - return SEMIGROUPS.AddTrivialCongruence(poset, RightSemigroupCongruence); -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); + return SEMIGROUPS.MakeLattice(CayleyDigraphOfRightCongruences(S)); end); InstallMethod(LatticeOfLeftCongruences, "for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], function(S, pairs) - _CheckCongruenceLatticeArgs(S, pairs); - return LatticeOfLeftCongruencesNC(S, pairs); + return SEMIGROUPS.MakeLattice(CayleyDigraphOfLeftCongruences(S, pairs)); end); InstallMethod(LatticeOfLeftCongruences, "for a semigroup", [IsSemigroup], function(S) - local poset; - # Although this duplicates code from LatticeOfLeftCongruencesNC above, it - # avoids recomputation of the PosetOfPrincipalCongruences if it's already - # known. - poset := PosetOfPrincipalLeftCongruences(S); - poset := JoinSemilatticeOfCongruences(poset, WrappedLeftCongruence); - return SEMIGROUPS.AddTrivialCongruence(poset, LeftSemigroupCongruence); + return SEMIGROUPS.MakeLattice(CayleyDigraphOfLeftCongruences(S)); end); ######################################################################## @@ -580,51 +631,78 @@ end); InstallMethod(LeftCongruencesOfSemigroup, "for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(LatticeOfLeftCongruences(S))); +S -> CongruencesOfPoset(CayleyDigraphOfLeftCongruences(S))); InstallMethod(RightCongruencesOfSemigroup, "for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(LatticeOfRightCongruences(S))); +S -> CongruencesOfPoset(CayleyDigraphOfRightCongruences(S))); InstallMethod(CongruencesOfSemigroup, "for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(LatticeOfCongruences(S))); +S -> CongruencesOfPoset(CayleyDigraphOfCongruences(S))); ######################################################################## # Principal congruences ######################################################################## -InstallMethod(PrincipalLeftCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(PosetOfPrincipalLeftCongruences(S))); +InstallMethod(PrincipalLeftCongruencesOfSemigroup, "for a semigroup", +[IsSemigroup], +function(S) + local pairs; + pairs := GeneratingPairsOfPrincipalLeftCongruences(S); + return SEMIGROUPS.PrincipalXCongruencesNC(S, + pairs, + LeftSemigroupCongruence); +end); -InstallMethod(PrincipalRightCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(PosetOfPrincipalRightCongruences(S))); +InstallMethod(PrincipalRightCongruencesOfSemigroup, "for a semigroup", +[IsSemigroup], +function(S) + local pairs; + pairs := GeneratingPairsOfPrincipalRightCongruences(S); + return SEMIGROUPS.PrincipalXCongruencesNC(S, + pairs, + RightSemigroupCongruence); +end); -InstallMethod(PrincipalCongruencesOfSemigroup, -"for a semigroup", [IsSemigroup], -S -> CongruencesOfPoset(PosetOfPrincipalCongruences(S))); +InstallMethod(PrincipalCongruencesOfSemigroup, "for a semigroup", +[IsSemigroup], +function(S) + local pairs; + pairs := GeneratingPairsOfPrincipalCongruences(S); + return SEMIGROUPS.PrincipalXCongruencesNC(S, + pairs, + SemigroupCongruence); +end); InstallMethod(PrincipalLeftCongruencesOfSemigroup, "for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], -function(S, restriction) - return CongruencesOfPoset(PosetOfPrincipalLeftCongruences(S, restriction)); +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return SEMIGROUPS.PrincipalXCongruencesNC(S, + pairs, + LeftSemigroupCongruence); end); InstallMethod(PrincipalRightCongruencesOfSemigroup, "for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], -function(S, restriction) - return CongruencesOfPoset(PosetOfPrincipalRightCongruences(S, restriction)); +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return SEMIGROUPS.PrincipalXCongruencesNC(S, + pairs, + RightSemigroupCongruence); end); InstallMethod(PrincipalCongruencesOfSemigroup, "for a semigroup and a list or collection", [IsSemigroup, IsListOrCollection], -function(S, restriction) - return CongruencesOfPoset(PosetOfPrincipalCongruences(S, restriction)); +function(S, pairs) + _CheckCongruenceLatticeArgs(S, pairs); + return SEMIGROUPS.PrincipalXCongruencesNC(S, + pairs, + SemigroupCongruence); end); ######################################################################## @@ -682,6 +760,57 @@ function(S, pairs) return MinimalCongruences(PosetOfPrincipalLeftCongruences(S, pairs)); end); +######################################################################## +# PosetOfPrincipalRight/LeftCongruences +######################################################################## + +InstallMethod(PosetOfPrincipalCongruences, "for a semigroup", [IsSemigroup], +function(S) + if HasLatticeOfCongruences(S) then + return PosetOfPrincipalCongruences(LatticeOfCongruences(S)); + fi; + return PosetOfCongruences(PrincipalCongruencesOfSemigroup(S)); +end); + +InstallMethod(PosetOfPrincipalRightCongruences, "for a semigroup", +[IsSemigroup], +function(S) + if HasLatticeOfRightCongruences(S) then + return PosetOfPrincipalRightCongruences(LatticeOfRightCongruences(S)); + fi; + return PosetOfCongruences(PrincipalRightCongruencesOfSemigroup(S)); +end); + +InstallMethod(PosetOfPrincipalLeftCongruences, "for a semigroup", +[IsSemigroup], +function(S) + if HasLatticeOfLeftCongruences(S) then + return PosetOfPrincipalLeftCongruences(LatticeOfLeftCongruences(S)); + fi; + return PosetOfCongruences(PrincipalLeftCongruencesOfSemigroup(S)); +end); + +InstallMethod(PosetOfPrincipalCongruences, +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + return PosetOfCongruences(PrincipalCongruencesOfSemigroup(S, pairs)); +end); + +InstallMethod(PosetOfPrincipalRightCongruences, +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + return PosetOfCongruences(PrincipalRightCongruencesOfSemigroup(S, pairs)); +end); + +InstallMethod(PosetOfPrincipalLeftCongruences, +"for a semigroup and list or collection", +[IsSemigroup, IsListOrCollection], +function(S, pairs) + return PosetOfCongruences(PrincipalLeftCongruencesOfSemigroup(S, pairs)); +end); + ######################################################################## # Printing, viewing, dot strings etc ######################################################################## @@ -692,7 +821,7 @@ function(poset) if DigraphNrVertices(poset) = 0 then Print(""); else - if IsLatticeDigraph(poset) then + if not IsMultiDigraph(poset) and IsLatticeDigraph(poset) then prefix := "lattice"; else prefix := "poset"; @@ -704,16 +833,13 @@ function(poset) if C = fail or IsMagmaCongruence(C) then hand := "two-sided"; else - hand := CongruenceHandednessString(C); + hand := ShallowCopy(CongruenceHandednessString(C)); fi; - Print("<\>", - prefix, - " of ", - DigraphNrVertices(poset), - " ", - hand, - " congruences over \<"); - ViewObj(UnderlyingSemigroupOfCongruencePoset(poset)); + Append(hand, " congruence"); + PrintFormatted("<\>{} of {} over \<", + prefix, + Pluralize(DigraphNrVertices(poset), hand)); + ViewObj(S); Print(">"); fi; end); @@ -789,3 +915,4 @@ function(poset, opts) return str; end); + diff --git a/gap/congruences/congpairs.gi b/gap/congruences/congpairs.gi index 4142fa33a..bb97cdef4 100644 --- a/gap/congruences/congpairs.gi +++ b/gap/congruences/congpairs.gi @@ -30,6 +30,28 @@ InstallImmediateMethod(GeneratingPairsOfLeftRightOrTwoSidedCongruence, 0, GeneratingPairsOfRightMagmaCongruence); +InstallImmediateMethod(GeneratingPairsOfLeftMagmaCongruence, + IsMagmaCongruence and IsSemigroupCongruence + and HasGeneratingPairsOfMagmaCongruence, + 0, + function(C) + if IsEmpty(GeneratingPairsOfMagmaCongruence(C)) then + return []; + fi; + TryNextMethod(); + end); + +InstallImmediateMethod(GeneratingPairsOfRightMagmaCongruence, + IsMagmaCongruence and IsSemigroupCongruence + and HasGeneratingPairsOfMagmaCongruence, + 0, + function(C) + if IsEmpty(GeneratingPairsOfMagmaCongruence(C)) then + return []; + fi; + TryNextMethod(); + end); + # Some types of congruences (such as CongruenceByKernelAndTrace) do not know # their generating pairs by default, and hence we require the following methods # in addition to the immediate methods above. diff --git a/gap/congruences/congsemigraph.gd b/gap/congruences/congsemigraph.gd index 3510e79fd..b665d5e10 100644 --- a/gap/congruences/congsemigraph.gd +++ b/gap/congruences/congsemigraph.gd @@ -25,5 +25,4 @@ DeclareOperation("AsCongruenceByWangPair", [IsSemigroupCongruence]); DeclareOperation("MinimalHereditarySubsetsVertex", [IsGraphInverseSemigroup, IsPosInt]); -DeclareOperation("GeneratingCongruencesOfLattice", - [IsGraphInverseSemigroup]); +DeclareAttribute("GeneratingCongruencesOfLattice", IsGraphInverseSemigroup); diff --git a/gap/congruences/congsemigraph.gi b/gap/congruences/congsemigraph.gi index 3ebf339d9..ab9199ffd 100644 --- a/gap/congruences/congsemigraph.gi +++ b/gap/congruences/congsemigraph.gi @@ -272,11 +272,18 @@ function(cong1, cong2) return IsSubset(Union(cong2!.H, cong2!.W), Union(cong1!.H, cong1!.W)); end); -InstallMethod(LatticeOfCongruences, +InstallMethod(CayleyDigraphOfCongruences, "for a graph inverse semigroup", [IsGraphInverseSemigroup], function(S) - local D; - D := PosetOfCongruences(GeneratingCongruencesOfLattice(S)); - return JoinSemilatticeOfCongruences(D, WrappedTwoSidedCongruence); + return _ClosureLattice(S, + GeneratingCongruencesOfLattice(S), + WrappedTwoSidedCongruence); +end); + +InstallMethod(TrivialCongruence, +"for a graph inverse semigroup", +[IsGraphInverseSemigroup], +function(S) + return AsCongruenceByWangPair(SemigroupCongruence(S, [])); end); diff --git a/gapbind14/include/gapbind14/to_cpp.hpp b/gapbind14/include/gapbind14/to_cpp.hpp index 51da5f073..79e6aa21c 100644 --- a/gapbind14/include/gapbind14/to_cpp.hpp +++ b/gapbind14/include/gapbind14/to_cpp.hpp @@ -63,6 +63,10 @@ namespace gapbind14 { void operator()() const noexcept {} }; + //////////////////////////////////////////////////////////////////////// + // Obj + //////////////////////////////////////////////////////////////////////// + template <> struct to_cpp { using cpp_type = Obj; diff --git a/src/conglatt.cpp b/src/conglatt.cpp new file mode 100644 index 000000000..9d4b5bb75 --- /dev/null +++ b/src/conglatt.cpp @@ -0,0 +1,275 @@ +// +// Semigroups package for GAP +// Copyright (C) 2022 James D. Mitchell +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// This file contains a function LATTICE_OF_CONGRUENCES for finding the lattice +// of congruences when there are too many generating congruences for +// Froidure-Pin to handle. + +#include "conglatt.hpp" + +#include // for equal, max +#include // for time_point +#include // for log2 +#include // for size_t +#include // for uint16_t, uint32_t +#include // for cout +#include // for unique_ptr +#include // for iota +#include // for unordered_map +#include // for swap, pair +#include // for vector + +// GAP headers +#include "compiled.h" + +// Semigroups package for GAP headers +#include "semigroups-debug.hpp" // for SEMIGROUPS_ASSERT + +// libsemigroups headers +#include "libsemigroups/adapters.hpp" // for Hash +#include "libsemigroups/report.hpp" // for should_report +#include "libsemigroups/string.hpp" // for group_digits + // + +namespace semigroups { + namespace { + // This class is minimally adapted from libsemigroups::detail::UF, but + // specialised for the problem at hand. + template + class UF { + std::vector _data; + size_t _log2_data_size; + + public: + //////////////////////////////////////////////////////////////////////// + // Aliases - public + //////////////////////////////////////////////////////////////////////// + using size_type = size_t; + using node_type = T; + using container_type = std::vector; + using index_type = T; + + // not noexcept because the constructors of std::vector and std::array + // aren't + explicit UF(size_type size) + : _data(size, 0), + _log2_data_size( + std::max(static_cast(std::log2(_data.size())), + static_cast(1))) { + SEMIGROUPS_ASSERT(size != 0); + std::iota(_data.begin(), _data.end(), 0); + } + + // not noexcept because the constructors of std::vector and std::array + // aren't + UF(UF const&) = default; + UF& operator=(UF const&) = default; + UF(UF&&) = default; + UF& operator=(UF&&) = default; + ~UF() = default; + + // not noexcept because std::vector::operator[] isn't + index_type find(index_type x) const { + SEMIGROUPS_ASSERT(x < _data.size()); + auto y = _data[x]; + while (y != _data[y]) { + y = _data[y]; + } + return y; + } + + // not noexcept because UF::find isn't + void unite(index_type x, index_type y) { + SEMIGROUPS_ASSERT(x < _data.size()); + SEMIGROUPS_ASSERT(y < _data.size()); + x = find(x); + y = find(y); + if (x < y) { + _data[y] = x; + } else { + _data[x] = y; + } + } + + // Not noexcept because std::equal isn't + bool operator==(UF const& that) const { + return std::equal(that._data.cbegin(), + that._data.cend(), + _data.cbegin(), + _data.cend()); + } + + void normalize() { + for (index_type i = 0; i < _data.size(); ++i) { + _data[i] = find(_data[i]); + } + } + + void join(UF const& x, UF const& y) { + SEMIGROUPS_ASSERT(size() == x.size()); + SEMIGROUPS_ASSERT(size() == y.size()); + for (index_type i = 0; i < _data.size(); ++i) { + _data[i] = x._data[i]; + unite(x._data[i], y._data[i]); + } + normalize(); + } + + size_t hash() const { + size_t val = 0; + for (auto it = _data.cbegin(); it < _data.cend(); + it += _log2_data_size) { + val ^= *it + 0x9e3779b97f4a7c16 + (val << 6); + } + return val; + } + + size_t size() const noexcept { + return _data.size(); + } + }; + } // namespace +} // namespace semigroups + +namespace libsemigroups { + template + struct Hash*> { + size_t operator()(semigroups::UF* x) const { + return x->hash(); + } + }; + + template + struct Hash> { + size_t operator()(semigroups::UF const& x) const { + return x.hash(); + } + }; + + template + struct EqualTo*> { + size_t operator()(semigroups::UF* x, semigroups::UF* y) const { + return *x == *y; + } + }; +} // namespace libsemigroups + +namespace semigroups { + namespace { + // should be to_cpp + auto to_uf(Obj lookup) { + using UF = UF; + SEMIGROUPS_ASSERT(IS_LIST(lookup)); + size_t const n = LEN_LIST(lookup); + SEMIGROUPS_ASSERT(n < 65536); + UF uf(n); + for (uint16_t i = 0; i < n; ++i) { + SEMIGROUPS_ASSERT(IS_INTOBJ(ELM_LIST(lookup, i))); + SEMIGROUPS_ASSERT(INT_INTOBJ(ELM_LIST(lookup, i)) >= 1); + SEMIGROUPS_ASSERT(INT_INTOBJ(ELM_LIST(lookup, i)) <= n); + uf.unite(i, INT_INTOBJ(ELM_LIST(lookup, i + 1)) - 1); + } + return uf; + } + } // namespace + + Obj LATTICE_OF_CONGRUENCES(Obj list) { + using UF = UF; + + using libsemigroups::EqualTo; + using libsemigroups::Hash; + using libsemigroups::detail::group_digits; + + using std::chrono::duration_cast; + using std::chrono::nanoseconds; + using std::chrono::seconds; + + if (LEN_LIST(list) == 0) { + ErrorQuit( + "the argument must be a list of length at least 1, found 0", 0L, 0L); + } + size_t const n = LEN_LIST(ELM_LIST(list, 1)); + if (n > 65535) { + // Then the values in the lookup won't fit into uint16_t + ErrorQuit("the lists in the argument must have length at most 65535, " + "found %d", + (Int) LEN_LIST(list), + 0L); + } + + auto start_time = std::chrono::high_resolution_clock::now(); + auto last_report = start_time; + uint32_t last_count = 1; + bool report = libsemigroups::report::should_report(); + + std::vector gens; + gens.reserve(LEN_LIST(list)); + + for (size_t i = 1; i <= LEN_LIST(list); ++i) { + gens.push_back(to_uf(ELM_LIST(list, i))); + } + + std::unordered_map, EqualTo> res; + Obj latt = NEW_PLIST(T_PLIST_TAB_RECT, 1); + AssPlist(latt, 1, NEW_PLIST(T_PLIST_CYC, gens.size())); + + auto one = std::make_unique(n); + auto tmp = std::make_unique(n); + + std::vector> todo; + res.emplace(one.get(), 0); + todo.push_back(std::move(one)); + + for (size_t i = 0; i < todo.size(); ++i) { + size_t const old_todo_size = todo.size(); + + for (size_t j = 0; j < gens.size(); ++j) { + auto const& g = gens[j]; + tmp->join(*todo[i], g); + auto it = res.find(tmp.get()); + if (it == res.end()) { + auto cpy = std::make_unique(*tmp); + res.emplace(cpy.get(), todo.size()); + AssPlist(ELM_PLIST(latt, i + 1), j + 1, INTOBJ_INT(todo.size() + 1)); + todo.push_back(std::move(cpy)); + } else { + AssPlist(ELM_PLIST(latt, i + 1), j + 1, INTOBJ_INT(it->second + 1)); + } + } + for (size_t k = old_todo_size; k < todo.size(); ++k) { + PushPlist(latt, NEW_PLIST(T_PLIST_CYC, gens.size())); + } + + if (report) { + auto now = std::chrono::high_resolution_clock::now(); + if (now - last_report > std::chrono::seconds(1)) { + auto total_time = duration_cast(now - start_time); + auto diff_time = duration_cast(now - last_report); + std::cout << "#I Found " << group_digits(res.size()) + << " congruences in " << total_time.count() << "s (" + << group_digits((todo.size() - last_count) + / diff_time.count()) + << "/s)!\n"; + std::swap(now, last_report); + last_count = todo.size(); + } + } + } + return latt; + } +} // namespace semigroups diff --git a/src/conglatt.hpp b/src/conglatt.hpp new file mode 100644 index 000000000..f2e7e0ce4 --- /dev/null +++ b/src/conglatt.hpp @@ -0,0 +1,31 @@ +// +// Semigroups package for GAP +// Copyright (C) 2022 James D. Mitchell +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// This file contains declarations of a function for computing the lattice of +// congruences of a semigroup from a set of generating congruences. + +#ifndef SEMIGROUPS_SRC_CONGLATT_HPP_ +#define SEMIGROUPS_SRC_CONGLATT_HPP_ + +#include "compiled.h" // for Obj, UInt + +namespace semigroups { + Obj LATTICE_OF_CONGRUENCES(Obj list); +} + +#endif // SEMIGROUPS_SRC_CONGLATT_HPP_ diff --git a/src/pkg.cpp b/src/pkg.cpp index 8633f4f52..c344b85e4 100644 --- a/src/pkg.cpp +++ b/src/pkg.cpp @@ -22,18 +22,23 @@ #include "pkg.hpp" -#include // for size_t -#include // for exception -#include // for string -#include // for conditional<>::type -#include // for vector +#include // for size_t +#include // for exception +#include // for string +#include // for conditional<>::type +#include // for unordered_map +#include // for swap +#include // for vector + +#include // for set // GAP headers #include "compiled.h" // Semigroups package for GAP headers -#include "bipart.hpp" // for Blocks, Bipartition -#include "cong.hpp" // for init_cong +#include "bipart.hpp" // for Blocks, Bipartition +#include "cong.hpp" // for init_cong +#include "conglatt.hpp" #include "froidure-pin-fallback.hpp" // for RUN_FROIDURE_PIN #include "froidure-pin.hpp" // for init_froidure_pin #include "semigroups-debug.hpp" // for SEMIGROUPS_ASSERT @@ -47,6 +52,7 @@ // libsemigroups headers #include "libsemigroups/bipart.hpp" // for Blocks, Bipartition #include "libsemigroups/cong-intf.hpp" // for congruence_kind +#include "libsemigroups/digraph.hpp" // for ActionDigraph #include "libsemigroups/fpsemi.hpp" // for FpSemigroup #include "libsemigroups/freeband.hpp" // for freeband_equal_to #include "libsemigroups/report.hpp" // for REPORTER, Reporter @@ -54,9 +60,15 @@ #include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, ToddCoxeter::table_type #include "libsemigroups/types.hpp" // for word_type, letter_type +#include "libsemigroups/adapters.hpp" +#include "libsemigroups/uf.hpp" + using libsemigroups::Bipartition; using libsemigroups::Blocks; +using libsemigroups::Hash; +using libsemigroups::detail::Duf; + namespace { void set_report(bool const val) { libsemigroups::REPORTER.report(val); @@ -85,6 +97,8 @@ GAPBIND14_MODULE(libsemigroups) { //////////////////////////////////////////////////////////////////////// gapbind14::InstallGlobalFunction("set_report", &set_report); + gapbind14::InstallGlobalFunction("should_report", + &libsemigroups::report::should_report); gapbind14::InstallGlobalFunction("hardware_concurrency", &std::thread::hardware_concurrency); gapbind14::InstallGlobalFunction( @@ -93,6 +107,9 @@ GAPBIND14_MODULE(libsemigroups) { libsemigroups::word_type>( &libsemigroups::freeband_equal_to)); + gapbind14::InstallGlobalFunction("LATTICE_OF_CONGRUENCES", + &semigroups::LATTICE_OF_CONGRUENCES); + //////////////////////////////////////////////////////////////////////// // Initialise from other cpp files //////////////////////////////////////////////////////////////////////// @@ -467,6 +484,7 @@ static StructGVarFunc GVarFuncs[] = { BIPART_NR_IDEMPOTENTS, 4, "o, scc, lookup, nr_threads"), + {0, 0, 0, 0, 0} /* Finish with an empty entry */ }; diff --git a/tst/standard/attributes/homomorph.tst b/tst/standard/attributes/homomorph.tst index 9f23e0b26..2c33e70c3 100644 --- a/tst/standard/attributes/homomorph.tst +++ b/tst/standard/attributes/homomorph.tst @@ -921,8 +921,11 @@ gap> S := Semigroup([Transformation([2, 1, 5, 1, 5]), > Transformation([1, 1, 1, 5, 3]), > Transformation([2, 5, 3, 5, 3])]);; gap> gens1 := GeneratorsOfSemigroup(S);; -gap> congs := CongruencesOfSemigroup(S);; -gap> cong := congs[3];; +gap> cong := SemigroupCongruence(S, +> [[Transformation([1, 1, 1, 5, 3]), Transformation([2, 5, 3, 5, 3])], +> [Transformation([1, 2, 5, 2, 5]), Transformation([2, 1, 5, 1, 5])]]); +<2-sided semigroup congruence over with 2 generating pairs> gap> T := S / cong;; gap> images1 := List(gens1, gen -> EquivalenceClassOfElement(cong, gen));; gap> hom1 := SemigroupHomomorphismByImages_NC(S, T, gens1, images1);; diff --git a/tst/standard/congruences/cong.tst b/tst/standard/congruences/cong.tst index 66ef6917b..5c5582247 100644 --- a/tst/standard/congruences/cong.tst +++ b/tst/standard/congruences/cong.tst @@ -1027,6 +1027,12 @@ gap> EquivalenceRelationPartitionWithSingletons(C); [ ], [ ] ] +# TrivialCongruence +gap> S := PartitionMonoid(3);; +gap> TrivialCongruence(S); +<2-sided semigroup congruence over with 0 generating pairs> + # SEMIGROUPS_UnbindVariables gap> Unbind(F); gap> Unbind(I); diff --git a/tst/standard/congruences/conglatt.tst b/tst/standard/congruences/conglatt.tst index b27981b85..efaf2d7a1 100644 --- a/tst/standard/congruences/conglatt.tst +++ b/tst/standard/congruences/conglatt.tst @@ -37,8 +37,8 @@ gap> S := OrderEndomorphisms(2);; gap> CongruencesOfSemigroup(S); [ <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); @@ -128,6 +128,9 @@ 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> PosetOfPrincipalLeftCongruences(S, Combinations(AsList(restriction), 2)); +> gap> latt := LatticeOfLeftCongruences(S, Combinations(AsList(restriction), 2)); > @@ -154,8 +157,8 @@ gap> InNeighbours(latt); [ [ 1 ], [ 1, 2 ] ] gap> restriction := [Transformation([3, 3, 3])];; gap> latt := LatticeOfCongruences(S, Combinations(restriction, 2)); -> +> gap> InNeighbours(latt); [ [ 1 ] ] @@ -206,10 +209,10 @@ PosetOfCongruences( [ [ Transformation( [ 1, 1, 1 ] ), Transformation( [ 1, 3, 1 ] ) ] ] ), SemigroupCongruence( Semigroup( [ Transformation( [ 1, 3, 1 ] ), Transformation( [ 2, 3, 3 ] ) ] ), - [ [ Transformation( [ 1, 1, 1 ] ), Transformation( [ 2, 2, 2 ] ) ] ] ), + [ [ Transformation( [ 1, 3, 1 ] ), Transformation( [ 3, 1, 3 ] ) ] ] ), SemigroupCongruence( Semigroup( [ Transformation( [ 1, 3, 1 ] ), Transformation( [ 2, 3, 3 ] ) ] ), - [ [ Transformation( [ 1, 3, 1 ] ), Transformation( [ 3, 1, 3 ] ) ] ] ) ] ) + [ [ Transformation( [ 1, 1, 1 ] ), Transformation( [ 2, 2, 2 ] ) ] ] ) ] ) gap> Size(PrincipalCongruencesOfSemigroup(S)); 3 @@ -304,7 +307,7 @@ gap> Size(minr); 9 gap> PositionsProperty(minl, c -> IsSubrelation(min[1], c)); [ 1, 2, 3 ] -gap> PositionsProperty(minr, c -> IsSubrelation(min[1], c)) in [[1], [5]]; +gap> PositionsProperty(minr, c -> IsSubrelation(min[1], c)) in [[7], [8]]; true # Biggish example which forces garbage collection @@ -322,15 +325,12 @@ 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(PosetOfCongruences(coll), -> WrappedRightCongruence); +gap> l := JoinSemilatticeOfCongruences(coll); > 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 `JoinSemilatticeOfCongruences' on 1 argu\ -ments +> # MinimalCongruences gap> S := SymmetricInverseMonoid(2);; @@ -349,22 +349,19 @@ gap> poset := LatticeOfCongruences(S); > gap> IsIsomorphicDigraph(poset, DigraphFromDigraph6String("&C|qK")); true -gap> Print(l, "\n"); +gap> Print(poset, "\n"); PosetOfCongruences( -[ RightSemigroupCongruence( InverseMonoid( - [ PartialPerm( [ 1, 2 ], [ 2, 1 ] ), PartialPerm( [ 1 ], [ 1 ] ) ] ), - [ [ PartialPerm( [ 1 ], [ 1 ] ), PartialPerm( [ 2 ], [ 1 ] ) ], - [ PartialPerm( [ 1 ], [ 1 ] ), PartialPerm( [ 1, 2 ], [ 1, 2 ] ) ] ] ), - RightSemigroupCongruence( InverseMonoid( - [ PartialPerm( [ 1, 2 ], [ 2, 1 ] ), PartialPerm( [ 1 ], [ 1 ] ) ] ), +[ SemigroupCongruence( InverseMonoid( [ PartialPerm( [ 1, 2 ], [ 2, 1 ] ), + PartialPerm( [ 1 ], [ 1 ] ) ] ), [ ] ), + SemigroupCongruence( InverseMonoid( [ PartialPerm( [ 1, 2 ], [ 2, 1 ] ), + PartialPerm( [ 1 ], [ 1 ] ) ] ), + [ [ PartialPerm( [ ], [ ] ), PartialPerm( [ 1, 2 ], [ 1, 2 ] ) ] ] ), + SemigroupCongruence( InverseMonoid( [ PartialPerm( [ 1, 2 ], [ 2, 1 ] ), + PartialPerm( [ 1 ], [ 1 ] ) ] ), [ [ PartialPerm( [ 1, 2 ], [ 1, 2 ] ), PartialPerm( [ 1, 2 ], [ 2, 1 ] ) - ] ] ), RightSemigroupCongruence( InverseMonoid( - [ PartialPerm( [ 1, 2 ], [ 2, 1 ] ), PartialPerm( [ 1 ], [ 1 ] ) ] ), - [ [ PartialPerm( [ 1 ], [ 1 ] ), PartialPerm( [ 2 ], [ 1 ] ) ] ] ), - RightSemigroupCongruence( InverseMonoid( + ] ] ), SemigroupCongruence( InverseMonoid( [ PartialPerm( [ 1, 2 ], [ 2, 1 ] ), PartialPerm( [ 1 ], [ 1 ] ) ] ), - [ [ PartialPerm( [ 1 ], [ 1 ] ), PartialPerm( [ 1, 2 ], [ 1, 2 ] ) ] ] ) - ] ) + [ [ PartialPerm( [ ], [ ] ), PartialPerm( [ 1 ], [ 1 ] ) ] ] ) ] ) gap> MinimalCongruences(poset); [ <2-sided semigroup congruence over wi\ th 0 generating pairs> ] @@ -391,8 +388,9 @@ gap> CongruencesOfPoset(poset); [ ] gap> DigraphNrVertices(poset); 0 -gap> JoinSemilatticeOfCongruences(poset, JoinSemigroupCongruences); - +gap> JoinSemilatticeOfCongruences(poset); +Error, cannot form the join semilattice of an empty congruence poset without t\ +he underlying semigroup being set gap> MinimalCongruences(poset); [ ] @@ -411,8 +409,7 @@ gap> S := InverseSemigroup(PartialPerm([1, 3], [2, 4]), > PartialPerm([1], [1]));; gap> D := DigraphReflexiveTransitiveReduction(LatticeOfRightCongruences(S)); -gap> x := DigraphSinks(D)[1]; -2 +gap> x := DigraphSinks(D)[1];; gap> NrEquivalenceClasses(RightCongruencesOfSemigroup(S)[x]); 1 diff --git a/tst/standard/congruences/congsemigraph.tst b/tst/standard/congruences/congsemigraph.tst index 2544d3754..d1f2bbc7b 100644 --- a/tst/standard/congruences/congsemigraph.tst +++ b/tst/standard/congruences/congsemigraph.tst @@ -325,17 +325,17 @@ gap> S := GraphInverseSemigroup(D); gap> L := LatticeOfCongruences(S); > -gap> C := CongruencesOfSemigroup(S); -[ , - , +gap> C := CongruencesOfSemigroup(S);; Set(C); +[ , + , + , , - , , - , , - , , - ] + , + , + ] gap> val := true;; > for i in [1 .. Length(C)] do > for j in [1 .. Length(C)] do @@ -373,19 +373,19 @@ gap> S := GraphInverseSemigroup(D); gap> L := LatticeOfCongruences(S); > -gap> C := CongruencesOfSemigroup(S); -[ , - , - , - , - , +gap> C := CongruencesOfSemigroup(S);; Set(C); +[ , , + , + , + , , - , , - , + , , - ] + , + , + ] gap> val := true;; > for i in [1 .. Length(C)] do > for j in [1 .. Length(C)] do diff --git a/tst/standard/libsemigroups/sims1.tst b/tst/standard/libsemigroups/sims1.tst index 7118ca769..2a34b145e 100644 --- a/tst/standard/libsemigroups/sims1.tst +++ b/tst/standard/libsemigroups/sims1.tst @@ -296,14 +296,14 @@ gap> LeftCongruencesOfSemigroup(S);; gap> it := IteratorOfLeftCongruences(S, 2); gap> NrEquivalenceClasses(NextIterator(it)); -1 -gap> NrEquivalenceClasses(NextIterator(it)); 2 gap> NrEquivalenceClasses(NextIterator(it)); 2 gap> NrEquivalenceClasses(NextIterator(it)); 2 gap> NrEquivalenceClasses(NextIterator(it)); +1 +gap> NrEquivalenceClasses(NextIterator(it)); 2 gap> NextIterator(it); Error, is exhausted diff --git a/tst/standard/semigroups/semiquo.tst b/tst/standard/semigroups/semiquo.tst index 7d7c23aae..dbfd2e0b4 100644 --- a/tst/standard/semigroups/semiquo.tst +++ b/tst/standard/semigroups/semiquo.tst @@ -235,9 +235,9 @@ gap> CongruencesOfSemigroup(Q); [ <2-sided semigroup congruence over with 1 generating pairs>> with 0 generating pairs>, - <2-sided semigroup congruence over with - 1 generating pairs>> with 1 generating pairs>, + with + 1 generating pairs>>>, <2-sided semigroup congruence over with 1 generating pairs>> with 1 generating pairs>, @@ -265,10 +265,10 @@ gap> CongruencesOfSemigroup(Q); e over with 1 generating pairs>> with 1 generating pairs>> with 0 generating pairs>, - <2-sided semigroup congruence over with 1 generating pairs>> with - 1 generating pairs>> with 1 generating pairs> ] + 1 generating pairs>>> ] gap> Size(Q); 2