RAQ, a GAP System package for Racks And Quandles.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

456 lines
14 KiB

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