Organize raq into standard GAP package file tree
In preparation for a beta release of the raq package, it's important for it to be conformant to current GAP package conventions. Therefore, I have rearranged the file tree to match the Example package quite thoroughly. In so doing, this laid the foundation for the following aspects of the package: - Package documentation. Added makedoc.g at the top level to generate AutoDoc/GAPDoc documentation for raq. Currently the documentation is empty; future development will bring the documentation to cover all existing code. - Package tests. The basic structure to add .tst files in the tst subdirectory of the package is in place. The coverage of tests provided in this commit is minimal; again, further development will extend the test coverage. In addition, PackageInfo.g has been elaborated to include much more complete information about raq and where it will (initially) be posted on line. Resolves: #1
This commit is contained in:
parent
d6b6a11d85
commit
be82518697
19 changed files with 113 additions and 45 deletions
27
lib/byconj.gd
Normal file
27
lib/byconj.gd
Normal file
|
@ -0,0 +1,27 @@
|
|||
# byconj.gd RAQ Quandles by conjugation
|
||||
|
||||
# The following outline of defining c
|
||||
|
||||
DeclareCategory("IsConjugatorObject",
|
||||
IsMultiplicativeElement and IsLeftQuotientElement and
|
||||
IsLSelfDistElement and IsIdempotent);
|
||||
DeclareCategoryCollections("IsConjugatorObject");
|
||||
|
||||
DeclareAttribute("ConjugatorFamily", IsFamily);
|
||||
DeclareAttribute("ConjugatorType", IsFamily);
|
||||
|
||||
DeclareSynonym("IsDefaultConjugatorObject",
|
||||
IsConjugatorObject and IsPositionalObjectOneSlotRep);
|
||||
|
||||
# As far as I can tell, we are not losing any generality here;
|
||||
# to define the conjugator, we need the quotient on one side, to define
|
||||
# left quotients of conjugators we need the quotient on the other side, and
|
||||
# to prove it works we need associativity, which makes the underlying elements
|
||||
# of conjugator objects automatically group elements.
|
||||
DeclareAttribute("ConjugatorObj", IsMultiplicativeElementWithInverse);
|
||||
|
||||
DeclareAttribute("UnderlyingMultiplicativeElement", IsConjugatorObject);
|
||||
|
||||
# The meat of the matter:
|
||||
|
||||
DeclareAttribute("ConjugationQuandle", IsGroup);
|
96
lib/byconj.gi
Normal file
96
lib/byconj.gi
Normal file
|
@ -0,0 +1,96 @@
|
|||
# byconj.gi RAQ Implementation of quandles by conjugation
|
||||
|
||||
InstallMethod(ConjugatorFamily, "for a family",
|
||||
[IsFamily],
|
||||
# Does GAP provide any way to get at the name of a family other than
|
||||
# fam!.NAME ?
|
||||
fam -> NewFamily(Concatenation("ConjugatorFamily(", fam!.NAME, ")"),
|
||||
IsConjugatorObject)
|
||||
);
|
||||
|
||||
InstallMethod(ConjugatorType, "for a family",
|
||||
[IsFamily],
|
||||
fam -> NewType(ConjugatorFamily(fam), IsDefaultConjugatorObject)
|
||||
);
|
||||
|
||||
InstallMethod(ConjugatorObj,
|
||||
"for a mult element that allows quotients (and should be assoc)",
|
||||
[IsMultiplicativeElementWithInverse],
|
||||
obj -> Objectify(ConjugatorType(FamilyObj(obj)), [Immutable(obj)])
|
||||
);
|
||||
|
||||
## Printing and viewing
|
||||
InstallMethod(String, "for conjugator objects",
|
||||
[IsDefaultConjugatorObject],
|
||||
obj -> Concatenation("ConjugatorObj( ", String(obj![1]), " )")
|
||||
);
|
||||
|
||||
InstallMethod(ViewString, "for conjugator objects",
|
||||
[IsDefaultConjugatorObject],
|
||||
obj -> Concatenation("^", ViewString(obj![1]), ":")
|
||||
);
|
||||
|
||||
InstallMethod(UnderlyingMultiplicativeElement, "for a conjugator object",
|
||||
[IsDefaultConjugatorObject],
|
||||
obj -> obj![1]
|
||||
);
|
||||
|
||||
InstallMethod(\=, "for two conjugator objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultConjugatorObject, IsDefaultConjugatorObject],
|
||||
function(l,r) return l![1] = r![1]; end
|
||||
);
|
||||
|
||||
InstallMethod(\<, "for two conjugator objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultConjugatorObject, IsDefaultConjugatorObject],
|
||||
function(l,r) return l![1] < r![1]; end
|
||||
);
|
||||
|
||||
InstallMethod(\*, "for two conjugator objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultConjugatorObject, IsDefaultConjugatorObject],
|
||||
function(l,r) return ConjugatorObj(LeftQuotient(l![1],r![1])*l![1]); end
|
||||
);
|
||||
|
||||
InstallOtherMethod(LeftQuotient, "for two conjugator objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultConjugatorObject,IsDefaultConjugatorObject],
|
||||
function(l,r) return ConjugatorObj((l![1]*r![1])/l![1]); end
|
||||
);
|
||||
|
||||
InstallMethod(ConjugationQuandle, "for a group",
|
||||
[IsGroup and IsFinite],
|
||||
function(G)
|
||||
local fam, elts, Q;
|
||||
fam := CollectionsFamily(ConjugatorFamily(ElementsFamily(FamilyObj(G))));
|
||||
# Question: how do we feasibly determine a set of generators of
|
||||
# Conj(G) from a set of generators of G, so that we can handle infinite
|
||||
# conj-quandles here?
|
||||
elts := List(Elements(G), g -> ConjugatorObj(g) );
|
||||
# What we would like to do is
|
||||
# return AsLeftQuandle[NC?](elts);
|
||||
# but that's NIY.
|
||||
Q := LeftQuandleNC(fam, elts);
|
||||
# We know that elts was actually closed under * and LeftQuotient, and
|
||||
# since we are in a method only for finite groups, ergo Q is finite:
|
||||
SetIsFinite(Q, true);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
## Methods that are easier in conjugator quandles:
|
||||
|
||||
InstallMethod(GeneratorsOfMagma,
|
||||
"for a quandle generated by conjugator objects",
|
||||
[IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup and
|
||||
IsConjugatorObjectCollection],
|
||||
function(Q)
|
||||
local gens, invgens;
|
||||
# idea: ^g^-1: * ^h: = ^g: \ ^h:, so the generators of the quasigroup
|
||||
# together with their inverses generate the quandle as a magma.
|
||||
gens := GeneratorsOfLeftQuasigroup(Q);
|
||||
invgens := Set(gens, g -> ConjugatorObj(Inverse(g![1])));
|
||||
UniteSet(invgens, gens);
|
||||
return invgens;
|
||||
end);
|
||||
|
43
lib/bytable.gd
Normal file
43
lib/bytable.gd
Normal file
|
@ -0,0 +1,43 @@
|
|||
## bytable.gd RAQ Quasigroups, racks, and quandles by multiplication tables.
|
||||
|
||||
## Self-distributivity
|
||||
# Predicates on tables:
|
||||
DeclareProperty( "IsLeftSelfDistributiveTable", IsMatrix );
|
||||
DeclareProperty( "IsRightSelfDistributiveTable", IsMatrix );
|
||||
DeclareProperty( "IsElementwiseIdempotentTable", IsMatrix );
|
||||
|
||||
## Attributes (typically used on the families of elements created from a
|
||||
## multiplication table) to store sections and division tables
|
||||
DeclareAttribute("LeftPerms", HasMultiplicationTable);
|
||||
DeclareAttribute("RightPerms", HasMultiplicationTable);
|
||||
DeclareAttribute("LeftDivisionTable", HasMultiplicationTable);
|
||||
DeclareAttribute("RightDivisionTable", HasMultiplicationTable);
|
||||
|
||||
## Create Quasigroups and racks from multiplication tables
|
||||
DeclareGlobalFunction("LeftQuasigroupByMultiplicationTable");
|
||||
DeclareGlobalFunction("LeftQuasigroupByMultiplicationTableNC");
|
||||
DeclareGlobalFunction("LeftRackByMultiplicationTable");
|
||||
DeclareGlobalFunction("LeftRackByMultiplicationTableNC");
|
||||
DeclareGlobalFunction("LeftQuandleByMultiplicationTable");
|
||||
DeclareGlobalFunction("LeftQuandleByMultiplicationTableNC");
|
||||
DeclareGlobalFunction("RightQuasigroupByMultiplicationTable");
|
||||
DeclareGlobalFunction("RightQuasigroupByMultiplicationTableNC");
|
||||
DeclareGlobalFunction("RightRackByMultiplicationTable");
|
||||
DeclareGlobalFunction("RightRackByMultiplicationTableNC");
|
||||
DeclareGlobalFunction("RightQuandleByMultiplicationTable");
|
||||
DeclareGlobalFunction("RightQuandleByMultiplicationTableNC");
|
||||
|
||||
## Property of a collection that its elements know their multiplication table
|
||||
DeclareProperty("IsBuiltFromMultiplicationTable", IsCollection);
|
||||
|
||||
## Create quasigroups etc by permutations
|
||||
DeclareGlobalFunction("LeftQuasigroupByPerms");
|
||||
DeclareGlobalFunction("LeftRackByPerms");
|
||||
DeclareGlobalFunction("LeftRackByPermsNC");
|
||||
DeclareGlobalFunction("LeftQuandleByPerms");
|
||||
DeclareGlobalFunction("LeftQuandleByPermsNC");
|
||||
DeclareGlobalFunction("RightQuasigroupByPerms");
|
||||
DeclareGlobalFunction("RightRackByPerms");
|
||||
DeclareGlobalFunction("RightRackByPermsNC");
|
||||
DeclareGlobalFunction("RightQuandleByPerms");
|
||||
DeclareGlobalFunction("RightQuandleByPermsNC");
|
456
lib/bytable.gi
Normal file
456
lib/bytable.gi
Normal file
|
@ -0,0 +1,456 @@
|
|||
## bytable.gi RAQ Implementation of racks etc. by multiplication tables.
|
||||
|
||||
## Predicates to check tables for distributivity
|
||||
InstallMethod(IsRightSelfDistributiveTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
T -> IsLeftSelfDistributiveTable(TransposedMat(T))
|
||||
);
|
||||
|
||||
InstallMethod(IsLeftSelfDistributiveTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
function(T)
|
||||
# Everybody else does it by checking all of the cases, so why not me, too?
|
||||
# Is there a better way?
|
||||
local n,i,j,k;
|
||||
n := Length(T);
|
||||
for i in [1..n] do for j in [1..n] do for k in [1..n] do
|
||||
if T[i, T[j,k]] <> T[T[i,j], T[i,k]] then return false; fi;
|
||||
od; od; od;
|
||||
return true;
|
||||
end);
|
||||
|
||||
InstallMethod(IsElementwiseIdempotentTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
T -> ForAll([1..Length(T)], i->(T[i,i]=i))
|
||||
);
|
||||
|
||||
## And a general principle: collections from finite families are finite.
|
||||
|
||||
InstallMethod(IsFinite, "for any collection (with a finite element family)",
|
||||
[IsCollection],
|
||||
function(C)
|
||||
local ef;
|
||||
ef := ElementsFamily(FamilyObj(C));
|
||||
if HasIsFinite(ef) and IsFinite(ef) then return true; fi;
|
||||
TryNextMethod();
|
||||
return fail;
|
||||
end);
|
||||
|
||||
|
||||
## And now create them from multiplication tables
|
||||
|
||||
# First a helper function
|
||||
MagmaNumber@ := 1;
|
||||
MagmaLetters@ := "VABCDEFGHJKMNPSTU";
|
||||
MagmaBase@ := Length(MagmaLetters@);
|
||||
NextMagmaString@ := function(filts)
|
||||
local str, n;
|
||||
str := "l";
|
||||
if not "IsLeftQuotientElement" in NamesFilter(filts) then
|
||||
str := "r";
|
||||
fi;
|
||||
n := MagmaNumber@;
|
||||
MagmaNumber@ := MagmaNumber@ + 1;
|
||||
while n > 0 do
|
||||
Add(str, MagmaLetters@[RemInt(n, MagmaBase@) + 1]);
|
||||
n := QuoInt(n, MagmaBase@);
|
||||
od;
|
||||
return str;
|
||||
end;
|
||||
|
||||
FiniteMagmaCreator@ := function(tbl, cnstr, filts)
|
||||
local M;
|
||||
M := MagmaByMultiplicationTableCreatorNC(
|
||||
tbl, cnstr, filts and IsMagmaByMultiplicationTableObj);
|
||||
# Is there such a thing as a non-finite table in GAP? Anyhow...
|
||||
SetIsFinite(ElementsFamily(FamilyObj(M)), IsFinite(tbl));
|
||||
SpecifyElmNamePrefix(M, NextMagmaString@(filts));
|
||||
return M;
|
||||
end;
|
||||
|
||||
|
||||
InstallGlobalFunction(LeftQuasigroupByMultiplicationTable,
|
||||
function(T)
|
||||
if not IsLeftQuasigroupTable(T) then
|
||||
Error("Multiplication table <T> must have each row a permutation of ",
|
||||
"the same entries.");
|
||||
fi;
|
||||
return LeftQuasigroupByMultiplicationTableNC(
|
||||
CanonicalCayleyTableOfLeftQuasigroupTable(T));
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftQuasigroupByMultiplicationTableNC,
|
||||
T -> FiniteMagmaCreator@(T, LeftQuasigroupNC, IsLeftQuotientElement)
|
||||
);
|
||||
|
||||
InstallGlobalFunction(RightQuasigroupByMultiplicationTable,
|
||||
function(T)
|
||||
if not IsRightQuasigroupTable(T) then
|
||||
Error("Multiplication table <T> must have each row a permutation of ",
|
||||
"the same entries.");
|
||||
fi;
|
||||
return RightQuasigroupByMultiplicationTableNC(CanonicalCayleyTable(T));
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuasigroupByMultiplicationTableNC,
|
||||
T -> FiniteMagmaCreator@(T, RightQuasigroupNC, IsRightQuotientElement)
|
||||
);
|
||||
|
||||
InstallGlobalFunction(LeftRackByMultiplicationTable,
|
||||
function(T)
|
||||
if not IsLeftQuasigroupTable(T) then
|
||||
Error("Multiplication table <T> must have each row a permutation of ",
|
||||
"the same entries.");
|
||||
fi;
|
||||
T := CanonicalCayleyTableOfLeftQuasigroupTable(T);
|
||||
if not IsLeftSelfDistributiveTable(T) then
|
||||
Error("Multiplication table <T> must be left self distributive.");
|
||||
fi;
|
||||
return LeftRackByMultiplicationTableNC(T);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftRackByMultiplicationTableNC,
|
||||
T -> FiniteMagmaCreator@(T, LeftRackNC,
|
||||
IsLeftQuotientElement and IsLSelfDistElement)
|
||||
);
|
||||
|
||||
InstallGlobalFunction(LeftQuandleByMultiplicationTable,
|
||||
function(T)
|
||||
if not IsLeftQuasigroupTable(T) then
|
||||
Error("Multiplication table <T> must have each row a permutation of ",
|
||||
"the same entries.");
|
||||
fi;
|
||||
T := CanonicalCayleyTableOfLeftQuasigroupTable(T);
|
||||
if not (IsLeftSelfDistributiveTable(T) and
|
||||
IsElementwiseIdempotentTable(T)) then
|
||||
Error("Multiplication table <T> must be left self-dist and idempotent.");
|
||||
fi;
|
||||
return LeftQuandleByMultiplicationTableNC(T);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftQuandleByMultiplicationTableNC,
|
||||
T -> FiniteMagmaCreator@(T, LeftQuandleNC,
|
||||
IsLeftQuotientElement and IsLSelfDistElement and IsIdempotent)
|
||||
);
|
||||
|
||||
InstallGlobalFunction(RightRackByMultiplicationTable,
|
||||
function(T)
|
||||
if not IsRightQuasigroupTable(T) then
|
||||
Error("Multiplication table <T> must have each column a permutation of ",
|
||||
"the same entries.");
|
||||
fi;
|
||||
T := CanonicalCayleyTable(T);
|
||||
if not IsRightSelfDistributiveTable(T) then
|
||||
Error("Multiplication table <T> must be right self distributive.");
|
||||
fi;
|
||||
return RightRackByMultiplicationTableNC(T);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightRackByMultiplicationTableNC,
|
||||
T -> FiniteMagmaCreator@(T, RightRackNC,
|
||||
IsRightQuotientElement and IsRSelfDistElement)
|
||||
);
|
||||
|
||||
InstallGlobalFunction(RightQuandleByMultiplicationTable,
|
||||
function(T)
|
||||
if not IsRightQuasigroupTable(T) then
|
||||
Error("Multiplication table <T> must have each column a permutation of ",
|
||||
"the same entries.");
|
||||
fi;
|
||||
T := CanonicalCayleyTable(T);
|
||||
if not (IsRightSelfDistributiveTable(T) and
|
||||
IsElementwiseIdempotentTable(T)) then
|
||||
Error("Multiplication table <T> must be right self-dist and idempotent.");
|
||||
fi;
|
||||
return RightQuandleByMultiplicationTableNC(T);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuandleByMultiplicationTableNC,
|
||||
T -> FiniteMagmaCreator@(T, RightQuandleNC,
|
||||
IsRightQuotientElement and IsRSelfDistElement and IsIdempotent)
|
||||
);
|
||||
|
||||
## Creators from permutations
|
||||
InstallGlobalFunction(LeftQuasigroupByPerms,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := LeftQuasigroupByMultiplicationTableNC(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)]));
|
||||
SetLeftPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftRackByPerms,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := LeftRackByMultiplicationTable(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)]));
|
||||
SetLeftPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftRackByPermsNC,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := LeftRackByMultiplicationTableNC(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)]));
|
||||
SetLeftPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftQuandleByPerms,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := LeftQuandleByMultiplicationTable(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)]));
|
||||
SetLeftPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftQuandleByPermsNC,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := LeftQuandleByMultiplicationTableNC(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)]));
|
||||
SetLeftPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuasigroupByPerms,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := RightQuasigroupByMultiplicationTableNC(
|
||||
TransposedMat(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)])));
|
||||
SetRightPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightRackByPerms,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := RightRackByMultiplicationTable(
|
||||
TransposedMat(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)])));
|
||||
SetRightPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightRackByPermsNC,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := RightRackByMultiplicationTableNC(
|
||||
TransposedMat(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)])));
|
||||
SetRightPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuandleByPerms,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := RightQuandleByMultiplicationTable(
|
||||
TransposedMat(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)])));
|
||||
SetRightPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuandleByPermsNC,
|
||||
function(perms)
|
||||
local Q;
|
||||
Q := RightQuandleByMultiplicationTableNC(
|
||||
TransposedMat(
|
||||
CayleyTableByPerms(perms, [1..Length(perms)])));
|
||||
SetRightPerms(Q, perms);
|
||||
return Q;
|
||||
end);
|
||||
|
||||
## And define the operations
|
||||
|
||||
InstallOtherMethod(LeftQuotient,
|
||||
"for two elts in magma by mult table, when left has left quotients",
|
||||
IsIdenticalObj,
|
||||
[IsLeftQuotientElement, IsMagmaByMultiplicationTableObj],
|
||||
function (l,r)
|
||||
local fam, ix;
|
||||
fam := FamilyObj(l);
|
||||
ix := LeftDivisionTable(fam)[l![1],r![1]];
|
||||
return fam!.set[ix];
|
||||
end);
|
||||
|
||||
InstallOtherMethod(\/,
|
||||
"for two elts in magma by mult table, when right has right quotients",
|
||||
IsIdenticalObj,
|
||||
[IsMagmaByMultiplicationTableObj, IsRightQuotientElement],
|
||||
function (l,r)
|
||||
local fam, ix;
|
||||
fam := FamilyObj(r);
|
||||
ix := RightDivisionTable(fam)[l![1],r![1]];
|
||||
return fam!.set[ix];
|
||||
end);
|
||||
|
||||
## Create division tables as needed
|
||||
InstallMethod(LeftDivisionTable,
|
||||
"for an object with a multiplication table",
|
||||
[HasMultiplicationTable],
|
||||
function(fam)
|
||||
local LS, n;
|
||||
LS := LeftPerms(fam);
|
||||
n := Size(LS);
|
||||
return List(LS, x->ListPerm(Inverse(x), n));
|
||||
end);
|
||||
|
||||
InstallMethod(RightDivisionTable,
|
||||
"for an object with a multiplication table",
|
||||
[HasMultiplicationTable],
|
||||
function (obj)
|
||||
local RS, n;
|
||||
RS := RightPerms(obj);
|
||||
n := Size(RS);
|
||||
return TransposedMat(List(RS, x->ListPerm(Inverse(x), n)));
|
||||
end);
|
||||
|
||||
## Create perm lists as needed
|
||||
InstallMethod(LeftPerms,
|
||||
"for an object with a multiplication table",
|
||||
[HasMultiplicationTable],
|
||||
function(fam)
|
||||
return List(MultiplicationTable(fam), x->PermList(x));
|
||||
end);
|
||||
|
||||
InstallMethod(RightPerms,
|
||||
"for an object with a muliplication table",
|
||||
[HasMultiplicationTable],
|
||||
function(fam)
|
||||
return List(TransposedMat(MultiplicationTable(fam)), x->PermList(x));
|
||||
end);
|
||||
|
||||
## Distributivity/idempotence checkers for when need be
|
||||
InstallMethod(IsLSelfDistributive, "for collections with multiplication tables",
|
||||
[IsMultiplicativeElementCollection and HasMultiplicationTable],
|
||||
M -> IsLeftSelfDistributiveTable(MultiplicationTable(M))
|
||||
);
|
||||
|
||||
InstallMethod(IsRSelfDistributive, "for collections with multiplication table",
|
||||
[IsMultiplicativeElementCollection and HasMultiplicationTable],
|
||||
M -> IsRightSelfDistributiveTable(MultiplicationTable(M))
|
||||
);
|
||||
|
||||
## Patch View/Print/Display for magma by mult objects
|
||||
InstallMethod(String, "for an element of magma by multiplication table",
|
||||
[IsMagmaByMultiplicationTableObj],
|
||||
function(obj)
|
||||
local fam;
|
||||
fam := FamilyObj(obj);
|
||||
if IsBound(fam!.elmNamePrefix) then
|
||||
return Concatenation(fam!.elmNamePrefix, String(obj![1]));
|
||||
fi;
|
||||
return Concatenation("m", String(obj![1]));
|
||||
end);
|
||||
|
||||
InstallMethod(ViewString, "for an element of magma by multiplication table",
|
||||
[IsMagmaByMultiplicationTableObj],
|
||||
obj -> String(obj));
|
||||
|
||||
InstallMethod(DisplayString, "for an element of magma by multiplication table",
|
||||
[IsMagmaByMultiplicationTableObj],
|
||||
obj -> String(obj));
|
||||
|
||||
InstallMethod(PrintObj, "for an element of magma by multiplication table",
|
||||
[IsMagmaByMultiplicationTableObj],
|
||||
function(obj) Print(String(obj)); end);
|
||||
|
||||
## Property of a collection that its elements know their multiplication table
|
||||
InstallImmediateMethod(IsBuiltFromMultiplicationTable,
|
||||
IsCollection, 1,
|
||||
C -> HasMultiplicationTable(ElementsFamily(FamilyObj(C)))
|
||||
);
|
||||
|
||||
## Special case the Opposite function from LOOPS package, since the opposite
|
||||
## of a left quasigroup is a right quasigroup and vice versa
|
||||
|
||||
# Is there a way to do this just once for each direction?
|
||||
InstallMethod(Opposite, "for left quasigroup",
|
||||
[ IsLeftQuasigroup and IsBuiltFromMultiplicationTable],
|
||||
L -> RightQuasigroupByMultiplicationTable(
|
||||
TransposedMat(MultiplicationTable(L))
|
||||
)
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for left rack",
|
||||
[ IsLeftRack and IsBuiltFromMultiplicationTable],
|
||||
L -> RightRackByMultiplicationTableNC(TransposedMat(MultiplicationTable(L)))
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for right quasigroup",
|
||||
[ IsRightQuasigroup and IsBuiltFromMultiplicationTable],
|
||||
L -> LeftQuasigroupByMultiplicationTable(
|
||||
TransposedMat(MultiplicationTable(L))
|
||||
)
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for right rack",
|
||||
[ IsRightRack and IsBuiltFromMultiplicationTable],
|
||||
L -> LeftRackByMultiplicationTableNC(TransposedMat(MultiplicationTable(L)))
|
||||
);
|
||||
|
||||
# Note that opposite of quandles seems to come for free from above, which is
|
||||
# good.
|
||||
|
||||
## Direct products
|
||||
# As in the case of general one-sided quasigroups, this implementation should
|
||||
# only be called with a second-argument collection which is not a group or
|
||||
# quasigroup.
|
||||
InstallOtherMethod(DirectProductOp,
|
||||
"for a list and non-quasigroup magma built from multiplication table",
|
||||
[IsList, IsMagma and IsBuiltFromMultiplicationTable],
|
||||
function (list, first)
|
||||
local n, item, i, jof, bigtable;
|
||||
n := Length(list);
|
||||
# Simple checks
|
||||
if n < 1 then
|
||||
Error("Usage: Cannot take DirectProduct of zero items.");
|
||||
elif n < 2 then
|
||||
return list[1];
|
||||
fi;
|
||||
# See if we can handle all objects
|
||||
for i in [2..n] do
|
||||
if not HasMultiplicationTable(list[i]) then
|
||||
return DirectProductOp(Permuted(list, (1,i)), list[i]);
|
||||
fi;
|
||||
od;
|
||||
# OK, safe to take everyone's multiplication table.
|
||||
# So go ahead and make the big thing
|
||||
bigtable := ProductTableOfCanonicalCayleyTables(
|
||||
List(list, MultiplicationTable));
|
||||
# But we have to figure out what to do with it.
|
||||
jof := RoughJoinOfFilters@(list, first);
|
||||
# Dispatch modeled after the general one
|
||||
if "IsMagmaWithOne" in jof then
|
||||
if "IsMonoid" in jof then
|
||||
return MonoidByMultiplicationTable(bigtable);
|
||||
fi;
|
||||
return MagmaWithOneByMultiplicationTable(bigtable);
|
||||
fi;
|
||||
if "IsAssociative" in jof then
|
||||
return SemigroupByMultiplicationTable(bigtable);
|
||||
elif "IsLeftQuasigroup" in jof then
|
||||
if "IsLSelfDistributive" in jof then
|
||||
if "IsElementwiseIdempotent" in jof then
|
||||
return LeftQuandleByMultiplicationTableNC(bigtable);
|
||||
fi;
|
||||
return LeftRackByMultiplicationTableNC(bigtable);
|
||||
fi;
|
||||
return LeftQuasigroupByMultiplicationTableNC(bigtable);
|
||||
elif "IsRightQuasigroup" in jof then
|
||||
if "IsRSelfDistributive" in jof then
|
||||
if "IsElementwiseIdempotent" in jof then
|
||||
return RightQuandleByMultiplicationTableNC(bigtable);
|
||||
fi;
|
||||
return RightRackByMultiplicationTableNC(bigtable);
|
||||
fi;
|
||||
return RightQuasigroupByMultiplicationTableNC(bigtable);
|
||||
fi;
|
||||
return MagmaByMultiplicationTable(bigtable);
|
||||
end);
|
11
lib/constructions.gd
Normal file
11
lib/constructions.gd
Normal file
|
@ -0,0 +1,11 @@
|
|||
## constructions.gd RAQ Other quandle constructions
|
||||
|
||||
## Trivial quandles
|
||||
|
||||
DeclareGlobalFunction("TrivialLeftQuandle");
|
||||
DeclareGlobalFunction("TrivialRightQuandle");
|
||||
|
||||
## Convenience functions
|
||||
|
||||
DeclareGlobalFunction("Quandle");
|
||||
DeclareGlobalFunction("Rack");
|
143
lib/constructions.gi
Normal file
143
lib/constructions.gi
Normal file
|
@ -0,0 +1,143 @@
|
|||
## constructions.gi RAQ Implementations of other quandle constructions
|
||||
|
||||
## Inert objects with which to build trivial quandles.
|
||||
DeclareCategory("IsLRInertum", IsMultiplicativeElement);
|
||||
DeclareCategory("IsLInertum", IsLRInertum and IsLeftQuotientElement);
|
||||
DeclareCategory("IsRInertum", IsLRInertum and IsRightQuotientElement);
|
||||
DeclareCategoryCollections("IsLInertum");
|
||||
DeclareCategoryCollections("IsRInertum");
|
||||
LInertumFamily@ := NewFamily("LeftInertFamily", IsLInertum,
|
||||
IsLSelfDistElement and IsIdempotent);
|
||||
SetMultiplicationTable(LInertumFamily@, []);
|
||||
RInertumFamily@ := NewFamily("RightInertFamily", IsRInertum,
|
||||
IsRSelfDistElement and IsIdempotent);
|
||||
SetMultiplicationTable(RInertumFamily@, []);
|
||||
LInertumType@ := NewType(LInertumFamily@,
|
||||
IsLInertum and IsPositionalObjectOneSlotRep);
|
||||
RInertumType@ := NewType(RInertumFamily@,
|
||||
IsRInertum and IsPositionalObjectOneSlotRep);
|
||||
LInertum@ := i -> Objectify(LInertumType@, [i]);
|
||||
RInertum@ := i -> Objectify(RInertumType@, [i]);
|
||||
|
||||
WhichI@ := obj -> obj![1];
|
||||
|
||||
InstallMethod(String, "for left inert objects",
|
||||
[IsLInertum],
|
||||
obj -> Concatenation("LInertum@raq(", String(WhichI@(obj)), ")")
|
||||
);
|
||||
InstallMethod(String, "for rightt inert objects",
|
||||
[IsRInertum],
|
||||
obj -> Concatenation("RInertum@raq(", String(WhichI@(obj)), ")")
|
||||
);
|
||||
|
||||
InstallMethod(ViewString, "for left inert objects",
|
||||
[IsLInertum],
|
||||
obj -> Concatenation("i", String(WhichI@(obj)), ">")
|
||||
);
|
||||
InstallMethod(ViewString, "for right inert objects",
|
||||
[IsRInertum],
|
||||
obj -> Concatenation("<i", String(WhichI@(obj)))
|
||||
);
|
||||
|
||||
InstallMethod(\=, "for two inert objects",
|
||||
IsIdenticalObj,
|
||||
[IsLRInertum, IsLRInertum],
|
||||
function(l,r) return l![1] = r![1]; end
|
||||
);
|
||||
|
||||
InstallMethod(\<, "for two inert objects",
|
||||
IsIdenticalObj,
|
||||
[IsLRInertum, IsLRInertum],
|
||||
function(l,r) return l![1] < r![1]; end
|
||||
);
|
||||
|
||||
InstallMethod(\*, "for a left inert object and anything",
|
||||
[IsLInertum, IsExtLElement],
|
||||
function(l,r) return r; end
|
||||
);
|
||||
|
||||
InstallMethod(\*, "for anything and a right inert object",
|
||||
[IsExtRElement, IsRInertum], ReturnFirst
|
||||
);
|
||||
|
||||
InstallOtherMethod(LeftQuotient, "for a left inert object and anything",
|
||||
[IsLInertum, IsExtLElement],
|
||||
function(l,r) return r; end
|
||||
);
|
||||
|
||||
InstallOtherMethod(\/, "for anything and a right inert object",
|
||||
[IsExtRElement, IsRInertum], ReturnFirst
|
||||
);
|
||||
|
||||
LeftTrivs@ := [];
|
||||
RightTrivs@ := [];
|
||||
|
||||
InstallGlobalFunction(TrivialLeftQuandle,
|
||||
function(n)
|
||||
local trivs, I;
|
||||
if not IsBound(LeftTrivs@[n]) then
|
||||
trivs := List([1..n], LInertum@);
|
||||
I := LeftQuandleNC(CollectionsFamily(LInertumFamily@), trivs);
|
||||
SetIsTrivial(I, true);
|
||||
SetAsSSortedList(I, trivs);
|
||||
SetMultiplicationTable(I, ListWithIdenticalEntries(n, [1..n]));
|
||||
LeftTrivs@[n] := I;
|
||||
fi;
|
||||
return LeftTrivs@[n];
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(TrivialRightQuandle,
|
||||
function(n)
|
||||
local trivs, I;
|
||||
if not IsBound(RightTrivs@[n]) then
|
||||
trivs := List([1..n], RInertum@);
|
||||
I := RightQuandleNC(CollectionsFamily(RInertumFamily@), trivs);
|
||||
SetIsTrivial(I, true);
|
||||
SetAsSSortedList(I, trivs);
|
||||
SetMultiplicationTable(I,
|
||||
List([1..n], i -> ListWithIdenticalEntries(n, i)));
|
||||
RightTrivs@[n] := I;
|
||||
fi;
|
||||
return RightTrivs@[n];
|
||||
end);
|
||||
|
||||
# The below needs to be immediate because otherwise it
|
||||
# is pre-empted by the immediate method that all collections of size
|
||||
# larger than one are non-trivial.
|
||||
InstallImmediateMethod(IsTrivial, "for a left quasigroup",
|
||||
IsLeftQuasigroup and HasSize, 1,
|
||||
function(Q)
|
||||
local gens;
|
||||
gens := GeneratorsOfLeftQuasigroup(Q);
|
||||
return ForAll(gens, x -> ForAll(gens, y -> x*y = y));
|
||||
end);
|
||||
|
||||
InstallImmediateMethod(IsTrivial, "for a right quasigroup",
|
||||
IsRightQuasigroup and HasSize, 1,
|
||||
function(Q)
|
||||
local gens;
|
||||
gens := GeneratorsOfRightQuasigroup(Q);
|
||||
return ForAll(gens, x -> ForAll(gens, y -> y*x = y));
|
||||
end);
|
||||
|
||||
## Convenience functions
|
||||
|
||||
InstallGlobalFunction(Quandle,
|
||||
function(arg)
|
||||
if Length(arg) = 0 then
|
||||
return LeftQuandleNC(CollectionsFamily(LInertumFamily@),[]);
|
||||
fi;
|
||||
if Length(arg) = 1 and IsInt(arg[1]) then
|
||||
return TrivialLeftQuandle(arg[1]);
|
||||
fi;
|
||||
## Will put more stuff here, but for now, just a convenient wrapper for
|
||||
## perms
|
||||
return LeftQuandleByPerms(arg);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(Rack,
|
||||
function(arg)
|
||||
## Will put more stuff here, but for now, just a convenient wrapper for
|
||||
## perms
|
||||
return LeftRackByPerms(arg);
|
||||
end);
|
99
lib/structure.gd
Normal file
99
lib/structure.gd
Normal file
|
@ -0,0 +1,99 @@
|
|||
## structure.gd RAQ Definitions, generation, and elementary ops and props.
|
||||
|
||||
## GAP Categories and representations
|
||||
|
||||
## Info class for RAQ
|
||||
DeclareInfoClass("InfoRAQ");
|
||||
|
||||
## Self-distributivity
|
||||
# Note these are properties that can and therefore should be defined just at
|
||||
# the level of MultiplicativeElements and Magmas, hence although the LOOPS
|
||||
# package defines IsLDistributive and IsRDistributive for quasigroups, they
|
||||
# would be ambiguous in the case of something like a semiring whose
|
||||
# multiplicative structure was a quasigroup
|
||||
# (cf. https://arxiv.org/abs/0910.4760). Hence, we implement them in RAQ with
|
||||
# new, non-conflicting terms.
|
||||
|
||||
# An element that knows that multiplication in its family is left
|
||||
# self-distributive:
|
||||
DeclareCategory("IsLSelfDistElement", IsMultiplicativeElement);
|
||||
DeclareCategoryCollections("IsLSelfDistElement");
|
||||
|
||||
# An element that knows that multiplication in its family is right
|
||||
# self-distributive:
|
||||
DeclareCategory("IsRSelfDistElement", IsMultiplicativeElement);
|
||||
DeclareCategoryCollections("IsRSelfDistElement");
|
||||
|
||||
# Left self-distributive collections of elements:
|
||||
DeclareProperty("IsLSelfDistributive", IsMultiplicativeElementCollection);
|
||||
InstallTrueMethod(IsLSelfDistributive, IsLSelfDistElementCollection);
|
||||
|
||||
# Right self-distributive collections of elements:
|
||||
DeclareProperty("IsRSelfDistributive", IsMultiplicativeElementCollection);
|
||||
InstallTrueMethod(IsRSelfDistributive, IsRSelfDistElementCollection);
|
||||
|
||||
## Idempotence
|
||||
# There is already a property IsIdempotent on elements, but to definw
|
||||
# structures which will automatically be quandles we need a corresponding
|
||||
# collections category:
|
||||
DeclareCategoryCollections("IsIdempotent");
|
||||
|
||||
# Collections in which every element is idempotent
|
||||
DeclareProperty("IsElementwiseIdempotent", IsMultiplicativeElementCollection);
|
||||
InstallTrueMethod(IsElementwiseIdempotent, IsIdempotentCollection);
|
||||
|
||||
## Left and right racks and quandles
|
||||
DeclareSynonym("IsLeftRack", IsLeftQuasigroup and IsLSelfDistributive);
|
||||
DeclareSynonym("IsRightRack", IsRightQuasigroup and IsRSelfDistributive);
|
||||
|
||||
DeclareSynonym("IsLeftQuandle", IsLeftRack and IsElementwiseIdempotent);
|
||||
DeclareSynonym("IsRightQuandle", IsRightRack and IsElementwiseIdempotent);
|
||||
|
||||
## One-sided quasigroups and racks and quandles by generators
|
||||
|
||||
# Returns the closure of <gens> under * and LeftQuotient;
|
||||
# the family of elements of M may be specified, and must be if <gens>
|
||||
# is empty (in which case M will be empty as well).
|
||||
DeclareGlobalFunction("LeftQuasigroup");
|
||||
DeclareGlobalFunction("LeftQuasigroupNC");
|
||||
DeclareGlobalFunction("RightQuasigroup");
|
||||
DeclareGlobalFunction("RightQuasigroupNC");
|
||||
DeclareGlobalFunction("LeftRack");
|
||||
DeclareGlobalFunction("LeftRackNC");
|
||||
DeclareGlobalFunction("RightRack");
|
||||
DeclareGlobalFunction("RightRackNC");
|
||||
DeclareGlobalFunction("LeftQuandle");
|
||||
DeclareGlobalFunction("LeftQuandleNC");
|
||||
DeclareGlobalFunction("RightQuandle");
|
||||
DeclareGlobalFunction("RightQuandleNC");
|
||||
|
||||
# Underlying operation
|
||||
DeclareGlobalFunction("CloneOfTypeByGenerators");
|
||||
|
||||
## Opposite structures
|
||||
DeclareCategory("IsOppositeObject", IsMultiplicativeElement);
|
||||
DeclareCategoryCollections("IsOppositeObject");
|
||||
DeclareAttribute("OppositeFamily", IsFamily);
|
||||
DeclareAttribute("OppositeType", IsFamily);
|
||||
DeclareSynonym("IsDefaultOppositeObject",
|
||||
IsOppositeObject and IsPositionalObjectOneSlotRep);
|
||||
DeclareAttribute("OppositeObj", IsMultiplicativeElement);
|
||||
DeclareAttribute("UnderlyingMultiplicativeElement", IsOppositeObject);
|
||||
|
||||
# Attributes for the generators
|
||||
|
||||
# Generates the structure by \* and LeftQuotient. Note that for finite
|
||||
# structures, these are the same as the GeneratorsOfMagma but in general more
|
||||
# elements might be required to generate the structure just under *
|
||||
DeclareAttribute("GeneratorsOfLeftQuasigroup", IsLeftQuasigroup);
|
||||
|
||||
# Generates the structure by \* and \/, same considerations as above
|
||||
DeclareAttribute("GeneratorsOfRightQuasigroup", IsRightQuasigroup);
|
||||
|
||||
## Conversions into quasigroup/rack/quandle
|
||||
DeclareAttribute("AsLeftQuasigroup", IsCollection);
|
||||
DeclareAttribute("AsLeftRack", IsCollection);
|
||||
DeclareAttribute("AsLeftQuandle", IsCollection);
|
||||
DeclareAttribute("AsRightQuasigroup", IsCollection);
|
||||
DeclareAttribute("AsRightRack", IsCollection);
|
||||
DeclareAttribute("AsRightQuandle", IsCollection);
|
715
lib/structure.gi
Normal file
715
lib/structure.gi
Normal file
|
@ -0,0 +1,715 @@
|
|||
## structure.gi RAQ Implementation of definitiions, reps, and elt operations
|
||||
|
||||
## Testing properties of collections the hard way if we have to
|
||||
|
||||
InstallMethod(IsElementwiseIdempotent, "for finite collections",
|
||||
[IsMultiplicativeElementCollection and IsFinite],
|
||||
M -> ForAll(Elements(M), m->IsIdempotent(m))
|
||||
);
|
||||
|
||||
InstallMethod(IsLSelfDistributive,
|
||||
"for arbitrary multiplicative collections, the hard way",
|
||||
[IsMultiplicativeElementCollection],
|
||||
function (C)
|
||||
local a,b,d;
|
||||
for a in C do for b in C do for d in C do
|
||||
if d*(a*b) <> (d*a)*(d*b) then return false; fi;
|
||||
od; od; od;
|
||||
return true;
|
||||
end);
|
||||
|
||||
InstallMethod(IsRSelfDistributive,
|
||||
"for arbitrary multiplicative collections, the hard way",
|
||||
[IsMultiplicativeElementCollection],
|
||||
function (C)
|
||||
local a,b,d;
|
||||
for a in C do for b in C do for d in C do
|
||||
if (a*b)*d <> (a*d)*(b*d) then return false; fi;
|
||||
od; od; od;
|
||||
return true;
|
||||
end);
|
||||
|
||||
## Create structures with generators
|
||||
|
||||
InstallMethod(GeneratorsOfMagma,
|
||||
"finite left quasigroups",
|
||||
[IsLeftQuasigroup and IsFinite],
|
||||
q -> GeneratorsOfLeftQuasigroup(q)
|
||||
);
|
||||
InstallMethod(GeneratorsOfMagma,
|
||||
"finite right quasigroups",
|
||||
[IsRightQuasigroup and IsFinite],
|
||||
q -> GeneratorsOfRightQuasigroup(q)
|
||||
);
|
||||
|
||||
InstallGlobalFunction(CloneOfTypeByGenerators,
|
||||
function(cat, fam, gens, genAttrib, tableCstr)
|
||||
local M, elf;
|
||||
if not(IsEmpty(gens) or IsIdenticalObj(FamilyObj(gens), fam)) then
|
||||
Error("<fam> and family of <gens> do not match");
|
||||
fi;
|
||||
M := Objectify(NewType( fam, cat and IsAttributeStoringRep), rec());
|
||||
Setter(genAttrib)(M, AsList(gens));
|
||||
SetConstructorFromTable(M, tableCstr);
|
||||
elf := ElementsFamily(fam);
|
||||
# Since there doesn't seem to be a way to make the IsFinite method based
|
||||
# on the family being finite into an immediate method:
|
||||
if HasIsFinite(elf) and IsFinite(elf) then SetIsFinite(M, true); fi;
|
||||
return M;
|
||||
end);
|
||||
|
||||
## Helpers for the constructors below:
|
||||
|
||||
ArgHelper@ := function(parmlist)
|
||||
local fam;
|
||||
# returns a list of the family and the list of elements of parmlist
|
||||
if Length(parmlist) = 0 then
|
||||
Error("usage: RAQ constructors take an optional family, followed by gens");
|
||||
fi;
|
||||
fam := fail;
|
||||
if IsFamily(parmlist[1]) then fam := Remove(parmlist,1); fi;
|
||||
if Length(parmlist) = 1 and IsList(parmlist[1])
|
||||
and not IsDirectProductElement(parmlist[1]) then
|
||||
parmlist := parmlist[1];
|
||||
fi;
|
||||
if fam = fail then fam := FamilyObj(parmlist); fi;
|
||||
return [fam, parmlist];
|
||||
end;
|
||||
|
||||
CheckLQGprop@ := function(gens)
|
||||
local g, h;
|
||||
# Make sure all elements in gens have left quotient property pairwise
|
||||
for g in gens do for h in gens do
|
||||
if g*LeftQuotient(g,h) <> h or LeftQuotient(g,g*h) <> h then
|
||||
Error("left quasigroup property of left quotients violated");
|
||||
fi;
|
||||
od; od;
|
||||
return;
|
||||
end;
|
||||
|
||||
CheckRQGprop@ := function(gens)
|
||||
local g, h;
|
||||
# Make sure all elements in gens have right quotient property pairwise
|
||||
for g in gens do for h in gens do
|
||||
if (h*g)/g <> h or (h/g)*g <> h then
|
||||
Error("right quasigroup property of / violated");
|
||||
fi;
|
||||
od; od;
|
||||
return;
|
||||
end;
|
||||
|
||||
## Functions for each of the magma categories here
|
||||
InstallGlobalFunction(LeftQuasigroup, function(arg)
|
||||
arg := ArgHelper@(arg);
|
||||
CheckLQGprop@(arg[2]);
|
||||
return LeftQuasigroupNC(arg[1], arg[2]);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftQuasigroupNC, function(fam, gens)
|
||||
return CloneOfTypeByGenerators(IsLeftQuasigroup, fam, gens,
|
||||
GeneratorsOfLeftQuasigroup,
|
||||
LeftQuasigroupByMultiplicationTableNC);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftRack, function(arg)
|
||||
arg := ArgHelper@(arg);
|
||||
CheckLQGprop@(arg[2]);
|
||||
if not IsLSelfDistributive(arg[2]) then
|
||||
Error("Left rack must have left distributive generators");
|
||||
fi;
|
||||
return LeftRackNC(arg[1], arg[2]);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftRackNC, function(fam, gens)
|
||||
return CloneOfTypeByGenerators(IsLeftRack, fam, gens,
|
||||
GeneratorsOfLeftQuasigroup,
|
||||
LeftRackByMultiplicationTableNC);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftQuandle, function(arg)
|
||||
arg := ArgHelper@(arg);
|
||||
CheckLQGprop@(arg[2]);
|
||||
if not IsLSelfDistributive(arg[2]) then
|
||||
Error("Left quandle must have left distributive generators");
|
||||
fi;
|
||||
if not IsElementwiseIdempotent(arg[2]) then
|
||||
Error("Quandles must contain only idempotent elements");
|
||||
fi;
|
||||
return LeftQuandleNC(arg[1], arg[2]);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(LeftQuandleNC, function(fam, gens)
|
||||
return CloneOfTypeByGenerators(IsLeftQuandle, fam, gens,
|
||||
GeneratorsOfLeftQuasigroup,
|
||||
LeftQuandleByMultiplicationTableNC);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuasigroup, function(arg)
|
||||
arg := ArgHelper@(arg);
|
||||
CheckRQGprop@(arg[2]);
|
||||
return RightQuasigroupNC(arg[1], arg[2]);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuasigroupNC, function(fam, gens)
|
||||
return CloneOfTypeByGenerators(IsRightQuasigroup, fam, gens,
|
||||
GeneratorsOfRightQuasigroup,
|
||||
RightQuasigroupByMultiplicationTableNC);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightRack, function(arg)
|
||||
arg := ArgHelper@(arg);
|
||||
CheckRQGprop@(arg[2]);
|
||||
if not IsRSelfDistributive(arg[2]) then
|
||||
Error("Right rack must have right distributive generators");
|
||||
fi;
|
||||
return RightRackNC(arg[1], arg[2]);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightRackNC, function(fam, gens)
|
||||
return CloneOfTypeByGenerators(IsRightRack, fam, gens,
|
||||
GeneratorsOfRightQuasigroup,
|
||||
RightRackByMultiplicationTableNC);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuandle, function(arg)
|
||||
arg := ArgHelper@(arg);
|
||||
CheckRQGprop@(arg[2]);
|
||||
if not IsRSelfDistributive(arg[2]) then
|
||||
Error("Right quandle must have right distributive generators");
|
||||
fi;
|
||||
if not IsElementwiseIdempotent(arg[2]) then
|
||||
Error("Quandles must contain only idempotent elements");
|
||||
fi;
|
||||
return RightQuandleNC(arg[1], arg[2]);
|
||||
end);
|
||||
|
||||
InstallGlobalFunction(RightQuandleNC, function(fam, gens)
|
||||
return CloneOfTypeByGenerators(IsRightQuandle, fam, gens,
|
||||
GeneratorsOfRightQuasigroup,
|
||||
RightQuandleByMultiplicationTableNC);
|
||||
end);
|
||||
|
||||
## NOTE: If it is the case that the magma generated by the
|
||||
## GeneratorsOf[Left|Right]Quasigroup is finite, then since the [left|right]
|
||||
## multiplication is injective, it is also surjective on that set and in fact
|
||||
## it is the whole quasigroup. However, it is not yet clear to me how best to
|
||||
## capture this fact in GAP in a computationally useful way.
|
||||
|
||||
## View and print and such
|
||||
LeftObjString@ := function(Q)
|
||||
# Don't test distributivity if we haven't already
|
||||
if HasIsLSelfDistributive(Q) and IsLeftRack(Q) then
|
||||
if HasIsElementwiseIdempotent(Q) and IsElementwiseIdempotent(Q) then
|
||||
return "LeftQuandle";
|
||||
fi;
|
||||
return "LeftRack";
|
||||
fi;
|
||||
return "LeftQuasigroup";
|
||||
end;
|
||||
|
||||
RightObjString@ := function(Q)
|
||||
# Don't test distributivity if we haven't already
|
||||
if HasIsRSelfDistributive(Q) and IsRightRack(Q) then
|
||||
if HasIsElementwiseIdempotent(Q) and IsElementwiseIdempotent(Q) then
|
||||
return "RightQuandle";
|
||||
fi;
|
||||
return "RightRack";
|
||||
fi;
|
||||
return "RightQuasigroup";
|
||||
end;
|
||||
|
||||
InstallMethod(String, "for a left quasigroup",
|
||||
[IsLeftQuasigroup],
|
||||
Q -> Concatenation(LeftObjString@(Q), "(...)"));
|
||||
|
||||
InstallMethod(String, "for a left quasigroup with generators",
|
||||
[IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup],
|
||||
Q -> Concatenation(LeftObjString@(Q), "( ",
|
||||
String(GeneratorsOfLeftQuasigroup(Q)), " )"));
|
||||
|
||||
InstallMethod(String, "for a left quasigroup with multiplication table",
|
||||
[IsLeftQuasigroup and HasMultiplicationTable],
|
||||
Q -> Concatenation(LeftObjString@(Q),
|
||||
"ByMultiplicationTableNC( ",
|
||||
String(MultiplicationTable(Q)), " )"));
|
||||
|
||||
InstallMethod(String, "for a right quasigroup",
|
||||
[IsRightQuasigroup],
|
||||
Q -> Concatenation(RightObjString@(Q), "(...)"));
|
||||
|
||||
InstallMethod(String, "for a right quasigroup with generators",
|
||||
[IsRightQuasigroup and HasGeneratorsOfRightQuasigroup],
|
||||
Q -> Concatenation(RightObjString@(Q), "( ",
|
||||
String(GeneratorsOfRightQuasigroup(Q)), " )"));
|
||||
|
||||
InstallMethod(String, "for a right quasigroup with multiplication table",
|
||||
[IsRightQuasigroup and HasMultiplicationTable],
|
||||
Q -> Concatenation(RightObjString@(Q),
|
||||
"ByMultiplicationTableNC( ",
|
||||
String(MultiplicationTable(Q)), " )"));
|
||||
|
||||
InstallMethod(PrintString, "for a left quasigroup",
|
||||
[IsLeftQuasigroup], Q -> String(Q));
|
||||
|
||||
InstallMethod(PrintString, "for a right quasigroup",
|
||||
[IsRightQuasigroup], Q -> String(Q));
|
||||
|
||||
InstallMethod(DisplayString, "for a left quasigroup",
|
||||
[IsLeftQuasigroup], Q -> String(Q));
|
||||
|
||||
InstallMethod(DisplayString, "for a right quasigroup",
|
||||
[IsRightQuasigroup], Q -> String(Q));
|
||||
|
||||
InstallMethod(Display, "for a left quasigroup with multiplication table",
|
||||
[IsLeftQuasigroup and HasMultiplicationTable],
|
||||
function(Q)
|
||||
Print(LeftObjString@(Q), " with ", Size(Q),
|
||||
" elements, generated by ",
|
||||
GeneratorsOfLeftQuasigroup(Q), ", with table\n");
|
||||
Display(MultiplicationTable(Q));
|
||||
end);
|
||||
|
||||
InstallMethod(Display, "for a right quasigroup with multiplication table",
|
||||
[IsRightQuasigroup and HasMultiplicationTable],
|
||||
function(Q)
|
||||
Print(RightObjString@(Q), " with ", Size(Q),
|
||||
" elements, generated by ",
|
||||
GeneratorsOfRightQuasigroup(Q), ", with table\n");
|
||||
Display(MultiplicationTable(Q));
|
||||
end);
|
||||
|
||||
LeftObjView@ := function(Q)
|
||||
# Don't test distributivity if we haven't already
|
||||
if HasIsLSelfDistributive(Q) and IsLeftRack(Q) then
|
||||
if HasIsElementwiseIdempotent(Q) and IsElementwiseIdempotent(Q) then
|
||||
return "<left quandle";
|
||||
fi;
|
||||
return "<left rack";
|
||||
fi;
|
||||
return "<left quasigroup";
|
||||
end;
|
||||
|
||||
RightObjView@ := function(Q)
|
||||
# Don't test distributivity if we haven't already
|
||||
if HasIsRSelfDistributive(Q) and IsRightRack(Q) then
|
||||
if HasIsElementwiseIdempotent(Q) and IsElementwiseIdempotent(Q) then
|
||||
return "<right quandle";
|
||||
fi;
|
||||
return "<right rack";
|
||||
fi;
|
||||
return "<right quasigroup";
|
||||
end;
|
||||
|
||||
InstallMethod(ViewString, "for a left quasigroup",
|
||||
[IsLeftQuasigroup],
|
||||
Q -> Concatenation(LeftObjView@(Q), ">"));
|
||||
|
||||
InstallMethod(ViewString, "for a left quasigroup with generators",
|
||||
[IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup],
|
||||
Q -> Concatenation(LeftObjView@(Q), " with ",
|
||||
String(Size(GeneratorsOfLeftQuasigroup(Q))),
|
||||
" generators>"));
|
||||
|
||||
InstallMethod(ViewString, "for a right quasigroup",
|
||||
[IsRightQuasigroup],
|
||||
Q -> Concatenation(RightObjView@(Q), ">"));
|
||||
|
||||
InstallMethod(ViewString, "for a right quasigroup with generators",
|
||||
[IsRightQuasigroup and HasGeneratorsOfRightQuasigroup],
|
||||
Q -> Concatenation(RightObjView@(Q), " with ",
|
||||
String(Size(GeneratorsOfRightQuasigroup(Q))),
|
||||
" generators>"));
|
||||
|
||||
## Opposite structures
|
||||
OFDir@ := NewDictionary("strings", true);
|
||||
# What property does the Opposite family have for each property of the
|
||||
# underlying elements? Hopefully we have gotten all of the relevant
|
||||
# properties. What we really want is the implied (and maybe the required)
|
||||
# properties of the family of the elements that we are taking the Opposite of,
|
||||
# but there is not any documented way of obtaining those.
|
||||
AddDictionary(OFDir@, "CanEasilyCompareElements", CanEasilyCompareElements);
|
||||
AddDictionary(OFDir@, "CanEasilySortElements", CanEasilySortElements);
|
||||
AddDictionary(OFDir@, "IsAssociativeElement", IsAssociativeElement);
|
||||
AddDictionary(OFDir@, "IsCommutativeElement", IsCommutativeElement);
|
||||
AddDictionary(OFDir@, "IsExtLElement", IsExtRElement);
|
||||
AddDictionary(OFDir@, "IsExtRElement", IsExtLElement);
|
||||
AddDictionary(OFDir@, "IsFiniteOrderElement", IsFiniteOrderElement);
|
||||
AddDictionary(OFDir@, "IsLeftQuotientElement", IsRightQuotientElement);
|
||||
AddDictionary(OFDir@, "IsLSelfDistElement", IsRSelfDistElement);
|
||||
AddDictionary(OFDir@, "IsMultiplicativeElementWithInverse",
|
||||
IsMultiplicativeElementWithInverse);
|
||||
AddDictionary(OFDir@, "IsMultiplicativeElementWithOne",
|
||||
IsMultiplicativeElementWithOne);
|
||||
AddDictionary(OFDir@, "IsRightQuotientElement", IsLeftQuotientElement);
|
||||
AddDictionary(OFDir@, "IsRSelfDistElement", IsLSelfDistElement);
|
||||
|
||||
InstallMethod(OppositeFamily, "for a family",
|
||||
[IsFamily],
|
||||
function(fam)
|
||||
local F, elt, elt_props, opp_props, prop, opp_filt, filt;
|
||||
elt := Representative(fam);
|
||||
elt_props := CategoriesOfObject(elt);
|
||||
Append(elt_props, KnownTruePropertiesOfObject(elt));
|
||||
opp_props := [];
|
||||
for prop in elt_props do
|
||||
if KnowsDictionary(OFDir@, prop) then
|
||||
Add(opp_props, LookupDictionary(OFDir@, prop));
|
||||
fi;
|
||||
od;
|
||||
opp_filt := IsMultiplicativeElement;
|
||||
for filt in opp_props do opp_filt := opp_filt and filt; od;
|
||||
return NewFamily(Concatenation("OppositeFamily(", fam!.NAME, ")"),
|
||||
IsOppositeObject, opp_filt);
|
||||
end);
|
||||
|
||||
InstallMethod(OppositeType, "for a family",
|
||||
[IsFamily],
|
||||
fam -> NewType(OppositeFamily(fam), IsDefaultOppositeObject)
|
||||
);
|
||||
|
||||
InstallMethod(OppositeObj, "for a multiplicative element",
|
||||
[IsMultiplicativeElement],
|
||||
function (obj)
|
||||
local fam;
|
||||
fam := FamilyObj(obj);
|
||||
SetRepresentative(fam, obj);
|
||||
return Objectify(OppositeType(fam), [Immutable(obj)]);
|
||||
end);
|
||||
|
||||
InstallMethod(OppositeObj, "for a commutative element",
|
||||
[IsMultiplicativeElement and IsCommutativeElement],
|
||||
IdFunc
|
||||
);
|
||||
|
||||
InstallMethod(UnderlyingMultiplicativeElement, "for a default opposite obj",
|
||||
[IsDefaultOppositeObject],
|
||||
obj -> obj![1]
|
||||
);
|
||||
|
||||
## Printing and viewing opposite objects
|
||||
InstallMethod(String, "for opposite objects",
|
||||
[IsDefaultOppositeObject],
|
||||
obj -> Concatenation("OppositeObj( ", String(obj![1]), " )")
|
||||
);
|
||||
|
||||
InstallMethod(ViewString, "for opposite objects",
|
||||
[IsDefaultOppositeObject],
|
||||
obj -> Concatenation("o", ViewString(obj![1]))
|
||||
);
|
||||
|
||||
InstallMethod(\=, "for two opposite objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultOppositeObject, IsDefaultOppositeObject],
|
||||
function(l,r) return l![1] = r![1]; end
|
||||
);
|
||||
|
||||
InstallMethod(\<, "for two opposite objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultOppositeObject, IsDefaultOppositeObject],
|
||||
function(l,r) return l![1] < r![1]; end
|
||||
);
|
||||
|
||||
InstallMethod(\*, "for two opposite objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultOppositeObject, IsDefaultOppositeObject],
|
||||
function(l,r) return OppositeObj(r![1]*l![1]); end
|
||||
);
|
||||
|
||||
InstallOtherMethod(LeftQuotient, "for two opposite objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultOppositeObject and IsLeftQuotientElement, IsDefaultOppositeObject],
|
||||
function(l,r) return OppositeObj(r![1]/l![1]); end
|
||||
);
|
||||
|
||||
InstallOtherMethod(\/, "for two opposite objects",
|
||||
IsIdenticalObj,
|
||||
[IsDefaultOppositeObject, IsDefaultOppositeObject and IsRightQuotientElement],
|
||||
function(l,r) return OppositeObj(LeftQuotient(r![1], l![1])); end
|
||||
);
|
||||
|
||||
InstallMethod(OneOp, "for an opposite object",
|
||||
[IsDefaultOppositeObject and IsMultiplicativeElementWithOne],
|
||||
ob -> OppositeObj(One(ob![1]))
|
||||
);
|
||||
|
||||
InstallMethod(InverseOp, "for an opposite object",
|
||||
[IsDefaultOppositeObject and IsMultiplicativeElementWithInverse],
|
||||
ob -> OppositeObj(Inverse(ob![1]))
|
||||
);
|
||||
|
||||
# Now the many Opposite implementations. How can we cut this down?
|
||||
|
||||
InstallMethod(Opposite, "for a commutative magma",
|
||||
[IsMagma and IsCommutative],
|
||||
IdFunc
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for a finitely generated magma",
|
||||
[IsMagma and HasGeneratorsOfMagma],
|
||||
function(M)
|
||||
local fam, elts;
|
||||
# Get elements first to prime the representative of the family
|
||||
elts := List(GeneratorsOfMagma(M), m -> OppositeObj(m));
|
||||
fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(M))));
|
||||
return Magma(fam, elts);
|
||||
end);
|
||||
|
||||
InstallMethod(Opposite, "for a finitely generated magma with one",
|
||||
[IsMagmaWithOne and HasGeneratorsOfMagmaWithOne],
|
||||
function(M)
|
||||
local fam, elts;
|
||||
# Get elements first to prime the representative of the family
|
||||
elts := List(GeneratorsOfMagmaWithOne(M), m -> OppositeObj(m));
|
||||
fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(M))));
|
||||
return MagmaWithOne(fam, elts);
|
||||
end);
|
||||
|
||||
InstallMethod(Opposite, "for a finitely generated magma with inverse",
|
||||
[IsMagmaWithInverses and HasGeneratorsOfMagmaWithInverses],
|
||||
function(M)
|
||||
local fam, elts;
|
||||
# Get elements first to prime the representative of the family
|
||||
elts := List(GeneratorsOfMagmaWithInverses(M), m -> OppositeObj(m));
|
||||
fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(M))));
|
||||
return MagmaWithInverses(fam, elts);
|
||||
end);
|
||||
|
||||
# Do we need Opposite() methods for Semigroups, Monoids, and Groups? Or have
|
||||
# we copied enough properties that those filters will simply carry over? The
|
||||
# couple of small tests I did the Opposite of a Group was again reported as a
|
||||
# Group, so let's leave those methods out for now, and move on to quasigroups,
|
||||
# racks, and quandles.
|
||||
|
||||
OppHelper@ := function(Q, whichgens, cnstr)
|
||||
local fam, elts, opp;
|
||||
elts := List(whichgens(Q), q -> OppositeObj(q));
|
||||
fam := CollectionsFamily(OppositeFamily(ElementsFamily(FamilyObj(Q))));
|
||||
opp := cnstr(fam, elts);
|
||||
if HasIsFinite(Q) then SetIsFinite(opp, IsFinite(Q)); fi;
|
||||
return opp;
|
||||
end;
|
||||
|
||||
|
||||
InstallMethod(Opposite, "for a left quasigroup",
|
||||
[IsLeftQuasigroup and HasGeneratorsOfLeftQuasigroup],
|
||||
Q -> OppHelper@(Q, GeneratorsOfLeftQuasigroup, RightQuasigroupNC)
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for a left rack",
|
||||
[IsLeftRack and HasGeneratorsOfLeftQuasigroup],
|
||||
Q -> OppHelper@(Q, GeneratorsOfLeftQuasigroup, RightRackNC)
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for a left quandle",
|
||||
[IsLeftQuandle and HasGeneratorsOfLeftQuasigroup],
|
||||
Q -> OppHelper@(Q, GeneratorsOfLeftQuasigroup, RightQuandleNC)
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for a right quasigroup",
|
||||
[IsRightQuasigroup and HasGeneratorsOfRightQuasigroup],
|
||||
Q -> OppHelper@(Q, GeneratorsOfRightQuasigroup, LeftQuasigroupNC)
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for a right rack",
|
||||
[IsRightRack and HasGeneratorsOfRightQuasigroup],
|
||||
Q -> OppHelper@(Q, GeneratorsOfRightQuasigroup, LeftRackNC)
|
||||
);
|
||||
|
||||
InstallMethod(Opposite, "for a right quandle",
|
||||
[IsRightQuandle and HasGeneratorsOfRightQuasigroup],
|
||||
Q -> OppHelper@(Q, GeneratorsOfRightQuasigroup, LeftQuandleNC)
|
||||
);
|
||||
|
||||
# Direct Products
|
||||
# We actually try to handle here the general cases that are not otherwise
|
||||
# handled by the groups functions in GAP as a whole, or the LOOPS package
|
||||
|
||||
# Helper for roughly creating the join of the filters of a bunch of objects.
|
||||
FiltersOfObj@ := function(obj)
|
||||
local f;
|
||||
f := CategoriesOfObject(obj);
|
||||
Append(f,KnownTruePropertiesOfObject(obj));
|
||||
Append(f,List(KnownAttributesOfObject(obj), x -> Concatenation("Has", x)));
|
||||
return f;
|
||||
end;
|
||||
|
||||
# From the implementation of DirectProductOp for groups and quasigroups, the
|
||||
# below will only be called with a second-argument collection which is not a
|
||||
# group or quasigroup.
|
||||
|
||||
# This is a helper that creates a rough join of the filters of a list of
|
||||
# objects, using the same arguments as directproduct op (the redundant "first"
|
||||
# argument, which is handy for seeding the filter list)
|
||||
RoughJoinOfFilters@ := function(list, first)
|
||||
local item, jof;
|
||||
jof := Set(FiltersOfObj@(first));
|
||||
for item in list{[2..Length(list)]} do
|
||||
IntersectSet(jof, FiltersOfObj@(item));
|
||||
od;
|
||||
return jof;
|
||||
end;
|
||||
|
||||
InstallOtherMethod(DirectProductOp, "for a list and non-quasigroup magma",
|
||||
[IsList, IsMagma],
|
||||
function (list, first)
|
||||
local item, i, jof, ids, gens, g, current, genfunc, genlists;
|
||||
# Simple checks
|
||||
if IsEmpty(list) then
|
||||
Error("Usage: Cannot take DirectProduct of zero items.");
|
||||
elif Length(list) = 1 then
|
||||
return list[1];
|
||||
fi;
|
||||
jof := RoughJoinOfFilters@(list, first);
|
||||
|
||||
# The dispatch below is somewhat ad-hoc, but covers the major existing cases
|
||||
|
||||
# First, if we don't have generators for all of the items, we're kinda out
|
||||
# of luck here:
|
||||
if not ForAny(jof, nm -> StartsWith(nm,"HasGenerators")) then
|
||||
TryNextMethod();
|
||||
fi;
|
||||
# Next, if anything is not even a magma, recommend cartesian product
|
||||
if not "IsMagma" in jof then
|
||||
Info(InfoRAQ, 1, "Try Cartesian() for products of collections.");
|
||||
TryNextMethod();
|
||||
fi;
|
||||
|
||||
# The primary division now is between entities with identities, for which
|
||||
# the product will have a smaller generating set, and those which do
|
||||
# not, for which the generators is the full cartesian product of generators.
|
||||
if "IsMagmaWithOne" in jof then
|
||||
# Code basically copied from DirectProductOp() for general groups;
|
||||
# would be better if this could be moved into the GAP core
|
||||
ids := List(list, One);
|
||||
gens := [];
|
||||
for i in [1..Length(list)] do
|
||||
for g in GeneratorsOfMagmaWithOne(list[i]) do
|
||||
current := ShallowCopy(ids);
|
||||
current[i] := g;
|
||||
Add(gens, DirectProductElement(current));
|
||||
od;
|
||||
od;
|
||||
# Now, we want the most specific structure we know about here.
|
||||
# Only aware of MagmaWithOne and Monoid, as direct products of
|
||||
# loops are already covered by the LOOPS package:
|
||||
if "IsMonoid" in jof then
|
||||
return MonoidByGenerators(gens);
|
||||
fi;
|
||||
return MagmaWithOneByGenerators(gens);
|
||||
fi;
|
||||
# OK, here we know not all structures have inverses. Therefore, we must
|
||||
# resort to the full cartesian product of generators.
|
||||
# But we need to figure out what function to use to obtain the generators.
|
||||
if "HasGeneratorsOfLeftQuasigroup" in jof then
|
||||
genfunc := GeneratorsOfLeftQuasigroup;
|
||||
elif "HasGeneratorsOfRightQuasigroup" in jof then
|
||||
genfunc := GeneratorsOfRightQuasigroup;
|
||||
elif "HasGeneratorsOfMagma" in jof then
|
||||
genfunc := GeneratorsOfMagma;
|
||||
elif "HasGeneratorsOfDomain" in jof then
|
||||
genfunc := GeneratorsOfDomain;
|
||||
else
|
||||
Info(InfoRAQ,1, "RAQ: Unusual product, each of ", list,
|
||||
" has generators, but not sure what kind; trying next method.");
|
||||
TryNextMethod();
|
||||
fi;
|
||||
|
||||
genlists := List(list, genfunc);
|
||||
gens := Cartesian(genlists);
|
||||
Apply(gens, DirectProductElement);
|
||||
# Again, we need to figure out what sort of structure we might have
|
||||
if "IsAssociative" in jof then
|
||||
return SemigroupByGenerators(gens);
|
||||
elif "IsLeftQuasigroup" in jof then
|
||||
if "IsLSelfDistributive" in jof then
|
||||
if "IsElementwiseIdempotent" in jof then
|
||||
return LeftQuandleNC(FamilyObj(gens), gens);
|
||||
fi;
|
||||
return LeftRackNC(FamilyObj(gens), gens);
|
||||
fi;
|
||||
return LeftQuasigroupNC(FamilyObj(gens), gens);
|
||||
elif "IsRightQuasigroup" in jof then
|
||||
if "IsRSelfDistributive" in jof then
|
||||
if "IsElementwiseIdempotent" in jof then
|
||||
return RightQuandleNC(FamilyObj(gens), gens);
|
||||
fi;
|
||||
return RightRackNC(FamilyObj(gens), gens);
|
||||
fi;
|
||||
return RightQuasigroupNC(FamilyObj(gens), gens);
|
||||
fi;
|
||||
# Not seeing any additional structure; did I miss anything?
|
||||
return MagmaByGenerators(gens);
|
||||
end);
|
||||
|
||||
# Make sure that we can take quotients properly in the direct product
|
||||
InstallOtherMethod(LeftQuotient,
|
||||
IsIdenticalObj,
|
||||
[IsDirectProductElement, IsDirectProductElement],
|
||||
function (l,r)
|
||||
local i, comps;
|
||||
comps := [];
|
||||
for i in [1..Length(l)] do
|
||||
Add(comps, LeftQuotient(l![i],r![i]));
|
||||
od;
|
||||
return DirectProductElement(comps);
|
||||
end);
|
||||
|
||||
InstallOtherMethod(\/,
|
||||
IsIdenticalObj,
|
||||
[IsDirectProductElement, IsDirectProductElement],
|
||||
function (l,r)
|
||||
local i, comps;
|
||||
comps := [];
|
||||
for i in [1..Length(l)] do
|
||||
Add(comps, l![i] / r![i]);
|
||||
od;
|
||||
return DirectProductElement(comps);
|
||||
end);
|
||||
|
||||
|
||||
|
||||
## Coversions among the structure types
|
||||
InstallMethod(AsLeftQuasigroup, "for a left quasigroup",
|
||||
[IsLeftQuasigroup], IdFunc);
|
||||
InstallMethod(AsLeftRack, "for a left rack",
|
||||
[IsLeftRack], IdFunc);
|
||||
InstallMethod(AsLeftQuandle, "for a left quandle",
|
||||
[IsLeftQuandle], IdFunc);
|
||||
InstallMethod(AsRightQuasigroup, "for a right quasigroup",
|
||||
[IsRightQuasigroup], IdFunc);
|
||||
InstallMethod(AsRightRack, "for a right rack",
|
||||
[IsRightRack], IdFunc);
|
||||
InstallMethod(AsRightQuandle, "for a left quandle",
|
||||
[IsLeftQuandle], IdFunc);
|
||||
|
||||
AsAStructure@ := function(struc, D)
|
||||
local T,S;
|
||||
D := AsSSortedList(D);
|
||||
# Check if D is closed under *
|
||||
T := MultiplicationTable(D);
|
||||
if ForAny(T, l -> fail in l) then
|
||||
return fail;
|
||||
fi;
|
||||
# it is, so check if it satisfies the properties of struc
|
||||
S := struc(D);
|
||||
# OK, good, so we are there, just record what we know
|
||||
SetAsSSortedList(S, D);
|
||||
SetMultiplicationTable(S, T);
|
||||
SetIsFinite(S, true);
|
||||
SetSize(S, Length(D));
|
||||
return S;
|
||||
end;
|
||||
|
||||
InstallMethod(AsLeftQuasigroup, "for an arbitrary collection",
|
||||
[IsCollection], D -> AsAStructure@(LeftQuasigroup, D));
|
||||
InstallMethod(AsLeftRack, "for an arbitrary collection",
|
||||
[IsCollection], D -> AsAStructure@(LeftRack, D));
|
||||
InstallMethod(AsLeftQuandle, "for an arbitrary collection",
|
||||
[IsCollection], D -> AsAStructure@(LeftQuandle, D));
|
||||
|
||||
InstallMethod(AsRightQuasigroup, "for an arbitrary collection",
|
||||
[IsCollection], D -> AsAStructure@(RightQuasigroup, D));
|
||||
InstallMethod(AsRightRack, "for an arbitrary collection",
|
||||
[IsCollection], D -> AsAStructure@(RightRack, D));
|
||||
InstallMethod(AsRightQuandle, "for an arbitrary collection",
|
||||
[IsCollection], D -> AsAStructure@(RightQuandle, D));
|
Loading…
Add table
Add a link
Reference in a new issue