Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
AndrewVSutherland
GitHub Repository: AndrewVSutherland/lmfdb
Path: blob/main/scripts/elliptic_curves/import_ec_lfunction_data.py
1128 views
1
# -*- coding: utf-8 -*-
2
r""" Import L-function data (as computed from Cremona tables by Andy Booker).
3
4
Initial version (Bristol March 2016)
5
6
NB This script has NOT been adapted to work with postgres
7
8
For the format of the collections Lfunctions and Instances in the
9
database Lfunctions, see
10
https://github.com/LMFDB/lmfdb-inventory/blob/master/db-Lfunctions.md.
11
These are duplicated here for convenience but the inventory takes
12
precedence in case of any discrepancy.
13
14
In general "number" means an int or double or string representing a number (e.g. '1/2').
15
16
(1) fields which are the same for every elliptic curve over Q:
17
18
- 'algebraic' (bool), whether the L-function is algebraic: True
19
20
- 'analytic_normalization' (number), translation needed to obtain
21
the analytic normalization: 0.5
22
23
- 'coefficient_field' (string), label of the coefficient field Q: '1.1.1.1'
24
- 'degree' (int), degree of the L-function: 2
25
- 'gamma_factors' (list of length 2 of lists of numbers), encoding of Gamma factors: [[],[0]]
26
- 'motivic_weight' (int), motivic weight: 1
27
- 'primitive' (bool), wheher this L-function is primitive: True
28
- 'self_dual' (bool), wheher this L-function is self-dual: True
29
- 'load_key' (string), person who uploaded the data
30
- 'type' (string), "ECQ"
31
32
(2) fields which depend on the curve (isogeny class)
33
34
- '_id': internal mogodb identifier
35
- 'Lhash' (string)
36
- 'conductor' (int) conductor, e.g. 1225
37
- 'url' (string): the URL of the object from which this
38
L-function originated, e.g. 'EllipticCurve/Q/11/a'
39
- 'instances' (list of strings): list of URLs of objects with this L-function, e.g. ['EllipticCurve/Q/11/a']
40
- 'order_of_vanishing': (int) order of vanishing at critical point, e.g. 0
41
- 'bad_lfactors' (list of lists) list of pairs [p,coeffs] where p
42
is a bad prime and coeffs is a list of 1 or 2 numbers,
43
coefficients of the bad Euler factor at p,
44
e.g. [[2,[1]],[3,[1,1]],[5,[1,-1]]]
45
- 'euler_factors' (list of lists of 3 ints): list of lists [1] or
46
[1,1] or [1,-1] or[1,-ap,p] of coefficients of the p'th Euler
47
factor for the first 100 primes (including any bad primes).
48
- 'A2',...,'A10' (int): first few (integral) Dirichlet coefficients, arithmetic normalization
49
- 'a2',...,'a10' (list of 2 floats): first few (complex) Dirichlet coefficients, analytic normalization
50
- 'central_character' (string): label of associated central character, '%s.1' % conductor
51
- 'root_number' (int): sign of the functional equation: 1 or -1
52
- 'leading_term' (number): value of L^{r}(1)/r! where r=order_of_vanishing, e.g. 0.253841860856
53
- 'st_group' (string): Sato-Tate group, either 'SU(2)' if not CM or 'N(U(1))' if CM
54
- 'positive_zeros' (list of strings): list of strings representing strictly positive
55
imaginary parts of zeros between 0 and 20.
56
- 'z1', 'z2', 'z3' (numbers): the first three positive zeros
57
- 'plot_delta' (number): x-increment for plot values
58
- 'plot_values' (list of numbers): list of y-coordinates of points on the plot
59
60
"""
61
62
import os
63
from sage.all import ZZ, primes, sqrt, EllipticCurve, prime_pi
64
65
from lmfdb.base import getDBConnection
66
print("getting connection")
67
C= getDBConnection()
68
print("authenticating on the elliptic_curves database")
69
import yaml
70
pw_dict = yaml.load(open(os.path.join(os.getcwd(), os.extsep, os.extsep, os.extsep, "passwords.yaml")))
71
username = pw_dict['data']['username']
72
password = pw_dict['data']['password']
73
C['elliptic_curves'].authenticate(username, password)
74
print("setting curves")
75
curves = C.elliptic_curves.curves
76
77
print("authenticating on the Lfunctions database")
78
C['Lfunctions'].authenticate(username, password)
79
Lfunctions = C.Lfunctions.Lfunctions
80
#Lfunctions = C.Lfunctions.LfunctionsECtest
81
Instances = C.Lfunctions.instances
82
#Instances = C.Lfunctions.instancesECtest
83
84
def constant_data():
85
r"""
86
Returns a dict containing the L-function data which is the same for all curves:
87
88
- 'algebraic', whether the L-function is algebraic: True
89
- 'analytic_normalization', translation needed to obtain the analytic normalization: 0.5
90
- 'coefficient_field', label of the coefficient field Q: '1.1.1.1'
91
- 'degree', degree of the L-function: 2
92
- 'gamma_factors', encoding of Gamma factors: [[],[0]]
93
- 'motivic_weight', motivic weight: 1
94
- 'primitive', wheher this L-function is primitive: True
95
- 'self_dual', wheher this L-function is self-dual: True
96
- 'load_key', person who uploaded the data
97
98
"""
99
return {
100
'algebraic': True,
101
'analytic_normalization': 0.5,
102
'coefficient_field': '1.1.1.1',
103
'degree': 2,
104
'gamma_factors': [[],[0]],
105
'motivic_weight': 1,
106
'primitive': True,
107
'self_dual': True,
108
'load_key': "Cremona"
109
}
110
111
def make_one_euler_factor(E, p):
112
r"""
113
Returns the Euler factor at p from a Sage elliptic curve E.
114
"""
115
ap = int(E.ap(p))
116
e = E.conductor().valuation(p)
117
if e==0:
118
return [1,-ap,int(p)]
119
if e==1:
120
return [1,-ap]
121
return [1]
122
123
def make_one_euler_factor_db(E, p):
124
r"""
125
Returns the Euler factor at p from a database elliptic curve E.
126
"""
127
ld = [ld for ld in E['local_data'] if ld['p']==p]
128
if ld: # p is bad, we get ap from the stored local data:
129
ap = ld[0]['red']
130
if ap:
131
return [1,-ap]
132
else:
133
return [1]
134
135
# Now p is good and < 100 so we retrieve ap from the stored aplist:
136
137
ap = E['aplist'][prime_pi(p)-1] # rebase count from 1 to 0
138
return [1,-ap,int(p)]
139
140
def make_euler_factors(E, maxp=100):
141
r"""
142
Returns a list of the Euler factors for all primes up to max_p,
143
given a Sage elliptic curve E.
144
"""
145
return [make_one_euler_factor(E, p) for p in primes(maxp)]
146
147
def make_euler_factors_db(E):
148
r"""
149
Returns a list of the Euler factors for all primes up to 100,
150
given a database elliptic curve E (which has this many ap stored)
151
"""
152
return [make_one_euler_factor_db(E, p) for p in primes(100)]
153
154
def make_bad_lfactors(E):
155
r"""
156
Returns a list of the bad Euler factors, given a Sage elliptic curve E,
157
"""
158
return [[int(p),make_one_euler_factor(E, p)] for p in E.conductor().support()]
159
160
def make_bad_lfactors_db(E):
161
r"""
162
Returns a list of the bad Euler factors, given a database elliptic curve E,
163
"""
164
return [[p,make_one_euler_factor_db(E, p)] for p in [ld['p'] for ld in E['local_data']]]
165
166
def read_line(line):
167
r""" Parses one line from input file. Returns the hash and a dict
168
containing fields with keys as above. This version expects 9
169
fields on each line, separated by a colon:
170
171
0. hash
172
1. label
173
2. root number
174
3. (not used)
175
4. [a(n) for n in [2..10]
176
5. Special value L^(r)(1)/r!
177
6. zeros
178
7. plot spacing
179
8. plot data
180
181
182
"""
183
fields = line.split(":")
184
if len(fields)==6:
185
return read_line_old(line)
186
assert len(fields)==9
187
label = fields[1]
188
# get a curve from the database in this isogeny class. It must
189
# have number 1 since only those have the ap and an stored.
190
E = curves.find_one({'iso': label, 'number':1})
191
192
data = constant_data()
193
instances = {}
194
195
# Set the fields in the Instances collection:
196
197
cond = data['conductor'] = int(E['conductor'])
198
iso = E['lmfdb_iso'].split('.')[1]
199
instances['url'] = 'EllipticCurve/Q/%s/%s' % (cond,iso)
200
instances['Lhash'] = Lhash = fields[0]
201
instances['type'] = 'ECQ'
202
203
# Set the fields in the Lfunctions collection:
204
205
data['Lhash'] = Lhash
206
data['root_number'] = int(fields[2])
207
data['order_of_vanishing'] = int(E['rank'])
208
data['central_character'] = '%s.1' % cond
209
data['st_group'] = 'N(U(1))' if E['cm'] else 'SU(2)'
210
data['leading_term'] = lt = float(fields[5])
211
#
212
lt_db = float(E['special_value'])
213
dif = abs(lt-lt_db)
214
eps = 1e-14
215
if dif > eps:
216
print("{}: special value in db = {}, in input file = {}, difference = {}".format(label,lt_db,lt,dif))
217
218
# Zeros
219
220
zeros = fields[6][1:-1].split(",")
221
# omit negative ones and 0, using only string tests:
222
data['positive_zeros'] = [y for y in zeros if y!='0' and y[0]!='-']
223
data['z1'] = data['positive_zeros'][0]
224
data['z2'] = data['positive_zeros'][1]
225
data['z3'] = data['positive_zeros'][2]
226
227
# plot data
228
229
# constant difference in x-coordinate sequence:
230
data['plot_delta'] = float(fields[7])
231
# list of y coordinates for x>0:
232
data['plot_values'] = [float(y) for y in fields[8][1:-2].split(",")]
233
234
# Euler factors:
235
236
data['bad_lfactors'] = make_bad_lfactors_db(E)
237
data['euler_factors'] = make_euler_factors_db(E)
238
239
# Dirichlet coefficients
240
241
an = E['anlist'] # list indexed from 0 to 10 inclusive
242
input_an = [int(a) for a in fields[4][1:-1].split(",")]
243
assert an[2:11]==input_an
244
for n in range(2,11):
245
data['A%s' % n] = str(an[n])
246
data['a%s' % n] = [an[n]/sqrt(float(n)),0]
247
248
return Lhash, data, instances
249
250
def read_line_old(line):
251
r""" Parses one line from input file. Returns the hash and a dict
252
containing fields with keys as above. This original version
253
expects 6 fields on each line, separated by a colon:
254
255
0. hash
256
1. label
257
2. root number
258
3. (not used)
259
4. zeros
260
5. plot data
261
262
"""
263
fields = line.split(":")
264
assert len(fields)==6
265
label = fields[1] # use this isogeny class label to get info about the curve
266
E = curves.find_one({'iso': label})
267
268
data = constant_data()
269
instances = {}
270
271
# Set the fields in the Instances collection:
272
273
cond = data['conductor'] = int(E['conductor'])
274
iso = E['lmfdb_iso'].split('.')[1]
275
instances['url'] = 'EllipticCurve/Q/%s/%s' % (cond,iso)
276
instances['Lhash'] = Lhash = fields[0]
277
instances['type'] = 'ECQ'
278
279
# Set the fields in the Lfunctions collection:
280
281
data['Lhash'] = Lhash
282
data['root_number'] = int(fields[2])
283
data['order_of_vanishing'] = int(E['rank'])
284
data['central_character'] = '%s.1' % cond
285
data['st_group'] = 'N(U(1))' if E['cm'] else 'SU(2)'
286
data['leading_term'] = float(E['special_value'])
287
288
# Zeros
289
290
zeros = fields[4][1:-1].split(",")
291
# omit negative ones and 0, using only string tests:
292
data['positive_zeros'] = [y for y in zeros if y!='0' and y[0]!='-']
293
data['z1'] = data['positive_zeros'][0]
294
data['z2'] = data['positive_zeros'][1]
295
data['z3'] = data['positive_zeros'][2]
296
297
# plot data
298
299
plot_xy = [[float(v) for v in vv.split(",")] for vv in fields[5][2:-3].split("],[")]
300
# constant difference in x-coordinate sequence:
301
data['plot_delta'] = plot_xy[1][0]-plot_xy[0][0]
302
# list of y coordinates for x>0:
303
data['plot_values'] = [y for x,y in plot_xy if x>=0]
304
305
# Euler factors: we need the ap which are currently not in the
306
# database so we call Sage. It might be a good idea to store in
307
# the ec database (1) all ap for p<100; (2) all ap for bad p.
308
Esage = EllipticCurve([ZZ(a) for a in E['ainvs']])
309
data['bad_lfactors'] = make_bad_lfactors(Esage)
310
data['euler_factors'] = make_euler_factors(Esage)
311
312
# Dirichlet coefficients
313
314
an = Esage.anlist(10)
315
for n in range(2,11):
316
data['A%s' % n] = str(an[n])
317
data['a%s' % n] = [an[n]/sqrt(float(n)),0]
318
319
return Lhash, data, instances
320
321
322
# To run this go into the top-level lmfdb directory, run sage and give
323
# the command
324
# %runfile lmfdb/elliptic_curves/import_ec_lfunction_data.py
325
#
326
# and then run the following function.
327
# Unless you set test=False it will not actually upload any data.
328
329
def upload_to_db(base_path, f, test=True):
330
f = os.path.join(base_path, f)
331
h = open(f)
332
print("opened %s" % f)
333
334
data_to_insert = {} # will hold all the data to be inserted
335
instances_to_insert = {} # will hold all the data to be inserted
336
count = 0
337
338
for line in h.readlines():
339
count += 1
340
if count%1000==0:
341
print("read %s lines" % count)
342
Lhash, data, instance = read_line(line)
343
if Lhash not in data_to_insert:
344
data_to_insert[Lhash] = data
345
instances_to_insert[Lhash] = instance
346
347
print("finished reading %s lines from file" % count)
348
vals = data_to_insert.values()
349
350
print("Number of records to insert = %s" % len(vals))
351
count = 0
352
353
if test:
354
print("Not inserting any records as in test mode")
355
print("First record is %s" % vals[0])
356
return
357
358
for val in vals:
359
#print val
360
Lfunctions.update_one({'Lhash': val['Lhash']}, {"$set": val}, upsert=True)
361
Instances.update_one({'Lhash': val['Lhash']}, {"$set": instances_to_insert[val['Lhash']]}, upsert=True)
362
count += 1
363
if count % 1000 == 0:
364
print("inserted %s items" % count)
365
366