Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement LeftSemigroupIdeal and RightSemigroupIdeal #1009

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
335f388
Adding Free Semilattice constructor
Jun2M Feb 7, 2024
5fb0319
For Transformation and PartialPerm Semigroups
Jun2M Feb 7, 2024
4685a06
Lint
Jun2M Feb 8, 2024
721a464
Update semicons.gd
Jun2M Feb 10, 2024
c4f56d9
Merge branch 'main' into main
Jun2M Feb 10, 2024
1ec0a1a
Add docs and a whole lot of tests
Jun2M Feb 10, 2024
f35961a
Minor test fix
Jun2M Feb 11, 2024
9ae4fd9
Merge branch 'main' into main
Jun2M Feb 14, 2024
432322b
Dial back on the number of tests
Jun2M Feb 14, 2024
708e4c1
Merge branch 'main' into main
james-d-mitchell Mar 11, 2024
917be05
Apply suggestions from code review
james-d-mitchell Mar 11, 2024
7977c3b
Change tst to reflect the new error msg
Jun2M Mar 12, 2024
f7338a2
Merge branch 'main' into main
Jun2M Mar 12, 2024
b2463bb
Add Monoid constructors to FreeSemilatticeCons
Jun2M Mar 13, 2024
19266ad
Unbind _IsXMonoid in semicons.gi
Jun2M Mar 13, 2024
58013b4
Update semicons.xml and semicons.gi files
Jun2M Mar 13, 2024
0b2d4d4
Merge branch 'semigroups:main' into main
Jun2M Mar 20, 2024
cfda100
Add left and right semigroup ideal support
Jun2M Mar 20, 2024
df1cc98
Edit docs
Jun2M Mar 26, 2024
0880917
Don't destroy the whole thing
Jun2M Mar 26, 2024
61ac68f
bug fix
Jun2M Mar 26, 2024
4f1200b
Fix ideals.gd
Jun2M Mar 26, 2024
eb93dfc
doc update
Jun2M Mar 26, 2024
4aefac6
Linting
Jun2M Mar 26, 2024
1140492
Factor out the input parsing
Jun2M Mar 27, 2024
abdf215
Hide internal functions and make first edits on ideal.tst
Jun2M Mar 27, 2024
eb4bc56
Appearently not a duplicate
Jun2M Mar 27, 2024
6e76d9b
Linting
Jun2M Mar 27, 2024
c74ebcd
Linting for tst file
Jun2M Mar 28, 2024
a538cc8
Linting for tst
Jun2M Mar 28, 2024
0a39a7c
More tests
Jun2M Apr 2, 2024
97eb398
Linting
Jun2M Apr 2, 2024
2f825bd
Lint
Jun2M Apr 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 50 additions & 0 deletions doc/ideals.xml
Expand Up @@ -98,6 +98,56 @@ gap> I := SemigroupIdeal(S, I, Idempotents(S));
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="LeftSemigroupIdeal">
<ManSection>
<Func Name = "LeftSemigroupIdeal" Arg = "S, obj1, obj2, .. . "/>
<Returns>
A left ideal of a semigroup.
</Returns>
<Description>
If <A>obj1</A>, <A>obj2</A>, .. . are (any combination) of elements of the
semigroup <A>S</A> or collections of elements of <A>S</A> (including
subsemigroups and ideals of <A>S</A>), then <C>LeftSemigroupIdeal</C> returns the
left ideal of the semigroup <A>S</A> generated by the union of
<A>obj1</A>, <A>obj2</A>, .. .. <P/>

The <Ref Func = "Parent" BookName = "ref"/> of the ideal returned by this
function is <A>S</A>.

