be82518697
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
457 lines
14 KiB
Plaintext
457 lines
14 KiB
Plaintext
## 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);
|