Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download

All published worksheets from http://sagenb.org

Views: 168757
Image: ubuntu2004

We choose an elliptic curve E:

E=EllipticCurve('37a'); E
Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field
E.galois_representation().is_surjective(2)
True
rho=E.galois_representation(); rho
Compatible family of Galois representations associated to the Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field

We choose a Heegner discriminant for E:

E.heegner_discriminants(100)
[-3, -4, -7, -11, -40, -47, -67, -71, -83, -84, -95]

With the choice of a Heegner discriminant, -3, we list possible Kolyvagin primes:

for ell in prime_range(1000): if GF(ell)(E.conductor())!=0 and E.heegner_point(-3,ell).satisfies_kolyvagin_hypothesis(3): ell
17 29 41 47 71 83 101 107 113 131 137 167 173 179 197 233 239 257 269 281 311 359 401 431 449 461 521 557 563 569 653 659 683 701 719 743 773 857 863 929 947 983

We define the field L=Q(E[2]):

L.<a>=NumberField(x^6 - 3*x^5 - 2*x^4 + 9*x^3 - 5*x + 1); L
Number Field in a with defining polynomial x^6 - 3*x^5 - 2*x^4 + 9*x^3 - 5*x + 1
L.is_galois()
True
EL=E.change_ring(L); EL
Elliptic Curve defined by y^2 + y = x^3 + (-1)*x over Number Field in a with defining polynomial x^6 - 3*x^5 - 2*x^4 + 9*x^3 - 5*x + 1
L.galois_group().is_isomorphic(SymmetricGroup(3))
True

Indeed, the 2-division polynomial of E factors completely over L:

EL.division_polynomial(2).roots(multiplicities=False)
[1/2*a^4 - a^3 - a^2 + 3/2*a, 1/2*a^5 - 3/2*a^4 - a^3 + 4*a^2 + a - 3/2, -1/2*a^5 + a^4 + 2*a^3 - 3*a^2 - 5/2*a + 3/2]

We use Robert Miller's awesome number field Selmer group function!

List=L.primes_above(2)
LSelmerGroup=L.selmer_group(List, 2); LSelmerGroup
[a^3 - 2*a^2 - 2*a + 3, -1, a^3 - 2*a^2 - a + 1, a^5 - 3*a^4 - a^3 + 7*a^2 - 2*a - 1, a, a^3 - 2*a^2 - 2*a + 2, a^4 - 3*a^3 - a^2 + 6*a - 1]

The key difficulty is implementing the L-Selmer group as a vector space. Robert Miller's function only gives a basis as elements of L--we need the structure of the actual group.

F2=FiniteField(2); F2
Finite Field of size 2
V=F2^(len(LSelmerGroup)); V
Vector space of dimension 7 over Finite Field of size 2

Two functions: this one goes from a vector to a Selmer class.

def V_to_Selmer(V, LSelmerGroup, v): a=1 for i in range(dim(V)): a=a*(LSelmerGroup[i]^v[i]) return a

This one goes the opposite way. It is by far the most time-consuming operation here.

def Selmer_to_V(V, LSelmerGroup, a): for v in V: if (a/V_to_Selmer(V, LSelmerGroup, v)).is_square()==True: return v

Here we learn the Galois action on 2-torsion points.

G=L.galois_group(); G
Galois group of Number Field in a with defining polynomial x^6 - 3*x^5 - 2*x^4 + 9*x^3 - 5*x + 1
P=EL.division_polynomial(2).roots(multiplicities=False)[0]
Q=EL.division_polynomial(2).roots(multiplicities=False)[2]
PQ=EL.division_polynomial(2).roots(multiplicities=False)[1]
tau=G.gens()[0]; sigma=G.gens()[1]^-1
tau(P)==Q; tau(Q)==P; tau(PQ)==PQ; sigma(P)==Q; sigma(Q)==PQ; sigma(PQ)==P
True True True True True True

W represents the tensor product vector space.

W=F2^(2*len(LSelmerGroup)); W
Vector space of dimension 14 over Finite Field of size 2

Functions for dealing with the tensor product:

