Copy of LOOPS 3.3.0
This commit is contained in:
commit
7e8b3b5562
510 changed files with 97978 additions and 0 deletions
21
gap/banner.g
Normal file
21
gap/banner.g
Normal file
|
@ -0,0 +1,21 @@
|
|||
#############################################################################
|
||||
##
|
||||
#A banner.g loops G. P. Nagy / P. Vojtechovsky
|
||||
##
|
||||
#H @(#)$Id: banner.g, v 3.3.0 2016/09/21 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
if not QUIET and BANNER then
|
||||
Print(
|
||||
" ______________________________________________________\n",
|
||||
" LOOPS: Computing with quasigroups and loops in GAP \n",
|
||||
" version 3.3.0 \n",
|
||||
" Gabor P. Nagy & Petr Vojtechovsky \n",
|
||||
" nagyg@math.u-szeged.hu petr@math.du.edu \n",
|
||||
" ------------------------------------------------------\n",
|
||||
" homepage: www.math.du.edu/loops \n",
|
||||
" ______________________________________________________\n\n");
|
||||
fi;
|
18
gap/bol_core_methods.gd
Normal file
18
gap/bol_core_methods.gd
Normal file
|
@ -0,0 +1,18 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W bol_core_methods.gd Common methods for Bol loops [loops]
|
||||
##
|
||||
#H @(#)$Id: bol_core_methods.gd, v 3.0.0 2015/06/12 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
DeclareAttribute( "AssociatedLeftBruckLoop", IsLeftBolLoop );
|
||||
DeclareAttribute( "AssociatedRightBruckLoop", IsRightBolLoop );
|
||||
|
||||
DeclareOperation( "IsExactGroupFactorization", [ IsGroup, IsGroup, IsGroup ] );
|
||||
DeclareGlobalFunction( "RightBolLoopByExactGroupFactorizationNC" ); # auxiliary
|
||||
DeclareGlobalFunction( "RightBolLoopByExactGroupFactorization" ); # variable arguments
|
||||
|
||||
|
133
gap/bol_core_methods.gi
Normal file
133
gap/bol_core_methods.gi
Normal file
|
@ -0,0 +1,133 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W bol_core_methods.gi Common methods for Bol loops [loops]
|
||||
##
|
||||
#H @(#)$Id: bol_core_methods.gi, v 3.0.0 2015/06/12 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#A AssociatedLeftBruckLoop( Q )
|
||||
##
|
||||
## Given a left Bol loop Q for which x -> x^2 is a permutation,
|
||||
## returns the associated left Bruck loop, defined by
|
||||
## x o y = (x*((y*y)*x))^(1/2) (default) or
|
||||
## x o y = x^(1/2)*(y*x^(1/2)) (commented out)
|
||||
|
||||
InstallMethod( AssociatedLeftBruckLoop,
|
||||
[ IsLeftBolLoop ],
|
||||
function( Q )
|
||||
local n, ct, squares, roots, new_ct, L, i, j;
|
||||
n := Size( Q );
|
||||
ct := CanonicalCayleyTable( CayleyTable( Q ) );
|
||||
squares := List( [ 1..n ], i -> ct[i][i] );
|
||||
if not Size( Set( squares ) ) = n then
|
||||
Error( "LOOPS: <1> must be a Bol loop in which squaring is a bijection." );
|
||||
fi;
|
||||
roots := Inverse( PermList( squares ) ); # square roots
|
||||
new_ct := List([1..n], i -> [1..n] );
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
new_ct[i][j] := (ct[i][ct[ct[j][j]][i]])^roots; # (x*((y*y)*x))^(1/2)
|
||||
# new_ct[i][j] := ct[i^roots][ct[ j ][ i^roots ]]; # x^(1/2)*(y*x^(1/2))
|
||||
od; od;
|
||||
L := LoopByCayleyTable( new_ct ); # the associated left Bruck loop
|
||||
SetIsLeftBruckLoop( L, true );
|
||||
return L;
|
||||
end );
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#A AssociatedRightBruckLoop( Q )
|
||||
##
|
||||
## Given a right Bol loop Q for which x -> x^2 is a permutation,
|
||||
## returns the associated right Bruck loop, defined by
|
||||
## x o y = ((x*(y*y))*x)^(1/2) (default) or
|
||||
## x o y = (x^(1/2)*y)*x^(1/2) (commented out)
|
||||
|
||||
InstallMethod( AssociatedRightBruckLoop,
|
||||
[ IsRightBolLoop ],
|
||||
function( Q )
|
||||
local n, ct, squares, roots, new_ct, L, i, j;
|
||||
n := Size( Q );
|
||||
ct := CanonicalCayleyTable( CayleyTable( Q ) );
|
||||
squares := List( [ 1..n ], i -> ct[i][i] );
|
||||
if not Size( Set( squares ) ) = n then
|
||||
Error( "LOOPS: <1> must be a right Bol loop in which squaring is a bijection." );
|
||||
fi;
|
||||
roots := Inverse( PermList( squares ) ); # square roots
|
||||
new_ct := List([1..n], i -> [1..n] );
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
new_ct[i][j] := (ct[ct[j][ct[i][i]]][j])^roots; # x o y = ((y*(x*x))*y)^(1/2)
|
||||
# new_ct[i][j] := ct[ct[j^roots][i]][j^roots]; # x o y = (y^(1/2)*x)*y^(1/2)
|
||||
od; od;
|
||||
L := LoopByCayleyTable( new_ct ); # the associated right Bruck loop
|
||||
SetIsRightBruckLoop( L, true );
|
||||
return L;
|
||||
end );
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsExactGroupFactorization( G, H1, H2 )
|
||||
##
|
||||
## Let G be a group and H_1, H_2 subgroups. The triple (G,H_1,H_2) is an
|
||||
## exact group factorization, if H_1 \cap H_2 = 1 and G=H_1H_2.
|
||||
##
|
||||
## Returns true if (G, H1, H2) is an exact group factorization.
|
||||
|
||||
InstallMethod( IsExactGroupFactorization, "for a group and two subgroups",
|
||||
[ IsGroup, IsGroup, IsGroup ],
|
||||
function( G, H1, H2 )
|
||||
return IsSubgroup(G,H1) and IsSubgroup(G,H2) and Size(G)=Size(H1)*Size(H2) and IsTrivial(Intersection(H1,H2));
|
||||
end);
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F RightBolLoopByExactGroupFactorization
|
||||
##
|
||||
## Let (G,H_1,H_2) be an exact group factorization. Define
|
||||
## U = G\times G, S = H_1 \times H_2, and T = {(g,g^{-1}) : g in G }.
|
||||
## Then (U,S,T) is a right Bol loop folder
|
||||
## Returns the right Bol loop corresponding to (U,S,T).
|
||||
|
||||
InstallGlobalFunction( RightBolLoopByExactGroupFactorizationNC,
|
||||
function( g, h1, h2 )
|
||||
local f,sect,stab,ghom,st,q,rmlt;
|
||||
f := DirectProduct( g, g );
|
||||
sect := List( g, x -> Image( Embedding( f, 1 ), x )*Image( Embedding( f, 2 ), x^-1 ) );
|
||||
stab := ClosureGroup( Image( Embedding( f, 1 ), h1 ), Image( Embedding( f, 2 ), h2 ) );
|
||||
ghom := ActionHomomorphism( f, RightCosets( f, stab ), OnRight );
|
||||
st := Image( ghom, sect );;
|
||||
q := LoopByRightSection( st );
|
||||
SetRightSection( q, st );
|
||||
rmlt := Subgroup( f, sect );
|
||||
SetRightMultiplicationGroup( q, Image( ghom, Subgroup( f, SmallGeneratingSet( rmlt ) ) ) );
|
||||
return q;
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( RightBolLoopByExactGroupFactorization,
|
||||
function( arg )
|
||||
local g, h1, h2;
|
||||
if Length( arg ) = 3 then
|
||||
g := arg[1]; h1 := arg[2]; h2 := arg[3];
|
||||
elif Length( arg ) = 2 then
|
||||
g := arg[1]; h1 := arg[2]; h2 := Stabilizer( g, Orbit( g )[1] );
|
||||
elif Length( arg ) = 1 and Length( arg[1] ) = 3 then
|
||||
g := arg[1][1]; h1 := arg[1][2]; h2 := arg[1][3];
|
||||
elif Length(arg) = 1 and Length( arg[1] ) = 2 then
|
||||
g := arg[1][1]; h1 := arg[1][2]; h2 := Stabilizer(g,Orbit(g)[1]);
|
||||
else
|
||||
Error("LOOPS: Argument must be an exact group factorization or a group with a regular subgroup.");
|
||||
fi;
|
||||
if not IsExactGroupFactorization( g, h1, h2 ) then
|
||||
Error("LOOPS: Argument does not correspond to an exact group factorization.");
|
||||
fi;
|
||||
return RightBolLoopByExactGroupFactorizationNC( g, h1, h2 );
|
||||
end);
|
||||
|
106
gap/classes.gd
Normal file
106
gap/classes.gd
Normal file
|
@ -0,0 +1,106 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W classes.gd Testing varieties [loops]
|
||||
##
|
||||
#H @(#)$Id: classes.gd, v 3.3.0 2016/10/26 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## ASSOCIATIVITY, COMMUTATIVITY AND GENERALIZATIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareProperty( "IsAssociative", IsLoop );
|
||||
DeclareProperty( "IsCommutative", IsQuasigroup );
|
||||
DeclareProperty( "IsPowerAssociative", IsQuasigroup );
|
||||
DeclareProperty( "IsDiassociative", IsQuasigroup );
|
||||
|
||||
#############################################################################
|
||||
## INVERSE PROPERTIES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareProperty( "HasLeftInverseProperty", IsLoop );
|
||||
DeclareProperty( "HasRightInverseProperty", IsLoop );
|
||||
DeclareProperty( "HasInverseProperty", IsLoop );
|
||||
DeclareProperty( "HasWeakInverseProperty", IsLoop );
|
||||
DeclareProperty( "HasTwosidedInverses", IsLoop );
|
||||
DeclareProperty( "HasAutomorphicInverseProperty", IsLoop );
|
||||
DeclareProperty( "HasAntiautomorphicInverseProperty", IsLoop );
|
||||
|
||||
#############################################################################
|
||||
## PROPERTIES OF QUASIGROUPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareProperty( "IsSemisymmetric", IsQuasigroup );
|
||||
DeclareProperty( "IsTotallySymmetric", IsQuasigroup );
|
||||
DeclareProperty( "IsIdempotent", IsQuasigroup );
|
||||
DeclareProperty( "IsSteinerQuasigroup", IsQuasigroup );
|
||||
DeclareProperty( "IsUnipotent", IsQuasigroup );
|
||||
DeclareProperty( "IsLDistributive", IsQuasigroup );
|
||||
DeclareProperty( "IsRDistributive", IsQuasigroup );
|
||||
DeclareSynonymAttr( "IsLeftDistributive", IsLDistributive );
|
||||
DeclareSynonymAttr( "IsRightDistributive", IsRDistributive );
|
||||
#IsDistributive already declared in GAP
|
||||
DeclareProperty( "IsEntropic", IsQuasigroup );
|
||||
DeclareSynonymAttr( "IsMedial", IsEntropic );
|
||||
|
||||
#############################################################################
|
||||
## LOOPS OF BOL-MOUFANG TYPE
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareProperty( "IsExtraLoop", IsLoop );
|
||||
DeclareProperty( "IsMoufangLoop", IsLoop );
|
||||
DeclareProperty( "IsCLoop", IsLoop );
|
||||
DeclareProperty( "IsLeftBolLoop", IsLoop );
|
||||
DeclareProperty( "IsRightBolLoop", IsLoop );
|
||||
DeclareProperty( "IsLCLoop", IsLoop );
|
||||
DeclareProperty( "IsRCLoop", IsLoop );
|
||||
DeclareProperty( "IsLeftNuclearSquareLoop", IsLoop );
|
||||
DeclareProperty( "IsMiddleNuclearSquareLoop", IsLoop );
|
||||
DeclareProperty( "IsRightNuclearSquareLoop", IsLoop );
|
||||
DeclareProperty( "IsNuclearSquareLoop", IsLoop );
|
||||
DeclareProperty( "IsFlexible", IsQuasigroup );
|
||||
DeclareProperty( "IsLeftAlternative", IsQuasigroup );
|
||||
DeclareProperty( "IsRightAlternative", IsQuasigroup );
|
||||
DeclareProperty( "IsAlternative", IsQuasigroup );
|
||||
|
||||
#############################################################################
|
||||
## POWER ALTERNATIVE LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareProperty( "IsLeftPowerAlternative", IsLoop );
|
||||
DeclareProperty( "IsRightPowerAlternative", IsLoop );
|
||||
DeclareProperty( "IsPowerAlternative", IsLoop );
|
||||
|
||||
#############################################################################
|
||||
## CC-LOOPS AND RELATED PROPERTIES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareProperty( "IsLCCLoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsLeftConjugacyClosedLoop", IsLCCLoop );
|
||||
DeclareProperty( "IsRCCLoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsRightConjugacyClosedLoop", IsRCCLoop );
|
||||
DeclareProperty( "IsCCLoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsConjugacyClosedLoop", IsCCLoop );
|
||||
DeclareProperty( "IsOsbornLoop", IsLoop );
|
||||
|
||||
############################################################################
|
||||
## ADDITIONAL VARIETIES OF LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareProperty( "IsCodeLoop", IsLoop );
|
||||
DeclareProperty( "IsSteinerLoop", IsLoop );
|
||||
DeclareProperty( "IsLeftBruckLoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsLeftKLoop", IsLeftBruckLoop );
|
||||
DeclareProperty( "IsRightBruckLoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsRightKLoop", IsRightBruckLoop );
|
||||
DeclareProperty( "IsALoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsAutomorphicLoop", IsALoop );
|
||||
DeclareProperty( "IsLeftALoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsLeftAutomorphicLoop", IsLeftALoop );
|
||||
DeclareProperty( "IsMiddleALoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsMiddleAutomorphicLoop", IsMiddleALoop );
|
||||
DeclareProperty( "IsRightALoop", IsLoop );
|
||||
DeclareSynonymAttr( "IsRightAutomorphicLoop", IsRightALoop );
|
916
gap/classes.gi
Normal file
916
gap/classes.gi
Normal file
|
@ -0,0 +1,916 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W classes.gi Testing properties/varieties [loops]
|
||||
##
|
||||
#H @(#)$Id: classes.gi, v 3.3.0 2016/10/26 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## ASSOCIATIVITY, COMMUTATIVITY AND GENERALIZATIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
# (PROG) IsAssociative is already implemented for magmas, but we provide
|
||||
# a new method based on sections. This new method is much faster for groups,
|
||||
# and a bit slower for nonassociative loops.
|
||||
|
||||
InstallOtherMethod( IsAssociative, "for loops",
|
||||
[ IsLoop ], 0,
|
||||
function( Q )
|
||||
local sLS, x, y;
|
||||
sLS := Set( LeftSection( Q ) );
|
||||
for x in LeftSection( Q ) do
|
||||
for y in LeftSection( Q ) do
|
||||
if not x*y in sLS then return false; fi;
|
||||
od;
|
||||
od;
|
||||
return true;
|
||||
end);
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsExtraLoop, IsAssociative and IsLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsCommutative( Q )
|
||||
##
|
||||
## Returns true if <Q> is commutative.
|
||||
|
||||
InstallOtherMethod( IsCommutative, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
return LeftSection( Q ) = RightSection( Q );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsPowerAssociative( Q )
|
||||
##
|
||||
## Returns true if <Q> is a power associative quasigroup.
|
||||
|
||||
InstallOtherMethod( IsPowerAssociative, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
local checked, x, S;
|
||||
checked := [];
|
||||
repeat
|
||||
x := Difference( Elements(Q), checked );
|
||||
if IsEmpty( x ) then
|
||||
return true;
|
||||
fi;
|
||||
S := Subquasigroup( Q, [x[1]] );
|
||||
if not IsAssociative( S ) then
|
||||
return false;
|
||||
fi;
|
||||
checked := Union( checked, Elements( S ) ); # S is a group, so every subquasigroup of S is a group
|
||||
until 0=1;
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( HasTwosidedInverses, IsPowerAssociative and IsLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsDiassociative( Q )
|
||||
##
|
||||
## Returns true if <Q> is a diassociative quasigroup.
|
||||
|
||||
InstallOtherMethod( IsDiassociative, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
local checked, all_pairs, x, S;
|
||||
checked := [];
|
||||
all_pairs := Combinations( PosInParent( Elements( Q ) ), 2 ); # it is faster to work with integers
|
||||
repeat
|
||||
x := Difference( all_pairs, checked );
|
||||
if IsEmpty( x ) then
|
||||
return true;
|
||||
fi;
|
||||
S := Subquasigroup( Q, x[1] );
|
||||
if not IsAssociative( S ) then
|
||||
return false;
|
||||
fi;
|
||||
checked := Union( checked, Combinations( PosInParent( Elements( S ) ), 2 ) );
|
||||
until 0=1;
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsPowerAlternative, IsDiassociative );
|
||||
InstallTrueMethod( IsFlexible, IsDiassociative );
|
||||
|
||||
#############################################################################
|
||||
## INVERSE PROPERTIES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P HasLeftInverseProperty( L )
|
||||
##
|
||||
## Returns true if <L> has the left inverse property.
|
||||
|
||||
InstallMethod( HasLeftInverseProperty, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( LeftSection( L ), a -> a^-1 in LeftSection( L ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P HasRightInverseProperty( L )
|
||||
##
|
||||
## Returns true if <L> has the right inverse property.
|
||||
|
||||
InstallMethod( HasRightInverseProperty, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( RightSection( L ), a -> a^-1 in RightSection( L ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P HasInverseProperty( L )
|
||||
##
|
||||
## Returns true if <L> has the inverse property.
|
||||
|
||||
InstallMethod( HasInverseProperty, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return HasLeftInverseProperty( L ) and HasRightInverseProperty( L );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P HasWeakInverseProperty( L )
|
||||
##
|
||||
## Returns true if <L> has the weak inverse property.
|
||||
|
||||
InstallMethod( HasWeakInverseProperty, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( L, x -> ForAll( L, y -> LeftInverse(x*y)*x=LeftInverse(y) ));
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P HasTwosidedInverses( L )
|
||||
##
|
||||
## Returns true if <L> has two-sided inverses.
|
||||
|
||||
InstallMethod( HasTwosidedInverses, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( L, x -> LeftInverse( x ) = RightInverse( x ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P HasAutomorphicInverseProperty( L )
|
||||
##
|
||||
## Returns true if <L> has the automorphic inverse property.
|
||||
|
||||
InstallMethod( HasAutomorphicInverseProperty, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( L, x -> ForAll( L, y ->
|
||||
LeftInverse( x*y ) = LeftInverse( x )*LeftInverse( y ) ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P HasAntiautomorphicInverseProperty( L )
|
||||
##
|
||||
## Returns true if <L> has the antiautomorphic inverse property.
|
||||
|
||||
InstallMethod( HasAntiautomorphicInverseProperty, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( L, x -> ForAll( L, y ->
|
||||
LeftInverse( x*y ) = LeftInverse( y )*LeftInverse( x ) ) );
|
||||
end );
|
||||
|
||||
# implies and is implied by (for inverse properties)
|
||||
InstallTrueMethod( HasAntiautomorphicInverseProperty, HasAutomorphicInverseProperty and IsCommutative );
|
||||
InstallTrueMethod( HasAutomorphicInverseProperty, HasAntiautomorphicInverseProperty and IsCommutative );
|
||||
InstallTrueMethod( HasLeftInverseProperty, HasInverseProperty );
|
||||
InstallTrueMethod( HasRightInverseProperty, HasInverseProperty );
|
||||
InstallTrueMethod( HasWeakInverseProperty, HasInverseProperty );
|
||||
InstallTrueMethod( HasAntiautomorphicInverseProperty, HasInverseProperty );
|
||||
InstallTrueMethod( HasTwosidedInverses, HasAntiautomorphicInverseProperty );
|
||||
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and IsCommutative );
|
||||
InstallTrueMethod( HasInverseProperty, HasRightInverseProperty and IsCommutative );
|
||||
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and HasRightInverseProperty );
|
||||
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and HasWeakInverseProperty );
|
||||
InstallTrueMethod( HasInverseProperty, HasRightInverseProperty and HasWeakInverseProperty );
|
||||
InstallTrueMethod( HasInverseProperty, HasLeftInverseProperty and HasAntiautomorphicInverseProperty );
|
||||
InstallTrueMethod( HasInverseProperty, HasRightInverseProperty and HasAntiautomorphicInverseProperty );
|
||||
InstallTrueMethod( HasInverseProperty, HasWeakInverseProperty and HasAntiautomorphicInverseProperty );
|
||||
InstallTrueMethod( HasTwosidedInverses, HasLeftInverseProperty );
|
||||
InstallTrueMethod( HasTwosidedInverses, HasRightInverseProperty );
|
||||
InstallTrueMethod( HasTwosidedInverses, IsFlexible and IsLoop );
|
||||
|
||||
|
||||
#############################################################################
|
||||
## PROPERTIES OF QUASIGROUPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsSemisymmetric( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is semisymmetric, i.e., (xy)x=y.
|
||||
|
||||
InstallMethod( IsSemisymmetric, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
return ForAll( Q, x ->
|
||||
LeftTranslation( Q, x ) * RightTranslation( Q, x ) = () );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsTotallySymmetric( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is totally symmetric, i.e,
|
||||
## commutative and semisymmetric.
|
||||
|
||||
InstallMethod( IsTotallySymmetric, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
return IsCommutative( Q ) and IsSemisymmetric( Q );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsIdempotent( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is idempotent, i.e., x*x=x.
|
||||
|
||||
InstallMethod( IsIdempotent, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
return ForAll( Q, x -> x*x = x );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsSteinerQuasigroup( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is a Steiner quasigroup, i.e.,
|
||||
## idempotent and totally symmetric.
|
||||
|
||||
InstallMethod( IsSteinerQuasigroup, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
return IsIdempotent( Q ) and IsTotallySymmetric( Q );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsUnipotent( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is unipotent, i.e., x*x=y*y.
|
||||
|
||||
InstallMethod( IsUnipotent, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
local square;
|
||||
if IsLoop( Q ) then return IsIdempotent( Q ); fi;
|
||||
square := Elements( Q )[ 1 ]^2;
|
||||
return ForAll( Q, y -> y^2 = square );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLDistributive( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is left distributive.
|
||||
|
||||
InstallOtherMethod( IsLDistributive, "for Quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
# BETTER ALGORITHM LATER?
|
||||
local x, y, z;
|
||||
for x in Q do for y in Q do for z in Q do
|
||||
if not x*(y*z) = (x*y)*(x*z) then return false; fi;
|
||||
od; od; od;
|
||||
return true;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRDistributive( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is right distributive.
|
||||
|
||||
InstallOtherMethod( IsRDistributive, "for Quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
# BETTER ALGORITHM LATER?
|
||||
local x, y, z;
|
||||
for x in Q do for y in Q do for z in Q do
|
||||
if not (x*y)*z = (x*z)*(y*z) then return false; fi;
|
||||
od; od; od;
|
||||
return true;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsEntropic( Q )
|
||||
##
|
||||
## Returns true if the quasigroup <Q> is entropic.
|
||||
|
||||
InstallMethod( IsEntropic, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
# BETTER ALGORITHM LATER?
|
||||
local x, y, z, w;
|
||||
for x in Q do for y in Q do for z in Q do for w in Q do
|
||||
if not (x*y)*(z*w) = (x*z)*(y*w) then return false; fi;
|
||||
od; od; od; od;
|
||||
return true;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## LOOPS OF BOL-MOUFANG
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsExtraLoop( L )
|
||||
##
|
||||
## Returns true if <L> is an extra loop.
|
||||
|
||||
InstallMethod( IsExtraLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return IsMoufangLoop( L ) and IsNuclearSquareLoop( L );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsMoufangLoop, IsExtraLoop );
|
||||
InstallTrueMethod( IsCLoop, IsExtraLoop );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsExtraLoop, IsMoufangLoop and IsLeftNuclearSquareLoop );
|
||||
InstallTrueMethod( IsExtraLoop, IsMoufangLoop and IsMiddleNuclearSquareLoop );
|
||||
InstallTrueMethod( IsExtraLoop, IsMoufangLoop and IsRightNuclearSquareLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsMoufangLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a Moufang loop.
|
||||
|
||||
InstallMethod( IsMoufangLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return IsLeftBolLoop( L ) and HasRightInverseProperty( L );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftBolLoop, IsMoufangLoop );
|
||||
InstallTrueMethod( IsRightBolLoop, IsMoufangLoop );
|
||||
InstallTrueMethod( IsDiassociative, IsMoufangLoop );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsMoufangLoop, IsLeftBolLoop and IsRightBolLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsCLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a C-loop.
|
||||
|
||||
InstallMethod( IsCLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return IsLCLoop( L ) and IsRCLoop( L );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLCLoop, IsCLoop );
|
||||
InstallTrueMethod( IsRCLoop, IsCLoop );
|
||||
InstallTrueMethod( IsDiassociative, IsCLoop and IsFlexible);
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsCLoop, IsLCLoop and IsRCLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLeftBolLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a left Bol loop.
|
||||
|
||||
InstallMethod( IsLeftBolLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( LeftSection( L ), a ->
|
||||
ForAll( LeftSection( L ), b -> a*b*a in LeftSection( L ) ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsRightBolLoop, IsLeftBolLoop and IsCommutative );
|
||||
InstallTrueMethod( IsLeftPowerAlternative, IsLeftBolLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRightBolLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a right Bol loop.
|
||||
|
||||
InstallMethod( IsRightBolLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( RightSection( L ), a ->
|
||||
ForAll( RightSection( L ), b -> a*b*a in RightSection( L ) ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftBolLoop, IsRightBolLoop and IsCommutative );
|
||||
InstallTrueMethod( IsRightPowerAlternative, IsRightBolLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLCLoop( L )
|
||||
##
|
||||
## Returns true if <L> is an LC-loop.
|
||||
|
||||
InstallMethod( IsLCLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( LeftSection( L ), a ->
|
||||
ForAll( RightSection( L ), b -> b^(-1)*a*a*b in LeftSection( L ) ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftPowerAlternative, IsLCLoop );
|
||||
InstallTrueMethod( IsLeftNuclearSquareLoop, IsLCLoop );
|
||||
InstallTrueMethod( IsMiddleNuclearSquareLoop, IsLCLoop );
|
||||
InstallTrueMethod( IsRCLoop, IsLCLoop and IsCommutative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRCLoop( L )
|
||||
##
|
||||
## Returns true if <L> is an RC-loop.
|
||||
|
||||
InstallMethod( IsRCLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( LeftSection( L ), a ->
|
||||
ForAll( RightSection( L ), b -> a^(-1)*b*b*a in RightSection( L ) ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsRightPowerAlternative, IsRCLoop );
|
||||
InstallTrueMethod( IsRightNuclearSquareLoop, IsRCLoop );
|
||||
InstallTrueMethod( IsMiddleNuclearSquareLoop, IsRCLoop );
|
||||
InstallTrueMethod( IsLCLoop, IsRCLoop and IsCommutative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLeftNuclearSquareLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a left nuclear square loop.
|
||||
|
||||
InstallMethod( IsLeftNuclearSquareLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( L, x -> x^2 in LeftNucleus( L ) );
|
||||
end );
|
||||
|
||||
#implies
|
||||
InstallTrueMethod( IsRightNuclearSquareLoop, IsLeftNuclearSquareLoop and IsCommutative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsMiddleNuclearSquareLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a middle nuclear square loop.
|
||||
|
||||
InstallMethod( IsMiddleNuclearSquareLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( L, x -> x^2 in MiddleNucleus( L ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRightNuclearSquareLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a right nuclear square loop.
|
||||
|
||||
InstallMethod( IsRightNuclearSquareLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( L, x -> x^2 in RightNucleus( L ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftNuclearSquareLoop, IsRightNuclearSquareLoop and IsCommutative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsNuclearSquareLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a nuclear square loop.
|
||||
|
||||
InstallMethod( IsNuclearSquareLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return IsLeftNuclearSquareLoop( L ) and IsRightNuclearSquareLoop( L )
|
||||
and IsMiddleNuclearSquareLoop( L );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftNuclearSquareLoop, IsNuclearSquareLoop );
|
||||
InstallTrueMethod( IsRightNuclearSquareLoop, IsNuclearSquareLoop );
|
||||
InstallTrueMethod( IsMiddleNuclearSquareLoop, IsNuclearSquareLoop );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsNuclearSquareLoop, IsLeftNuclearSquareLoop
|
||||
and IsRightNuclearSquareLoop and IsMiddleNuclearSquareLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsFlexible( Q )
|
||||
##
|
||||
## Returns true if <Q> is a flexible quasigroup.
|
||||
|
||||
InstallMethod( IsFlexible, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
local LS, RS;
|
||||
LS := LeftSection( Q );
|
||||
RS := RightSection( Q );
|
||||
return ForAll( [1..Size( Q )], i -> LS[ i ] * RS[ i ] = RS[ i ] * LS[ i ] );
|
||||
end );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsFlexible, IsCommutative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLeftAlternative( Q )
|
||||
##
|
||||
## Returns true if <Q> is a left alternative quasigroup.
|
||||
|
||||
InstallMethod( IsLeftAlternative, "for quasigroup",
|
||||
[ IsQuasigroup],
|
||||
function( Q )
|
||||
if IsLoop( Q ) then
|
||||
return ForAll( LeftSection( Q ), a -> a*a in LeftSection( Q ) );
|
||||
fi;
|
||||
return ForAll( Q, x -> ForAll( Q, y -> x*(x*y) = (x*x)*y ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsRightAlternative, IsLeftAlternative and IsCommutative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRightAlternative( Q )
|
||||
##
|
||||
## Returns true if <Q> is a right alternative quasigroup.
|
||||
|
||||
InstallMethod( IsRightAlternative, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
if IsLoop( Q ) then
|
||||
return ForAll( RightSection( Q ), a -> a*a in RightSection( Q ) );
|
||||
fi;
|
||||
return ForAll( Q, x -> ForAll( Q, y -> (x*y)*y = x*(y*y) ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftAlternative, IsRightAlternative and IsCommutative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsAlternative( Q )
|
||||
##
|
||||
## Returns true if <Q> is an alternative quasigroup.
|
||||
|
||||
InstallMethod( IsAlternative, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
return IsLeftAlternative( Q ) and IsRightAlternative( Q );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftAlternative, IsAlternative );
|
||||
InstallTrueMethod( IsRightAlternative, IsAlternative );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsAlternative, IsLeftAlternative and IsRightAlternative );
|
||||
|
||||
#############################################################################
|
||||
## POWER ALTERNATIVE LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLeftPowerAlternative( L )
|
||||
##
|
||||
## Returns true if <L> is a left power alternative loop.
|
||||
|
||||
InstallMethod( IsLeftPowerAlternative, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
local i, M;
|
||||
if Size( L ) = 1 then return true; fi;
|
||||
for i in [ 2..Size( L )] do
|
||||
M := Subloop( L, [ Elements( L )[ i ] ]);
|
||||
if not Size( RelativeLeftMultiplicationGroup( L, M ) ) = Size( M ) then
|
||||
return false;
|
||||
fi;
|
||||
od;
|
||||
return true;
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftAlternative, IsLeftPowerAlternative );
|
||||
InstallTrueMethod( HasLeftInverseProperty, IsLeftPowerAlternative );
|
||||
InstallTrueMethod( IsPowerAssociative, IsLeftPowerAlternative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRightPowerAlternative( L )
|
||||
##
|
||||
## Returns true if <L> is a right power alternative loop.
|
||||
|
||||
InstallMethod( IsRightPowerAlternative, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
local i, M;
|
||||
if Size( L ) = 1 then return true; fi;
|
||||
for i in [ 2..Size( L ) ] do
|
||||
M := Subloop( L, [ Elements( L )[ i ] ] );
|
||||
if not Size( RelativeRightMultiplicationGroup( L, M ) ) = Size( M ) then
|
||||
return false;
|
||||
fi;
|
||||
od;
|
||||
return true;
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsRightAlternative, IsRightPowerAlternative );
|
||||
InstallTrueMethod( HasRightInverseProperty, IsRightPowerAlternative );
|
||||
InstallTrueMethod( IsPowerAssociative, IsRightPowerAlternative );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsPowerAlternative( L )
|
||||
##
|
||||
## Returns true if <L> is a power alternative loop.
|
||||
|
||||
InstallMethod( IsPowerAlternative, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ( IsLeftPowerAlternative( L ) and IsRightPowerAlternative( L ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftPowerAlternative, IsPowerAlternative );
|
||||
InstallTrueMethod( IsRightPowerAlternative, IsPowerAlternative );
|
||||
|
||||
#############################################################################
|
||||
## CC-LOOPS AND RELATED PROPERTIES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLCCLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a left conjugacy closed loop.
|
||||
|
||||
InstallMethod( IsLCCLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( LeftSection( L ), a ->
|
||||
ForAll( LeftSection( L ), b -> b*a*b^(-1) in LeftSection( L ) ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsAssociative, IsLCCLoop and IsCommutative );
|
||||
InstallTrueMethod( IsExtraLoop, IsLCCLoop and IsMoufangLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRCCLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a right conjugacy closed loop.
|
||||
|
||||
InstallMethod( IsRCCLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return ForAll( RightSection( L ), a ->
|
||||
ForAll( RightSection( L ), b -> b*a*b^(-1) in RightSection( L ) ) );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsAssociative, IsRCCLoop and IsCommutative );
|
||||
InstallTrueMethod( IsExtraLoop, IsRCCLoop and IsMoufangLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsCCLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a conjugacy closed loop.
|
||||
|
||||
InstallMethod( IsCCLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return IsLCCLoop( L ) and IsRCCLoop( L );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLCCLoop, IsCCLoop );
|
||||
InstallTrueMethod( IsRCCLoop, IsCCLoop );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsCCLoop, IsLCCLoop and IsRCCLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsOsbornLoop( L )
|
||||
##
|
||||
## Returns true if <L> is an Osborn loop.
|
||||
|
||||
InstallMethod( IsOsbornLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
# MIGHT REDO LATER
|
||||
return ForAll(L, x-> ForAll(L, y->
|
||||
ForAll(L, z-> x*((y*z)*x) = LeftDivision(LeftInverse(x),y)*(z*x))
|
||||
));
|
||||
end );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsOsbornLoop, IsMoufangLoop );
|
||||
InstallTrueMethod( IsOsbornLoop, IsCCLoop );
|
||||
|
||||
#############################################################################
|
||||
## ADDITIONAL VARIETIES OF LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsCodeLoop( L )
|
||||
##
|
||||
## Returns true if <L> is an even code loop.
|
||||
|
||||
InstallMethod( IsCodeLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
# even code loops are precisely Moufang 2-loops with Frattini subloop of order 1, 2
|
||||
return Set( Factors( Size( L ) ) ) = [ 2 ]
|
||||
and IsMoufangLoop( L )
|
||||
and Size( FrattiniSubloop( L ) ) in [1, 2];
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsExtraLoop, IsCodeLoop );
|
||||
InstallTrueMethod( IsCCLoop, IsCodeLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsSteinerLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a Steiner loop.
|
||||
|
||||
InstallMethod( IsSteinerLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
# Steiner loops are inverse property loops of exponent at most 2.
|
||||
return HasInverseProperty( L ) and Exponent( L )<=2;
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsCommutative, IsSteinerLoop );
|
||||
InstallTrueMethod( IsCLoop, IsSteinerLoop );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLeftBruckLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a left Bruck loop.
|
||||
|
||||
InstallMethod( IsLeftBruckLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return IsLeftBolLoop( L ) and HasAutomorphicInverseProperty( L );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( HasAutomorphicInverseProperty, IsLeftBruckLoop );
|
||||
InstallTrueMethod( IsLeftBolLoop, IsLeftBruckLoop );
|
||||
InstallTrueMethod( IsRightBruckLoop, IsLeftBruckLoop and IsCommutative );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsLeftBruckLoop, IsLeftBolLoop and HasAutomorphicInverseProperty );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRightBruckLoop( L )
|
||||
##
|
||||
## Returns true if <L> is a right Bruck loop.
|
||||
|
||||
InstallMethod( IsRightBruckLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
return IsRightBolLoop( L ) and HasAutomorphicInverseProperty( L );
|
||||
end );
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( HasAutomorphicInverseProperty, IsRightBruckLoop );
|
||||
InstallTrueMethod( IsRightBolLoop, IsRightBruckLoop );
|
||||
InstallTrueMethod( IsLeftBruckLoop, IsRightBruckLoop and IsCommutative );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsRightBruckLoop, IsRightBolLoop and HasAutomorphicInverseProperty );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsLeftALoop( L )
|
||||
##
|
||||
## Returns true if <L> is a left A-loop, that is if
|
||||
## all left inner mappings are automorphisms of <L>.
|
||||
|
||||
InstallMethod( IsLeftALoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
local gens;
|
||||
gens := GeneratorsOfGroup( LeftInnerMappingGroup( L ) );
|
||||
return ForAll(gens, f -> ForAll(L, x -> ForAll(L, y -> (x * y)^f = x^f * y^f )));
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsRightALoop( L )
|
||||
##
|
||||
## Returns true if <L> is a right A-loop, that is if
|
||||
## all right inner mappings are automorphisms of <L>.
|
||||
|
||||
InstallMethod( IsRightALoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
local gens;
|
||||
gens := GeneratorsOfGroup( RightInnerMappingGroup( L ) );
|
||||
return ForAll(gens, f -> ForAll(L, x -> ForAll(L, y -> (x * y)^f = x^f * y^f )));
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsMiddleALoop( L )
|
||||
##
|
||||
## Returns true if <L> is a middle A-loop, that is if
|
||||
## all middle inner mappings (conjugations) are automorphisms of <L>.
|
||||
|
||||
InstallMethod( IsMiddleALoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
local gens;
|
||||
gens := GeneratorsOfGroup( MiddleInnerMappingGroup( L ) );
|
||||
return ForAll(gens, f -> ForAll(L, x -> ForAll(L, y -> (x * y)^f = x^f * y^f )));
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#P IsALoop( L )
|
||||
##
|
||||
## Returns true if <L> is an A-loop, that is if
|
||||
## all inner mappings are automorphisms of <L>.
|
||||
|
||||
InstallMethod( IsALoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( Q )
|
||||
return IsLeftALoop(Q) and IsRightALoop(Q) and IsMiddleALoop(Q);
|
||||
end);
|
||||
|
||||
# implies
|
||||
InstallTrueMethod( IsLeftALoop, IsALoop );
|
||||
InstallTrueMethod( IsRightALoop, IsALoop );
|
||||
InstallTrueMethod( IsMiddleALoop, IsALoop );
|
||||
InstallTrueMethod( IsMiddleALoop, IsCommutative );
|
||||
InstallTrueMethod( IsALoop, IsLeftALoop and IsCommutative );
|
||||
InstallTrueMethod( IsALoop, IsRightALoop and IsCommutative );
|
||||
InstallTrueMethod( IsLeftALoop, IsRightALoop and HasAntiautomorphicInverseProperty );
|
||||
InstallTrueMethod( IsRightALoop, IsLeftALoop and HasAntiautomorphicInverseProperty );
|
||||
InstallTrueMethod( IsFlexible, IsMiddleALoop );
|
||||
InstallTrueMethod( HasAntiautomorphicInverseProperty, IsFlexible and IsLeftALoop );
|
||||
InstallTrueMethod( HasAntiautomorphicInverseProperty, IsFlexible and IsRightALoop );
|
||||
InstallTrueMethod( IsMoufangLoop, IsALoop and IsLeftAlternative );
|
||||
InstallTrueMethod( IsMoufangLoop, IsALoop and IsRightAlternative );
|
||||
InstallTrueMethod( IsMoufangLoop, IsALoop and HasLeftInverseProperty );
|
||||
InstallTrueMethod( IsMoufangLoop, IsALoop and HasRightInverseProperty );
|
||||
InstallTrueMethod( IsMoufangLoop, IsALoop and HasWeakInverseProperty );
|
||||
|
||||
# is implied by
|
||||
InstallTrueMethod( IsLeftALoop, IsLeftBruckLoop );
|
||||
InstallTrueMethod( IsLeftALoop, IsLCCLoop );
|
||||
InstallTrueMethod( IsRightALoop, IsRightBruckLoop );
|
||||
InstallTrueMethod( IsRightALoop, IsRCCLoop );
|
||||
InstallTrueMethod( IsALoop, IsCommutative and IsMoufangLoop );
|
20
gap/convert.gd
Normal file
20
gap/convert.gd
Normal file
|
@ -0,0 +1,20 @@
|
|||
############################################################################
|
||||
##
|
||||
#W convert.gd Converting between numerical bases [loops]
|
||||
##
|
||||
#H @(#)$Id: convert.gd, v 3.3.0 2016/10/19 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
# auxiliary
|
||||
DeclareOperation( "LOOPS_DigitToChar", [ IsInt ] );
|
||||
DeclareOperation( "LOOPS_CharToDigit", [ IsChar ] );
|
||||
DeclareOperation( "LOOPS_EncodeCayleyTable", [ IsList ] );
|
||||
DeclareOperation( "LOOPS_DecodeCayleyTable", [ IsString ] );
|
||||
DeclareOperation( "LOOPS_ConvertToDecimal", [ IsString, IsInt ] );
|
||||
DeclareGlobalFunction( "LOOPS_ConvertFromDecimal" ); # has variable number of arguments
|
||||
DeclareGlobalFunction( "LOOPS_ConvertBase" ); # has variable number of arguments
|
||||
DeclareOperation( "LOOPS_EncodeCocycle", [ IsList, IsList ] );
|
||||
DeclareOperation( "LOOPS_DecodeCocycle", [ IsList, IsList ] );
|
272
gap/convert.gi
Normal file
272
gap/convert.gi
Normal file
|
@ -0,0 +1,272 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W convert.gi Conversions, encoding and decoding [loops]
|
||||
##
|
||||
#H @(#)$Id: convert.gi, v 3.0.0 2015/06/02 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
############################################################################
|
||||
##
|
||||
## CONVERSIONS BETWEEN CHARACTERS AND "DIGITS" IN BASE 91.
|
||||
## -------------------------------------------------------------------------
|
||||
##
|
||||
## Suitable characters to represent digits in GAP are found in the interval
|
||||
## CHAR_INT(35)..CHAR_INT(126), except for CHAR_INT[92] = '\\'.
|
||||
## This leads to natural base 91 = 126-35.
|
||||
## To implement binary, decimal and hexadecimal numbers naturally,
|
||||
## we reorder the suitable 91 characters to read as follows:
|
||||
|
||||
BindGlobal(
|
||||
"LOOPS_conversion_string",
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%&'()*+,-./:;<=>?@[]^_`{|}~"
|
||||
);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LOOPS_DigitToChar( d )
|
||||
##
|
||||
## Converts an integer <d> in the range [0..90] to a character.
|
||||
|
||||
InstallMethod( LOOPS_DigitToChar, "for integer",
|
||||
[ IsInt ],
|
||||
function( d )
|
||||
return LOOPS_conversion_string[ d + 1 ];
|
||||
end );
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LOOPS_CharToDigit( c )
|
||||
##
|
||||
## Converts a character <c> to an integer in the range [0..90].
|
||||
|
||||
InstallMethod( LOOPS_CharToDigit, "for character",
|
||||
[ IsChar ],
|
||||
function( c )
|
||||
return Position( LOOPS_conversion_string, c ) - 1;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LOOPS_EncodeCayleyTable( ct )
|
||||
#O LOOPS_DecodeCayleyTable( str )
|
||||
##
|
||||
## Auxiliary routines for encoding and decoding of loop Cayley tables
|
||||
## of order in [1..91], using characters instead of numbers.
|
||||
## When n>2, first row and column are ignored because they can be
|
||||
## reconstructed from the rest of the table.
|
||||
## When <ct> is commutative, only "half" of the table is saved.
|
||||
## This can be detected from <str> and decoded appropriately.
|
||||
|
||||
InstallMethod( LOOPS_EncodeCayleyTable, "for list",
|
||||
[ IsList ],
|
||||
function( ct )
|
||||
local n, ret, start, i, j;
|
||||
n := Length( ct );
|
||||
ret := "";
|
||||
start := 2;
|
||||
# small cases
|
||||
if n<3 then
|
||||
start := 1;
|
||||
fi;
|
||||
if n>91 then
|
||||
Error("LOOPS: Encoding of Cayley tables is supported only for order less than 92.");
|
||||
fi;
|
||||
ret := "";
|
||||
if ct <> TransposedMat( ct ) then # general case
|
||||
for i in [start..n] do for j in [start..n] do
|
||||
Add(ret, LOOPS_DigitToChar( ct[i][j]-1 ) );
|
||||
od; od;
|
||||
else # commutative case
|
||||
for i in [start..n] do for j in [i..n] do
|
||||
Add(ret, LOOPS_DigitToChar( ct[i][j]-1 ) );
|
||||
od; od;
|
||||
fi;
|
||||
return ret;
|
||||
end );
|
||||
|
||||
InstallMethod( LOOPS_DecodeCayleyTable, "for string",
|
||||
[ IsString ],
|
||||
function( str )
|
||||
local symbols, n, pos, ret, i, j;
|
||||
symbols := Set( Set( str ), c -> LOOPS_CharToDigit(c) + 1 );
|
||||
if Length(str)=1 then
|
||||
return [[LOOPS_CharToDigit(str[1])+1]];
|
||||
fi;
|
||||
if Length(str)=3 and Size(symbols)=2 then # n=2 (automatically commutative)
|
||||
return [
|
||||
[LOOPS_CharToDigit(str[1])+1, LOOPS_CharToDigit(str[2])+1],
|
||||
[LOOPS_CharToDigit(str[2])+1, LOOPS_CharToDigit(str[3])+1]
|
||||
];
|
||||
fi;
|
||||
# n>2
|
||||
n := Size( symbols ); # number of distinct symbols
|
||||
if n>91 then
|
||||
Error("LOOPS: Decoding of Cayley tables is supported only for order less than 92.");
|
||||
fi;
|
||||
ret := List([1..n], i -> List( [1..n], j -> -1 ) );
|
||||
# the table except for the first row and first column
|
||||
pos := 1;
|
||||
if Length(str) = (n-1)^2 then # noncommutative case
|
||||
for i in [2..n] do for j in [2..n] do
|
||||
ret[i][j] := LOOPS_CharToDigit( str[pos] ) + 1;
|
||||
pos := pos+1;
|
||||
od; od;
|
||||
else # commutative case
|
||||
for i in [2..n] do for j in [i..n] do
|
||||
ret[i][j] := LOOPS_CharToDigit( str[pos] ) + 1;
|
||||
ret[j][i] := ret[i][j];
|
||||
pos := pos + 1;
|
||||
od; od;
|
||||
fi;
|
||||
# determining the first row and first column
|
||||
for i in [2..n] do
|
||||
ret[i][1] := Difference( symbols, ret[i] )[1];
|
||||
ret[1][i] := Difference( symbols, List( [2..n], j->ret[j][i] ) )[1];
|
||||
od;
|
||||
ret[1][1] := Difference( symbols, ret[1] )[1];
|
||||
return ret;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LOOPS_ConvertToDecimal( s, n )
|
||||
##
|
||||
## Converts an <n>-ary number represented by a string <s> to a decimal
|
||||
## number represented as integer.
|
||||
|
||||
InstallMethod( LOOPS_ConvertToDecimal, "for string and integer",
|
||||
[ IsString, IsInt ],
|
||||
function( s, n )
|
||||
local ls, d, i;
|
||||
ls := Length( s );
|
||||
d := 0;
|
||||
for i in [1..ls] do
|
||||
d := d + LOOPS_CharToDigit(s[i])*(n^(ls-i));
|
||||
od;
|
||||
return d;
|
||||
end );
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ConvertFromDecimal( arg )
|
||||
##
|
||||
## arg = [ <d>, <m>, optional <k> ]
|
||||
## Converts a decimal number <d> to a number in base <m>.
|
||||
## Optional parameter <k> is the minimal required number of "digits"
|
||||
## of the output in new base <m>, including zeros at the beginning
|
||||
## Returns the corresponding number as a string in base <m> (of length at least <k>).
|
||||
|
||||
InstallGlobalFunction( LOOPS_ConvertFromDecimal, function( arg )
|
||||
local d, m, s, r, prefix_s;
|
||||
d := arg[1];
|
||||
m := arg[2];
|
||||
s := "";
|
||||
while d>0 do
|
||||
r := d mod m;
|
||||
Add( s, LOOPS_DigitToChar( r ) );
|
||||
d := (d-r)/m;
|
||||
od;
|
||||
s := Reversed( s );
|
||||
if Length( arg ) > 2 then
|
||||
prefix_s := List( [1..arg[3]-Length(s)], i -> LOOPS_DigitToChar( 0 ) );
|
||||
s := Concatenation( prefix_s, s );
|
||||
fi;
|
||||
return s;
|
||||
end );
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ConvertBase( arg )
|
||||
##
|
||||
## arg = [ s, n, m, optional k ]
|
||||
## s is a string that represents a number in base n
|
||||
## m is a new base
|
||||
## optional parameter k is the minimal required number of "digits"
|
||||
## of the output in new base m, including zeros at the beginning
|
||||
## Returns the corresponding number in base m (with at least k digits).
|
||||
|
||||
InstallGlobalFunction( LOOPS_ConvertBase, function( arg )
|
||||
local d;
|
||||
d := LOOPS_ConvertToDecimal( arg[1], arg[2] );
|
||||
if Length(arg)>3 then
|
||||
return LOOPS_ConvertFromDecimal( d, arg[3], arg[4] );
|
||||
fi;
|
||||
return LOOPS_ConvertFromDecimal( d, arg[3] );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LOOPS_EncodeCocycle( coc, values )
|
||||
#O LOOPS_DecodeCocycle( ecoc, values )
|
||||
##
|
||||
## Given loops F and K, a cocycle is a mapping from F x F to K.
|
||||
## Let n=|F| and b=|K|.
|
||||
## Cocycles are represented as n x n arrays with entries in a b-element set.
|
||||
##
|
||||
## These are auxiliary routines for encoding and decoding of cocycles.
|
||||
## <coc> is a cocycle, an n x n matrix
|
||||
## <values> is a set of cardinality b
|
||||
## every entry in <coc> must lie in <values> but not vice versa
|
||||
## <ecoc> is an encoded cocycle, a list of the form [n, is_comm, data],
|
||||
## where n=|F|, is_comm is true iff <coc> is commutative, and
|
||||
## data is an encoded cocycle table
|
||||
## Note: The encoded cocycle has default values in [0..b-1]. The argument
|
||||
## <values> can be used to populate the cocycle with other values.
|
||||
|
||||
InstallMethod( LOOPS_EncodeCocycle, "for two lists",
|
||||
[ IsList, IsList ],
|
||||
function( coc, values )
|
||||
local b, n, is_commutative, ret, i, start, j;
|
||||
b := Length( values );
|
||||
if not b < 92 then
|
||||
Error("LOOPS: Encoding of cocycles is supported only for loops of order less than 92.");
|
||||
fi;
|
||||
n := Length(coc);
|
||||
is_commutative := coc = TransposedMat(coc);
|
||||
ret := [ n, is_commutative, "" ];
|
||||
for i in [1..n] do
|
||||
start := 1;
|
||||
if is_commutative then start := i; fi;
|
||||
for j in [start..n] do
|
||||
Add( ret[3], LOOPS_DigitToChar( Position( values, coc[i][j] ) - 1 ) );
|
||||
od;
|
||||
od;
|
||||
ret[3] := LOOPS_ConvertBase( ret[3], b, 91 );
|
||||
return ret;
|
||||
end);
|
||||
|
||||
InstallMethod( LOOPS_DecodeCocycle, "for two lists",
|
||||
[ IsList, IsList ],
|
||||
function( ecoc, values )
|
||||
local n, is_commutative, b, s, coc, pos, i, j;
|
||||
n := ecoc[1];
|
||||
is_commutative := ecoc[2];
|
||||
b := Length( values );
|
||||
if is_commutative then
|
||||
s := LOOPS_ConvertBase( ecoc[3], 91, b, n*(n+1)/2 );
|
||||
else
|
||||
s := LOOPS_ConvertBase( ecoc[3], 91, b, n^2 );
|
||||
fi;
|
||||
coc := List([1..n], i -> [1..n]);
|
||||
pos := 1;
|
||||
if is_commutative then
|
||||
for i in [1..n] do for j in [i..n] do
|
||||
coc[i][j] := values[ LOOPS_CharToDigit( s[pos] ) + 1 ];
|
||||
coc[j][i] := coc[i][j];
|
||||
pos := pos + 1;
|
||||
od; od;
|
||||
else
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
coc[i][j] := values[ LOOPS_CharToDigit( s[pos] ) + 1 ];
|
||||
pos := pos + 1;
|
||||
od; od;
|
||||
fi;
|
||||
return coc;
|
||||
end);
|
||||
|
126
gap/core_methods.gd
Normal file
126
gap/core_methods.gd
Normal file
|
@ -0,0 +1,126 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W core_methods.gd Most common structural methods [loops]
|
||||
##
|
||||
#H @(#)$Id: core_methods.gd, v 3.3.0 2016/09/21 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## GENERATORS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareSynonymAttr( "GeneratorsOfQuasigroup", GeneratorsOfMagma );
|
||||
DeclareSynonymAttr( "GeneratorsOfLoop", GeneratorsOfQuasigroup );
|
||||
# attribute GeneratorsSmallest already declared for groups
|
||||
# attribite SmallGeneratingSet already declared for groups
|
||||
|
||||
#############################################################################
|
||||
## SECTIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareAttribute( "LeftSection", IsQuasigroup );
|
||||
DeclareAttribute( "RightSection", IsQuasigroup );
|
||||
DeclareOperation( "LeftTranslation",
|
||||
[ IsQuasigroup, IsQuasigroupElement ] );
|
||||
DeclareOperation( "RightTranslation",
|
||||
[ IsQuasigroup, IsQuasigroupElement ] );
|
||||
|
||||
#############################################################################
|
||||
## MULTIPLICATION GROUPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareAttribute( "LeftMultiplicationGroup", IsQuasigroup );
|
||||
DeclareAttribute( "RightMultiplicationGroup", IsQuasigroup );
|
||||
DeclareAttribute( "MultiplicationGroup", IsQuasigroup );
|
||||
DeclareOperation( "RelativeRightMultiplicationGroup",
|
||||
[ IsQuasigroup, IsQuasigroup ] );
|
||||
DeclareOperation( "RelativeLeftMultiplicationGroup",
|
||||
[ IsQuasigroup, IsQuasigroup ] );
|
||||
DeclareOperation( "RelativeMultiplicationGroup",
|
||||
[ IsQuasigroup, IsQuasigroup ] );
|
||||
|
||||
#############################################################################
|
||||
## INNER MAPPING GROUPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "LeftInnerMapping", [ IsLoop, IsLoopElement, IsLoopElement ] );
|
||||
DeclareOperation( "RightInnerMapping", [ IsLoop, IsLoopElement, IsLoopElement ] );
|
||||
DeclareOperation( "MiddleInnerMapping", [ IsLoop, IsLoopElement ] );
|
||||
DeclareAttribute( "InnerMappingGroup", IsLoop );
|
||||
DeclareAttribute( "LeftInnerMappingGroup", IsLoop );
|
||||
DeclareAttribute( "MiddleInnerMappingGroup", IsLoop );
|
||||
DeclareAttribute( "RightInnerMappingGroup", IsLoop );
|
||||
|
||||
#############################################################################
|
||||
## SUBQUASIGROUPS AND SUBLOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "PosInParent", [ IsCollection ] );
|
||||
# Position already declared in GAP
|
||||
DeclareOperation( "SubquasigroupNC", [ IsQuasigroup, IsSet ] );
|
||||
DeclareOperation( "Subquasigroup", [ IsQuasigroup, IsList ] );
|
||||
DeclareOperation( "Subloop", [ IsLoop, IsList ] );
|
||||
DeclareOperation( "IsSubquasigroup", [ IsQuasigroup, IsQuasigroup ] );
|
||||
DeclareOperation( "IsSubloop", [ IsLoop, IsLoop ] );
|
||||
DeclareOperation( "AllSubquasigroups", [ IsQuasigroup ] );
|
||||
DeclareOperation( "AllSubloops", [ IsLoop ] );
|
||||
# RightCosets already declared in GAP
|
||||
DeclareOperation( "RightTransversal", [ IsLoop, IsLoop ] );
|
||||
|
||||
#############################################################################
|
||||
## NUCLEUS, COMMUTANT, CENTER
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareAttribute( "LeftNucleus", IsQuasigroup );
|
||||
DeclareAttribute( "RightNucleus", IsQuasigroup );
|
||||
DeclareAttribute( "MiddleNucleus", IsQuasigroup );
|
||||
DeclareAttribute( "Nuc", IsQuasigroup );
|
||||
DeclareSynonymAttr( "NucleusOfLoop", Nuc );
|
||||
DeclareSynonymAttr( "NucleusOfQuasigroup", Nuc );
|
||||
DeclareAttribute( "Commutant", IsQuasigroup );
|
||||
DeclareAttribute( "Center", IsQuasigroup );
|
||||
DeclareAttribute( "AssociatorSubloop", IsLoop );
|
||||
|
||||
#############################################################################
|
||||
## EXPONENT
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareAttribute( "Exponent", IsLoop );
|
||||
|
||||
#############################################################################
|
||||
## NORMALITY
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
InParentFOA( "IsNormal", IsLoop, IsLoop, DeclareProperty );
|
||||
InParentFOA( "NormalClosure", IsLoop, IsLoop, DeclareAttribute );
|
||||
# IsSimple is already declared for groups
|
||||
|
||||
#############################################################################
|
||||
## FACTOR LOOP
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "FactorLoop", [ IsLoop, IsLoop ] );
|
||||
DeclareOperation( "NaturalHomomorphismByNormalSubloop", [ IsLoop, IsLoop ] );
|
||||
|
||||
#############################################################################
|
||||
## NILPOTENCY
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareAttribute( "NilpotencyClassOfLoop", IsLoop );
|
||||
# IsNilpotent is already declared for groups
|
||||
DeclareAttribute( "IsStronglyNilpotent", IsLoop );
|
||||
# UpperCentralSeries already declared for groups.
|
||||
# LowerCentralSeries already declared for groups.
|
||||
|
||||
#############################################################################
|
||||
## SOLVABILITY
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
# IsSolvable is already declared for groups
|
||||
DeclareAttribute( "DerivedSubloop", IsLoop );
|
||||
# DerivedLength already declared for groups
|
||||
DeclareAttribute( "FrattiniSubloop", IsLoop );
|
||||
# FrattinifactorSize already declared for groups
|
1124
gap/core_methods.gi
Normal file
1124
gap/core_methods.gi
Normal file
File diff suppressed because it is too large
Load diff
37
gap/elements.gd
Normal file
37
gap/elements.gd
Normal file
|
@ -0,0 +1,37 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W elements.gd Elements and basic arithmetic operations [loops]
|
||||
##
|
||||
#H @(#)$Id: quasigroups.gd, v 2.0.0 2008/01/21 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## DIVISION
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "LeftDivision",
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ] );
|
||||
DeclareOperation( "RightDivision",
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ] );
|
||||
DeclareOperation( "LeftDivisionCayleyTable", [ IsQuasigroup ] );
|
||||
DeclareOperation( "RightDivisionCayleyTable", [ IsQuasigroup ] );
|
||||
|
||||
#############################################################################
|
||||
## POWERS AND INVERSES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareAttribute( "One", IsLoopElement );
|
||||
DeclareAttribute( "RightInverse", IsLoopElement );
|
||||
DeclareAttribute( "LeftInverse", IsLoopElement );
|
||||
|
||||
#############################################################################
|
||||
## ASSOCIATORS AND COMMUTATORS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "Associator",
|
||||
[ IsQuasigroupElement, IsQuasigroupElement, IsQuasigroupElement ] );
|
||||
DeclareOperation( "Commutator",
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ] );
|
294
gap/elements.gi
Normal file
294
gap/elements.gi
Normal file
|
@ -0,0 +1,294 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W elements.gd Elements and basic arithmetic operations [loops]
|
||||
##
|
||||
#H @(#)$Id: quasigroups.gd, v 3.0.0 2015/06/12 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## DISPLAYING AND COMPARING ELEMENTS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
InstallMethod( PrintObj, "for a quasigroup element",
|
||||
[ IsQuasigroupElement ],
|
||||
function( obj )
|
||||
local F;
|
||||
F := FamilyObj( obj );
|
||||
Print( F!.names, obj![ 1 ] );
|
||||
end );
|
||||
|
||||
InstallMethod( PrintObj, "for a loop element",
|
||||
[ IsLoopElement ],
|
||||
function( obj )
|
||||
local F;
|
||||
F := FamilyObj( obj );
|
||||
Print( F!.names, obj![ 1 ] );
|
||||
end );
|
||||
|
||||
InstallMethod( \=, "for two elements of a quasigroup",
|
||||
IsIdenticalObj,
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( x, y )
|
||||
return FamilyObj( x ) = FamilyObj( y ) and x![ 1 ] = y![ 1 ];
|
||||
end );
|
||||
|
||||
InstallMethod( \<, "for two elements of a quasigroup",
|
||||
IsIdenticalObj,
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( x, y )
|
||||
return FamilyObj( x ) = FamilyObj( y ) and x![ 1 ] < y![ 1 ];
|
||||
end );
|
||||
|
||||
InstallMethod( \., "for quasigroup and positive integer",
|
||||
[ IsQuasigroup, IsPosInt ],
|
||||
function( Q, k )
|
||||
return GeneratorsOfQuasigroup( Q )[ Int( NameRNam( k ) ) ];
|
||||
end );
|
||||
|
||||
|
||||
#############################################################################
|
||||
## MULTIPLICATION
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
## Multiplication without parentheses is evaluated from left to right,
|
||||
## i.e., a*b*c=(a*b)*c. Powers use binary decomposition.
|
||||
InstallMethod( \*, "for two quasigroup elements",
|
||||
IsIdenticalObj,
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( x, y )
|
||||
local F;
|
||||
F := FamilyObj( x );
|
||||
return F!.set[ F!.cayleyTable[ x![ 1 ] ][ y![ 1 ] ] ];
|
||||
end );
|
||||
|
||||
InstallOtherMethod( \*, "for a QuasigroupElement and a list",
|
||||
[ IsQuasigroupElement , IsList ],
|
||||
function( x, ly )
|
||||
return List( ly, y -> x*y );
|
||||
end );
|
||||
|
||||
InstallOtherMethod( \*, "for a list and a QuasigroupElement",
|
||||
[ IsList, IsQuasigroupElement ],
|
||||
function( lx, y )
|
||||
return List( lx, x -> x*y );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## DIVISION
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
## z=x/y means zy=x
|
||||
InstallMethod( RightDivision, "for two quasigroup elements",
|
||||
IsIdenticalObj,
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( x, y )
|
||||
local F, ycol;
|
||||
F := FamilyObj( x );
|
||||
ycol := F!.cayleyTable{ [ 1 .. F!.size ] }[ y![ 1 ] ];
|
||||
return F!.set[ Position( ycol, x![ 1 ] ) ];
|
||||
end );
|
||||
|
||||
InstallOtherMethod( RightDivision,
|
||||
"for a list and a quasigroup element",
|
||||
[ IsList, IsQuasigroupElement ],
|
||||
0,
|
||||
function( lx, y )
|
||||
return List( lx, x -> RightDivision(x, y) );
|
||||
end );
|
||||
|
||||
InstallOtherMethod( RightDivision,
|
||||
"for a quasigroup element and a list",
|
||||
[ IsQuasigroupElement, IsList ],
|
||||
0,
|
||||
function( x, ly )
|
||||
return List( ly, y -> RightDivision(x, y) );
|
||||
end );
|
||||
|
||||
InstallOtherMethod( \/,
|
||||
"for two elements of a quasigroup",
|
||||
IsIdenticalObj,
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ],
|
||||
0,
|
||||
function( x, y )
|
||||
return RightDivision( x, y );
|
||||
end );
|
||||
|
||||
InstallOtherMethod( \/,
|
||||
"for a list and a quasigroup element",
|
||||
[ IsList, IsQuasigroupElement ],
|
||||
0,
|
||||
function( lx, y )
|
||||
return List( lx, x -> RightDivision(x, y) );
|
||||
end );
|
||||
|
||||
InstallOtherMethod( \/,
|
||||
"for a quasigroup element and a list",
|
||||
[ IsQuasigroupElement, IsList ],
|
||||
0,
|
||||
function( x, ly )
|
||||
return List( ly, y -> RightDivision(x, y) );
|
||||
end );
|
||||
|
||||
## z = x\y means xz=y
|
||||
InstallMethod( LeftDivision, "for two quasigroup elements",
|
||||
IsIdenticalObj,
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( x, y )
|
||||
local F;
|
||||
F := FamilyObj( x );
|
||||
return F!.set[ Position( F!.cayleyTable[ x![ 1 ] ], y![ 1 ] ) ];
|
||||
end );
|
||||
|
||||
InstallOtherMethod( LeftDivision,
|
||||
"for a list and a quasigroup element",
|
||||
[ IsList, IsQuasigroupElement ],
|
||||
0,
|
||||
function( lx, y )
|
||||
return List( lx, x -> LeftDivision(x, y) );
|
||||
end );
|
||||
|
||||
InstallOtherMethod( LeftDivision,
|
||||
"for a quasigroup element and a list",
|
||||
[ IsQuasigroupElement, IsList ],
|
||||
0,
|
||||
function( x, ly )
|
||||
return List( ly, y -> LeftDivision(x, y) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LeftDivisionCayleyTable( Q )
|
||||
##
|
||||
## Returns the Cayley table for the operation x\y of the quasigroup <Q>.
|
||||
|
||||
InstallMethod( LeftDivisionCayleyTable, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
# This would be slow using LeftDivision.
|
||||
# Must take care of the fact that entries in ct are not necessarily 1..n
|
||||
local n, ct, pos_in_Q, pos_in_parent, i, t, j;
|
||||
n := Size( Q );
|
||||
ct := CayleyTable( Q );
|
||||
pos_in_Q := 0*[ 1..Size( Parent( Q ) ) ];
|
||||
pos_in_parent := PosInParent( Q );
|
||||
for i in pos_in_parent do
|
||||
pos_in_Q[ i ] := Position( pos_in_parent, i );
|
||||
od;
|
||||
t := List( [1..n], i -> 0*[1..n] );
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
t[ i ][ pos_in_Q[ ct[ i ][ j ] ] ] := pos_in_parent[ j ];
|
||||
od; od;
|
||||
return t;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O RightDivisionCayleyTable( Q )
|
||||
##
|
||||
## Returns the Cayley table for the operation x/y of the quasigroup <Q>.
|
||||
|
||||
InstallMethod( RightDivisionCayleyTable, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
# This would be slow using RightDivision.
|
||||
# Must take care of the fact that entries in ct are not necessarily 1..n
|
||||
local n, ct, pos_in_Q, pos_in_parent, i, t, j;
|
||||
n := Size( Q );
|
||||
ct := CayleyTable( Q );
|
||||
pos_in_Q := 0*[ 1..Size( Parent( Q ) ) ];
|
||||
pos_in_parent := PosInParent( Q );
|
||||
for i in pos_in_parent do
|
||||
pos_in_Q[ i ] := Position( pos_in_parent, i );
|
||||
od;
|
||||
t := List( [1..n], i -> 0*[1..n] );
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
t[ pos_in_Q[ ct[ i ][ j ] ] ][ j ] := pos_in_parent[ i ];
|
||||
od; od;
|
||||
return t;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## POWERS AND INVERSES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
InstallMethod( \^, "for a quasigroup element and a permutation",
|
||||
[ IsQuasigroupElement, IsPerm ],
|
||||
function( x, p )
|
||||
local F;
|
||||
F := FamilyObj( x );
|
||||
return F!.set[ ( x![ 1 ] )^p ];
|
||||
end );
|
||||
|
||||
InstallMethod( OneOp, "for loop elements",
|
||||
[ IsLoopElement ],
|
||||
function( x )
|
||||
local F;
|
||||
F := FamilyObj( x );
|
||||
return F!.set[ 1 ];
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#A LeftInverse( <x> )
|
||||
##
|
||||
## If <x> is a loop element, returns the left inverse of <x>
|
||||
|
||||
InstallMethod( LeftInverse, "for loop elements",
|
||||
[ IsLoopElement ],
|
||||
x -> RightDivision( One( x ), x )
|
||||
);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#A RightInverse( <x> )
|
||||
##
|
||||
## If <x> is a loop element, returns the left inverse of <x>
|
||||
|
||||
InstallMethod( RightInverse, "for loop elements",
|
||||
[ IsLoopElement ],
|
||||
x -> LeftDivision( x, One( x ) )
|
||||
);
|
||||
|
||||
InstallMethod( InverseOp, "for loop elements",
|
||||
[ IsLoopElement ],
|
||||
function( x )
|
||||
local y;
|
||||
y := RightInverse( x );
|
||||
if y = LeftInverse( x ) then return y; fi;
|
||||
return fail;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## ASSOCIATORS AND COMMUTATORS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O Associator( x, y , z )
|
||||
##
|
||||
## When <x>, <y>, <z> are elements of a quasigroup Q, returns the
|
||||
## associator of <x>, <y>, <z>, i.e., the unique element u satisfying
|
||||
## (xy)z = (x(yz))u.
|
||||
|
||||
InstallMethod( Associator, "for three quasigroup elements",
|
||||
[ IsQuasigroupElement, IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( x, y, z )
|
||||
return LeftDivision( x*(y*z), (x*y)*z );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O Commutator( x, y )
|
||||
##
|
||||
## When <x>, <y> are elements of a quasigroup Q, returns the
|
||||
## commutator of <x>, <y>, i.e., the unique element u satisfying
|
||||
## (xy) = (yx)u.
|
||||
|
||||
InstallMethod( Commutator, "for two quasigroup elements",
|
||||
[ IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( x, y )
|
||||
return LeftDivision( y*x, x*y );
|
||||
end);
|
54
gap/examples.gd
Normal file
54
gap/examples.gd
Normal file
|
@ -0,0 +1,54 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W examples.gd Examples [loops]
|
||||
##
|
||||
#H @(#)$Id: examples.gd, v 3.1.0 2015/09/23 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## DISPLAYING INFORMATION ABOUT A LIBRARY
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "DisplayLibraryInfo" );
|
||||
|
||||
#############################################################################
|
||||
## READING LOOP FROM THE LIBRARY - GENERIC METHOD
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "LibraryLoop" );
|
||||
|
||||
# up to isomorphism
|
||||
|
||||
DeclareGlobalFunction( "LeftBolLoop" );
|
||||
DeclareGlobalFunction( "RightBolLoop" );
|
||||
DeclareGlobalFunction( "MoufangLoop" );
|
||||
DeclareGlobalFunction( "PaigeLoop" );
|
||||
DeclareGlobalFunction( "CodeLoop" );
|
||||
DeclareGlobalFunction( "SteinerLoop" );
|
||||
DeclareGlobalFunction( "CCLoop" );
|
||||
DeclareGlobalFunction( "ConjugacyClosedLoop" );
|
||||
DeclareGlobalFunction( "RCCLoop" );
|
||||
DeclareGlobalFunction( "RightConjugacyClosedLoop" );
|
||||
DeclareGlobalFunction( "LCCLoop" );
|
||||
DeclareGlobalFunction( "LeftConjugacyClosedLoop" );
|
||||
DeclareGlobalFunction( "SmallLoop" );
|
||||
DeclareGlobalFunction( "InterestingLoop" );
|
||||
DeclareGlobalFunction( "NilpotentLoop" );
|
||||
DeclareGlobalFunction( "AutomorphicLoop" );
|
||||
|
||||
# up to isotopism
|
||||
|
||||
DeclareGlobalFunction( "ItpSmallLoop" );
|
||||
|
||||
# auxiliary
|
||||
DeclareGlobalFunction( "LOOPS_LibraryByName" );
|
||||
DeclareGlobalFunction( "LOOPS_SmallestNonsquare" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateLeftBolLoopPQ" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateLeftBolLoop" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateMoufangLoop" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateSteinerLoop" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateRCCLoop" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateCCLoop" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateNilpotentLoop" );
|
||||
DeclareGlobalFunction( "LOOPS_ActivateAutomorphicLoop" );
|
836
gap/examples.gi
Normal file
836
gap/examples.gi
Normal file
|
@ -0,0 +1,836 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W examples.gi Examples [loops]
|
||||
##
|
||||
#H @(#)$Id: examples.gi, v 3.3.0 2016/10/19 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## Binding global variable LOOPS_aux
|
||||
##
|
||||
## The variable is used for temporary storage throughout the package.
|
||||
## We therefore do not want the variable to be read only.
|
||||
|
||||
LOOPS_aux := [];
|
||||
|
||||
#############################################################################
|
||||
## READING DATA
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
# up to isomorphism
|
||||
ReadPackage("loops", "data/leftbol.tbl"); # left Bol loops
|
||||
ReadPackage("loops", "data/moufang.tbl"); # Moufang loops
|
||||
ReadPackage("loops", "data/paige.tbl"); # Paige loops
|
||||
ReadPackage("loops", "data/code.tbl"); # code loops
|
||||
ReadPackage("loops", "data/steiner.tbl"); # Steiner loops
|
||||
ReadPackage("loops", "data/cc.tbl"); # CC loops
|
||||
ReadPackage("loops", "data/rcc.tbl"); # RCC loops (more is read upon calling RCCLoop(n,m) for the first time
|
||||
ReadPackage("loops", "data/small.tbl"); # small loops
|
||||
ReadPackage("loops", "data/interesting.tbl"); # interesting loops
|
||||
ReadPackage("loops", "data/nilpotent.tbl"); # nilpotent loops
|
||||
ReadPackage("loops", "data/automorphic.tbl"); # automorphic loops
|
||||
|
||||
# up to isotopism
|
||||
ReadPackage("loops", "data/itp_small.tbl"); # small loops up to isotopism
|
||||
|
||||
#############################################################################
|
||||
## DISPLAYING INFORMATION ABOUT A LIBRARY
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_LibraryByName( name )
|
||||
##
|
||||
## Auxiliary. Returns the library corresponding to <name>.
|
||||
|
||||
InstallGlobalFunction( LOOPS_LibraryByName,
|
||||
function( name )
|
||||
#up to isomorphism
|
||||
if name = "left Bol" then return LOOPS_left_bol_data;
|
||||
elif name = "Moufang" then return LOOPS_moufang_data;
|
||||
elif name = "Paige" then return LOOPS_paige_data;
|
||||
elif name = "code" then return LOOPS_code_data;
|
||||
elif name = "Steiner" then return LOOPS_steiner_data;
|
||||
elif name = "CC" then return LOOPS_cc_data;
|
||||
elif name = "RCC" then return LOOPS_rcc_data;
|
||||
elif name = "small" then return LOOPS_small_data;
|
||||
elif name = "interesting" then return LOOPS_interesting_data;
|
||||
elif name = "nilpotent" then return LOOPS_nilpotent_data;
|
||||
elif name = "automorphic" then return LOOPS_automorphic_data;
|
||||
#up to isotopism
|
||||
elif name = "itp small" then return LOOPS_itp_small_data;
|
||||
fi;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F DisplayLibraryInfo( name )
|
||||
##
|
||||
## Display information about library named <name>.
|
||||
|
||||
InstallGlobalFunction( DisplayLibraryInfo, function( name )
|
||||
local s, lib, k;
|
||||
# up to isomorphism
|
||||
if name = "left Bol" then
|
||||
s := "The library contains all nonassociative left Bol loops of order less than 17\nand all nonassociative left Bol loops of order p*q, where p>q>2 are primes.";
|
||||
elif name = "right Bol" then
|
||||
s := "The library contains all nonassociative right Bol loops of order less than 17\nand all nonassociative left Bol loops of order p*q, where p>q>2 are primes.";
|
||||
name := "left Bol"; # using dual data
|
||||
elif name = "Moufang" then
|
||||
s := "The library contains all nonassociative Moufang loops \nof order less than 65, and all nonassociative Moufang \nloops of order 81 and 243.";
|
||||
elif name = "Paige" then
|
||||
s := "The library contains the smallest nonassociative finite \nsimple Moufang loop.";
|
||||
elif name = "code" then
|
||||
s := "The library contains all nonassociative even code loops \nof order less than 65.";
|
||||
elif name = "Steiner" then
|
||||
s := "The library contains all nonassociative Steiner loops \nof order less or equal to 16. It also contains the \nassociative Steiner loops of order 4 and 8.";
|
||||
elif name = "CC" then
|
||||
s := "The library contains all nonassociative CC loops of order less than 28 \nand all nonassociative CC loops of order p^2 and 2*p for any odd prime p.";
|
||||
elif name = "RCC" then
|
||||
s := "The library contains all nonassociative RCC loops of order less than 28.";
|
||||
elif name = "LCC" then
|
||||
s := "The library contains all nonassociative LCC loops of order less than 28.";
|
||||
name := "RCC"; # using dual data
|
||||
elif name = "small" then
|
||||
s := "The library contains all nonassociative loops of order less than 7.";
|
||||
elif name = "interesting" then
|
||||
s := "The library contains a few interesting loops.";
|
||||
elif name = "nilpotent" then
|
||||
s := "The library contains all nonassociative nilpotent loops \nof order less than 12.";
|
||||
elif name = "automorphic" then
|
||||
s := "The library contains:\n";
|
||||
s := Concatenation(s," - all nonassociative automorphic loops of order less than 16,\n");
|
||||
s := Concatenation(s," - all commutative automorphic loops of order 3, 9, 27, 81,\n");
|
||||
s := Concatenation(s," - all commutative automorphic loops of order 243 that are central\n");
|
||||
s := Concatenation(s," extensions of Z_3 by F, where F is not the elem. ab. 3-group.\n");
|
||||
s := Concatenation(s,"Note: Abelian groups are included among the commutative loops.");
|
||||
# up to isotopism
|
||||
elif name = "itp small" then
|
||||
s := "The library contains all nonassociative loops of order less than 7 up to isotopism.";
|
||||
else
|
||||
Info( InfoWarning, 1, Concatenation(
|
||||
"The admissible names for loop libraries are: \n",
|
||||
"[ \"left Bol\", \"right Bol\", \"Moufang\", \"Paige\", \"code\", \"Steiner\", \"CC\", \"RCC\", \"LCC\", \"small\", \"itp small\", \"interesting\", \"nilpotent\", \"automorphic\" ]."
|
||||
) );
|
||||
return fail;
|
||||
fi;
|
||||
|
||||
s := Concatenation( s, "\n------\nExtent of the library:" );
|
||||
|
||||
lib := LOOPS_LibraryByName( name );
|
||||
for k in [1..Length( lib[ 1 ] ) ] do
|
||||
if lib[ 2 ][ k ] = 1 then
|
||||
s := Concatenation( s, "\n ", String( lib[ 2 ][ k ] ), " loop of order ", String( lib[ 1 ][ k ] ) );
|
||||
else
|
||||
s := Concatenation( s, "\n ", String( lib[ 2 ][ k ] ), " loops of order ", String( lib[ 1 ][ k ] ) );
|
||||
fi;
|
||||
od;
|
||||
if name = "left Bol" or name = "right Bol" then
|
||||
s := Concatenation( s, "\n (p-q)/2 loops of order p*q for primes p>q>2 such that q divides p-1");
|
||||
s := Concatenation( s, "\n (p-q+2)/2 loops of order p*q for primes p>q>2 such that q divides p+1" );
|
||||
fi;
|
||||
if name = "CC" then
|
||||
s := Concatenation( s, "\n 3 loops of order p^2 for every odd prime p,\n 1 loop of order 2*p for every odd prime p" );
|
||||
fi;
|
||||
s := Concatenation( s, "\n" );
|
||||
Print( s );
|
||||
return true;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## AUXILIARY FUNCTIONS
|
||||
## -------------------------------------------------------------------------
|
||||
## When the data in the database is encoded in some way, we usually
|
||||
## retrieve the loop via function LOOPS_ActivateXLoop, where X is the name
|
||||
## of the class.
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_SmallestNonsquare( p )
|
||||
##
|
||||
## Auxiliary function.
|
||||
## Returns the smallest nonsquare modulo p.
|
||||
|
||||
InstallGlobalFunction( LOOPS_SmallestNonsquare,
|
||||
function( p )
|
||||
local squares, i;
|
||||
squares := Set( [1..p-1], i->i^2 mod p );
|
||||
for i in [2..p-1] do
|
||||
if not i in squares then return i; fi;
|
||||
od;
|
||||
return fail; # will never happen if p>2 is prime
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateLeftBolLoopPQ( p, q, m )
|
||||
##
|
||||
## Auxiliary function for activating left Bol loop of order p*q.
|
||||
## See paper by Kinyon, Nagy and Vojtechovsky.
|
||||
## p>q>2 are primes such that q divides p^2-1, m is an integer in the range [1..(p-q)/2] or [1..(p-q+2)/2]
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateLeftBolLoopPQ,
|
||||
function( p, q, m )
|
||||
local F, omega, lambda, ev, inv_ev, params, t, sqrt_t, final_params, alpha, theta, GFp, ct, i, j, k, l, u, v, w, x, y;
|
||||
F := GF(p^2);
|
||||
omega := PrimitiveRoot( F )^((p^2-1)/q);
|
||||
lambda := omega + omega^(-1);
|
||||
ev := List([0..q-1], j -> omega^j);
|
||||
inv_ev := List([0..q-1], j -> omega^(-j));
|
||||
if IsInt((p-1)/q) then
|
||||
params := List([2..p-1], j->j*One(F)); # GF(p); 0 and 1 correspond to isomorphic nonabelian groups
|
||||
params := Filtered( params, x -> not ((One(F) - x^(-1)) in ev) );
|
||||
else # q divides p+1
|
||||
t := LOOPS_SmallestNonsquare( p );
|
||||
sqrt_t := RootsOfPolynomial( F, X(F,"x")^2 - t )[ 1 ]; # a squre root of t in GF(p^2)
|
||||
params := List([0..p-1], j -> (1/2)*One(F) + j*One(F)*sqrt_t ); # 1/2 + GF(p)\sqrt{t}
|
||||
params := Filtered( params, x -> not ((One(F) - x^(-1)) in ev) );
|
||||
fi;
|
||||
final_params := [];
|
||||
for x in params do
|
||||
if not ( (One(F)-x) in final_params ) then
|
||||
Add( final_params, x );
|
||||
fi;
|
||||
od;
|
||||
alpha := final_params[ m ];
|
||||
theta := alpha*ev + (One(F)-alpha)*inv_ev;
|
||||
theta := List( theta, x -> x^(-1) );
|
||||
GFp := List([0..p-1], j -> j*One(F));
|
||||
theta := List( theta, x -> Position( GFp, x )-1 );
|
||||
# construct the Cayley table according to (a^i b^j)*(a^k b^l) = a^{i+k} b^{ w + (l+w)*theta[k]^{-1}*theta[i+k], where w+w*theta[i] = j
|
||||
ct := List([1..p*q], i -> 0*[1..p*q]);
|
||||
for i in [0..q-1] do for j in [0..p-1] do for k in [0..q-1] do for l in [0..p-1] do
|
||||
u := (i+k) mod q;
|
||||
w := ( j/(1+theta[i+1]) ) mod p;
|
||||
v := ( w + (l+w)*theta[ ((i+k) mod q) + 1 ]/theta[k+1] ) mod p;
|
||||
x := i*p+j+1;
|
||||
y := k*p+l+1;
|
||||
ct[x][y] := u*p+v+1;
|
||||
od; od; od; od;
|
||||
# return the loop
|
||||
return LoopByCayleyTable( ct );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateLeftBolLoop( pos_n, m, case )
|
||||
##
|
||||
## Auxiliary function for activating left Bol loops from the database.
|
||||
## case = [p,q] if it is a left Bol loop of order p*q, with p>q>2 primes such that q divides p^2-1
|
||||
## case = false otherwise
|
||||
## pos_n is meaningless when case = [p,q]
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateLeftBolLoop,
|
||||
function( pos_n, m, case )
|
||||
local rep_m, ct, perm;
|
||||
if not case=false then # p*q
|
||||
return LOOPS_ActivateLeftBolLoopPQ( case[1], case[2], m );
|
||||
fi;
|
||||
# in database
|
||||
rep_m := m;
|
||||
# searching for a Cayley table on which the loop is based
|
||||
while not IsString( LOOPS_left_bol_data[ 3 ][ pos_n ][ rep_m ] ) do
|
||||
rep_m := rep_m - 1;
|
||||
od;
|
||||
if rep_m = m then # loop given by encoded Cayley table
|
||||
ct := LOOPS_DecodeCayleyTable( LOOPS_left_bol_data[ 3 ][ pos_n ][ m ] );
|
||||
else # loop given as an isotope of another loop
|
||||
ct := LOOPS_DecodeCayleyTable( LOOPS_left_bol_data[ 3 ][ pos_n ][ rep_m ] );
|
||||
perm := PermList( ct[ LOOPS_left_bol_data[ 3 ][ pos_n ][ m ] ] );
|
||||
ct := Set( List( ct, row -> OnTuples( row, perm^-1 ) ) );
|
||||
fi;
|
||||
return LoopByCayleyTable( ct );
|
||||
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateMoufangLoop( n, pos_n, m )
|
||||
##
|
||||
## Auxiliary function for activating Moufang loops from the database.
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateMoufangLoop,
|
||||
function( n, pos_n, m )
|
||||
local d, parent_m, parent, S, a, h, e, f, G, ret, x, row, y, b, c, z;
|
||||
|
||||
d := LOOPS_moufang_data[ 3 ][ pos_n ][ m ]; #data
|
||||
|
||||
# orders 81 and 243 are represented as central extensions
|
||||
if n = 81 or n = 243 then
|
||||
return LoopByExtension(
|
||||
IntoLoop( SmallGroup( d[1], d[2] ) ), # K
|
||||
IntoLoop( SmallGroup( d[3], d[4] ) ), # F
|
||||
List([1..d[3]], i -> () ), # trivial action F -> Aut( K )
|
||||
LOOPS_DecodeCocycle( [ n/d[1], false, d[5] ], [1..d[1]] ) # cocycle
|
||||
);
|
||||
fi;
|
||||
|
||||
# all other orders
|
||||
if d[ 1 ] > 0 then # must activate parent first
|
||||
# determine position of parent ( d[1] gives it relative to the class leader )
|
||||
while LOOPS_moufang_data[ 3 ][ pos_n ][ m ][ 1 ] > 0 do
|
||||
m := m - 1;
|
||||
od;
|
||||
m := m + d[ 1 ] - 1;
|
||||
parent := LOOPS_ActivateMoufangLoop( n, pos_n, m );
|
||||
|
||||
if d[ 2 ] = "C" then #cyclic modification
|
||||
S := List( d[ 3 ], i -> Elements( parent )[ i ] );
|
||||
a := Elements( parent )[ d[ 4 ] ];
|
||||
h := Elements( parent )[ d[ 5 ] ];
|
||||
return LoopByCyclicModification( parent, S, a, h );
|
||||
fi;
|
||||
|
||||
if d[ 2 ] = "D" then # dihedral modification
|
||||
S := List( d[ 3 ], i -> Elements( parent )[ i ] );
|
||||
e := Elements( parent )[ d[ 4 ] ];
|
||||
f := Elements( parent )[ d[ 5 ] ];
|
||||
h := Elements( parent )[ d[ 6 ] ];
|
||||
return LoopByDihedralModification( parent, S, e, f, h );
|
||||
fi;
|
||||
fi;
|
||||
|
||||
# no parent;
|
||||
if d[ 2 ]="G" then # loop of type MG2
|
||||
G := AllGroups( d[ 3 ], IsCommutative, false)[ d[ 4 ] ]; #relies on GAP group libraries !!
|
||||
return LoopMG2( G );
|
||||
fi;
|
||||
if d[ 2 ]="T" then # special loop (direct product of an old loop and a cyclic group)
|
||||
parent := MoufangLoop( d[ 3 ][ 1 ], d[ 3 ][ 2 ]);
|
||||
return DirectProduct( parent, CyclicGroup( d[ 3 ][ 3 ] ) );
|
||||
fi;
|
||||
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateSteinerLoop( n, pos_n, m )
|
||||
##
|
||||
## Auxiliary function activating Steiner loops from the database.
|
||||
##
|
||||
## The database LOOPS_steiner_data contains blocks of steiner triple systems.
|
||||
## If the system is on k points, the poitns are labelled 0,...,k-1.
|
||||
## The constructed Steiner loop has elements labelled 1,...,k+1=n
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateSteinerLoop,
|
||||
function( n, pos_n, m )
|
||||
local d, blocks, i, T, i_in, ij_in, j, MyInt;
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F MyInt( s )
|
||||
##
|
||||
## Auxiliary function.
|
||||
## Given a digit or a lower case letter, returns the numerical value, where
|
||||
## a = 10, f=15
|
||||
|
||||
MyInt := function( s )
|
||||
return Position( "0123456789abcdef", s ) - 1;
|
||||
end;
|
||||
|
||||
d := LOOPS_steiner_data[ 3 ][ pos_n ][ m ]; # data for the loop = three strings
|
||||
# creating the blocks
|
||||
blocks := [];
|
||||
for i in [1..Length( d[ 1 ] )] do
|
||||
Add( blocks, [ MyInt( d[1][i] ), MyInt( d[2][i] ), MyInt( d[3][i] ) ] );
|
||||
od;
|
||||
|
||||
#creating the multiplication table
|
||||
T := List( [1..n], i->[1..n] );
|
||||
for i in [1..n] do T[i][1] := i; od;
|
||||
for i in [0..n-2] do
|
||||
i_in := Filtered( blocks, B->i in B);
|
||||
for j in [0..n-2] do
|
||||
if j=i then T[i+2][j+2] := 1; fi;
|
||||
if not j=i then
|
||||
ij_in := Filtered( i_in, B->j in B )[1]; #unique block;
|
||||
T[i+2][j+2] := Difference( ij_in, [i,j])[1] + 2;
|
||||
fi;
|
||||
od;
|
||||
od;
|
||||
|
||||
return LoopByCayleyTable( T );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateRCCLoop( n, pos_n, m )
|
||||
##
|
||||
## Activates an RCC loop from the library.
|
||||
## See manual for complete discussion concerning this library.
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateRCCLoop,
|
||||
function( n, pos_n, m )
|
||||
local pos_m, g, nr_conj_classes, data, data2, next_compactified, x, i, rel_m, G, section, pos_conjugacy_classes;
|
||||
|
||||
if IsEmpty( LOOPS_rcc_transitive_groups ) then # data not read yet
|
||||
ReadPackage( "loops", "data/rcc/rcc_transitive_groups.tbl" );
|
||||
fi;
|
||||
# determining the transitive group corresponding to pos_n, m
|
||||
pos_m := Length( LOOPS_rcc_data[ 3 ][ pos_n ][ 1 ] ); # nr of transitive groups associated with order n
|
||||
while LOOPS_rcc_data[ 3 ][ pos_n ][ 2 ][ pos_m ] > m do
|
||||
pos_m := pos_m - 1;
|
||||
od;
|
||||
g := LOOPS_rcc_data[ 3 ][ pos_n ][ 1 ][ pos_m ]; # index of transitive group (of degree n) in GAP library
|
||||
# activating data for the group, if needed
|
||||
if not IsBound( LOOPS_rcc_sections[ pos_n ][ pos_m ] ) then
|
||||
# data must be read from file and decoded
|
||||
ReadPackage( "loops", Concatenation( "data/rcc/sections", String(n), ".", String(g), ".tbl" ) );
|
||||
# variable LOOPS_aux is now read and ready to be processed
|
||||
nr_conj_classes := Length( LOOPS_rcc_transitive_groups[ pos_n ][ pos_m ][ 2 ] );
|
||||
data := SplitString( LOOPS_aux, " " );
|
||||
data2 := [];
|
||||
next_compactified := false;
|
||||
for x in data do
|
||||
if x="" then # the next entry is compactified, eg. "a$X" means "a", "$", "X"
|
||||
next_compactified := true;
|
||||
elif not next_compactified then
|
||||
Add( data2, x );
|
||||
else # compactified string
|
||||
next_compactified := false;
|
||||
for i in [1..Length(x)] do
|
||||
Add( data2, x{[i..i]} ); # Add( data2, x[i] ) is not safe when data2 is empty
|
||||
od;
|
||||
fi;
|
||||
od;
|
||||
data := List( data2, x -> LOOPS_ConvertToDecimal( x, 91 ) );
|
||||
# reconstructing the sequence from the difference sequence
|
||||
for i in [2..Length( data )] do
|
||||
data[ i ] := data[ i-1 ] - data[ i ];
|
||||
od;
|
||||
LOOPS_rcc_sections[ pos_n ][ pos_m ] := data;
|
||||
fi;
|
||||
# data is now loaded
|
||||
G := TransitiveGroup(n, g);
|
||||
rel_m := m - LOOPS_rcc_data[ 3 ][ pos_n ][ 2 ][ pos_m ] + 1; # relative position of the loop in the file for G
|
||||
section := [];
|
||||
nr_conj_classes := Length( LOOPS_rcc_transitive_groups[ pos_n ][ pos_m ][ 2 ] );
|
||||
if not LOOPS_rcc_conjugacy_classes[ 1 ] = [ n, g ] then # must calculate conjugacy classes, so let's reset old data
|
||||
LOOPS_rcc_conjugacy_classes[ 1 ] := [ n, g ];
|
||||
LOOPS_rcc_conjugacy_classes[ 2 ] := List( [1..nr_conj_classes], x->[] );
|
||||
fi;
|
||||
x := LOOPS_rcc_sections[ pos_n ][ pos_m ][ rel_m ];
|
||||
x := LOOPS_ConvertFromDecimal( x, 2, nr_conj_classes ); # convert to a binary string of prescribed length
|
||||
pos_conjugacy_classes := Positions( x, '1' );
|
||||
for i in [1..nr_conj_classes] do
|
||||
if LOOPS_rcc_conjugacy_classes[ 2 ][ i ] = [] then
|
||||
LOOPS_rcc_conjugacy_classes[ 2 ][ i ] := Elements( ConjugacyClass( G, LOOPS_rcc_transitive_groups[ pos_n ][ pos_m ][ 2 ][ i ] ) );
|
||||
fi;
|
||||
od;
|
||||
section := Concatenation( LOOPS_rcc_conjugacy_classes[ 2 ]{pos_conjugacy_classes} );
|
||||
Add( section, One( G ) ); # the trivial class is contained in all loops and never stored
|
||||
Sort( section );
|
||||
return LoopByRightSection( section );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateCCLoop( n, pos_n, m, case )
|
||||
##
|
||||
## Activates a CC-loop from the library.
|
||||
## The argument p_case is set to [p,"p^2"] if n = p^2, to [p,"2*p"] if n=2*p, and false otherwise.
|
||||
## See manual for complete discussion concerning this library.
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateCCLoop,
|
||||
function( n, pos_n, m, case )
|
||||
local T, x, y, k, a, b, p;
|
||||
|
||||
if case=false then # use library of RCC loops, must recalculate pos_n
|
||||
return LOOPS_ActivateRCCLoop( n, Position(LOOPS_rcc_data[ 1 ], n), LOOPS_cc_data[ 3 ][ pos_n ][ m ] );
|
||||
fi;
|
||||
|
||||
# parameters n, m are already checked to be permissible
|
||||
p := case[ 1 ];
|
||||
if case[ 2 ] = "2*p" then # 2*p
|
||||
T := List( [1..n], i -> [1..n ] );
|
||||
for a in [0..p-1] do for b in [0..p-1] do
|
||||
T[a+1][b+1]:= ((a+b) mod p) + 1;
|
||||
T[a+1][p+b+1] := p + ((-a+b) mod p ) + 1;
|
||||
T[p+a+1][b+1] := p + ((a+b) mod p) + 1;
|
||||
T[p+a+1][p+b+1] := ((1-a+b) mod p) + 1;
|
||||
od; od;
|
||||
return LoopByCayleyTable( T );
|
||||
fi;
|
||||
|
||||
# p^2
|
||||
T := List([1..n], i->[1..n]);
|
||||
if m = 1 then
|
||||
for x in [0..n-1] do for y in [0..n-1] do
|
||||
T[ x+1 ][ y+1 ] := ((x + y + p*(x^2)*y) mod n) + 1;
|
||||
od; od;
|
||||
elif m = 2 then
|
||||
k := LOOPS_SmallestNonsquare( p );
|
||||
for x in [0..n-1] do for y in [0..n-1] do
|
||||
T[ x+1 ][ y+1 ] := ((x + y + k*p*(x^2)*y) mod n) + 1;
|
||||
od; od;
|
||||
elif m = 3 then
|
||||
for x in [0..p-1] do for y in [0..p-1] do for a in [0..p-1] do for b in [0..p-1] do
|
||||
T[ x*p+a+1 ][ y*p+b+1 ] := ((x+y) mod p)*p + ((a+b+(x^2)*y) mod p) + 1;
|
||||
od; od; od; od;
|
||||
fi;
|
||||
return LoopByCayleyTable( T );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateNilpotentLoop( data )
|
||||
##
|
||||
## Activates the nilpotent loop based on data = [ K, F, t ], where
|
||||
## K determines a central (normal) subloop,
|
||||
## F determines the factor loop,
|
||||
## t determines the cocycle.
|
||||
## Understanding K and F:
|
||||
## If the value of K or F is in [2,3,4,5], it is the cyclic group of order V.
|
||||
## If the value of K or F is 0, it is the Klein group.
|
||||
## Understanding t:
|
||||
## The cocycle is mapping from F x F to K. Let f = |F|. Let k = |K|.
|
||||
## The value t corresponds to a (f-1)x(f-1) array of values in [0..k-1].
|
||||
## It is represented by a single integer in base k, with the least
|
||||
## significant digit in the first row and first column, then following
|
||||
## the rows.
|
||||
## Once t is decoded into a (f-1)x(f-1) array, 1 is added to all entries.
|
||||
## Then a first row and forst column of all ones is added to t,
|
||||
## resulting in a f x f array.
|
||||
## The loop is then obtained via LoopByExtension( K, F, phi, t), where
|
||||
## phi is trivial.
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateNilpotentLoop,
|
||||
function( data )
|
||||
local K, F, f, k, t, theta, i, j, phi;
|
||||
|
||||
# preparing normal subloop and factor loop
|
||||
if data[ 1 ] = 0 then
|
||||
K := IntoLoop( Group( (1,2),(3,4) ) );
|
||||
else
|
||||
K := IntoLoop( CyclicGroup( data[ 1 ] ) );
|
||||
fi;
|
||||
if data[ 2 ] = 0 then
|
||||
F := IntoLoop( Group( (1,2),(3,4) ) );
|
||||
else
|
||||
F := IntoLoop( CyclicGroup( data[ 2 ] ) );
|
||||
fi;
|
||||
|
||||
# preparing cocycle
|
||||
f := Size( F );
|
||||
k := Size( K );
|
||||
t := data[ 3 ];
|
||||
theta := List( [1..f], i->[1..f] );
|
||||
for i in [2..f] do
|
||||
theta[ 1 ][ i ] := 1;
|
||||
od;
|
||||
for i in [2..f] do for j in [2..f] do
|
||||
theta[ i ][ j ] := t mod k;
|
||||
t := (t - theta[i][j])/k;
|
||||
theta[ i ][ j ] := theta[ i ][ j ] + 1;
|
||||
od; od;
|
||||
|
||||
# preparing trivial action
|
||||
phi := List([1..f], i -> () );
|
||||
|
||||
# constructing the loop
|
||||
return LoopByExtension( K, F, phi, theta );
|
||||
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ActivateAutomorphicLoop( n, m )
|
||||
##
|
||||
## Activates an automorphic loop from the library.
|
||||
|
||||
InstallGlobalFunction( LOOPS_ActivateAutomorphicLoop,
|
||||
function( n, m )
|
||||
local i, pos_n, factor_id, F, dim, coords, basis, coc;
|
||||
if IsEmpty( LOOPS_automorphic_cocycles ) then # only read on demand
|
||||
ReadPackage( "loops", "data/automorphic/automorphic_cocycles.tbl");
|
||||
# decode cocycles
|
||||
for i in [1..3] do
|
||||
LOOPS_automorphic_cocycles[ i ] := List( LOOPS_automorphic_cocycles[ i ],
|
||||
c -> LOOPS_DecodeCocycle( [ 3^(i+2), true, c ], [0,1,2] )
|
||||
);
|
||||
od;
|
||||
# separate coordinates (from a long string )
|
||||
for i in [1..3] do
|
||||
LOOPS_automorphic_coordinates[ i ] := SplitString( LOOPS_automorphic_coordinates[ i ], " " );
|
||||
od;
|
||||
fi;
|
||||
# factor loop
|
||||
pos_n := Position( [27,81,243], n );
|
||||
factor_id := LOOPS_CharToDigit( LOOPS_automorphic_coordinates[ pos_n ][ m ][ 1 ] );
|
||||
F := AutomorphicLoop( n/3, factor_id );
|
||||
# coordinates determining the cocycle
|
||||
dim := Length( LOOPS_automorphic_bases[ pos_n ][ factor_id ] );
|
||||
coords := LOOPS_automorphic_coordinates[ pos_n ][ m ];
|
||||
coords := coords{[2..Length(coords)]}; # remove the character that determines factor id
|
||||
coords := LOOPS_ConvertBase( coords, 91, 3, dim );
|
||||
coords := List( coords, LOOPS_CharToDigit );
|
||||
# basis
|
||||
basis := List( LOOPS_automorphic_bases[ pos_n ][ factor_id ],
|
||||
i -> LOOPS_automorphic_cocycles[ pos_n ][ i ]
|
||||
);
|
||||
# calculate cocycle
|
||||
coc := (coords*basis) mod 3;
|
||||
coc := List( coc, i -> i+1 );
|
||||
# return extension of Z_3 by F using cocycle and trivial action
|
||||
return LoopByExtension( AutomorphicLoop(3,1), F, List([1..n/3], i -> () ), coc );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## READING LOOPS FROM THE LIBRARY - GENERIC METHOD
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LibraryLoop( name, n, m )
|
||||
##
|
||||
## returns he <m>th loop of order <n> from the library named <name>
|
||||
|
||||
InstallGlobalFunction( LibraryLoop, function( name, n, m )
|
||||
|
||||
local lib, implemented_orders, NOL, loop, pos_n, p, q, divs, PG, m_inv, root, half, case, g, h;
|
||||
|
||||
# selecting data library
|
||||
if name = "right Bol" then # using dual data
|
||||
lib := LOOPS_LibraryByName( "left Bol" );
|
||||
elif name = "LCC" then # using dual data
|
||||
lib := LOOPS_LibraryByName( "RCC" );
|
||||
else
|
||||
lib := LOOPS_LibraryByName( name );
|
||||
fi;
|
||||
|
||||
# extent of the library
|
||||
implemented_orders := lib[ 1 ];
|
||||
|
||||
# number of loops of given order in the library
|
||||
NOL := lib[ 2 ];
|
||||
|
||||
# testing arguments
|
||||
if (not n in Integers) or (not m in Integers) or not (n>0) or not (m>0) then
|
||||
Error("LOOPS: Both arguments must be positive integers.");
|
||||
fi;
|
||||
# parameters for handling systematic cases, such as CCLoop( p^2, 1 )
|
||||
pos_n := fail;
|
||||
case := false;
|
||||
if name="left Bol" or name="right Bol" then
|
||||
divs := DivisorsInt( n );
|
||||
if Length( divs ) = 4 and not IsInt( divs[3]/divs[2] ) then # case n = p*q
|
||||
q := divs[ 2 ];
|
||||
p := divs[ 3 ];
|
||||
case := [p,q];
|
||||
if not (IsOddInt( q ) and IsInt((p^2-1)/q)) then
|
||||
Error("LOOPS: Nonassociative ", name, " loops of order p*q exist only for primes p>q>2 such that q divides p^2-1.");
|
||||
fi;
|
||||
if IsInt((p-1)/q) and (not m in [1..(p-q)/2]) then
|
||||
Error("LOOPS: There are only ", (p-q)/2, " nonassociative ", name, " loops of order ", n, ".");
|
||||
fi;
|
||||
if IsInt((p+1)/q) and (not m in [1..(p-q+2)/2]) then
|
||||
Error("LOOPS: There are only ", (p-q+2)/2, " nonassociative ", name, " loops of order ", n, ".");
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
if name="CC" then
|
||||
divs := DivisorsInt( n );
|
||||
if Length( divs ) = 3 then # case p^2
|
||||
p := divs[ 2 ];
|
||||
case := [p,"p^2"];
|
||||
if not m in [1..3] then
|
||||
Error("LOOPS: There are only 3 nonassociative CC-loops of order p^2 for an odd prime p.");
|
||||
fi;
|
||||
elif Length( divs ) = 4 and not IsInt( divs[3]/divs[2] ) then # p*q
|
||||
p := divs[ 3 ];
|
||||
case := [p,"2*p"];
|
||||
if not divs[2] = 2 then
|
||||
Error("LOOPS: Order ", n, " not implemented.");
|
||||
fi;
|
||||
if not m=1 then
|
||||
Error("LOOPS: There is only 1 nonassociative CC-loop of order 2*p for an odd prime p.");
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
if case=false then
|
||||
if not n in implemented_orders then
|
||||
Error("LOOPS: Order ", n, " not implemented.");
|
||||
fi;
|
||||
pos_n := Position( implemented_orders, n );
|
||||
if NOL[ pos_n ] < m then
|
||||
if NOL[ pos_n ] = 1 then
|
||||
Error("LOOPS: There is only ", NOL[ pos_n ], " ", name, " loop of order ", n, " in the library.");
|
||||
else
|
||||
Error("LOOPS: There are only ", NOL[ pos_n ], " ", name, " loops of order ", n, " in the library.");
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
|
||||
# activating the desired loop (treat cases separately below)
|
||||
|
||||
# up to isomorphism
|
||||
if name = "left Bol" then
|
||||
loop := LOOPS_ActivateLeftBolLoop( pos_n, m, case );
|
||||
SetIsLeftBolLoop( loop, true );
|
||||
elif name = "right Bol" then
|
||||
loop := OppositeLoop( LOOPS_ActivateLeftBolLoop( pos_n, m, case ) );
|
||||
SetIsRightBolLoop( loop, true );
|
||||
elif name = "Moufang" then
|
||||
# renaming loops so that they agree with Goodaire's classification
|
||||
PG := List([1..243], i->());
|
||||
PG[16] := (2,5,3,4);
|
||||
PG[24] := (1,3,4)(2,5);
|
||||
PG[32] := (1,4,61,7,64,31,19,44,3,51,24,46,22,49,65,9,50,39,41,2,63,27,57,14,5,55,13,69,32,58,17,53,15)
|
||||
(6,71,28,36,30,47,18,42,26,60,21,59,35,52,25,40,23,43,29,56,20,66,8,70,33,67,38,54,16,62,37,11,68,34,45,10,48,12);
|
||||
PG[36] := (1,2);
|
||||
PG[40] := (1,2,5,3,4);
|
||||
PG[48] := (1,11,43,49,30,7,28,23,36,21,50,31,41,32,46,51,29,12,42,15)
|
||||
(2,25,24,35,22,8,39,5,6,38,16,19,17,37,34,33,4,47,13,20,10,40,48,14,3,26,45)
|
||||
(9,27,44);
|
||||
PG[56] := (1,2,4);
|
||||
PG[60] := (1,2);
|
||||
m_inv := m^Inverse( PG[ n ] );
|
||||
# activating the loop
|
||||
loop := LOOPS_ActivateMoufangLoop( n, pos_n, m_inv );
|
||||
SetIsMoufangLoop( loop, true );
|
||||
elif name = "Paige" then
|
||||
loop := LoopByCayleyTable( lib[ 3 ][ 1 ][ 1 ] ); #only one loop there at this point
|
||||
SetIsMoufangLoop( loop, true );
|
||||
elif name = "code" then
|
||||
loop := LibraryLoop( "Moufang", n, lib[ 3 ][ pos_n ][ m ] );
|
||||
SetIsCodeLoop( loop, true );
|
||||
elif name = "Steiner" then
|
||||
loop := LOOPS_ActivateSteinerLoop( n, pos_n, m );
|
||||
SetIsSteinerLoop( loop, true );
|
||||
elif name = "CC" then
|
||||
loop := LOOPS_ActivateCCLoop( n, pos_n, m, case );
|
||||
SetIsCCLoop( loop, true );
|
||||
elif name = "RCC" then
|
||||
loop := LOOPS_ActivateRCCLoop( n, pos_n, m );
|
||||
SetIsRCCLoop( loop, true );
|
||||
elif name = "LCC" then
|
||||
loop := OppositeLoop( LOOPS_ActivateRCCLoop( n, pos_n, m ) );
|
||||
SetIsLCCLoop( loop, true );
|
||||
elif name = "small" then
|
||||
loop := LoopByCayleyTable( LOOPS_DecodeCayleyTable( lib[ 3 ][ pos_n ][ m ] ) );
|
||||
elif name = "interesting" then
|
||||
if [n,m] = [96,1] then # simple Bol loop of order 96
|
||||
g := Group((1,4)(2,9)(3,10)(6,11)(7,12)(13,21)(14,22)(15,24)(16,23)(17,30)(18,29)(19,31)(20,32)(33,35)(38,40),
|
||||
(1,2,4,6,8,7,5,3)(9,13,25,18,10,14,26,17)(11,15,27,20,12,16,28,19)(21,30,38,34,23,31,40,35)(22,32,39,36,24,29,37,33));
|
||||
h := Normalizer( g, SylowSubgroup( g, 5) );
|
||||
g := Action( g, RightCosets( g, h ), OnRight );
|
||||
loop := LoopByRightSection(Union(Filtered(ConjugacyClasses(g),c->Size(c) in [1,15,80])));
|
||||
else
|
||||
loop := LoopByCayleyTable( LOOPS_DecodeCayleyTable( lib[ 3 ][ pos_n ][ m ][ 1 ] ) );
|
||||
fi;
|
||||
SetName( loop, lib[ 3 ][ pos_n ][ m ][ 2 ] );
|
||||
elif name = "nilpotent" then
|
||||
loop := LOOPS_ActivateNilpotentLoop( lib[ 3 ][ pos_n ][ m ] );
|
||||
elif name = "automorphic" then
|
||||
if not n in [27,81,243] then # use Cayley table
|
||||
loop := LoopByCayleyTable( LOOPS_DecodeCayleyTable( lib[ 3 ][ pos_n ][ m ] ) );
|
||||
else # use cocycles
|
||||
loop := LOOPS_ActivateAutomorphicLoop( n, m );
|
||||
fi;
|
||||
SetIsAutomorphicLoop( loop, true );
|
||||
# up to isotopism
|
||||
elif name = "itp small" then
|
||||
return LibraryLoop( "small", n, lib[ 3 ][ pos_n ][ m ] );
|
||||
fi;
|
||||
|
||||
# setting the name
|
||||
if not name = "interesting" then
|
||||
SetName( loop, Concatenation( "<", name, " loop ", String( n ), "/", String( m ), ">" ) );
|
||||
fi;
|
||||
|
||||
# returning the loop
|
||||
return loop;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## READING LOOPS FROM THE LIBRARY - SPECIFIC CALLS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LeftBolLoop( n, m )
|
||||
#F RightBolLoop( n, m )
|
||||
#F MoufangLoop( n, m )
|
||||
#F PaigeLoop( q )
|
||||
#F CodeLoop( n, m )
|
||||
#F SteinerLoop( n, m )
|
||||
#F CCLoop( n, m )
|
||||
#F SmallLoop( n, m )
|
||||
#F InterestingLoop( n, m )
|
||||
#F NilpotentLoop( n, m )
|
||||
#F AutomorphicLoop( n, m )
|
||||
#F ItpSmallLoop( n, m )
|
||||
##
|
||||
|
||||
InstallGlobalFunction( LeftBolLoop, function( n, m )
|
||||
return LibraryLoop( "left Bol", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( RightBolLoop, function( n, m )
|
||||
return LibraryLoop( "right Bol", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( MoufangLoop, function( n, m )
|
||||
return LibraryLoop( "Moufang", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( PaigeLoop, function( q )
|
||||
# Paige loop over GF(q)
|
||||
if not q=2 then return Error("LOOPS: Only q=2 is implemented."); fi;
|
||||
return LibraryLoop( "Paige", 120, 1 );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( CodeLoop, function( n, m )
|
||||
return LibraryLoop( "code", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( SteinerLoop, function( n, m )
|
||||
return LibraryLoop( "Steiner", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( CCLoop, function( n, m )
|
||||
return LibraryLoop( "CC", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( ConjugacyClosedLoop, function( n, m )
|
||||
return LibraryLoop( "CC", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( RCCLoop, function( n, m )
|
||||
return LibraryLoop( "RCC", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( RightConjugacyClosedLoop, function( n, m )
|
||||
return LibraryLoop( "RCC", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( LCCLoop, function( n, m )
|
||||
return LibraryLoop( "LCC", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( LeftConjugacyClosedLoop, function( n, m )
|
||||
return LibraryLoop( "LCC", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( SmallLoop, function( n, m )
|
||||
return LibraryLoop( "small", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( InterestingLoop, function( n, m )
|
||||
return LibraryLoop( "interesting", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( NilpotentLoop, function( n, m )
|
||||
return LibraryLoop( "nilpotent", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( AutomorphicLoop, function( n, m )
|
||||
return LibraryLoop( "automorphic", n, m );
|
||||
end);
|
||||
|
||||
InstallGlobalFunction( ItpSmallLoop, function( n, m )
|
||||
return LibraryLoop( "itp small", n, m );
|
||||
end);
|
14
gap/extensions.gd
Normal file
14
gap/extensions.gd
Normal file
|
@ -0,0 +1,14 @@
|
|||
############################################################################
|
||||
##
|
||||
#W extensions.gd Extensions [loops]
|
||||
##
|
||||
#H @(#)$Id: extensions.gd, v 2.0.0 2008/01/21 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
DeclareOperation( "LoopByExtension", [ IsLoop, IsLoop, IsList, IsMatrix ] );
|
||||
DeclareOperation( "NuclearExtension", [ IsLoop, IsLoop ] );
|
||||
|
||||
|
79
gap/extensions.gi
Normal file
79
gap/extensions.gi
Normal file
|
@ -0,0 +1,79 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W extensions.gi Extensions [loops]
|
||||
##
|
||||
#H @(#)$Id: extensions.gi, v 3.0.0 2015/06/15 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopByExtension( K, F, phi, theta )
|
||||
##
|
||||
## Let K be an abelian group, F a loop, phi: F --> Aut( K ) a homomorphism,
|
||||
## and theta: F x F --> K a map (cocycle) satisfying theta(1,x) = theta(x,1)
|
||||
## = 1. Then the function returns the extension K x F with multiplication *
|
||||
## defined by (a,x)*(b,y) = (a phi(x)(b) theta(x,y), xy).
|
||||
##
|
||||
## Note about arguments:
|
||||
## phi is a list of length |F| of permutations of [1, ..., K ],
|
||||
## theta is am |F| by |F| matrix of numbers from [1, ..., K].
|
||||
|
||||
InstallMethod( LoopByExtension, "for a commutative associative loop and a loop",
|
||||
[ IsLoop, IsLoop, IsList, IsMatrix ],
|
||||
function( K, F, phi, theta )
|
||||
local nK, nF, ctK, ctF, ct, a, b, x, y, c, z;
|
||||
# only some properties of the arguments are checked
|
||||
if not IsAssociative( K ) then Error("LOOPS: <1> must be an associative loop."); fi;
|
||||
if not IsCommutative( K ) then Error("LOOPS: <1> must be a commutative loop."); fi;
|
||||
# sizes
|
||||
nK := Size( K );
|
||||
nF := Size( F );
|
||||
# making sure all is canonical
|
||||
ctK := CanonicalCayleyTable( CayleyTable( K ) );
|
||||
ctF := CanonicalCayleyTable( CayleyTable( F ) );
|
||||
# future Cayley table
|
||||
ct := List([1..nK*nF], i -> 0 * [1..nK*nF] );
|
||||
# constructing the Cayley table
|
||||
for a in [1..nK] do for b in [1..nK] do for x in [1..nF] do for y in [1..nF] do
|
||||
c := ctK[ a ][ b^phi[x] ];
|
||||
c := ctK[ c ][ theta[ x ][ y ] ];
|
||||
z := ctF[ x ][ y ];
|
||||
ct[ nK*(x-1) + a ][ nK*(y-1) + b ] := nK*(z-1) + c;
|
||||
od; od; od; od;
|
||||
return LoopByCayleyTable( ct );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O NuclearExtension( Q, K )
|
||||
##
|
||||
## Let Q be a loop and K an abelian normal subloop of Q contained in the
|
||||
## nucleus of Q. Then Q is an extension of K by Q/K = F via a map
|
||||
## phi: F --> Aut( K ) and a cocycle theta: F x F --> K, as above.
|
||||
## This function returns the for ingredients [ K, F, phi, theta ],
|
||||
## all in a canonical form.
|
||||
|
||||
InstallMethod( NuclearExtension, "for a loop and a normal nuclear subloop",
|
||||
[ IsLoop, IsLoop ],
|
||||
function( Q, K )
|
||||
local F, t, phi, theta, i, j, elmK;
|
||||
# checking arguments
|
||||
if not IsNormal( Q, K ) then Error("LOOPS: <2> must be a normal subloop of <1>."); fi;
|
||||
if not IsCommutative( K ) then Error("LOOPS: <2> must be a commutative loop."); fi;
|
||||
if not ForAll( K, k -> k in Nuc( Q ) ) then Error("LOOPS: <2> must be contained in the nucleus of <1>."); fi;
|
||||
# partitioning Q nicely into cosets of K (not needed but it aids in visualization)
|
||||
Q := IsomorphicCopyByNormalSubloop( Q, K );
|
||||
K := Subloop( Q, [1..Size(K)] );
|
||||
F := FactorLoop( Q, K );
|
||||
t := List( [1..Size(F)], i -> Elements(Q)[(i-1)*Size(K) + 1] ); #transversal
|
||||
phi := List( t, x -> RestrictedPerm( MiddleInnerMapping( Q, x ), [1..Size(K)] ) ); #action
|
||||
theta := List( [1..Size(F)], i -> [1..Size(F)] ); #cocycle
|
||||
for i in [1..Size(F)] do for j in [1..Size(F)] do
|
||||
elmK := RightDivision( t[i]*t[j], t[ CayleyTable( F )[ i ][ j ] ] );
|
||||
theta[ i ][ j ] := Position( Elements( K ), elmK );
|
||||
od; od;
|
||||
return [ K, F, phi, theta ];
|
||||
end);
|
52
gap/iso.gd
Normal file
52
gap/iso.gd
Normal file
|
@ -0,0 +1,52 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W iso.gd Isomorphisms and isotopisms [loops]
|
||||
##
|
||||
#H @(#)$Id: iso.gd, v 3.2.0 2015/06/12 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
|
||||
#############################################################################
|
||||
## DISCRIMINATOR
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareOperation( "Discriminator", [ IsQuasigroup ] );
|
||||
DeclareOperation( "AreEqualDiscriminators", [ IsList, IsList ] );
|
||||
|
||||
|
||||
#############################################################################
|
||||
## ISOMORPHISMS OF QUASIGROUPS AND LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareOperation( "IsomorphismQuasigroups", [ IsQuasigroup, IsQuasigroup ] );
|
||||
DeclareOperation( "IsomorphismLoops", [ IsLoop, IsLoop ] );
|
||||
DeclareOperation( "QuasigroupsUpToIsomorphism", [ IsList ] );
|
||||
DeclareOperation( "LoopsUpToIsomorphism", [ IsList ] );
|
||||
DeclareOperation( "IsomorphicCopyByPerm", [ IsQuasigroup, IsPerm ] );
|
||||
DeclareOperation( "IsomorphicCopyByNormalSubloop", [ IsLoop, IsLoop ] );
|
||||
|
||||
|
||||
#############################################################################
|
||||
## AUTOMORPHISMS AND AUTOMORPHISM GROUPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
# AutomorphismGroup already declared for groups
|
||||
|
||||
|
||||
#############################################################################
|
||||
## ISOTOPISM OF LOOPS
|
||||
## ------------------------------------------------------------------------
|
||||
DeclareOperation( "IsotopismLoops", [ IsLoop, IsLoop ] );
|
||||
DeclareOperation( "LoopsUpToIsotopism", [ IsList ] );
|
||||
|
||||
|
||||
#############################################################################
|
||||
## AUXILIARY
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "IsomorphismQuasigroupsNC" );
|
||||
DeclareGlobalFunction( "LOOPS_EfficientGenerators" );
|
||||
DeclareGlobalFunction( "LOOPS_ExtendHomomorphismByClosingSource" );
|
||||
DeclareGlobalFunction( "LOOPS_ExtendIsomorphism" );
|
||||
DeclareGlobalFunction( "LOOPS_SublistPosition" );
|
||||
DeclareGlobalFunction( "LOOPS_AutomorphismsFixingSet" );
|
690
gap/iso.gi
Normal file
690
gap/iso.gi
Normal file
|
@ -0,0 +1,690 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W iso.gi Isomorphisms and isotopisms [loops]
|
||||
##
|
||||
#H @(#)$Id: iso.gi, v 3.3.0 2016/10/26 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## DISCRIMINATOR
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O Discriminator( Q )
|
||||
##
|
||||
## Returns the dicriminator of a quasigroup <Q>.
|
||||
## It is a list [ A, B ], where A is a list of the form
|
||||
## [ [I1,n1], [I2,n2],.. ], where the invariant Ii occurs ni times in Q,
|
||||
## and where B[i] is a subset of [1..Size(Q)] corresponding to elements of
|
||||
## Q with invariant Ii.
|
||||
##
|
||||
## PROG: Invariants 8, 9 can be slow for large quasigroups.
|
||||
|
||||
InstallMethod( Discriminator, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
local n, T, I, case, i, j, k, ebo, js, ks, count1, count2, A, P, B, perm, p;
|
||||
|
||||
# making sure the quasigroup is canonical
|
||||
if not Q = Parent( Q ) then Q := CanonicalCopy( Q ); fi;
|
||||
n := Size( Q );
|
||||
|
||||
# converting a non-declared loop into a loop
|
||||
perm := ();
|
||||
if (not IsLoop( Q )) and (not MultiplicativeNeutralElement( Q )=fail) then
|
||||
p := Position( Elements( Q ), MultiplicativeNeutralElement( Q ) );
|
||||
if p<>1 then
|
||||
perm := (1,p);
|
||||
fi;
|
||||
Q := IntoLoop( Q );
|
||||
fi;
|
||||
|
||||
T := CayleyTable( Q );
|
||||
|
||||
# Calculating 9 invariants for three cases: quasigroup, loop, power associative loop
|
||||
# I[i] will contain the invariant vector for ith element of Q
|
||||
I := List( [1..n], i -> 0*[1..9] );
|
||||
|
||||
# invariant 1
|
||||
# to distinguish the 3 cases
|
||||
if (not IsLoop( Q ) )
|
||||
then case := 1;
|
||||
elif (not IsPowerAssociative( Q ))
|
||||
then case := 2;
|
||||
else
|
||||
case := 3;
|
||||
fi;
|
||||
for i in [1..n] do I[i][1] := case; od;
|
||||
|
||||
# invariant 2
|
||||
# for given x, cycle structure of L_x, R_x
|
||||
for i in [1..n] do
|
||||
I[i][2] := [
|
||||
CycleStructurePerm( PermList( List( [1..n], j -> T[i][j]) ) ),
|
||||
CycleStructurePerm( PermList( List( [1..n], j -> T[j][i]) ) )
|
||||
];
|
||||
od;
|
||||
|
||||
# invariant 3
|
||||
if case = 1 then # am I an idempotent?
|
||||
for i in [1..n] do I[i][3] := T[i][i]=i; od;
|
||||
elif case = 2 then # am I an involution?
|
||||
for i in [1..n] do I[i][3] := T[i][i]=1; od;
|
||||
else # what's my order?
|
||||
for i in [1..n] do I[i][3] := Order( Elements( Q )[i] ); od;
|
||||
fi;
|
||||
|
||||
# invariant 4
|
||||
if case <> 3 then # how many times am I a square ?
|
||||
for i in [1..n] do j := T[i][i]; I[j][4] := I[j][4] + 1; od;
|
||||
else # how many times am I a square, third power, fourth power?
|
||||
for i in [1..n] do I[i][4] := [0,0,0]; od;
|
||||
for i in [1..n] do
|
||||
j := T[i][i]; I[j][4][1] := I[j][4][1] + 1;
|
||||
j := T[i][j]; I[j][4][2] := I[j][4][2] + 1;
|
||||
j := T[i][j]; I[j][4][3] := I[j][4][3] + 1;
|
||||
od;
|
||||
fi;
|
||||
|
||||
# invariant 5
|
||||
if case <> 3 then # with how many elements do I commute?
|
||||
for i in [1..n] do
|
||||
I[i][5] := Length( Filtered( [1..n], j -> T[i][j] = T[j][i] ) );
|
||||
od;
|
||||
else # with how many elements of given order do I commute?
|
||||
ebo := List( [1..n], i -> Filtered( [1..n], j -> I[j][3]=i ) ); # elements by order. PROG: must point to order invariant
|
||||
ebo := Filtered( ebo, x -> not IsEmpty( x ) );
|
||||
for i in [1..n] do
|
||||
I[i][5] := List( ebo, J -> Length( Filtered( J, j -> T[ i ][ j ] = T[ j ][ i ] ) ) );
|
||||
od;
|
||||
fi;
|
||||
|
||||
# invariant 6
|
||||
# is it true that (x*x)*x = x*(x*x)?
|
||||
for i in [1..n] do
|
||||
I[i][6] := T[T[i][i]][i] = T[i][T[i][i]];
|
||||
od;
|
||||
|
||||
# invariant 7
|
||||
if case <> 3 then # for how many elements y is (x*x)*y = x*(x*y)?
|
||||
for i in [1..n] do
|
||||
I[i][7] := Length( Filtered( [1..n], j -> T[T[i][i]][j] = T[i][T[i][j]] ) );
|
||||
od;
|
||||
else # for how many elements y of given order is (x*x)*y=x*(x*y)
|
||||
for i in [1..n] do
|
||||
I[i][7] := List( ebo, J -> Length( Filtered( J, j -> T[T[i][i]][j] = T[i][T[j][i]] ) ) );
|
||||
od;
|
||||
fi;
|
||||
|
||||
# invariants 8 and 9 (these take longer)
|
||||
if case <> 3 then # with how many pairs of elements do I associate in the first, second position?
|
||||
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]][k] then I[i][8] := I[i][8] + 1; fi;
|
||||
if T[j][T[i][k]] = T[T[j][i]][k] then I[i][9] := I[i][9] + 1; fi;
|
||||
od; od;
|
||||
od;
|
||||
else # for how many pairs of elements of given orders do I associate in the first, second position?
|
||||
for i in [1..n] do
|
||||
I[i][8] := []; I[i][9] := [];
|
||||
for js in ebo do for ks in ebo do
|
||||
count1 := 0; count2 := 0;
|
||||
for j in js do for k in ks do
|
||||
if T[i][T[j][k]] = T[T[i][j]][k] then count1 := count1 + 1; fi;
|
||||
if T[j][T[i][k]] = T[T[j][i]][k] then count2 := count2 + 1; fi;
|
||||
od; od;
|
||||
Add( I[i][8], count1 ); Add( I[i][9], count2 );
|
||||
od; od;
|
||||
od;
|
||||
fi;
|
||||
|
||||
# all invariants have now been calculated
|
||||
|
||||
# note that it can be deduced from the invariants when an element is central, for instance
|
||||
|
||||
# setting up the first part of the discriminator (invariants with the number of occurence)
|
||||
A := Collected( I );
|
||||
P := Sortex( List( A, x -> x[2] ) ); # rare invariants will be listed first, but the set ordering of A is otherwise not disrupted
|
||||
A := Permuted( A, P );
|
||||
|
||||
# setting up the second part of the discriminator (blocks of elements invariant under isomorphisms)
|
||||
B := List( [1..Length(A)], j -> Filtered( [1..n], i -> I[i] = A[j][1] ) );
|
||||
|
||||
# if a non-declared loop was converted into a loop, correcting for this
|
||||
if not perm = () then
|
||||
B := List( B, x -> Set( x, i -> i^perm ) );
|
||||
fi;
|
||||
|
||||
return [ A, B ];
|
||||
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_EfficientGenerators( Q, D )
|
||||
##
|
||||
## Auxiliary function.
|
||||
## Given a quasigroup <Q> with discriminator <D>, it returns a list of
|
||||
## indices of generators of <Q> deemed best for an isomorphism filter.
|
||||
## It mimics the function SmallGeneratingSet, but it considers
|
||||
## the elements in order determined by block size of the disciminator.
|
||||
|
||||
InstallGlobalFunction( LOOPS_EfficientGenerators,
|
||||
function( Q, D )
|
||||
local gens, sub, elements, candidates, max, S, best_gen, best_S;
|
||||
|
||||
gens := []; # generating set to be returned
|
||||
sub := []; # substructure generated so far
|
||||
elements := Concatenation( D[2] ); # all elements ordered by block size
|
||||
candidates := ShallowCopy( elements ); # candidates for next generator
|
||||
while sub <> Q do
|
||||
# find an element not in sub that most enlarges sub
|
||||
max := 0;
|
||||
while not IsEmpty( candidates ) do
|
||||
S := Subquasigroup( Q, Union( gens, [candidates[1]] ) );
|
||||
if Size(S) > max then
|
||||
max := Size( S );
|
||||
best_gen := candidates[1];
|
||||
best_S := S;
|
||||
fi;
|
||||
# discard elements of S since they cannot do better
|
||||
candidates := Filtered( candidates, x -> not Elements(Q)[x] in S );
|
||||
od;
|
||||
Add( gens, best_gen );
|
||||
sub := best_S;
|
||||
# reset candidates for next round
|
||||
candidates := Filtered( elements, x -> not Elements(Q)[x] in sub );
|
||||
od;
|
||||
return gens;
|
||||
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O AreEqualDicriminators( D, E )
|
||||
##
|
||||
## Returns true if the invarinats of the two discriminators are the same,
|
||||
## including number of occurrences of each invariant.
|
||||
|
||||
InstallMethod( AreEqualDiscriminators, "for two lists (discrimninators)",
|
||||
[ IsList, IsList ],
|
||||
function( D, E )
|
||||
return D[ 1 ] = E[ 1 ];
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## EXTENDING MAPPINS (AUXILIARY)
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
# Here, we identity the map f: A --> B with the triple [ m, a, b ],
|
||||
# where a is a subset of A, b[ i ] is the image of a[ i ], and m[ i ] > 0
|
||||
# if and only if i is in a.
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ExtendHomomorphismByClosingSource( f, L, M )
|
||||
##
|
||||
## Auxiliary.
|
||||
## <L>, <M> are multiplication tables of quasigroups, <f> is a partial map
|
||||
## from a subset of elements of <L> to a subset of elements of <M>.
|
||||
## This function attempts to extend <f> into a homomorphism of quasigroups by
|
||||
## extending the source of <f> into (the smallest possible) subqusigroup of <L>.
|
||||
|
||||
InstallGlobalFunction( LOOPS_ExtendHomomorphismByClosingSource,
|
||||
function( f, L, M )
|
||||
local oldS, newS, pairs, x, y, newNow, p, z, fz;
|
||||
oldS := [ ];
|
||||
newS := f[ 2 ];
|
||||
|
||||
repeat
|
||||
pairs := [];
|
||||
for x in oldS do for y in newS do
|
||||
Add( pairs, [ x, y ] );
|
||||
Add( pairs, [ y, x ] );
|
||||
od; od;
|
||||
for x in newS do for y in newS do
|
||||
Add( pairs, [ x, y ] );
|
||||
od; od;
|
||||
newNow := [];
|
||||
for p in pairs do
|
||||
x := p[ 1 ];
|
||||
y := p[ 2 ];
|
||||
z := L[ x ][ y ];
|
||||
fz := M[ f[ 1 ][ x ] ][ f[ 1 ][ y ] ];
|
||||
if f[ 1 ][ z ] = 0 then
|
||||
f[ 1 ][ z ] := fz; AddSet( f[ 2 ], z ); AddSet( f[ 3 ], fz );
|
||||
Add( newNow, z );
|
||||
else
|
||||
if not f[ 1 ][ z ] = fz then return fail; fi;
|
||||
fi;
|
||||
od;
|
||||
oldS := Union( oldS, newS );
|
||||
newS := ShallowCopy( newNow );
|
||||
until IsEmpty( newS );
|
||||
return f;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_SublistPosition( S, x )
|
||||
##
|
||||
## auxiliary function
|
||||
## input: list of lists <S>, element <x>
|
||||
## returns: smallest i such that x in S[i]; or fail.
|
||||
|
||||
InstallGlobalFunction( LOOPS_SublistPosition,
|
||||
function( S, x )
|
||||
local i;
|
||||
for i in [ 1..Length( S ) ] do if x in S[ i ] then return i; fi; od;
|
||||
return fail;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ExtendIsomorphism( f, L, GenL, DisL, M, DisM )
|
||||
##
|
||||
## Auxiliary.
|
||||
## Given a partial map <f> from a quasigroup <L> to a quasigroup <M>,
|
||||
## it attempts to extend <f> into an isomorphism betweem <L> and <M>.
|
||||
## <GenL>, <DisL> and <DisM> are precalculated and stand for:
|
||||
## efficient generators of <L>, invariant subsets of <L>, efficient generators
|
||||
## of <M>, respectively.
|
||||
|
||||
InstallGlobalFunction( LOOPS_ExtendIsomorphism,
|
||||
function( f, L, GenL, DisL, M, DisM )
|
||||
local x, possible_images, y, g;
|
||||
f := LOOPS_ExtendHomomorphismByClosingSource( f, L, M );
|
||||
if f = fail or Length( f[ 2 ] ) > Length( f[ 3 ] ) then return fail; fi;
|
||||
if Length( f[ 2 ] ) = Length( L ) then return f; fi; #isomorphism found
|
||||
|
||||
x := GenL[ 1 ];
|
||||
GenL := GenL{[2..Length(GenL)]};
|
||||
possible_images := Filtered( DisM[ LOOPS_SublistPosition( DisL, x ) ], y -> not y in f[ 3 ] );
|
||||
for y in possible_images do
|
||||
g := StructuralCopy( f );
|
||||
g[ 1 ][ x ] := y; AddSet( g[ 2 ], x ); AddSet( g[ 3 ], y );
|
||||
g := LOOPS_ExtendIsomorphism( g, L, GenL, DisL, M, DisM );
|
||||
if not g = fail then return g; fi; #isomorphism found
|
||||
od;
|
||||
return fail;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## ISOMORPHISMS OF LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsomorphismQuasigroupsNC( L, GenL, DisL, M, DisM )
|
||||
##
|
||||
## Auxiliary. Given a quasigroup <L>, its efficient generators <GenL>, the
|
||||
## disciminator <DisL> of <L>, and another loop <M> with discriminator
|
||||
## <DisM>, it returns an isomorophism from <L> onto <M>, or it fails.
|
||||
|
||||
InstallGlobalFunction( IsomorphismQuasigroupsNC,
|
||||
function( L, GenL, DisL, M, DisM )
|
||||
local map, iso;
|
||||
if not AreEqualDiscriminators( DisL, DisM ) then return fail; fi;
|
||||
|
||||
#mapping
|
||||
map := 0 * [ 1.. Size( L ) ];
|
||||
|
||||
if IsLoop( L ) and IsLoop( M ) then
|
||||
map[ 1 ] := 1; # identity element is certainly preserved
|
||||
iso := LOOPS_ExtendIsomorphism( [ map, [ 1 ], [ 1 ] ], CayleyTable( L ), GenL, DisL[2], CayleyTable( M ), DisM[2] );
|
||||
else
|
||||
iso := LOOPS_ExtendIsomorphism( [ map, [ ], [ ] ], CayleyTable( L ), GenL, DisL[2], CayleyTable( M ), DisM[2] );
|
||||
fi;
|
||||
if not iso = fail then return SortingPerm( iso[ 1 ] ); fi;
|
||||
return fail;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsomorphismQuasigroups( L, M )
|
||||
##
|
||||
## If the quasigroups <L>, <M> are isomorophic, it returns an isomorphism
|
||||
## from <L> onto <M>. Fails otherwise.
|
||||
|
||||
InstallMethod( IsomorphismQuasigroups, "for two quasigroups",
|
||||
[ IsQuasigroup, IsQuasigroup ],
|
||||
function( L, M )
|
||||
local GenL1, GenL2, GenL, DisL, DisM, permL, permM, p, iso;
|
||||
|
||||
# making sure the quasigroups have canonical Cayley tables
|
||||
if not L = Parent( L ) then L := CanonicalCopy( L ); fi;
|
||||
if not M = Parent( M ) then M := CanonicalCopy( M ); fi;
|
||||
|
||||
# turning non-declared loops into loops
|
||||
permL := ();
|
||||
if (not IsLoop( L )) and (not MultiplicativeNeutralElement( L )=fail) then
|
||||
p := Position( Elements( L ), MultiplicativeNeutralElement( L ) );
|
||||
if p<>1 then
|
||||
permL := (1,p);
|
||||
fi;
|
||||
L := IntoLoop( L );
|
||||
fi;
|
||||
permM := ();
|
||||
if (not IsLoop( M )) and (not MultiplicativeNeutralElement( M )=fail) then
|
||||
p := Position( Elements( M ), MultiplicativeNeutralElement( M ) );
|
||||
if p<>1 then
|
||||
permM := (1,p);
|
||||
fi;
|
||||
M := IntoLoop( M );
|
||||
fi;
|
||||
|
||||
DisL := Discriminator( L );
|
||||
GenL := LOOPS_EfficientGenerators( L, DisL );
|
||||
DisM := Discriminator( M );
|
||||
iso := IsomorphismQuasigroupsNC( L, GenL, DisL, M, DisM );
|
||||
if not iso = fail then
|
||||
iso := permL*iso*permM; # accounting for possible internal conversions to loops
|
||||
fi;
|
||||
return iso;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsomorphismLoops( L, M )
|
||||
##
|
||||
## If the loops <L>, <M> are isomorophic, it returns an isomorphism
|
||||
## from <L> onto <M>. Fails otherwise.
|
||||
|
||||
InstallMethod( IsomorphismLoops, "for loops",
|
||||
[ IsLoop, IsLoop ],
|
||||
function( L, M )
|
||||
return IsomorphismQuasigroups( L, M );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O QuasigroupsUpToIsomorphism( ls )
|
||||
##
|
||||
## Given a list <ls> of quasigroups, returns a sublist of <ls> consisting
|
||||
## of represenatives of isomorphism classes of <ls>.
|
||||
|
||||
InstallMethod( QuasigroupsUpToIsomorphism, "for a list of quasigroups",
|
||||
[ IsList ],
|
||||
function( ls )
|
||||
local i, quasigroups, L, D, G, with_same_D, is_new_quasigroup, K;
|
||||
|
||||
# making sure only quasigroups are on the list
|
||||
if not IsEmpty( Filtered( ls, x -> not IsQuasigroup( x ) ) ) then
|
||||
Error("LOOPS: <1> must be a list of quasigroups");
|
||||
fi;
|
||||
# special case: one quasigroup
|
||||
if Length( ls ) = 1 then
|
||||
return ls;
|
||||
fi;
|
||||
# making everything canonical
|
||||
ls := ShallowCopy( ls ); # otherwise a side efect occurs in ls
|
||||
for i in [1..Length(ls)] do
|
||||
if not Parent(ls[i]) = ls[i] then
|
||||
ls[i] := CanonicalCopy( ls[i] );
|
||||
fi;
|
||||
od;
|
||||
|
||||
quasigroups := [];
|
||||
for L in ls do
|
||||
D := Discriminator( L );
|
||||
G := LOOPS_EfficientGenerators( L, D );
|
||||
# will be testing only quasigroups with the same discriminator
|
||||
with_same_D := Filtered( quasigroups, K -> AreEqualDiscriminators( K[2], D ) );
|
||||
is_new_quasigroup := true;
|
||||
for K in with_same_D do
|
||||
if not IsomorphismQuasigroupsNC( L, G, D, K[1], K[2] ) = fail then
|
||||
is_new_quasigroup := false;
|
||||
break;
|
||||
fi;
|
||||
od;
|
||||
if is_new_quasigroup then Add( quasigroups, [ L, D ] ); fi;
|
||||
od;
|
||||
# returning only quasigroups, not their discriminators
|
||||
return List( quasigroups, L -> L[1] );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopsUpToIsomorphism( ls )
|
||||
##
|
||||
## Given a list <ls> of loops, returns a sublist of <ls> consisting
|
||||
## of represenatives of isomorphism classes of <ls>.
|
||||
|
||||
InstallMethod( LoopsUpToIsomorphism, "for a list of loops",
|
||||
[ IsList ],
|
||||
function( ls )
|
||||
# making sure only quasigroups are on the list
|
||||
if not IsEmpty( Filtered( ls, x -> not IsQuasigroup( x ) ) ) then
|
||||
Error("LOOPS: <1> must be a list of quasigroups");
|
||||
fi;
|
||||
return QuasigroupsUpToIsomorphism( ls );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsomorphicCopyByPerm( Q, p )
|
||||
##
|
||||
## If <Q> is a quasigroup of order n and <p> a permutation of [1..n], returns
|
||||
## the quasigroup (Q,*) such that p(xy) = p(x)*p(y).
|
||||
## If <Q> is a loop, p is first composed with (1,1^p) to make sure
|
||||
## that the neutral element of (Q,*) remains 1.
|
||||
|
||||
InstallMethod( IsomorphicCopyByPerm, "for a quasigroup and permutation",
|
||||
[ IsQuasigroup, IsPerm ],
|
||||
function( Q, p )
|
||||
local ctQ, ct, inv_p;
|
||||
ctQ := CanonicalCayleyTable( CayleyTable( Q ) );
|
||||
# if Q is a loop and 1^p > 1, must normalize
|
||||
if (IsLoop( Q ) and (not 1^p = 1)) then
|
||||
p := p * (1, 1^p );
|
||||
fi;
|
||||
inv_p := Inverse( p );
|
||||
ct := List([1..Size(Q)], i-> List([1..Size(Q)], j ->
|
||||
( ctQ[ i^inv_p ][ j^inv_p ] )^p
|
||||
) );
|
||||
if IsLoop( Q ) then return LoopByCayleyTable( ct ); fi;
|
||||
return QuasigroupByCayleyTable( ct );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsomorphicCopyByNormalSubloop( L, S )
|
||||
##
|
||||
## Given a loop <L> and its normal subloop <S>, it returns an isomorphic
|
||||
## copy of <L> with elements reordered according to right cosests of S
|
||||
|
||||
InstallMethod( IsomorphicCopyByNormalSubloop, "for two loops",
|
||||
[ IsLoop, IsLoop ],
|
||||
function( L, S )
|
||||
local p;
|
||||
if not IsNormal( L, S ) then
|
||||
Error( "LOOPS: <2> must be a normal subloop of <1>");
|
||||
fi;
|
||||
# (PROG) SortingPerm is used rather than PermList since L is not necessarily canonical
|
||||
p := Inverse( SortingPerm( PosInParent( Concatenation( RightCosets( L, S ) ) ) ) );
|
||||
return IsomorphicCopyByPerm( L, p );
|
||||
end);
|
||||
|
||||
|
||||
#############################################################################
|
||||
## AUTOMORPHISMS AND AUTOMORPHISM GROUPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_AutomorphismsFixingSet( S, Q, GenQ, DisQ )
|
||||
##
|
||||
## Auxiliary function.
|
||||
## Given a quasigroup <Q>, its subset <S>, the efficient generators
|
||||
## <GenQ> of <Q> and and invariant subsets <DisQ> of <Q>, it returns all
|
||||
## automorphisms of <Q> fixing the set <S> pointwise.
|
||||
|
||||
InstallGlobalFunction( LOOPS_AutomorphismsFixingSet,
|
||||
function( S, Q, GenQ, DisQ )
|
||||
local n, x, A, possible_images, y, i, map, g;
|
||||
|
||||
# this is faster than extending a map
|
||||
n := Size( Q );
|
||||
S := Subquasigroup( Q, S ); # can be empty if Q is not a loop
|
||||
if Size( S ) = n then return []; fi; # identity, no need to return
|
||||
S := List( S, x -> Position( Elements( Q ), x ) );
|
||||
|
||||
#pruning blocks
|
||||
DisQ := List( DisQ, B -> Filtered( B, x -> not x in S ) );
|
||||
|
||||
# first unmapped generator
|
||||
x := GenQ[ 1 ];
|
||||
GenQ := GenQ{[2..Length(GenQ)]};
|
||||
|
||||
A := [];
|
||||
|
||||
possible_images := Filtered( DisQ[ LOOPS_SublistPosition( DisQ, x ) ], y -> y <> x );
|
||||
for y in possible_images do
|
||||
# constructing map
|
||||
map := 0*[1..n];
|
||||
for i in [1..n] do if i in S then map[ i ] := i; fi; od;
|
||||
map[ x ] := y;
|
||||
g := [ map, Union( S, [ x ] ), Union( S, [ y ] ) ];
|
||||
# extending map
|
||||
g := LOOPS_ExtendIsomorphism( g, CayleyTable( Q ), GenQ, DisQ, CayleyTable( Q ), DisQ );
|
||||
if not g = fail then AddSet( A, g[ 1 ] ); fi;
|
||||
od;
|
||||
|
||||
S := Union( S, [ x ] );
|
||||
return Union( A, LOOPS_AutomorphismsFixingSet( S, Q, GenQ, DisQ ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F AutomorphismGroup( Q )
|
||||
##
|
||||
## Returns the automorphism group of a quasigroup <Q>,
|
||||
## as a permutation group on [1..Size(Q)].
|
||||
|
||||
InstallOtherMethod( AutomorphismGroup, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
local DisQ, GenQ, A;
|
||||
# making sure Q has canonical Cayley table
|
||||
if not Q = Parent( Q ) then Q := CanonicalCopy( Q ); fi;
|
||||
DisQ := Discriminator( Q );
|
||||
GenQ := LOOPS_EfficientGenerators( Q, DisQ );
|
||||
|
||||
if IsLoop( Q ) then
|
||||
A := LOOPS_AutomorphismsFixingSet( [ 1 ], Q, GenQ, DisQ[2] );
|
||||
else
|
||||
A := LOOPS_AutomorphismsFixingSet( [ ], Q, GenQ, DisQ[2] );
|
||||
fi;
|
||||
|
||||
if IsEmpty( A ) then return Group( () ); fi; # no notrivial automorphism
|
||||
return Group( List( A, p -> SortingPerm( p ) ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## ISOTOPISM OF LOOPS
|
||||
## ------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsotopismLoops( L1, L2 )
|
||||
##
|
||||
## If L1, L2 are isotopic loops, returns true, else fail.
|
||||
|
||||
# (MATH) First we calculate all principal loop isotopes of L1 of the form
|
||||
# PrincipalLoopIsotope(L1, f, g), where f, g, are elements of L1.
|
||||
# Then we filter these up to isomorphism. If L2 is isotopic to L1, then
|
||||
# L2 is isomorphic to one of these principal isotopes.
|
||||
|
||||
InstallMethod( IsotopismLoops, "for two loops",
|
||||
[ IsLoop, IsLoop ],
|
||||
function( L1, L2 )
|
||||
local istps, fg, f, g, L, phi, pos, alpha, beta, gamma, p;
|
||||
|
||||
# make all loops canonical to be able to calculate isotopisms
|
||||
if not L1 = Parent( L1 ) then L1 := LoopByCayleyTable( CayleyTable( L1 ) ); fi;
|
||||
if not L2 = Parent( L2 ) then L2 := LoopByCayleyTable( CayleyTable( L2 ) ); fi;
|
||||
|
||||
# first testing for isotopic invariants
|
||||
if not Size(L1)=Size(L2) then return fail; fi;
|
||||
if IsomorphismLoops( Center(L1), Center(L2) ) = fail then return fail; fi;
|
||||
if IsomorphismLoops( LeftNucleus(L1), LeftNucleus(L2) ) = fail then return fail; fi;
|
||||
if IsomorphismLoops( RightNucleus(L1), RightNucleus(L2) ) = fail then return fail; fi;
|
||||
if IsomorphismLoops( MiddleNucleus(L1), MiddleNucleus(L2) ) = fail then return fail; fi;
|
||||
# we could test for isomorphism among multiplication groups and inner mapping group, too
|
||||
if not Size(MultiplicationGroup(L1)) = Size(MultiplicationGroup(L2)) then return fail; fi;
|
||||
if not Size(InnerMappingGroup(L1)) = Size(InnerMappingGroup(L2)) then return fail; fi;
|
||||
|
||||
# now trying to construct an isotopism
|
||||
istps := [];
|
||||
fg := [];
|
||||
for f in L1 do for g in L1 do
|
||||
Add(istps, PrincipalLoopIsotope( L1, f, g ));
|
||||
Add(fg, [ f, g ] );
|
||||
od; od;
|
||||
for L in LoopsUpToIsomorphism( istps ) do
|
||||
phi := IsomorphismLoops( L, L2 );
|
||||
if not phi = fail then
|
||||
# must reconstruct the isotopism (alpha, beta, gamma)
|
||||
# first figure out what f and g were
|
||||
pos := Position( istps, L );
|
||||
f := fg[ pos ][ 1 ];
|
||||
g := fg[ pos ][ 2 ];
|
||||
alpha := RightTranslation( L1, g );
|
||||
beta := LeftTranslation( L1, f );
|
||||
# we also applied an isomorphism (1,f*g) inside PrincipalLoopIsotope
|
||||
p := Position( L1, f*g );
|
||||
gamma := ();
|
||||
if p > 1 then
|
||||
alpha := alpha * (1,p);
|
||||
beta := beta * (1,p);
|
||||
gamma := gamma * (1,p);
|
||||
fi;
|
||||
# finally, we apply the isomorphism phi
|
||||
alpha := alpha * phi;
|
||||
beta := beta * phi;
|
||||
gamma := gamma * phi;
|
||||
return [ alpha, beta, gamma ];
|
||||
fi;
|
||||
od;
|
||||
return fail;
|
||||
end);
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopsUpToIsotopism( ls )
|
||||
##
|
||||
## Given a list <ls> of loops, returns a sublist of <ls> consisting
|
||||
## of represenatives of isotopism classes of <ls>. VERY SLOW!
|
||||
|
||||
InstallMethod( LoopsUpToIsotopism, "for a list of loops",
|
||||
[ IsList ],
|
||||
function( ls )
|
||||
local loops, L, is_new_loop, K, M, istps, f, g;
|
||||
# making sure only loops are on the list
|
||||
if not IsEmpty( Filtered( ls, x -> not IsLoop( x ) ) ) then
|
||||
Error("LOOPS: <1> must be a list of loops");
|
||||
fi;
|
||||
loops := [];
|
||||
for L in ls do
|
||||
is_new_loop := true;
|
||||
# find all principal isotopes of L up to isomorphism
|
||||
istps := [];
|
||||
for f in L do for g in L do
|
||||
Add(istps, PrincipalLoopIsotope( L, f, g ) );
|
||||
od; od;
|
||||
istps := LoopsUpToIsomorphism(istps);
|
||||
# check if any is isomorphic to a found loop
|
||||
for K in loops do for M in istps do
|
||||
if not IsomorphismLoops( K, M ) = fail then
|
||||
is_new_loop := false; break;
|
||||
fi;
|
||||
od; od;
|
||||
if is_new_loop then Add( loops, L ); fi;
|
||||
od;
|
||||
return loops;
|
||||
end);
|
11
gap/memory.gd
Normal file
11
gap/memory.gd
Normal file
|
@ -0,0 +1,11 @@
|
|||
############################################################################
|
||||
##
|
||||
#W memory.gd Memory management [loops]
|
||||
##
|
||||
#H @(#)$Id: memory.gd, v 3.0.0 2015/06/18 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
DeclareGlobalFunction( "LOOPS_FreeMemory" );
|
31
gap/memory.gi
Normal file
31
gap/memory.gi
Normal file
|
@ -0,0 +1,31 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W memory.gi Memory management [loops]
|
||||
##
|
||||
#H @(#)$Id: memory.gi, v 3.3.0 2016/10/20 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_FreeMemory( )
|
||||
##
|
||||
## Frees memory by unbinding some global variables, mostly those
|
||||
## declared during activation of libraries.
|
||||
## Returns the amount of freed memory in kbytes.
|
||||
|
||||
InstallGlobalFunction( LOOPS_FreeMemory, function( )
|
||||
# RCC loops
|
||||
LOOPS_rcc_transitive_groups := [];
|
||||
LOOPS_rcc_sections := List( [1..Length(LOOPS_rcc_data[1])], i-> [] );
|
||||
LOOPS_rcc_conjugacy_classes := [ [], [] ];
|
||||
# automorphic loops
|
||||
LOOPS_automorphic_cocycles := [];
|
||||
LOOPS_automorphic_coordinates := [];
|
||||
GASMAN("collect");
|
||||
return GasmanStatistics().full.deadkb;
|
||||
end);
|
||||
|
||||
|
17
gap/mlt_search.gd
Normal file
17
gap/mlt_search.gd
Normal file
|
@ -0,0 +1,17 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W mlt_search.gd Realizing groups as multiplication groups of loops [loops]
|
||||
##
|
||||
#H @(#)$Id: mlt_search.gd, v 3.0.0 2015/06/12 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
DeclareOperation( "AllLoopTablesInGroup", [IsGroup] );
|
||||
DeclareOperation( "AllProperLoopTablesInGroup", [IsGroup] );
|
||||
DeclareOperation( "OneLoopTableInGroup", [IsGroup] );
|
||||
DeclareOperation( "OneProperLoopTableInGroup", [IsGroup] );
|
||||
DeclareOperation( "AllLoopsWithMltGroup", [IsGroup] );
|
||||
DeclareOperation( "OneLoopWithMltGroup", [IsGroup] );
|
||||
|
607
gap/mlt_search.gi
Normal file
607
gap/mlt_search.gi
Normal file
|
@ -0,0 +1,607 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W mlt_search.gi Realizing groups as multiplication groups of loops [loops]
|
||||
##
|
||||
#H @(#)$Id: mlt_search.gi, v 3.0.0 2015/06/12 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
##########################################################################
|
||||
#
|
||||
# The following commands look for loops such that the
|
||||
# multiplication group is contained in a given group <G>.
|
||||
#
|
||||
# The resulting loops are given by their right sections.
|
||||
#
|
||||
# One can speed up the search by setting the argument <depth>
|
||||
# higher; the price is much higher memory consumption.
|
||||
#
|
||||
# <depth> is optimally choosen if in the permutation group <G>
|
||||
# there are "not many" permutations fixing <depth> elements.
|
||||
#
|
||||
# You can omit this argument, or set depth=2 with no harm.
|
||||
#
|
||||
# The parameter <infolevel> determines the amount of information
|
||||
# you get during the search.
|
||||
#
|
||||
# With infolevel=1, you get the information on timing and hits.
|
||||
# With infolevel=2, the results are printed, as well.
|
||||
#
|
||||
##########################################################################
|
||||
#
|
||||
# AllLoopTablesInGroup(G[, depth[, infolevel]] )
|
||||
# returns all loop tables <T> such that Mlt(T)<=G
|
||||
#
|
||||
# AllProperLoopTablesInGroup(G[, depth[, infolevel]] )
|
||||
# returns all non-associative loop tables <T> such that Mlt(T)<=G
|
||||
#
|
||||
# OneLoopTableInGroup(G[, depth[, infolevel]] )
|
||||
# returns one loop table <T> such that Mlt(T)<=G
|
||||
#
|
||||
# OneProperLoopTableInGroup(G[, depth[, infolevel]] )
|
||||
# returns one non-associative loop table <T> such that Mlt(T)<=G
|
||||
#
|
||||
# AllLoopsWithMltGroup(G[, depth[, infolevel]] )
|
||||
# returns all loop tables <T> such that Mlt(T)=G
|
||||
#
|
||||
# OneLoopWithMltGroup(G[, depth[, infolevel]] )
|
||||
# returns one loop table <T> such that Mlt(T)=G
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
# Debugging
|
||||
LOOPS_SearchRuntime:=function(tstart)
|
||||
return Concatenation("[ ", StringTime(Runtime()-tstart)," ]");
|
||||
end;
|
||||
|
||||
LOOPS_SearchInfo:=NewInfoClass("InfoCayleySearch");
|
||||
SetInfoLevel(LOOPS_SearchInfo,1);
|
||||
|
||||
|
||||
# LOOPS_TableSearchNC(G, depth, infolevel, task)
|
||||
# restiction in ["", "all", "all proper", "one", "one proper", "all exact", "one exact"]
|
||||
# if task="" then task:="all";
|
||||
|
||||
# auxiliary function
|
||||
LOOPS_TableSearchNC:=function(g, ldepth, infolevel, task)
|
||||
local
|
||||
# task variables
|
||||
only_one, only_proper, only_exact, takeit,
|
||||
# local external variables
|
||||
results, V, hash, row, pi, col_chunks, degree, level, depth,
|
||||
# local scanner functions
|
||||
move_right, next_node, possible_next_rows,
|
||||
# main search loop variables
|
||||
fpf_classes, reps, x0, x1, k, i, j, ct, ls, rs,
|
||||
# debugging
|
||||
old_infolevel,time_start;
|
||||
|
||||
# setting the local external variables
|
||||
results:=[];
|
||||
V:=[];
|
||||
hash:=[];
|
||||
row:=[];
|
||||
pi:=[];
|
||||
col_chunks:=[];
|
||||
degree:=0;
|
||||
level:=0;
|
||||
depth:=0;
|
||||
|
||||
# functions
|
||||
move_right:=function()
|
||||
local i;
|
||||
if level=0 then
|
||||
return false;
|
||||
fi;
|
||||
if pi[level]<>[] then
|
||||
row[level]:=Remove(pi[level],1);
|
||||
for i in [1..degree] do
|
||||
col_chunks[i][level]:=i^V[row[level]];
|
||||
od;
|
||||
return true;
|
||||
else
|
||||
Unbind(row[level]);
|
||||
Unbind(pi[level]);
|
||||
for i in [1..degree] do
|
||||
Unbind(col_chunks[i][level]);
|
||||
od;
|
||||
level:=level-1;
|
||||
return move_right();
|
||||
fi;
|
||||
end;
|
||||
|
||||
# next_node()
|
||||
# we call it when
|
||||
# a) row[level+1] and pi[level+1] are not defined ---> MOVE RIGHT
|
||||
# b) row[level+1] is not defined and pi[level+1] is defined
|
||||
# and non empty ---> MOVE DOWN
|
||||
next_node:=function()
|
||||
local i;
|
||||
if IsBound(pi[level+1])
|
||||
and (not IsBound(row[level+1]))
|
||||
and (pi[level+1]<>[])
|
||||
then
|
||||
level:=level+1;
|
||||
row[level]:=Remove(pi[level],1);
|
||||
for i in [1..degree] do
|
||||
col_chunks[i][level]:=i^V[row[level]];
|
||||
od;
|
||||
return true;
|
||||
elif (not IsBound(pi[level+1])) and (not IsBound(row[level+1])) then
|
||||
return move_right();
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end;
|
||||
|
||||
possible_next_rows:=function()
|
||||
local np,vp,dp;
|
||||
dp:=Minimum(level, depth);
|
||||
np:=List([2..degree],i->Sum([1..dp],j->(col_chunks[i][j]-1)*degree^(dp-j))+1);
|
||||
if ForAny(np,i->not(IsBound(hash[dp][i]))) then
|
||||
pi[level+1]:=[];
|
||||
return false;
|
||||
fi;
|
||||
np:=List(np,i->[hash[dp][i][1]..hash[dp][i][1]+hash[dp][i][2]]);
|
||||
np:=List(np,x->Set(V{x},y->(level+1)^y));
|
||||
vp:=List(Cartesian(np{[1..depth-1]}),x->Concatenation([level+1],x));
|
||||
vp:=List(vp,x->Sum([1..depth],j->(x[j]-1)*degree^(depth-j))+1);
|
||||
vp:=Filtered(vp,i->IsBound(hash[depth][i]));
|
||||
vp:=List(vp,i->[hash[depth][i][1]..hash[depth][i][1]+hash[depth][i][2]]);
|
||||
vp:=Filtered(Concatenation(vp),x->
|
||||
ForAll([2..degree],i->(i^V[x] in np[i-1]) and not(i^V[x] in col_chunks[i]))
|
||||
);
|
||||
pi[level+1]:=vp;
|
||||
return vp<>[];
|
||||
end;
|
||||
|
||||
# main search part
|
||||
# printing the task ["", "all", "all proper", "one", "one proper", "all exact", "one exact"]
|
||||
|
||||
old_infolevel:=InfoLevel(LOOPS_SearchInfo);
|
||||
SetInfoLevel(LOOPS_SearchInfo,infolevel);
|
||||
|
||||
if task="" then task:="all"; fi;
|
||||
if task="all" then
|
||||
only_one:=false;
|
||||
only_proper:=false;
|
||||
only_exact:=false;
|
||||
Info(LOOPS_SearchInfo, 1, "### Search for all loops in the given group ###");
|
||||
elif task="all proper" then
|
||||
only_one:=false;
|
||||
only_proper:=true;
|
||||
only_exact:=false;
|
||||
Info(LOOPS_SearchInfo, 1, "### Search for all nonassociative loops in the given group ###");
|
||||
elif task="one" then
|
||||
only_one:=true;
|
||||
only_proper:=false;
|
||||
only_exact:=false;
|
||||
Info(LOOPS_SearchInfo, 1, "### Search for one loop in the given group ###");
|
||||
elif task="one proper" then
|
||||
only_one:=true;
|
||||
only_proper:=true;
|
||||
only_exact:=false;
|
||||
Info(LOOPS_SearchInfo, 1, "### Search for one nonassociative loops loops in the given group ###");
|
||||
elif task="all exact" then
|
||||
only_one:=false;
|
||||
only_proper:=false;
|
||||
only_exact:=true;
|
||||
Info(LOOPS_SearchInfo, 1, "### Search for all loops with given multiplication group ###");
|
||||
elif task="one exact" then
|
||||
only_one:=true;
|
||||
only_proper:=false;
|
||||
only_exact:=true;
|
||||
Info(LOOPS_SearchInfo, 1, "### Search for one loop with given multiplication group ###");
|
||||
else
|
||||
SetInfoLevel(LOOPS_SearchInfo,old_infolevel);
|
||||
return fail;
|
||||
fi;
|
||||
|
||||
time_start:=Runtime();
|
||||
degree:=NrMovedPoints(g);
|
||||
depth:=ldepth;
|
||||
Info(LOOPS_SearchInfo, 1, "# Size of the input group: ", Size(g));
|
||||
Info(LOOPS_SearchInfo, 1, "# Degree of the permutation group: ", degree);
|
||||
|
||||
# analysis of the fixed point free elements
|
||||
fpf_classes:=Filtered(ConjugacyClasses(g),x->NrMovedPoints(Representative(x))=degree);
|
||||
if Length(fpf_classes)=1 then
|
||||
V:=Elements(fpf_classes[1]);
|
||||
else
|
||||
V:=Union(fpf_classes);;
|
||||
fi;
|
||||
MakeImmutable(V);
|
||||
reps:=List(fpf_classes,x->Minimum(Elements(x)));
|
||||
reps:=Set(reps,x->Position(V,x));
|
||||
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start), " Search started.");
|
||||
|
||||
Info(LOOPS_SearchInfo, 1, "# Collected the fixed point free elements." );
|
||||
Info(LOOPS_SearchInfo, 1, "# Number of conjugugacy classes = ", Size(reps), "." );
|
||||
Info(LOOPS_SearchInfo, 1, "# Number of fixed point free elements = ", Size(V), "." );
|
||||
|
||||
# hash tables
|
||||
hash:=List([1..depth],i->[]);
|
||||
x0:=0*[1..depth];
|
||||
k:=0*[1..depth];
|
||||
for j in [1..Length(V)] do
|
||||
x1:=List([1..depth],i->i^V[j]);
|
||||
for i in [1..depth] do
|
||||
if x0{[1..i]}<>x1{[1..i]} then
|
||||
k[i]:=Sum([1..i],t->(x1[t]-1)*degree^(i-t))+1;
|
||||
hash[i][k[i]]:=[j,0];
|
||||
else
|
||||
hash[i][k[i]][2]:=hash[i][k[i]][2]+1;
|
||||
fi;
|
||||
od;
|
||||
x0:=ShallowCopy(x1);
|
||||
od;
|
||||
MakeImmutable(hash);
|
||||
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start), " Hash table of depth ", depth, " created." );
|
||||
|
||||
# initialization
|
||||
row:=['*'];
|
||||
pi:=[[],ShallowCopy(reps)];
|
||||
col_chunks:=List([1..degree],i->[i]);
|
||||
level:=1;
|
||||
results:=[];
|
||||
|
||||
while next_node() do
|
||||
if level=2 then
|
||||
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start),
|
||||
" We have ", Length(pi[2])+1, " more step(s)." );
|
||||
fi;
|
||||
if level=degree then
|
||||
ct:=List(V{row{[2..degree]}},ListPerm);
|
||||
ct:=Concatenation([[1..degree]],ct);
|
||||
rs:=Immutable(List(ct,PermList));
|
||||
ls:=Immutable(List(TransposedMat(ct),PermList));
|
||||
if only_exact then
|
||||
takeit:=(Group(Union(ls,rs))=g);
|
||||
elif only_proper then
|
||||
takeit:=(ForAll(ls,y->y in g) and
|
||||
not ForAll(rs,x->ForAll(x*rs,y->y in rs)));
|
||||
else
|
||||
takeit:=ForAll(ls,y->y in g);
|
||||
fi;
|
||||
if takeit then
|
||||
Add(results, ShallowCopy(row));
|
||||
Info(LOOPS_SearchInfo, 2, "##############################");
|
||||
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start),
|
||||
" Hit number ", Length(results));
|
||||
Info(LOOPS_SearchInfo, 2, ct);
|
||||
if only_one then break; fi;
|
||||
fi;
|
||||
else
|
||||
possible_next_rows();
|
||||
if pi[level+1]=[] then Unbind(pi[level+1]); fi;
|
||||
fi;
|
||||
od;
|
||||
|
||||
Info(LOOPS_SearchInfo, 1, "##############################");
|
||||
Info(LOOPS_SearchInfo, 1, "# ", LOOPS_SearchRuntime(time_start), " Finished. ", Length(results), " loops found." );
|
||||
|
||||
SetInfoLevel(LOOPS_SearchInfo,old_infolevel);
|
||||
return List(results,x->Concatenation([()],V{x{[2..degree]}}));
|
||||
end;
|
||||
|
||||
# auxiliary function
|
||||
LOOPS_SearchInputCheck:=function( G, depth, infolevel, task )
|
||||
local degree;
|
||||
if not IsPermGroup(G) then
|
||||
Info( InfoWarning, 1, "<G> must be a permutation group" );
|
||||
return false;
|
||||
fi;
|
||||
degree:=NrMovedPoints(G);
|
||||
if MovedPoints(G)<>[1..degree] then
|
||||
Info( InfoWarning, 1, "<G> must act transitively on [1..degree]" );
|
||||
return false;
|
||||
fi;
|
||||
if depth=fail then depth:=LogInt(Size(G),degree)-1; fi;
|
||||
if not IsInt(depth) or depth<=0 then
|
||||
Info( InfoWarning, 1, "<depth> must be a positive integer" );
|
||||
return false;
|
||||
fi;
|
||||
if infolevel=fail then infolevel:=1; fi;
|
||||
if not IsInt(infolevel) or infolevel<0 then
|
||||
Info( InfoWarning, 1, "<infolevel> must be a positive integer" );
|
||||
return false;
|
||||
fi;
|
||||
if not task in ["", "all", "all proper", "one", "one proper", "all exact", "one exact"] then
|
||||
Info( InfoWarning, 1, "<task> must be one of the following:");
|
||||
Info( InfoWarning, 1, "\t\"\", \"all\", \"all proper\", \"one\", ",
|
||||
"\"one proper\", \"all exact\", \"one exact\"." );
|
||||
return false;
|
||||
fi;
|
||||
return [G,depth,infolevel,task];
|
||||
end;
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O AllLoopTablesInGroup(G[, depth[, infolevel]] )
|
||||
##
|
||||
## returns all loop tables <T> such that Mlt(T)<=G.
|
||||
|
||||
InstallMethod( AllLoopTablesInGroup, "for a group",
|
||||
[ IsGroup ],
|
||||
function( G )
|
||||
local depth, infolevel, task, a;
|
||||
task:="all";
|
||||
depth:=fail;
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( AllLoopTablesInGroup, "for a group and integer",
|
||||
[ IsGroup, IsInt ],
|
||||
function( G, depth )
|
||||
local infolevel, task, a;
|
||||
task:="all";
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( AllLoopTablesInGroup, "for a group and two integers",
|
||||
[ IsGroup, IsInt, IsInt ],
|
||||
function( G, depth, infolevel )
|
||||
local task, a;
|
||||
task:="all";
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O AllProperLoopTablesInGroup(G[, depth[, infolevel]] )
|
||||
##
|
||||
## returns all non-associative loop tables <T> such that Mlt(T)<=G.
|
||||
|
||||
InstallMethod( AllProperLoopTablesInGroup, "for a group",
|
||||
[ IsGroup ],
|
||||
function( G )
|
||||
local depth, infolevel, task, a;
|
||||
task:="all proper";
|
||||
depth:=fail;
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( AllProperLoopTablesInGroup, "for a group and integer",
|
||||
[ IsGroup, IsInt ],
|
||||
function( G, depth )
|
||||
local infolevel, task, a;
|
||||
task:="all proper";
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( AllProperLoopTablesInGroup, "for a group and two integers",
|
||||
[ IsGroup, IsInt, IsInt ],
|
||||
function( G, depth, infolevel )
|
||||
local task, a;
|
||||
task:="all proper";
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O OneLoopTableInGroup(G[, depth[, infolevel]] )
|
||||
##
|
||||
## returns one loop table <T> such that Mlt(T)<=G.
|
||||
|
||||
InstallMethod( OneLoopTableInGroup, "for a group",
|
||||
[ IsGroup ],
|
||||
function( G )
|
||||
local depth, infolevel, task, a;
|
||||
task:="one";
|
||||
depth:=fail;
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( OneLoopTableInGroup, "for a group and integer",
|
||||
[ IsGroup, IsInt ],
|
||||
function( G, depth )
|
||||
local infolevel, task, a;
|
||||
task:="one";
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( OneLoopTableInGroup, "for a group and two integers",
|
||||
[ IsGroup, IsInt, IsInt ],
|
||||
function( G, depth, infolevel )
|
||||
local task, a;
|
||||
task:="one";
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O OneProperLoopTableInGroup(G[, depth[, infolevel]] )
|
||||
##
|
||||
## returns one non-associative loop table <T> such that Mlt(T)<=G.
|
||||
|
||||
InstallMethod( OneProperLoopTableInGroup, "for a group",
|
||||
[ IsGroup ],
|
||||
function( G )
|
||||
local depth, infolevel, task, a;
|
||||
task:="one proper";
|
||||
depth:=fail;
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( OneProperLoopTableInGroup, "for a group and integer",
|
||||
[ IsGroup, IsInt ],
|
||||
function( G, depth )
|
||||
local infolevel, task, a;
|
||||
task:="one proper";
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( OneProperLoopTableInGroup, "for a group and two integers",
|
||||
[ IsGroup, IsInt, IsInt ],
|
||||
function( G, depth, infolevel )
|
||||
local task, a;
|
||||
task:="one proper";
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O AllLoopsWithMltGroup(G[, depth[, infolevel]] )
|
||||
##
|
||||
## returns all loop tables <T> such that Mlt(T)=G.
|
||||
|
||||
InstallMethod( AllLoopsWithMltGroup, "for a group",
|
||||
[ IsGroup ],
|
||||
function( G )
|
||||
local depth, infolevel, task, a;
|
||||
task:="all exact";
|
||||
depth:=fail;
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( AllLoopsWithMltGroup, "for a group and integer",
|
||||
[ IsGroup, IsInt ],
|
||||
function( G, depth )
|
||||
local infolevel, task, a;
|
||||
task:="all exact";
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( AllLoopsWithMltGroup, "for a group and two integers",
|
||||
[ IsGroup, IsInt, IsInt ],
|
||||
function( G, depth, infolevel )
|
||||
local task, a;
|
||||
task:="all exact";
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O OneLoopWithMltGroup(G[, depth[, infolevel]] )
|
||||
##
|
||||
## returns one loop table <T> such that Mlt(T)=G.
|
||||
|
||||
InstallMethod( OneLoopWithMltGroup, "for a group",
|
||||
[ IsGroup ],
|
||||
function( G )
|
||||
local depth, infolevel, task, a;
|
||||
task:="one exact";
|
||||
depth:=fail;
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( OneLoopWithMltGroup, "for a group and integer",
|
||||
[ IsGroup, IsInt ],
|
||||
function( G, depth )
|
||||
local infolevel, task, a;
|
||||
task:="one exact";
|
||||
infolevel:=fail;
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
||||
InstallOtherMethod( OneLoopWithMltGroup, "for a group and two integers",
|
||||
[ IsGroup, IsInt, IsInt ],
|
||||
function( G, depth, infolevel )
|
||||
local task, a;
|
||||
task:="one exact";
|
||||
a:=LOOPS_SearchInputCheck(G,depth,infolevel,task);
|
||||
if a<>false then
|
||||
return LOOPS_TableSearchNC(a[1],a[2],a[3],a[4]);
|
||||
else
|
||||
return fail;
|
||||
fi;
|
||||
end);
|
||||
|
31
gap/moufang_modifications.gd
Normal file
31
gap/moufang_modifications.gd
Normal file
|
@ -0,0 +1,31 @@
|
|||
############################################################################
|
||||
##
|
||||
#W moufang_modifications.gd Moufang modifications [loops]
|
||||
##
|
||||
#H @(#)$Id: moufang_modifications.gd, v 2.0.0 2008/01/21 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## LOOPS M(G,2)
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "LoopMG2", IsGroup );
|
||||
|
||||
#############################################################################
|
||||
## CYCLIC MODIFICATION
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "LoopByCyclicModification" );
|
||||
|
||||
#############################################################################
|
||||
## DIHEDRAL MODIFICATION
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "LoopByDihedralModification" );
|
||||
|
||||
#############################################################################
|
||||
## AUXILIARY
|
||||
## -------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "LOOPS_PositionList" );
|
||||
DeclareGlobalFunction( "LOOPS_Modular" );
|
||||
DeclareGlobalFunction( "LOOPS_DVSigma" );
|
218
gap/moufang_modifications.gi
Normal file
218
gap/moufang_modifications.gi
Normal file
|
@ -0,0 +1,218 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W moufang_modifications.gi Moufang modifications [loops]
|
||||
##
|
||||
#H @(#)$Id: moufang_modifications.gi, v 3.0.0 2015/06/15 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## LOOPS M(G,2)
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LoopMG2( G )
|
||||
##
|
||||
## Returns the Chein loop M(G,2) constructed from a group <G>.
|
||||
## See documentation for details.
|
||||
|
||||
InstallGlobalFunction( LoopMG2, function( G )
|
||||
|
||||
local T, inv, n, L, i, j;
|
||||
|
||||
T := MultiplicationTable( Elements( G ) );
|
||||
n := Size( G );
|
||||
inv := List( [1..n], i->Position( T[i], 1 ) ); #inverses
|
||||
L := List( [1..2*n], i->[]);
|
||||
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
L[ i ][ j ] := T[ i ][ j ]; # g*h = gh
|
||||
L[ i ][ j+n ] := T[ j ][ i ] + n; # g*hu = (hg)u
|
||||
L[ i+n ][ j ] := T[ i ][ inv[ j ] ] + n; # gu*h = (gh^{-1})u
|
||||
L[ i+n ][ j+n ] := T[ inv[ j ] ][ i ]; # gu*hu = h^{-1}g
|
||||
od; od;
|
||||
|
||||
return LoopByCayleyTable( L );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## AUXILIARY FUNCTIONS FOR MOUFANG MODIFICATIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_PositionList( A, B )
|
||||
##
|
||||
## input: lists A, B
|
||||
## returns: list P, where P[i] is the position of B[i] in A
|
||||
|
||||
InstallGlobalFunction( LOOPS_PositionList,
|
||||
function( A, B )
|
||||
local P, b;
|
||||
P := [];
|
||||
for b in B do Add( P, Position( A, b ) ); od;
|
||||
return P;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_Modular( i, m )
|
||||
##
|
||||
## returns i modulo the set [-m+1..m]
|
||||
|
||||
InstallGlobalFunction( LOOPS_Modular,
|
||||
function(i, m)
|
||||
while i>m do i:=i-2*m; od;
|
||||
while i<1-m do i:=i+2*m; od;
|
||||
return i;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_DVSigma( i, m )
|
||||
##
|
||||
## Calculates overflow of i modulo [-m+1..m].
|
||||
## See documentation for more details.
|
||||
|
||||
InstallGlobalFunction( LOOPS_DVSigma,
|
||||
function( i, m )
|
||||
if i > m then return 1; fi;
|
||||
if i < 1 - m then return -1; fi;
|
||||
return 0;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## CYCLIC MODIFICATION
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LoopByCyclicModification( L, S, a, h)
|
||||
##
|
||||
## Returns the cyclic modification of Moufang loop <L> with parameters
|
||||
## <S>, <a>, <h>, as described in the documentation.
|
||||
|
||||
# (MATH)
|
||||
# L is a loop,
|
||||
# S is a normal subloop of L, or a set generating a normal subloop of L,
|
||||
# L/S is cyclic of order 2m, generated by aS,
|
||||
# h is an element of Z(L) and S
|
||||
# (PROG)
|
||||
# NOTHING IS CHECKED!!
|
||||
|
||||
InstallGlobalFunction( LoopByCyclicModification, function( L, S, a, h )
|
||||
|
||||
local n, ih, m, aP, aa, i, iL, T, x, y, z, exponent;
|
||||
|
||||
# making sure that S is a subloop of L
|
||||
if not IsLoop( S ) then S := Subloop( L, S ); fi;
|
||||
|
||||
# converting all into numbers for faster calculation
|
||||
n := Size( L );
|
||||
ih := Position( Elements( L ), h^(-1) ); #inverse of h
|
||||
h := Position( Elements( L ), h );
|
||||
a := Position( Elements( L ), a );
|
||||
S := LOOPS_PositionList( Elements( L ), Elements( S ) );
|
||||
L := CayleyTable( L );
|
||||
|
||||
# setting parameter m of the construction
|
||||
m := n / ( 2 * Length( S ) );
|
||||
|
||||
# calculating all cosets a^i, for i in M
|
||||
aP := []; aa := 1;
|
||||
for i in [ 0..2*m-1 ] do
|
||||
Add( aP, List( S, j -> L[ aa ][ j ] ) );
|
||||
aa := L[ aa ][ a ];
|
||||
od;
|
||||
|
||||
# into which cosets belong elements of L
|
||||
iL := List( [ 1..n ], x -> LOOPS_Modular( LOOPS_SublistPosition(aP, x ) - 1, m ) );
|
||||
|
||||
# setting up the multiplication table
|
||||
T := List( [ 1..n ], i->[] );
|
||||
for x in [ 1..n ] do for y in [ 1..n ] do
|
||||
z := L[ x ][ y ];
|
||||
exponent := LOOPS_DVSigma( iL[ x ] + iL[ y ], m );
|
||||
if exponent = 1 then z := L[ z ][ h ]; fi;
|
||||
if exponent = -1 then z := L[ z ][ ih ]; fi;
|
||||
T[ x ][ y ] := z;
|
||||
od; od;
|
||||
|
||||
return LoopByCayleyTable( T );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## DIHEDRAL MODIFICATION
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LoopByDihedralModification( L, S, e, f, h)
|
||||
##
|
||||
## Returns the dihedral modification of Moufang loop <L> with parameters
|
||||
## <S>, <e>, <f>, <h>, as described in the documentation.
|
||||
|
||||
# (MATH)
|
||||
# L is a loop,
|
||||
# S is a normal subloop of L, or a set generating a normal subloop of L,
|
||||
# L/S is dihedral of order 4m,
|
||||
# eS, fS are involutions of L/S such that eS*fS is of order 2m,
|
||||
# let G0 be the union of cosets of L/S generated by eS*fS,
|
||||
# h is an element of N(L), S and Z(G0).
|
||||
# (PROG)
|
||||
# NOTHING IS CHECKED!!
|
||||
|
||||
InstallGlobalFunction( LoopByDihedralModification, function( L, S, e, f, h )
|
||||
|
||||
local a, G0, n, m, ih, aP, aa, i, eP, fP, eL, fL, T, x, y, z, exp;
|
||||
|
||||
# making sure that S is a subloop of L
|
||||
if not IsLoop( S ) then S := Subloop( L, S ); fi;
|
||||
|
||||
# obtaining a and G0
|
||||
a := e * f;
|
||||
G0:= Subloop( L, a * Elements( S ) );
|
||||
|
||||
# all in numbers
|
||||
n := Size( L );
|
||||
ih := Position( Elements( L ), h^(-1) ); #inverse of h
|
||||
h := Position( Elements( L ), h );
|
||||
a := Position( Elements( L ), a );
|
||||
e := Position( Elements( L ), e );
|
||||
f := Position( Elements( L ), f );
|
||||
S := LOOPS_PositionList( Elements( L ), Elements( S ) );
|
||||
G0 := LOOPS_PositionList( Elements( L ), Elements( G0 ) );
|
||||
L := CayleyTable( L );
|
||||
|
||||
# setting parameter m
|
||||
m := n / ( 4 * Length( S ) );
|
||||
|
||||
# powers of aS, eS and fS
|
||||
aP := []; aa := 1;
|
||||
for i in [ 0..2*m-1 ] do
|
||||
Add( aP, List( S, i -> L[ aa ][ i ] ) );
|
||||
aa := L[ aa ][ a ];
|
||||
od;
|
||||
eP := List( aP, x -> Union( List( x, y -> [ y, L[ e ][ y ] ] ) ) );
|
||||
fP := List( aP, x -> Union( List( x, y -> [ y, L[ y ][ f ] ] ) ) );
|
||||
|
||||
# into which cosets belong elements of L
|
||||
eL := List( [ 1..n ], x -> LOOPS_Modular( LOOPS_SublistPosition(eP, x ) - 1, m ) );
|
||||
fL := List( [ 1..n ], x -> LOOPS_Modular( LOOPS_SublistPosition(fP, x ) - 1, m ) );
|
||||
|
||||
# setting up multiplication table
|
||||
T := List( L, x->[] );
|
||||
for x in [ 1..n ] do for y in [ 1..n ] do
|
||||
if y in G0 then exp := LOOPS_DVSigma( eL[ x ] + fL[ y ], m );
|
||||
else exp := (-1)*LOOPS_DVSigma( eL[ x ] + fL[ y ], m ); fi;
|
||||
z := L[ x ][ y ];
|
||||
if exp = 1 then z := L[ z ][ h ]; fi;
|
||||
if exp = -1 then z := L[ z ][ ih ]; fi;
|
||||
T[ x ][ y ] := z;
|
||||
od; od;
|
||||
|
||||
return LoopByCayleyTable(T);
|
||||
end);
|
12
gap/moufang_triality.gd
Normal file
12
gap/moufang_triality.gd
Normal file
|
@ -0,0 +1,12 @@
|
|||
############################################################################
|
||||
##
|
||||
#W moufang_triality.gd Triality of Moufang loops [loops]
|
||||
##
|
||||
#H @(#)$Id: moufang_triality.gd, v 2.0.0 2008/01/21 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
DeclareGlobalFunction( "TrialityPermGroup" );
|
||||
DeclareGlobalFunction( "TrialityPcGroup" );
|
98
gap/moufang_triality.gi
Normal file
98
gap/moufang_triality.gi
Normal file
|
@ -0,0 +1,98 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W moufang_triality.gi Triality of Moufang loops [loops]
|
||||
##
|
||||
#H @(#)$Id: moufang_triality.gi, v 3.0.0 2015/06/12 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F TrialityPermGroup( L )
|
||||
##
|
||||
## Returns the triality group associated with Moufang loop <L>,
|
||||
## as a permutation group.
|
||||
|
||||
InstallGlobalFunction( TrialityPermGroup, function( L )
|
||||
local AA, BB, L_inv, i, trg_perm, trrho, trsigma;
|
||||
|
||||
|
||||
if IsGroup( L ) then L:=IntoLoop( L ); fi;
|
||||
|
||||
if not( IsMoufangLoop( L ) ) then
|
||||
Error( "LOOPS: <1> has to be a Moufang loop." );
|
||||
fi;
|
||||
|
||||
trrho := Product( List( [1..Size(L)], k -> (k,k+Size(L),k+2*Size(L)) ) );
|
||||
L_inv := PermList( List( [1..Size(L)], k -> 1^(LeftSection(L)[k]^-1) ) );
|
||||
trsigma := L_inv^(trrho^2) *
|
||||
Product( List( [1..Size(L)], k ->
|
||||
(k, Size(L)+k^L_inv ) ) );
|
||||
|
||||
AA := [];
|
||||
BB := [];
|
||||
for i in PosInParent( GeneratorsSmallest( L ) ) do
|
||||
Add( AA, (LeftSection(L)[i]*RightSection(L)[i])^-1
|
||||
* LeftSection(L)[i]^trrho
|
||||
* RightSection(L)[i]^(trrho^2) );
|
||||
od;
|
||||
BB := OnTuples( AA, trrho );
|
||||
|
||||
trg_perm := Group( Concatenation( AA, BB ) );
|
||||
SetName(trg_perm, Concatenation(
|
||||
"<Triality (dual collineation) group of order ",
|
||||
StringPP( Order( trg_perm ) ),
|
||||
">"
|
||||
));
|
||||
|
||||
return rec( group := trg_perm, sigma := trsigma, rho := trrho );
|
||||
end);
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F TrialityPcGroup( L )
|
||||
##
|
||||
## Returns the triality group associated with Moufang loop <L>,
|
||||
## as a pc group.
|
||||
|
||||
InstallGlobalFunction( TrialityPcGroup, function( L )
|
||||
local trcoll, trcoll_pcgs, pc_gens, big_gr, big_pcgs,
|
||||
trg_pc,sigma_pc,rho_pc;
|
||||
|
||||
if not( IsSolvable( MultiplicationGroup( L ) ) ) then
|
||||
Error( "LOOPS: The triality group is not solvable." );
|
||||
fi;
|
||||
|
||||
trcoll := TrialityPermGroup( L );
|
||||
if IsNilpotent( L ) then
|
||||
trcoll_pcgs := SpecialPcgs( trcoll.group );
|
||||
else
|
||||
trcoll_pcgs := Pcgs( trcoll.group );
|
||||
fi;
|
||||
|
||||
pc_gens := PcgsByPcSequence(
|
||||
FamilyObj( One( trcoll.group ) ),
|
||||
Concatenation(
|
||||
[ trcoll.sigma, trcoll.rho ],
|
||||
trcoll_pcgs
|
||||
)
|
||||
);
|
||||
|
||||
big_gr := PcGroupWithPcgs( pc_gens );
|
||||
big_pcgs := Pcgs( big_gr );
|
||||
|
||||
trg_pc := Group( big_pcgs{[3..Length(big_pcgs)]} );
|
||||
sigma_pc := big_pcgs[1];
|
||||
rho_pc := big_pcgs[2];
|
||||
|
||||
SetName(trg_pc, Concatenation(
|
||||
"<Triality pc group of order ",
|
||||
StringPP( Order( trg_pc ) ),
|
||||
">"
|
||||
));
|
||||
|
||||
return rec( group := trg_pc, sigma := sigma_pc, rho := rho_pc );
|
||||
end);
|
104
gap/quasigroups.gd
Normal file
104
gap/quasigroups.gd
Normal file
|
@ -0,0 +1,104 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W quasigroups.gd Representing, creating and displaying quasigroups [loops]
|
||||
##
|
||||
#H @(#)$Id: quasigroups.gd, v 3.2.0 2016/05/02 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## GAP CATEGORIES AND REPRESENTATIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
## element of a quasigroup
|
||||
DeclareCategory( "IsQuasigroupElement", IsMultiplicativeElement );
|
||||
DeclareRepresentation( "IsQuasigroupElmRep",
|
||||
IsPositionalObjectRep and IsMultiplicativeElement, [1] );
|
||||
|
||||
## element of a loop
|
||||
DeclareCategory( "IsLoopElement",
|
||||
IsQuasigroupElement and IsMultiplicativeElementWithInverse );
|
||||
DeclareRepresentation( "IsLoopElmRep",
|
||||
IsPositionalObjectRep and IsMultiplicativeElementWithInverse, [1] );
|
||||
|
||||
## latin (auxiliary category for GAP to tell apart IsMagma and IsQuasigroup)
|
||||
DeclareCategory( "IsLatin", IsObject );
|
||||
|
||||
## quasigroup
|
||||
DeclareCategory( "IsQuasigroup", IsMagma and IsLatin );
|
||||
|
||||
## loop
|
||||
DeclareCategory( "IsLoop", IsQuasigroup and IsMultiplicativeElementWithInverseCollection);
|
||||
|
||||
#############################################################################
|
||||
## TESTING MULTIPLICATION TABLES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "IsQuasigroupTable", [ IsMatrix ] );
|
||||
DeclareSynonym( "IsQuasigroupCayleyTable", IsQuasigroupTable );
|
||||
DeclareOperation( "IsLoopTable", [ IsMatrix ] );
|
||||
DeclareSynonym( "IsLoopCayleyTable", IsLoopTable );
|
||||
DeclareOperation( "CanonicalCayleyTable", [ IsMatrix ] );
|
||||
DeclareOperation( "NormalizedQuasigroupTable", [ IsMatrix ] );
|
||||
|
||||
#############################################################################
|
||||
## CREATING QUASIGROUPS AND LOOPS MANUALLY
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareAttribute( "CayleyTable", IsQuasigroup );
|
||||
DeclareOperation( "QuasigroupByCayleyTable", [ IsMatrix ] );
|
||||
DeclareOperation( "LoopByCayleyTable", [ IsMatrix ] );
|
||||
DeclareOperation( "SetQuasigroupElmName", [ IsQuasigroup, IsString ] );
|
||||
DeclareSynonym( "SetLoopElmName", SetQuasigroupElmName );
|
||||
DeclareOperation( "CanonicalCopy", [ IsQuasigroup ] );
|
||||
|
||||
#############################################################################
|
||||
## CREATING QUASIGROUPS AND LOOPS FROM A FILE
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "QuasigroupFromFile", [ IsString, IsString ] );
|
||||
DeclareOperation( "LoopFromFile", [ IsString, IsString ] );
|
||||
|
||||
#############################################################################
|
||||
## CREATING QUASIGROUPS AND LOOPS BY SECTIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "CayleyTableByPerms", [ IsPermCollection ] );
|
||||
DeclareOperation( "QuasigroupByLeftSection", [ IsPermCollection ] );
|
||||
DeclareOperation( "LoopByLeftSection", [ IsPermCollection ] );
|
||||
DeclareOperation( "QuasigroupByRightSection", [ IsPermCollection ] );
|
||||
DeclareOperation( "LoopByRightSection", [ IsPermCollection ] );
|
||||
DeclareOperation( "QuasigroupByRightFolder", [ IsGroup, IsGroup, IsMultiplicativeElementCollection ] );
|
||||
DeclareOperation( "LoopByRightFolder", [ IsGroup, IsGroup, IsMultiplicativeElementCollection ] );
|
||||
|
||||
#############################################################################
|
||||
## CONVERSIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "IntoQuasigroup", [ IsMagma ] );
|
||||
DeclareOperation( "PrincipalLoopIsotope",
|
||||
[ IsQuasigroup, IsQuasigroupElement, IsQuasigroupElement ] );
|
||||
DeclareOperation( "IntoLoop", [ IsMagma ] );
|
||||
DeclareOperation( "IntoGroup", [ IsMagma ] );
|
||||
|
||||
#############################################################################
|
||||
## PRODUCTS OF QUASIGROUPS AND LOOPS
|
||||
## --------------------------------------------------------------------------
|
||||
|
||||
#DirectProduct already declared for groups.
|
||||
|
||||
#############################################################################
|
||||
## OPPOSITE QUASIGROUPS AND LOOPS
|
||||
## --------------------------------------------------------------------------
|
||||
|
||||
DeclareOperation( "OppositeQuasigroup", [ IsQuasigroup ] );
|
||||
DeclareOperation( "OppositeLoop", [ IsLoop ] );
|
||||
DeclareAttribute( "Opposite", IsQuasigroup );
|
||||
|
||||
#############################################################################
|
||||
## AUXILIARY
|
||||
## --------------------------------------------------------------------------
|
||||
DeclareGlobalFunction( "LOOPS_ReadCayleyTableFromFile" );
|
||||
DeclareGlobalFunction( "LOOPS_CayleyTableByRightFolder" );
|
836
gap/quasigroups.gi
Normal file
836
gap/quasigroups.gi
Normal file
|
@ -0,0 +1,836 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W quasigroups.gi Representing, creating and displaying quasigroups [loops]
|
||||
##
|
||||
#H @(#)$Id: creation.gi, v 3.2.0 2015/11/22 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
## TESTING MULTIPLICATION TABLES
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IsQuasigroupTable( ls )
|
||||
##
|
||||
## Returns true if <ls> is an n by n latin square with n distinct
|
||||
## integral entries.
|
||||
|
||||
InstallMethod( IsQuasigroupTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
function( ls )
|
||||
local first_row;
|
||||
# checking rows
|
||||
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 IsLoopTable( ls )
|
||||
##
|
||||
## Returns true if <ls> is a normalized latin square. An n by n latin square
|
||||
## is normalized if the first row and first column read the same, and the
|
||||
## entries in the first row are ordered.
|
||||
|
||||
InstallMethod( IsLoopTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
function( ls )
|
||||
if not IsQuasigroupTable( ls ) then return false; fi;
|
||||
return Set( ls[ 1 ] ) = ls[ 1 ]
|
||||
and ls[ 1 ] = List( [1..Length(ls)], i -> ls[ i ][ 1 ] );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O CanonicalCayleyTable( ls )
|
||||
##
|
||||
## Returns a Cayley table isomorphic to <ls>, 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.
|
||||
|
||||
InstallMethod( CanonicalCayleyTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
function( ls )
|
||||
local n, entries, i, j, T;
|
||||
n := Length( ls );
|
||||
# finding all distinct entries in the table
|
||||
entries := [];
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
AddSet( entries, ls[ i ][ j ] );
|
||||
od; od;
|
||||
# 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 NormalizedQuasigroupTable( ls )
|
||||
##
|
||||
## Given a latin square <ls>, returns the corresponding normalized
|
||||
## latin square with entries 1, ..., n.
|
||||
|
||||
InstallMethod( NormalizedQuasigroupTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
function( ls )
|
||||
local T, perm;
|
||||
if not IsQuasigroupTable( ls ) then
|
||||
Error( "LOOPS: <1> must be a latin square." );
|
||||
fi;
|
||||
# renaming the entries to be 1, ..., n
|
||||
T := CanonicalCayleyTable( ls );
|
||||
# permuting the columns so that the first row reads 1, ..., n
|
||||
perm := PermList( T[ 1 ] );
|
||||
T := List( T, row -> Permuted( row, perm ) );
|
||||
# permuting the rows so that the first column reads 1, ..., n
|
||||
return Set( T );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## CREATING QUASIGROUPS AND LOOPS MANUALLY
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#A CayleyTable( Q )
|
||||
##
|
||||
## Returns the Cayley table of the quasigroup <Q>
|
||||
|
||||
InstallMethod( CayleyTable, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
local elms, parent_elms;
|
||||
elms := Elements( Q );
|
||||
parent_elms := Elements( Parent( Q ) );
|
||||
return List( elms, x-> List( elms, y -> Position( parent_elms, x * y ) ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O QuasigroupByCayleyTable( ct )
|
||||
##
|
||||
## Returns quasigroup with multiplication table <ct>.
|
||||
|
||||
InstallMethod( QuasigroupByCayleyTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
function( ct )
|
||||
|
||||
local F, Q, elms, n;
|
||||
if not IsQuasigroupTable( ct ) then
|
||||
Error( "LOOPS: <1> must be a latin square." );
|
||||
fi;
|
||||
# Making sure that entries are 1, ..., n
|
||||
ct := CanonicalCayleyTable( ct );
|
||||
# constructing the family
|
||||
F := NewFamily( "QuasigroupByCayleyTableFam", IsQuasigroupElement );
|
||||
# installing data for the family
|
||||
n := Length ( ct );
|
||||
F!.size := n;
|
||||
elms := Immutable( List( [1..n], i -> Objectify(
|
||||
NewType( F, IsQuasigroupElement and IsQuasigroupElmRep), [ i ] ) ) );
|
||||
F!.set := elms;
|
||||
F!.cayleyTable := ct;
|
||||
F!.names := "q";
|
||||
# creating the quasigroup
|
||||
Q := Objectify( NewType( FamilyObj( elms ),
|
||||
IsQuasigroup and IsAttributeStoringRep ), rec() );
|
||||
# setting attributes for the quasigroup
|
||||
SetSize( Q, n );
|
||||
SetAsSSortedList( Q, elms );
|
||||
SetParent( Q, Q );
|
||||
SetCayleyTable( Q, ct );
|
||||
return Q;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopByCayleyTable( ct )
|
||||
##
|
||||
## Returns loop with multiplication table <ct>.
|
||||
|
||||
InstallMethod( LoopByCayleyTable, "for matrix",
|
||||
[ IsMatrix ],
|
||||
function( ct )
|
||||
local F, L, elms, n;
|
||||
if not IsLoopTable( ct ) then
|
||||
Error( "LOOPS: <1> must be a normalized latin square." );
|
||||
fi;
|
||||
# Making sure that the entries are 1, ..., n.
|
||||
# The table will remain normalized.
|
||||
ct := CanonicalCayleyTable( ct );
|
||||
# constructing the family
|
||||
F := NewFamily( "LoopByCayleyTableFam", IsLoopElement );
|
||||
# installing the data for the family
|
||||
n := Length ( ct );
|
||||
F!.size := n;
|
||||
elms := Immutable( List( [1..n], i -> Objectify(
|
||||
NewType( F, IsLoopElement and IsLoopElmRep), [ i ] ) ) );
|
||||
F!.set := elms;
|
||||
F!.cayleyTable := ct;
|
||||
F!.names := "l";
|
||||
# creating the loop
|
||||
L := Objectify( NewType( FamilyObj( elms ),
|
||||
IsLoop and IsAttributeStoringRep ), rec() );
|
||||
# setting attributes for the loop
|
||||
SetSize( L, n );
|
||||
SetAsSSortedList( L, elms );
|
||||
SetParent( L, L );
|
||||
SetCayleyTable( L, ct );
|
||||
SetOne( L, elms[ 1 ] );
|
||||
return L;
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O SetQuasigroupElmName( Q, name )
|
||||
##
|
||||
## Changes the name of elements of quasigroup or loop <Q> to <name>
|
||||
|
||||
InstallMethod( SetQuasigroupElmName, "for quasigroup and string",
|
||||
[ IsQuasigroup, IsString ],
|
||||
function( Q, name )
|
||||
local F;
|
||||
F := FamilyObj( Elements( Q )[ 1 ] );
|
||||
F!.names := name;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O CanonicalCopy( Q )
|
||||
##
|
||||
## Returns a canonical copy of <Q>, that is, an isomorphic object <O> with
|
||||
## canonical multiplication table and Parent( <O> ) = <O>.
|
||||
## (PROG) Properties and attributes are lost!
|
||||
|
||||
InstallMethod( CanonicalCopy, "for quasigroup or loop",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
if IsLoop( Q ) then
|
||||
return LoopByCayleyTable( CayleyTable( Q ) );
|
||||
fi;
|
||||
return QuasigroupByCayleyTable( CayleyTable( Q ) );
|
||||
end);
|
||||
|
||||
|
||||
#############################################################################
|
||||
## CREATING QUASIGROUPS AND LOOPS FROM A FILE
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F LOOPS_ReadCayleyTableFromFile( filename, replace_by_spaces )
|
||||
##
|
||||
## Auxiliary function. Reads the content of <filename> and tries to
|
||||
## interpret the data as a multiplication table, according to the rules
|
||||
## summarized below. If successful, it returns the multiplication table as
|
||||
## an n by n array.
|
||||
## ALGORITHM:
|
||||
## 1) the content of the file is read into one string
|
||||
## 2) all end-of-lines and all characters in the string <replace_by_spaces>
|
||||
## are replaced by spaces
|
||||
## 3) the string is split into chunks, where chunk = maximal substring
|
||||
## without spaces
|
||||
## 4) the number n of distinct chunks is found. If the number of chunks
|
||||
## is not n^2, error is announced and function terminates
|
||||
## 5) a numerical value 1 .. n is assigned to each chunk, depending on
|
||||
## its position among the distinct chunks
|
||||
## 6) multiplication table is constructed and returned
|
||||
|
||||
InstallGlobalFunction( LOOPS_ReadCayleyTableFromFile,
|
||||
function( filename, replace_by_spaces)
|
||||
|
||||
local input, s, i, chunks, started, starting_pos, z, j, distinct_chunks, c, n, T;
|
||||
|
||||
if not ( IsString( filename) and IsString( replace_by_spaces ) ) then
|
||||
Error( "LOOPS: <1> must be a file name, and <2> must be a string." );
|
||||
fi;
|
||||
|
||||
input := InputTextFile( filename );
|
||||
if input = fail then Error( "LOOPS: <1> is not a valid file name." ); fi;
|
||||
|
||||
s := ReadAll( input );
|
||||
|
||||
# removing end-of-lines, etc.
|
||||
for i in [1..Length( s )] do
|
||||
if (s[ i ] = '\n') or (s[ i ] in replace_by_spaces) then
|
||||
s[ i ] := ' ';
|
||||
fi;
|
||||
od;
|
||||
s[ Length( s ) + 1 ] := ' '; #to make sure that string ends with space
|
||||
|
||||
#parsing string into chunks separated by spaces
|
||||
chunks := [];
|
||||
started := false; starting_pos := 0;
|
||||
for i in [1..Length( s )] do
|
||||
if not started then
|
||||
if not s[ i ] = ' ' then
|
||||
started := true;
|
||||
starting_pos := i;
|
||||
fi;
|
||||
else
|
||||
if s[ i ] = ' ' then #end of chunk
|
||||
z := "";
|
||||
for j in [ starting_pos..i-1 ] do
|
||||
z[ j - starting_pos + 1 ] := s[ j ];
|
||||
od;
|
||||
Add( chunks, z );
|
||||
started := false;
|
||||
fi;
|
||||
fi;
|
||||
od;
|
||||
|
||||
distinct_chunks := [];
|
||||
for c in chunks do if not c in distinct_chunks then
|
||||
Add( distinct_chunks, c );
|
||||
fi; od;
|
||||
|
||||
n := Length( distinct_chunks );
|
||||
if not Length( chunks ) = n^2 then
|
||||
Error( "LOOPS: The data in the file cannot be arranged into a square table." );
|
||||
fi;
|
||||
|
||||
T := List( [1..n], i -> 0*[1..n] );
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
T[ i ][ j ] := Position( distinct_chunks, chunks[ (i-1)*n + j ] );
|
||||
od; od;
|
||||
|
||||
return T;
|
||||
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O QuasigroupFromFile( filename, replace] )
|
||||
##
|
||||
## Calls LOOPS_ReadCayleyTableFromFile( filename, replace ) in order to return
|
||||
## the quasigroup with multiplication table in file <filename>.
|
||||
|
||||
InstallMethod( QuasigroupFromFile, "for string and string",
|
||||
[ IsString, IsString ],
|
||||
function( filename, replace )
|
||||
return QuasigroupByCayleyTable( LOOPS_ReadCayleyTableFromFile( filename, replace ) );
|
||||
end );
|
||||
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopFromFile( filename , replace] )
|
||||
##
|
||||
## Calls LOOPS_ReadCayleyTableFromFile( filename, replace ) in order to return
|
||||
## the loop with multiplication table in file <filename>.
|
||||
|
||||
InstallMethod( LoopFromFile, "for string and string",
|
||||
[ IsString, IsString ],
|
||||
function( filename, replace )
|
||||
return LoopByCayleyTable( LOOPS_ReadCayleyTableFromFile( filename, replace ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## CREATING QUASIGROUPS AND LOOPS BY SECTIONS AND FOLDERS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O CayleyTableByPerms( perms )
|
||||
##
|
||||
## Given a set <perms> of n permutations of an n-element set X, returns
|
||||
## n by n Cayley table ct such that ct[i][j] = X[j]^perms[i].
|
||||
## The operation is safe only if at most one permutation of <perms> is
|
||||
## the identity permutation, and all other permutations of <perms>
|
||||
## move all points of X.
|
||||
|
||||
InstallMethod( CayleyTableByPerms,
|
||||
"for a list of permutations",
|
||||
[ IsPermCollection ],
|
||||
function( perms )
|
||||
local n, pts, max;
|
||||
n := Length( perms );
|
||||
if n=1 then
|
||||
return [ [ 1 ] ];
|
||||
fi;
|
||||
# one of perms[ 1 ], perms[ 2 ] must move all points
|
||||
pts := MovedPoints( perms[ 2 ] );
|
||||
if pts = [] then
|
||||
pts := MovedPoints( perms[ 1 ] );
|
||||
fi;
|
||||
max := Maximum( pts );
|
||||
# we permute the whole interval [1..max] and then keep only those coordinates corresponding to pts
|
||||
return List( perms, p -> Permuted( [1..max], p^(-1) ){ pts } );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O QuasigroupByLeftSection( sect )
|
||||
##
|
||||
## Returns the quasigroup whose left section is the list of permutations
|
||||
## <sect>.
|
||||
|
||||
InstallMethod( QuasigroupByLeftSection,
|
||||
"for a set of left translation maps",
|
||||
[ IsPermCollection ],
|
||||
function( sect )
|
||||
return QuasigroupByCayleyTable( CayleyTableByPerms( sect ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopByLeftSection( sect )
|
||||
##
|
||||
## Returns the loop whose left section is the list of permutations <sect>.
|
||||
## Since the order of translations in <sect> is determined by their
|
||||
## image of the neutral element 1, we disregard the order.
|
||||
|
||||
InstallMethod( LoopByLeftSection,
|
||||
"for a set of left translation maps",
|
||||
[ IsPermCollection ],
|
||||
function( sect )
|
||||
return LoopByCayleyTable( Set ( CayleyTableByPerms( sect ) ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O QuasigroupByRightSection( sect )
|
||||
##
|
||||
## Returns the quasigroup whose right section is the list of permutations
|
||||
## <sect>.
|
||||
|
||||
InstallMethod( QuasigroupByRightSection,
|
||||
"for a set of left translation maps",
|
||||
[ IsPermCollection ],
|
||||
function( sect )
|
||||
return QuasigroupByCayleyTable( TransposedMat ( CayleyTableByPerms( sect ) ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopByRightSection( sect )
|
||||
##
|
||||
## Returns the loop whose right section is the list of permutations <sect>.
|
||||
## Since the order of translations in <sect> is determined by their
|
||||
## image of the neutral element 1, we disregard the order.
|
||||
|
||||
InstallMethod( LoopByRightSection,
|
||||
"for a set of left translation maps",
|
||||
[ IsPermCollection ],
|
||||
function( sect )
|
||||
return LoopByCayleyTable( TransposedMat ( Set ( CayleyTableByPerms( sect ) ) ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LOOPS_CayleyTableByRightFolder( G, H, T )
|
||||
##
|
||||
## Auxiliary operation.
|
||||
##
|
||||
## A right folder is a triple (G,H,T) such that G is a group, H is
|
||||
## a subgroup of G, and T is a right transversal to H in G.
|
||||
##
|
||||
## Returns the multiplication table on {Hx: x in G} by Ht*Hs = H(ts).
|
||||
##
|
||||
## The multiplication table is a quasigroup if and only if
|
||||
## T is a right transversal to every conjugate H^g in G.
|
||||
|
||||
InstallGlobalFunction( LOOPS_CayleyTableByRightFolder,
|
||||
function( G, H, T )
|
||||
local act, nT, actT, i, p, ct;
|
||||
# act = action of G on right cosest G/H
|
||||
act := ActionHomomorphism( G, RightCosets( G, H ), OnRight );
|
||||
nT := Length( T );
|
||||
# actT = permutations on G/H induced by elements of T
|
||||
actT := [1..nT];
|
||||
for i in [1..nT] do
|
||||
actT[ i ] := T[ i ]^act;
|
||||
od;
|
||||
# the order of right cosets determined by T might not agree with the default order of right cosets ...
|
||||
p := PermList( List( [1..nT], i -> 1^actT[ i ] ) );
|
||||
ct := List( [1..nT], i -> ListPerm( p * actT[ i ] * p^(-1) ) );
|
||||
for i in [1..nT] do
|
||||
if ct[ i ] = [] then # this can happen since ListPerm( () ) = []
|
||||
ct[ i ] := [1..nT];
|
||||
fi;
|
||||
od;
|
||||
return TransposedMat( ct );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O QuasigroupByRightFolder( G, H, T )
|
||||
##
|
||||
## See CayleyTableByRightFolder. We do not check if the right folder
|
||||
## is a quasigroup right folder.
|
||||
|
||||
InstallMethod( QuasigroupByRightFolder,
|
||||
"for a group, a subgroup and right transversal",
|
||||
[ IsGroup, IsGroup, IsMultiplicativeElementCollection ],
|
||||
function( G, H, T )
|
||||
return QuasigroupByCayleyTable( LOOPS_CayleyTableByRightFolder( G, H, T ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O LoopByRightFolder( G, H, T )
|
||||
##
|
||||
## See CayleyTableByRigthFolder. We do not check if the right folder
|
||||
## is a loop right folder.
|
||||
|
||||
InstallOtherMethod( LoopByRightFolder,
|
||||
"for a group, a subgroup and right transversal",
|
||||
[ IsGroup, IsGroup, IsMultiplicativeElementCollection ],
|
||||
function( G, H, T )
|
||||
return LoopByCayleyTable( LOOPS_CayleyTableByRightFolder( G, H, T ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## CONVERSIONS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IntoQuasigroup( M )
|
||||
##
|
||||
## Given a magma, returns the corresponding quasigroup, if possible.
|
||||
|
||||
InstallMethod( IntoQuasigroup, "for magma",
|
||||
[ IsMagma ],
|
||||
function( M )
|
||||
local ct;
|
||||
if IsQuasigroup( M ) then # leave quasigroups and loops intact
|
||||
return M;
|
||||
fi;
|
||||
# magma, not necessarily a quasigroup
|
||||
ct := MultiplicationTable( Elements( M ) );
|
||||
if IsQuasigroupTable( ct ) then
|
||||
return QuasigroupByCayleyTable( ct );
|
||||
fi;
|
||||
return fail;
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O PrincipalLoopIsotope( Q, f, g )
|
||||
##
|
||||
## Let Q be a quasigroup and f, g elements of Q.
|
||||
## Define new operation on Q by x+y = R^{-1}(g)(x) * L^{-1}(f)(y).
|
||||
## Then (Q,+) is a loop with neutral element f*g.
|
||||
## We return isomorphic copy of (Q,+) via the isomorphism (1,f*g).
|
||||
|
||||
InstallMethod( PrincipalLoopIsotope,
|
||||
"for quasigroup and two quasigroup elements",
|
||||
[ IsQuasigroup, IsQuasigroupElement, IsQuasigroupElement ],
|
||||
function( Q, f, g )
|
||||
local n, L, R, i, j, ct, p;
|
||||
if not (f in Q and g in Q) then
|
||||
Error("LOOPS: <2> and <3> must be elements of quasigroup <1>.");
|
||||
fi;
|
||||
# constructing new multiplication
|
||||
n := Size( Q );
|
||||
ct := List( [1..n], i -> [1..n] );
|
||||
L := Inverse( LeftTranslation( Q, f ) ); # inverse of left translation by f
|
||||
R := Inverse( RightTranslation( Q, g ) ); # inverse or right translation by g
|
||||
for i in [1..n] do for j in [1..n] do
|
||||
ct[ i ][ j ] := CayleyTable( Q )[ i^R ][ j^L ];
|
||||
od; od;
|
||||
# the neutral element of ct is now f*g. We apply isomorphism (1, f*g).
|
||||
p := Position(Q, f*g);
|
||||
if p>1 then
|
||||
p := (1, p); # note that p is its own inverse
|
||||
ct := List([1..n], i-> List([1..n], j -> ( ct[ i^p ][ j^p ] )^p ) );
|
||||
fi;
|
||||
return LoopByCayleyTable( ct );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IntoLoop( M )
|
||||
##
|
||||
## Given a magma, returns the corresponding loop, if possible.
|
||||
|
||||
InstallMethod( IntoLoop, "for magma",
|
||||
[ IsMagma ],
|
||||
function( M )
|
||||
local e, p, ct;
|
||||
if IsLoop( M ) then # loops are left intact
|
||||
return M;
|
||||
fi;
|
||||
# magma, not necessarily a loop
|
||||
M := IntoQuasigroup( M );
|
||||
if M = fail then
|
||||
return fail;
|
||||
fi;
|
||||
# quasigroup, not necesarily a loop
|
||||
e := MultiplicativeNeutralElement( M );
|
||||
if e = fail then # no neutral element, use principal isotope
|
||||
return PrincipalLoopIsotope( M, Elements( M )[ 1 ], Elements( M )[ 1 ] );
|
||||
fi;
|
||||
# quasigroup with neutral element, i.e., a loop
|
||||
p := Position( M, e );
|
||||
if p>1 then
|
||||
p := (1,p); # note that p is its own inverse
|
||||
ct := List([1..Size(M)], i-> List([1..Size(M)], j ->
|
||||
( CayleyTable( M )[ i^p ][ j^p ] )^p
|
||||
) );
|
||||
else
|
||||
ct := CayleyTable( M );
|
||||
fi;
|
||||
return LoopByCayleyTable( ct );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O IntoGroup( M )
|
||||
##
|
||||
## Given a magma <M>, returns the corresponding group, if possible.
|
||||
|
||||
InstallOtherMethod( IntoGroup, "for magma",
|
||||
[ IsMagma ],
|
||||
function( M )
|
||||
if IsGroup( M ) then # groups are left intact
|
||||
return M;
|
||||
fi;
|
||||
# magma, not necessarily a group
|
||||
M := IntoLoop( M );
|
||||
if M=fail or (not IsAssociative( M ) ) then
|
||||
return fail;
|
||||
fi;
|
||||
# group
|
||||
return RightMultiplicationGroup( M );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
## PRODUCTS OF QUASIGROUPS AND LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#F DirectProduct( Q1, Q2, ..., Qn )
|
||||
##
|
||||
## Returns the direct product of quasigroups <Q1>, <Q2>, ... , <Qn>.
|
||||
## The quasigroups can be declared as quasigroups, loops or groups.
|
||||
|
||||
# The following is necessary due to implementation of DirectProduct for
|
||||
# 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.
|
||||
# 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.
|
||||
|
||||
InstallOtherMethod( 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();
|
||||
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;
|
||||
fi;
|
||||
od;
|
||||
|
||||
return DirectProductOp( list, list[ 1 ] );
|
||||
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;
|
||||
|
||||
# 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();
|
||||
fi;
|
||||
|
||||
# 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 ) );
|
||||
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
|
||||
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 cannonical
|
||||
for s in [1..n] do
|
||||
quasigroup_list[ s ] := QuasigroupByCayleyTable( CanonicalCayleyTable( CayleyTable( quasigroup_list[ s ] ) ) );
|
||||
od;
|
||||
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 ] );
|
||||
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 );
|
||||
od;
|
||||
if are_all_loops then
|
||||
return IntoLoop( quasigroup_list[1] );
|
||||
fi;
|
||||
return quasigroup_list[ 1 ];
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## OPPOSITE QUASIGROUPS AND LOOPS
|
||||
## --------------------------------------------------------------------------
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O OppositeQuasigroup( Q )
|
||||
##
|
||||
## Returns the quasigroup opposite to the quasigroup <Q>.
|
||||
|
||||
InstallMethod( OppositeQuasigroup, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
return QuasigroupByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O OppositeLoop( Q )
|
||||
##
|
||||
## Returns the loop opposite to the loop <Q>.
|
||||
|
||||
InstallMethod( OppositeLoop, "for loop",
|
||||
[ IsLoop ],
|
||||
function( Q )
|
||||
return LoopByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#A Opposite( Q )
|
||||
##
|
||||
## Returns the quasigroup opposite to the quasigroup <Q>. When
|
||||
## <Q> is a loop, a loop is returned.
|
||||
|
||||
InstallMethod( Opposite, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
if IsLoop( Q ) then
|
||||
return LoopByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
|
||||
fi;
|
||||
return QuasigroupByCayleyTable( TransposedMat( CayleyTable( Q ) ) );
|
||||
end );
|
||||
|
||||
#############################################################################
|
||||
## DISPLAYING QUASIGROUPS AND LOOPS
|
||||
## -------------------------------------------------------------------------
|
||||
|
||||
InstallMethod( ViewObj, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
Print( "<quasigroup of order ", Size( Q ), ">" );
|
||||
end );
|
||||
|
||||
## dangerous for large quasigroups
|
||||
InstallMethod( PrintObj, "for quasigroup",
|
||||
[ IsQuasigroup ],
|
||||
function( Q )
|
||||
if HasCayleyTable( Q ) then
|
||||
Print( "<quasigroup with multiplication table\n" );
|
||||
PrintArray( CayleyTable( Q ) );
|
||||
else
|
||||
Print( "<quasigroup with elements\n" );
|
||||
Print( Elements( Q ) );
|
||||
fi;
|
||||
Print( ">\n" );
|
||||
end );
|
||||
|
||||
InstallMethod( ViewObj, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
if HasIsAssociative( L ) and IsAssociative( L ) then
|
||||
Print( "<associative loop of order ", Size( L ), ">");
|
||||
elif HasIsExtraLoop( L ) and IsExtraLoop( L ) then
|
||||
Print( "<extra loop of order ", Size( L ), ">");
|
||||
elif HasIsMoufangLoop( L ) and IsMoufangLoop( L ) then
|
||||
Print( "<Moufang loop of order ", Size( L ), ">");
|
||||
elif HasIsCLoop( L ) and IsCLoop( L ) then
|
||||
Print( "<C loop of order ", Size( L ), ">");
|
||||
elif HasIsLeftBolLoop( L ) and IsLeftBolLoop( L ) then
|
||||
Print( "<left Bol loop of order ", Size( L ), ">");
|
||||
elif HasIsRightBolLoop( L ) and IsRightBolLoop( L ) then
|
||||
Print( "<right Bol loop of order ", Size( L ), ">");
|
||||
elif HasIsLCLoop( L ) and IsLCLoop( L ) then
|
||||
Print( "<LC loop of order ", Size( L ), ">");
|
||||
elif HasIsRCLoop( L ) and IsRCLoop( L ) then
|
||||
Print( "<RC loop of order ", Size( L ), ">");
|
||||
elif HasIsLeftAlternative( L ) and IsLeftAlternative( L ) then
|
||||
if HasIsRightAlternative( L ) and IsRightAlternative( L ) then
|
||||
Print( "<alternative loop of order ", Size( L ), ">");
|
||||
else
|
||||
Print( "<left alternative loop of order ", Size( L ), ">");
|
||||
fi;
|
||||
elif HasIsRightAlternative( L ) and IsRightAlternative( L ) then
|
||||
Print( "<right alternative loop of order ", Size( L ), ">");
|
||||
elif HasIsFlexible( L ) and IsFlexible( L ) then
|
||||
Print( "<flexible loop of order ", Size( L ), ">");
|
||||
else
|
||||
# MORE ??
|
||||
Print( "<loop of order ", Size( L ), ">" );
|
||||
fi;
|
||||
end );
|
||||
|
||||
## dangerous for large loops
|
||||
InstallMethod( PrintObj, "for loop",
|
||||
[ IsLoop ],
|
||||
function( L )
|
||||
if HasCayleyTable( L ) then
|
||||
Print( "<loop with multiplication table\n" );
|
||||
PrintArray( CayleyTable( L ) );
|
||||
else
|
||||
Print( "<loop with elements\n" );
|
||||
Print( Elements( L ) );
|
||||
fi;
|
||||
Print( ">\n" );
|
||||
end );
|
17
gap/random.gd
Normal file
17
gap/random.gd
Normal file
|
@ -0,0 +1,17 @@
|
|||
############################################################################
|
||||
##
|
||||
#W random.gd Random loops [loops]
|
||||
##
|
||||
#H @(#)$Id: random.gd, v 2.1.0 2008/12/08 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
DeclareOperation( "RandomQuasigroup", [IsInt, IsInt ] );
|
||||
# There is also a version of RandomLoop with [ IsInt ].
|
||||
DeclareOperation( "RandomLoop", [ IsInt, IsInt ] );
|
||||
# There is also a version of RandomLoop with [ IsInt ].
|
||||
DeclareOperation( "RandomNilpotentLoop", [ IsList ] );
|
||||
|
||||
|
208
gap/random.gi
Normal file
208
gap/random.gi
Normal file
|
@ -0,0 +1,208 @@
|
|||
#############################################################################
|
||||
##
|
||||
#W random.gi Random loops [loops]
|
||||
##
|
||||
#H @(#)$Id: random.gi, v 2.1.0 2008/12/08 gap Exp $
|
||||
##
|
||||
#Y Copyright (C) 2004, G. P. Nagy (University of Szeged, Hungary),
|
||||
#Y P. Vojtechovsky (University of Denver, USA)
|
||||
##
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O RandomQuasigroup( n, iter )
|
||||
##
|
||||
## Returns a random quasigroup of order <n> using <iter> random steps to move into an
|
||||
## initial position in the Jacobson & Matthews method.
|
||||
## (MATH) This is an implementation of the Jacobson & Matthews random walk method with
|
||||
## some ad hoc mixing parameters. We always start with the cyclic group of order n.
|
||||
## It is proved in Jacobson & Matthews that a random walk in the graph visits all
|
||||
## latin squares uniformly. (But the problem is how to move to an initial position.)
|
||||
|
||||
InstallMethod( RandomQuasigroup, "for two integers",
|
||||
[ IsInt, IsInt ],
|
||||
function( n, iter )
|
||||
local f, x, y, z, is_proper, xx, yy, zz, random_walk_step, i, ct;
|
||||
|
||||
if not ( n > 0 and iter > 0 ) then
|
||||
Error("LOOPS: the arguments must be positive integers.");
|
||||
fi;
|
||||
|
||||
# n=1 is a special case
|
||||
if n=1 then
|
||||
return LoopByCayleyTable( [[1]] );
|
||||
fi;
|
||||
|
||||
# initializing funciton for proper and improper Latin squares
|
||||
# the meaning of f(x,y,z)=1 is that there is symbol z in row x and column y
|
||||
f := List([1..n], i -> List([1..n], j -> 0*[1..n]));
|
||||
# cyclic group of order n on symbols [1..n]
|
||||
for x in [1..n] do for y in [1..n] do
|
||||
z := x+y-1;
|
||||
if z > n then
|
||||
z := z - n;
|
||||
fi;
|
||||
f[x][y][z] := 1;
|
||||
od; od;
|
||||
is_proper := true; # proper latin square to start with
|
||||
|
||||
# one random walk step
|
||||
random_walk_step := function()
|
||||
local x, y, z, triples, x2, y2, z2, triple;
|
||||
if is_proper then
|
||||
repeat
|
||||
x := Random([1..n]);
|
||||
y := Random([1..n]);
|
||||
z := Random([1..n]);
|
||||
until f[x][y][z]=0;
|
||||
fi;
|
||||
if not is_proper then # use unique point with f(x,y,z)=-1
|
||||
x := xx;
|
||||
y := yy;
|
||||
z := zz;
|
||||
fi;
|
||||
# find all suitable triples
|
||||
x2 := Filtered( [1..n], a -> f[a][y][z] = 1 );
|
||||
y2 := Filtered( [1..n], a -> f[x][a][z] = 1 );
|
||||
z2 := Filtered( [1..n], a -> f[x][y][a] = 1 );
|
||||
# pick a random suitable triple
|
||||
x2 := Random( x2 );
|
||||
y2 := Random( y2 );
|
||||
z2 := Random( z2 );
|
||||
# shuffle values
|
||||
f[x][y][z] := f[x][y][z] + 1;
|
||||
f[x][y2][z2] := f[x][y2][z2] + 1;
|
||||
f[x2][y][z2] := f[x2][y][z2] + 1;
|
||||
f[x2][y2][z] := f[x2][y2][z] + 1;
|
||||
f[x2][y][z] := f[x2][y][z] - 1;
|
||||
f[x][y2][z] := f[x][y2][z] - 1;
|
||||
f[x][y][z2] := f[x][y][z2] - 1;
|
||||
f[x2][y2][z2] := f[x2][y2][z2] - 1;
|
||||
# determine properness
|
||||
if f[x2][y2][z2] = 0 then
|
||||
is_proper := true;
|
||||
else
|
||||
is_proper := false;
|
||||
xx := x2;
|
||||
yy := y2;
|
||||
zz := z2;
|
||||
fi;
|
||||
end;
|
||||
|
||||
# moving into an initial point in the graph
|
||||
for i in [1..iter] do
|
||||
random_walk_step();
|
||||
od;
|
||||
# finding a proper square nearby
|
||||
while not is_proper do
|
||||
random_walk_step();
|
||||
od;
|
||||
# constructing the multiplication table from the function
|
||||
ct := List([1..n], i->[1..n]);
|
||||
for x in [1..n] do for y in [1..n] do
|
||||
ct[x][y] := Filtered([1..n], z -> f[x][y][z] = 1)[ 1 ];
|
||||
od; od;
|
||||
return QuasigroupByCayleyTable( ct );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O RandomQuasigroup( n )
|
||||
##
|
||||
## Returns random quasigroup of order <n> using n^3 steps to move into an
|
||||
## initial position in the Jacobson & Matthews algorithm.
|
||||
|
||||
InstallOtherMethod( RandomQuasigroup, "for a positive integer",
|
||||
[ IsInt ],
|
||||
function( n )
|
||||
return RandomQuasigroup( n, n^3 );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O RandomLoop( n, iter )
|
||||
##
|
||||
## Returns a normalized random quasigroup of order <n>.
|
||||
|
||||
InstallOtherMethod( RandomLoop, "for two positive integers",
|
||||
[ IsInt, IsInt ],
|
||||
function( n, iter )
|
||||
local Q;
|
||||
Q := RandomQuasigroup( n, iter );
|
||||
return LoopByCayleyTable( NormalizedQuasigroupTable( CayleyTable( Q ) ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O RandomLoop( n )
|
||||
##
|
||||
## Returns random loop of order <n> using n^3 steps to move into an
|
||||
## initial position in the Jacobson & Matthews algorithm.
|
||||
|
||||
InstallOtherMethod( RandomLoop, "for a positive integer",
|
||||
[ IsInt ],
|
||||
function( n )
|
||||
local Q;
|
||||
Q := RandomQuasigroup( n );
|
||||
return LoopByCayleyTable( NormalizedQuasigroupTable( CayleyTable( Q ) ) );
|
||||
end);
|
||||
|
||||
#############################################################################
|
||||
##
|
||||
#O RandomNilpotentLoop( lst )
|
||||
##
|
||||
## lst must be a list of positive integers and/or finite abelian groups.
|
||||
## If lst = [n] and n is an integer, returns a random abelian group of order n.
|
||||
## If lst = [A] and A is an abelian group, returns AsLoop( A ).
|
||||
## If lst = [a1,..,am] and a1 is an integer, returns a central extension
|
||||
## of an abelian group of order a1 by RandomNilpotentLoop( [a2,...,am] ).
|
||||
## If lst = [a1,..,am] and a1 is a group, returns a central extension
|
||||
## of a1 by RandomNilpotentLoop( [a2,...,am] ).
|
||||
## To determine the nilpotency class CL of the resulting loop, assume that
|
||||
## lst has length at least 2, contains only integers bigger than 1 (the "1" entries are trivial),
|
||||
## and let m be the last entry of lst. If m>2 then CL=Length(lst), else CL = Length(lst)-1.
|
||||
|
||||
InstallMethod( RandomNilpotentLoop, "for a list of abelian groups and positive integers",
|
||||
[ IsList ],
|
||||
function( lst )
|
||||
local n, K, F, f, theta, i, j, phi;
|
||||
if IsEmpty( lst ) then
|
||||
Error("LOOPS: the argument must be a list of finite abelian groups and/or positive integers.");
|
||||
fi;
|
||||
n := lst[1];
|
||||
if not ( IsPosInt( n ) or (IsGroup( n ) and IsAbelian( n ) and IsFinite( n ) ) ) then
|
||||
Error("LOOPS: the argument must be a list of finite abelian groups and/or positive integers.");
|
||||
fi;
|
||||
# central subloop
|
||||
if IsInt( n ) then # first argument is a positive integer
|
||||
K := IntoLoop( Random( AllGroups( n, IsAbelian ) ) );
|
||||
else # first argument is an abelian group
|
||||
K := IntoLoop( n );
|
||||
fi;
|
||||
# factor loop
|
||||
if Length( lst ) = 1 then # trivial factor
|
||||
F := LoopByCayleyTable( [ [ 1 ] ] );
|
||||
else
|
||||
F := RandomNilpotentLoop( lst{[2..Length(lst)]} );
|
||||
fi;
|
||||
# cocycle (random)
|
||||
f := Size( F );
|
||||
theta := List([1..f], i->[1..f]);
|
||||
for i in [2..f] do
|
||||
theta[1][i]:=1;
|
||||
od;
|
||||
for i in [2..f] do for j in [2..f] do
|
||||
theta[i][j] := Random( [1..Size(K)] );
|
||||
od; od;
|
||||
# To guarantee that the resulting loop has maximal nilpotency class,
|
||||
# it suffices to make sure that theta is not symmetric.
|
||||
if f>2 then
|
||||
i := Random([2..f]);
|
||||
j := Random( Difference( [2..f], [i] ) );
|
||||
theta[i][j] := Random( Difference( [1..Size(K)], [ theta[j][i] ] ) );
|
||||
fi;
|
||||
# trivial action
|
||||
phi := List( [1..f], i->() );
|
||||
# the loop
|
||||
return LoopByExtension( K, F, phi, theta );
|
||||
end);
|
Loading…
Add table
Add a link
Reference in a new issue