// Computing geometric invariants of genus one curves // Author : T.A.Fisher // Version : February 2008 // Requires Magma version 2.14. MC := MonomialCoefficient; MD := MonomialsOfDegree; Coeff := Coefficient; intrinsic RationalGCD(S::Setq[RngElt]) -> RngElt {The greatest common divisor of the elements of the set/sequence S.} require #S gt 0 : "Illegal null set/sequence"; B := Universe(S); require ISA(Type(B),FldRat) or ISA(Type(B),RngInt) : "Universe of set/sequence must be Z or Q."; d := LCM([Denominator(Rationals()!x):x in S| x ne 0] cat [1]); return B!(GCD([IntegerRing()!(x*d): x in S])/d); end intrinsic; function PrimitiveModel(phi) // The GCD of the coefficients of the model phi B := CoefficientRing(phi); R := PolynomialRing(phi); n := Degree(phi); assert n in {3,4,5}; if B cmpeq Rationals() then coeffs := Eltseq(phi); dd := RationalGCD(coeffs); coeffs := [x/dd : x in coeffs]; return GenusOneModel(B,n,coeffs:PolyRing:=R); else return phi; end if; end function; intrinsic InvariantDifferential(phi::ModelG1) -> AlgMatElt {The invariant differential defined by the genus one model phi (of degree 3,4 or 5).} n := Degree(phi); require n in {3,4,5} : "Input must be a genus one model of degree 3,4 or 5."; R := PolynomialRing(phi); B := CoefficientRing(phi); case n : when 3 : f := Equation(phi); Omega := ZeroMatrix(R,4,4); for pi in SymmetricGroup(3) do r,s,i := Explode([a^pi: a in [1..3]]); Omega[r,s] := Sign(pi)*Derivative(f,i); end for; when 4 : P := Equations(phi); Q := [-P[2],P[1]]; Omega := ZeroMatrix(R,4,4); for pi in SymmetricGroup(4) do r,s,i,j := Explode([a^pi: a in [1..4]]); Omega[r,s] +:= Sign(pi)*Derivative(P[1],i)*Derivative(P[2],j); end for; when 5 : P := Equations(phi); PHI := Matrix(phi); Jmat := Matrix(R,5,5,[Derivative(P[i],R.j): i,j in [1..5]]); M1 := [Matrix(B,5,5,[MC(PHI[i,j],R.k):i,j in [1..5]]): k in [1..5]]; M2 := [Transpose(Jmat)*M*Jmat: M in M1]; Omega := ZeroMatrix(R,5,5); for pi in SymmetricGroup(5) do r,s,i,j,k := Explode([a^pi: a in [1..5]]); if i lt j and j lt k then Omega[r,s] := Sign(pi)*M2[j][i,k]; end if; end for; end case; return Omega; end intrinsic; intrinsic InvariantDifferential(C::Crv) -> AlgMatElt {An invariant differential on the genus one normal curve C, computed by linear algebra.} R := CoordinateRing(Ambient(C)); K := BaseRing(R); n := Rank(R); require n ge 4 : "C must have degree at least 4."; qq := Equations(C); mons := MD(R,2); V := VectorSpace(K,#mons); U := sub; W,quomap := quo; quads := [&+[x[i]*mons[i] : i in [1..#mons]] where x is Eltseq(W.j @@ quomap) : j in [1..Dimension(W)]]; bb := [Matrix(R,n,n,[,]): i,j in [1..n] | i lt j]; BB := [q*b: q in quads,b in bb]; mons := MD(R,3); V:= VectorSpace(K,#mons); U := sub; W,quomap := quo; function polytoseq(f) vec := V![MC(f,mon):mon in mons]; return Eltseq(quomap(vec)); end function; Jmat := Matrix(#qq,n,[Derivative(q,R.i): i in [1..n],q in qq]); mat := Matrix(K,#BB,n*#qq*Dimension(W), [&cat[polytoseq(f): f in Eltseq(Jmat*B)]: B in BB]); km := KernelMatrix(mat); assert Nrows(km) eq 1; return &+[km[1,i]*BB[i]: i in [1..#BB]]; end intrinsic; function ordinalstring(r) str := "th"; if r mod 10 eq 1 and r mod 100 ne 11 then str:= "st"; end if; if r mod 10 eq 2 and r mod 100 ne 12 then str:= "nd"; end if; if r mod 10 eq 3 and r mod 100 ne 13 then str:= "rd"; end if; return IntegerToString(r) cat str; end function; intrinsic WronskianPFunction(C::Crv,Omega::AlgMatElt) -> FldFunFracSchElt, FldFunFracSchElt {The Weierstrass P function and its derivative, computed using Anderson\'s method.} Rx := CoordinateRing(Ambient(C)); n := Rank(Rx); K := FunctionField(C); Ry := PreimageRing(Parent(Numerator(K.1*K.2))); assert forall{i : i in [1..n-1] | K!(Rx.i/Rx.n) eq K!Ry.i}; dd := [K|(Omega[i,n]/Rx.n^2) : i in [1..n-1]]; function deriv1(x) y := Ry!x; return &+[K!Derivative(y,Ry.i)*dd[i]: i in [1..n-1]]; end function; function deriv(x) a := Ry!Numerator(x); b := Ry!Denominator(x); return K!(((K!b)*deriv1(a)-(K!a)*deriv1(b))/(K!b)^2); end function; b := [K|Rx.i/Rx.n : i in [1..n]]; bb := [b]; for r in [1..n+1] do printf " Computing %o derivatives\n",ordinalstring(r); time bnew := [deriv(x)/r: x in b]; b := bnew; bb cat:= [b]; end for; W := Determinant(Matrix(n,n,[bb[i]: i in [1..n]])); W1 := Determinant(Matrix(n,n,[bb[i]: i in [1..n-1] cat [n+1]])); W2 := Determinant(Matrix(n,n,[bb[i]: i in [1..n-1] cat [n+2]])); W11 := Determinant(Matrix(n,n,[bb[i]: i in [1..n-2] cat [n,n+1]])); printf " Computing P\n"; time P := (W1/W)^2-(W2/W)-(W11/W); printf " Computing P'\n"; time PP := deriv(P)/n; return P,PP; end intrinsic; function NextElement(m,seq,y) F1 := Domain(m); A := Codomain(m); P := PolynomialRing(F1); poly := P!MinimalPolynomial(y); ff := [f[1]: f in Factorization(poly)]; assert exists(f){f : f in ff | &+[m(Coeff(f,i))*y^i: i in [0..Degree(f)]] eq 0}; F2 := ext; F3 := AbsoluteField(F2); seq := [F3!x: x in seq] cat [F3!F2.1]; f := Eltseq(F2!F3.1); elt := &+[m(f[i])*y^(i-1): i in [1..#f]]; m := homA|elt>; return m,seq; end function; function PassToField(seq) // Computes the field generated by a sequence of elements // of an algebraically closed field, and returns the sequence // as elements of the new field. A := Universe(seq); m := homA|>; ans := []; for y in seq do m,ans := NextElement(m,ans,y); end for; return ans; end function; function IntersectWithHyperplane(C) // Finds a point on C (over a suitable field extension) // by intersecting with a hyperplane. P := Ambient(C); R := CoordinateRing(P); X := Scheme(P,Equations(C) cat [R.1]); pt := PointsOverSplittingField(X)[1]; pt := PassToField(Eltseq(pt)); K := Universe(pt); return C(K)!pt; end function; function MovePoint(C,Omega,P) // Given a point P on the curve C, changes co-ordinates // so that P = (0:0: ... :0:1). The matrix Omega, describing // an invariant differential on C is also updated. K := Ring(Parent(P)); C := BaseChange(C,K); R := CoordinateRing(Ambient(C)); n := Rank(R); pt := Eltseq(P); assert exists(i){i : i in [n..1 by -1]|pt[i] ne 0}; I := IdentityMatrix(K,n); II := [Eltseq(I[j]) : j in [1..n] | j ne i]; M := Transpose(Matrix(K,n,n,II cat [pt])); eqns := Equations(C); eqns := [f^M : f in eqns]; Omega := Matrix(R,n,n,[(R!Omega[i,j])^M: i,j in [1..n]]); M1 := M^(-1); Omega := M1*Omega*Transpose(M1); assert forall{f : f in eqns | MC(f,R.n^2) eq 0}; return Scheme(Proj(R),eqns),Omega; // Using "Curve" instead of "Scheme" prompts Magma // to do some unnecessary and time consuming checks. end function; function ProjectFromPoint(C,Omega) // Let C be a genus one normal curve of degree n>=4, that passes // through the point P = (0:0: ... :0:1). Projecting away from P // we obtain a genus one normal curve of degree n-1. The matrix Omega, // describing an invariant differential on C is updated, and // the image of P is also returned. eqns := Equations(C); R := Universe(eqns); K := BaseRing(R); n := Rank(R); assert #eqns eq n*(n-3)/2; assert forall{f : f in eqns | TotalDegree(f) eq 2}; assert forall{f : f in eqns | MC(f,R.n^2) eq 0}; mat := Matrix(K,#eqns,n,[MC(f,R.i*R.n):i in [1..n],f in eqns]); mat1 := Matrix(K,n-1,#eqns,[mat[i,j]:i in [1..#eqns],j in [1..n-1]]); function RemoveLast(f) vec := Vector(K,n,[MC(f,R.i*R.n): i in [1..n]]); sol := Solution(mat,vec); g := &+[sol[i]*eqns[i]: i in [1..#eqns]]; return f-g; end function; for i,j in [1..n-1] do Omega[i,j] := RemoveLast(Omega[i,j]); end for; m := Integers()!((n-1)*(n-4)/2); km := RMatrixSpace(R,#eqns,m)!Transpose(KernelMatrix(mat)); if n eq 4 then ll := [&+[MC(f,R.i*R.4)*R.i: i in [1..3]] : f in eqns]; eqns := [ll[1]*eqns[2]-ll[2]*eqns[1]]; else eqns := Eltseq(Vector(eqns)*km); end if; S := PolynomialRing(K,n-1); xx := [S.i: i in [1..n-1]] cat [0]; eqns := [S|Evaluate(f,xx):f in eqns]; // C := Curve(Proj(S),eqns); C := Scheme(Proj(S),eqns); Omega := Matrix(S,n-1,n-1,[Evaluate(Omega[i,j],xx):i,j in [1..n-1]]); km := KernelMatrix(mat1); P := C(K)!Eltseq(km[1]); return C,Omega,P; end function; function LinearDependence(seq) // Finds a linear dependence between a sequence of // elements of a function field. K := Universe(seq); n := #seq; for i in [1..n] do d := K!Denominator(seq[i]); seq := [d*x: x in seq]; end for; A := Parent(Numerator(K.1)); seq := ChangeUniverse(seq,A); R := PreimageRing(A); polys := ChangeUniverse(seq,R); mons := &join[SequenceToSet(Monomials(f)): f in polys]; mons := SetToSequence(mons); mat := Matrix(#polys,#mons,[MC(f,mon): mon in mons,f in polys]); km := KernelMatrix(mat); return km[1]; end function; function SchemeToGenusOneModel(C) K := BaseRing(C); assert IsField(K); P := AmbientSpace(C); R := CoordinateRing(P); n := Rank(R); assert n in {3,4,5}; eqns := MinimalBasis(Ideal(C)); return GenusOneModel(eqns); end function; intrinsic GeometricInvariants(C::Sch,Omega::AlgMatElt : Method:="Invariants",nproj:=1,P:=0) -> SeqEnum {Computes the geometric invariants c4 and c6 of the pair (C,omega), where C is a genus one normal curve, and omega is an invariant differential on C specified by a matrix of quadrics Omega by the rule omega = x_j^2 d(x_i/x_j) / Omega_\{ij\} for all i<>j. The methods available are "Invariants", "Projection" and "Wronskians". The parameter nproj specifies the number of projections used. A point P may be specified for the projection method.} require Method in {"Invariants","Wronskians","Projection"} : "Method should be either \"Invariants\", \"Wronskians\" or \"Projection\""; n := Dimension(Ambient(C))+1; if n eq 3 and Method eq "Projection" then Method := "Invariants"; end if; printf "n = %o. Using the %o method\n",n,Method; case Method : when "Projection" : if P cmpeq 0 then P := IntersectWithHyperplane(C); end if; C1,Omega1 := MovePoint(C,Omega,P); C2,Omega2,P := ProjectFromPoint(C1,Omega1); mm := nproj eq 1 select "Invariants" else "Projection"; cc := GeometricInvariants(C2,Omega2:Method:=mm,nproj:=nproj-1,P:=P); when "Invariants" : require n le 5 : "Curve must have degree at most 5"; phi := SchemeToGenusOneModel(C); phi := PrimitiveModel(phi); Omega1 := InvariantDifferential(phi); nf1 := NormalForm(Omega[1,2],Ideal(C)); nf2 := NormalForm(Omega1[1,2],Ideal(C)); u := nf1/nf2; c4,c6 := Explode(cInvariants(phi)); cc := [u^4*c4,u^6*c6]; when "Wronskians" : P,PP := WronskianPFunction(C,Omega); km := LinearDependence([PP^2-4*P^3,P,1]); cc := [12*km[2]/km[1],216*km[3]/km[1]]; end case; return ChangeUniverse(cc,BaseRing(C)); end intrinsic;