/* We consider ZG-lattices embedded in QG-modules. More precisely, if M is a ZG-lattice, we store it as the basis matrix V`Mat of a QG-module V. (For this to work, the action generators of V must always be integral). Example: Attach("Lattices.m"); G := SymmetricGroup(3); V := PermutationModule(G, sub, Rationals()); // V = Q^6 = regular Q[S_3]-module V`Mat := IdentityMatrix(Integers(), 6); // the lattice is Z^6 Functions: ZGHomSpace(V, W) Compute a Z-basis of the ZG-homomorphisms V -> W. IsLocallyIsomorphic(V, W, p, MC := false) Checks if the two lattices are locally isomorphic at p. If MC is set to a positive number e with 0 < e < 1, then a Monte Carlo version is run with probability e. IsLocallyIsomorphic(V, W, MC := false) Checks if the two lattices are locally isomorphic at every prime. If MC is set to a positive number e with 0 < e < 1, then the local isomorphism tests are run in Monte Carlo mode with probability e. IsLocallyFree(V, MC := false) Checks if the lattice is locally free. If MC is set to a positive number e with 0 < e < 1, then the local isomorphism tests are run in Monte Carlo mode with probability e. ComputeZGModule(K, m) Returns the ZG-lattice of the ring of integers of K, where m : G -> Aut(K/Q) describes the automorphisms. K must be normal over Q. Example: > K := NumberField(x^2 - 6); > _, _, mG := AutomorphismGroup(K); > V := ComputeZGModule(K, mG); > IsLocallyFree(V); false > IsTamelyRamified(K); false > K := NumberField(x^2 - 13); > _, _, mG := AutomorphismGroup(K); > V := ComputeZGModule(K, mG); > IsLocallyFree(V); true > IsTamelyRamified(K); true */ declare attributes ModGrp: Mat; intrinsic ZGHomSpace(M::ModGrp, N::ModGrp) -> Any {} d := Dimension(M); n := Dimension(N); globalhomring := GHom(M, N); r := Dimension(globalhomring); globalhombasis := Basis(globalhomring); MMatrat := ChangeRing(M`Mat, Rationals()); NMatratinv := ChangeRing(N`Mat, Rationals())^-1; globalhombasis := [ MMatrat * ChangeRing(x,Rationals()) * NMatratinv : x in globalhombasis]; if false in (&cat[[ IsIntegral(x) : x in Eltseq(globalhombasis[i])] : i in [1..#globalhombasis]]) then for i in [1..#globalhombasis] do globalhombasis[i] := Denominator(globalhombasis[i])*globalhombasis[i]; end for; end if; MM := Matrix(Integers(), r, d*n, &cat[ Eltseq(x) : x in globalhombasis ]); MM := Saturation(MM); basis := [ Matrix(Rationals(), d, n, Eltseq(MM[i])) : i in [1..Nrows(MM)]]; //basis := [ ChangeRing(N`Mat, Rationals()) * b * ChangeRing(M`Mat, Rationals())^-1 : b in basis]; return basis; end intrinsic; intrinsic IsLocallyIsomorphic(M::ModGrp, N::ModGrp, p::RngIntElt: MC := false) -> BoolElt {} assert Group(M) eq Group(N); if not IsIsomorphic(M, N) then return false, _; end if; n := Dimension(M); E := ZGHomSpace(M, N); if MC cmpeq true then EPSILON := -20; elif MC cmpeq false then EPSILON := -20; else EPSILON := MC; MC := true; end if; F := FiniteField(p); residuebasis := [ ChangeRing(b, F) : b in E ]; e := #E; assert e eq #residuebasis; if e eq 1 then if Determinant(residuebasis[1]) eq 0 then return false, _; else return true, E[1]; end if; end if; for i in [1..e] do if Determinant(residuebasis[i]) ne 0 then return true, E[i]; end if; end for; extdeg := Ceiling(Log(2,n+1)/Log(2,p)); extdeg2 := extdeg; if not p^extdeg gt 2^20 then extdeg2 := Floor(20/Log(2,p)); end if; FF := FiniteField(p^(extdeg)); FF := FiniteField(p); RANDOM := Ceiling(EPSILON/Log(2,n/(#FF - 1))); // Schwarz-Zippel says I need RANDOM elements cart := CartesianProduct([ FF : x in [1..#residuebasis]]); for i in [1..RANDOM] do j := Random(cart); det := Determinant(&+[j[i]*residuebasis[i] : i in [1..#j]]); if not IsZero(det) then return true, &+[j[j]*E[i] : i in [1..#j]]; end if; end for; if MC eq true then return false; end if; cart := CartesianProduct([ F : x in [1..#residuebasis]]); k := 0; for j in cart do k := k+1; det := Determinant(&+[j[i]*residuebasis[i] : i in [1..#j]]); if IsUnit(det) then return true, &+[j[j]*E[i] : i in [1..#j]]; end if; end for; return false, _; end intrinsic; intrinsic IsLocallyIsomorphic(L::ModGrp, R::ModGrp: MC := false) -> BoolElt {} assert Group(L) eq Group(R); G := Group(L); for p in PrimeDivisors(#G) do if not IsLocallyIsomorphic(L, R, p: MC := MC) then return false; end if; end for; return true; end intrinsic; intrinsic IsLocallyFree(L::ModGrp: MC := false) -> BoolElt {} G := Group(L); if not (Dimension(L) mod #G eq 0) then return false; end if; k := Dimension(L) div #G; F := PermutationModule(G, sub, Rationals()); V := DirectSum([F : i in [1..k]]); V`Mat := IdentityMatrix(Integers(), Dimension(V)); return IsLocallyIsomorphic(L, V: MC := false); end intrinsic; intrinsic ComputeZGModule(K, m) -> BoolElt {} G := Domain(m); O := MaximalOrder(K); d := Degree(K); B := Basis(O); act := []; for g in GeneratorsSequence(G) do M := Matrix(Rationals(), d, d, &cat[ Eltseq(O!(B[i]@(g@m))) : i in [1..d]]); Append(~act, M); end for; V := GModule(G, act); V`Mat := IdentityMatrix(Integers(), d); return V; end intrinsic;