Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement Bröker's algorithm for constructing supersingular curves #36665

Merged
merged 3 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/doc/en/reference/references/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,9 @@ REFERENCES:
:doi:`10.1109/SFCS.1989.63516`,
<http://www.cs.cmu.edu/~15859n/RelatedWork/Broder-GenRanSpanningTrees.pdf>_

.. [Bro2009] \R. Bröker: *Constructing supersingular elliptic curves*.
Journal of Combinatorics and Number Theory 1.3 (2009), pp. 269--273.

.. [Broder2000] Broder, A.Z., Kumar, R., Maghoul, F., Raghavan, P., Rajagopalan,
S., Stata, R., Tomkins, A., Wiener, J.L.: Graph structure in
the web. Computer Networks 33(1-6), 309–320 (2000)
Expand Down
2 changes: 2 additions & 0 deletions src/sage/schemes/elliptic_curves/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
lazy_import('sage.schemes.elliptic_curves.ell_rational_field',
['cremona_curves', 'cremona_optimal_curves'])

lazy_import('sage.schemes.elliptic_curves.ell_finite_field', 'special_supersingular_curve')

from .cm import ( cm_orders,
cm_j_invariants,
cm_j_invariants_and_orders,
Expand Down
199 changes: 199 additions & 0 deletions src/sage/schemes/elliptic_curves/ell_finite_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
- Mariah Lenox (2011-03): Added ``set_order`` method

- Lorenz Panny, John Cremona (2023-02): ``.twists()``

- Lorenz Panny (2023): ``special_supersingular_curve()``
"""

# ****************************************************************************
Expand Down Expand Up @@ -2414,3 +2416,200 @@
# expensive since it involves counting the number of points on E):

return E.trace_of_frobenius() % p == 0

def special_supersingular_curve(F, *, endomorphism=False):
r"""
Given a finite field ``F``, construct a "special" supersingular
elliptic curve `E` defined over ``F``.

yyyyx4 marked this conversation as resolved.
Show resolved Hide resolved
Such a curve

- has coefficients in `\mathbb F_p`;

- 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
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`.)

INPUT:

- ``F`` -- finite field `\mathbb F_{p^r}`;

- ``endomorphism`` -- boolean (optional, default ``False``):
When set to ``True``, it is required that `2 \mid r`, and
the function then additionally returns `\vartheta`.

EXAMPLES::

sage: special_supersingular_curve(GF(1013^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 1013^2,
Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 1013^2 to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 1013^2)

sage: special_supersingular_curve(GF(1019^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1019^2,
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1019^2
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)

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,
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1031^2
Via: (u,r,s,t) = (747*z2 + 284, 0, 0, 0))

sage: special_supersingular_curve(GF(1033^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + 53*x + 980 over Finite Field in z2 of size 1033^2,
Isogeny of degree 11 from Elliptic Curve defined by y^2 = x^3 + 53*x + 980 over Finite Field in z2 of size 1033^2 to Elliptic Curve defined by y^2 = x^3 + 53*x + 980 over Finite Field in z2 of size 1033^2)

sage: special_supersingular_curve(GF(1039^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1039^2,
Elliptic-curve endomorphism of Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1039^2
Via: (u,r,s,t) = (626*z2 + 200, 0, 0, 0))

sage: special_supersingular_curve(GF(1049^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 1049^2,
Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 1049^2 to Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field in z2 of size 1049^2)

sage: special_supersingular_curve(GF(1051^2), endomorphism=True)
(Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 1051^2,
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))

TESTS::

sage: p = random_prime(1000)
sage: E = special_supersingular_curve(GF(p))
sage: E.is_supersingular()
True
sage: E.order() == p + 1
True
sage: F.<t> = GF((p,2))
sage: E, endo = special_supersingular_curve(F, endomorphism=True)
sage: E.is_supersingular()
True
sage: E.j_invariant() in GF(p)
True
sage: E.abelian_group().invariants() == (p+1, p+1)
True
sage: endo.domain() is endo.codomain() is E
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

Also try it for larger-degree fields::

sage: k = ZZ(randrange(3, 10, 2))
sage: E = special_supersingular_curve(GF((p, k)))
sage: E.is_supersingular()
True
sage: F.<t> = GF((p, 2*k))
sage: E, endo = special_supersingular_curve(F, 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.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
the output. The current implementation is deterministic in
many cases.

ALGORITHM: [Bro2009]_, Algorithm 2.4
"""
if not isinstance(F, FiniteField):
raise TypeError('input must be a finite field')

Check warning on line 2542 in src/sage/schemes/elliptic_curves/ell_finite_field.py

View check run for this annotation

Codecov / codecov/patch

src/sage/schemes/elliptic_curves/ell_finite_field.py#L2542

Added line #L2542 was not covered by tests
p = F.characteristic()
deg = F.degree()

if endomorphism and deg % 2:
raise ValueError('endomorphism was requested but is not defined over given field')

Check warning on line 2547 in src/sage/schemes/elliptic_curves/ell_finite_field.py

View check run for this annotation

Codecov / codecov/patch

src/sage/schemes/elliptic_curves/ell_finite_field.py#L2547

Added line #L2547 was not covered by tests

E = None

yyyyx4 marked this conversation as resolved.
Show resolved Hide resolved
# first find the degree q of our special endomorphism
if p == 2:
q = 3
E = EllipticCurve(F, [0,0,1,0,0])

Check warning on line 2554 in src/sage/schemes/elliptic_curves/ell_finite_field.py

View check run for this annotation

Codecov / codecov/patch

src/sage/schemes/elliptic_curves/ell_finite_field.py#L2553-L2554

Added lines #L2553 - L2554 were not covered by tests

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

Check warning on line 2572 in src/sage/schemes/elliptic_curves/ell_finite_field.py

View check run for this annotation

Codecov / codecov/patch

src/sage/schemes/elliptic_curves/ell_finite_field.py#L2572

Added line #L2572 was not covered by tests
if legendre_symbol(-q, p) == -1:
break
else:
assert False # should never happen

Check warning on line 2576 in src/sage/schemes/elliptic_curves/ell_finite_field.py

View check run for this annotation

Codecov / codecov/patch

src/sage/schemes/elliptic_curves/ell_finite_field.py#L2576

Added line #L2576 was not covered by tests

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()
a = 27 * j / (4 * (1728-j))
E = EllipticCurve(F, [a,-a])

if ZZ(2).divides(deg):
k = deg//2
E.set_order((p**k - (-1)**k)**2)
else:
E.set_order(p**deg - (-1)**deg)

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)

Check warning on line 2599 in src/sage/schemes/elliptic_curves/ell_finite_field.py

View check run for this annotation

Codecov / codecov/patch

src/sage/schemes/elliptic_curves/ell_finite_field.py#L2599

Added line #L2599 was not covered by tests
for iso in phi.codomain().isomorphisms(E))
endo = next(endo for endo in endos if endo.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

endo._degree = ZZ(q)
endo.trace.set_cache(ZZ.zero())
return E, endo