diff --git a/lib/Orru/HAP_SL3ZSubgroupTree_fast.gi b/lib/Orru/HAP_SL3ZSubgroupTree_fast.gi new file mode 100644 index 00000000..1a8d0d3e --- /dev/null +++ b/lib/Orru/HAP_SL3ZSubgroupTree_fast.gi @@ -0,0 +1,80 @@ +#descrive come i generatori di un gruppo agiscono sui coset di un sottogruppo. + +InstallGlobalFunction(HAP_SL3ZSubgroupTree_fast, +function(G) + local ambientGenerators, tree, InGmodU, Ugrp, S, T, U, v, p, g, s, n, q, vv, leaves, genTriples, generators, InLowDegreeNodesModG, csts, cnt, vertex2word, one, triple2word, i, j, u,c, a, b; + + S := [ [1,0,1], [0,-1,-1], [0,1,0] ]; + T := [ [0,1,0], [0,0,1], [1,0,0]]; + U := [[0,1,0], [1,0,0], [-1,-1,-1]]; + + ambientGenerators := [S,T,U]; + one := IdentityMat(3); #to change? stabilizers to consider + Ugrp := G!.ugrp; + Ugrp := Elements( Ugrp ); + tree := [ ]; + genTriples := [ ]; + cnt := 1; + leaves := NewDictionary( S, true, SL( 3, Integers ) ); + csts := [ ]; + csts[G!.cosetPos( one )] := 1; + AddDictionary( leaves, one, G!.cosetPos( one ) ); + InLowDegreeNodesModG := function ( g ) + local pos; + pos := G!.cosetPos( g ); + if not IsBound( csts[pos] ) then + return false; + else + ; + return pos; + fi; + return; + end; + while Size( leaves ) > 0 do + vv := 1 * leaves!.entries[1]; + v := vv[1]; + p := G!.cosetPos( v ); + for s in [ 1 .. Length( ambientGenerators ) ] do + g := ambientGenerators[s] * v; + q := InLowDegreeNodesModG( g ); + if q = false then + q := G!.cosetPos( g ); + AddDictionary( leaves, g, q ); + csts[q] := 1; + tree[q] := [ p, s ]; + else + Add( genTriples, [ p, s, v, g ] ); + fi; + od; + RemoveDictionary( leaves, v ); + od; + vertex2word := function ( v ) + local word; + word := one; + while IsBound( tree[v] ) do + word := word * ambientGenerators[tree[v][2]]; + v := tree[v][1]; + od; + return word; + end; + triple2word := function ( x ) + local u, uu, g, q, c; + for u in Ugrp do + c := x[4] ^ -1 * u * vertex2word( G!.cosetPos( x[4] ) ); + if G!.membership( c ) then + return c; + fi; + od; + return fail; + end; + genTriples := List( genTriples, function ( x ) + return triple2word( x ); + end ); + genTriples := List( genTriples, function ( x ) + return Minimum( x, x ^ -1 ); + end ); + genTriples := SSortedList( genTriples ); + G!.tree := tree; + G!.GeneratorsOfMagmaWithInverses := genTriples; + return tree; +end); \ No newline at end of file diff --git a/lib/Orru/decsOrru.gd b/lib/Orru/decsOrru.gd index ce6cfd07..1a56af76 100644 --- a/lib/Orru/decsOrru.gd +++ b/lib/Orru/decsOrru.gd @@ -1,5 +1,8 @@ IsHapCongruenceSubgroup:=NewFilter("IsHapCongruenceSubgroup");; DeclareGlobalFunction("HAP_GenericCongruenceSubgroup"); +DeclareGlobalFunction("FiniteProjectiveLine"); +DeclareGlobalFunction("FiniteProjectivePlane"); +DeclareGlobalFunction("HAP_SL3ZSubgroupTree_fast"); DeclareOperation("HAPCongruenceSubgroupGamma0",[IsInt,IsInt]); DeclareOperation("HAPCongruenceSubgroupTree",[IsHapCongruenceSubgroup]); @@ -9,7 +12,7 @@ InstallMethod( ViewObj, 10000000, #Ensures that this method is chosen function(G) Print(G!.name," of ",G!.fam); - end); +end); InstallMethod( PrintObj, "for HapCongruenceSubgroup", @@ -17,5 +20,5 @@ InstallMethod( PrintObj, 100000000, #Ensures that this method is chosen function(G) Print(G!.name," of ",G!.fam); - end); +end); diff --git a/lib/Orru/finiteProjectiveSpaces.gi b/lib/Orru/finiteProjectiveSpaces.gi new file mode 100644 index 00000000..bf54c28a --- /dev/null +++ b/lib/Orru/finiteProjectiveSpaces.gi @@ -0,0 +1,88 @@ +InstallGlobalFunction(FiniteProjectiveLine, +function(n) + local UnitEls, x, y, i, c, d, u, UnitsAction, Representatives, + RepOf, r, v, w, m, min; + + UnitEls := Units(Integers mod n); + + UnitsAction := function(c, u) + local uu; + uu :=Int (u); + return List(c, x -> (uu * x) mod n); + end; + + Representatives := []; + + RepOf := []; + for x in [1..n] do + RepOf[x] := []; + od; + + for x in [0..n-1] do + for y in [0..n-1] do + if Gcd(x,y,n) = 1 then + v := [x,y]; + if not IsBound(RepOf[x+1][y+1]) then + m := Orbit(UnitEls,v,UnitsAction); + min := Minimum(m); + AddSet(Representatives,min); + for w in m + 1 do + RepOf[w[1]][w[2]] := min; + od; + fi; + fi; + od; + od; + + return rec( + Reps := Set(Representatives), + RepOf:= RepOf + ); +end); + +InstallGlobalFunction(FiniteProjectivePlane, +function(n) + local UnitEls, x, y, z, i, c, d, u, UnitsAction, Representatives, + RepOf, r, v, w, m, min; + + UnitEls := Units(Integers mod n); + + UnitsAction := function(c, u) + local uu; + uu :=Int (u); + return List(c, x -> (uu * x) mod n); + end; + + Representatives := []; + + RepOf := []; + for x in [1..n] do + RepOf[x] := []; + for y in [1..n] do + RepOf[x][y] := []; + od; + od; + + for x in [0..n-1] do + for y in [0..n-1] do + for z in [0..n-1] do + if Gcd(x,y,z,n) = 1 then + v := [x,y,z]; + if not IsBound(RepOf[x+1][y+1][z+1]) then + m := Orbit(UnitEls,v,UnitsAction); + min := Minimum(m); + AddSet(Representatives,min); + for w in m + 1 do + RepOf[w[1]][w[2]][w[3]] := min; + od; + fi; + fi; + od; + od; + od; + + return rec( + Reps := Set(Representatives), + RepOf:= RepOf + ); +end); \ No newline at end of file diff --git a/lib/Orru/initOrru.gi b/lib/Orru/initOrru.gi index 635a8155..7df0f304 100644 --- a/lib/Orru/initOrru.gi +++ b/lib/Orru/initOrru.gi @@ -1 +1,3 @@ ReadPackageHap( "lib/Orru/subgroups.gi"); +ReadPackageHap( "lib/Orru/finiteProjectiveSpaces.gi"); +ReadPackageHap( "lib/Orru/HAP_SL3ZSubgroupTree_fast.gi"); \ No newline at end of file diff --git a/lib/Orru/subgroups.gi b/lib/Orru/subgroups.gi index d172021f..966e193c 100644 --- a/lib/Orru/subgroups.gi +++ b/lib/Orru/subgroups.gi @@ -2,7 +2,7 @@ ################################################################### InstallGlobalFunction(HAP_GenericCongruenceSubgroup, function(family,integer,ring,ideal) -local R, n, one, mat, type, G, I, fam; + local R, n, one, mat, type, G, I, fam; # This function returns a template for a congruence subgroup G in some # classical group such as GL(n,R), SL(n,R), Sp_n(R) over a ring of integers R. @@ -18,18 +18,21 @@ local R, n, one, mat, type, G, I, fam; # compute the coset "tree", The *slow* method will use a general brute force # algorithm to first comput the "tree" and then implement "cosetPos". -R:=ring; -n:=integer; -if IsInt(ideal) then I:=IdealByGenerators(R,[ideal]); -else - I:=ideal; -fi; -if not IsBound(I) then return fail; -fi; + R := ring; + n := integer; -one:=One(R); -mat:=one*IdentityMat(n); -fam:=Concatenation(family,"(",String(n),", ",Name(R)," )"); + if IsInt(ideal) then + I := IdealByGenerators(R,[ideal]); + else + I := ideal; + fi; + if not IsBound(I) then + return fail; + fi; + + one := One(R); + mat := one*IdentityMat(n); + fam := Concatenation(family,"(",String(n),", ",Name(R)," )"); type := NewType( FamilyObj([ mat ]), IsGroup and @@ -38,27 +41,27 @@ fam:=Concatenation(family,"(",String(n),", ",Name(R)," )"); IsMatrixGroup and IsHapCongruenceSubgroup); -G:=rec( - ringOfIntegers:=R, #Matrices in the subgroup G lie in the ring R. - level:=I, #This ideal I<=R is used to define G. - fam:=fam, #The string "SL(n,R)". - dimension:=n, - membership:= fail, #true if a matrix g lies in G. - membershipLight:=fail, #true if a matrix g in SL(n,R) lies in G. - gens:=fail, #"Nice" generating set for SL(n,R). - tree:= fail, #Coset tree of G with respect to gens. - generators:= fail, #Generating set for G. - index:=fail, #Index of G in SL(n,R). - cosetRep:= fail, #CosetRep(g) represents g*G for g in SL(n,R). - cosetPos:= fail, #cosetPos(g) is the position of the coset g*G. - ugrp:= Group(mat), #The trivial (or vertex stabilizer) group. - name:="Congruence subgroup"); - -ObjectifyWithAttributes( G, type, + G:=rec( + ringOfIntegers:=R, #Matrices in the subgroup G lie in the ring R. + level:=I, #This ideal I<=R is used to define G. + fam:=fam, #The string "SL(n,R)". + dimension:=n, + membership:= fail, #true if a matrix g lies in G. + membershipLight:=fail, #true if a matrix g in SL(n,R) lies in G. + gens:=fail, #"Nice" generating set for SL(n,R). + tree:= fail, #Coset tree of G with respect to gens. + generators:= fail, #Generating set for G. + index:=fail, #Index of G in SL(n,R). + cosetRep:= fail, #CosetRep(g) represents g*G for g in SL(n,R). + cosetPos:= fail, #cosetPos(g) is the position of the coset g*G. + ugrp:= Group(mat), #The trivial (or vertex stabilizer) group. + name:="Congruence subgroup"); + + ObjectifyWithAttributes(G, type, DimensionOfMatrixGroup, n ); -return G; + return G; end); ################################################################### ################################################################### @@ -69,64 +72,82 @@ InstallMethod( HAPCongruenceSubgroupGamma0, "for integer n and integer m", [IsInt, IsInt], function(n,m) -local G,sl,membership,membershipLight,CosetRep, CosetPos; -if not (n=2 and m>0) then TryNextMethod(); fi; - -#The following implements G=Gamm0(m) < SL(2,Z) - -sl:=SL(2,Integers); -G:=HAP_GenericCongruenceSubgroup("SL",2,Integers,m); - -################################################### -membership:=function(g) -if not g in sl then return false; fi; -if not g[2][1] mod m = 0 then return false; -else return true; fi; -end; -################################################### -################################################### -membershipLight:=function(g) -if not g[2][1] mod m = 0 then return false; -else return true; fi; -end; -################################################### - -G!.membership:=membership; -G!.membershipLight:=membershipLight; -G!.level:=m; - -G!.ugrp:=Group([[1,0],[0,1]]); -G!.name:="CongruenceSubgroupGamma0"; -if m=1 then -G!.index:=m; -else -G!.index:=m*Product(List(SSortedList(Factors(m)), p->1+1/p)); -fi; - -if IsPrimeInt(m) then #NEEDS TO BE EXTENDED TO NONE PRIMES. THAT IS, NEED - #TO IMPLEMENT THE PROJECTIVE LINE P^1(Z/mZ) -########################################### -CosetPos:=function(g) -if g[1][1] mod m =0 then return m+1; fi; -return 1 +((g[2][1]*g[1][1]^-1) mod m); -end; -########################################### - -########################################### -CosetRep:=function(g) -if g[1][1] mod m=0 then return [[0,-1],[1,0]]; fi; -return [[1,0],[(g[2][1]*g[1][1]^-1) mod m,1]]; -end; -########################################### -G!.cosetRep:=CosetRep; -G!.cosetPos:=CosetPos; -fi; - -G:=ObjectifyWithAttributes(G, TypeObj(G), -IsIntegerMatrixGroup, true, -IsFinite, false); -return G; + local G, sl, membership, membershipLight, CosetRep, CosetPos, ProjLine; + if not (n=2 and m>0) then TryNextMethod(); fi; + + #The following implements G=Gamm0(m) < SL(2,Z) + + sl := SL(2,Integers); + G := HAP_GenericCongruenceSubgroup("SL",2,Integers,m); + + ################################################### + membership:=function(g) + if not g in sl then + return false; + fi; + if not g[2][1] mod m = 0 then + return false; + else + return true; + fi; + end; + ################################################### + ################################################### + membershipLight:=function(g) + if not g[2][1] mod m = 0 then + return false; + else + return true; + fi; + end; + ################################################### + + G!.membership := membership; + G!.membershipLight := membershipLight; + G!.level := m; + + G!.ugrp := Group([[1,0],[0,1]]); + G!.name := "CongruenceSubgroupGamma0"; + if m = 1 then + G!.index := m; + else + G!.index := m*Product(List(SSortedList(Factors(m)), p->1+1/p)); + fi; + + ProjLine := FiniteProjectiveLine(m); + CosetPos := function(g) + local v, vv, U, u, w; + v := [g[1][1], g[2][1]]; + vv := List(v, x -> x mod m); + U := Units(Integers mod m); + for u in U do + w := List(vv, x -> (Int(u)*x) mod m); + if w in ProjLine.Reps then + return Position(ProjLine.Reps,w); + fi; + od; + end; + CosetRep := function(i) + local a, c, b, d, gg; + a := ProjLine.Reps[i][1]; + c := ProjLine.Reps[i][2]; + if a = 0 then + return [[0,-1],[1,0]]; + fi; + gg := Gcdex(a,c); + b := -gg.coeff2; + d := gg.coeff1; + return [[a,b],[c,d]]; + end; + G!.cosetRep := CosetRep; + G!.cosetPos := CosetPos; + + G := ObjectifyWithAttributes(G, TypeObj(G), + IsIntegerMatrixGroup, true, + IsFinite, false); + + return G; end); ################################################################### ################################################################### @@ -139,8 +160,10 @@ InstallMethod(HAPCongruenceSubgroupTree, "Coset tree for congruence subgroup", [IsHapCongruenceSubgroup], function(G) -if not (G!.dimension=2 and G!.ringOfIntegers=Integers) then TryNextMethod(); fi; -HAP_SL2ZSubgroupTree_fast(G); + if not (G!.dimension = 2 and G!.ringOfIntegers = Integers) then + TryNextMethod(); + fi; + HAP_SL2ZSubgroupTree_fast(G); end); ################################################################### ################################################################### @@ -153,11 +176,102 @@ InstallOtherMethod( RightTransversal, [IsMatrixGroup, IsHapCongruenceSubgroup], 100000, function(G,H) -if not (H!.dimension=2 and Name(G)="SL(2,Integers)") then TryNextMethod(); fi; -HAPCongruenceSubgroupTree(H); -return HAP_TransversalCongruenceSubgroups(G,H); + if not (H!.dimension = 2 and Name(G) = "SL(2,Integers)") then + TryNextMethod(); + fi; + + HAPCongruenceSubgroupTree(H); + + return HAP_TransversalCongruenceSubgroups(G,H); end); ################################################################### ################################################################### +#SL3 + +InstallMethod( HAPCongruenceSubgroupGamma0, +"for SL(3,Z)", +[IsInt, IsInt], +function(n,m) + local G,sl,membership,membershipLight, ProjPlane, CosetRep, CosetPos, MatrixInSL3_Hermite; + + if not (n = 3 and m > 0) then + TryNextMethod(); + fi; + sl := SL(3, Integers); + G := HAP_GenericCongruenceSubgroup("SL", 3, Integers, m); + + membership := function(g) + if not g in sl then + return false; + fi; + if g[2][1] mod m <> 0 then + return false; + fi; + if g[3][1] mod m <> 0 then + return false; + fi; + return true; + end; + + membershipLight := function(g) + if g[2][1] mod m <> 0 then + return false; + fi; + if g[3][1] mod m <> 0 then + return false; + fi; + return true; + end; + + G!.membership := membership; + G!.membershipLight := membershipLight; + G!.level := m; + G!.name := "CongruenceSubgroupGamma0"; + + MatrixInSL3_Hermite := function(v) + local Herm; + Herm := HermiteNormalFormIntegerMatTransform([[v[1]],[v[2]],[v[3]]]); + return Inverse(Herm!.rowtrans); + end; + + ProjPlane := FiniteProjectivePlane(m); + CosetPos := function(g) + local v, vv, U, u, w; + v := [g[1][1], g[2][1], g[3][1]]; + vv := List(v, x -> x mod m); + U := Units(Integers mod m); + for u in U do + w := List(vv, x -> (Int(u)*x) mod m); + if w in ProjPlane.Reps then + return Position(ProjPlane.Reps,w); + fi; + od; + end; + CosetRep := function(i) + local x,y,z; + x := ProjPlane.Reps[i][1]; + y := ProjPlane.Reps[i][2]; + z := ProjPlane.Reps[i][3]; + + return MatrixInSL3_Hermite([x,y,z]); + end; + + G!.cosetRep := CosetRep; + G!.cosetPos := CosetPos; + + G := ObjectifyWithAttributes(G, TypeObj(G), + IsIntegerMatrixGroup, true, + IsFinite, false); + + return G; +end); + +InstallMethod(HAPCongruenceSubgroupTree, +"Coset tree for congruence subgroup", +[IsHapCongruenceSubgroup], +function(G) +if not (G!.dimension=3 and G!.ringOfIntegers=Integers) then TryNextMethod(); fi; +HAP_SL3ZSubgroupTree_fast(G); +end); \ No newline at end of file