1125 lines
36 KiB
Plaintext
1125 lines
36 KiB
Plaintext
|
#############################################################################
|
||
|
##
|
||
|
#W core_methods.gi Most common structural methods [loops]
|
||
|
##
|
||
|
#H @(#)$Id: core_methods.gi, 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
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A GeneratorsOfQuasigroup( Q )
|
||
|
##
|
||
|
## Returns a subset of <Q> gnerating <Q>. In most circumstances,
|
||
|
## Elements( Q ) is returned.
|
||
|
|
||
|
InstallMethod( GeneratorsOfQuasigroup, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
GeneratorsOfDomain
|
||
|
);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A GeneratorsSmallest( Q )
|
||
|
##
|
||
|
## Returns a smallest generating set of a quasigroup <Q> with respect
|
||
|
## to lexicographic ordering of elements.
|
||
|
|
||
|
InstallOtherMethod( GeneratorsSmallest, "for a quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local gens, diff;
|
||
|
gens := [ ];
|
||
|
diff := Elements( Q );
|
||
|
while diff <> [ ] do
|
||
|
Add( gens, diff[ Length(diff) ] );
|
||
|
diff := Difference( diff, Subquasigroup( Q, gens ) );
|
||
|
od;
|
||
|
return Set( gens );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A SmallGeneratingSet( Q )
|
||
|
##
|
||
|
## Returns a small generating set of a quasigroup <Q>. There is no
|
||
|
## guarantee that the set is of minimal cardinality.
|
||
|
|
||
|
InstallOtherMethod( SmallGeneratingSet, "for a quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local gens, sub, candidates, max, S, best_gen, best_S;
|
||
|
|
||
|
gens := []; # generating set to be returned
|
||
|
sub := []; # substructure generated so far
|
||
|
while sub <> Q do
|
||
|
# find an element not in sub that most enlarges sub
|
||
|
candidates := Difference( Elements(Q), 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 := Difference( candidates, Elements( S ) );
|
||
|
od;
|
||
|
AddSet( gens, best_gen );
|
||
|
sub := best_S;
|
||
|
od;
|
||
|
return gens;
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
## COMPARING QUASIGROUPS WITH COMMON PARENT
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A \<( L, K )
|
||
|
##
|
||
|
## Ordering between quasigroups with common parent quasigroup. Analogous to the
|
||
|
## basic method for groups. We use the lexicographical ordering between the
|
||
|
## small (greedy) generating sets.
|
||
|
|
||
|
InstallMethod(\<,
|
||
|
"for quasigroups by lexicographically ordered small generating sets",
|
||
|
IsIdenticalObj,
|
||
|
[IsQuasigroup, IsQuasigroup],
|
||
|
function(a,b)
|
||
|
local ga,gb;
|
||
|
ga:=GeneratorsSmallest(a);
|
||
|
gb:=GeneratorsSmallest(b);
|
||
|
if Length(ga)<Length(gb) then
|
||
|
a:=Elements(a)[Size(a)];
|
||
|
ga:=ShallowCopy(ga);
|
||
|
while Length(ga)<Length(gb) do Add(ga,a); od;
|
||
|
else
|
||
|
b:=Elements(b)[Size(b)];
|
||
|
gb:=ShallowCopy(gb);
|
||
|
while Length(gb)<Length(ga) do Add(gb,b); od;
|
||
|
fi;
|
||
|
return ga<gb;
|
||
|
end);
|
||
|
|
||
|
|
||
|
#############################################################################
|
||
|
## SECTIONS
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A LeftSection( Q )
|
||
|
##
|
||
|
## Returns the left section of a quasigroup <Q>. Left section consists of
|
||
|
## all left translations L_x: y --> x*y.
|
||
|
|
||
|
InstallMethod( LeftSection, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local P;
|
||
|
P := PosInParent( Q );
|
||
|
return List( CayleyTable( Q ), row -> MappingPermListList( P, row ) );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A RightSection( Q )
|
||
|
##
|
||
|
## Returns the right section of a quasigroup <Q>. Right section consists of
|
||
|
## all right translations R_x: y --> y*x.
|
||
|
|
||
|
InstallMethod( RightSection, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local P;
|
||
|
P := PosInParent( Q );
|
||
|
return List( TransposedMat( CayleyTable( Q ) ), row -> MappingPermListList( P, row ) );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O LeftTranslation( Q, x )
|
||
|
##
|
||
|
## Let <x> be an element of a quasigroup <Q>. Returns the left translation
|
||
|
## by <x> in <Q>.
|
||
|
|
||
|
InstallMethod( LeftTranslation, "for quasigroup and quasigroup element",
|
||
|
[ IsQuasigroup, IsQuasigroupElement ],
|
||
|
function( Q, x )
|
||
|
if not x in Q then
|
||
|
Error( "LOOPS: <2> must be an element of the quasigroup <1>.");
|
||
|
fi;
|
||
|
return LeftSection( Q )[ Position( Q, x ) ];
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O RightTranslation( Q, x )
|
||
|
##
|
||
|
## Let <x> be an element of a quasigroup <Q>. Returns the right translation
|
||
|
## by <x> in <Q>.
|
||
|
|
||
|
InstallMethod( RightTranslation, "for quasigroup and quasigroup element",
|
||
|
[ IsQuasigroup, IsQuasigroupElement ],
|
||
|
function( Q, x )
|
||
|
if not x in Q then
|
||
|
Error( "LOOPS: <2> must be an element of the quasigroup <1>.");
|
||
|
fi;
|
||
|
return RightSection( Q )[ Position( Q, x ) ];
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
## MULTIPLICATION GROUPS
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A LeftMultiplicationGroup( Q )
|
||
|
##
|
||
|
## Returns the subgroup of S_<Q> generated by all left translations of the
|
||
|
## quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( LeftMultiplicationGroup, "for quasigroup",
|
||
|
[IsQuasigroup ],
|
||
|
function( Q )
|
||
|
return Group( LeftSection( Q ) );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A RightMultiplicationGroup( Q )
|
||
|
##
|
||
|
## Returns the subgroup of S_<Q> generated by all right translations of the
|
||
|
## quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( RightMultiplicationGroup, "for quasigroup",
|
||
|
[IsQuasigroup ],
|
||
|
function( Q )
|
||
|
return Group( RightSection( Q ) );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A MultiplicationGroup( Q )
|
||
|
##
|
||
|
## Returns the smallest subgroup of S_<Q> containing the both the left
|
||
|
## and right multiplication groups of the quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( MultiplicationGroup, "for quasigroup",
|
||
|
[IsQuasigroup ],
|
||
|
function( Q )
|
||
|
return Group( Union( LeftSection( Q ), RightSection( Q ) ) );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O RelativeLeftMultiplicationGroup( L, S )
|
||
|
##
|
||
|
## Returns the relative left multiplication group of a quasigroup <L>
|
||
|
## with respect to its subquasigroup <S>.
|
||
|
|
||
|
InstallMethod( RelativeLeftMultiplicationGroup, "for two quasigroups",
|
||
|
[ IsQuasigroup, IsQuasigroup ],
|
||
|
function( L, S )
|
||
|
local perms;
|
||
|
if not IsSubquasigroup( L, S ) then
|
||
|
Error("LOOPS: <2> must be a subquasigroup of <1>.");
|
||
|
fi;
|
||
|
perms := List( S, x -> LeftTranslation( L, x ) );
|
||
|
return Group( perms );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O RelativeRightMultiplicationGroup( L, S )
|
||
|
##
|
||
|
## Returns the relative right multiplication group of a quasigroup <L>
|
||
|
## with respect to its subquasigroup <S>.
|
||
|
|
||
|
InstallMethod( RelativeRightMultiplicationGroup, "for two quasigroups",
|
||
|
[ IsQuasigroup, IsQuasigroup ],
|
||
|
function( L, S )
|
||
|
local perms;
|
||
|
if not IsSubquasigroup( L, S ) then
|
||
|
Error("LOOPS: <2> must be a subquasigroup of <1>.");
|
||
|
fi;
|
||
|
perms := List( S, x -> RightTranslation( L, x ) );
|
||
|
return Group( perms );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O RelativeMultiplicationGroup( L, S )
|
||
|
##
|
||
|
## Returns the relative multiplication group of a quasigroup <L>
|
||
|
## with respect to its subquasigroup <S>.
|
||
|
|
||
|
InstallMethod( RelativeMultiplicationGroup, "for two quasigroups",
|
||
|
[ IsQuasigroup, IsQuasigroup ],
|
||
|
function( L, S )
|
||
|
return Group( Union(
|
||
|
RelativeLeftMultiplicationGroup( L, S ),
|
||
|
RelativeRightMultiplicationGroup( L, S)
|
||
|
) );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
## INNER MAPPING GROUPS
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O LeftInnerMapping( L, x, y )
|
||
|
##
|
||
|
## Returns the left inner mapping of loop <L> determined by elements
|
||
|
## <x>, <y>, i.e., the mapping L(x,y) = L_{yx}^{-1} L_y L_x, where we compose
|
||
|
## mappings from right to left.
|
||
|
|
||
|
InstallMethod( LeftInnerMapping, "for loop and a loop elements",
|
||
|
[ IsLoop, IsLoopElement, IsLoopElement ],
|
||
|
function( L, x, y )
|
||
|
if not (x in L and y in L) then
|
||
|
Error( "LOOPS: <2>, <3> must be elements of the loop <1>.");
|
||
|
fi;
|
||
|
return LeftTranslation( L, x ) * LeftTranslation( L, y ) * LeftTranslation( L, y*x )^(-1);
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O MiddleInnerMapping( L, x )
|
||
|
##
|
||
|
## Let <x> be an element of a loop <L>. Returns the middle inner mapping
|
||
|
## determined by <x>, i.e., the mapping L(x)^{-1} R(x), where we compose
|
||
|
## mappings from right to left.
|
||
|
|
||
|
InstallMethod( MiddleInnerMapping, "for loop and a loop elements",
|
||
|
[ IsLoop, IsLoopElement ],
|
||
|
function( L, x )
|
||
|
if not x in L then
|
||
|
Error( "LOOPS: <2> must be an element of the loop <1>.");
|
||
|
fi;
|
||
|
return RightTranslation( L, x ) * LeftTranslation( L, x )^(-1);
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O RightInnerMapping( L, x, y )
|
||
|
##
|
||
|
## Returns the right inner mapping of loop <L> determined by elements
|
||
|
## <x>, <y>, i.e., the mapping R(x,y) = R_{xy}^{-1} R_y R_x, where we compose
|
||
|
## mappings from right to left.
|
||
|
|
||
|
InstallMethod( RightInnerMapping, "for loop and a loop elements",
|
||
|
[ IsLoop, IsLoopElement, IsLoopElement ],
|
||
|
function( L, x, y )
|
||
|
if not (x in L and y in L) then
|
||
|
Error( "LOOPS: <2>, <3> must be elements of the loop <1>.");
|
||
|
fi;
|
||
|
return RightTranslation( L, x ) * RightTranslation( L, y ) * RightTranslation( L, x*y )^(-1);
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A InnerMappingGroup( L )
|
||
|
##
|
||
|
## Returns the inner mapping group of a loop <L>, i.e., the subgroup
|
||
|
## of MultplicationGroup( L ) consisting of maps fixing One( L ).
|
||
|
|
||
|
InstallMethod( InnerMappingGroup, "for loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return Stabilizer( MultiplicationGroup( L ), 1 );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A LeftInnerMappingGroup( L )
|
||
|
##
|
||
|
## Returns the left inner mapping group of a loop <L>, i.e., the subgroup
|
||
|
## of LeftMultiplicationGroup( L ) consisting of maps fixing One( L ).
|
||
|
|
||
|
InstallMethod( LeftInnerMappingGroup, "for loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return Stabilizer( LeftMultiplicationGroup( L ), 1 );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A MiddleInnerMappingGroup( L )
|
||
|
##
|
||
|
## returns the middle inner mapping group of a loop <L>
|
||
|
|
||
|
InstallMethod( MiddleInnerMappingGroup, "for loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return Group( List( L, x -> MiddleInnerMapping( L, x ) ) );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A RightInnerMappingGroup( L )
|
||
|
##
|
||
|
## Returns the right inner mapping group of a loop <L>, i.e., the subgroup
|
||
|
## of RightMultiplicationGroup( L ) consisting of maps fixing One( L ).
|
||
|
|
||
|
InstallMethod( RightInnerMappingGroup, "for loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return Stabilizer( RightMultiplicationGroup( L ), 1 );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
## SUBQUASIGROUPS AND SUBLOOPS
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
InstallOtherMethod( Position, "for quasigroup and quasigroup element",
|
||
|
[ IsQuasigroup, IsQuasigroupElement, IsInt],
|
||
|
function( Q, x, dummy )
|
||
|
return Position( Elements( Q ), x );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O PosInParent( coll )
|
||
|
##
|
||
|
## Let <coll> be a collection of elements of a quasigroup Q, and let P
|
||
|
## be the parent of Q. Then PosInParent( coll )[ i ] is equal to
|
||
|
## Pos ( Elements( P ), coll[ i ] ).
|
||
|
## Note that we do not demand that all elements of <coll>
|
||
|
## belong to the same quasigroup.
|
||
|
|
||
|
InstallMethod( PosInParent, "for collection of quasigroup elements",
|
||
|
[ IsCollection ],
|
||
|
function( coll )
|
||
|
if not ForAll( coll, x -> IsQuasigroupElement( x ) ) then
|
||
|
Error( "LOOPS: <1> must be a list of quasigroup elements." );
|
||
|
fi;
|
||
|
return List( coll, x -> x![ 1 ] );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O SubquasigroupNC( Q, pos_of_gens )
|
||
|
##
|
||
|
## This auxiliary function assumes that:
|
||
|
## a) Q is a quasigroup with Q = Parent( Q )
|
||
|
## b) pos_of_gens is a nonempty subset of [ 1..Size( Q ) ] (not a sublist)
|
||
|
## c) pos_of_gens determines a subquasigroup of Q
|
||
|
## It then returns the corresponding subquasigroup of Q.
|
||
|
|
||
|
InstallMethod( SubquasigroupNC, "for quasigroup and set of integers",
|
||
|
[ IsQuasigroup, IsSet ],
|
||
|
function( Q, pos_of_gens )
|
||
|
local subqg, Qtype, elms;
|
||
|
if IsEmpty( pos_of_gens ) then
|
||
|
Error( "LOOPS: <2> must be a nonempty subset.");
|
||
|
fi;
|
||
|
if not( Q = Parent( Q ) ) then
|
||
|
Error( "LOOPS: <1> must be its own parent." );
|
||
|
fi;
|
||
|
elms := Immutable( Elements( Q ){ pos_of_gens } );
|
||
|
if IsLoop( Q ) then
|
||
|
Qtype := NewType( FamilyObj( elms ), IsLoop and IsAttributeStoringRep );
|
||
|
else
|
||
|
Qtype := NewType( FamilyObj( elms ), IsQuasigroup and IsAttributeStoringRep );
|
||
|
fi;
|
||
|
subqg := Objectify( Qtype, rec( ) );
|
||
|
SetSize( subqg, Length( pos_of_gens ) );
|
||
|
SetAsSSortedList( subqg, elms );
|
||
|
SetParent( subqg, Q );
|
||
|
if IsLoop( Q ) then
|
||
|
SetOne( subqg, One( Q ) );
|
||
|
fi;
|
||
|
return subqg;
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O Subquasigroup( Q, gens )
|
||
|
##
|
||
|
## When <gens> is a nonempty list of elements of a quasigroup <Q>, or the
|
||
|
## indices of them, it returns the subquasigroup of <Q> generated by <gens>.
|
||
|
|
||
|
InstallMethod( Subquasigroup, "for quasigroup and list of elements",
|
||
|
[ IsQuasigroup, IsList ],
|
||
|
function( Q, gens )
|
||
|
local pos_gens, transl, pos_of_elms, relmultgr, subqg;
|
||
|
# NG: we allow list of indices as well
|
||
|
# PV: we allow gens to be empty, too. When Q is a loop, the trivial subloop is returned.
|
||
|
# When Q is a quasigroup, we return []. This is useful when Nuc(Q) is empty in a quasigroup,
|
||
|
# for instance.
|
||
|
if IsEmpty( gens ) then
|
||
|
if IsLoop( Q ) then
|
||
|
return SubquasigroupNC( Q, [1] ); # trivial subloop
|
||
|
fi;
|
||
|
return [];
|
||
|
fi;
|
||
|
if not( ForAll( gens, g -> g in Q ) or ForAll( gens, g-> g in PosInParent( Q ) ) ) then
|
||
|
Error("LOOPS: <2> must be a list of elements of quasigroup <1> or their indices.");
|
||
|
fi;
|
||
|
if not IsInt( gens[1] ) then # convert elements to indices
|
||
|
pos_gens := PosInParent( gens );
|
||
|
else
|
||
|
pos_gens := ShallowCopy( gens );
|
||
|
gens := Elements( Parent( Q ) ){ gens };
|
||
|
fi;
|
||
|
# calculating the subquasigroup
|
||
|
pos_of_elms := [];
|
||
|
while pos_gens<>pos_of_elms do
|
||
|
pos_of_elms := pos_gens;
|
||
|
transl := Union( LeftSection( Parent( Q ) ){ pos_gens }, RightSection( Parent( Q ) ){ pos_gens } );
|
||
|
relmultgr := Subgroup( MultiplicationGroup( Parent( Q ) ), transl );
|
||
|
pos_gens := Set( Orbits( relmultgr, pos_gens )[ 1 ] );
|
||
|
od;
|
||
|
subqg := SubquasigroupNC( Parent( Q ), Set( pos_of_elms ) );
|
||
|
SetGeneratorsOfMagma( subqg, gens );
|
||
|
return subqg;
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O Subloop( L, gens )
|
||
|
##
|
||
|
## When <gens> is a list of elements of a loop <L>, it returns the
|
||
|
## subloop of <L> generated by <gens>. When <gens> is empty, it returns
|
||
|
## the trivial subloop of <L>.
|
||
|
|
||
|
InstallMethod( Subloop, "for loop and list of elements",
|
||
|
[ IsLoop, IsList ],
|
||
|
function( L, gens )
|
||
|
return Subquasigroup( L, gens );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O IsSubquasigroup( Q, S )
|
||
|
##
|
||
|
## Returns true if <S> is a subquasigorup of a quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( IsSubquasigroup, "for two quasigroups",
|
||
|
[ IsQuasigroup, IsQuasigroup ],
|
||
|
function( Q, S )
|
||
|
return Parent( Q ) = Parent( S ) and
|
||
|
IsSubset( Elements( Q ), Elements( S ) );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O IsSubloop( L, S )
|
||
|
##
|
||
|
## Returns true if <S> is a subloop of a loop <L>.
|
||
|
|
||
|
InstallMethod( IsSubloop, "for two loops",
|
||
|
[ IsLoop, IsLoop ],
|
||
|
function( L, S )
|
||
|
return IsSubquasigroup( L, S );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#0 AllSubquasigroups( Q )
|
||
|
##
|
||
|
## Returns a set of all subquasigroups of a quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( AllSubquasigroups, "for a quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
# MATH:
|
||
|
# If S is a subquasigroup of L and x is in L\S, then the cosets S and Sx are disjoint.
|
||
|
# If S is a proper subquasigroup of L then |L| is at least 2|S|.
|
||
|
# If S is a proper subquasigroup of L and |S| > |L|/4 then S is maximal in L.
|
||
|
# If S is a subloop of a power-associative loop L and x is in L\S then <S,x> = <S,x^m> whenever m is relatively prime to |x|.
|
||
|
# If S is a subloop of a LIP loop L and x is in L\S then <S,zx> = <S,x> for every z in S.
|
||
|
# If S is a subloop of a LIP power associative loop L and x is in L\S then <S,z(x^m)> = <S,x> for every z in S and every m relatively prime to |x|.
|
||
|
|
||
|
local All, Last, New, A, Out, B, x, coprime_powers, n, m;
|
||
|
|
||
|
# initialization
|
||
|
All := []; #all subquasigroups
|
||
|
if IsLoop( Q ) then
|
||
|
New := [ Subloop( Q, [ One( Q ) ] ) ]; # the trivial subloop
|
||
|
else
|
||
|
New := Set( Elements( Q ), x -> Subquasigroup( Q, [ x ] ) ); # all mono-generated subquasigroups
|
||
|
fi;
|
||
|
|
||
|
# rounds
|
||
|
repeat
|
||
|
Append( All, New );
|
||
|
Last := Filtered( New, A -> Size( A ) <= Size( Q )/4 ); # subquasigroups found in the previous round that are possibly not maximal in Q
|
||
|
New := []; # subquasigroups generated in this round
|
||
|
for A in Last do
|
||
|
Out := Difference( Elements( Q ), Elements( A ) );
|
||
|
while not IsEmpty(Out) do
|
||
|
x := Out[ 1 ];
|
||
|
Out := Out{[2..Length(Out)]};
|
||
|
B := Subquasigroup( Q, Union( Elements( A ), [ x ] ) );
|
||
|
if not B in New and not B in All then
|
||
|
Add( New, B ); # new subquasigroup found
|
||
|
fi;
|
||
|
# attempting to reduce the number of elements to be checked. This is critical for speed.
|
||
|
if Size( B ) < 4*Size( A ) then # A is maximal in B, removing everything in B
|
||
|
Out := Difference( Out, Elements( B ) );
|
||
|
elif IsLoop( Q ) then # additional removal methods for loops
|
||
|
coprime_powers := [ 1 ];
|
||
|
if IsPowerAssociative( Q ) then
|
||
|
n := Order( x );
|
||
|
coprime_powers := Filtered( [1..n], m -> Gcd(m,n) = 1 );
|
||
|
fi;
|
||
|
Out := Difference( Out, List( coprime_powers, m -> x^m ) );
|
||
|
if HasLeftInverseProperty( Q ) then
|
||
|
for m in coprime_powers do
|
||
|
Out := Difference( Out, Elements(A)*(x^m) );
|
||
|
od;
|
||
|
fi;
|
||
|
if HasRightInverseProperty( Q ) then
|
||
|
for m in coprime_powers do
|
||
|
Out := Difference( Out, (x^m)*Elements(A) );
|
||
|
od;
|
||
|
fi;
|
||
|
fi; # end of removal
|
||
|
od; # end of cycle for x
|
||
|
od; # end of cycle for A
|
||
|
until IsEmpty( New );
|
||
|
|
||
|
# finishing
|
||
|
if not Q in All then Add( All, Q ); fi;
|
||
|
return All;
|
||
|
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#0 AllSubloops( L )
|
||
|
##
|
||
|
## Returns a set of all subloops of a loop <L>.
|
||
|
|
||
|
InstallMethod( AllSubloops, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return AllSubquasigroups( L );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O RightCosetsNC( L, S )
|
||
|
##
|
||
|
## Returns a list of duplicate-free right cosets S*u, for u in L.
|
||
|
|
||
|
# (PROG) RightCosets(L,S) is implemented as a global function in GAP. It
|
||
|
# checks if S is a subset of L and then calls operation RightCosetsNC.
|
||
|
# LeftCosets is not implemented in GAP.
|
||
|
InstallOtherMethod( RightCosetsNC, "for two loops",
|
||
|
[ IsLoop, IsLoop ],
|
||
|
function( L, S )
|
||
|
local R, cosets, p, last_coset;
|
||
|
if not IsSubloop( L, S ) then
|
||
|
Error( "LOOPS: <2> must be a subloop of <1>" );
|
||
|
fi;
|
||
|
R := RightSection( L );
|
||
|
cosets := [];
|
||
|
while not IsEmpty( R ) do
|
||
|
p := R[1];
|
||
|
last_coset := List( S, x -> x^p );
|
||
|
R := Filtered( R, r -> not One(L)^r in last_coset );
|
||
|
Add(cosets, last_coset);
|
||
|
od;
|
||
|
return cosets;
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O RightTransversal( L, S )
|
||
|
##
|
||
|
## Returns a right transversal for the subloop S of loop L.
|
||
|
|
||
|
InstallMethod( RightTransversal, "for two loops",
|
||
|
[ IsLoop, IsLoop ],
|
||
|
function( L, S )
|
||
|
return List( RightCosetsNC( L, S ), x -> x[1] );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
## NUCLEUS, COMMUTANT, CENTER
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A LeftNucleus( Q )
|
||
|
##
|
||
|
## The left nucleus of quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( LeftNucleus, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local S, LS, n, e;
|
||
|
LS := LeftSection( Q );
|
||
|
n := Size( Q );
|
||
|
e := Elements( Q );
|
||
|
S := Filtered( [ 1..n ], i -> ForAll( [ 1..n ], j ->
|
||
|
LS[ j ]*LS[ i ] = LS[ Position( e, e[ i ]*e[ j ] ) ] ) );
|
||
|
S := Subquasigroup( Q, Elements( Q ){ S } );
|
||
|
if Size(S)>0 then
|
||
|
SetIsAssociative( S, true );
|
||
|
fi;
|
||
|
return S;
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A MiddleNucleus( Q )
|
||
|
##
|
||
|
## The middle nucleus of quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( MiddleNucleus, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local S, LS, n, e;
|
||
|
LS := LeftSection( Q );
|
||
|
n := Size( Q );
|
||
|
e := Elements( Q );
|
||
|
S := Filtered( [ 1..n ], i -> ForAll( [ 1..n ], j ->
|
||
|
LS[ i ]*LS[ j ] = LS[ Position( e, e[ j ]*e[ i ] ) ] ) );
|
||
|
S := Subquasigroup( Q, Elements( Q ){ S } );
|
||
|
if Size(S)>0 then
|
||
|
SetIsAssociative( S, true );
|
||
|
fi;
|
||
|
return S;
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A RightNucleus( Q )
|
||
|
##
|
||
|
## The right nucleus of quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( RightNucleus, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local S, RS, n, e;
|
||
|
RS := RightSection( Q );
|
||
|
n := Size( Q );
|
||
|
e := Elements( Q );
|
||
|
S := Filtered( [ 1..n ], i -> ForAll( [ 1..n ], j ->
|
||
|
RS[ j ]*RS[ i ] = RS[ Position( e, e[ j ]*e[ i ] ) ] ) );
|
||
|
S := Subquasigroup( Q, Elements( Q ){ S } );
|
||
|
if Size(S)>0 then
|
||
|
SetIsAssociative( S, true );
|
||
|
fi;
|
||
|
return S;
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A Nuc( Q )
|
||
|
##
|
||
|
## The nucleus of quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( Nuc, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
local N, S;
|
||
|
N := Intersection( Elements( LeftNucleus( Q ) ),
|
||
|
Elements( RightNucleus( Q ) ), Elements( MiddleNucleus( Q ) ) );
|
||
|
S := Subquasigroup( Q, N );
|
||
|
if Size(S)>0 then
|
||
|
SetIsAssociative( S, true );
|
||
|
fi;
|
||
|
return S;
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A Commutant( Q )
|
||
|
##
|
||
|
## The commutant of quasigroup <Q>.
|
||
|
|
||
|
InstallMethod( Commutant, "for quasigroup",
|
||
|
[ IsQuasigroup ],
|
||
|
function( Q )
|
||
|
return Elements( Q ){ Filtered( [1..Size( Q )], i
|
||
|
-> LeftSection( Q )[ i ] = RightSection( Q )[ i ] ) };
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A Center( Q )
|
||
|
##
|
||
|
## The center of a quasigroup <Q>.
|
||
|
|
||
|
# (PROG) setting rank high to make sure that Center for
|
||
|
# IsMagma and IsCommutative is not called first
|
||
|
InstallOtherMethod( Center, "for quasigroup",
|
||
|
[ IsQuasigroup ], 1*SUM_FLAGS + 20,
|
||
|
function( Q )
|
||
|
local S;
|
||
|
S := Intersection( Nuc( Q ), Commutant( Q ) );
|
||
|
S := Subquasigroup( Q, S );
|
||
|
if Size( S ) > 0 then
|
||
|
SetIsAssociative( S, true );
|
||
|
SetIsCommutative( S, true );
|
||
|
fi;
|
||
|
return S;
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A AssociatorSubloop( L )
|
||
|
##
|
||
|
## Returns the associator subloop of the loop <L>, i.e., the subloop of <L>
|
||
|
## generated by all associators.
|
||
|
|
||
|
InstallOtherMethod( AssociatorSubloop, "for Loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
local LeftInn, gens;
|
||
|
LeftInn := LeftInnerMappingGroup( L );
|
||
|
gens := List( L, x-> LeftDivision( x, Orbit( LeftInn, x ) ) );
|
||
|
return NormalClosure( L, Union( gens ) );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
## EXPONENT
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A Exponent( L )
|
||
|
##
|
||
|
## Returns the exponent of the loop <L>. Assumes that <L> is power
|
||
|
## associative.
|
||
|
|
||
|
InstallMethod( Exponent, "for loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
local ords;
|
||
|
ords := List( Elements( L ), x -> Order( x ) );
|
||
|
return Lcm( ords );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
## NORMALITY
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#F IsNormal( L, S )
|
||
|
##
|
||
|
## Returns true if <S> is a normal subloop of loop <L>.
|
||
|
|
||
|
InstallOtherMethod( IsNormal,
|
||
|
"for loops",
|
||
|
[ IsLoop, IsLoop ],
|
||
|
function( L, S )
|
||
|
local upos;
|
||
|
if not( IsSubloop( L, S ) ) then
|
||
|
Error( "LOOPS: <2> must be a subloop of <1>." );
|
||
|
fi;
|
||
|
upos := PosInParent( S );
|
||
|
return ForAll( GeneratorsOfGroup( InnerMappingGroup( L ) ),
|
||
|
g -> upos = OnSets( upos, g ) );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#F NormalClosure( L, S )
|
||
|
##
|
||
|
## When <S> is a subset of a loop <L> or a subloop of < L >, returns the
|
||
|
## normal closure of <S> in <L>, i.e., the smallest normal subloop of <L>
|
||
|
## containing <S>.
|
||
|
|
||
|
InstallOtherMethod( NormalClosure,
|
||
|
"for loops",
|
||
|
[ IsLoop, IsLoop ],
|
||
|
function( L, S )
|
||
|
if not IsSubloop( L, S ) then
|
||
|
Error( "LOOPS: <2> must be a subloop of <1>." );
|
||
|
fi;
|
||
|
return NormalClosure( L, Elements( S ) );
|
||
|
end);
|
||
|
|
||
|
InstallOtherMethod( NormalClosure,
|
||
|
"for loops",
|
||
|
[ IsLoop, IsCollection ],
|
||
|
function( L, gens )
|
||
|
local transl_of_gens, nc_mltgr;
|
||
|
if ForAny( gens, x -> not x in L ) then
|
||
|
Error( "LOOPS: <2> must be a subset of <1>." );
|
||
|
fi;
|
||
|
transl_of_gens := List( gens, x -> LeftTranslation( L, x ) );
|
||
|
nc_mltgr := Group( Union(
|
||
|
GeneratorsOfGroup( InnerMappingGroup( L ) ), transl_of_gens
|
||
|
) );
|
||
|
return SubquasigroupNC( Parent( L ), Set( Orbit( nc_mltgr, 1 ) ) );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#P IsSimple( L )
|
||
|
##
|
||
|
## Returns true if <L> is a simple loop.
|
||
|
|
||
|
InstallOtherMethod( IsSimple,
|
||
|
"for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return IsPrimitive( MultiplicationGroup( L ) );
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
## FACTOR LOOP
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O FactorLoop( L, N )
|
||
|
##
|
||
|
## When <N> is a normal subloop of loop <L>, returns the factor loop L/N.
|
||
|
|
||
|
InstallMethod( FactorLoop, "for two loops",
|
||
|
[ IsLoop, IsLoop ],
|
||
|
function( L, N )
|
||
|
local cosets, ltrans, f_ct, f_loop;
|
||
|
if not( IsNormal( L, N ) ) then
|
||
|
Error( "LOOPS: <2> must be normal in <1>." );
|
||
|
fi;
|
||
|
cosets := Set( Orbit( MultiplicationGroup( L ), Elements( N ), OnSets ) );
|
||
|
f_ct := List( cosets, x -> List( cosets, y ->
|
||
|
First( [1..Length(cosets)], i->x[1]*y[1] in cosets[i] )
|
||
|
) );
|
||
|
f_loop := LoopByCayleyTable( f_ct );
|
||
|
return f_loop;
|
||
|
end);
|
||
|
|
||
|
InstallOtherMethod( \/, "for two loops",
|
||
|
IsIdenticalObj,
|
||
|
[ IsLoop, IsLoop ], 0,
|
||
|
function( L, N )
|
||
|
return FactorLoop( L, N );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#O NaturalHomomorphismByNormalSubloop( L, N )
|
||
|
##
|
||
|
## When <N> is a normal subloop of loop <L>, returns the natural projection
|
||
|
## from <L> onto the factor loop L/N.
|
||
|
|
||
|
InstallMethod( NaturalHomomorphismByNormalSubloop, "for two loops",
|
||
|
[ IsLoop, IsLoop ],
|
||
|
function( L, N )
|
||
|
local cosets, in_coset, f_ct, f_loop, map;
|
||
|
if not( IsNormal( L, N ) ) then
|
||
|
Error( "LOOPS: <2> must be normal in <1>." );
|
||
|
fi;
|
||
|
cosets := Set( Orbit( MultiplicationGroup( L ), Elements( N ), OnSets ) );
|
||
|
in_coset := function( n )
|
||
|
return First( [1..Size( L )/Size( N )], i -> n in cosets[ i ] );
|
||
|
end;
|
||
|
f_ct := List( cosets, x -> List( cosets, y -> in_coset( x[1]*y[1] ) ) );
|
||
|
f_loop := LoopByCayleyTable( f_ct );
|
||
|
map := function( x )
|
||
|
return Elements( f_loop )[ in_coset( x ) ];
|
||
|
end;
|
||
|
return MappingByFunction( L, f_loop, map );
|
||
|
end );
|
||
|
|
||
|
###########################################################################
|
||
|
## NILPOTENCY
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A NilpotencyClassOfLoop( L )
|
||
|
##
|
||
|
## Returns the nilpotency class of loop <L>. When <L> is not nilpotent,
|
||
|
## returns fail.
|
||
|
|
||
|
InstallMethod( NilpotencyClassOfLoop, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
local n;
|
||
|
if Size( L ) = 1 then
|
||
|
return 0;
|
||
|
elif
|
||
|
Size( Centre( L ) ) = 1 then
|
||
|
return fail;
|
||
|
else
|
||
|
n := NilpotencyClassOfLoop( L / Centre( L ) );
|
||
|
if n = fail then
|
||
|
return fail;
|
||
|
else
|
||
|
return n + 1;
|
||
|
fi;
|
||
|
fi;
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#P IsNilpotent( L )
|
||
|
##
|
||
|
## Returns true if <L> is nilpotent.
|
||
|
|
||
|
InstallOtherMethod( IsNilpotent, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return NilpotencyClassOfLoop( L ) <> fail;
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#P IsStronglyNilpotent( L )
|
||
|
##
|
||
|
## Returns true if <L> is strongly nilpotent, i.e., when the multiplication
|
||
|
## group of <L> is nilpotent.
|
||
|
|
||
|
InstallMethod( IsStronglyNilpotent, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
return IsNilpotent( MultiplicationGroup( L ) );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A UpperCentralSeries( L )
|
||
|
##
|
||
|
## Returs the upper central series of the loop L.
|
||
|
|
||
|
InstallOtherMethod( UpperCentralSeries, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function(L)
|
||
|
local fmap, Lbar;
|
||
|
|
||
|
if Size(Center(L))=1 then return [ Subloop(L, []) ]; fi;
|
||
|
fmap := NaturalHomomorphismByNormalSubloop( L, Center( L ) );
|
||
|
Lbar := Range( fmap );
|
||
|
return Concatenation(
|
||
|
List( UpperCentralSeries( Lbar ), s -> Subloop(L, PreImage( fmap, s ) ) ),
|
||
|
[ Subloop( L, [] ) ]
|
||
|
);
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A LowerCentralSeries( L )
|
||
|
##
|
||
|
## Returs the lower central series of the loop L.
|
||
|
|
||
|
InstallOtherMethod( LowerCentralSeries, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function(L)
|
||
|
local last, new, ret, x;
|
||
|
|
||
|
last := [];
|
||
|
new := L;
|
||
|
ret := [];
|
||
|
while new <> last do
|
||
|
last := ShallowCopy(new);
|
||
|
Add( ret, last );
|
||
|
new := [ One(L) ];
|
||
|
for x in last do
|
||
|
new := Union( new, Orbit(InnerMappingGroup(L),x)/x );
|
||
|
od;
|
||
|
new := NormalClosure(L, new);
|
||
|
od;
|
||
|
return ret;
|
||
|
end);
|
||
|
|
||
|
#############################################################################
|
||
|
## SOLVABILITY
|
||
|
## -------------------------------------------------------------------------
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#P IsSolvable( L )
|
||
|
##
|
||
|
## Returns true if <L> is solvable.
|
||
|
|
||
|
InstallOtherMethod( IsSolvable, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
local N;
|
||
|
N := DerivedSubloop( L );
|
||
|
if Size( N ) = 1 then return true; fi;
|
||
|
if N = L then return false; fi;
|
||
|
return IsSolvable( N ) and IsSolvable( L / N );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#F DerivedSubloop( L )
|
||
|
##
|
||
|
## Returns the derived subloop of <L>.
|
||
|
|
||
|
InstallMethod( DerivedSubloop, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
local D;
|
||
|
D := Orbit( DerivedSubgroup( MultiplicationGroup( L ) ), One( L ) );
|
||
|
return Subloop( L, D );
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A DerivedLength( L )
|
||
|
##
|
||
|
## Returns the derived length of <L>.
|
||
|
|
||
|
InstallOtherMethod( DerivedLength, "for a loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
local D;
|
||
|
D := DerivedSubloop( L );
|
||
|
if Size( D ) = Size ( L ) then
|
||
|
return 0;
|
||
|
else
|
||
|
return DerivedLength( D ) + 1;
|
||
|
fi;
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A FrattiniSubloop( L )
|
||
|
##
|
||
|
## Returns the Frattini subloop of a strongly nilpotent loop <L>.
|
||
|
|
||
|
InstallMethod( FrattiniSubloop, "for a strongly nilpotent loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
local F;
|
||
|
if IsStronglyNilpotent( L ) then
|
||
|
F := Orbit( FrattiniSubgroup( MultiplicationGroup( L ) ), One( L ) );
|
||
|
return Subloop( L, F );
|
||
|
else
|
||
|
TryNextMethod();
|
||
|
return;
|
||
|
fi;
|
||
|
end );
|
||
|
|
||
|
#############################################################################
|
||
|
##
|
||
|
#A FrattinifactorSize( L )
|
||
|
##
|
||
|
## Returns the Frattini factor size of loop <L>, i.e., the index of
|
||
|
## the Frattini subloop of <L> in <L>.
|
||
|
|
||
|
InstallOtherMethod( FrattinifactorSize, "for a strongly nilpotent loop",
|
||
|
[ IsLoop ],
|
||
|
function( L )
|
||
|
if IsStronglyNilpotent( L ) then
|
||
|
return Size( L ) / Size( FrattiniSubloop( L ) );
|
||
|
else
|
||
|
TryNextMethod();
|
||
|
return;
|
||
|
fi;
|
||
|
end );
|