|
###############################################################################
##
## attributes/properties.gi
## Copyright (C) 2013-2022 James D. Mitchell
##
## Licensing information can be found in the README file of this package.
##
#############################################################################
##
# This file contains methods for determining properties of arbitrary
# semigroups. There are not very many specialised methods for acting semigroups
# and so we only have a single file.
# Ecom (commuting idempotents), LI (locally trivial),
# LG (locally group), B1 (dot-depth one), DA (regular D-classes are idempotent)
# R v L (???), IsNilpotentSemigroup, inverses, local submonoid, right ideal,
# left ideal, kernel!?
# IsLeftCancellative,
# IsRightCancellative, IsRightGroup, IsLeftGroup, IsUnitarySemigroup,
# IsRightUnitarySemigp, IsLeftUnitarySemigp, IsCongruenceFree,
# PrimitiveIdempotents, IdempotentOrder,
# IsLeftNormalBand, IsRightNormalBand, IsNormalBand, IsEUnitarySemigroup
# IsRectangularGroup, IsBandOfGroups, IsFreeBand, IsFreeSemilattice,
# IsFreeNormalBand, , IsFundamentalInverseSemigp,
# IsFullSubsemigroup (of an inverse semigroup), IsFactorizableInverseMonoid,
# IsFInverseSemigroup, IsSemigroupWithCentralIdempotents, IsLeftUnipotent,
# IsRightUnipotent, IsSemigroupWithClosedIdempotents, .
# same method for ideals, works for finite and infinite
InstallMethod(IsBand, "for a semigroup",
[IsSemigroup],
function(S)
if HasParent(S) and HasIsBand(Parent(S)) and IsBand(Parent(S)) then
return true;
fi;
return IsCompletelyRegularSemigroup(S) and IsHTrivial(S);
end);
# same method for ideals, works for finite and infinite
InstallMethod(IsBand, "for an inverse semigroup", [IsInverseSemigroup],
IsSemilattice);
# same method for ideals
InstallMethod(IsBlockGroup, "for a semigroup",
[IsSemigroup],
function(S)
local D;
if HasParent(S) and HasIsBlockGroup(Parent(S))
and IsBlockGroup(Parent(S)) then
return true;
elif (HasIsRegularSemigroup(S) and IsRegularSemigroup(S))
and (HasIsInverseSemigroup(S) and not IsInverseSemigroup(S)) then
Info(InfoSemigroups, 2, "regular but non-inverse semigroup");
return false;
elif not IsFinite(S) then
TryNextMethod();
fi;
for D in DClasses(S) do
if IsRegularDClass(D)
and (ForAny(RClasses(D), x -> NrIdempotents(x) > 1)
or NrRClasses(D) <> NrLClasses(D)) then
return false;
fi;
od;
return true;
end);
InstallMethod(IsSemigroupWithCommutingIdempotents, "for a semigroup",
[IsSemigroup],
function(S)
local ids, n, i, j;
ids := Idempotents(S);
n := Length(ids);
for i in [1 .. n - 1] do
for j in [i + 1 .. n] do
if ids[i] * ids[j] <> ids[j] * ids[i] then
return false;
fi;
od;
od;
return true;
end);
InstallMethod(IsSemigroupWithCommutingIdempotents,
"for a semigroup with idempotent generated subsemigroup",
[IsSemigroup and HasIdempotentGeneratedSubsemigroup],
S -> IsCommutativeSemigroup(IdempotentGeneratedSubsemigroup(S)));
# same method for ideals
InstallMethod(IsBrandtSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if not IsFinite(S) then
return false;
fi;
return IsZeroSimpleSemigroup(S) and IsInverseSemigroup(S);
end);
# same method for ideals
InstallMethod(IsCongruenceFreeSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
local rowsDiff, T, P;
if not IsFinite(S) then
TryNextMethod();
fi;
rowsDiff := function(p)
local i, j;
for i in [1 .. Size(p) - 1] do
for j in [i + 1 .. Size(p)] do
if p[i] = p[j] then
return false;
fi;
od;
od;
return true;
end;
if Size(S) <= 2 then
return true;
elif MultiplicativeZero(S) <> fail then
# CASE 1: S has zero
if IsZeroSimpleSemigroup(S) then
# Find an isomorphic RZMS
T := Range(IsomorphismReesZeroMatrixSemigroup(S));
if IsTrivial(UnderlyingSemigroup(T)) then
# Check that no two rows or columns are identical
P := Matrix(T);
if rowsDiff(P) and rowsDiff(TransposedMat(P)) then
return true;
fi;
fi;
fi;
return false;
fi;
# CASE 2: S has no zero
return (IsGroup(S) and IsSimpleGroup(S))
or (IsGroupAsSemigroup(S)
and IsSimpleGroup(Range(IsomorphismPermGroup(S))));
end);
# same method for regular ideals, or non-regular without a generating set
InstallMethod(IsCliffordSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if HasParent(S) and HasIsCliffordSemigroup(Parent(S))
and IsCliffordSemigroup(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
return IsRegularSemigroup(S) and NrHClasses(S) = NrDClasses(S);
end);
# same method for non-regular ideals
InstallMethod(IsCliffordSemigroup,
"for a semigroup with generators",
[IsSemigroup and HasGeneratorsOfSemigroup],
function(S)
local gens, idem, f, g;
if HasParent(S) and HasIsCliffordSemigroup(Parent(S))
and IsCliffordSemigroup(Parent(S)) then
return true;
elif HasIsInverseSemigroup(S) and not IsInverseSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not inverse");
return false;
elif HasIsCompletelyRegularSemigroup(S)
and not IsCompletelyRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not completely regular");
return false;
elif not IsFinite(S) then
TryNextMethod();
elif IsGroupAsSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is a group");
return true;
elif not IsRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not regular");
return false;
fi;
gens := GeneratorsOfSemigroup(S);
idem := List(gens, x -> One(GreensHClassOfElementNC(S, x)));
for f in gens do
for g in idem do
if not f * g = g * f then
Info(InfoSemigroups, 2, "the idempotents are not central:");
Info(InfoSemigroups, 2, " ", f, "\n#I and\n#I ", g,
"\n#I do not commute,");
return false;
fi;
od;
od;
return true;
end);
# same method for inverse ideals
InstallMethod(IsCliffordSemigroup, "for an inverse acting semigroup",
[IsInverseSemigroup and IsActingSemigroup],
S -> ForAll(OrbSCC(LambdaOrb(S)), x -> Length(x) = 1));
InstallMethod(IsCliffordSemigroup,
"for an partial perm semigroup with generators",
[IsPartialPermSemigroup and HasGeneratorsOfSemigroup], 100,
function(S)
local x, y;
for x in GeneratorsOfSemigroup(S) do
for y in GeneratorsOfSemigroup(S) do
if Intersection(DomainOfPartialPerm(x), DomainOfPartialPerm(y)) <>
DomainOfPartialPerm(x * y) then
return false;
fi;
od;
od;
return true;
end);
# different method for ideals
InstallMethod(IsCommutativeSemigroup, "for a semigroup with generators",
[IsSemigroup and HasGeneratorsOfSemigroup],
function(S)
local gens, n, i, j;
if not IsFinite(S) then
TryNextMethod();
fi;
gens := GeneratorsOfSemigroup(S);
n := Length(gens);
for i in [1 .. n - 1] do
for j in [i + 1 .. n] do
if not gens[i] * gens[j] = gens[j] * gens[i] then
Info(InfoSemigroups, 2, "generators ", i, " and ", j,
" do not commute");
return false;
fi;
od;
od;
return true;
end);
# same method for non-regular ideals with generators
InstallMethod(IsCompletelyRegularSemigroup,
"for an acting semigroup with generators",
[IsActingSemigroup and HasGeneratorsOfSemigroup],
function(S)
local record, gens, f, o, pos;
if HasParent(S) and HasIsCompletelyRegularSemigroup(Parent(S))
and IsCompletelyRegularSemigroup(Parent(S)) then
return true;
elif HasIsRegularSemigroup(S) and not IsRegularSemigroup(S) then
Info(InfoSemigroups, 2, "semigroup is not regular");
return false;
fi;
record := ShallowCopy(LambdaOrbOpts(S));
record.treehashsize := SEMIGROUPS.OptionsRec(S).hashlen;
gens := List(GeneratorsOfSemigroup(S), x -> ConvertToInternalElement(S, x));
for f in GeneratorsOfSemigroup(S) do
f := ConvertToInternalElement(S, f);
o := Orb(gens, LambdaFunc(S)(f), LambdaAct(S), record);
pos := LookForInOrb(o,
{o, x} -> LambdaRank(S)(LambdaAct(S)(x, f))
<> LambdaRank(S)(x),
1);
# for transformations we could use IsInjectiveListTrans instead
# and the performance would be better!
if pos <> false then
Info(InfoSemigroups, 2, "at least one H-class is not a subgroup");
return false;
fi;
od;
return true;
end);
# same method for regular ideals, or non-regular without a generating set
InstallMethod(IsCompletelyRegularSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if HasParent(S) and HasIsCompletelyRegularSemigroup(Parent(S))
and IsCompletelyRegularSemigroup(Parent(S)) then
return true;
elif HasIsRegularSemigroup(S) and not IsRegularSemigroup(S) then
Info(InfoSemigroups, 2, "semigroup is not regular");
return false;
elif not IsFinite(S) then
TryNextMethod();
fi;
return NrHClasses(S) = NrIdempotents(S);
end);
# same method for inverse ideals
InstallMethod(IsCompletelyRegularSemigroup, "for an inverse semigroup",
[IsInverseSemigroup], IsCliffordSemigroup);
# Notes: this test required to avoid conflict with Smallsemi,
# DeclareSynonymAttr causes problems.
# same method for ideals
InstallMethod(IsCompletelySimpleSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if not IsFinite(S) then
TryNextMethod();
fi;
return IsSimpleSemigroup(S);
end);
# same method for ideals
InstallMethod(IsEUnitaryInverseSemigroup,
"for an inverse semigroup with inverse op",
[IsInverseSemigroup and IsGeneratorsOfInverseSemigroup],
function(S)
if not IsFinite(S) then
TryNextMethod();
fi;
return IsMajorantlyClosed(S, IdempotentGeneratedSubsemigroup(S));
end);
InstallMethod(IsEUnitaryInverseSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if not IsFinite(S) then
TryNextMethod();
elif not IsInverseSemigroup(S) then
return false;
fi;
return IsEUnitaryInverseSemigroup(AsSemigroup(IsPartialPermSemigroup, S));
end);
# different method for ideals TODO(later) or same?
InstallMethod(IsFactorisableInverseMonoid,
"for an inverse semigroup with generators",
[IsInverseSemigroup and HasGeneratorsOfSemigroup],
function(S)
local G, iso, enum, func, x;
if not IsFinite(S) then
TryNextMethod();
fi;
G := GroupOfUnits(S);
if G = fail then
return false;
elif IsTrivial(G) then
return IsSemilattice(S);
fi;
iso := InverseGeneralMapping(IsomorphismPermGroup(G));
enum := Enumerator(Source(iso));
func := NaturalLeqInverseSemigroup(S);
for x in Generators(S) do
if not x in G then
if not ForAny(enum, y -> func(x, y ^ iso)) then
return false;
fi;
fi;
od;
return true;
end);
InstallMethod(IsHTrivial, "for a trans. semigroup with known generators",
[IsTransformationSemigroup and HasGeneratorsOfSemigroup],
function(S)
local x;
for x in GeneratorsOfSemigroup(S) do
if IndexPeriodOfTransformation(x)[2] <> 1 then
return false;
fi;
od;
TryNextMethod();
end);
InstallMethod(IsHTrivial, "for a p. perm. semigroup with known generators",
[IsPartialPermSemigroup and HasGeneratorsOfSemigroup],
function(S)
local x;
for x in GeneratorsOfSemigroup(S) do
if IndexPeriodOfPartialPerm(x)[2] <> 1 then
return false;
fi;
od;
TryNextMethod();
end);
# same method for ideals
InstallMethod(IsHTrivial, "for an acting semigroup",
[IsActingSemigroup],
function(S)
local D;
if HasParent(S) and HasIsHTrivial(Parent(S)) and IsHTrivial(Parent(S)) then
return true;
fi;
for D in IteratorOfDClasses(S) do
if not IsTrivial(SchutzenbergerGroup(D)) then
return false;
fi;
od;
return true;
end);
InstallMethod(IsHTrivial, "for a semigroup", [IsSemigroup],
function(S)
if HasParent(S) and HasIsHTrivial(Parent(S)) and IsHTrivial(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
# TODO(later) use IndexPeriodOfSemigroupElement as above
return NrHClasses(S) = Size(S);
end);
# same method for non-inverse ideals
InstallMethod(IsLTrivial, "for an acting semigroup",
[IsActingSemigroup],
function(S)
local D;
if HasParent(S) and HasIsLTrivial(Parent(S)) and IsLTrivial(Parent(S)) then
return true;
fi;
for D in IteratorOfDClasses(S) do
if not IsTrivial(SchutzenbergerGroup(D)) or Length(RhoOrbSCC(D)) <> 1 then
return false;
fi;
od;
return true;
end);
# same method for inverse ideals
InstallMethod(IsLTrivial, "for an inverse acting semigroup",
[IsInverseActingSemigroupRep],
function(S)
if HasParent(S) and HasIsLTrivial(Parent(S)) and IsLTrivial(Parent(S)) then
return true;
fi;
return ForAll(OrbSCC(LambdaOrb(S)), x -> Length(x) = 1);
end);
# same method for ideals
InstallMethod(IsLTrivial, "for a semigroup",
[IsSemigroup],
function(S)
if HasParent(S) and HasIsLTrivial(Parent(S)) and IsLTrivial(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
return NrLClasses(S) = Size(S);
end);
# same method for ideals
InstallMethod(IsRTrivial, "for an inverse semigroup",
[IsInverseSemigroup], IsLTrivial);
# different method for ideals
InstallMethod(IsRTrivial, "for a transformation semigroup with generators",
[IsTransformationSemigroup and HasGeneratorsOfSemigroup],
2, # to beat the method for acting semigroups
S -> IsAcyclicDigraph(DigraphRemoveLoops(DigraphOfActionOnPoints(S))));
# different method for ideals
InstallMethod(IsRTrivial, "for a partial perm semigroup with generators",
[IsPartialPermSemigroup and HasGeneratorsOfSemigroup],
function(S)
if ForAny(GeneratorsOfSemigroup(S),
x -> ForAny(CyclesOfPartialPerm(x), y -> Length(y) > 1)) then
return false;
fi;
return ForAll(CyclesOfPartialPermSemigroup(S), x -> Length(x) = 1);
end);
# same method for non-inverse ideals
InstallMethod(IsRTrivial, "for an acting semigroup",
[IsActingSemigroup],
function(S)
local x;
if HasParent(S) and HasIsRTrivial(Parent(S)) and IsRTrivial(Parent(S)) then
return true;
elif IsClosedData(SemigroupData(S)) and IsClosedOrbit(RhoOrb(S)) then
for x in GreensDClasses(S) do
if (not IsTrivial(SchutzenbergerGroup(x)))
or Length(LambdaOrbSCC(x)) > 1 then
return false;
fi;
od;
return true;
fi;
for x in IteratorOfRClasses(S) do
if (not IsTrivial(SchutzenbergerGroup(x)))
or Length(LambdaOrbSCC(x)) > 1 then
return false;
fi;
od;
return true;
end);
InstallMethod(IsRTrivial, "for a semigroup", [IsSemigroup],
function(S)
if HasParent(S) and HasIsRTrivial(Parent(S)) and IsRTrivial(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
return Size(S) = NrRClasses(S);
end);
InstallMethod(IsGroupAsSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if HasParent(S) and HasIsGroupAsSemigroup(Parent(S))
and IsGroupAsSemigroup(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
return NrRClasses(S) = 1 and NrLClasses(S) = 1;
end);
# same method for non-regular ideals
InstallMethod(IsGroupAsSemigroup, "for an acting semigroup",
[IsActingSemigroup],
function(S)
local gens, lambdafunc, lambda, rhofunc, rho, tester, lambda_f, rho_f, f;
if HasParent(S) and HasIsGroupAsSemigroup(Parent(S))
and IsGroupAsSemigroup(Parent(S)) then
return true;
fi;
gens := GeneratorsOfSemigroup(S); # not GeneratorsOfMonoid!
if IsActingSemigroupWithFixedDegreeMultiplication(S)
and ForAll(gens, x -> ActionRank(S)(x) = ActionDegree(x)) then
return true;
fi;
lambdafunc := LambdaFunc(S);
lambda := lambdafunc(gens[1]);
rhofunc := RhoFunc(S);
rho := rhofunc(gens[1]);
tester := IdempotentTester(S);
for f in gens do
lambda_f := lambdafunc(f);
rho_f := rhofunc(f);
if lambda_f <> lambda or rho_f <> rho or not tester(lambda_f, rho_f) then
return false;
fi;
od;
return true;
end);
# same method for ideals
InstallMethod(IsIdempotentGenerated, "for a semigroup",
[IsSemigroup],
function(S)
local gens, T, ranks, min, new, max, i;
if not IsFinite(S) then
TryNextMethod();
elif HasIsMonoidAsSemigroup(S) and IsMonoidAsSemigroup(S)
and not IsTrivial(GroupOfUnits(S)) then
return false;
fi;
gens := GeneratorsOfSemigroup(S);
if ForAll(gens, IsIdempotent) then
Info(InfoSemigroups, 2, "all the generators are idempotents");
return true;
elif HasIdempotentGeneratedSubsemigroup(S) or not IsActingSemigroup(S) then
T := IdempotentGeneratedSubsemigroup(S);
else
ranks := List(gens, x -> ActionRank(S)(x));
min := MinimumList(ranks);
if HasIdempotents(S) then
new := Filtered(Idempotents(S), x -> ActionRank(S)(x) >= min);
else
max := MaximumList(ranks);
new := [];
for i in [min .. max] do
Append(new, Idempotents(S, i));
od;
fi;
if IsEmpty(new) then
return false;
fi;
T := Semigroup(new, rec(acting := true));
fi;
# T is not always the idempotent generated subsemigroup!
return ForAll(gens, x -> x in T);
end);
# same method for inverse ideals
InstallMethod(IsIdempotentGenerated, "for an inverse semigroup",
[IsInverseSemigroup], IsSemilattice);
# same method for ideals
InstallMethod(IsInverseSemigroup, "for a semigroup",
[IsSemigroup],
1, # to beat sgpviz
function(S)
local lambda, rho, x;
if HasIsRegularSemigroup(S) and not IsRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not regular");
return false;
elif not IsFinite(S) then
TryNextMethod();
elif HasGeneratorsOfSemigroup(S) and
IsGeneratorsOfInverseSemigroup(GeneratorsOfSemigroup(S)) and
ForAll(GeneratorsOfSemigroup(S), x -> x ^ -1 in S) then
return true;
elif IsCompletelyRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is completely regular");
return IsCliffordSemigroup(S);
elif IsActingSemigroup(S) then
lambda := LambdaOrb(S);
Enumerate(lambda);
rho := RhoOrb(S);
Enumerate(rho, Length(lambda) + 1);
if not (IsClosedOrbit(rho) and Length(rho) = Length(lambda)) then
Info(InfoSemigroups, 2,
"the numbers of lambda and rho values are not equal");
return false;
fi;
fi;
if HasGreensDClasses(S) then
for x in GreensDClasses(S) do
if not IsRegularGreensClass(x)
or NrRClasses(x) <> NrLClasses(x)
or NrIdempotents(x) <> NrRClasses(x) then
return false;
fi;
od;
return true;
fi;
return NrLClasses(S) = NrRClasses(S)
and IsRegularSemigroup(S)
and NrIdempotents(S) = NrRClasses(S);
end);
# same method for ideals
InstallMethod(IsLeftSimple, "for a semigroup", [IsSemigroup],
function(S)
if HasIsRegularSemigroup(S) and not IsRegularSemigroup(S) then
return false;
elif not IsFinite(S) then
TryNextMethod();
fi;
return NrLClasses(S) = 1;
end);
# same method for ideals
InstallMethod(IsLeftSimple, "for an inverse semigroup",
[IsInverseSemigroup], IsGroupAsSemigroup);
# different method for ideals without generators
InstallMethod(IsLeftZeroSemigroup,
"for an acting semigroup with generators",
[IsActingSemigroup and HasGeneratorsOfSemigroup],
function(S)
local gens, lambda, val, x;
if HasParent(S) and HasIsLeftZeroSemigroup(Parent(S))
and IsLeftZeroSemigroup(Parent(S)) then
return true;
fi;
gens := GeneratorsOfSemigroup(S);
lambda := LambdaFunc(S);
val := lambda(gens[1]);
for x in gens do
if lambda(x) <> val then
return false;
fi;
od;
return ForAll(gens, IsIdempotent);
end);
# works for finite and infinite
InstallMethod(IsLeftZeroSemigroup, "for a semigroup", [IsSemigroup],
function(S)
if HasParent(S) and HasIsLeftZeroSemigroup(Parent(S))
and IsLeftZeroSemigroup(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
return IsLeftSimple(S) and IsRTrivial(S);
end);
# same method for ideals
InstallMethod(IsLeftZeroSemigroup, "for an inverse semigroup",
[IsInverseSemigroup], IsTrivial);
# not applicable for ideals
InstallImmediateMethod(IsMonogenicSemigroup,
IsSemigroup and IsFinite and HasGeneratorsOfSemigroup,
0,
function(S)
local gens;
gens := GeneratorsOfSemigroup(S);
if Length(gens) <= 1 then
SetMinimalSemigroupGeneratingSet(S, gens);
return Length(gens) = 1;
elif CanEasilyCompareElements(gens)
and ForAll([2 .. Length(gens)], i -> gens[1] = gens[i]) then
SetMinimalSemigroupGeneratingSet(S, [gens[1]]);
return true;
fi;
TryNextMethod();
end);
InstallImmediateMethod(IsMonogenicMonoid,
IsMonoid and IsFinite and HasGeneratorsOfMonoid,
0,
function(S)
local gens;
gens := GeneratorsOfMonoid(S);
if Length(gens) <= 1 then
SetMinimalMonoidGeneratingSet(S, gens);
return true;
elif CanEasilyCompareElements(gens)
and ForAll([2 .. Length(gens)], i -> gens[1] = gens[i]) then
SetMinimalMonoidGeneratingSet(S, [gens[1]]);
return true;
fi;
TryNextMethod();
end);
# same method for ideals
InstallMethod(IsMonogenicSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
local I, gens, y, i;
if HasGeneratorsOfSemigroup(S) then
gens := GeneratorsOfSemigroup(S);
if not IsDuplicateFreeList(gens) then
gens := ShallowCopy(DuplicateFreeList(gens));
Info(InfoSemigroups, 2, "there are repeated generators");
fi;
if Length(gens) = 1 then
Info(InfoSemigroups, 2, "the semigroup only has one generator");
SetMinimalSemigroupGeneratingSet(S, gens);
return true;
fi;
fi;
if not IsFinite(S) then
TryNextMethod();
fi;
I := MinimalIdeal(S);
if not (IsGroup(I) or IsGroupAsSemigroup(I)) then
Info(InfoSemigroups, 2, "the minimal ideal is not a group.");
return false;
elif not IsCyclic(Range(IsomorphismPermGroup(I))) then
Info(InfoSemigroups, 2, "the minimal ideal is a non-cyclic group.");
return false;
elif HasGreensDClasses(S) then
i := NrDClasses(S);
if not IsTrivial(I) then
i := i - 1;
fi;
if i <> Number(GreensDClasses(S), IsTrivial) then
return false;
fi;
fi;
gens := GeneratorsOfSemigroup(S);
for i in [1 .. Length(gens)] do
y := gens[i];
if ForAll(gens, x -> x in Semigroup(y)) then
Info(InfoSemigroups, 2, "the semigroup is generated by generator ", i);
SetMinimalSemigroupGeneratingSet(S, [y]);
return true;
fi;
od;
Info(InfoSemigroups, 2, "at least one generator does not belong to the",
" semigroup generated by any ");
Info(InfoSemigroups, 2, "other generator.");
return false;
end);
# same method for ideals
InstallMethod(IsMonogenicInverseSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if not IsInverseSemigroup(S) then
return false;
elif not IsFinite(S) then
TryNextMethod();
fi;
return IsMonogenicInverseSemigroup(AsSemigroup(IsPartialPermSemigroup, S));
end);
# same method for ideals
InstallMethod(IsMonogenicInverseSemigroup,
"for an inverse semigroup with inverse op",
[IsInverseSemigroup and IsGeneratorsOfInverseSemigroup],
function(S)
local gens, I, y, i;
if HasGeneratorsOfInverseSemigroup(S) then
gens := GeneratorsOfInverseSemigroup(S);
if not IsDuplicateFreeList(gens) then
gens := ShallowCopy(DuplicateFreeList(gens));
Info(InfoSemigroups, 2, "there are repeated generators");
fi;
if Length(gens) = 1 then
Info(InfoSemigroups, 2, "the inverse semigroup only has one generator");
SetMinimalInverseSemigroupGeneratingSet(S, gens);
return true;
fi;
fi;
if not IsFinite(S) then
TryNextMethod();
fi;
I := MinimalIdeal(S);
if not IsCyclic(Range(IsomorphismPermGroup(I))) then
Info(InfoSemigroups, 2, "the minimal ideal is a non-cyclic group.");
return false;
fi;
gens := GeneratorsOfInverseSemigroup(S);
for i in [1 .. Length(gens)] do
y := gens[i];
if ForAll(gens, x -> x in InverseSemigroup(y)) then
Info(InfoSemigroups, 2, "the inverse semigroup is generated by ",
"generator ", i);
SetMinimalInverseSemigroupGeneratingSet(S, [y]);
return true;
fi;
od;
Info(InfoSemigroups, 2, "at least one generator does not belong to the",
" inverse semigroup generated by any ");
Info(InfoSemigroups, 2, "other generator.");
return false;
end);
InstallMethod(IsMonogenicMonoid, "for a monoid",
[IsMonoid],
function(S)
local I, gens, y, i;
if HasGeneratorsOfMonoid(S) then
gens := GeneratorsOfMonoid(S);
if not IsDuplicateFreeList(gens) then
gens := ShallowCopy(DuplicateFreeList(gens));
Info(InfoSemigroups, 2, "there are repeated generators");
fi;
if Length(gens) = 1 then
Info(InfoSemigroups, 2, "the monoid only has one generator");
SetMinimalMonoidGeneratingSet(S, gens);
return true;
fi;
fi;
if not IsFinite(S) then
TryNextMethod();
fi;
I := MinimalIdeal(S);
if not (IsGroup(I) or IsGroupAsSemigroup(I)) then
Info(InfoSemigroups, 2, "the minimal ideal is not a group.");
return false;
elif not IsCyclic(Range(IsomorphismPermGroup(I))) then
Info(InfoSemigroups, 2, "the minimal ideal is a non-cyclic group.");
return false;
elif HasGreensDClasses(S) then
i := NrDClasses(S);
if not IsTrivial(I) then
i := i - 1;
fi;
if i <> Number(GreensDClasses(S), IsTrivial) then
return false;
fi;
fi;
gens := GeneratorsOfMonoid(S);
for i in [1 .. Length(gens)] do
y := gens[i];
if ForAll(gens, x -> x in Monoid(y)) then
Info(InfoSemigroups, 2, "the monoid is generated by generator ", i);
SetMinimalMonoidGeneratingSet(S, [y]);
return true;
fi;
od;
Info(InfoSemigroups, 2, "at least one generator does not belong to the",
" monoid generated by any ");
Info(InfoSemigroups, 2, "other generator.");
return false;
end);
InstallMethod(IsMonogenicInverseMonoid, "for a monoid",
[IsMonoid],
function(S)
if not IsInverseMonoid(S) then
return false;
elif not IsFinite(S) then
TryNextMethod();
fi;
return IsMonogenicInverseMonoid(AsMonoid(IsPartialPermMonoid, S));
end);
InstallMethod(IsMonogenicInverseMonoid,
"for an inverse monoid with inverse op",
[IsInverseMonoid and IsGeneratorsOfInverseSemigroup],
function(S)
local gens, I, y, i;
if HasGeneratorsOfInverseMonoid(S) then
gens := GeneratorsOfInverseMonoid(S);
if not IsDuplicateFreeList(gens) then
gens := ShallowCopy(DuplicateFreeList(gens));
Info(InfoSemigroups, 2, "there are repeated generators");
fi;
if Length(gens) = 1 then
Info(InfoSemigroups, 2, "the inverse monoid only has one generator");
SetMinimalInverseMonoidGeneratingSet(S, gens);
return true;
fi;
fi;
if not IsFinite(S) then
# WW I cannot find an example of an infinite inverse monoid
# with InverseOp - FreeGroup(1) should work, but it is not in the category
TryNextMethod();
fi;
I := MinimalIdeal(S);
if not IsCyclic(Range(IsomorphismPermGroup(I))) then
Info(InfoSemigroups, 2, "the minimal ideal is a non-cyclic group.");
return false;
fi;
gens := GeneratorsOfInverseMonoid(S);
for i in [1 .. Length(gens)] do
y := gens[i];
if ForAll(gens, x -> x in InverseMonoid(y)) then
Info(InfoSemigroups, 2, "the inverse monoid is generated by generator ",
i);
SetMinimalInverseMonoidGeneratingSet(S, [y]);
return true;
fi;
od;
Info(InfoSemigroups, 2, "at least one generator does not belong to the",
" inverse monoid generated by any ");
Info(InfoSemigroups, 2, "other generator.");
return false;
end);
# same method for ideals
InstallMethod(IsMonoidAsSemigroup, "for a semigroup",
[IsSemigroup], S -> MultiplicativeNeutralElement(S) <> fail);
# same method for ideals
InstallMethod(IsOrthodoxSemigroup, "for a semigroup",
[IsSemigroup], SUM_FLAGS, # to beat the Smallsemi method
function(S)
if not IsFinite(S) then
# WW we cannot test the following line, since the error message we
# eventually get depends on whether or not Smallsemi is loaded
TryNextMethod();
elif not IsRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not regular");
return false;
fi;
Info(InfoSemigroups, 2, "the idempotents do not form a subsemigroup");
return IsSemigroupWithClosedIdempotents(S);
end);
InstallMethod(IsSemigroupWithClosedIdempotents, "for a semigroup",
[IsSemigroup],
function(S)
local e, m, i, j;
if not IsFinite(S) then
TryNextMethod();
fi;
e := Idempotents(S);
m := NrIdempotents(S);
for i in [1 .. m] do
for j in [i + 1 .. m] do
if not IsIdempotent(e[i] * e[j]) or not IsIdempotent(e[j] * e[i]) then
Info(InfoSemigroups, 2, "the product of idempotents ", i, " and ", j,
" (in some order) is not idempotent");
return false;
fi;
od;
od;
return true;
end);
InstallTrueMethod(IsSemigroupWithClosedIdempotents,
IsSemigroupWithCommutingIdempotents);
# same method for ideals, works for finite and infinite
InstallMethod(IsRectangularBand, "for a semigroup",
[IsSemigroup],
function(S)
if HasParent(S) and HasIsRectangularBand(Parent(S))
and IsRectangularBand(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
elif not IsSimpleSemigroup(S) then
# this case is not true for infinite semigroups:
# the bicyclic monoid is a simple h-trivial semigroup which is not a band
Info(InfoSemigroups, 2, "the semigroup is not simple");
return false;
elif HasIsBand(S) then
return IsBand(S);
fi;
return IsHTrivial(S);
end);
# same method for ideals
InstallMethod(IsRectangularBand, "for an inverse semigroup",
[IsInverseSemigroup], IsTrivial);
# different method for ideals (ideals know at their point of creation if they
# are regular or not)
InstallMethod(IsRegularSemigroup, "for an acting semigroup with generators",
[IsActingSemigroup and HasGeneratorsOfSemigroup],
function(S)
local tester, rhofunc, lookfunc, data, i;
if IsSimpleSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is simple");
return true;
elif HasIsCompletelyRegularSemigroup(S)
and IsCompletelyRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is completely regular");
return true;
elif HasGreensDClasses(S) then
return ForAll(GreensDClasses(S), IsRegularDClass);
fi;
tester := IdempotentTester(S);
rhofunc := RhoFunc(S);
# look for <S> not being regular
lookfunc := function(data, x)
local rho, scc, i;
if data!.repslens[x[2]][data!.orblookup1[x[6]]] > 1 then
return true;
elif IsActingSemigroupWithFixedDegreeMultiplication(S)
and ActionRank(S)(x[4]) = ActionDegree(x[4]) then
return false;
fi;
rho := rhofunc(x[4]);
scc := OrbSCC(x[3])[x[2]];
for i in scc do
if tester(x[3][i], rho) then
return false;
fi;
od;
return true;
end;
data := SemigroupData(S);
for i in [2 .. Length(data)] do
if lookfunc(data, data[i]) then
return false;
fi;
od;
if IsClosedData(data) then
return true;
fi;
data := Enumerate(data, infinity, lookfunc);
return data!.found = false;
end);
# same method for ideals
InstallMethod(IsRegularSemigroupElement,
"for an acting semigroup and multiplicative element",
[IsActingSemigroup, IsMultiplicativeElement],
function(S, x)
local o, scc, rho, tester, i;
if not x in S then
Info(InfoSemigroups, 2, "the element does not belong to the semigroup,");
return false;
elif HasIsRegularSemigroup(S) and IsRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is regular,");
return true;
fi;
o := LambdaOrb(S);
scc := OrbSCC(o)[OrbSCCLookup(o)[Position(o, LambdaFunc(S)(x))]];
rho := RhoFunc(S)(x);
tester := IdempotentTester(S);
for i in scc do
if tester(o[i], rho) then
return true;
fi;
od;
return false;
end);
# same method for ideals
InstallMethod(IsRegularSemigroupElementNC,
"for an acting semigroup and multiplicative element",
[IsActingSemigroup, IsMultiplicativeElement],
function(S, x)
local o, l, scc, rho, tester, i;
if IsClosedOrbit(LambdaOrb(S)) then
o := LambdaOrb(S);
l := Position(o, LambdaFunc(S)(x));
if l = fail then
return false;
fi;
else
# this has to be false, since we're not sure if <x> in <S>
o := GradedLambdaOrb(S, x, false);
l := 1;
fi;
scc := OrbSCC(o)[OrbSCCLookup(o)[l]];
rho := RhoFunc(S)(x);
tester := IdempotentTester(S);
for i in scc do
if tester(o[i], rho) then
return true;
fi;
od;
return false;
end);
InstallMethod(IsRegularSemigroupElementNC,
[IsSemigroup, IsMultiplicativeElement], IsRegularSemigroupElement);
InstallMethod(IsRegularSemigroupElement, "for semigroup", IsCollsElms,
[IsSemigroup, IsMultiplicativeElement],
{S, x} -> x in S and IsRegularGreensClass(RClass(S, x)));
# same method for ideals
InstallMethod(IsRightSimple, "for a semigroup", [IsSemigroup],
function(S)
if HasIsRegularSemigroup(S) and not IsRegularSemigroup(S) then
return false;
elif not IsFinite(S) then
TryNextMethod();
fi;
return NrRClasses(S) = 1;
end);
# same method for ideals
InstallMethod(IsRightSimple, "for an inverse semigroup",
[IsInverseSemigroup], IsGroupAsSemigroup);
# different method for ideals
InstallMethod(IsRightZeroSemigroup,
"for an acting semigroup with generators",
[IsActingSemigroup and HasGeneratorsOfSemigroup],
function(S)
local gens, rho, val, x;
if HasParent(S) and HasIsRightZeroSemigroup(Parent(S))
and IsRightZeroSemigroup(Parent(S)) then
return true;
fi;
gens := GeneratorsOfSemigroup(S);
rho := RhoFunc(S);
val := rho(gens[1]);
for x in gens do
if rho(x) <> val or not IsIdempotent(x) then
return false;
fi;
od;
return true;
end);
InstallMethod(IsRightZeroSemigroup, "for a semigroup", [IsSemigroup],
function(S)
if HasParent(S) and HasIsRightZeroSemigroup(Parent(S))
and IsRightZeroSemigroup(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
return NrRClasses(S) = 1 and Size(S) = NrLClasses(S);
end);
# same method for ideals
InstallMethod(IsRightZeroSemigroup, "for an inverse semigroup",
[IsInverseSemigroup], IsTrivial);
# same method for ideals
InstallMethod(IsSemiband, "for a semigroup", [IsSemigroup],
IsIdempotentGenerated);
# same method for ideals
InstallMethod(IsSemilattice, "for a semigroup", [IsSemigroup],
function(S)
# Do not have to check if HasParent(S) and HasIsSemilattice(Parent(S)) and
# IsSemilattice(Parent(S)) since if this is true, then S is an inverse
# semigroup and the method after the next is used in preference to this one
return IsCommutativeSemigroup(S) and IsBand(S);
end);
# not applicable to ideals
InstallMethod(IsSemilattice,
"for an inverse semigroup with generators",
[IsInverseSemigroup and HasGeneratorsOfSemigroup],
function(S)
if not IsFinite(S) then
TryNextMethod();
elif HasParent(S) and HasIsSemilattice(Parent(S))
and IsSemilattice(Parent(S)) then
return true;
fi;
return ForAll(GeneratorsOfSemigroup(S), IsIdempotent);
end);
# same method for ideals
InstallMethod(IsSemilattice, "for an inverse semigroup",
[IsInverseSemigroup],
function(S)
if HasParent(S) and HasIsSemilattice(Parent(S))
and IsSemilattice(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
return IsDTrivial(S);
end);
InstallMethod(IsSimpleSemigroup, "for a finite semigroup",
[IsSemigroup and IsFinite], S -> NrDClasses(S) = 1);
# same method for ideals
InstallMethod(IsSimpleSemigroup, "for an acting semigroup",
[IsActingSemigroup],
function(S)
local gens, lambdafunc, lambdarank, rank, opts, o, pos, iter, name, f;
if HasIsRegularSemigroup(S) and not IsRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not regular");
return false;
elif HasIsCompletelyRegularSemigroup(S)
and not IsCompletelyRegularSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not completely regular");
return false;
elif HasNrDClasses(S) then
return NrDClasses(S) = 1;
elif HasGeneratorsOfSemigroup(S) then
gens := GeneratorsOfSemigroup(S); # not GeneratorsOfMonoid!
lambdafunc := LambdaFunc(S);
lambdarank := LambdaRank(S);
rank := lambdarank(lambdafunc(gens[1]));
if not ForAll([2 .. Length(gens)],
i -> lambdarank(lambdafunc(gens[i])) = rank) then
return false;
fi;
opts := rec(treehashsize := SEMIGROUPS.OptionsRec(S).hashlen);
for name in RecNames(LambdaOrbOpts(S)) do
opts.(name) := LambdaOrbOpts(S).(name);
od;
for f in gens do
o := Orb(S, LambdaFunc(S)(f), LambdaAct(S), opts);
pos := LookForInOrb(o, {o, x} -> LambdaRank(S)(x) < rank, 1);
if pos <> false then
return false;
fi;
od;
return true;
fi;
# regular ideal case
iter := IteratorOfDClasses(S);
NextIterator(iter);
return IsDoneIterator(iter);
end);
# same method for ideals
InstallMethod(IsSimpleSemigroup, "for a finite inverse semigroup",
[IsInverseSemigroup and IsFinite], IsGroupAsSemigroup);
# different method for ideals
InstallMethod(IsTrivial, "for a semigroup with generators",
[IsSemigroup and HasGeneratorsOfSemigroup],
function(S)
local gens;
if not IsFinite(S) then
return false;
fi;
gens := GeneratorsOfSemigroup(S);
return Length(gens) > 0 and IsIdempotent(gens[1])
and ForAll(gens, x -> gens[1] = x);
end);
InstallMethod(IsUnitRegularMonoid, "for a semigroup",
[IsSemigroup],
function(S)
local G, x;
if not IsRegularSemigroup(S) then
return false;
elif not IsFinite(S) then
TryNextMethod();
fi;
G := GroupOfUnits(S);
if G = fail then
return false;
elif IsTrivial(G) then
return IsBand(S);
fi;
for x in S do
if ForAll(G, y -> x * y * x <> x) then
return false;
fi;
od;
return true;
end);
# same method for ideals
InstallMethod(IsZeroGroup, "for a semigroup", [IsSemigroup],
function(S)
if HasParent(S) and HasIsZeroGroup(Parent(S)) and IsZeroGroup(Parent(S)) then
return S = Parent(S);
elif not IsFinite(S) then
TryNextMethod();
elif MultiplicativeZero(S) = fail then
Info(InfoSemigroups, 2, "the semigroup does not have a zero");
return false;
elif NrHClasses(S) = 2 then
return ForAll(GreensHClasses(S), IsGroupHClass);
fi;
Info(InfoSemigroups, 2, "the semigroup has more than two H-classes");
return false;
end);
# same method for ideals
InstallMethod(IsZeroRectangularBand, "for a semigroup",
[IsSemigroup],
function(S)
if not IsFinite(S) then
TryNextMethod();
elif not IsZeroSimpleSemigroup(S) then
Info(InfoSemigroups, 2, "the semigroup is not 0-simple");
return false;
fi;
return IsHTrivial(S);
end);
# different method for ideals
InstallMethod(IsZeroSemigroup, "for a semigroup", [IsSemigroup],
function(S)
local z, gens, i, j;
if HasParent(S) and HasIsZeroSemigroup(Parent(S))
and IsZeroSemigroup(Parent(S)) then
return true;
elif not IsFinite(S) then
TryNextMethod();
fi;
z := MultiplicativeZero(S);
if z = fail then
Info(InfoSemigroups, 2, "the semigroup does not have a zero");
return false;
fi;
gens := GeneratorsOfSemigroup(S);
for i in [1 .. Length(gens)] do
for j in [1 .. Length(gens)] do
if not gens[i] * gens[j] = z then
Info(InfoSemigroups, 2, "the product of generators ", i, " and ", j,
" is not the multiplicative zero \n", z);
return false;
fi;
od;
od;
return true;
end);
# same method for ideals
InstallMethod(IsZeroSemigroup, "for an inverse semigroup",
[IsInverseSemigroup], IsTrivial);
# same method for ideals
InstallMethod(IsZeroSimpleSemigroup, "for an acting semigroup",
[IsActingSemigroup],
function(S)
local iter, D;
if MultiplicativeZero(S) = fail then
return false;
elif IsClosedData(SemigroupData(S)) then
return IsRegularSemigroup(S) and NrDClasses(S) = 2;
fi;
iter := IteratorOfDClasses(S);
D := NextIterator(iter);
if IsDoneIterator(iter) or not IsRegularDClass(D) then
return false;
fi;
D := NextIterator(iter);
return IsDoneIterator(iter) and IsRegularDClass(D);
end);
# same method for ideals
InstallMethod(IsZeroSimpleSemigroup, "for a semigroup",
[IsSemigroup], 1, # to beat the library method
function(S)
if not IsFinite(S) then
TryNextMethod();
fi;
return MultiplicativeZero(S) <> fail and NrDClasses(S) = 2 and
IsRegularSemigroup(S);
end);
# same method for ideals
InstallMethod(IsZeroSimpleSemigroup, "for a finite inverse semigroup",
[IsInverseSemigroup and IsFinite],
S -> MultiplicativeZero(S) <> fail and NrDClasses(S) = 2);
InstallMethod(IsNilpotentSemigroup, "for a semigroup",
[IsSemigroup],
function(S)
if not IsFinite(S) then
TryNextMethod();
elif HasNrIdempotents(S) and NrIdempotents(S) <> 1 then
return false;
elif MultiplicativeZero(S) = fail then
return false;
fi;
return NrIdempotents(S) = 1;
end);
# WW the following lets us get higher code coverage
InstallMethod(IsFinite, "for a finitely presented semigroup",
[IsFpSemigroup],
function(S)
if IsEmpty(RelationsOfFpSemigroup(S)) or
ForAll(RelationsOfFpSemigroup(S),
x -> IsIdenticalObj(x[1], x[2]))
or Length(GeneratorsOfSemigroup(S)) >
Length(RelationsOfFpSemigroup(S)) then
return false;
fi;
TryNextMethod();
end);
InstallMethod(IsSemigroupWithAdjoinedZero, "for a semigroup", [IsSemigroup],
x -> UnderlyingSemigroupOfSemigroupWithAdjoinedZero(x) <> fail);
InstallMethod(IsSurjectiveSemigroup, "for a semigroup",
[IsSemigroup],
S -> IsEmpty(IndecomposableElements(S)));
InstallMethod(IsFullInverseSubsemigroup,
"for an inverse semigroup and an inverse subsemigroup",
[IsInverseSemigroup, IsInverseSemigroup],
function(S, T)
return IsSubsemigroup(S, T) and IsInverseSemigroup(T)
and NrIdempotents(S) = NrIdempotents(T);
end);
# The filter IsActingSemigroup is added here because otherwise we don't know
# that G and N in the loop in the method below act on the same set.
InstallMethod(IsNormalInverseSubsemigroup,
"for an inverse acting semigroup and subsemigroup",
[IsInverseSemigroup and IsActingSemigroup,
IsInverseSemigroup and IsActingSemigroup],
function(S, T)
local DS, G, N, p, DT;
if not IsSubsemigroup(S, T) then
return false;
fi;
for DT in DClasses(T) do
DS := DClass(S, Representative(DT));
G := Image(IsomorphismPermGroup(GroupHClass(DS)));
N := Image(IsomorphismPermGroup(GroupHClass(DT)));
if not IsSubset(MovedPoints(G), MovedPoints(N)) then
p := MappingPermListList(MovedPoints(N), MovedPoints(G));
N := N ^ p;
fi;
Assert(0, IsSubset(MovedPoints(G), MovedPoints(N)));
if not IsNormal(G, N) then
return false;
fi;
od;
return true;
end);
InstallMethod(IsSelfDualSemigroup,
"for a finite semigroup with CanUseFroidurePin",
[IsSemigroup and CanUseFroidurePin],
function(S)
local T, map;
if IsCommutativeSemigroup(S) then # TODO(later) any more?
return true;
elif NrRClasses(S) <> NrLClasses(S) then
return false;
fi;
T := AsSemigroup(IsFpSemigroup, S);
map := AntiIsomorphismDualFpSemigroup(T);
return SemigroupIsomorphismByImages(T,
Range(map),
GeneratorsOfSemigroup(T),
List(GeneratorsOfSemigroup(T),
x -> x ^ map)) <> fail;
end);
[ Dauer der Verarbeitung: 0.38 Sekunden
(vorverarbeitet)
]
|