Skip to content

Commit

Permalink
sagemathgh-36285: add Rémy Oudompheng's implementation of the BMSS al…
Browse files Browse the repository at this point in the history
…gorithm

    
The BMSS algorithm computes (the kernel polynomial of) a *normalized*
isogeny from its degree $\ell$ and its domain and codomain in time
$\widetilde O(\ell)$. Currently Sage uses Stark's algorithm for the same
task, which takes time in $\Omega(\ell^2)$. An implementation in Sage of
the BMSS algorithm is available thanks to @remyoudompheng, and in this
patch we add it to the Sage library (following Rémy's suggestion).

Benchmark results for isogenies over a ~280-bit prime field (red is
Stark, blue is BMSS; axes are degree $\ell$ and seconds taken):

![Benchmark results for {Stark, BMSS} algorithms](https://github.com/sag
emath/sage/assets/84067835/2a9f47d3-f1f9-4bb1-b3f1-6539de2a7562)
    
URL: sagemath#36285
Reported by: Lorenz Panny
Reviewer(s): github-actions[bot], Kwankyu Lee, Lorenz Panny
  • Loading branch information
Release Manager committed Oct 1, 2023
2 parents b2e0816 + 81996bb commit 462d3f6
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 9 deletions.
5 changes: 3 additions & 2 deletions src/doc/en/reference/references/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1046,9 +1046,10 @@ REFERENCES:
equations: I. Fibonacci and Lucas perfect powers." Annals
of Math, 2006.
.. [BMSS2006] Alin Bostan, Bruno Salvy, Francois Morain, Eric Schost. Fast
algorithms for computing isogenies between elliptic
.. [BMSS2006] Alin Bostan, Bruno Salvy, François Morain, Éric Schost.
Fast algorithms for computing isogenies between elliptic
curves. [Research Report] 2006, pp.28. <inria-00091441>
https://arxiv.org/pdf/cs/0609020.pdf
.. [BN2010] \D. Bump and M. Nakasuji.
Integration on `p`-adic groups and crystal bases.
Expand Down
104 changes: 97 additions & 7 deletions src/sage/schemes/elliptic_curves/ell_curve_isogeny.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@
use of univariate vs. bivariate polynomials and rational functions.
- Lorenz Panny (2022-04): major cleanup of code and documentation
- Lorenz Panny (2022): inseparable duals
- Rémy Oudompheng (2023): implementation of the BMSS algorithm
"""

# ****************************************************************************
Expand Down Expand Up @@ -3326,6 +3329,80 @@ def _composition_impl(left, right):
return NotImplemented


def compute_isogeny_bmss(E1, E2, l):
r"""
Compute the kernel polynomial of the unique normalized isogeny
of degree ``l`` between ``E1`` and ``E2``.
Both curves must be given in short Weierstrass form, and the
characteristic must be either `0` or no smaller than `4l+4`.
ALGORITHM: [BMSS2006]_, algorithm *fastElkies'*.
EXAMPLES::
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_bmss
sage: E1 = EllipticCurve(GF(167), [153, 112])
sage: E2 = EllipticCurve(GF(167), [56, 40])
sage: compute_isogeny_bmss(E1, E2, 13)
x^6 + 139*x^5 + 73*x^4 + 139*x^3 + 120*x^2 + 88*x
"""
# Original author of this function: Rémy Oudompheng.
# https://github.com/remyoudompheng/isogeny_weber/blob/64289127a337ac1bf258b711e02fea02b7df5275/isogeny_weber/isogenies.py#L272-L332
# Released under the MIT license: https://github.com/remyoudompheng/isogeny_weber/blob/64289127a337ac1bf258b711e02fea02b7df5275/LICENSE
# Slightly adjusted for inclusion in the Sage library.
if E1.a1() or E1.a2() or E1.a3():
raise ValueError('E1 must be a short Weierstrass curve')
if E2.a1() or E2.a2() or E2.a3():
raise ValueError('E2 must be a short Weierstrass curve')
char = E1.base_ring().characteristic()
if char != 0 and char < 4*l + 4:
raise ValueError('characteristic must be at least 4*degree+4')
Rx, x = E1.base_ring()["x"].objgen()
# Compute C = 1/(1 + Ax^4 + Bx^6) mod x^4l
A, B = E1.a4(), E1.a6()
C = (1 + A * x**4 + B * x**6).inverse_series_trunc(4 * l)
# Solve differential equation
# The number of terms doubles at each iteration.
# S'^2 = G(x,S) = (1 + A2 S^4 + B2 S^6) / (1 + Ax^4 + Bx^6)
# S = x + O(x^2)
A2, B2 = E2.a4(), E2.a6()
S = x + (A2 - A) / 10 * x**5 + (B2 - B) / 14 * x**7
sprec = 8
while sprec < 4 * l:
assert sprec % 2 == 0
if sprec > 2 * l:
sprec = 2 * l
# s1 => s1 + x^k s2
# 2 s1' s2' - dG/dS(x, s1) s2 = G(x, s1) - s1'2
s1 = S
ds1 = s1.derivative()
s1pows = [1, s1]
while len(s1pows) < 7:
s1pows.append(s1._mul_trunc_(s1pows[-1], 2 * sprec))
GS = C * (1 + A2 * s1pows[4] + B2 * s1pows[6])
dGS = C * (4 * A2 * s1pows[3] + 6 * B2 * s1pows[5])
# s2' = (dGS / 2s1') s2 + (G(x, s1) - s1'2) / (2s1')
denom = (2 * ds1).inverse_series_trunc(2 * sprec)
a = dGS._mul_trunc_(denom, 2 * sprec)
b = (GS - ds1**2)._mul_trunc_(denom, 2 * sprec)
s2 = a.add_bigoh(2 * sprec).solve_linear_de(prec=2 * sprec, b=b, f0=0)
S = s1 + Rx(s2)
sprec = 2 * sprec
# Reconstruct:
# S = x * T(x^2)
# Compute U = 1/T^2
# Reconstruct N(1/x) / D(1/x) = U
T = Rx([S[2 * i + 1] for i in range(2 * l)])
U = T._mul_trunc_(T, 2 * l).inverse_series_trunc(2 * l)
_, Q = Rx(U).rational_reconstruction(x ** (2 * l), l, l)
Q = Q.add_bigoh((l + 1) // 2)
if not Q.is_square():
raise ValueError(f"the two curves are not linked by a cyclic normalized isogeny of degree {l}")
Q = Q.sqrt()
ker = Rx(Q).reverse(degree=l//2)
return ker.monic()

def compute_isogeny_stark(E1, E2, ell):
r"""
Return the kernel polynomial of an isogeny of degree ``ell``
Expand Down Expand Up @@ -3437,10 +3514,11 @@ def compute_isogeny_stark(E1, E2, ell):
from sage.misc.superseded import deprecated_function_alias
compute_isogeny_starks = deprecated_function_alias(34871, compute_isogeny_stark)

def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="stark"):

