diff --git a/ModFrmHilD/Restriction.m b/ModFrmHilD/Restriction.m index d9b30d98..72f5196d 100644 --- a/ModFrmHilD/Restriction.m +++ b/ModFrmHilD/Restriction.m @@ -1,4 +1,4 @@ -intrinsic RestrictionToDiagonal(f::ModFrmHilDElt,M::ModFrmHilDGRng,bb::RngQuadIdl) -> ModFrmElt +intrinsic RestrictionToDiagonal(f::ModFrmHilDElt,M::ModFrmHilDGRng,bb::RngOrdIdl) -> ModFrmElt {Given an HMF f of parallel weight k, returns the classical modular curve of weight nk and level obtained from restricting the component bb of the HMF to the diagonal.} require #SequenceToSet(Weight(f)) eq 1: "Only defined for parallel weight."; @@ -36,41 +36,29 @@ intrinsic RestrictionToDiagonal(f::ModFrmHilDElt,M::ModFrmHilDGRng,bb::RngQuadId return modForms!(denom*(restriction +O(q^(prec)))); end intrinsic; -/* -// Totally Positive Elements in an Ideal - From a basis {a,b} for the ideal bb where - Tr(a) = n and Tr(b) = 0. - Elements in ideal will look like xa + yb where x,y in ZZ - and have embedding xa_1 + yb_1 and xa_2 + yb_2. - All totally positive elements of given trace t will satisfy - 1) t = Tr(xa+yb) <=> t = xn - 2) xa+yb totally positive <=> y > -x*a_1/b_1 and y > -x*a_2/b_2 - Eq 1) determines the value for x, - and Eq 2) allows us to loop over values of y -*/ intrinsic PositiveElementsOfTrace(aa::RngOrdFracIdl, t::RngIntElt) -> SeqEnum[RngOrdFracIdl] - {Given aa a fractional ideal and t a nonnegative integer, - returns the totally positive elements of aa with trace t.} + { + Given aa a fractional ideal and t a nonnegative integer, + returns the totally positive elements of aa with trace t. + } basis := TraceBasis(aa); - places := InfinitePlaces(NumberField(Parent(basis[1]))); - smallestTrace := Integers()!Trace(basis[1]); - T := []; - if t mod smallestTrace eq 0 then - x := t div smallestTrace; - a_1 := Evaluate(basis[1],places[1]); - a_2 := Evaluate(basis[1],places[2]); - b_1 := Evaluate(basis[2],places[1]); - b_2 := Evaluate(basis[2],places[2]); - assert b_1 lt 0 and b_2 gt 0; // if this assumption changes, the inequalities get swapped - // at place 2, x*a2 + y*b2 >= 0 => y >= -x*a2/b2 - lower_bnd := Ceiling(-x*a_2/b_2); - // at place 1, x*a1 + y*b1 >= 0 => y <= -x*a1/b1 - upper_bnd := Floor(-x*a_1/b_1); - for y in [lower_bnd .. upper_bnd] do - Append(~T, x*basis[1]+y*basis[2]); - end for; - end if; - return T; -end intrinsic; + smallest_trace := Integers()!Trace(basis[1]); + if (t mod smallest_trace) eq 0 then + F := NumberField(Parent(basis[1])); + n := Degree(F); + B := Matrix([[Evaluate(b, v) : v in InfinitePlaces(F)] : b in basis]); + B := t * B^-1; + // drop the first coordinate, it'll always be t / smallest_trace + vertices := [Rationalize(Vector([v[i] : i in [2 .. n]])) : v in Rows(B)]; + assert #vertices[1] eq n-1 and #vertices eq n; + P := Polytope(vertices); + pts := InteriorPoints(P); + // put t / smallest_trace back in each vector + x := t div smallest_trace; + return [x * basis[1] + &+[Eltseq(pt)[i] * basis[i+1] : i in [1 .. n-1]] : pt in pts]; + else + return []; + end if; +end intrinsic; diff --git a/Tests/cubic_cuspforms.m b/Tests/cubic_cuspforms.m new file mode 100644 index 00000000..4db76822 --- /dev/null +++ b/Tests/cubic_cuspforms.m @@ -0,0 +1,26 @@ +// Checks that the restrictions of parallel weight cusp forms over +// cubic fields are classical cusp forms. +// +// TODO abhijitm - this test will be made more robust shortly, once we can compute +// parallel weight k forms for k > 2. + +R := PolynomialRing(Rationals()); +F := NumberField(x^3-x^2-2*x+1); +ZF := Integers(F); + +M := GradedRingOfHMFs(F, 150); +N := 3*ZF; +M222 := HMFSpace(M, N, [2,2,2]); +S222 := CuspFormBasis(M222); +assert #S222 eq 1; +f := S222[1]; + +// The restriction of a parallel weight 2 form f to the diagonal g(z) := f(z,z,z) +// should be a weight 6 classical modular form of level 3: +g := RestrictionToDiagonal(f, M, 1*ZF); + +// the space of level 3 weight 6 forms has dimension 1 +// https://www.lmfdb.org/ModularForm/GL2/Q/holomorphic/3/6/a/a/ +h := Basis(CuspForms(Gamma0(3), 6))[1]; + +assert Degree(F) * h eq g; diff --git a/Tests/cubic_mult.m b/Tests/cubic_mult.m new file mode 100644 index 00000000..cdcca605 --- /dev/null +++ b/Tests/cubic_mult.m @@ -0,0 +1,23 @@ +// tests multiplication for HMFs over cubic fields +// +// checks that the square of an Eisenstein series of parallel weight 1 +// lies in the computed space of [2,2,2] forms. +// +// TODO abhijitm - this test will be made more robust once we can compute +// cusp spaces for weights beyond parallel weight 2. + +R := PolynomialRing(Rationals()); +F := NumberField(x^3-x^2-2*x+1); +ZF := Integers(F); + +M := GradedRingOfHMFs(F, 150); +N := 3*ZF; +M222 := HMFSpace(M, N, [2,2,2]); +B222 := Basis(M222); +H := HeckeCharacterGroup(N, [1,2,3]); +chi := H.1; +assert Order(chi) eq 2; +M111 := HMFSpace(M, N, [1,1,1], chi); +E111 := EisensteinBasis(M111); +E111_squared := [f^2 : f in E111]; +assert #Intersection(E111_squared, B222) eq #E111_squared; diff --git a/Tests/pos_elts_of_trace.m b/Tests/pos_elts_of_trace.m new file mode 100644 index 00000000..f52602e7 --- /dev/null +++ b/Tests/pos_elts_of_trace.m @@ -0,0 +1,59 @@ +// we use the old PositiveElementsOfTrace function to test +// the new one + +/* + Totally Positive Elements in an Ideal + From a basis {a,b} for the ideal bb where + Tr(a) = n and Tr(b) = 0. + Elements in ideal will look like xa + yb where x,y in ZZ + and have embedding xa_1 + yb_1 and xa_2 + yb_2. + All totally positive elements of given trace t will satisfy + 1) t = Tr(xa+yb) <=> t = xn + 2) xa+yb totally positive <=> y > -x*a_1/b_1 and y > -x*a_2/b_2 + Eq 1) determines the value for x, + and Eq 2) allows us to loop over values of y +*/ + +function pos_elts_of_trace_quadratic(aa, t) + // aa - RngOrdFracIdl + // t - RngIntElt + // returns - SeqEnum[RngOrdFracIdl] + basis := TraceBasis(aa); + F := NumberField(Parent(basis[1])); + assert Degree(F) eq 2; // only works for quadratic fields + places := InfinitePlaces(F); + smallestTrace := Integers()!Trace(basis[1]); + T := []; + if t mod smallestTrace eq 0 then + x := t div smallestTrace; + a_1 := Evaluate(basis[1],places[1]); + a_2 := Evaluate(basis[1],places[2]); + b_1 := Evaluate(basis[2],places[1]); + b_2 := Evaluate(basis[2],places[2]); + assert b_1 lt 0 and b_2 gt 0; // if this assumption changes, the inequalities get swapped + // at place 2, x*a2 + y*b2 >= 0 => y >= -x*a2/b2 + lower_bnd := Ceiling(-x*a_2/b_2); + // at place 1, x*a1 + y*b1 >= 0 => y <= -x*a1/b1 + upper_bnd := Floor(-x*a_1/b_1); + for y in [lower_bnd .. upper_bnd] do + Append(~T, x*basis[1]+y*basis[2]); + end for; + end if; + return T; +end function; + +max_N_norm := 20; +max_x := 5; +F := QuadraticField(5); +for N in [I : I in IdealsUpTo(max_N_norm, F) | not IsZero(I)] do + basis := TraceBasis(N); + smallest_trace := Integers()!Trace(basis[1]); + for x in [1 .. max_x] do + assert SequenceToSet(PositiveElementsOfTrace(N, x * smallest_trace)) \ + eq SequenceToSet(pos_elts_of_trace_quadratic(N, x * smallest_trace)); + if smallest_trace ne 1 then + assert #PositiveElementsOfTrace(N, x * smallest_trace - 1) eq 0; + end if; + end for; +end for; +