Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
"""
2
Triple product L-series.
3
4
WARNING: The code in here gets the algorithm down, but is (1) pure
5
Python, and (2) runs into a limit since in order to actually use it in
6
even the first example, one needs to send a huge number of
7
coefficients to GP/PARI over a ptty, which results in a crash.
8
So currently this code does not actually work in a single case!
9
To fix it:
10
11
* find a better way to send a large number of coefficients to pari
12
13
* make a version of the code that instead uses lcalc
14
15
Note that this code still has some value, since it can be used to
16
compute the Dirichlet series coefficients of the triple product.
17
"""
18
19
#################################################################################
20
#
21
# (c) Copyright 2011 William Stein
22
#
23
# This file is part of PSAGE
24
#
25
# PSAGE is free software: you can redistribute it and/or modify
26
# it under the terms of the GNU General Public License as published by
27
# the Free Software Foundation, either version 3 of the License, or
28
# (at your option) any later version.
29
#
30
# PSAGE is distributed in the hope that it will be useful,
31
# but WITHOUT ANY WARRANTY; without even the implied warranty of
32
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33
# GNU General Public License for more details.
34
#
35
# You should have received a copy of the GNU General Public License
36
# along with this program. If not, see <http://www.gnu.org/licenses/>.
37
#
38
#################################################################################
39
40
41
from sage.all import ZZ, RDF, CDF
42
R_cdf = CDF['x']
43
44
pi = RDF.pi()
45
46
def quad_roots(a, p):
47
"""
48
Return the two complex roots of X^2 - a*X + p.
49
50
INPUT:
51
- `a` -- a real number
52
- `p` -- a prime number
53
OUTPUT:
54
- 2-tuple of complex numbers
55
56
EXAMPLES::
57
"""
58
t = R_cdf([p, -a, 1]).roots()
59
return (t[0][0], t[1][0])
60
61
class TripleProductLseries(object):
62
"""
63
A triple product `L`-series attached to three newforms of level `N`.
64
"""
65
def __init__(self, N, f, g, h):
66
"""
67
INPUT:
68
- N -- squarefree integer
69
- f -- an object such that for n>=0, we have
70
f[n] = a_n(f) = n-th Fourier coefficient of
71
a newform on Gamma_0(N).
72
- g -- like f
73
- h -- like f
74
"""
75
self._N = ZZ(N)
76
if not (self._N.is_squarefree() and self._N > 0):
77
raise ValueError, "N (=%s) must be a squarefree positive integer"%self._N
78
self._newforms = (f,g,h)
79
self._gen = RDF['X'].gen()
80
self._genC = CDF['X'].gen()
81
self._series = RDF[['X']]
82
83
def __repr__(self):
84
"""
85
Return text representation of this triple product `L`-function.
86
"""
87
return "Triple product L-function L(s,f,g,h) of three newforms on Gamma_0(%s)"%self._N
88
89
def level(self):
90
"""
91
Return the common level `N` of the newforms in the triple product.
92
93
OUTPUT:
94
- Integer
95
96
EXAMPLES::
97
"""
98
return self._N
99
100
def newforms(self):
101
"""
102
Return 3-tuple (f,g,h) of the data that defines the newforms
103
in the triple product.
104
105
OUTPUT:
106
- 3-tuple
107
108
EXAMPLES::
109
"""
110
return self._newforms
111
112
def _local_series(self, p, prec):
113
"""
114
Return power series in `X` (which you should think of as `p^{-s}`)
115
that is the expansion to precision prec of the local factor at `p`
116
of this `L`-series.
117
118
INPUT:
119
- p -- prime
120
- prec -- positive integer
121
122
OUTPUT:
123
- power series that ends in a term ``O(X^prec)``.
124
"""
125
f = self._series(self.charpoly(p), prec)
126
return f**(-1)
127
128
def dirichlet_series(self, prec, eps=1e-10):
129
"""
130
Return the Dirichlet series representation of self, up to the given
131
precision.
132
133
INPUT:
134
- prec -- positive integer
135
- eps -- None or a positive real; any coefficient with absolute
136
value less than eps is set to 0.
137
"""
138
coeffs = self.dirichlet_series_coeffs(prec, eps)
139
return DirichletSeries(coeffs, 's')
140
141
def dirichlet_series_coeffs(self, prec, eps=1e-10):
142
"""
143
Return the coefficients of the Dirichlet series representation
144
of self, up to the given precision.
145
146
INPUT:
147
- prec -- positive integer
148
- eps -- None or a positive real; any coefficient with absolute
149
value less than eps is set to 0.
150
"""
151
# Use multiplicativity to compute the Dirichlet series
152
# coefficients, then make a DirichletSeries object.
153
zero = RDF(0)
154
coeffs = [RDF(0),RDF(1)] + [None]*(prec-2)
155
156
from sage.all import log, floor # TODO: slow
157
158
# prime-power indexed coefficients
159
for p in prime_range(2, prec):
160
B = floor(log(prec, p)) + 1
161
series = self._local_series(p, B)
162
p_pow = p
163
for i in range(1, B):
164
coeffs[p_pow] = series[i] if (eps is None or abs(series[i])>eps) else zero
165
p_pow *= p
166
167
# non-prime-powers
168
from sage.all import factor
169
for n in range(2, prec):
170
if coeffs[n] is None:
171
a = prod(coeffs[p**e] for p, e in factor(n))
172
coeffs[n] = a if (eps is None or abs(a) > eps) else zero
173
174
return coeffs
175
176
def charpoly(self, p):
177
"""
178
Return the denominator as a polynomial in `X` (=`p^{-s}`) of the local
179
factor at `p` of this `L`-series.
180
181
The degree of the polynomial is ???? [[todo]].
182
183
INPUT:
184
- `p` -- prime
185
186
OUTPUT:
187
- polynomial in `X`
188
189
EXAMPLES::
190
191
"""
192
if self._N % p == 0:
193
return self._charpoly_bad(p)
194
else:
195
return self._charpoly_good(p)
196
197
def _charpoly_good(self, p):
198
"""
199
Internal function that returns the local charpoly at a good prime.
200
201
INPUT:
202
- `p` -- prime
203
204
OUTPUT:
205
- polynomial in `X`
206
207
EXAMPLES::
208
209
"""
210
Y = self._genC
211
a = [quad_roots(f[p], p) for f in self._newforms]
212
L = 1
213
for n in range(8):
214
d = ZZ(n).digits(2)
215
d = [0]*(3-len(d)) + d
216
L *= 1 - prod(a[i][d[i]] for i in range(3))*Y
217
return self._gen.parent()([x.real_part() for x in L])
218
219
def _charpoly_bad(self, p):
220
"""
221
Internal function that returns the local charpoly at a bad prime.
222
223
INPUT:
224
- `p` -- prime
225
226
OUTPUT:
227
- polynomial in `X`
228
229
EXAMPLES::
230
231
"""
232
X = self._gen
233
a_p, b_p, c_p = [f[p] for f in self._newforms]
234
return (1 - a_p*b_p*c_p * X) * (1 - a_p*b_p*c_p*p*X)**2
235
236
def epsilon(self, p=None):
237
"""
238
Return the local or global root number of this triple product
239
L-function.
240
241
INPUT:
242
- p -- None (default) or a prime divisor of the level
243
"""
244
if p is None:
245
# Right below equation (1.11) in [Gross-Kudla]
246
return -prod(self.epsilon(p) for p in self.level().prime_divisors())
247
else:
248
if not ZZ(p).is_prime():
249
raise ValueError, "p must be prime"
250
if self.level() % p != 0:
251
raise ValueError, "p must divide the level"
252
# Equation (1.3) in [Gross-Kudla]
253
a_p, b_p, c_p = [f[p] for f in self._newforms]
254
return -a_p*b_p*c_p
255
256
def dokchitser(self, prec):
257
# NOTE: In order to get the Dokchitser parameters of an L-function,
258
# it is useful to know that
259
#
260
# gamma(s) = 2^s*gamma(s/2)*gamma((s+1)/2) / (2*sqrt(pi))
261
#
262
conductor = self.level()**10
263
gammaV = [-1,-1,-1,0,0,0,0,1]
264
weight = 4
265
eps = self.epsilon()
266
poles = []
267
residues = []
268
269
from sage.lfunctions.dokchitser import Dokchitser
270
L = Dokchitser(conductor = conductor,
271
gammaV = gammaV,
272
weight = weight,
273
eps = eps,
274
poles = poles, residues=[])
275
#s = 'v=%s; a(k)=if(k>%s,0,v[k])'%(self.dirichlet_series_coeffs(prec), prec)
276
s = 'v=%s; a(k)=v[k]'%(self.dirichlet_series_coeffs(prec))
277
L.init_coeffs('a(k)', pari_precode=s)
278
return L
279
280
281
282
class DirichletSeries(object):
283
"""
284
A Dirichlet series.
285
"""
286
def __init__(self, coeffs, variable='s'):
287
"""
288
INPUT:
289
- ``coeffs`` -- a list of the coefficients of the Dirichlet series
290
such that the coefficient `a_n` in the sum `a_n/n^s` is ``coeffs[n]``.
291
- ``variable`` - a string
292
"""
293
self._coeffs = coeffs
294
self._variable = variable
295
296
def __repr__(self):
297
if self._coeffs[0]._is_atomic():
298
v = ['%s/%s^%s'%(self._coeffs[n], n, self._variable) for
299
n in range(1,len(self._coeffs)) if self._coeffs[n]]
300
s = ' + '.join(v)
301
s = s.replace(' + -',' - ')
302
else:
303
v = ['(%s)/%s^%s'%(self._coeffs[n], n, self._variable) for
304
n in range(1,len(self._coeffs)) if self._coeffs[n]]
305
s = ' + '.join(v)
306
return s
307
308
def __getitem__(self, *args):
309
return self._coeffs.__getitem__(*args)
310
311
312
313