/*
see the file readme.ecff.m for examples and explanations
to load these functions use Attach("ecff.m");
*/
/*
the algorithms are a simple-minded adaptation of the already existing algorithms in magma for treating the number field case.
christian wuthrich, feb 2006
*/
/***********************************************************************/
////////////////////////////////////////////////////////////////////////
// //
// TATE'S ALGORITHM OVER Function FIELDS //
// cw //
// modified from the number field alg. //
// //
////////////////////////////////////////////////////////////////////////
declare verbose Tateff , 4;
// Transforms an elliptic curve E with standard parameters [r,s,t]
// Returns the transformed curve F and the isomorphism E->F
function rst_transform(E, rst)
//{transforms the elliptic curve using the unimodular (u=1) transform with
//standard parameters [r,s,t]. Returns the transformed curve F and the
//isomorphism E->F.}
r,s,t:=Explode(rst);
a1,a2,a3,a4,a6:=Explode(aInvariants(E));
a6 := a6 + r*(a4 + r*(a2 + r)) - t*(a3 + r*a1 + t);
a4 := a4 - s*a3 + 2*r*a2 - (t + r*s)*a1 + 3*r*r - 2*s*t;
a3 := a3 + r*a1 +t+t;
a2 := a2 - s*a1 + 3*r - s*s;
a1 := a1 + s+s;
F:=EllipticCurve([a1,a2,a3,a4,a6]);
iso:=Isomorphism(F,E,[r,s,t,1]);
return F,iso;
end function;
function ScaleCurve(E, u)
//{transforms the elliptic curve using scale factor u:
//i.e. multiplies c_i by u^i.}
a1,a2,a3,a4,a6:=Explode(aInvariants(E));
a1*:=u; a2*:=u^2; a3*:=u^3; a4*:=u^4; a6*:=u^6;
F:=EllipticCurve([a1,a2,a3,a4,a6]);
iso:=Isomorphism(E,F,[0,0,0,u]);
return F,iso;
end function;
// Tate's algorithm for elliptic curves over number fields
// Given curve E and a prime ideal P,
// returns
intrinsic LocalInformation(E::CrvEll[FldFunG], v::PlcFunElt) -> Tup,CrvEll
{Tate's algorithm for an elliptic curve over a function field:
computes local reduction data at the place v,
and a local minimal model.
The model is not required to be integral on input.
If v is principal, uses a generator as uniformizer, so will not
affect integrality or minimality at other primes. Otherwise it will!
Output is
, Emin
where
v = the given place,
vpd = valuation of local minimal discriminant
fp = valuation of conductor
cp = Tamagawa number
K = Kodaira Symbol
Emin is a model (integral and) minimal at v
}
// require IsPrime(P) : "second argument must be a prime ideal";
// require Order(P) eq MaximalOrder(BaseRing(E)): "mismatch between order of P and base field of E";
vprint Tateff,1: "Running Tate's algorithm at the place ",v;
P := Ideal(v);
kv,red := ResidueClassField(P);
l:=Characteristic(kv);
require l gt 4 : "The implementation for characteristic 2 and 3 has not been cone yet.";
pdiv2 := false;
pdiv3 := false;
if IsFinite(v) then
princ,pi:=IsPrincipal(P);
else
princ := false;
end if;
if princ
then
vprint Tateff,2: "P is principal, generator pi = ",pi;
else
pi:=LocalUniformizer(v);
vprint Tateff,2: "P is not principal, uniformizer pi = ",pi;
end if;
function val(x) return Valuation(x,v); end function;
function lift(x) return Lift(x,v); end function;
function pdiv(x) return val(x) gt 0; end function;
function invmod(x) return lift(1/red(x)); end function;
function rootmod(x,e)
xx:=red(x);
fl,y:=IsPower(xx,e);
if fl then return lift(y); else return 0; end if;
end function;
function redmod(x) return lift(red(x)); end function;
function rootsexist(a,b,c)
f:=PolynomialRing(kv)![red(c),red(b),red(a)];
return #Roots(f) gt 0;
end function;
function nrootscubic(b,c,d)
f:=PolynomialRing(kv)![red(d),red(c),red(b),red(1)];
return #Roots(f);
end function;
halfmodp := invmod(2);
a1,a2,a3,a4,a6:=Explode(aInvariants(E));
if Min([val(ai) : ai in [a1,a2,a3,a4,a6] | ai ne 0]) lt 0 then
vprint Tateff,1: "Non-integral model at P: valuations are ",,": making integral";
e:=0;
if a1 ne 0 then e:=Max(e,-val(a1)); end if;
if a2 ne 0 then e:=Max(e,Ceiling(-val(a2)/2)); end if;
if a3 ne 0 then e:=Max(e,Ceiling(-val(a3)/3)); end if;
if a4 ne 0 then e:=Max(e,Ceiling(-val(a4)/4)); end if;
if a6 ne 0 then e:=Max(e,Ceiling(-val(a6)/6)); end if;
pie:=pi^e;
a1*:=pie; a2*:=pie^2; a3*:=pie^3; a4*:=pie^4; a6*:=pie^6;
vprint Tateff,1: "P-integral model is ",[a1,a2,a3,a4,a6]," with valuations ",;
end if;
// main algorithm
while true do
C:=EllipticCurve([a1,a2,a3,a4,a6]);
b2,b4,b6,b8:=Explode(bInvariants(C));
c4,c6:=Explode(cInvariants(C));
delta:=Discriminant(C);
vpd := Valuation(delta,v);
if vpd eq 0 then // Good reduction already
c_p := 1;
KS:=KodairaSymbol("I0");
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
//change coords so that p|a3,a4,a6
if pdiv2 then
if pdiv(b2) then
r := rootmod(a4,2);
t := rootmod(((r+a2)*r+a4)*r+a6,2);
else temp:=invmod(a1);
r := temp*a3;
t := temp*(a4 + r*r);
end if;
else if pdiv3 then
r := pdiv(b2) select rootmod(-b6,3) else -invmod(b2)*b4;
t := a1*r + a3;
else
r := pdiv(c4) select -invmod(12)*b2 else -invmod(12*c4)*(c6+b2*c4);
t := -halfmodp*(a1*r+a3);
end if; end if;
r := redmod(r);
t := redmod(t);
//print "Before first transform, C = ",C;
//print "[a1,a2,a3,a4,a6] = ",[a1,a2,a3,a4,a6];
C:=rst_transform(C,[r,0,t]);
a1,a2,a3,a4,a6:=Explode(aInvariants(C));
b2,b4,b6,b8:=Explode(bInvariants(C));
error if Min([val(ai) : ai in [a1,a2,a3,a4,a6] | ai ne 0]) lt 0,
"Non-integral model after first transform!";
vprint Tateff,4: "After first transform ",[r,0,t];
vprint Tateff,4: "[a1,a2,a3,a4,a6] = ",[a1,a2,a3,a4,a6];
vprint Tateff,4: "Valuations: ",;
error if val(a3) eq 0, "p ndiv a3 after first transform!";
error if val(a4) eq 0, "p ndiv a4 after first transform!";
error if val(a6) eq 0, "p ndiv a6 after first transform!";
// test for Types In, II, III, IV
if not pdiv(c4) then // Type In (n:=vpd)
temp:=-a2;
c_p := rootsexist(1,a1,temp) select vpd
else IsOdd(vpd) select 1 else 2;
KS:=KodairaSymbol("I" cat IntegerToString(vpd));
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
if val(a6) lt 2 then // Type II
KS:=KodairaSymbol("II");
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
if val(b8) lt 3 then // Type III
KS:=KodairaSymbol("III");
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
if val(b6) lt 3 then // Type IV
temp := -(a6/pi)/pi;
temp2 := a3/pi;
c_p := rootsexist(1,temp2,temp) select 3 else 1;
KS:=KodairaSymbol("IV");
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
// else change coords so that p|a1,a2, p^2|a3,a4, p^3|a6
if pdiv2 then
s := rootmod(a2,2);
t := pi*rootmod((a6/pi)/pi,2);
else if pdiv3 then
s := a1;
t := a3;
else
s := -a1*halfmodp;
t := -a3*halfmodp;
end if; end if;
C:=rst_transform(C,[0,s,t]);
a1,a2,a3,a4,a6:=Explode(aInvariants(C));
b2,b4,b6,b8:=Explode(bInvariants(C));
vprint Tateff,4: "After second transform ",[0,s,t];
vprint Tateff,4: "[a1,a2,a3,a4,a6] = ",[a1,a2,a3,a4,a6];
vprint Tateff,4: "Valuations: ",;
error if val(a1) eq 0, "p ndiv a1 after second transform!";
error if val(a2) eq 0, "p ndiv a2 after second transform!";
error if val(a3) lt 2, "p^2 ndiv a3 after second transform!";
error if val(a4) lt 2, "p^2 ndiv a4 after second transform!";
error if val(a6) lt 3, "p^3 ndiv a6 after second transform!";
error if Min([val(ai) : ai in [a1,a2,a3,a4,a6] | ai ne 0]) lt 0,
"Non-integral model after second transform!";
// 3 2
// Analyse roots of the cubic T + bT + cT + d := 0, where
// b:=a2/p, c:=(a4/p)/p, d:=((a6/p)/p)/p
b:=a2/pi;
c:=(a4/pi)/pi;
d:=((a6/pi)/pi)/pi;
bb:=b*b; cc:=c*c; bc:=b*c;
w := 27*d*d - bb*cc + 4*b*bb*d - 18*bc*d + 4*c*cc;
x := 3*c - bb;
sw := pdiv(w) select pdiv(x) select 3 else 2 else 1;
vprint Tateff,2: "Analysing roots of cubic ",[1,b,c,d],"; case " , sw;
case sw:
when 1: //Three distinct roots - Type I*0
KS:=KodairaSymbol("I0*");
c_p:=1+nrootscubic(b,c,d);
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
when 2: // One double root - Type I*m for some m
// Change coords so that the double root is T:=0 mod p
r:= pdiv2 select rootmod(c,2) else
pdiv3 select c*invmod(b) else (bc - 9*d)*invmod(2*x);
r := pi * redmod(r);
C:=rst_transform(C,[r,0,0]);
a1,a2,a3,a4,a6:=Explode(aInvariants(C));
b2,b4,b6,b8:=Explode(bInvariants(C));
ix := 3; iy := 3; mx := pi*pi; my := pi*pi;
loop := true;
while loop do
a2t := a2/pi;
a3t := a3/my;
a4t := (a4/pi)/mx;
a6t := (a6/mx)/my;
temp := a3t*a3t + 4*a6t;
if pdiv(temp) then
t:= pdiv2 select my*rootmod(a6t,2) else my*redmod(-a3t*halfmodp);
C:=rst_transform(C,[0,0,t]);
a1,a2,a3,a4,a6:=Explode(aInvariants(C));
b2,b4,b6,b8:=Explode(bInvariants(C));
my := my*pi;
iy+:=1;
a2t := a2/pi;
a3t := a3/my;
a4t := (a4/pi)/mx;
a6t := (a6/mx)/my;
temp := a4t*a4t - 4*a6t*a2t;
if pdiv(temp) then
r := pdiv2 select mx*rootmod( a6t*invmod(a2t), 2)
else mx*redmod( -a4t*invmod(2*a2t));
C:=rst_transform(C,[r,0,0]);
a1,a2,a3,a4,a6:=Explode(aInvariants(C));
b2,b4,b6,b8:=Explode(bInvariants(C));
mx := mx*pi;
ix+:=1; // and stay in loop
else
c_p := rootsexist(a2t,a4t,a6t) select 4 else 2;
loop := false;
end if; // and exit loop
else
temp := -a6t;
c_p := rootsexist(1,a3t,temp) select 4 else 2;
loop := false;
end if;
end while;
KS:= KodairaSymbol("I" cat IntegerToString(ix+iy-5) cat "*");
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
when 3: // Triple root
// change coords so that T:=0 mod p
r := pdiv2 select b else pdiv3 select rootmod(-d,3) else -b*invmod(3);
r := pi*redmod(r);
C:=rst_transform(C,[r,0,0]);
a1,a2,a3,a4,a6:=Explode(aInvariants(C));
b2,b4,b6,b8:=Explode(bInvariants(C));
vprint Tateff,4: "After third transform ",[r,0,0];
vprint Tateff,4: "[a1,a2,a3,a4,a6] = ",[a1,a2,a3,a4,a6];
vprint Tateff,4: "Valuations: ",;
vprint Tateff,4: "[a1,a2,a3,a4,a6] = ",[a1,a2,a3,a4,a6];
error if Min([val(ai) : ai in [a1,a2,a3,a4,a6] | ai ne 0]) lt 0,
"Non-integral model after third transform!";
error if (val(a2) lt 2) or (val(a4) lt 3) or (val(a6) lt 4),
"Cubic after transform does not have triple root at 0";
a3t := (a3/pi)/pi;
a6t := (((a6/pi)/pi)/pi)/pi;
// test for Type IV*
temp := a3t*a3t + 4*a6t;
if not pdiv(temp) then
temp := -a6t;
c_p := rootsexist(1,a3t,temp) select 3 else 1;
KS:=KodairaSymbol("IV*");
reduct_array := ;
vprint Tate,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
// change coords so that p^3|a3, p^5|a6
t := pdiv2 select -pi*pi*rootmod(a6t,2)
else pi*pi*redmod(-a3t*halfmodp);
C:=rst_transform(C,[0,0,t]);
a1,a2,a3,a4,a6:=Explode(aInvariants(C));
b2,b4,b6,b8:=Explode(bInvariants(C));
// test for types III*, II*
if val(a4) lt 4 then // Type III*
KS:=KodairaSymbol("III*");
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
if val(a6) lt 6 then // Type II*
KS:=KodairaSymbol("II*");
reduct_array := ;
vprint Tateff,1: "Tate returns ",reduct_array;
return reduct_array, C;
end if;
vprint Tateff,1: "Non-minimal equation, dividing out...";
a1/:=pi; a2/:=pi^2; a3/:=pi^3; a4/:=pi^4; a6/:=pi^6;
vprint Tateff,1: "new model is ",[a1,a2,a3,a4,a6];
end case;
end while;
end intrinsic;
// ---------
intrinsic LocalInformation(E::CrvEll[FldFun]) -> SeqEnum
{Local reduction data at all bad primes}
D := Discriminant(E);
places := SequenceToSet(Poles(D) cat Zeroes(D));
return [LocalInformation(E,v) : v in places];
end intrinsic;
intrinsic Conductor(E::CrvEll[FldFunG]) -> DivFunElt
{Conductor divisor of an elliptic curve E over a function field}
D := Discriminant(E);
places := SequenceToSet(Poles(D) cat Zeroes(D));
N := DivisorGroup(BaseField(E))! 0 ;
for v in places do
lred, _ := LocalInformation(E,v);
fv := lred[3];
N := N + fv * Divisor(v);
end for;
return N;
end intrinsic;
intrinsic TamagawaNumber(E::CrvEll[FldFunG],v::PlcFunElt) -> RngIntElt
{ Tamagawa number of an elliptic curve over a function field
}
lred, _ := LocalInformation(E,v);
return lred[4];
end intrinsic;
intrinsic TamagawaNumbers(E::CrvEll[FldFunG]) -> SeqEnum
{ Tamagawa numbers of an elliptic curve over a function field
}
D := Discriminant(E);
places := SequenceToSet(Poles(D) cat Zeroes(D));
tam := [];
for v in places do
lred, _ := LocalInformation(E,v);
cp := lred[4];
if lred[5] ne KodairaSymbol("I0") then
tam := tam cat [cp];
end if;
end for;
return tam;
end intrinsic;
intrinsic BadPlaces(E::CrvEll[FldFunG]) -> .
{ the set of places of bad reduction of the neron model of E }
if not assigned E`BadPlaces then
E`BadPlaces := Support(Conductor(E));
end if;
return E`BadPlaces;
end intrinsic;
intrinsic Reduction(E::CrvEll[FldFunG],v::PlcFunElt) -> CrvEll
{ returns the reduction of E at the places v.}
lred, Emin := LocalInformation(E,v);
require lred[5] eq KodairaSymbol("I0") : "E must have good reduction at v";
P := Ideal(v);
kv,red := ResidueClassField(P);
Ov := Domain(red);
EOv := ChangeRing(Emin,Ov);
Ckv := BaseChange(EOv,red);
Etilde := EllipticCurve(Ckv);
return Etilde;
end intrinsic;
intrinsic EllNp(E::CrvEll[FldFunG],v::PlcFunElt) -> RngIntElt
{ The number of points in the reduction of E at v }
Et := Reduction(E,v);
kv := BaseField(Et);
return #kv + 1 - TraceOfFrobenius(Et);
end intrinsic;
// TwoTorsionSubgroup and
// pPowerTorsion seem to work already
intrinsic TorsionSubgroup(E::CrvEll[FldFunG]) -> GrpAb, Map
{ Computes the torsion subgroup of an elliptic curve over a function field
}
F := BaseField(E);
places := Places(F,1);
badplaces := BadPlaces(E);
places := [v : v in places | v notin badplaces and IsFinite(v)];
if #places lt 10 then
places cat:= Places(F,2);
places := [v : v in places | v notin badplaces and IsFinite(v)];
end if;
// places is now a list of places with good reduction.
nps := [ EllNp(E,v) : v in places ];
b := GCD(nps); // is an upper bound
if b eq 1 then
G := AbelianGroup([1]);
i := map < G -> E | G.1 -> E!0 >;
else
ordtors := [];
getors := [];
for p in PrimeDivisors(b) do
Gp, ip := pPowerTorsion(E,p: Bound:= p^Valuation(b,p) );
Tp := [ip(g) : g in Generators(Gp) ];
op := [Order(g) : g in Generators(Gp) ];
ordtors cat:= op ;
getors cat:= Tp;
end for;
G := AbelianGroup(ordtors);
i := map< G -> E | g :-> &+[ s[i] * Tp[i] : i in [1..#s]] where s := Eltseq(g) >;
end if;
// E`TorsionGroup := G;
// E`TorsionMap := i;
return G, i;
end intrinsic;