Rudimentary support for left/right quasigroups.

This commit is contained in:
Glen Whitney 2017-10-17 21:42:47 +02:00
parent 7e8b3b5562
commit 722f25e51f
2 changed files with 118 additions and 28 deletions

View File

@ -12,8 +12,40 @@
## GAP CATEGORIES AND REPRESENTATIONS
## -------------------------------------------------------------------------
## Categories convenient for defining quasigroups
## element which is an admissible argument for the right argument of /
DeclareCategory( "IsRightQuotientElement", IsExtLElement);
DeclareCategoryCollections("IsRightQuotientElement");
DeclareCategoryCollections("IsRightQuotientElementCollection");
## Every element with an inverse can form right quotients
## (in fact, in some sense it might be enough to have just a left inverse,
## but there doesn't seem to be any benefit to delving to that level of
## detail at this point.)
InstallTrueMethod(IsRightQuotientElement, IsMultiplicativeElementWithInverse);
## Now what we would like to do is re-declare
## DeclareOperation( "/", [IsExtRElement, IsRightQuotientElement] );
## but we can't since "/" is in the kernel, so we will have to content
## ourselves with InstallOtherMethod() calls on /. (I am not actually sure what
## the practical upshot of that is, i.e. if it has any shortcomings as compared
## to if we could declare "/" more generally.)
## Element which is admissible for the left argument of LeftQuotient()
DeclareCategory( "IsLeftQuotientElement", IsExtRElement);
DeclareCategoryCollections("IsLeftQuotientElement");
DeclareCategoryCollections("IsLeftQuotientElementCollection");
## Every element with an inverse can form left quotients
InstallTrueMethod(IsLeftQuotientElement, IsMultiplicativeElementWithInverse);
## Again, ideally (in some sense) we'd like to redeclare
## DeclareOperation("LeftQuotient", [IsLeftQuotientElement,IsExtLElement]);
## element of a quasigroup
DeclareCategory( "IsQuasigroupElement", IsMultiplicativeElement );
DeclareCategory( "IsQuasigroupElement",
IsLeftQuotientElement and IsRightQuotientElement );
DeclareRepresentation( "IsQuasigroupElmRep",
IsPositionalObjectRep and IsMultiplicativeElement, [1] );
@ -23,23 +55,39 @@ DeclareCategory( "IsLoopElement",
DeclareRepresentation( "IsLoopElmRep",
IsPositionalObjectRep and IsMultiplicativeElementWithInverse, [1] );
## Right quasigroup
DeclareCategory("IsRightQuasigroup",
IsMagma and IsRightQuotientElementCollection);
## Left quasigroup
DeclareCategory("IsLeftQuasigroup",
IsMagma and IsLeftQuotientElementCollection);
## latin (auxiliary category for GAP to tell apart IsMagma and IsQuasigroup)
DeclareCategory( "IsLatin", IsObject );
DeclareSynonym( "IsLatin",
IsRightQuotientElementCollection and
IsLeftQuotientElementCollection );
## quasigroup
DeclareCategory( "IsQuasigroup", IsMagma and IsLatin );
## loop
DeclareCategory( "IsLoop", IsQuasigroup and IsMultiplicativeElementWithInverseCollection);
DeclareCategory( "IsLoop", IsQuasigroup and IsMagmaWithOne and
IsMultiplicativeElementWithInverseCollection);
#############################################################################
## TESTING MULTIPLICATION TABLES
## -------------------------------------------------------------------------
DeclareOperation( "IsQuasigroupTable", [ IsMatrix ] );
DeclareProperty( "IsLeftQuasigroupTable", [ IsMatrix ]);
DeclareProperty( "IsRightQuasigroupTable", [ IsMatrix ]);
DeclareSynonym( "IsQuasigroupTable",
IsLeftQuasigroupTable and IsRightQuasigroupTable );
DeclareSynonym( "IsQuasigroupCayleyTable", IsQuasigroupTable );
DeclareOperation( "IsLoopTable", [ IsMatrix ] );
DeclareProperty( "IsLoopTable", [ IsMatrix ] );
DeclareSynonym( "IsLoopCayleyTable", IsLoopTable );
DeclareGlobalFunction("CanonicalCayleyTableOfLeftQuasigroupTable");
DeclareOperation( "CanonicalCayleyTable", [ IsMatrix ] );
DeclareOperation( "NormalizedQuasigroupTable", [ IsMatrix ] );
@ -52,7 +100,7 @@ DeclareOperation( "QuasigroupByCayleyTable", [ IsMatrix ] );
DeclareOperation( "LoopByCayleyTable", [ IsMatrix ] );
DeclareOperation( "SetQuasigroupElmName", [ IsQuasigroup, IsString ] );
DeclareSynonym( "SetLoopElmName", SetQuasigroupElmName );
DeclareOperation( "CanonicalCopy", [ IsQuasigroup ] );
DeclareOperation( "CanonicalCopy", [ IsMagma ] );
#############################################################################
## CREATING QUASIGROUPS AND LOOPS FROM A FILE

View File

@ -14,12 +14,12 @@
#############################################################################
##
#O IsQuasigroupTable( ls )
#O IsLeftQuasigroupTable( ls )
##
## Returns true if <ls> is an n by n latin square with n distinct
## integral entries.
## Returns true if <ls> is an n by n matrix with n distinct
## integral entries, each occurring exactly once in each row
InstallMethod( IsQuasigroupTable, "for matrix",
InstallMethod( IsLeftQuasigroupTable, "for matrix",
[ IsMatrix ],
function( ls )
local first_row;
@ -27,14 +27,21 @@ function( ls )
first_row := Set( ls[ 1 ] );
if not Length( first_row ) = Length( ls[ 1 ] ) then return false; fi;
if ForAll( ls, row -> Set( row ) = first_row ) = false then return false; fi;
# checking columns
ls := TransposedMat( ls );
first_row := Set( ls[ 1 ] );
if not Length( first_row ) = Length( ls[ 1 ] ) then return false; fi;
if ForAll( ls, row -> Set( row ) = first_row ) = false then return false; fi;
return true;
end );
#############################################################################
##
#O IsRighttQuasigroupTable( ls )
##
## Returns true if <ls> is an n by n matrix with n distinct
## integral entries, each occurring exactly once in each column
InstallMethod( IsRighttQuasigroupTable, "for matrix",
[ IsMatrix ],
mat -> IsLeftQuasigroupTable(TransposedMat(mat))
);
#############################################################################
##
#O IsLoopTable( ls )
@ -51,6 +58,36 @@ function( ls )
and ls[ 1 ] = List( [1..Length(ls)], i -> ls[ i ][ 1 ] );
end );
#############################################################################
##
#O CanonicalCayleyTableOfLeftQuasigroupTable( ls )
##
## Returns a Cayley table isomorphic to <ls>, which must already be known
## to be a left quasigroup table, in which the entries of ls
## have been replaced by numerical values 1, ..., n in the following way:
## Let e_1 < ... < e_n be all distinct entries of ls. Then e_i is renamed
## to i. In particular, when {e_1,...e_n} = {1,...,n}, the operation
## does nothing.
InstallGlobalFunction( CanonicalCayleyTableOfLeftQuasigroupTable,
function( ls )
local n, entries, i, j, T;
n := Length( ls );
# finding all distinct entries in the table
entries := [];
for i in ls[1] do
AddSet( entries, i );
od;
if entries = [1..n] then return List(ls, l -> ShallowCopy(l));
fi;
# renaming the entries and making a mutable copy, too
T := List( [1..n], i -> [1..n] );
for i in [1..n] do for j in [1..n] do
T[ i ][ j ] := Position( entries, ls[ i ][ j ] );
od; od;
return T;
end );
#############################################################################
##
#O CanonicalCayleyTable( ls )
@ -71,13 +108,15 @@ function( ls )
for i in [1..n] do for j in [1..n] do
AddSet( entries, ls[ i ][ j ] );
od; od;
if entries = [1..n] then return List(ls, l -> ShallowCopy(l));
fi;
# renaming the entries and making a mutable copy, too
T := List( [1..n], i -> [1..n] );
for i in [1..n] do for j in [1..n] do
T[ i ][ j ] := Position( entries, ls[ i ][ j ] );
od; od;
return T;
end );
end
#############################################################################
##
@ -94,7 +133,7 @@ function( ls )
Error( "LOOPS: <1> must be a latin square." );
fi;
# renaming the entries to be 1, ..., n
T := CanonicalCayleyTable( ls );
T := CanonicalCayleyTableOfLeftQuasigroupTable( ls );
# permuting the columns so that the first row reads 1, ..., n
perm := PermList( T[ 1 ] );
T := List( T, row -> Permuted( row, perm ) );
@ -136,7 +175,7 @@ function( ct )
Error( "LOOPS: <1> must be a latin square." );
fi;
# Making sure that entries are 1, ..., n
ct := CanonicalCayleyTable( ct );
ct := CanonicalCayleyTableOfLeftQuasigroupTable( ct );
# constructing the family
F := NewFamily( "QuasigroupByCayleyTableFam", IsQuasigroupElement );
# installing data for the family
@ -173,7 +212,7 @@ function( ct )
fi;
# Making sure that the entries are 1, ..., n.
# The table will remain normalized.
ct := CanonicalCayleyTable( ct );
ct := CanonicalCayleyTableOfLeftQuasigroupTable( ct );
# constructing the family
F := NewFamily( "LoopByCayleyTableFam", IsLoopElement );
# installing the data for the family
@ -695,27 +734,30 @@ function( list, dummy )
return quasigroup_list[ 1 ];
fi;
# at least 2 quasigroups and loops; we will not use recursion
# making all Cayley tables cannonical
# making all Cayley tables canonical
for s in [1..n] do
quasigroup_list[ s ] := QuasigroupByCayleyTable( CanonicalCayleyTable( CayleyTable( quasigroup_list[ s ] ) ) );
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 := Size( quasigroup_list[ 1 ] );
nM := Size( quasigroup_list[ s ] );
TL := CayleyTable( quasigroup_list[ 1 ] );
TM := CayleyTable( quasigroup_list[ s ] );
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 ] := QuasigroupByCayleyTable( T );
quasigroup_list[ 1 ] := T;
od;
if are_all_loops then
return IntoLoop( quasigroup_list[1] );
return LoopByCayleyTable( quasigroup_list[1] );
fi;
return quasigroup_list[ 1 ];
return QuasigroupByCayleyTable( quasigroup_list[1] );
end );
#############################################################################