%hide
D2 = load(DATA+'D2.sobj')
D5 = load(DATA+'D5.sobj')
D7 = load(DATA+'D7.sobj')
D13 = load(DATA+'D13.sobj')
Ds = [ D2, D5, D7, D13 ]
def inject_D(D):
from sage.misc.misc import inject_variable
for k, v in D.items():
inject_variable(k, v)
# assert H.base_ring() is k
# assert K.order(basis).is_maximal()
if False: # remains to give some ideas of which variables are injected
H = D['H']
K = D['K']
Kr = D['Kr']
k = D['k']
k_over_Kr = D['k_over_Kr']
k_over_Kr_into_k = D['k_over_Kr_into_k']
k_into_k_over_Kr = D['k_into_k_over_Kr']
k_into_C = D['k_into_C']
# for basis
basis = D['basis']
# for eta
Kr_into_Ls = D['Kr_into_Ls']
L = D['L']
K_into_L = D['K_into_L']
def my_red(a, P):
r"""Reduce a number field element modulo a degree one prime of special form."""
assert a in P.number_field()
p, alpha = P.gens()
alpha = alpha.polynomial()
d = alpha.degree()
if d == 1:
Kbar = GF(p)
alpha_in_Kbar = Kbar(-alpha(0))
else:
Kbar = GF(p^d, 'a', modulus=alpha)
alpha_in_Kbar = Kbar.gen()
pp = a.polynomial()
return pp.numerator()(alpha_in_Kbar)/Kbar(pp.denominator())
def my_mod(H, P):
r"""Reduce a hyperelliptic curve over a number field modulo a degree one prime of special form."""
L = [ my_red(a, P) for a in H.hyperelliptic_polynomials()[0] ]
return HyperellipticCurve( L[0].parent()['x']( L ) )
def my_below(P):
x = ZZ['x'].gen()
Kr_gen_in_k = k_over_Kr_into_k( k_over_Kr(Kr.gen()) )
r = my_red( Kr_gen_in_k, P )
if r.parent() == 1:
pol = x - r
Pbelow = Kr.ideal( [ P.gens()[0], pol(Kr.gen()) ] )
else:
# pol = r.polynomial().change_ring(ZZ)
Pbelow = k_into_k_over_Kr(P).relative_norm() # XXX improvements?
return Pbelow
def is_large_prime(p):
return p > 5*10^3
def is_large_prime_ideal(P):
(p, alpha) = P.gens()
return is_large_prime(ZZ(p)) or alpha.polynomial().degree() > 1
chi = lambda tt: QQ(DirichletGroup(4).gen()(tt))
def good_primes(k):
Ps_deg1 = k.primes_of_degree_one_list(10)[3:6]
Ps_large = []
p = 2^30
while p < 2^30 + 2^12 and len(Ps_large) < 3:
p = next_probable_prime(p)
rts = k.polynomial().roots(GF(p), multiplicities=False)
if len(rts) > 0:
P = k.ideal([ p, k.gen() - ZZ(rts[0]) ])
Ps_large.append(P)
Ps_deg2 = []
for p in prime_range(150, 180):
# a non-degree one prime, in two generator form
facs = [ fac for fac, e in GF(p)['t'](k.polynomial()).factor() if fac.degree() > 1 ]
if facs:
gg = choice(facs)
P = k.ideal([ p, gg.change_ring(ZZ) ])
Ps_deg2.append( P )
return (Ps_deg1, Ps_deg2, Ps_large)
def my_cm_eta(prime_of_Kr):
pp = prime_of_Kr # an ideal of Kr
eta_pp_in_L = prod( emb(pp) for emb in Kr_into_Ls )
L_over_K = L.relativize(K_into_L, L.variable_name())
L_over_K_into_L, L_into_L_over_K = L_over_K.structure()
J = L_over_K.ideal([ L_into_L_over_K(g) for g in eta_pp_in_L.gens() ])
JJ = J.ideal_below()
return JJ
# -*- sage -*-
def _frobenius_coefficient_bounds(self):
# Bound from Weil conjectures, via Kedlaya's paper:
# | a_i | <= (2g choose i) * q**(i/2)
p = self.base_ring().characteristic() # XXX higher degree
sqrtp = RR(p).sqrt()
g = self.genus()
Bs = []
for i in range(2*g + 1):
B = 2 * binomial(2*g, i) * sqrtp**i
Bs.append(ZZ(ceil(B)))
Bs.reverse()
return Bs
def _frobenius_coefficient_bound(self):
assert self.base_ring().is_finite()
p = self.base_ring().characteristic() # XXX higher degree
Bs = self._frobenius_coefficient_bounds()
M = ZZ(max(Bs))
B = M.exact_log(p)
if p**B < M:
B += 1
assert p**B >= M
# B += 2*self.genus()
return B # XXX
def frobenius_matrix(self, N=None):
from sage.schemes.hyperelliptic_curves.hypellfrob import hypellfrob
p = self.base_ring().characteristic()
f, h = self.hyperelliptic_polynomials()
if h != 0:
# need y^2 = f(x)
raise NotImplementedError
sign = 1
if not f.is_monic():
# at this time we need a monic f
c = f.leading_coefficient()
f = f / c
if c.is_square():
# solutions of $y^2 = c * f(x)$ correspond naturally to solutions of
# $(sqrt(c) y)^2 = f(x)$
pass
else:
# we'll count points on a twist and then correct by untwisting...
sign = -1
if not f.is_monic():
raise NotImplementedError
if N is None:
N = self._frobenius_coefficient_bound()
matrix_of_frobenius = hypellfrob(p, N, f)
matrix_of_frobenius = sign * matrix_of_frobenius
return matrix_of_frobenius
def cnt_wo_g(f, kbar):
if kbar is None:
kbar = Hbar.base_ring()
cnt = 1 # pt at \infty
if f.degree() % 2 == 0:
cnt = 2
for x in kbar:
c = f(x)
if c == 0:
cnt += 1
elif c.is_square():
cnt += 2
return cnt
def cnt_w_g(f, g, kbar):
if kbar is None:
kbar = Hbar.base_ring()
cnt = 1 # pt at \infty
# if f.degree() % 2 == 0:
# cnt = 2
y = kbar['y'].gen()
for x in kbar:
print x
p = y**2 + g(x)*y - f(x)
cnt += len( p.roots() )
return cnt
def cnt(Hbar, kbar=None):
if kbar is None:
kbar = Hbar.base_ring()
f, g = Hbar.hyperelliptic_polynomials()
if g == 0:
return cnt_wo_g(f, kbar)
else:
return cnt_w_g(f, g, kbar)
# cnt = 1 # pt at \infty
# for x in kbar:
# c = f(x)
# if c == 0:
# cnt += 1
# elif c.is_square():
# cnt += 2
# return cnt
def frobenius_polynomial_r(Hbar):
g = Hbar.genus()
kbar = Hbar.base_ring()
assert kbar.is_finite()
q = kbar.order()
e = SFAElementary(QQ)
p = SFAPower(QQ)
P = [0] * (g+1)
P[1] = q + 1 - cnt(Hbar)
for i in range(2, g+1):
# N_k = q^k + 1 - \sum_{i=1}^{2 g} \alpha_i^k .
kbar2 = GF(ZZ(kbar.order())**i, 'a')
P[i] = q**i + 1 - cnt(Hbar, kbar2)
A = [0] * (2*g+1)
A[0] = 1
for i in range(1, g+1):
f = p(e([i])) # express coefficient of frob poly in terms of power basis
A[i] = sum( coeff*prod(P[mon] for mon in mons) for mons, coeff in f.monomial_coefficients().items())
for i in range(0, g+1):
# a_{2 g - i} = q^{g - i} a_i for i = 0, \ldots, g.
A[2*g - i] = q**(g - i) * A[i]
return QQ['x'](ZZ['x'](A).reverse()(-ZZ['x'].gen()))
def frobenius_polynomial(self, N=None):
if N is None:
N = self._frobenius_coefficient_bound()
try:
p = self.frobenius_matrix(N=N).charpoly()
except ValueError:
return frobenius_polynomial_r(self)
try:
Bs = self._frobenius_coefficient_bounds()
cs = []
for c, B in zip(p.coeffs(), Bs):
d = ZZ(c)
if d > B:
d = -ZZ(-c)
assert abs(d) <= B
cs.append(d)
return ZZ['x'](cs)
except:
return self.frobenius_polynomial(2*N) # otherwise increase precision
def frobenius_order(self):
f = self.frobenius_polynomial().factor()[0][0]
return ZZ.extension(f, names='phi')
def frobenius(self):
return self.frobenius_order().fraction_field().gen()
def _reduce(self, P):
return self.change_ring(self.base_ring().residue_field(P))
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic._frobenius_coefficient_bounds = _frobenius_coefficient_bounds
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic._frobenius_coefficient_bound = _frobenius_coefficient_bound
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.frobenius_matrix = frobenius_matrix
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.frobenius_polynomial = frobenius_polynomial
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.frobenius_order = frobenius_order
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.frobenius = frobenius
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.mod = _reduce
# sage: x = QQ['x'].0
# sage: f = x^7 + 143*x^6 + 41*x^5 + 58*x^4 + 61*x^3 + 52*x^2 + 164*x + 64
# sage: p = 211
# sage: HyperellipticCurve(GF(211)['x']('x^7 + 143*x^6 + 41*x^5 + 58*x^4 + 61*x^3 + 52*x^2 + 164*x + 64')).frobenius_polynomial()
def short_weierstrass_model(self, complete=True):
r"""
Return a Weierstrass model of the form $y^2 = f(x)$.
If \code{complete} is \code{True}, depress the resulting polynomial $f(x)$
so that if $d = \deg(f)$ it has no term $x^{d-1}$. This requires that the
characteristic of the ground field does not divide $\deg f$.
EXAMPLES:
sage: x = polygen(QQ)
sage: H = HyperellipticCurve(x^7 + x^6 + 2, x); H
Hyperelliptic Curve over Rational Field defined by y^2 + x*y = x^7 + x^6 + 2
sage: H.short_weierstrass_model()
Hyperelliptic Curve over Rational Field defined by y^2 = x^7 - 3/7*x^5 + 10/49*x^4 - 15/343*x^3 + 2449/9604*x^2 - 5/16807*x + 1647092/823543
sage: Hbar = H.change_ring(GF(2))
sage: Hbar.short_weierstrass_model()
Traceback (most recent call last):
...
ValueError: short_weierstrass_model(): no short model for Hyperelliptic Curve over Finite Field of size 7 defined by y^2 + x*y = x^7 + x^6 + 2 (characteristic is 2)
sage: Hbar = H.change_ring(GF(7))
sage: Hbar.short_weierstrass_model()
Traceback (most recent call last):
...
ValueError: short_weierstrass_model(): no short model for Hyperelliptic Curve over Finite Field of size 7 defined by y^2 + x*y = x^7 + x^6 + 2 (characteristic is 7)
sage: Hbar.short_weierstrass_model(complete=False)
Hyperelliptic Curve over Finite Field of size 7 defined by y^2 = x^7 + x^6 + 2*x^2 + 2
"""
f, h = self.hyperelliptic_polynomials()
K = self.base_ring()
if K.characteristic() == 2:
raise ValueError, "short_weierstrass_model(): no short model for %s (characteristic is %s)" % (self, K.characteristic())
new_f = f + h*h/4 # always complete the square
if complete:
d = ZZ(f.degree())
if ZZ(K.characteristic()).divides(d):
raise ValueError, "short_weierstrass_model(): no short model for %s (characteristic is %s)" % (self, K.characteristic())
x = f.parent().gen()
new_f = new_f(x - (f[d-1]/d)) # complete the $d - 1$ power
return HyperellipticCurve(new_f)
def quadratic_twist(self, d=None):
r"""
XXX characteristics, d == 0
"""
if d is None:
d = self.base_ring().quadratic_nonresidue()
f, _ = self.short_weierstrass_model().hyperelliptic_polynomials()
n = f.degree()
fp = f.parent()([ f[i]*d**(n-i) for i in range(0, n+1) ])
return HyperellipticCurve(fp, 0)
def apply_morphism(self, phi):
f, h = self.hyperelliptic_polynomials()
P = f.parent().change_ring(phi.codomain())
fp = P([ phi(c) for c in f.coeffs() ])
hp = P([ phi(c) for c in h.coeffs() ])
return HyperellipticCurve(fp, hp)
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.short_weierstrass_model = short_weierstrass_model
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.quadratic_twist = quadratic_twist
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.apply_morphism = apply_morphism
def random_points_on_curve(C, numpts=10):
pts = []
K = C.base_ring()
while len(pts) < numpts:
x = K.random_element()
try:
pt = C.lift_x(x)
except ValueError:
continue
pts.append(pt)
return pts
def random_points_on_jacobian(J, numpts=10):
C = J.curve()
pts = random_points_on_curve(C)
g = C.genus()
divs = []
for ptsg in Subsets(pts, g):
div = sum(J(pt) for pt in list(ptsg))
divs.append(div)
if len(divs) > numpts:
break
return divs
def random_points_on_jacobian_of_curve(C, numpts=10):
J = C.jacobian()(C.base_ring())
return random_points_on_jacobian(J, numpts)
def is_multiple_of_order(self, N, numpts=10):
r"""
Return True if N is a multiple of the order of this curve.
"""
N = ZZ(N)
pts = random_points_on_jacobian(self, numpts)
return all((N*pt).is_zero() for pt in pts)
sage.schemes.hyperelliptic_curves.jacobian_homset.JacobianHomset_divisor_classes.is_multiple_of_order = is_multiple_of_order
def is_multiple_of_order_over(self, R, N, numpts=3):
return self.change_ring(R).jacobian().point_set(R).is_multiple_of_order(N, numpts=numpts)
def is_multiple_of_order_2(self, N, numpts=3):
R = self.base_ring()
assert R.is_finite()
return self.jacobian().point_set(R).is_multiple_of_order(N, numpts=numpts)
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.is_multiple_of_order_over = is_multiple_of_order_over
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic.is_multiple_of_order = is_multiple_of_order_2
def magma_init(self, magma):
"""
Internal function. Returns a string to initialize this
elliptic curve in the Magma subsystem.
EXAMPLES:
sage: E = EllipticCurve(QQ, [1, 1]); E
Elliptic Curve defined by y^2 = x^3 + x +1 over Rational Field
sage: E._magma_init_(magma) # optional - magma
'EllipticCurve([0,0,0,1,1])'
sage: magma(E) # optional - magma
Elliptic Curve defined by y^2 = x^3 + x + 1 over Rational Field
sage: magma(E)._sage_() # optional - magma
Elliptic Curve defined by y^2 = x^3 + x +1 over Rational Field
sage: E == magma(E)._sage_() # optional - magma
True
sage: H = HyperellipticCurve(QQ['x']('x^5 + 1'), QQ['x']('x')); H
Hyperelliptic Curve over Rational Field defined by y^2 = x^5 + 1
sage: magma(H) # optional - magma
Hyperelliptic Curve defined by y^2 + x*y = x^5 + 1 over Rational Field
sage: magma(H)._sage_() # optional - magma
Hyperelliptic Curve over Rational Field defined by y^2 + x*y = x^5 + 1
sage: H == magma(H)._sage_() # optional - magma
True
"""
return 'HyperellipticCurve(%s)' % ', '.join([x._magma_init_(magma) for x in self.hyperelliptic_polynomials()])
sage.schemes.hyperelliptic_curves.hyperelliptic_generic.HyperellipticCurve_generic._magma_init_ = magma_init
def items_for(list_of_dicts):
L = []
for D in list_of_dicts:
K = D['K']
L.append( (D, 'CM by %s (%s, class number %s)'
% (K.polynomial(), 'Cyclic Galois' if K.is_galois() else 'Dihedral non-Galois', K.class_number())) )
return L
def cap(x, bnd=1e-10):
if isinstance(x.parent(), sage.rings.polynomial.multi_polynomial_ring_generic.MPolynomialRing_generic):
s = 0
for m in x.monomials():
c = cap(x.monomial_coefficient(m), bnd=bnd)
if c != 0:
s += m*c
return s
if isinstance(x.parent(), sage.rings.polynomial.polynomial_ring.PolynomialRing_commutative):
return x.parent()( [ cap(c, bnd=bnd) for c in x ] )
if x.parent() is CDF:
r, i = x.real(), x.imag()
elif hasattr(x, 'apply_map'):
return x.apply_map(lambda u: cap(u, bnd=bnd))
else:
r, i = list(x)
if abs(r) < bnd:
r = 0
if abs(i) < bnd:
i = 0
if x.parent() is CDF:
return CDF(r, i)
return x.parent()([r, i])
def test_one(P, Pbelow):
if True:
Hp = my_mod(H, P)
etaPbelow = my_cm_eta(Pbelow)
(fr, ) = etaPbelow.gens_reduced()
lamb = choice([-1, 1])*fr # little extra randomness
if not is_large_prime_ideal(P):
f1 = Hp.frobenius_polynomial()
else:
f1 = lamb.charpoly()
if not Hp.is_multiple_of_order( f1(1) ):
f1 = f1(-f1.parent().gen())
assert not Hp.is_multiple_of_order( f1(-1) )
assert Hp.is_multiple_of_order( f1(1) )
# print f1.factor()
# check that my theorem is working :)
v = matrix(map(vector, basis)).solve_left(vector(lamb))
a, b, c, d = v
epsilon = chi((c + d) % 4)
f2 = (epsilon * lamb).charpoly()
good = (f2 == f1)
if good:
html(r"<table style='background-color:LightGreen'>")
else:
html(r"<table style='background-color:LightPink'>")
if True:
html("<tr>")
html(r"<th>$\text{degree of }\mathcal{P}$</th>")
html(r"<td>$%s$</td>" % P.gens()[1].polynomial().degree())
html("</tr>")
html("<tr>")
AN = P.absolute_norm()
html(r"<th>$\mathrm{N}_{k/\mathbf{Q}}(\mathcal{P})$</th>")
html('<td>$%s = %s$</td>' % (latex(AN), latex(AN.factor())))
html("</tr>")
html("<tr>")
html(r"<th>$\mathrm{N}_{k/K'}(\mathcal{P})$</th>")
html('<td>$%s$</td>' % latex(Pbelow))
html("</tr>")
html("<tr>")
html(r"<th>$\lambda = \eta(\mathrm{N}_{k/K'}(\mathcal{P}))$</th>")
html(r"<td>$%s</td>$" % latex(K.ideal(lamb)))
html("</tr>")
html("<tr>")
html(r"<th>$\lambda = a z + b z w' + c w + d$</th>")
html(r"<td>$(%s) \cdot z + (%s) \cdot z w' + (%s) \cdot w + (%s)$</td>" % (a, b, c, d))
html("</tr>")
html("<tr>")
html(r"<th>$\epsilon$</th>")
html(r"<td>$%s$</td>" % epsilon)
html("</tr>")
html("<tr>")
html(r"<th>$N = \mathrm{N}_{K/\mathbf{Q}}(1 - \epsilon \cdot \lambda)$</th>")
html(r"<td>$%s$</td>" % f2(1))
html("</tr>")
html("<tr>")
html(r"<th>$H \pmod{\mathcal{P}}$</th>")
html(r"<td>$%s$</td>" % latex(Hp.hyperelliptic_polynomials()[0]))
html("</tr>")
html("<tr>")
html(r"<th>$\# \mathrm{Jac}(H)(O_k/\mathcal{P})$</th>")
html(r"<td>$%s$</td>" % f1(1))
html("</tr>")
html(r"</table>")
def show_test_summary():
html("<table>")
html("<tr><td>")
html("$K = %s$ with $%s \mapsto %s \in \mathbf{C}$" % (latex(K), latex(K.gen()), latex(cap(CDF(K_into_QQbar(K.gen()))))))
html("</td></tr>")
html("<tr><td>")
K0 = K.subfields(2)[0][0]
html("$K_0 = %s$ with $D_{K_0} = %s$" % (latex(K0), K0.disc()))
html("</td></tr>")
html("<tr><td>")
html("$O_K = (%s) O_{K_0} + O_{K_0}$" % latex(basis[0]))
html("</td></tr>")
html("<tr><td>")
html("this choice of $z$ leads to $z_0 \sim %s \in \mathcal{H}_2$" % latex(z_0.change_ring(CDF).apply_map(cap)))
html("</td></tr>")
html("<tr><td>")
html("$k = %s$" % latex(k.change_names('b')))
html("</td></tr>")
html("<tr><td>")
html("with $%s \mapsto %s \in \mathbf{C}$" % (latex(k.change_names('b').gen()), latex(cap(CDF(k_into_QQbar(k.gen()))))))
html("</td></tr>")
html("<tr><td>")
html("$[k : \mathbf{Q}] = %s$ and $[k : K'] = %s$" % (latex(k.degree()), latex(k.degree() // Kr.degree())))
html("</td></tr>")
html("<tr><td>")
html("$H \sim %s$" % latex(CDF['x'](map(cap, map(CDF, map(k_into_QQbar, H.hyperelliptic_polynomials()[0]))))))
html("</td></tr>")
@interact
def _(example_dict = selector(items_for(Ds), label="Example"),
primes = input_box(default = '', label="Rational primes equal or after", type=str),
degrees = input_box(default = '1', label="of degrees", type=str),
):
inject_D(example_dict)
degrees = set(map(abs, vector(ZZ, degrees.split(','))))
Ps1, Ps2, Psl = good_primes(k)
html("<p>Some rational primes below small degree one primes are:")
html(', '.join([ "%s" % P.gens()[0] for P in Ps1 ]) )
html("</p>")
html("<p>Some rational primes below small degree two or larger primes are:")
html(', '.join([ "%s" % P.absolute_norm().factor()[0][0] for P in Ps2 ]) )
html("</p>")
html("<p>Some rational primes below large degree one primes are:")
html(', '.join([ "%s" % P.gens()[0] for P in Psl ]) )
html("</p>")
html("<table style='border:1px solid black'>")
try:
ps = vector(ZZ, primes.split(','))
except:
ps = []
for p in ps:
if not p:
continue
if not is_pseudoprime(p):
p = next_probable_prime(p)
html(r"<tr>")
html(r"<td>p = $%s$</td>" % p)
# a prime, in two generator form
facs = [ fac for fac, e in GF(p)['t'](k.polynomial()).factor() if fac.degree() in degrees ]
if not facs:
html(r"<td style='background-color:red'>There is no prime above $%s$ with degree in $%s$</td>" % (p, latex(vector(sorted(degrees))).replace("(", r"\{").replace(")", r"\}")))
html("</tr>") # close row
continue
afac = choice(facs)
P = k.ideal([ p, afac.change_ring(ZZ) ])
try:
Pbelow = my_below(P)
except ZeroDivisionError:
html(r"<td style='background-color:red'><td>Please try a prime larger than %s</td>" % p)
html("</tr>") # close row
continue
html("<td>")
test_one(P, Pbelow)
html("</td>")
html("</tr>") # close row
html('</table>')
show_test_summary()
|
|
Click to the left again to hide and once more to show the dynamic interactive window
|