<Example><![CDATA[
gap> S := SymmetricInverseMonoid(10);
<symmetric inverse monoid of degree 10>
gap> I := LeftSemigroupIdeal(S, PartialPerm([1, 2]));
<LeftMagmaIdeal with 1 generator>]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="RightSemigroupIdeal">
<ManSection>
<Func Name = "RightSemigroupIdeal" Arg = "S, obj1, obj2, .. . "/>
<Returns>
A Right ideal of a semigroup.
</Returns>
<Description>
If <A>obj1</A>, <A>obj2</A>, .. . are (any combination) of elements of the
semigroup <A>S</A> or collections of elements of <A>S</A> (including
subsemigroups and ideals of <A>S</A>), then <C>RightSemigroupIdeal</C> returns the
Right ideal of the semigroup <A>S</A> generated by the union of
<A>obj1</A>, <A>obj2</A>, .. .. <P/>

The <Ref Func = "Parent" BookName = "ref"/> of the ideal returned by this
function is <A>S</A>.

<Example><![CDATA[
gap> S := SymmetricInverseMonoid(10);
<symmetric inverse monoid of degree 10>
gap> I := RightSemigroupIdeal(S, PartialPerm([1, 2]));
<RightMagmaIdeal with 1 generator>]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="SupersemigroupOfIdeal">
<ManSection>
<Attr Name = "SupersemigroupOfIdeal" Arg = "I"/>
Expand Down
2 changes: 2 additions & 0 deletions doc/z-chap09.xml
Expand Up @@ -29,6 +29,8 @@
</Heading>

<#Include Label = "SemigroupIdeal">
<#Include Label = "LeftSemigroupIdeal">
<#Include Label = "RightSemigroupIdeal">
<#Include Label = "Ideals">
</Section>

Expand Down
2 changes: 2 additions & 0 deletions gap/ideals/froidure-pin.gd
Expand Up @@ -9,3 +9,5 @@
##

DeclareAttribute("PositionsInSupersemigroup", IsSemigroupIdeal);
DeclareAttribute("PositionsInSupersemigroup", IsLeftSemigroupIdeal);
DeclareAttribute("PositionsInSupersemigroup", IsRightSemigroupIdeal);
42 changes: 42 additions & 0 deletions gap/ideals/froidure-pin.gi
Expand Up @@ -39,6 +39,48 @@ function(I)
return result;
end);

InstallMethod(PositionsInSupersemigroup,
"for a left semigroup ideal with known generators",
[IsLeftSemigroupIdeal and HasGeneratorsOfSemigroupIdeal and
CanUseFroidurePin],
function(I)
local S, L, result, pos, x;
S := SupersemigroupOfIdeal(I);
L := LeftCayleyDigraph(S);

result := [];
for x in GeneratorsOfSemigroupIdeal(I) do
pos := PositionCanonical(S, x);
if not pos in result then
AddSet(result, pos);
UniteSet(result, VerticesReachableFrom(L, pos));
fi;
od;

return result;
end);

InstallMethod(PositionsInSupersemigroup,
"for a right semigroup ideal with known generators",
[IsRightSemigroupIdeal and HasGeneratorsOfSemigroupIdeal and
CanUseFroidurePin],
function(I)
local S, R, result, pos, x;
S := SupersemigroupOfIdeal(I);
R := RightCayleyDigraph(S);

result := [];
for x in GeneratorsOfSemigroupIdeal(I) do
pos := PositionCanonical(S, x);
if not pos in result then
AddSet(result, pos);
UniteSet(result, VerticesReachableFrom(R, pos));
fi;
od;

return result;
end);

InstallMethod(GeneratorsOfInverseSemigroup,
"for an inverse semigroup ideal with inverse op and generators",
[IsSemigroupIdeal and IsInverseSemigroup
Expand Down
15 changes: 14 additions & 1 deletion gap/ideals/ideals.gd
Expand Up @@ -8,7 +8,20 @@
#############################################################################

DeclareSynonymAttr("GeneratorsOfSemigroupIdeal", GeneratorsOfMagmaIdeal);

DeclareGlobalFunction("SemigroupIdeal");
DeclareGlobalFunction("LeftSemigroupIdeal");
DeclareGlobalFunction("RightSemigroupIdeal");
DeclareGlobalFunction("AnySemigroupIdealInputParsing");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DeclareGlobalFunction("AnySemigroupIdealInputParsing");
DeclareGlobalFunction("AnySemigroupIdealInputParsing");

I'd suggest removing this from here, and instead doing

BindGlobal("_AnySemigroupIdealInputParsing", ...)

in the .gi file instead. Note also the _ in the name (either do this or just put the function into the record SEMIGROUPS, by doing:

SEMIGROUPS.AnySemigroupIdealInputParsing := function(...

again in the .gi file. Same goes for all of the "internal" functions that start with "Any"


DeclareOperation("AnySemigroupIdealByGenerators",
[IsSemigroup, IsOperation, IsListOrCollection]);

DeclareOperation("AnySemigroupIdealByGenerators",
[IsSemigroup, IsOperation, IsListOrCollection, IsRecord]);

DeclareOperation("AnySemigroupIdealByGeneratorsNC",
[IsSemigroup, IsOperation, IsListOrCollection, IsRecord]);

DeclareOperation("SemigroupIdealByGenerators",
[IsSemigroup, IsListOrCollection]);
Expand Down Expand Up @@ -40,6 +53,6 @@ DeclareAttribute("MinimalIdealGeneratingSet", IsSemigroupIdeal);

DeclareAttribute("SupersemigroupOfIdeal", IsSemigroupIdeal);

InstallTrueMethod(IsSemigroup, IsSemigroupIdeal);
InstallTrueMethod(IsSemigroup, IsSemigroupIdeal); # Duplicate with ideal.gi?
Jun2M marked this conversation as resolved.
Show resolved Hide resolved

DeclareAttribute("Ideals", IsSemigroup);
154 changes: 111 additions & 43 deletions gap/ideals/ideals.gi
Expand Up @@ -187,81 +187,121 @@ InstallMethod(Representative, "for a semigroup ideal",

# a convenience, similar to the functions <Semigroup>, <Monoid>, etc

InstallGlobalFunction(SemigroupIdeal,
function(arg...)
InstallGlobalFunction(AnySemigroupIdealInputParsing,
function(inputArgs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to stick to arg... and whenever you want to call this function do:

CallFuncList(AnySemigroup..., arg);

this is like doing

AnySemigroup...(*arg)

in python

local out, i;

if Length(arg) <= 1 then
if Length(inputArgs) <= 1 then
ErrorNoReturn("there must be 2 or more arguments");
elif not IsSemigroup(arg[1]) then
elif not IsSemigroup(inputArgs[1]) then
ErrorNoReturn("the 1st argument is not a semigroup");
elif Length(arg) = 2 and IsMatrix(arg[2]) then
elif Length(inputArgs) = 2 and IsMatrix(inputArgs[2]) then
# special case for matrices, because they may look like lists
return SemigroupIdealByGenerators(arg[1], [arg[2]]);
return [inputArgs[1], [inputArgs[2]]];

elif Length(arg) = 2 and IsList(arg[2]) and 0 < Length(arg[2]) then
elif Length(inputArgs) = 2 and IsList(inputArgs[2]) and 0 < Length(inputArgs[2]) then
# list of generators
return SemigroupIdealByGenerators(arg[1], arg[2]);
elif (IsMultiplicativeElement(arg[2])
and IsGeneratorsOfSemigroup([arg[2]]))
or (IsListOrCollection(arg[2])
and IsGeneratorsOfSemigroup(arg[2]))
or (HasIsEmpty(arg[2]) and IsEmpty(arg[2])) then
return [inputArgs[1], inputArgs[2]];
elif (IsMultiplicativeElement(inputArgs[2])
and IsGeneratorsOfSemigroup([inputArgs[2]]))
or (IsListOrCollection(inputArgs[2])
and IsGeneratorsOfSemigroup(inputArgs[2]))
or (HasIsEmpty(inputArgs[2]) and IsEmpty(inputArgs[2])) then
# generators and collections of generators
out := [];
for i in [2 .. Length(arg)] do
for i in [2 .. Length(inputArgs)] do
# so that we can pass the options record
if i = Length(arg) and IsRecord(arg[i]) then
return SemigroupIdealByGenerators(arg[1], out, arg[i]);
elif IsMultiplicativeElement(arg[i]) and
IsGeneratorsOfSemigroup([arg[i]]) then
Add(out, arg[i]);
elif IsGeneratorsOfSemigroup(arg[i]) then
if HasGeneratorsOfSemigroupIdeal(arg[i]) then
Append(out, GeneratorsOfSemigroupIdeal(arg[i]));
elif HasGeneratorsOfSemigroup(arg[i]) then
Append(out, GeneratorsOfSemigroup(arg[i]));
elif IsList(arg[i]) then
Append(out, arg[i]);
if i = Length(inputArgs) and IsRecord(inputArgs[i]) then
return [inputArgs[1], out, inputArgs[i]];
elif IsMultiplicativeElement(inputArgs[i]) and
IsGeneratorsOfSemigroup([inputArgs[i]]) then
Add(out, inputArgs[i]);
elif IsGeneratorsOfSemigroup(inputArgs[i]) then
if HasGeneratorsOfSemigroupIdeal(inputArgs[i]) then
Append(out, GeneratorsOfSemigroupIdeal(inputArgs[i]));
elif HasGeneratorsOfSemigroup(inputArgs[i]) then
Append(out, GeneratorsOfSemigroup(inputArgs[i]));
elif IsList(inputArgs[i]) then
Append(out, inputArgs[i]);
else
Append(out, AsList(arg[i]));
Append(out, AsList(inputArgs[i]));
fi;
else
ErrorNoReturn("the 2nd argument is not a combination ",
"of generators, lists of generators, ",
"nor semigroups");
fi;
od;
return SemigroupIdealByGenerators(arg[1], out);
return [inputArgs[1], out];
fi;
ErrorNoReturn("invalid arguments");
end);

InstallMethod(SemigroupIdealByGenerators,
"for a semigroup and list or collections",
[IsSemigroup, IsListOrCollection],
{S, gens} -> SemigroupIdealByGenerators(S, gens, SEMIGROUPS.OptionsRec(S)));
InstallGlobalFunction(SemigroupIdeal,
function(arg...)
local parsed;
parsed := AnySemigroupIdealInputParsing(arg);
if Length(parsed) = 3 then
return AnySemigroupIdealByGenerators(parsed[1],
IsMagmaIdeal, parsed[2], parsed[3]);
else
return AnySemigroupIdealByGenerators(parsed[1],
IsMagmaIdeal, parsed[2]);
fi;
end);

InstallMethod(SemigroupIdealByGenerators,
"for semigroup, list or collection, and record",
[IsSemigroup, IsListOrCollection, IsRecord],
function(S, gens, opts)
InstallGlobalFunction(LeftSemigroupIdeal,
function(arg...)
local parsed;
parsed := AnySemigroupIdealInputParsing(arg);
if Length(parsed) = 3 then
return AnySemigroupIdealByGenerators(parsed[1],
IsLeftMagmaIdeal, parsed[2], parsed[3]);
else
return AnySemigroupIdealByGenerators(parsed[1],
IsLeftMagmaIdeal, parsed[2]);
fi;
end);

InstallGlobalFunction(RightSemigroupIdeal,
function(arg...)
local parsed;
parsed := AnySemigroupIdealInputParsing(arg);
if Length(parsed) = 3 then
return AnySemigroupIdealByGenerators(parsed[1],
IsRightMagmaIdeal, parsed[2], parsed[3]);
else
return AnySemigroupIdealByGenerators(parsed[1],
IsRightMagmaIdeal, parsed[2]);
fi;
end);

InstallMethod(AnySemigroupIdealByGenerators,
"for a semigroup, filter and list or collections",
[IsSemigroup, IsOperation, IsListOrCollection],
{S, filter, gens} -> AnySemigroupIdealByGenerators(S,
filter, gens, SEMIGROUPS.OptionsRec(S)));

InstallMethod(AnySemigroupIdealByGenerators,
"for semigroup, filter, list or collection, and record",
[IsSemigroup, IsOperation, IsListOrCollection, IsRecord],
function(S, filter, gens, opts)
if not ForAll(gens, x -> x in S) then
ErrorNoReturn("the 2nd argument (a mult. elt. coll.) do not all ",
"belong to the semigroup");
fi;
return SemigroupIdealByGeneratorsNC(S, gens, opts);
return AnySemigroupIdealByGeneratorsNC(S, filter, gens, opts);
end);

InstallMethod(SemigroupIdealByGeneratorsNC,
"for a semigroup, list or collections, and record",
[IsSemigroup, IsListOrCollection, IsRecord],
function(S, gens, opts)
InstallMethod(AnySemigroupIdealByGeneratorsNC,
"for a semigroup, filter, (list or collections) and record",
[IsSemigroup, IsOperation, IsListOrCollection, IsRecord],
function(S, filter, gens, opts)
local filts, I;
opts := SEMIGROUPS.ProcessOptionsRec(SEMIGROUPS.DefaultOptionsRec, opts);
gens := AsList(gens);

filts := IsMagmaIdeal and IsAttributeStoringRep;
filts := filter and IsAttributeStoringRep;

if opts.acting
and (IsActingSemigroup(S) or IsGeneratorsOfActingSemigroup(gens)) then
Expand Down Expand Up @@ -313,7 +353,16 @@ function(S, gens, opts)
fi;

SetParent(I, S);
SetGeneratorsOfMagmaIdeal(I, gens);
if "IsLeftActedOnBySuperset" in NamesFilter(filter) and
"IsRightActedOnBySuperset" in NamesFilter(filter) then
SetGeneratorsOfMagmaIdeal(I, gens);
elif "IsLeftActedOnBySuperset" in NamesFilter(filter) then
SetGeneratorsOfLeftMagmaIdeal(I, gens);
elif "IsRightActedOnBySuperset" in NamesFilter(filter) then
SetGeneratorsOfRightMagmaIdeal(I, gens);
else
# PANIC
fi;

if not IsActingSemigroup(I) then
# to keep the craziness in the library happy!
Expand All @@ -328,6 +377,25 @@ function(S, gens, opts)
return I;
end);

InstallMethod(SemigroupIdealByGenerators,
"for a semigroup and list or collections",
[IsSemigroup, IsListOrCollection],
{S, gens} -> SemigroupIdealByGenerators(S, gens, SEMIGROUPS.OptionsRec(S)));

InstallMethod(SemigroupIdealByGenerators,
"for semigroup, list or collection, and record",
[IsSemigroup, IsListOrCollection, IsRecord],
function(S, gens, opts)
return AnySemigroupIdealByGenerators(S, IsMagmaIdeal, gens, opts);
end);

InstallMethod(SemigroupIdealByGeneratorsNC,
"for a semigroup, list or collections, and record",
[IsSemigroup, IsListOrCollection, IsRecord],
function(S, gens, opts)
return AnySemigroupIdealByGeneratorsNC(S, IsMagmaIdeal, gens, opts);
end);

InstallMethod(MinimalIdealGeneratingSet,
"for a semigroup ideal with generators",
[IsSemigroupIdeal and HasGeneratorsOfSemigroupIdeal],
Expand Down