Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241852 views
1
#################################################################################
2
#
3
# (c) Copyright 2011 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
#### WARNING #### WARNING #### WARNING #####
23
##
24
# This file is now largely irrelevant and replaced by aplist_sqrt5.pyx
25
##
26
#### WARNING #### WARNING #### WARNING #####
27
28
"""
29
Fast Cython code needed to compute L-series of elliptic curves over F = Q(sqrt(5)).
30
31
USES:
32
33
- The fast residue class rings defined in
34
psage.modform.hilbert.sqrt5.sqrt5_fast for naive enumeration.
35
36
- Drew Sutherlands smalljac for point counting
37
38
- Lcalc for evaluating the L-series
39
40
- Dokchitser as well.
41
42
- Computes the *root number* in addition to the L-series.
43
44
"""
45
46
####################################################################
47
# Straightforward Elliptic Curve Pointcount
48
####################################################################
49
50
from psage.modform.hilbert.sqrt5.sqrt5_fast cimport (
51
ResidueRingElement, ResidueRing_abstract, residue_element
52
)
53
54
from psage.modform.hilbert.sqrt5.sqrt5_fast import ResidueRing
55
56
cpdef long ap_via_enumeration(ResidueRingElement a4, ResidueRingElement a6) except -1:
57
"""
58
Compute the trace of Frobenius `a_p` on the elliptic curve defined
59
by `y^2 = x^3 + a_4 x + a_6` using a straightforward enumeration
60
algorithm. Here `p` must be a prime of good reduction for the
61
equation of the curve.
62
63
EXAMPLES::
64
65
sage: import psage.ellcurve.lseries.sqrt5 as s
66
sage: K.<a> = NumberField(x^2-x-1)
67
sage: E = EllipticCurve([1,-a,a,5*a-4,-3*a+4])
68
sage: _,_,_,a4,a6 = E.short_weierstrass_model().a_invariants()
69
sage: P = K.ideal(163)
70
sage: import psage.modform.hilbert.sqrt5.sqrt5_fast as t
71
sage: R = t.ResidueRing(P, 1)
72
sage: s.ap_via_enumeration(R(a4), R(a6))
73
120
74
sage: k = P.residue_field(); E0 = E.change_ring(k); k.cardinality() + 1 - E0.cardinality()
75
120
76
"""
77
cdef long i, cnt = 1 # point at infinity
78
cdef ResidueRing_abstract R = a4.parent()
79
80
if R.e != 1:
81
raise ValueError, "residue ring must be a field"
82
83
cdef long p = R.p
84
cdef long n = p*(p-1)/2
85
cdef residue_element x, z, w
86
for i in range(R.cardinality()):
87
R.unsafe_ith_element(x, i)
88
R.mul(z, x, x) # z = x*x
89
R.mul(z, z, x) # z = x^3
90
R.mul(w, a4.x, x) # w = a4*x
91
R.add(z, z, w) # z = z + w = x^3 + a4*x
92
R.add(z, z, a6.x) # z = x^3 + a4*x + a6
93
if R.element_is_0(z):
94
cnt += 1
95
elif R.is_square(z):
96
cnt += 2
97
return R.cardinality() + 1 - cnt
98
99
from sage.libs.gmp.mpz cimport (
100
mpz_t, mpz_set, mpz_init, mpz_clear,
101
mpz_fdiv_ui)
102
103
cdef extern from "pari/pari.h":
104
unsigned long Fl_sqrt(unsigned long a, unsigned long p)
105
unsigned long Fl_div(unsigned long a, unsigned long b, unsigned long p)
106
107
cpdef unsigned long sqrt_mod(unsigned long a, unsigned long p):
108
return Fl_sqrt(a, p)
109
110
from sage.rings.integer cimport Integer
111
112
from sage.all import prime_range, pari, verbose, get_verbose, prime_pi
113
114
from psage.libs.smalljac.wrapper1 import elliptic_curve_ap
115
116
import aplist
117
118
include "stdsage.pxi"
119
include "interrupt.pxi"
120
121
cdef long UNKNOWN = 2**31 - 1
122
123
cdef class TracesOfFrobenius:
124
cdef long bound, table_size
125
cdef long* primes
126
cdef long* sqrt5
127
cdef long* ap
128
cdef mpz_t Ax, Ay, Bx, By
129
cdef object E, j, c_quo
130
131
##########################################################
132
# Allocate and de-allocate basic data structures
133
##########################################################
134
135
def __cinit__(self):
136
self.primes = NULL
137
self.sqrt5 = NULL
138
self.ap = NULL
139
mpz_init(self.Ax); mpz_init(self.Ay); mpz_init(self.Bx); mpz_init(self.By)
140
141
def __init__(self, E, long bound):
142
# Make table of primes up to the bound, and uninitialized
143
# corresponding a_p (traces of Frobenius)
144
self._initialize_coefficients(E)
145
self._initialize_prime_ap_tables(bound)
146
#self._compute_split_traces()
147
148
def _initialize_prime_ap_tables(self, long bound):
149
self.bound = bound
150
cdef long max_size = 2*bound+20
151
self.primes = <long*> sage_malloc(sizeof(long)*max_size)
152
self.sqrt5 = <long*> sage_malloc(sizeof(long)*max_size)
153
self.ap = <long*> sage_malloc(sizeof(long)*max_size)
154
155
cdef long p, t, i=0, sr0, sr1
156
for p in prime_range(bound):
157
if i >= max_size:
158
raise RuntimeError, "memory assumption violated"
159
t = p % 5
160
if t == 1 or t == 4:
161
# split case
162
self.primes[i] = p
163
self.primes[i+1] = p
164
self.ap[i] = UNKNOWN
165
self.ap[i+1] = UNKNOWN
166
sr0 = sqrt_mod(5, p)
167
sr1 = p - sr0
168
if sr0 > sr1: # swap
169
sr0, sr1 = sr1, sr0
170
self.sqrt5[i] = sr0
171
self.sqrt5[i+1] = sr1
172
i += 2
173
else:
174
# inert or ramified cases (ignore primes with too big norm)
175
if p == 5 or p*p < bound:
176
self.primes[i] = p
177
self.sqrt5[i] = 0
178
self.ap[i] = UNKNOWN
179
i += 1
180
181
self.table_size = i
182
183
def _initialize_coefficients(self,E):
184
# Store all coefficients -- may be needed for trace of frob mod 2 and 3 and bad primes.
185
self.E = E
186
a1, a2, a3, a4, a6 = E.a_invariants()
187
188
# Compute short Weierstrass form directly (for speed purposes)
189
if a1 or a2 or a3:
190
b2 = a1*a1 + 4*a2; b4 = a1*a3 + 2*a4; b6 = a3**2 + 4*a6
191
if b2:
192
c4 = b2**2 - 24*b4; c6 = -b2**3 + 36*b2*b4 - 216*b6
193
A = -27*c4; B = -54*c6
194
else:
195
A = 8*b4; B = 16*b6
196
else:
197
A = a4; B = a6
198
199
# Now do the change of variables y|-->y/8; x|-->x/4 to get an isomorphic
200
# curve so that A and B are in Z[sqrt(5)], and the reduction isn't
201
# any worse at 2.
202
A *= 16
203
B *= 64
204
205
self.c_quo = (-48*A)/(-864*B)
206
self.j = (-110592*A*A*A)/(-64*A*A*A - 432*B*B)
207
208
# Store the short Weierstrass form coefficients as a 4-tuple of GMP ints,
209
# where (Ax,Ay) <--> Ax + sqrt(5)*Ay.
210
# By far the best way to get these Ax, Ay are to use the parts
211
# method, which is very fast, and gives precisely what we want
212
# (for elements of a quadratic field). As a bonus, at least
213
# presently, non-quadratic number field elements don't have a
214
# parts method, which is a safety check.
215
v = A.parts()
216
cdef Integer z
217
z = Integer(v[0]); mpz_set(self.Ax, z.value)
218
z = Integer(v[1]); mpz_set(self.Ay, z.value)
219
v = B.parts()
220
z = Integer(v[0]); mpz_set(self.Bx, z.value)
221
z = Integer(v[1]); mpz_set(self.By, z.value)
222
223
def __dealloc__(self):
224
mpz_clear(self.Ax); mpz_clear(self.Ay); mpz_clear(self.Bx); mpz_clear(self.By)
225
if self.primes:
226
sage_free(self.primes)
227
if self.sqrt5:
228
sage_free(self.sqrt5)
229
if self.ap:
230
sage_free(self.ap)
231
232
def _tables(self):
233
"""
234
Return Python dictionary with copies of the internal tables.
235
This is used mainly for debugging.
236
237
OUTPUT:
238
- dictionary {'primes':primes, 'sqrt5':sqrt5, 'ap':ap}
239
"""
240
cdef long i
241
primes = [self.primes[i] for i in range(self.table_size)]
242
sqrt5 = [self.sqrt5[i] for i in range(self.table_size)]
243
ap = [self.ap[i] if self.ap[i] != UNKNOWN else None for i in range(self.table_size)]
244
return {'primes':primes, 'sqrt5':sqrt5, 'ap':ap}
245
246
def aplist(self):
247
cdef long i
248
v = []
249
for i in range(self.table_size):
250
v.append( (self.primes[i], self.sqrt5[i], self.ap[i] if self.ap[i] != UNKNOWN else None) )
251
return v
252
253
##########################################################
254
# Compute traces
255
##########################################################
256
def _compute_split_traces(self, algorithm='smalljac'):
257
cdef long i, p, a, b
258
for i in range(self.table_size):
259
if self.sqrt5[i] != 0:
260
p = self.primes[i]
261
# 1. Reduce A, B modulo the prime to get a curve over GF(p)
262
a = mpz_fdiv_ui(self.Ax, p) + mpz_fdiv_ui(self.Ay, p)*self.sqrt5[i]
263
b = mpz_fdiv_ui(self.Bx, p) + mpz_fdiv_ui(self.By, p)*self.sqrt5[i]
264
265
# 2. Check that the resulting curve is nonsingular
266
if (-64*((a*a) % p) *a - 432*b*b)%p == 0:
267
# curve is singular at p -- leave this to some other algorithm
268
#print "p = %s singular"%p
269
continue
270
271
# 3. Compute the trace using smalljac (or PARI?)
272
if algorithm == 'smalljac':
273
self.ap[i] = elliptic_curve_ap(a, b, p)
274
elif algorithm == 'pari':
275
self.ap[i] = pari('ellap(ellinit([0,0,0,%s,%s],1),%s)'%(a,b,p)) # the 1 in the elliptic curve constructor is super important!
276
else:
277
raise ValueError
278
279
def _compute_inert_and_ramified_traces(self, T): # T = InertTraceCalculator
280
cdef long i, p
281
for i in range(self.table_size):
282
if self.sqrt5[i] == 0:
283
p = self.primes[i]
284
if p >= 7:
285
try:
286
self.ap[i] = T.trace_of_frobenius(self.j, self.c_quo, p)
287
except Exception, msg:
288
if get_verbose() > 0:
289
verbose("skipping inert table computation for p=%s (%s)"%(p, msg))
290
291
def _compute_remaining_traces_naively(self):
292
"""
293
Compute any traces not already computed using naive algorithm.
294
"""
295
cdef long i
296
for i in range(self.table_size):
297
if self.ap[i] == UNKNOWN:
298
self._compute_naive_trace(i)
299
300
def _compute_naive_trace(self, i):
301
"""
302
Compute trace at position i using naive algorithm.
303
"""
304
if get_verbose() > 0: # since forming verbose error message is expensive
305
verbose("naive i=%s, a_{%s}"%(i,self.primes[i]))
306
self.ap[i] = aplist.ap(self.E, self.prime(i))
307
308
def prime(self, long i):
309
"""
310
Return the i-th prime as a fractional ideal of Q(sqrt(5)).
311
312
INPUT:
313
i -- positive integer
314
"""
315
if i < 0 or i >= self.table_size:
316
raise IndexError, "i must be between 0 and %s"%(self.table_size-1)
317
318
# Non-optimized code that returns the ith prime of the quadratic number field.
319
from psage.modform.hilbert.sqrt5.sqrt5 import F # F = Q(sqrt(5))
320
cdef long a, p = self.primes[i], s = self.sqrt5[i]
321
if p == 5: # ramified
322
return F.ideal(2*F.gen() - 1)
323
324
a = p % 5
325
if a==2 or a==3: # inert
326
return F.ideal(p)
327
else: # split case
328
return F.ideal([p, 2*F.gen()-1 - s])
329
330
def _compute_traces(self, inert_table, algorithm='smalljac'):
331
self._compute_split_traces()
332
self._compute_inert_and_ramified_traces(inert_table)
333
self._compute_remaining_traces_naively()
334
335
###############################################################
336
# Specialized code for computing traces modulo the inert primes
337
###############################################################
338
339
def inert_primes(N):
340
r"""
341
Return a list of the inert primes of `\QQ(\sqrt{5})` of norm less than `N`.
342
343
INPUT:
344
- N -- positive integer
345
OUTPUT:
346
- list of Sage integers
347
348
EXAMPLES::
349
350
sage: import psage.ellcurve.lseries.sqrt5 as sqrt5
351
sage: sqrt5.inert_primes(10^4)
352
[2, 3, 7, 13, 17, 23, 37, 43, 47, 53, 67, 73, 83, 97]
353
"""
354
from math import sqrt
355
s = set([Integer(2), Integer(3)])
356
return [p for p in prime_range(int(sqrt(N))) if p%5 in s]
357
358
from sage.stats.intlist cimport IntList
359
360
def unpickle_InertTraceCalculator(tables):
361
C = InertTraceCalculator()
362
C.tables = tables
363
return C
364
365
cdef class InertTraceCalculator:
366
cdef public dict tables
367
368
def __init__(self):
369
self.tables = {}
370
371
def __repr__(self):
372
return "Inert trace calculator with precomputed tables for p in {%s}"%(sorted(self.tables.keys()))
373
374
def __reduce__(self):
375
return unpickle_InertTraceCalculator, (self.tables, )
376
377
cpdef long trace_of_frobenius(self, j0, c_quo0, long p) except 9223372036854775808:
378
T = self.tables[p]
379
cdef ResidueRing_abstract R = T['R']
380
cdef ResidueRingElement j = R(j0), c_quo = R(c_quo0)
381
if R.element_is_0(j.x) or j.x[0]==1728%p and j.x[1]==0:
382
raise NotImplementedError
383
384
cdef int i = R.index_of_element(j.x)
385
386
cdef IntList ap = T['ap'], c_quos = T['c_quo'], squares = T['squares']
387
cdef long a = ap[i]
388
cdef residue_element z
389
R.ith_element(z, c_quos[i])
390
R.mul(z, z, c_quo.x)
391
if not squares[R.index_of_element(z)]: # not a square, so curves are not isomorphic, so there is a quadratic twist
392
return -a
393
else:
394
return a
395
396
397
def init_table(self, int p):
398
assert p >= 7 and (p%5 == 2 or p%5 == 3) # inert prime >= 7
399
if self.tables.has_key(p):
400
return
401
# create the table for the given prime p.
402
from psage.modform.hilbert.sqrt5.sqrt5 import F
403
cdef ResidueRing_abstract R = ResidueRing(F.ideal(p), 1)
404
405
cdef IntList ap, c_quo
406
ap = IntList(R.cardinality())
407
c_quo = IntList(R.cardinality())
408
squares = IntList(R.cardinality())
409
self.tables[p] = {'R':R, 'ap':ap, 'c_quo':c_quo, 'squares':squares}
410
411
self.init_squares_table(squares, R)
412
413
cdef IntList cubes = IntList(R.cardinality())
414
self.cube_table(cubes, R)
415
416
cdef long i
417
cdef residue_element j, a4, a6
418
sig_on()
419
for i in range(R.cardinality()):
420
R.unsafe_ith_element(j, i)
421
self.elliptic_curve_from_j(a4, a6, j, R)
422
self.ap_via_enumeration(&ap._values[i], &c_quo._values[i], a4, a6, R, squares, cubes)
423
sig_off()
424
425
cdef int cube_table(self, IntList cubes, ResidueRing_abstract R) except -1:
426
cdef long i
427
cdef residue_element x, y
428
for i in range(R.cardinality()):
429
R.unsafe_ith_element(x, i)
430
R.mul(y, x, x) # y = x^2
431
R.mul(y, y, x) # y = x^3
432
cubes._values[i] = R.index_of_element(y)
433
return 0
434
435
cdef int init_squares_table(self, IntList squares,
436
ResidueRing_abstract R) except -1:
437
438
cdef long i
439
cdef residue_element x, y
440
for i in range(R.cardinality()):
441
R.unsafe_ith_element(x, i)
442
R.mul(y, x, x)
443
squares._values[R.index_of_element(y)] = 1
444
return 0
445
446
cdef int elliptic_curve_from_j(self,
447
residue_element a4,
448
residue_element a6,
449
residue_element j,
450
ResidueRing_abstract R) except -1:
451
cdef residue_element k, m_three, m_two
452
453
# k = 1728
454
k[0] = 1728 % R.p; k[1] = 0
455
456
if R.element_is_0(j): # if j==0
457
R.set_element_to_0(a4)
458
R.set_element_to_1(a6)
459
return 0
460
461
if R.cmp_element(j, k) == 0: # if j==1728
462
R.set_element_to_1(a4)
463
R.set_element_to_0(a6)
464
return 0
465
466
# -3 and -2
467
m_three[0] = R.p - 3; m_three[1] = 0
468
m_two[0] = R.p - 2; m_two[1] = 0
469
470
# k = j-1728
471
R.sub(k, j, k)
472
473
# a4 = -3*j*k
474
R.mul(a4, m_three, j)
475
R.mul(a4, a4, k)
476
477
# a6 = -2*j*k^2
478
R.mul(a6, m_two, j)
479
R.mul(a6, a6, k)
480
R.mul(a6, a6, k)
481
482
483
cdef int ap_via_enumeration(self, int* ap, int* c_quo,
484
residue_element a4, residue_element a6,
485
ResidueRing_abstract R,
486
IntList squares,
487
IntList cubes) except -1:
488
assert R.p >= 7
489
cdef long i, j, cnt = 1 # start 1 because of point at infinity
490
cdef residue_element x, z, w
491
for i in range(R.cardinality()):
492
R.unsafe_ith_element(x, i)
493
R.unsafe_ith_element(z, cubes._values[i]) # z = x^3
494
R.mul(w, a4, x) # w = a4*x
495
R.add(z, z, w) # z = z + w = x^3 + a4*x
496
R.add(z, z, a6) # z = x^3 + a4*x + a6
497
if R.element_is_0(z):
498
cnt += 1
499
else:
500
if squares._values[R.index_of_element(z)]: # assumes p!=2.
501
cnt += 2
502
503
ap[0] = R.cardinality() + 1 - cnt
504
505
# Now compute c4/c6 = a4/(18*a6)
506
if R.element_is_0(a6):
507
c_quo[0] = -1 # signifies "infinity"
508
else:
509
x[0] = 18 % R.p; x[1] = 0 # x = 18
510
R.mul(z, x, a6) # z = 18*a6
511
R.inv(w, z) # w = 1/(18*a6)
512
R.mul(z, a4, w) # z = a4/(18*a6)
513
c_quo[0] = R.index_of_element(z)
514
return 0 # no error occurred
515
516
517
518