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 6e68b9a62..f9b1bf905 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 be2cee829..42b0f1886 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 1a2a3b525..30ceab110 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);
+[ [ ],
+ [ ],
+ [ ],
+ [