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

View File

@ -732,98 +732,104 @@ end);
# groups in GAP. The idea is as follows: # groups in GAP. The idea is as follows:
# We want to calculate direct product of quasigroups, loops and groups. # We want to calculate direct product of quasigroups, loops and groups.
# If only groups are on the list, standard GAP DirectProduct will take care # 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, # of it. If there are also some quasigroups or loops on the list (but nothing
# we must take care of it. # that is not a quasigroup), we must take care of it.
# However, we do not know if such a list will be processed with # However, we do not know if such a list will be processed with
# DirectProductOp( <IsList>, <IsGroup> ), or # DirectProductOp( <IsList>, <IsGroup> ), or
# DirectProductOp( <IsList>, <IsQuasigroup> ), # DirectProductOp( <IsList>, <IsQuasigroup> ),
# since this depends on which algebra is listed first. # 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], [ IsList, IsGroup],
function( list, first ) function( list, first )
local L, p; local L, p;
# Check the arguments. # Check the arguments.
if IsEmpty( list ) then Error( "LOOPS: <1> must be nonempty." ); fi; if IsEmpty( list ) then
if not ForAny( list, IsQuasigroup ) then Error( "LOOPS: <1> must be nonempty." );
# there are no quasigroups or loops on the list elif Length(list) = 1 then
TryNextMethod(); return list[1];
fi; fi;
if ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then for p in [1..Length(list)] do
# there are other objects beside groups, loops and quasigroups on the list if not IsGroup(list[p]) then
TryNextMethod(); return DirectProductOp(Permuted(list, (1,p)), list[p]);
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;
fi; fi;
od; 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); end);
InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsQuasigroup> )", InstallOtherMethod( DirectProductOp, "for DirectProduct( <IsList>, <IsQuasigroup> )",
[ IsList, IsQuasigroup ], [ IsList, IsQuasigroup ],
function( list, dummy ) function( list, dummy )
local group_list, quasigroup_list, group_product, are_all_loops, local group_list, quasigroup_list, group_product, are_all_loops, n, i, T;
n, i, nL, nM, TL, TM, T, j, k, s;
# check the arguments # check the arguments
if IsEmpty( list ) then if IsEmpty( list ) then
Error( "LOOPS: <1> must be nonempty." ); Error( "LOOPS: <1> must be nonempty." );
elif ForAny( list, G -> (not IsGroup( G )) and (not IsQuasigroup( G ) ) ) then elif Length(list) = 1 then
TryNextMethod(); return list(1);
fi; 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 # only groups, quasigroups and loops are on the list, with at least one
group_list := Filtered( list, G -> IsGroup( G ) ); # non-group; moreover, we have partitioned the list into groups and
quasigroup_list := Filtered( list, G -> IsQuasigroup( G ) ); # non-groups and checked whether all quasigroups are really loops.
if not IsEmpty( group_list ) then # some groups are on the list if not IsEmpty( group_list ) then # some groups are on the list
group_product := DirectProductOp( group_list, group_list[ 1 ] ); group_product := DirectProductOp( group_list, group_list[ 1 ] );
Add( quasigroup_list, IntoLoop( group_product ) ); Add( quasigroup_list, IntoLoop( group_product ) );
fi; 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 ); n := Length( quasigroup_list );
if n=1 then # We willl not use recursion; start by making all Cayley tables canonical
return quasigroup_list[ 1 ]; Apply(quasigroup_list,
fi; Q -> CanonicalCayleyTableOfLeftQuasigroupTable( CayleyTable( Q ) ) );
# at least 2 quasigroups and loops; we will not use recursion T := ProductTableOfCanonicalCayleyTables( quasigroup_list );
# 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->[] );
# 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 if are_all_loops then
return LoopByCayleyTable( quasigroup_list[1] ); return LoopByCayleyTable( T );
fi; fi;
return QuasigroupByCayleyTable( quasigroup_list[1] ); return QuasigroupByCayleyTable( T );
end ); end );
############################################################################# #############################################################################