Skip to content

Commit b8bae02

Browse files
author
Release Manager
committed
gh-38483: allow supplying a value of q for special_supersingular_curve() This is a follow-up to #36665: There, a CM endomorphism is chosen automatically to construct a "special" supersingular elliptic curve. In some applications, it can be useful to explicitly supply the desired degree of such an endomorphism to Bröker's algorithm, which is made possible by this patch. Note that some of the subroutines used in the new code paths are relatively fragile, which can cause the functionality added here to fail for some inputs, but this has been documented in the appropriate places. URL: #38483 Reported by: Lorenz Panny Reviewer(s): Giacomo Pope, Lorenz Panny
2 parents e3cc29d + b2cdaa0 commit b8bae02

File tree

1 file changed

+120
-54
lines changed

1 file changed

+120
-54
lines changed

src/sage/schemes/elliptic_curves/ell_finite_field.py

Lines changed: 120 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,10 +2565,12 @@ def is_j_supersingular(j, proof=True):
25652565
return E.trace_of_frobenius() % p == 0
25662566

25672567

2568-
def special_supersingular_curve(F, *, endomorphism=False):
2568+
def special_supersingular_curve(F, q=None, *, endomorphism=False):
25692569
r"""
2570-
Given a finite field ``F``, construct a "special" supersingular
2571-
elliptic curve `E` defined over ``F``.
2570+
Given a finite field ``F`` of characteristic `p`, and optionally
2571+
a positive integer `q` such that the Hilbert conductor of `-q`
2572+
and `-p` equals `p`, construct a "special" supersingular elliptic
2573+
curve `E` defined over ``F``.
25722574
25732575
Such a curve
25742576
@@ -2577,21 +2579,32 @@ def special_supersingular_curve(F, *, endomorphism=False):
25772579
- has group structure `E(\mathbb F_p) \cong \ZZ/(p+1)` and
25782580
`E(\mathbb F_{p^2}) \cong \ZZ/(p+1) \times \ZZ/(p+1)`;
25792581
2580-
- has an endomorphism `\vartheta` of small degree `q` that
2582+
- has an endomorphism `\vartheta` of degree `q` that
25812583
anticommutes with the `\mathbb F_p`-Frobenius on `E`.
25822584
25832585
(The significance of `\vartheta` is that any such endomorphism,
25842586
together with the `\mathbb F_p`-Frobenius, generates the endomorphism
25852587
algebra `\mathrm{End}(E) \otimes \QQ`.)
25862588
2589+
The complexity grows exponentially in `\log(q)`. Automatically
2590+
chosen values of `q` lie in `O((\log p)^2)` assuming GRH.
2591+
25872592
INPUT:
25882593
2589-
- ``F`` -- finite field `\mathbb F_{p^r}`;
2594+
- ``F`` -- finite field `\mathbb F_{p^r}`
2595+
2596+
- ``q`` -- positive integer (optional, default ``None``)
25902597
25912598
- ``endomorphism`` -- boolean (default: ``False``); when set to ``True``,
25922599
it is required that `2 \mid r`, and the function then additionally
25932600
returns `\vartheta`
25942601
2602+
.. WARNING::
2603+
2604+
Due to :issue:`38481`, calling this function with a value of `q`
2605+
larger than approximately `p/4` may currently fail. This failure
2606+
will not occur for automatically chosen values of `q`.
2607+
25952608
EXAMPLES::
25962609
25972610
sage: special_supersingular_curve(GF(1013^2), endomorphism=True)
@@ -2604,8 +2617,8 @@ def special_supersingular_curve(F, *, endomorphism=False):
26042617
Via: (u,r,s,t) = (389*z2 + 241, 0, 0, 0))
26052618
26062619
sage: special_supersingular_curve(GF(1021^2), endomorphism=True)
2607-
(Elliptic Curve defined by y^2 = x^3 + 785*x + 794 over Finite Field in z2 of size 1021^2,
2608-
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)
2620+
(Elliptic Curve defined by y^2 = x^3 + 791*x + 230 over Finite Field in z2 of size 1021^2,
2621+
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)
26092622
26102623
sage: special_supersingular_curve(GF(1031^2), endomorphism=True)
26112624
(Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1031^2,
@@ -2630,6 +2643,20 @@ def special_supersingular_curve(F, *, endomorphism=False):
26302643
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1051^2
26312644
Via: (u,r,s,t) = (922*z2 + 129, 0, 0, 0))
26322645
2646+
We can also supply a suitable value of `q` ourselves::
2647+
2648+
sage: special_supersingular_curve(GF(1019), q=99)
2649+
Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field of size 1019
2650+
2651+
sage: special_supersingular_curve(GF(1019^2), q=99, endomorphism=True)
2652+
(Elliptic Curve defined by y^2 = x^3 + 211*x + 808 over Finite Field in z2 of size 1019^2,
2653+
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)
2654+
2655+
sage: special_supersingular_curve(GF(1013), q=99)
2656+
Traceback (most recent call last):
2657+
...
2658+
ValueError: invalid choice of q
2659+
26332660
TESTS::
26342661
26352662
sage: p = random_prime(1000)
@@ -2678,6 +2705,35 @@ def special_supersingular_curve(F, *, endomorphism=False):
26782705
sage: pi * endo == -endo * pi
26792706
True
26802707
2708+
Also try it when `q` is given:
2709+
2710+
sage: p = random_prime(300, lbound=10)
2711+
sage: k = ZZ(randrange(1, 5))
2712+
sage: while True:
2713+
....: q = randrange(1, p//4) # upper bound p//4 is a workaround for #38481
2714+
....: if QuaternionAlgebra(-q, -p).discriminant() == p:
2715+
....: break
2716+
sage: E = special_supersingular_curve(GF((p, k)), q)
2717+
sage: E.is_supersingular()
2718+
True
2719+
sage: F.<t> = GF((p, 2*k))
2720+
sage: E, endo = special_supersingular_curve(F, q, endomorphism=True)
2721+
sage: E.is_supersingular()
2722+
True
2723+
sage: E.j_invariant() in GF(p)
2724+
True
2725+
sage: endo.domain() is endo.codomain() is E
2726+
True
2727+
sage: endo.degree() == q
2728+
True
2729+
sage: endo.trace()
2730+
0
2731+
sage: pi = E.frobenius_isogeny()
2732+
sage: pi.codomain() is pi.domain() is E
2733+
True
2734+
sage: pi * endo == -endo * pi
2735+
True
2736+
26812737
.. NOTE::
26822738
26832739
This function makes no guarantees about the distribution of
@@ -2694,42 +2750,49 @@ def special_supersingular_curve(F, *, endomorphism=False):
26942750
if endomorphism and deg % 2:
26952751
raise ValueError('endomorphism was requested but is not defined over given field')
26962752

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

26992758
# first find the degree q of our special endomorphism
2700-
if p == 2:
2701-
q = 3
2702-
E = EllipticCurve(F, [0,0,1,0,0])
2703-
2704-
elif p % 4 == 3:
2705-
q = 1
2706-
E = EllipticCurve(F, [1,0])
2707-
2708-
elif p % 3 == 2:
2709-
q = 3
2710-
E = EllipticCurve(F, [0,1])
2711-
2712-
elif p % 8 == 5:
2713-
q = 2
2714-
E = EllipticCurve(F, [-4320, 96768])
2715-
2716-
else:
2717-
from sage.arith.misc import legendre_symbol
2718-
for q in map(ZZ, range(3,p,4)):
2719-
if not q.is_prime():
2720-
continue
2721-
if legendre_symbol(-q, p) == -1:
2722-
break
2759+
if q is None:
2760+
if p == 2:
2761+
q = 3
2762+
elif p % 4 == 3:
2763+
q = 1
2764+
elif p % 3 == 2:
2765+
q = 3
2766+
elif p % 8 == 5:
2767+
q = 2
27232768
else:
2724-
assert False # should never happen
2769+
from sage.arith.misc import legendre_symbol
2770+
for q in map(ZZ, range(3,p,4)):
2771+
if not q.is_prime():
2772+
continue
2773+
if legendre_symbol(-q, p) == -1:
2774+
break
2775+
else: # should never happen
2776+
assert False, 'bug in special_supersingular_curve()'
2777+
q = ZZ(q)
27252778

2726-
if E is None:
2727-
from sage.arith.misc import fundamental_discriminant
2728-
from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial
2729-
H = hilbert_class_polynomial(fundamental_discriminant(-q))
2730-
j = H.change_ring(GF(p)).any_root()
2779+
from sage.arith.misc import fundamental_discriminant
2780+
from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial
2781+
H = hilbert_class_polynomial(fundamental_discriminant(-q))
2782+
j = H.change_ring(GF(p)).any_root()
2783+
if j.is_zero():
2784+
if p == 2:
2785+
ainvs = [0,0,1,0,0]
2786+
elif p == 3:
2787+
ainvs = [1,0]
2788+
else:
2789+
ainvs = [0,1]
2790+
elif j == 1728:
2791+
ainvs = [1,0]
2792+
else:
27312793
a = 27 * j / (4 * (1728-j))
2732-
E = EllipticCurve(F, [a,-a])
2794+
ainvs = [a,-a]
2795+
E = EllipticCurve(F, ainvs)
27332796

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

2743-
if q == 1 or p <= 13:
2744-
if q == 1:
2745-
endos = E.automorphisms()
2746-
else:
2747-
endos = (iso*phi for phi in E.isogenies_prime_degree(q)
2748-
for iso in phi.codomain().isomorphisms(E))
2749-
endo = next(endo for endo in endos if endo.trace().is_zero())
2750-
2806+
if q.is_one():
2807+
endo = next(auto for auto in E.automorphisms() if auto.trace().is_zero())
27512808
else:
2752-
from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
2753-
iso = WeierstrassIsomorphism(None, (F(-q).sqrt(),0,0,0), E)
2754-
if q == 3 and E.a_invariants() == (0,0,0,0,1):
2755-
# workaround for #21883
2756-
endo = E.isogeny(E(0,1))
2757-
else:
2758-
endo = E.isogeny(None, iso.domain(), degree=q)
2759-
endo = iso * endo
2809+
iso = E.isomorphism(F(-q).sqrt(), is_codomain=True)
2810+
try:
2811+
endo = iso * E.isogeny(None, iso.domain(), degree=q)
2812+
except (NotImplementedError, ValueError): #FIXME catching ValueError here is a workaround for #38481
2813+
#FIXME this code could be simplified/optimized after #37388 and/or #35949
2814+
def _isogs(E, d):
2815+
if d.is_one():
2816+
yield E.identity_morphism()
2817+
return
2818+
l = d.prime_factors()[-1]
2819+
for phi in E.isogenies_prime_degree(l):
2820+
for psi in _isogs(phi.codomain(), d//l):
2821+
yield psi * phi
2822+
endos = (iso*phi for phi in _isogs(E, q) for iso in phi.codomain().isomorphisms(E))
2823+
# endos = (iso*phi for phi in E.isogenies_degree(q)
2824+
# for iso in phi.codomain().isomorphisms(E))
2825+
endo = next(endo for endo in endos if endo.trace().is_zero())
27602826

27612827
endo._degree = ZZ(q)
27622828
endo.trace.set_cache(ZZ.zero())

0 commit comments

Comments
 (0)