factor out parts of direct product computation that can be reused for racks

This commit is contained in:
Glen Whitney 2017-10-25 16:18:04 +02:00
parent 68390c3869
commit 1493a9c480
2 changed files with 71 additions and 70 deletions

View File

@ -44,14 +44,14 @@ InstallTrueMethod(IsLeftQuotientElement, IsMultiplicativeElementWithInverse);
## DeclareOperation("LeftQuotient", [IsLeftQuotientElement,IsExtLElement]);
## element of a quasigroup
DeclareCategory( "IsQuasigroupElement",
IsMultiplicativeElement and
IsLeftQuotientElement and IsRightQuotientElement );
DeclareSynonym( "IsQuasigroupElement",
IsMultiplicativeElement and
IsLeftQuotientElement and IsRightQuotientElement );
DeclareRepresentation( "IsQuasigroupElmRep",
IsPositionalObjectRep and IsMultiplicativeElement, [1] );
## element of a loop
DeclareCategory( "IsLoopElement",
DeclareSynonym( "IsLoopElement",
IsQuasigroupElement and IsMultiplicativeElementWithInverse );
DeclareRepresentation( "IsLoopElmRep",
IsPositionalObjectRep and IsMultiplicativeElementWithInverse, [1] );
@ -64,17 +64,11 @@ DeclareCategory("IsRightQuasigroup",
DeclareCategory("IsLeftQuasigroup",
IsMagma and IsLeftQuotientElementCollection);
## latin (auxiliary category for GAP to tell apart IsMagma and IsQuasigroup)
DeclareSynonym( "IsLatin",
IsRightQuotientElementCollection and
IsLeftQuotientElementCollection );
## quasigroup
DeclareCategory( "IsQuasigroup", IsMagma and IsLatin );
DeclareSynonym( "IsQuasigroup", IsRightQuasigroup and IsLeftQuasigroup );
## loop
DeclareCategory( "IsLoop", IsQuasigroup and IsMagmaWithOne and
DeclareSynonym( "IsLoop", IsQuasigroup and IsMagmaWithOne and
IsMultiplicativeElementWithInverseCollection);
#############################################################################
@ -139,6 +133,7 @@ DeclareOperation( "IntoGroup", [ IsMagma ] );
## PRODUCTS OF QUASIGROUPS AND LOOPS
## --------------------------------------------------------------------------
DeclareGlobalFuntion("ProductTableOfCanonicalCayleyTables");
#DirectProduct already declared for groups.
#############################################################################

View File

@ -732,98 +732,104 @@ end);
# groups in GAP. The idea is as follows:
# We want to calculate direct product of quasigroups, loops and groups.
# If only groups are on the list, standard GAP DirectProduct will take care
# of it. If there are also some quasigroups or loops on the list,
# we must take care of it.
# of it. If there are also some quasigroups or loops on the list (but nothing
# that is not a quasigroup), we must take care of it.
# However, we do not know if such a list will be processed with
# DirectProductOp( <IsList>, <IsGroup> ), or
# DirectProductOp( <IsList>, <IsQuasigroup> ),
# since this depends on which algebra is listed first.
# We therefore take care of both situations.
# Call the item in the second argument of DirectProductOp the "distinguished"
# item. To produce the correct result whichever of the two above cases for
# DirectProductOp ends up being called, we add a method in the first case
# which repeats the product call with the first non-group it encounters (if
# any) as the distinguished item. Further, the method for the
# latter case must itself repeat the call with a similar reordering if it
# finds anyitem that is not a quasigroup.
InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsGroup> )",
InstallMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsGroup> )",
[ IsList, IsGroup],
function( list, first )
local L, p;
# Check the arguments.
if IsEmpty( list ) then Error( "LOOPS: <1> must be nonempty." ); fi;
if not ForAny( list, IsQuasigroup ) then
# there are no quasigroups or loops on the list
TryNextMethod();
if IsEmpty( list ) then
Error( "LOOPS: <1> must be nonempty." );
elif Length(list) = 1 then
return list[1];
fi;
if ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then
# there are other objects beside groups, loops and quasigroups on the list
TryNextMethod();
fi;
# all arguments are groups, quasigroups or loops, and there is at least one loop
# making sure that a loop is listed first so that this method is not called again
for L in list do
if not IsGroup( L ) then
p := Position( list, L );
list[ 1 ] := L;
list[ p ] := first;
break;
for p in [1..Length(list)] do
if not IsGroup(list[p]) then
return DirectProductOp(Permuted(list, (1,p)), list[p]);
fi;
od;
# OK, everything is a group, so let the rest of GAP do the work.
TryNextMethod();
end);
return DirectProductOp( list, list[ 1 ] );
InstallGlobalFunction(ProductTableOfCanonicalCayleyTables,
function(tablist)
local n, i, nL, nM, TL, TM, T, j, k, s;
TL := tablist[1];
for s in [2..n] do
TM := tablist[ s ];
nL := Length( TL); nM := Length( TM );
T := List( [1..nL*nM], j->[] );
# not efficient, but it does the job
for i in [1..nM] do for j in [1..nM] do for k in [1..nL] do
Append( T[ (i-1)*nL + k ], TL[ k ] + nL*(TM[i][j]-1) );
od; od; od;
TL := T;
od;
return TL;
end);
InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsQuasigroup> )",
[ IsList, IsQuasigroup ],
function( list, dummy )
local group_list, quasigroup_list, group_product, are_all_loops,
n, i, nL, nM, TL, TM, T, j, k, s;
local group_list, quasigroup_list, group_product, are_all_loops, n, i, T;
# check the arguments
if IsEmpty( list ) then
Error( "LOOPS: <1> must be nonempty." );
elif ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then
TryNextMethod();
elif Length(list) = 1 then
return list(1);
fi;
group_list := [];
quasigroup_list := list{[1]};
are_all_loops := true;
for i in [2..Length(list)] do
if IsGroup(list[i]) then
Add(group_list, list[i]);
elif IsQuasigroup(list[i]) then
Add(quasigroup_list, list[i]);
if are_all_loops and not IsLoop(list[i]) then
are_all_loops := false;
fi;
else # Oops, something that's neither a group or quasigroup
return DirectProductOp(Permuted(list,(1.i)), list[i]);
fi;
od;
# only groups, quasigroups and loops are on the list, with at least one non-group
group_list := Filtered( list, G -> IsGroup( G ) );
quasigroup_list := Filtered( list, G -> IsQuasigroup( G ) );
# only groups, quasigroups and loops are on the list, with at least one
# non-group; moreover, we have partitioned the list into groups and
# non-groups and checked whether all quasigroups are really loops.
if not IsEmpty( group_list ) then # some groups are on the list
group_product := DirectProductOp( group_list, group_list[ 1 ] );
Add( quasigroup_list, IntoLoop( group_product ) );
fi;
# keeping track of whether all algebras are in fact loops
are_all_loops := ForAll( quasigroup_list, IsLoop );
# now only quasigroups and loops are on the list
# now only quasigroups and loops are on the list, with at least 2 of them
n := Length( quasigroup_list );
if n=1 then
return quasigroup_list[ 1 ];
fi;
# at least 2 quasigroups and loops; we will not use recursion
# making all Cayley tables canonical
for s in [1..n] do
quasigroup_list[ s ] :=
CanonicalCayleyTableOfLeftQuasigroupTable(
CayleyTable( quasigroup_list[ s ] ) );
od;
# At this point the entries in quasigroup_list are really just tables
for s in [2..n] do
nL := Length( quasigroup_list[ 1 ] );
nM := Length( quasigroup_list[ s ] );
TL := quasigroup_list[ 1 ];
TM := quasigroup_list[ s ];
T := List( [1..nL*nM], j->[] );
# We willl not use recursion; start by making all Cayley tables canonical
Apply(quasigroup_list,
Q -> CanonicalCayleyTableOfLeftQuasigroupTable( CayleyTable( Q ) ) );
T := ProductTableOfCanonicalCayleyTables( quasigroup_list );
# not efficient, but it does the job
for i in [1..nM] do for j in [1..nM] do for k in [1..nL] do
Append( T[ (i-1)*nL + k ], TL[ k ] + nL*(TM[i][j]-1) );
od; od; od;
quasigroup_list[ 1 ] := T;
od;
if are_all_loops then
return LoopByCayleyTable( quasigroup_list[1] );
return LoopByCayleyTable( T );
fi;
return QuasigroupByCayleyTable( quasigroup_list[1] );
return QuasigroupByCayleyTable( T );
end );
#############################################################################