def Glue(u, v, V, W): w=W.zero_vector() for i in range(dim(V)): w[i]=u[i] w[i+dim(V)]=v[i] return w
def Split(w, V, W): u=V.zero_vector(); v=V.zero_vector() for i in range(dim(V)): u[i]=w[i] v[i]=w[i+dim(V)] return [u, v]

Now we can write the Galois generators as matrices. They are big, but Sage handles them beautifully.

ListTau=list(W.basis()) for j in range(dim(V)): ListTau[j]=Glue(V.zero_vector(), Selmer_to_V(V, LSelmerGroup, tau(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V, W) ListTau[j+dim(V)]=Glue(Selmer_to_V(V, LSelmerGroup, tau(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V.zero_vector(), V, W) MatrixTau=Matrix(ListTau); print MatrixTau.str()
[0 0 0 0 0 0 0 1 1 1 0 0 1 0] [0 0 0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 0 0 0 0 1 0 0 1 0 0] [0 0 0 0 0 0 0 0 1 1 1 0 1 0] [0 0 0 0 0 0 0 0 1 1 0 0 0 0] [0 0 0 0 0 0 0 0 1 1 0 1 1 0] [0 0 0 0 0 0 0 0 0 1 0 1 0 1] [1 1 1 0 0 1 0 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 0 0 0 0 0 0 0] [0 1 0 0 1 0 0 0 0 0 0 0 0 0] [0 1 1 1 0 1 0 0 0 0 0 0 0 0] [0 1 1 0 0 0 0 0 0 0 0 0 0 0] [0 1 1 0 1 1 0 0 0 0 0 0 0 0] [0 0 1 0 1 0 1 0 0 0 0 0 0 0]
ListSigma=list(W.basis()) for j in range(dim(V)): ListSigma[j]=Glue(V.zero_vector(), Selmer_to_V(V, LSelmerGroup, sigma(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V, W) ListSigma[j+dim(V)]=Glue(Selmer_to_V(V, LSelmerGroup, sigma(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), Selmer_to_V(V, LSelmerGroup, sigma(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V, W) MatrixSigma=Matrix(ListSigma); print MatrixSigma.str()
[0 0 0 0 0 0 0 1 1 1 1 1 0 0] [0 0 0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 0 0 0 0 1 0 1 0 0 0] [0 0 0 0 0 0 0 0 1 1 1 0 0 0] [0 0 0 0 0 0 0 0 1 1 1 1 1 0] [0 0 0 0 0 0 0 0 1 1 0 1 0 0] [0 0 0 0 0 0 0 0 1 1 0 1 1 1] [1 1 1 1 1 0 0 1 1 1 1 1 0 0] [0 1 0 0 0 0 0 0 1 0 0 0 0 0] [0 1 0 1 0 0 0 0 1 0 1 0 0 0] [0 1 1 1 0 0 0 0 1 1 1 0 0 0] [0 1 1 1 1 1 0 0 1 1 1 1 1 0] [0 1 1 0 1 0 0 0 1 1 0 1 0 0] [0 1 1 0 1 1 1 0 1 1 0 1 1 1]

Our problem is reduced to finding the common Eigenspace of these two matrices.

Id=Matrix(W.basis()) Eigens=(MatrixSigma-Id).left_kernel().intersection((MatrixTau-Id).left_kernel()); Eigens
Vector space of degree 14 and dimension 2 over Finite Field of size 2 Basis matrix: [0 0 0 1 1 0 0 0 0 0 1 0 1 0] [0 0 0 0 0 1 0 0 1 1 0 1 1 0]
for w in Eigens: [V_to_Selmer(V, LSelmerGroup, Split(w, V, W)[0]), V_to_Selmer(V, LSelmerGroup, Split(w, V, W)[1])]
[1, 1] [a^4 - 2*a^3 - 2*a^2 + 4*a - 1, a^5 - 4*a^4 + a^3 + 9*a^2 - 5*a - 1] [a^3 - 2*a^2 - 2*a + 2, 2*a^4 - 5*a^3 - a^2 + 4*a - 1] [-a^5 + 3*a^4 - 5*a^2 + 4*a - 1, -a^2 + a]

We have bounded the Selmer group inside a group of size 4. Now finding it exactly is a matter of checking a modified local condition at primes above 2. However, we cheat our way out of this process using a different property of the standard Selmer group:

RR.<tt>=L[] M.<b>=L.extension(tt^2-(a^4 - 2*a^3 - 2*a^2 + 4*a - 1)) SS.<uu>=M[] N.<c>=M.extension(uu^2-(a^5 - 4*a^4 + a^3 + 9*a^2 - 5*a - 1)) EN=EL.change_ring(N) MWPoint=EN(0,0,1); MWPoint MWPoint.division_points(2)
[(((-1/2*a^5 + a^4 + 2*a^3 - 5/2*a^2 - 3*a)*b - 1/2*a^5 + a^4 + 2*a^3 - 5/2*a^2 - 3*a)*c + (-1/2*a^5 + 3/2*a^4 + a^3 - 4*a^2 - a + 3/2)*b : ((1/2*a^5 - a^4 - 2*a^3 + 2*a^2 + 7/2*a + 1)*b + 1/2*a)*c + (a^5 - 5/2*a^4 - 3*a^3 + 7*a^2 + 3*a - 5/2)*b + 1/2 : 1), (((1/2*a^5 - a^4 - 2*a^3 + 5/2*a^2 + 3*a)*b + 1/2*a^5 - a^4 - 2*a^3 + 5/2*a^2 + 3*a)*c + (-1/2*a^5 + 3/2*a^4 + a^3 - 4*a^2 - a + 3/2)*b : ((-1/2*a^5 + a^4 + 2*a^3 - 2*a^2 - 7/2*a - 1)*b - 1/2*a)*c + (a^5 - 5/2*a^4 - 3*a^3 + 7*a^2 + 3*a - 5/2)*b + 1/2 : 1), (((1/2*a^5 - a^4 - 2*a^3 + 5/2*a^2 + 3*a)*b - 1/2*a^5 + a^4 + 2*a^3 - 5/2*a^2 - 3*a)*c + (1/2*a^5 - 3/2*a^4 - a^3 + 4*a^2 + a - 3/2)*b : ((-1/2*a^5 + a^4 + 2*a^3 - 2*a^2 - 7/2*a - 1)*b + 1/2*a)*c + (-a^5 + 5/2*a^4 + 3*a^3 - 7*a^2 - 3*a + 5/2)*b + 1/2 : 1), (((-1/2*a^5 + a^4 + 2*a^3 - 5/2*a^2 - 3*a)*b + 1/2*a^5 - a^4 - 2*a^3 + 5/2*a^2 + 3*a)*c + (1/2*a^5 - 3/2*a^4 - a^3 + 4*a^2 + a - 3/2)*b : ((1/2*a^5 - a^4 - 2*a^3 + 2*a^2 + 7/2*a + 1)*b - 1/2*a)*c + (-a^5 + 5/2*a^4 + 3*a^3 - 7*a^2 - 3*a + 5/2)*b + 1/2 : 1)]

The first nonzero element in the list above is the true Selmer generator. The others fail (in calculations not shown here). Hence the 2-Selmer group has rank 1, and so does E!

Now let's try a modified Selmer group, using the Kolyvagin prime 17.

List=list(set(L.primes_above(2)).union(set(L.primes_above(17)))); List
[Fractional ideal (a^5 - 2*a^4 - 4*a^3 + 4*a^2 + 6*a), Fractional ideal (a^3 - 2*a^2 - 2*a + 3), Fractional ideal (2*a^3 - 2*a^2 - 5*a), Fractional ideal (-a^5 + 3*a^4 + 2*a^3 - 10*a^2 + a + 5)]
LSelmerGroup=L.selmer_group(List, 2); LSelmerGroup
[a^5 - 2*a^4 - 4*a^3 + 4*a^2 + 6*a, a^3 - 2*a^2 - 2*a + 3, 2*a^3 - 2*a^2 - 5*a, -a^5 + 3*a^4 + 2*a^3 - 10*a^2 + a + 5, -1, a^3 - 2*a^2 - a + 1, a^5 - 3*a^4 - a^3 + 7*a^2 - 2*a - 1, a, a^3 - 2*a^2 - 2*a + 2, a^4 - 3*a^3 - a^2 + 6*a - 1]
V=F2^(len(LSelmerGroup)); V
Vector space of dimension 10 over Finite Field of size 2
W=F2^(2*len(LSelmerGroup)); W
Vector space of dimension 20 over Finite Field of size 2
ListTau=list(W.basis()) for j in range(dim(V)): ListTau[j]=Glue(V.zero_vector(), Selmer_to_V(V, LSelmerGroup, tau(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V, W) ListTau[j+dim(V)]=Glue(Selmer_to_V(V, LSelmerGroup, tau(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V.zero_vector(), V, W) MatrixTau=Matrix(ListTau); print MatrixTau.str()
[0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0] [0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0] [0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1] [1 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0] [0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0] [0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0]
ListSigma=list(W.basis()) for j in range(dim(V)): ListSigma[j]=Glue(V.zero_vector(), Selmer_to_V(V, LSelmerGroup, sigma(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V, W) ListSigma[j+dim(V)]=Glue(Selmer_to_V(V, LSelmerGroup, sigma(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), Selmer_to_V(V, LSelmerGroup, sigma(V_to_Selmer(V, LSelmerGroup, V.basis()[j]))), V, W) MatrixSigma=Matrix(ListSigma); print MatrixSigma.str()
[0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0] [0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 1 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0] [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1] [0 0 1 0 1 1 1 1 0 0 0 0 1 0 1 1 1 1 0 0] [0 1 0 0 1 1 1 1 0 0 0 1 0 0 1 1 1 1 0 0] [0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0] [1 0 0 0 1 1 0 0 1 0 1 0 0 0 1 1 0 0 1 0] [0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0] [0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0] [0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0] [0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0] [0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 1 0 1 0 0] [0 0 0 0 1 1 0 1 1 1 0 0 0 0 1 1 0 1 1 1]
Id=Matrix(W.basis()) Eigens=(MatrixSigma-Id).left_kernel().intersection((MatrixTau-Id).left_kernel()); Eigens
Vector space of degree 20 and dimension 3 over Finite Field of size 2 Basis matrix: [1 0 1 0 0 0 0 0 0 0 1 0 0 1 1 1 1 1 0 0] [0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 1 0] [0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 1 0]
for w in Eigens: [V_to_Selmer(V, LSelmerGroup, Split(w, V, W)[0]), V_to_Selmer(V, LSelmerGroup, Split(w, V, W)[1])]
[1, 1] [-7*a^5 + 14*a^4 + 23*a^3 - 32*a^2 - 25*a + 5, -5*a^4 + 10*a^3 - a^2 - 4*a + 2] [a^4 - 2*a^3 - 2*a^2 + 4*a - 1, a^5 - 4*a^4 + a^3 + 9*a^2 - 5*a - 1] [-2*a^5 + 12*a^3 - 5*a^2 - 16*a + 4, -5*a^5 + 30*a^4 - 41*a^3 - 17*a^2 + 35*a - 8] [a^3 - 2*a^2 - 2*a + 2, 2*a^4 - 5*a^3 - a^2 + 4*a - 1] [5*a^5 - 10*a^4 - 14*a^3 + 18*a^2 + 13*a - 6, 5*a^5 - 19*a^4 + 2*a^3 + 43*a^2 - 16*a - 10] [-a^5 + 3*a^4 - 5*a^2 + 4*a - 1, -a^2 + a] [-a^5 + 6*a^3 + 6*a^2 - 8*a + 2, 2*a^4 - 4*a^3 - 3*a^2 + 5*a + 6]

To narrow down this group of size 8, we must check the transverse condition at primes above 17. The function below will do this:

def local_conditions(A, p, u, a): F=A.residue_field(p) vp=a.valuation(p) b=F(a/u^vp) unramified=(vp%2==0) transverse=(b.nth_root(2, all=True)!=[]) return [unramified, transverse]