Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
AndrewVSutherland
GitHub Repository: AndrewVSutherland/lmfdb
Path: blob/main/scripts/genus2_curves/g2LocSolv.py
1127 views
1
# -*- coding: utf-8 -*-
2
3
from sage.all import ZZ, sqrt, Set, valuation, kronecker_symbol, GF, floor, Mod, PolynomialRing, next_prime
4
5
def IsSquareInQp(x, p):
6
if x==0:
7
return True
8
# Check parity of valuation
9
v=valuation(x,p)
10
if v%2:
11
return False
12
# Renormalise to get a unit
13
x//=p**v
14
# Reduce mod p and conclude
15
if p==2:
16
return Mod(x,8)==1
17
else:
18
return kronecker_symbol(x,p)==1
19
20
21
def HasFinitePointAt(F, p, c):
22
"""
23
Tests whether y²=c*F(x) has a finite Qp-point with x and y both in Zp,
24
assuming that deg F = 6 and F integral
25
"""
26
Fp = GF(p)
27
if p > 2 and Fp(F.leading_coefficient()): # Tests to accelerate case of large p
28
# Write F(x) = c*lc(F)*R(x)²*S(x) mod p, R as big as possible
29
R = F.parent()(1)
30
S = F.parent()(1)
31
for X in (F.base_extend(Fp)/Fp(F.leading_coefficient())).squarefree_decomposition():
32
[G,v] = X # Term of the form G(x)^v in the factorisation of F(x) mod p
33
S *= G**(v%2)
34
R *= G**(v//2)
35
r = R.degree()
36
s = S.degree()
37
if s == 0: # F(x) = C*R(x)² mod p, C = c*lc(F) constant
38
if IsSquareInQp(c*F.leading_coefficient(),p):
39
if p>r:# Then there is x s.t. R(x) nonzero and C is a square
40
return True
41
#else: # C nonsquare, so if we have a Zp-point it must have R(x) = 0 mod p
42
#Z = R.roots()
43
##TODO
44
else:
45
g = S.degree()//2 - 1 # genus of the curve y²=C*S(x)
46
B = floor(p-1-2*g*sqrt(p)) # lower bound on number of points on y²=C*S(x) not at infty
47
if B > r+s: # Then there is a point on y²=C*S(x) not at infty and with R(x) and S(x) nonzero
48
return True
49
#Now p is small, we can run a naive search
50
q = p
51
Z = []
52
if p == 2:
53
q = 8
54
for x in range(q):
55
y = F(x)
56
# If we have found a root, save it (take care of the case p=2!)
57
if (p > 2 or x < 2) and Fp(y) == 0:
58
Z.append(x)
59
# If we have a mod p point with y nonzero mod p, then it lifts, so we're done
60
if IsSquareInQp(c*y, p):
61
return True
62
#So now, if we have a Qp-point, then its y-coordinate must be 0 mod p
63
t = F.variables()[0]
64
for z in Z:
65
F1 = F(z+p*t)
66
c1 = F1.content()
67
F1 //= c1
68
if HasFinitePointAt(F1,p,(c*c1).squarefree_part()):
69
return True
70
return False
71
72
def IsSolubleAt(F, p):
73
"""
74
Tests whether y² = F(x) has a Qp-point, assuming F integral of degree 6 and lc(F) squarefree
75
"""
76
lc = F.leading_coefficient()
77
if lc % p:
78
# The leading coeff. a6 is not 0 mod p
79
# Are the points at Infty defined over Qp ?
80
if IsSquareInQp(lc,p):
81
return True
82
# If we have a point (x,y) with v_p(x) = -A, then v_p(f(x)) = v_p(a6*x^6) = -6A so v_p(y) = -3A
83
# So we have x = x'/p^A, y = y'/p^3A, with x' and y' p-adic units
84
# Renormalise : y'² = a_d x'^6 + a5 p^A x'^5 + a4 p^2A x'^4 + ...
85
if p > 2:
86
# Then a6 must be a square mod p, hence a square in Qp, contradiction
87
# So x and y must be in Zp
88
return HasFinitePointAt(F, p, 1)
89
else:
90
# x'6 = y'² = 1 mod 8, so if A >= 3, then a6 = 1 mod 8, contradiction. So A <= 2, renormalise.
91
t = F.variables()[0]
92
Zx = PolynomialRing(ZZ,'x')
93
return HasFinitePointAt(Zx(4**6*F(t/4)),2,1)
94
else:
95
# p || a6
96
# In particular the points at Infty are not defined over Qp
97
# Let us try points over Zp first
98
if HasFinitePointAt(F,p,1):
99
return True
100
# Now, if we had a point with v_p(x) = -A, then v_p(a6 x^6) = 1-6A whereas the other terms have v_p >= -5A
101
# So if A >= 2, then 1-6A dominates, so v_p(f(x))=1-6A is odd, contradiction.
102
# So A=1, and there must be cancellation mod p to prevent v_p(f(x)) = 5
103
# --> x = -a5/a6 + O(p^0), and v_p(y) >= -2
104
# Just renormalise
105
a5 = F[F.degree()-1]
106
if a5 % p == 0:
107
return False
108
t = F.variables()[0]
109
Zx = PolynomialRing(ZZ,'x')
110
x0 = -a5/lc # v_p(x0) = -1, mustr have x = x0 + O(p^0)
111
x0 = ((p*x0)%p)/p # Clear non-p-part of denominator
112
return HasFinitePointAt(Zx(p**4*F(x0+t)),p,1)
113
114
115
def InsolublePlaces(f, h=0):
116
"""
117
List of primes at which the curve y²+h(x)*y=f(x) is not soluble
118
119
Assumes f and h have integer coefficients
120
"""
121
S = [] # List of primes at which not soluble
122
# Get eqn of the form y²=F(x)
123
F = f
124
if h:
125
F = 4*f + h**2
126
# Do we have a rational point an Infty ?
127
if F.degree()%2 or F.leading_coefficient().is_square():
128
return []
129
# Treat case of RR
130
if F.leading_coefficient() < 0 and F.number_of_real_roots() == 0:
131
S.append(0)
132
# Remove squares from lc(F)
133
d = F.degree()
134
lc = F.leading_coefficient()
135
t = F.variables()[0]
136
a = lc.squarefree_part() # lc/a is a square
137
a = lc//a
138
Zx = PolynomialRing(ZZ,'x')
139
F = Zx(a**(d-1) * F(t/a))
140
# Find primes of bad reduction for our model
141
D = F.disc()
142
P = Set(D.prime_divisors())
143
# Add primes at which Weil bounds do not guarantee a mod p point
144
g = (d-2)//2
145
Weil = []
146
p = 2
147
while (p+1)**2 <= 4 * g**2 * p:
148
Weil.append(p)
149
p = next_prime(p)
150
P = P.union(Set(Weil))
151
# Test for solubility
152
for p in P:
153
if not IsSolubleAt(F,p):
154
S.append(p)
155
S.sort()
156
return S
157
158
159
## The next function is meant to be used with the "rewrite_collection" script". It adds the 'insoluble_places' entry to its argument.
160
161
def Process_One(c):
162
Zx = PolynomialRing(ZZ,'x')
163
#label = str(c['label'])
164
#print "Updating",label
165
eqn = c['eqn']
166
f,h = eqn.split("],[")
167
f = Zx([int(x) for x in f[2:].split(",")])
168
if h == "]]":
169
h = Zx(0)
170
else:
171
h = Zx([int(x) for x in h[:-2].split(",")])
172
P = InsolublePlaces(f,h)
173
P = [int(p) for p in P]
174
c['non_solvable_places'] = P
175
# Former name, remove it
176
if 'insoluble_places' in c:
177
c.pop('insoluble_places')
178
return c
179
180