RAQ/lib/bytable.gi
Glen Whitney be82518697 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
2018-08-16 21:12:38 -07:00

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);