Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241782 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 2 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
We study low zeros.
24
"""
25
26
from sage.all import is_fundamental_discriminant, ZZ, parallel, var
27
28
from sage.libs.lcalc.lcalc_Lfunction import (
29
Lfunction_from_character,
30
Lfunction_from_elliptic_curve)
31
32
33
34
class LowZeros(object):
35
def __init__(self, num_zeros, params, ncpus=None):
36
self.num_zeros = num_zeros
37
self.params = params
38
self.ncpus = ncpus
39
self.zeros = self.compute_all_low_zeros(self.ncpus)
40
41
def compute_all_low_zeros(self, ncpus=None):
42
if ncpus is not None:
43
if ncpus == 1:
44
return [self.compute_low_zeros(x) for x in self.params]
45
else:
46
@parallel(ncpus)
47
def f(z):
48
return self.compute_low_zeros(z)
49
else:
50
@parallel
51
def f(z):
52
return self.compute_low_zeros(z)
53
54
# Get the answers back, and sort them in the same order
55
# as the input params. This is *very* important to do, i.e.,
56
# to get the order right!
57
Z = dict([(x[0][0][0], x[1]) for x in f(self.params)])
58
return [Z[x] for x in self.params]
59
60
def ith_zero_means(self, i=0):
61
z = self.zeros
62
s = 0.0
63
m = []
64
for j in range(len(self.params)):
65
s += z[j][i] # i-th zero for j-th parameter (e.g., j-th discriminant)
66
# The mean is s/(j+1)
67
m.append( (self.params[j], s/(j+1)) )
68
return m
69
70
def ith_zeros(self, i=0):
71
return [(self.params[j], self.zeros[j][i]) for j in range(len(self.params))]
72
73
def fundamental_discriminants(A, B):
74
"""Return the fundamental discriminants between A and B (inclusive), as Sage integers,
75
ordered by absolute value, with negatives first when abs same."""
76
v = [ZZ(D) for D in range(A, B+1) if is_fundamental_discriminant(D)]
77
v.sort(lambda x,y: cmp((abs(x),sgn(x)),(abs(y),sgn(y))))
78
return v
79
80
class RealQuadratic(LowZeros):
81
def __init__(self, num_zeros, max_D, **kwds):
82
self.max_D = max_D
83
params = fundamental_discriminants(2,max_D)
84
super(RealQuadratic, self).__init__(num_zeros, params, **kwds)
85
86
def __repr__(self):
87
return "Family of real quadratic zeta functions with discriminant <= %s"%self.max_D
88
89
def compute_low_zeros(self, D):
90
return quadratic_twist_zeros(D, self.num_zeros)
91
92
class QuadraticImaginary(LowZeros):
93
def __init__(self, num_zeros, min_D, **kwds):
94
self.min_D = min_D
95
params = fundamental_discriminant(min_D, -1)
96
super(QuadraticImaginary, self).__init__(num_zeros, params, **kwds)
97
98
def __repr__(self):
99
return "Family of quadratic imaginary zeta functions with discriminant <= %s"%self.max_D
100
101
def compute_low_zeros(self, D):
102
return quadratic_twist_zeros(D, self.num_zeros)
103
104
105
class EllCurveZeros(LowZeros):
106
def __init__(self, num_zeros, curves, **kwds):
107
# sort the curves into batches by conductor
108
d = {}
109
for E in curves:
110
N = E.conductor()
111
if d.has_key(N):
112
d[N].append(E)
113
else:
114
d[N] = [E]
115
params = list(sorted(d.keys()))
116
self.d = d
117
super(EllCurveZeros, self).__init__(num_zeros, params, **kwds)
118
119
def __repr__(self):
120
return "Family of %s elliptic curve L functions"%len(self.params)
121
122
def compute_low_zeros(self, N):
123
a = []
124
for E in self.d[N]:
125
L = Lfunction_from_elliptic_curve(E)
126
a.append(L.find_zeros_via_N(self.num_zeros))
127
num_curves = len(a)
128
return [sum(a[i][j] for i in range(num_curves))/num_curves
129
for j in range(self.num_zeros)]
130
131
class EllQuadraticTwists(LowZeros):
132
def __init__(self, num_zeros, curve, discs, number_of_coeffs=10000, **kwds):
133
self.curve = curve
134
self.number_of_coeffs = number_of_coeffs
135
params = discs
136
super(EllQuadraticTwists, self).__init__(num_zeros, params, **kwds)
137
138
def __repr__(self):
139
return "Family of %s elliptic curve L functions"%len(self.params)
140
141
def compute_low_zeros(self, D):
142
L = Lfunction_from_elliptic_curve(self.curve.quadratic_twist(D),
143
number_of_coeffs=self.number_of_coeffs)
144
return L.find_zeros_via_N(self.num_zeros)
145
146
147
def quadratic_twist_zeros(D, n, algorithm='clib'):
148
"""
149
Return imaginary parts of the first n zeros of all the Dirichlet
150
character corresponding to the quadratic field of discriminant D.
151
152
INPUT:
153
- D -- fundamental discriminant
154
- n -- number of zeros to find
155
- algorithm -- 'clib' (use C library) or 'subprocess' (open another process)
156
"""
157
if algorithm == 'clib':
158
L = Lfunction_from_character(kronecker_character(D), type="int")
159
return L.find_zeros_via_N(n)
160
elif algorithm == 'subprocess':
161
assert is_fundamental_discriminant(D)
162
cmd = "lcalc -z %s --twist-quadratic --start %s --finish %s"%(n, D, D)
163
out = os.popen(cmd).read().split()
164
return [float(out[i]) for i in range(len(out)) if i%2!=0]
165
else:
166
raise ValueError, "unknown algorithm '%s'"%algorithm
167
168
169
170
171
172
def fit_to_power_of_log(v):
173
"""
174
INPUT:
175
- v -- a list of (x,y) values, with x increasing.
176
OUTPUT:
177
- number a such that data is "approximated" by b*log(x)^a.
178
"""
179
# ALGORITHM: transform data to (log(log(x)), log(y)) and find the slope
180
# of the best line that fits this transformed data.
181
# This is the right thing to do, since if y = log(x)^a, then log(y) = a*log(log(x)).
182
from math import log, exp
183
w = [(log(log(x)), log(y)) for x,y in v]
184
a, b = least_squares_fit(w)
185
return float(a), exp(float(b))
186
187
188
def least_squares_fit(v):
189
"""
190
INPUT:
191
- v -- a list of (x,y) values that are floats
192
OUTPUT:
193
- a and b such that the line y=a*x + b is the least squares fit for the data.
194
195
All computations are done using floats.
196
"""
197
import numpy
198
x = numpy.array([a[0] for a in v])
199
y = numpy.array([a[1] for a in v])
200
A = numpy.vstack([x, numpy.ones(len(x))]).T
201
a, b = numpy.linalg.lstsq(A,y)[0]
202
return a, b
203
204
205
class XLogInv(object):
206
def __init__(self, a):
207
self.a = a
208
x = var('x')
209
self.f = (x * (log(x) - a))._fast_float_(x)
210
211
def __repr__(self):
212
return "The inverse of the function x*(log(x) - %s), for sufficiently large positive x"%self.a
213
214
def __call__(self, y):
215
return find_root(self.f - float(y), 1, 10e9)
216
217