Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
#################################################################################
2
#
3
# (c) Copyright 2010 William Stein
4
#
5
# This file is part of PSAGE
6
#
7
# PSAGE is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# PSAGE is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#
20
#################################################################################
21
22
23
include 'stdsage.pxi'
24
include 'interrupt.pxi'
25
26
from sage.rings.all import ZZ, Zp, Qp, Integers, infinity, binomial, Mod, O
27
28
from sage.rings.integer cimport Integer
29
from modular_symbol_map cimport ModularSymbolMap
30
31
# Global temps so we don't have to call mpz_init repeatedly in mulmod.
32
# Also, this way we ensure that we don't leak 3 gmp ints.
33
cdef Integer aa=Integer(0), bb=Integer(0), cc=Integer(0)
34
# Necessary gmp imports for the mulmod function
35
from sage.libs.gmp.mpz cimport mpz_fdiv_ui, mpz_set_si, mpz_mul
36
cdef long mulmod(long a, long b, long n):
37
"""
38
Return (a*b)%n, which is >= 0.
39
40
The point of this function is that it is safe even if a*b would
41
overflow a long, since it uses GMP.
42
"""
43
global aa, bb
44
mpz_set_si(aa.value, a)
45
mpz_set_si(bb.value, b)
46
mpz_mul(cc.value, aa.value, bb.value)
47
return mpz_fdiv_ui(cc.value, n)
48
49
cdef class pAdicLseries:
50
cdef bint parallel
51
cdef public object E
52
cdef public long p, prec
53
cdef public long normalization, normalization_mulmod
54
cdef public long _alpha
55
cdef long alpha_inv[64], alpha_inv_mulmod[64]
56
cdef long p_pow[64]
57
cdef long *teich
58
cdef long *teich_mulmod
59
cdef public ModularSymbolMap modsym
60
61
def __cinit__(self):
62
self.teich = NULL
63
self.teich_mulmod = NULL
64
65
def __dealloc__(self):
66
if self.teich:
67
sage_free(self.teich)
68
if self.teich_mulmod:
69
sage_free(self.teich_mulmod)
70
71
72
def __init__(self, E, p, algorithm='eclib', parallel=False):
73
"""
74
INPUT:
75
- E -- an elliptic curve over QQ
76
- p -- a prime of good ordinary reduction with E[p] surjective
77
- algorithm -- str (default: 'eclib')
78
- 'eclib' -- use elliptic curve modular symbol computed using eclib
79
- 'sage' -- use elliptic curve modular symbol computed using sage
80
- 'modsym' -- use sage modular symbols directly (!! not correctly normalized !!)
81
- parallel -- bool (default: False); if True default to using parallel techniques.
82
83
EXAMPLES::
84
85
sage: from psage.modform.rational.padic_elliptic_lseries_fast import pAdicLseries
86
sage: E = EllipticCurve('389a'); L = pAdicLseries(E, 7)
87
sage: L.series()
88
O(7) + O(7)*T + (5 + O(7))*T^2 + (3 + O(7))*T^3 + (6 + O(7))*T^4 + O(T^5)
89
sage: L.series(n=3)
90
O(7^2) + O(7^2)*T + (5 + 4*7 + O(7^2))*T^2 + (3 + 6*7 + O(7^2))*T^3 + (6 + 3*7 + O(7^2))*T^4 + O(T^5)
91
sage: L.series(n=4)
92
O(7^3) + O(7^3)*T + (5 + 4*7 + 5*7^2 + O(7^3))*T^2 + (3 + 6*7 + 4*7^2 + O(7^3))*T^3 + (6 + 3*7 + 3*7^2 + O(7^3))*T^4 + O(T^5)
93
94
We can also get the series just mod 7::
95
96
sage: L.series_modp()
97
5*T^2 + 3*T^3 + 6*T^4 + O(T^5)
98
sage: L.series_modp().list()
99
[0, 0, 5, 3, 6]
100
101
We use Sage modular symbols instead of eclib's::
102
103
sage: L = pAdicLseries(E, 7, algorithm='sage')
104
sage: L.series(n=3)
105
O(7^2) + O(7^2)*T + (5 + 4*7 + O(7^2))*T^2 + (3 + 6*7 + O(7^2))*T^3 + (6 + 3*7 + O(7^2))*T^4 + O(T^5)
106
107
When we use algorithm='modsym', we see that the normalization
108
is not correct (as is documented above -- no attempt is made
109
to normalize!)::
110
111
sage: L = pAdicLseries(E, 7, algorithm='modsym')
112
sage: L.series(n=3)
113
O(7^2) + O(7^2)*T + (6 + 5*7 + O(7^2))*T^2 + (5 + 6*7 + O(7^2))*T^3 + (3 + 5*7 + O(7^2))*T^4 + O(T^5)
114
sage: 2*L.series(n=3)
115
O(7^2) + O(7^2)*T + (5 + 4*7 + O(7^2))*T^2 + (3 + 6*7 + O(7^2))*T^3 + (6 + 3*7 + O(7^2))*T^4 + O(T^5)
116
117
These agree with the L-series computed directly using separate code in Sage::
118
119
sage: L = E.padic_lseries(7)
120
sage: L.series(3)
121
O(7^5) + O(7^2)*T + (5 + 4*7 + O(7^2))*T^2 + (3 + 6*7 + O(7^2))*T^3 + (6 + 3*7 + O(7^2))*T^4 + O(T^5)
122
123
Comparing the parallel and serial version::
124
125
sage: from psage.modform.rational.padic_elliptic_lseries_fast import pAdicLseries
126
sage: L = pAdicLseries(EllipticCurve('389a'),997)
127
sage: L2 = pAdicLseries(EllipticCurve('389a'),997,parallel=True)
128
sage: L.series_modp() == L2.series_modp()
129
True
130
131
If you time the left and right above separately, and have a
132
multicore machine, you should see that the right is much
133
faster than the left.
134
"""
135
self.parallel = parallel
136
self.E = E
137
self.p = p
138
139
# prec = biggest n such that p^n <= 2^63, so n = floor(log_p(2^63))
140
self.prec = ZZ(2**63).exact_log(p)
141
142
assert Integer(p).is_pseudoprime(), "p (=%s) must be prime"%p
143
assert E.is_ordinary(p), "p (=%s) must be ordinary for E"%p
144
assert E.is_good(p), "p (=%s) must be good for E"%p
145
146
if algorithm == 'eclib':
147
f = E.modular_symbol(sign=1, use_eclib=True)
148
self.modsym = ModularSymbolMap(f)
149
elif algorithm == 'sage':
150
f = E.modular_symbol(sign=1, use_eclib=False)
151
self.modsym = ModularSymbolMap(f)
152
elif algorithm == "modsym":
153
A = E.modular_symbol_space(sign=1)
154
self.modsym = ModularSymbolMap(A)
155
else:
156
raise ValueError, "unknown algorithm '%s'"%algorithm
157
158
# the denom must be a unit, given our hypotheses (and assumptions in code!)
159
assert ZZ(self.modsym.denom)%self.p != 0, "internal modsym denominator must be a p(=%s)-unit, but it is %s"%(self.p, self.modsym.denom)
160
161
# Compute alpha:
162
f = ZZ['x']([p, -E.ap(p), 1])
163
G = f.factor_padic(p, self.prec)
164
Zp = None
165
for pr, e in G:
166
alpha = -pr[0]
167
if alpha.valuation() < 1:
168
Zp = alpha.parent()
169
self._alpha = alpha.lift()
170
break
171
assert Zp is not None, "bug -- must have unit root (p=%s)"%p
172
173
cdef int n
174
K = Qp(self.p, self.prec//2)
175
# Compute array of powers of inverse of alpha, modulo p^prec
176
# for each power up to prec. This is only used for the
177
# non-mulmod version.
178
u = 1
179
self.alpha_inv[0] = u
180
for n in range(self.prec):
181
u *= alpha
182
self.alpha_inv[n+1] = K(1/u).lift() # coerce to K to lower precision
183
184
# Now do the same, but modulo p^(prec). This is used for the
185
# mulmod larger precision version of computations.
186
K_mulmod = Qp(self.p, self.prec)
187
u = 1
188
self.alpha_inv_mulmod[0] = u
189
for n in range(self.prec):
190
u *= alpha
191
self.alpha_inv_mulmod[n+1] = K_mulmod(1/u).lift()
192
193
# Make a table of powers of p up to prec
194
ppow = 1
195
self.p_pow[0] = ppow
196
for n in range(self.prec):
197
ppow *= self.p
198
self.p_pow[n+1] = ppow
199
200
# Make a table of Teichmuller lifts to precision p^(prec//2)
201
self.teich = <long*> sage_malloc(sizeof(long)*self.p)
202
self.teich[0] = 0
203
for n in range(1,p):
204
self.teich[n] = K.teichmuller(n).lift()
205
206
# Make a table of Teichmuller lifts to precision p^prec
207
self.teich_mulmod = <long*> sage_malloc(sizeof(long)*self.p)
208
self.teich_mulmod[0] = 0
209
for n in range(1,p):
210
self.teich_mulmod[n] = K_mulmod.teichmuller(n).lift()
211
212
# Compute normalization
213
self.normalization = ZZ(self.E.real_components()).inverse_mod(self.p_pow[self.prec//2])
214
self.normalization_mulmod = ZZ(self.E.real_components()).inverse_mod(self.p_pow[self.prec])
215
216
def __repr__(self):
217
return "%s-adic L-series of %s"%(self.p, self.E)
218
219
def alpha(self):
220
return self._alpha
221
222
cpdef long modular_symbol(self, long a, long b):
223
cdef long v[1]
224
self.modsym.evaluate(v, a, b)
225
return v[0]
226
227
cpdef long measure(self, long a, int n) except 9223372036854775807: # 64-bit maxint
228
"""
229
Compute mu(a/p^n). Note that the second input is the exponent of p.
230
231
INPUT:
232
- a -- long
233
- n -- int (very small)
234
"""
235
if n+1 > self.prec: # for safety
236
raise ValueError, "n too large to compute measure"
237
238
# TODO/WARNING: The case when p divides level is different -- but we check in __init__ that p is good.
239
return (self.alpha_inv[n] * self.modular_symbol(a, self.p_pow[n])
240
- self.alpha_inv[n+1] * self.modular_symbol(a, self.p_pow[n-1]))
241
242
cpdef long measure_mulmod(self, long a, int n, long pp) except 9223372036854775807: # 64-bit maxint
243
"""
244
Compute mu(a/p^n). Note that the second input is the exponent of p.
245
246
Uses GMP to do the multiplies modulo pp, to avoid overflow. Slower, but safe.
247
248
INPUT:
249
- a -- long
250
- n -- int (very small)
251
- pp -- long (modulus, a power of p).
252
"""
253
if n+1 > self.prec: # for safety
254
raise ValueError, "n too large to compute measure"
255
256
# TODO/WARNING: The case when p divides level is different -- but we check in __init__ that p is good.
257
258
cdef long ans = (mulmod(self.alpha_inv_mulmod[n], self.modular_symbol(a, self.p_pow[n]), pp)
259
- mulmod(self.alpha_inv_mulmod[n+1], self.modular_symbol(a, self.p_pow[n-1]), pp))
260
261
# mulmod returns a number between 0 and pp-1, inclusive, and so does this function. Since
262
# we compute ans as a difference of two such numbers, it is already in the interval [0..pp-1],
263
# or it is in the interval [-(pp-1)..-1], in which case we add pp.
264
if ans < 0:
265
ans += pp
266
return ans
267
268
def _series(self, int n, prec, ser_prec=5, bint verb=0, bint force_mulmod=False,
269
long start=-1, long stop=-1):
270
"""
271
EXAMPLES::
272
273
sage: import psage.modform.rational.padic_elliptic_lseries_fast as p; L = p.pAdicLseries(EllipticCurve('389a'),5)
274
sage: f = L._series(2, 3, ser_prec=6); f.change_ring(Integers(5^3))
275
73*T^4 + 42*T^3 + 89*T^2 + 120*T
276
sage: f = L._series(3, 4, ser_prec=6); f.change_ring(Integers(5^3))
277
36*T^5 + 53*T^4 + 47*T^3 + 99*T^2
278
sage: f = L._series(4, 5, ser_prec=6); f.change_ring(Integers(5^3))
279
61*T^5 + 53*T^4 + 22*T^3 + 49*T^2
280
sage: f = L._series(5, 6, ser_prec=6); f.change_ring(Integers(5^3))
281
111*T^5 + 53*T^4 + 22*T^3 + 49*T^2
282
"""
283
if verb:
284
print "_series %s computing mod p^%s"%(n, prec)
285
286
cdef long a, b, j, s, gamma_pow, gamma, pp
287
288
assert prec >= n, "prec (=%s) must be as large as approximation n (=%s)"%(prec, n)
289
290
pp = self.p_pow[prec]
291
292
gamma = 1 + self.p
293
gamma_pow = 1
294
295
R = Integers(pp)['T']
296
T = R.gen()
297
one_plus_T_factor = R(1)
298
L = R(0)
299
one_plus_T = 1+T
300
301
if start == -1:
302
start = 0
303
stop = self.p_pow[n-1]
304
305
if start != 0:
306
# initialize gamma_pow and one_plus_T_factor to be
307
# gamma_pow = gamma^start
308
# one_plus_T_factor = one_plus_T^start
309
gamma_pow = Mod(gamma, pp)**start
310
one_plus_T_factor = ((one_plus_T + O(T**ser_prec))**start).truncate(ser_prec)
311
312
if not force_mulmod and prec <= self.prec // 2:
313
# no concerns about overflow when multiplying together two longs, then reducing modulo pp
314
for j in range(start, stop):
315
sig_on()
316
s = 0
317
for a in range(1, self.p):
318
b = self.teich[a] * gamma_pow
319
s += self.measure(b, n)
320
sig_off()
321
L += (s * one_plus_T_factor).truncate(ser_prec)
322
one_plus_T_factor = (one_plus_T*one_plus_T_factor).truncate(ser_prec)
323
gamma_pow = (gamma_pow * gamma)%pp
324
#if verb: print j, s, one_plus_T_factor, gamma_pow
325
return L * self.normalization
326
else:
327
if verb: print "Using mulmod"
328
# Since prec > self.prec//2, where self.prec =
329
# ZZ(2**63).exact_log(p) = floor(log_p(2^63)),
330
# all multiplies in the loop above of longs must be done with
331
# long long, then reduced modulo pp. This is slower, but
332
# is necessary to ensure no overflow.
333
334
assert prec <= self.prec, "requested precision (%s) too large (max: %s)"%(prec, self.prec)
335
for j in range(start, stop):
336
sig_on()
337
s = 0
338
for a in range(1, self.p):
339
b = mulmod(self.teich_mulmod[a], gamma_pow, pp)
340
s += self.measure_mulmod(b, n, pp)
341
if s >= pp: s -= pp # normalize
342
sig_off()
343
L += (s * one_plus_T_factor).truncate(ser_prec)
344
one_plus_T_factor = (one_plus_T*one_plus_T_factor).truncate(ser_prec)
345
gamma_pow = mulmod(gamma_pow, gamma, pp)
346
return L * self.normalization_mulmod
347
348
def _series_parallel(self, int n, prec, ser_prec=5, bint verb=0, bint force_mulmod=False,
349
ncpus=None):
350
return series_parallel(self, n, prec, ser_prec, verb, force_mulmod, ncpus)
351
352
def _prec_bounds(self, n, ser_prec):
353
pn = Integer(self.p_pow[n-1])
354
enj = infinity
355
res = [enj]
356
for j in range(1, ser_prec):
357
bino = binomial(pn,j).valuation(self.p)
358
if bino < enj:
359
enj = bino
360
res.append(enj)
361
return res
362
363
def series(self, int n=2, prec=None, ser_prec=5, int check=True, bint verb=False,
364
parallel=None):
365
"""
366
EXAMPLES::
367
368
sage: from psage.modform.rational.padic_elliptic_lseries_fast import pAdicLseries
369
sage: E = EllipticCurve('389a'); L = pAdicLseries(E,5)
370
sage: L.series()
371
O(5) + O(5)*T + (4 + O(5))*T^2 + (2 + O(5))*T^3 + (3 + O(5))*T^4 + O(T^5)
372
sage: L.series(1)
373
O(T^0)
374
sage: L.series(3)
375
O(5^2) + O(5^2)*T + (4 + 4*5 + O(5^2))*T^2 + (2 + 4*5 + O(5^2))*T^3 + (3 + O(5^2))*T^4 + O(T^5)
376
sage: L.series(2, 8)
377
O(5^6) + O(5)*T + (4 + O(5))*T^2 + (2 + O(5))*T^3 + (3 + O(5))*T^4 + O(T^5)
378
sage: L.series(3, 8)
379
O(5^6) + O(5^2)*T + (4 + 4*5 + O(5^2))*T^2 + (2 + 4*5 + O(5^2))*T^3 + (3 + O(5^2))*T^4 + O(T^5)
380
sage: L.series(3, ser_prec=8)
381
O(5^2) + O(5^2)*T + (4 + 4*5 + O(5^2))*T^2 + (2 + 4*5 + O(5^2))*T^3 + (3 + O(5^2))*T^4 + (1 + O(5))*T^5 + O(5)*T^6 + (4 + O(5))*T^7 + O(T^8)
382
"""
383
if prec is None: prec = n+1
384
p = self.p
385
if check:
386
assert self.E.galois_representation().is_surjective(p), "p (=%s) must be surjective for E"%p
387
if parallel is None:
388
parallel = self.parallel
389
390
if parallel:
391
f = self._series_parallel(n, prec, ser_prec, verb=verb)
392
else:
393
f = self._series(n, prec, ser_prec, verb=verb)
394
aj = f.list()
395
R = Zp(p, prec)
396
if len(aj) > 0:
397
bounds = self._prec_bounds(n, ser_prec)
398
aj = [R(aj[0], prec-2)] + [R(aj[j], bounds[j]) for j in range(1,len(aj))]
399
# make unknown coefficients show as 0 precision.
400
aj.extend([R(0,0) for _ in range(ser_prec-len(aj))])
401
ser_prec = min([ser_prec] + [i for i in range(len(aj)) if aj[i].precision_absolute() == 0])
402
L = R[['T']](aj, ser_prec)
403
return L / self.modsym.denom
404
405
def series_modp(self, int n=2, ser_prec=5, int check=True):
406
"""
407
EXAMPLES::
408
409
sage: from psage.modform.rational.padic_elliptic_lseries_fast import pAdicLseries
410
sage: E = EllipticCurve('389a')
411
sage: L = pAdicLseries(E,5)
412
sage: L.series_modp()
413
4*T^2 + 2*T^3 + 3*T^4 + O(T^5)
414
sage: L.series_modp(3, 8)
415
4*T^2 + 2*T^3 + 3*T^4 + T^5 + 4*T^7 + O(T^8)
416
sage: L.series_modp(2, 20)
417
4*T^2 + 2*T^3 + 3*T^4 + O(T^5)
418
"""
419
L = self.series(n=n, ser_prec=ser_prec, check=check)
420
return L.change_ring(Integers(self.p))
421
422
def series_to_enough_prec(self, bint verb=0):
423
r = self.E.rank()
424
n = 2
425
while True:
426
L = self.series(n, ser_prec=4+r, check=True, verb=verb)
427
if verb: print "L = ", L
428
if L.prec() > r:
429
for i in range(r):
430
assert L[i] == 0, "bug in computing p-adic L-series for %s and p=%s"%(self.E.a_invariants(), self.p)
431
if L[r] != 0:
432
return L
433
n += 1
434
435
def sha_modp(self, bint verb=0):
436
"""
437
Return the p-adic conjectural order of Sha mod p using p-adic
438
BSD, along with the p-adic L-series and p-adic regulator.
439
440
EXAMPLES::
441
442
sage: E = EllipticCurve('10050s1')
443
sage: from psage.modform.rational.padic_elliptic_lseries_fast import pAdicLseries
444
sage: L = pAdicLseries(E, 13)
445
sage: sha, L, reg = L.sha_modp()
446
sage: sha
447
1 + O(13)
448
sage: L
449
O(13^2) + O(13^2)*T + (10*13 + O(13^2))*T^2 + (12*13 + O(13^2))*T^3 + (9 + 10*13 + O(13^2))*T^4 + (7 + 9*13 + O(13^2))*T^5 + O(T^6)
450
sage: reg
451
12*13^3 + 4*13^4 + 9*13^5 + 11*13^6 + 5*13^7 + 7*13^9 + 6*13^10 + 5*13^12 + 4*13^13 + 4*13^14 + 5*13^15 + 5*13^16 + 4*13^17 + 10*13^18 + 2*13^19 + 6*13^20 + O(13^21)
452
"""
453
L = self.series_to_enough_prec(verb=verb)
454
p = ZZ(self.p)
455
reg = self.E.padic_regulator(p)
456
r = self.E.rank()
457
lg = (1 + p + O(p**10)).log()
458
tam = self.E.tamagawa_product()
459
eps = (1 - 1/(self.alpha()+O(p**10)))**2 # assumes good ordinary
460
tor = self.E.torsion_order()**2
461
462
#sha = Mod(tam * (reg / lg**r) * eps / tor, p)
463
sha = L[r] / (tam * (reg / lg**r) * eps / tor)
464
465
return sha, L, reg
466
467
def series_parallel(L, n, prec, ser_prec=5, verb=False, force_mulmod=False, ncpus=None):
468
# Use @parallel to do this computation by dividing it up into
469
# p separate tasks, doing those in separate processes,
470
# combining the results, etc.
471
from sage.all import parallel
472
if ncpus is None:
473
import sage.parallel.ncpus
474
ncpus = sage.parallel.ncpus.ncpus()
475
@parallel(ncpus)
476
def f(start, stop):
477
return L._series(n, prec, ser_prec, verb, force_mulmod, start, stop)
478
479
# intervals is going to be a list of (start, stop) pairs that give
480
# the (Python) range of j's to sum over. We thus must divide
481
# range(0, p^(n-1))
482
# up into ncpus sublists.
483
last = ZZ(L.p)**(n-1)
484
intervals = []
485
start = 0; stop = last//ncpus
486
for i in range(ncpus):
487
intervals.append((start, stop))
488
start = stop
489
stop += last//ncpus
490
if start < last:
491
intervals.append((start, last))
492
P = 0
493
for x in f(intervals):
494
P += x[-1]
495
return P
496
497