Skip to content

Commit

Permalink
allow supplying a value of q for special_supersingular_curve()
Browse files Browse the repository at this point in the history
  • Loading branch information
yyyyx4 committed Sep 16, 2024
1 parent a880b3f commit 72b99f9
Showing 1 changed file with 120 additions and 54 deletions.
174 changes: 120 additions & 54 deletions src/sage/schemes/elliptic_curves/ell_finite_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -2565,10 +2565,12 @@ def is_j_supersingular(j, proof=True):
return E.trace_of_frobenius() % p == 0


def special_supersingular_curve(F, *, endomorphism=False):
def special_supersingular_curve(F, q=None, *, endomorphism=False):
r"""
Given a finite field ``F``, construct a "special" supersingular
elliptic curve `E` defined over ``F``.
Given a finite field ``F`` of characteristic `p`, and optionally
a positive integer `q` such that the Hilbert conductor of `-q`
and `-p` equals `p`, construct a "special" supersingular elliptic
curve `E` defined over ``F``.
Such a curve
Expand All @@ -2577,21 +2579,32 @@ def special_supersingular_curve(F, *, endomorphism=False):
- has group structure `E(\mathbb F_p) \cong \ZZ/(p+1)` and
`E(\mathbb F_{p^2}) \cong \ZZ/(p+1) \times \ZZ/(p+1)`;
- has an endomorphism `\vartheta` of small degree `q` that
- has an endomorphism `\vartheta` of degree `q` that
anticommutes with the `\mathbb F_p`-Frobenius on `E`.
(The significance of `\vartheta` is that any such endomorphism,
together with the `\mathbb F_p`-Frobenius, generates the endomorphism
algebra `\mathrm{End}(E) \otimes \QQ`.)
The complexity grows exponentially in `\log(q)`. Automatically
chosen values of `q` lie in `O((\log p)^2)` assuming GRH.
INPUT:
- ``F`` -- finite field `\mathbb F_{p^r}`;
- ``F`` -- finite field `\mathbb F_{p^r}`
- ``q`` -- positive integer (optional, default ``None``)
- ``endomorphism`` -- boolean (default: ``False``); when set to ``True``,
it is required that `2 \mid r`, and the function then additionally
returns `\vartheta`
.. WARNING::
Due to :issue:`38481`, calling this function with a value of `q`
larger than approximately `p/4` may currently fail. This failure
will not occur for automatically chosen values of `q`.
EXAMPLES::
sage: special_supersingular_curve(GF(1013^2), endomorphism=True)
Expand All @@ -2604,8 +2617,8 @@ def special_supersingular_curve(F, *, endomorphism=False):
Via: (u,r,s,t) = (389*z2 + 241, 0, 0, 0))
sage: special_supersingular_curve(GF(1021^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2,
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2)
(Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2,
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2 to Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2)
sage: special_supersingular_curve(GF(1031^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1031^2,
Expand All @@ -2630,6 +2643,20 @@ def special_supersingular_curve(F, *, endomorphism=False):
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1051^2
Via: (u,r,s,t) = (922*z2 + 129, 0, 0, 0))
We can also supply a suitable value of `q` ourselves::
sage: special_supersingular_curve(GF(1019), q=99)
Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field of size 1019
sage: special_supersingular_curve(GF(1019^2), q=99, endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2,
Isogeny of degree 99 from Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2 to Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2)
sage: special_supersingular_curve(GF(1013), q=99)
Traceback (most recent call last):
...
ValueError: invalid choice of q
TESTS::
sage: p = random_prime(1000)
Expand Down Expand Up @@ -2678,6 +2705,35 @@ def special_supersingular_curve(F, *, endomorphism=False):
sage: pi * endo == -endo * pi
True
Also try it when `q` is given:
sage: p = random_prime(300, lbound=10)
sage: k = ZZ(randrange(1, 5))
sage: while True:
....: q = randrange(1, p//4) # upper bound p//4 is a workaround for #38481
....: if QuaternionAlgebra(-q, -p).discriminant() == p:
....: break
sage: E = special_supersingular_curve(GF((p, k)), q)
sage: E.is_supersingular()
True
sage: F.<t> = GF((p, 2*k))
sage: E, endo = special_supersingular_curve(F, q, endomorphism=True)
sage: E.is_supersingular()
True
sage: E.j_invariant() in GF(p)
True
sage: endo.domain() is endo.codomain() is E
True
sage: endo.degree() == q
True
sage: endo.trace()
0
sage: pi = E.frobenius_isogeny()
sage: pi.codomain() is pi.domain() is E
True
sage: pi * endo == -endo * pi
True
.. NOTE::
This function makes no guarantees about the distribution of
Expand All @@ -2694,42 +2750,49 @@ def special_supersingular_curve(F, *, endomorphism=False):
if endomorphism and deg % 2:
raise ValueError('endomorphism was requested but is not defined over given field')

E = None
if q is not None:
from sage.arith.misc import hilbert_conductor
if p.divides(q) or hilbert_conductor(-q, -p) != p:
raise ValueError('invalid choice of q')

# first find the degree q of our special endomorphism
if p == 2:
q = 3
E = EllipticCurve(F, [0,0,1,0,0])

elif p % 4 == 3:
q = 1
E = EllipticCurve(F, [1,0])

elif p % 3 == 2:
q = 3
E = EllipticCurve(F, [0,1])

elif p % 8 == 5:
q = 2
E = EllipticCurve(F, [-4320, 96768])

else:
from sage.arith.misc import legendre_symbol
for q in map(ZZ, range(3,p,4)):
if not q.is_prime():
continue
if legendre_symbol(-q, p) == -1:
break
if q is None:
if p == 2:
q = 3
elif p % 4 == 3:
q = 1
elif p % 3 == 2:
q = 3
elif p % 8 == 5:
q = 2
else:
assert False # should never happen
from sage.arith.misc import legendre_symbol
for q in map(ZZ, range(3,p,4)):
if not q.is_prime():
continue
if legendre_symbol(-q, p) == -1:
break
else: # should never happen
assert False, 'bug in special_supersingular_curve()'
q = ZZ(q)

if E is None:
from sage.arith.misc import fundamental_discriminant
from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial
H = hilbert_class_polynomial(fundamental_discriminant(-q))
j = H.change_ring(GF(p)).any_root()
from sage.arith.misc import fundamental_discriminant
from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial
H = hilbert_class_polynomial(fundamental_discriminant(-q))
j = H.change_ring(GF(p)).any_root()
if j.is_zero():
if p == 2:
ainvs = [0,0,1,0,0]
elif p == 3:
ainvs = [1,0]
else:
ainvs = [0,1]
elif j == 1728:
ainvs = [1,0]
else:
a = 27 * j / (4 * (1728-j))
E = EllipticCurve(F, [a,-a])
ainvs = [a,-a]
E = EllipticCurve(F, ainvs)

if ZZ(2).divides(deg):
k = deg//2
Expand All @@ -2740,23 +2803,26 @@ def special_supersingular_curve(F, *, endomorphism=False):
if not endomorphism:
return E

if q == 1 or p <= 13:
if q == 1:
endos = E.automorphisms()
else:
endos = (iso*phi for phi in E.isogenies_prime_degree(q)
for iso in phi.codomain().isomorphisms(E))
endo = next(endo for endo in endos if endo.trace().is_zero())

if q.is_one():
endo = next(auto for auto in E.automorphisms() if auto.trace().is_zero())
else:
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
iso = WeierstrassIsomorphism(None, (F(-q).sqrt(),0,0,0), E)
if q == 3 and E.a_invariants() == (0,0,0,0,1):
# workaround for #21883
endo = E.isogeny(E(0,1))
else:
endo = E.isogeny(None, iso.domain(), degree=q)
endo = iso * endo
iso = E.isomorphism(F(-q).sqrt(), is_codomain=True)
try:
endo = iso * E.isogeny(None, iso.domain(), degree=q)
except (NotImplementedError, ValueError): #FIXME catching ValueError here is a workaround for #38481
#FIXME this code could be simplified/optimized after #37388 and/or #35949
def _isogs(E, d):
if d.is_one():
yield E.identity_morphism()
return
l = d.prime_factors()[-1]
for phi in E.isogenies_prime_degree(l):
for psi in _isogs(phi.codomain(), d//l):
yield psi * phi
endos = (iso*phi for phi in _isogs(E, q) for iso in phi.codomain().isomorphisms(E))
# endos = (iso*phi for phi in E.isogenies_degree(q)
# for iso in phi.codomain().isomorphisms(E))
endo = next(endo for endo in endos if endo.trace().is_zero())

endo._degree = ZZ(q)
endo.trace.set_cache(ZZ.zero())
Expand Down

0 comments on commit 72b99f9

Please sign in to comment.