From 71e7ca5eaf39c73af66de313e7b006b3044d4e08 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 6 Nov 2023 12:23:16 +0100 Subject: [PATCH] =?UTF-8?q?implement=20Br=C3=B6ker's=20algorithm=20for=20c?= =?UTF-8?q?onstructing=20supersingular=20curves?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/doc/en/reference/references/index.rst | 3 + src/sage/schemes/elliptic_curves/all.py | 2 + .../elliptic_curves/ell_finite_field.py | 171 ++++++++++++++++++ 3 files changed, 176 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index d9f406d055d..334f0c12ab3 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1231,6 +1231,9 @@ REFERENCES: :doi:`10.1109/SFCS.1989.63516`, _ +.. [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) diff --git a/src/sage/schemes/elliptic_curves/all.py b/src/sage/schemes/elliptic_curves/all.py index fd2fb992360..3e2b9f233cd 100644 --- a/src/sage/schemes/elliptic_curves/all.py +++ b/src/sage/schemes/elliptic_curves/all.py @@ -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, diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 52115c51c7d..8d33c2df783 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -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()`` """ # **************************************************************************** @@ -2414,3 +2416,172 @@ def is_j_supersingular(j, proof=True): # 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`` of degree `1` or `2`, construct a + "special" supersingular elliptic curve `E` defined over ``F``. + + Such a curve + + - has coefficients in `\FF_p`; + + - has group structure `E(\FF_p) \cong \ZZ/(p+1)` and + `E(\FF_{p^2}) \cong \ZZ/(p+1) \times \ZZ/(p+1)`; + + - has an endomorphism `\vartheta` of small degree `q` that + anticommutes with the `\FF_p`-Frobenius on `E`. + + (The significance of `\vartheta` is that any such endomorphism, + together with the `\FF_p`-Frobenius, generates the endomorphism + algebra `\End(E) \otimes \QQ`.) + + INPUT: + + - ``F`` -- finite field `\FF_{p^r}` of degree `r \in \{1,2\}`. + + - ``endomorphism`` -- boolean (optional, default ``False``): + When set to ``True``, it is required that `r=2`, 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. = 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 + + .. 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) or (deg := F.degree()) > 2: + raise TypeError('input must be a finite field of degree 1 or 2') + p = F.characteristic() + + if endomorphism and deg % 2: + raise ValueError('endomorphism was requested but is not defined over given field') + + E = None + + 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 + else: + assert False # should never happen + + 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]) + + E.set_order((p+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) + 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