def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm=None):
r"""
Return the kernel polynomial of an isogeny of degree ``ell``
from ``E1`` to ``E2``.
Return the kernel polynomial of a cyclic, separable, normalized
isogeny of degree ``ell`` from ``E1`` to ``E2``.
INPUT:
Expand All @@ -3450,7 +3528,9 @@ def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="stark"):
- ``ell`` -- the degree of an isogeny from ``E1`` to ``E2``
- ``algorithm`` -- currently only ``"stark"`` (default) is implemented
- ``algorithm`` -- ``None`` (default, choose automatically) or
``"bmss"`` (:func:`compute_isogeny_bmss`) or
``"stark"`` (:func:`compute_isogeny_stark`)
OUTPUT:
Expand Down Expand Up @@ -3507,9 +3587,19 @@ def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="stark"):
from sage.misc.superseded import deprecation
deprecation(34871, 'The "starks" algorithm is being renamed to "stark".')
algorithm = 'stark'
if algorithm != "stark":
raise NotImplementedError(f'unknown algorithm {algorithm}')
return compute_isogeny_stark(E1, E2, ell).radical()

if algorithm is None:
char = E1.base_ring().characteristic()
if char != 0 and char < 4*ell + 4:
raise NotImplementedError(f'no algorithm for computing kernel polynomial from domain and codomain is implemented for degree {ell} and characteristic {char}')
algorithm = 'stark' if ell < 10 else 'bmss'

if algorithm == 'bmss':
return compute_isogeny_bmss(E1, E2, ell)
if algorithm == 'stark':
return compute_isogeny_stark(E1, E2, ell).radical()

raise NotImplementedError(f'unknown algorithm {algorithm}')

def compute_intermediate_curves(E1, E2):
r"""
Expand Down

0 comments on commit 462d3f6

Please sign in to comment.