Saturation of Mordell-Weil groups of elliptic curves over number fields¶
Points
The process of p_saturation()
does one step of
this, while full_p_saturation()
repeats until the points are
The method saturation()
of the class EllipticCurve_number_field
applies full
AUTHORS:
Robert Bradshaw
John Cremona
- class sage.schemes.elliptic_curves.saturation.EllipticCurveSaturator(E, verbose=False)[source]¶
Bases:
SageObject
Class for saturating points on an elliptic curve over a number field.
INPUT:
E
– an elliptic curve defined over a number field, orverbose
– boolean (default:False
); verbosity flag
Note
This function is not normally called directly by users, who may access the data via methods of the EllipticCurve classes.
- add_reductions(q)[source]¶
Add reduction data at primes above
q
if not already there.INPUT:
q
– a prime number not dividing the defining polynomial ofself.__field
OUTPUT:
Returns nothing, but updates self._reductions dictionary for key
q
to a dict whose keys are the roots of the defining polynomial modq
and values tuples (nq
,Eq
) whereEq
is an elliptic curve over andnq
its cardinality. Ifq
divides the conductor norm or order discriminant nothing is added.EXAMPLES:
Over
:sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator sage: E = EllipticCurve('11a1') sage: saturator = EllipticCurveSaturator(E) sage: saturator._reductions {} sage: saturator.add_reductions(19) sage: saturator._reductions {19: {0: (20, Elliptic Curve defined by y^2 + y = x^3 + 18*x^2 + 9*x + 18 over Finite Field of size 19)}}
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator >>> E = EllipticCurve('11a1') >>> saturator = EllipticCurveSaturator(E) >>> saturator._reductions {} >>> saturator.add_reductions(Integer(19)) >>> saturator._reductions {19: {0: (20, Elliptic Curve defined by y^2 + y = x^3 + 18*x^2 + 9*x + 18 over Finite Field of size 19)}}
from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator E = EllipticCurve('11a1') saturator = EllipticCurveSaturator(E) saturator._reductions saturator.add_reductions(19) saturator._reductions
Over a number field:
sage: x = polygen(QQ); K.<a> = NumberField(x^2 + 2) sage: E = EllipticCurve(K, [0,1,0,a,a]) sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator sage: saturator = EllipticCurveSaturator(E) sage: for q in primes(20): ....: saturator.add_reductions(q) sage: saturator._reductions {2: {}, 3: {}, 5: {}, 7: {}, 11: {3: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 3*x + 3 over Finite Field of size 11), 8: (8, Elliptic Curve defined by y^2 = x^3 + x^2 + 8*x + 8 over Finite Field of size 11)}, 13: {}, 17: {7: (20, Elliptic Curve defined by y^2 = x^3 + x^2 + 7*x + 7 over Finite Field of size 17), 10: (18, Elliptic Curve defined by y^2 = x^3 + x^2 + 10*x + 10 over Finite Field of size 17)}, 19: {6: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 6*x + 6 over Finite Field of size 19), 13: (12, Elliptic Curve defined by y^2 = x^3 + x^2 + 13*x + 13 over Finite Field of size 19)}}
>>> from sage.all import * >>> x = polygen(QQ); K = NumberField(x**Integer(2) + Integer(2), names=('a',)); (a,) = K._first_ngens(1) >>> E = EllipticCurve(K, [Integer(0),Integer(1),Integer(0),a,a]) >>> from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator >>> saturator = EllipticCurveSaturator(E) >>> for q in primes(Integer(20)): ... saturator.add_reductions(q) >>> saturator._reductions {2: {}, 3: {}, 5: {}, 7: {}, 11: {3: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 3*x + 3 over Finite Field of size 11), 8: (8, Elliptic Curve defined by y^2 = x^3 + x^2 + 8*x + 8 over Finite Field of size 11)}, 13: {}, 17: {7: (20, Elliptic Curve defined by y^2 = x^3 + x^2 + 7*x + 7 over Finite Field of size 17), 10: (18, Elliptic Curve defined by y^2 = x^3 + x^2 + 10*x + 10 over Finite Field of size 17)}, 19: {6: (16, Elliptic Curve defined by y^2 = x^3 + x^2 + 6*x + 6 over Finite Field of size 19), 13: (12, Elliptic Curve defined by y^2 = x^3 + x^2 + 13*x + 13 over Finite Field of size 19)}}
x = polygen(QQ); K.<a> = NumberField(x^2 + 2) E = EllipticCurve(K, [0,1,0,a,a]) from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator saturator = EllipticCurveSaturator(E) for q in primes(20): saturator.add_reductions(q) saturator._reductions
- full_p_saturation(Plist, p)[source]¶
Full
-saturation ofPlist
.INPUT:
Plist
– list of independent points on one elliptic curvep
– integer; a prime number
OUTPUT:
(
newPlist
,exponent
) wherenewPlist
has the same length asPlist
and spans the -saturation of the span ofPlist
, which contains that span with indexp**exponent
.EXAMPLES:
sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator sage: E = EllipticCurve('389a') sage: K.<i> = QuadraticField(-1) sage: EK = E.change_ring(K) sage: P = EK(1 + i, -1 - 2*i) sage: saturator = EllipticCurveSaturator(EK, verbose=True) sage: saturator.full_p_saturation([8*P], 2) --starting full 2-saturation Points were not 2-saturated, exponent was 3 ([(i + 1 : -2*i - 1 : 1)], 3) sage: Q = EK(0, 0) sage: R = EK(-1, 1) sage: saturator = EllipticCurveSaturator(EK, verbose=False) sage: saturator.full_p_saturation([P, Q, R], 3) ([(i + 1 : -2*i - 1 : 1), (0 : 0 : 1), (-1 : 1 : 1)], 0)
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator >>> E = EllipticCurve('389a') >>> K = QuadraticField(-Integer(1), names=('i',)); (i,) = K._first_ngens(1) >>> EK = E.change_ring(K) >>> P = EK(Integer(1) + i, -Integer(1) - Integer(2)*i) >>> saturator = EllipticCurveSaturator(EK, verbose=True) >>> saturator.full_p_saturation([Integer(8)*P], Integer(2)) --starting full 2-saturation Points were not 2-saturated, exponent was 3 ([(i + 1 : -2*i - 1 : 1)], 3) >>> Q = EK(Integer(0), Integer(0)) >>> R = EK(-Integer(1), Integer(1)) >>> saturator = EllipticCurveSaturator(EK, verbose=False) >>> saturator.full_p_saturation([P, Q, R], Integer(3)) ([(i + 1 : -2*i - 1 : 1), (0 : 0 : 1), (-1 : 1 : 1)], 0)
from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator E = EllipticCurve('389a') K.<i> = QuadraticField(-1) EK = E.change_ring(K) P = EK(1 + i, -1 - 2*i) saturator = EllipticCurveSaturator(EK, verbose=True) saturator.full_p_saturation([8*P], 2) Q = EK(0, 0) R = EK(-1, 1) saturator = EllipticCurveSaturator(EK, verbose=False) saturator.full_p_saturation([P, Q, R], 3)
An example where the points are not 7-saturated and we gain index exponent 1. Running this example with verbose=True would show that it uses the code for when the reduction has
-rank 2 (which occurs for the reduction modulo ), which uses the Weil pairing:sage: saturator.full_p_saturation([P, Q + 3*R, Q - 4*R], 7) ([(i + 1 : -2*i - 1 : 1), (2869/676 : 154413/17576 : 1), (-7095/502681 : -366258864/356400829 : 1)], 1)
>>> from sage.all import * >>> saturator.full_p_saturation([P, Q + Integer(3)*R, Q - Integer(4)*R], Integer(7)) ([(i + 1 : -2*i - 1 : 1), (2869/676 : 154413/17576 : 1), (-7095/502681 : -366258864/356400829 : 1)], 1)
saturator.full_p_saturation([P, Q + 3*R, Q - 4*R], 7)
- p_saturation(Plist, p, sieve=True)[source]¶
Checks whether the list of points is
-saturated.INPUT:
Plist
– list of independent points on one elliptic curvep
– integer; a prime numbersieve
– boolean; ifTrue
, use a sieve (when there are at least 2 points), otherwise test all combinations
Note
The sieve is much more efficient when the points are saturated and the number of points or the prime are large.
OUTPUT:
Either
False
if the points are -saturated, or (i
,newP
) if they are not -saturated, in which case after replacing the i’th point withnewP
, the subgroup generated contains that generated byPlist
with index .EXAMPLES:
sage: from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator sage: E = EllipticCurve('389a') sage: K.<i> = QuadraticField(-1) sage: EK = E.change_ring(K) sage: P = EK(1 + i, -1 - 2*i) sage: saturator = EllipticCurveSaturator(EK) sage: saturator.p_saturation([P], 2) False sage: saturator.p_saturation([2*P], 2) (0, (i + 1 : -2*i - 1 : 1)) sage: Q = EK(0, 0) sage: R = EK(-1, 1) sage: saturator.p_saturation([P, Q, R], 3) False
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator >>> E = EllipticCurve('389a') >>> K = QuadraticField(-Integer(1), names=('i',)); (i,) = K._first_ngens(1) >>> EK = E.change_ring(K) >>> P = EK(Integer(1) + i, -Integer(1) - Integer(2)*i) >>> saturator = EllipticCurveSaturator(EK) >>> saturator.p_saturation([P], Integer(2)) False >>> saturator.p_saturation([Integer(2)*P], Integer(2)) (0, (i + 1 : -2*i - 1 : 1)) >>> Q = EK(Integer(0), Integer(0)) >>> R = EK(-Integer(1), Integer(1)) >>> saturator.p_saturation([P, Q, R], Integer(3)) False
from sage.schemes.elliptic_curves.saturation import EllipticCurveSaturator E = EllipticCurve('389a') K.<i> = QuadraticField(-1) EK = E.change_ring(K) P = EK(1 + i, -1 - 2*i) saturator = EllipticCurveSaturator(EK) saturator.p_saturation([P], 2) saturator.p_saturation([2*P], 2) Q = EK(0, 0) R = EK(-1, 1) saturator.p_saturation([P, Q, R], 3)
Here we see an example where 19-saturation is proved, with the verbose flag set to
True
so that we can see what is going on:sage: saturator = EllipticCurveSaturator(EK, verbose=True) sage: saturator.p_saturation([P, Q, R], 19) Using sieve method to saturate... E has 19-torsion over Finite Field of size 197, projecting points --> [(15 : 168 : 1), (0 : 0 : 1), (196 : 1 : 1)] --rank is now 1 E has 19-torsion over Finite Field of size 197, projecting points --> [(184 : 27 : 1), (0 : 0 : 1), (196 : 1 : 1)] --rank is now 2 E has 19-torsion over Finite Field of size 293, projecting points --> [(139 : 16 : 1), (0 : 0 : 1), (292 : 1 : 1)] --rank is now 3 Reached full rank: points were 19-saturated False
>>> from sage.all import * >>> saturator = EllipticCurveSaturator(EK, verbose=True) >>> saturator.p_saturation([P, Q, R], Integer(19)) Using sieve method to saturate... E has 19-torsion over Finite Field of size 197, projecting points --> [(15 : 168 : 1), (0 : 0 : 1), (196 : 1 : 1)] --rank is now 1 E has 19-torsion over Finite Field of size 197, projecting points --> [(184 : 27 : 1), (0 : 0 : 1), (196 : 1 : 1)] --rank is now 2 E has 19-torsion over Finite Field of size 293, projecting points --> [(139 : 16 : 1), (0 : 0 : 1), (292 : 1 : 1)] --rank is now 3 Reached full rank: points were 19-saturated False
saturator = EllipticCurveSaturator(EK, verbose=True) saturator.p_saturation([P, Q, R], 19)
An example where the points are not 11-saturated:
sage: saturator = EllipticCurveSaturator(EK, verbose=False) sage: res = saturator.p_saturation([P + 5*Q, P - 6*Q, R], 11); res (0, (-5783311/14600041*i + 1396143/14600041 : 37679338314/55786756661*i + 3813624227/55786756661 : 1))
>>> from sage.all import * >>> saturator = EllipticCurveSaturator(EK, verbose=False) >>> res = saturator.p_saturation([P + Integer(5)*Q, P - Integer(6)*Q, R], Integer(11)); res (0, (-5783311/14600041*i + 1396143/14600041 : 37679338314/55786756661*i + 3813624227/55786756661 : 1))
saturator = EllipticCurveSaturator(EK, verbose=False) res = saturator.p_saturation([P + 5*Q, P - 6*Q, R], 11); res
That means that the 0’th point may be replaced by the displayed point to achieve an index gain of 11:
sage: saturator.p_saturation([res[1], P - 6*Q, R], 11) False
>>> from sage.all import * >>> saturator.p_saturation([res[Integer(1)], P - Integer(6)*Q, R], Integer(11)) False
saturator.p_saturation([res[1], P - 6*Q, R], 11)
- sage.schemes.elliptic_curves.saturation.p_projections(Eq, Plist, p, debug=False)[source]¶
INPUT:
Eq
– an elliptic curve over a finite fieldPlist
– list of points onp
– a prime number
OUTPUT:
A list of
vectors in , the images of the points in , where is the number of vectors is the -rank of .ALGORITHM:
First project onto the
-primary part of . If that has -rank 1 (i.e. is cyclic), use discrete logs there to define a map to , otherwise use the Weil pairing to define two independent maps to .EXAMPLES:
This curve has three independent rational points:
sage: E = EllipticCurve([0,0,1,-7,6])
>>> from sage.all import * >>> E = EllipticCurve([Integer(0),Integer(0),Integer(1),-Integer(7),Integer(6)])
E = EllipticCurve([0,0,1,-7,6])
We reduce modulo
where its order is ; the -primary part is non-cyclic while the -primary part is cyclic of order :sage: F = GF(409) sage: EF = E.change_ring(F) sage: G = EF.abelian_group() sage: G Additive abelian group isomorphic to Z/147 + Z/3 embedded in Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 + 402*x + 6 over Finite Field of size 409 sage: G.order().factor() 3^2 * 7^2
>>> from sage.all import * >>> F = GF(Integer(409)) >>> EF = E.change_ring(F) >>> G = EF.abelian_group() >>> G Additive abelian group isomorphic to Z/147 + Z/3 embedded in Abelian group of points on Elliptic Curve defined by y^2 + y = x^3 + 402*x + 6 over Finite Field of size 409 >>> G.order().factor() 3^2 * 7^2
F = GF(409) EF = E.change_ring(F) G = EF.abelian_group() G G.order().factor()
We construct three points and project them to the
-primary parts for , yielding 0,2,0,1 vectors of length 3 modulo respectively. The exact vectors output depend on the computed generators of :sage: Plist = [EF([-2,3]), EF([0,2]), EF([1,0])] sage: from sage.schemes.elliptic_curves.saturation import p_projections sage: [(p, p_projections(EF, Plist, p)) for p in primes(11)] # random [(2, []), (3, [(0, 2, 2), (2, 2, 1)]), (5, []), (7, [(5, 1, 1)])] sage: [(p, len(p_projections(EF, Plist, p))) for p in primes(11)] [(2, 0), (3, 2), (5, 0), (7, 1)]
>>> from sage.all import * >>> Plist = [EF([-Integer(2),Integer(3)]), EF([Integer(0),Integer(2)]), EF([Integer(1),Integer(0)])] >>> from sage.schemes.elliptic_curves.saturation import p_projections >>> [(p, p_projections(EF, Plist, p)) for p in primes(Integer(11))] # random [(2, []), (3, [(0, 2, 2), (2, 2, 1)]), (5, []), (7, [(5, 1, 1)])] >>> [(p, len(p_projections(EF, Plist, p))) for p in primes(Integer(11))] [(2, 0), (3, 2), (5, 0), (7, 1)]
Plist = [EF([-2,3]), EF([0,2]), EF([1,0])] from sage.schemes.elliptic_curves.saturation import p_projections [(p, p_projections(EF, Plist, p)) for p in primes(11)] # random [(p, len(p_projections(EF, Plist, p))) for p in primes(11)]
- sage.schemes.elliptic_curves.saturation.reduce_mod_q(x, amodq)[source]¶
The reduction of
x
modulo the prime ideal defined byamodq
.INPUT:
x
– an element of a number fieldamodq
– an element of which is a root mod of the defining polynomial of . This defines a degree 1 prime ideal of , where =amodq
.
OUTPUT: the image of
x
in the residue field of at the primeEXAMPLES:
sage: from sage.schemes.elliptic_curves.saturation import reduce_mod_q sage: x = polygen(QQ) sage: pol = x^3 - x^2 - 3*x + 1 sage: K.<a> = NumberField(pol) sage: [(q, [(amodq, reduce_mod_q(1 - a + a^4, amodq)) ....: for amodq in sorted(pol.roots(GF(q), multiplicities=False))]) ....: for q in primes(50, 70)] [(53, []), (59, [(36, 28)]), (61, [(40, 35)]), (67, [(10, 8), (62, 28), (63, 60)])]
>>> from sage.all import * >>> from sage.schemes.elliptic_curves.saturation import reduce_mod_q >>> x = polygen(QQ) >>> pol = x**Integer(3) - x**Integer(2) - Integer(3)*x + Integer(1) >>> K = NumberField(pol, names=('a',)); (a,) = K._first_ngens(1) >>> [(q, [(amodq, reduce_mod_q(Integer(1) - a + a**Integer(4), amodq)) ... for amodq in sorted(pol.roots(GF(q), multiplicities=False))]) ... for q in primes(Integer(50), Integer(70))] [(53, []), (59, [(36, 28)]), (61, [(40, 35)]), (67, [(10, 8), (62, 28), (63, 60)])]
from sage.schemes.elliptic_curves.saturation import reduce_mod_q x = polygen(QQ) pol = x^3 - x^2 - 3*x + 1 K.<a> = NumberField(pol) [(q, [(amodq, reduce_mod_q(1 - a + a^4, amodq)) for amodq in sorted(pol.roots(GF(q), multiplicities=False))]) for q in primes(50, 70)]