############################################################################# ## #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 gnerating . In most circumstances, ## Elements( Q ) is returned. InstallMethod( GeneratorsOfQuasigroup, "for quasigroup", [ IsQuasigroup ], GeneratorsOfDomain ); ############################################################################# ## #A GeneratorsSmallest( Q ) ## ## Returns a smallest generating set of a quasigroup 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 . 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). 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 . 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 be an element of a quasigroup . Returns the left translation ## by in . 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 be an element of a quasigroup . Returns the right translation ## by in . 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_ generated by all left translations of the ## quasigroup . InstallMethod( LeftMultiplicationGroup, "for quasigroup", [IsQuasigroup ], function( Q ) return Group( LeftSection( Q ) ); end ); ############################################################################# ## #A RightMultiplicationGroup( Q ) ## ## Returns the subgroup of S_ generated by all right translations of the ## quasigroup . InstallMethod( RightMultiplicationGroup, "for quasigroup", [IsQuasigroup ], function( Q ) return Group( RightSection( Q ) ); end ); ############################################################################# ## #A MultiplicationGroup( Q ) ## ## Returns the smallest subgroup of S_ containing the both the left ## and right multiplication groups of the quasigroup . 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 ## with respect to its subquasigroup . 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 ## with respect to its subquasigroup . 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 ## with respect to its subquasigroup . 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 determined by elements ## , , 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 be an element of a loop . Returns the middle inner mapping ## determined by , 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 determined by elements ## , , 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 , 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 , 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 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 , 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 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 ## 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 is a nonempty list of elements of a quasigroup , or the ## indices of them, it returns the subquasigroup of generated by . 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 is a list of elements of a loop , it returns the ## subloop of generated by . When is empty, it returns ## the trivial subloop of . 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 is a subquasigorup of a quasigroup . 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 is a subloop of a loop . 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 . 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 = whenever m is relatively prime to |x|. # If S is a subloop of a LIP loop L and x is in L\S then = for every z in S. # If S is a subloop of a LIP power associative loop L and x is in L\S then = 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 . 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 . 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 . 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 . 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 . 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 . 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 . # (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 , i.e., the subloop of ## 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 . Assumes that 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 is a normal subloop of loop . 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 is a subset of a loop or a subloop of < L >, returns the ## normal closure of in , i.e., the smallest normal subloop of ## containing . 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 is a simple loop. InstallOtherMethod( IsSimple, "for a loop", [ IsLoop ], function( L ) return IsPrimitive( MultiplicationGroup( L ) ); end); ############################################################################# ## FACTOR LOOP ## ------------------------------------------------------------------------- ############################################################################# ## #O FactorLoop( L, N ) ## ## When is a normal subloop of loop , 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 is a normal subloop of loop , returns the natural projection ## from 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 . When 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 is nilpotent. InstallOtherMethod( IsNilpotent, "for a loop", [ IsLoop ], function( L ) return NilpotencyClassOfLoop( L ) <> fail; end ); ############################################################################# ## #P IsStronglyNilpotent( L ) ## ## Returns true if is strongly nilpotent, i.e., when the multiplication ## group of 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 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 . 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 . 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 . 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 , i.e., the index of ## the Frattini subloop of in . 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 );