Skip to content

Commit 7d47e1e

Browse files
author
Release Manager
committed
gh-35370: Faster Kohel isogenies without bivariate polynomials ### 📚 Description This patch accelerates computation of Kohel formulas by replacing internal bivariate polynomials k[x,y] by a tower of polynomial rings k[x][y]. Because the y-coordinate of isogenies are always defined by a polynomial of y-degree 1, this is equivalent to working with a pair of univariate polynomials, which often have efficient representations especially over finite fields. The public API still exposes bivariate rational functions and is not modified. The resulting representation is several times faster. ### 📝 Checklist - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies This change is self-contained but is meant to be combined with 2 other changes: - (to be published) faster `__call__` for NTL ZZ_pX polynomials - #35358 : provides additional performance (independent patch) URL: #35370 Reported by: Rémy Oudompheng Reviewer(s): Lorenz Panny
2 parents 6510b7c + f6802e1 commit 7d47e1e

File tree

1 file changed

+26
-20
lines changed

1 file changed

+26
-20
lines changed

src/sage/schemes/elliptic_curves/ell_curve_isogeny.py

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,7 @@ class EllipticCurveIsogeny(EllipticCurveHom):
963963
#
964964
__base_field = None
965965
__poly_ring = None # univariate in x over __base_field
966-
__mpoly_ring = None # bivariate in x, y over __base_field
966+
__mpoly_ring = None # __base_field[x][y], internal use only
967967

968968
#
969969
# Rational Maps
@@ -1003,7 +1003,7 @@ class EllipticCurveIsogeny(EllipticCurveHom):
10031003
#
10041004
__psi = None # psi polynomial
10051005
__phi = None # phi polynomial
1006-
__omega = None # omega polynomial
1006+
__omega = None # omega polynomial, an element of k[x][y]
10071007

10081008

10091009
#
@@ -1559,7 +1559,7 @@ def __init_algebraic_structs(self, E):
15591559
sage: phi._EllipticCurveIsogeny__poly_ring # optional - sage.rings.finite_rings
15601560
Univariate Polynomial Ring in x over Finite Field of size 17
15611561
sage: phi._EllipticCurveIsogeny__mpoly_ring # optional - sage.rings.finite_rings
1562-
Multivariate Polynomial Ring in x, y over Finite Field of size 17
1562+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 17
15631563
15641564
Now, calling the initialization function does nothing more::
15651565
@@ -1571,7 +1571,7 @@ def __init_algebraic_structs(self, E):
15711571
sage: phi._EllipticCurveIsogeny__poly_ring # optional - sage.rings.finite_rings
15721572
Univariate Polynomial Ring in x over Finite Field of size 17
15731573
sage: phi._EllipticCurveIsogeny__mpoly_ring # optional - sage.rings.finite_rings
1574-
Multivariate Polynomial Ring in x, y over Finite Field of size 17
1574+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 17
15751575
15761576
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
15771577
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
@@ -1583,7 +1583,7 @@ def __init_algebraic_structs(self, E):
15831583
sage: phi._EllipticCurveIsogeny__poly_ring
15841584
Univariate Polynomial Ring in x over Rational Field
15851585
sage: phi._EllipticCurveIsogeny__mpoly_ring
1586-
Multivariate Polynomial Ring in x, y over Rational Field
1586+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Rational Field
15871587
15881588
sage: F = GF(19); R.<x> = F[] # optional - sage.rings.finite_rings
15891589
sage: E = EllipticCurve(j=GF(19)(0)) # optional - sage.rings.finite_rings
@@ -1596,14 +1596,16 @@ def __init_algebraic_structs(self, E):
15961596
sage: phi._EllipticCurveIsogeny__poly_ring # optional - sage.rings.finite_rings
15971597
Univariate Polynomial Ring in x over Finite Field of size 19
15981598
sage: phi._EllipticCurveIsogeny__mpoly_ring # optional - sage.rings.finite_rings
1599-
Multivariate Polynomial Ring in x, y over Finite Field of size 19
1599+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 19
16001600
"""
16011601
self._domain = E
16021602
self.__base_field = E.base_ring()
16031603
self.__poly_ring = PolynomialRing(self.__base_field, ['x'])
1604-
self.__mpoly_ring = PolynomialRing(self.__base_field, ['x','y'])
1604+
self.__mpoly_ring = PolynomialRing(self.__poly_ring, ['y'])
1605+
# The fraction fields are implicitly part of the public API, being the parents
1606+
# of the rational maps.
16051607
self.__xfield = FractionField(self.__poly_ring)
1606-
self.__xyfield = FractionField(self.__mpoly_ring)
1608+
self.__xyfield = FractionField(PolynomialRing(self.__base_field, ['x', 'y']))
16071609

16081610
def __compute_codomain(self):
16091611
r"""
@@ -2157,7 +2159,7 @@ def __initialize_rational_maps_via_velu(self):
21572159
((x^4 + 5*x^3 + x^2 + 4*x)/(x^3 + 5*x^2 + 3*x + 5), (x^5*y - 2*x^3*y - x^2*y - 2*x*y + 2*y)/(x^5 + 3*x^3 + 3*x^2 + x - 1))
21582160
"""
21592161
x = self.__poly_ring.gen()
2160-
y = self.__mpoly_ring.gen(1)
2162+
y = self.__xyfield.gen(1)
21612163
return self.__compute_via_velu(x,y)
21622164

21632165
def __init_kernel_polynomial_velu(self):
@@ -2314,7 +2316,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
23142316
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
23152317
sage: psig = two_torsion_part(E,x) # optional - sage.rings.finite_rings
23162318
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig) # optional - sage.rings.finite_rings
2317-
(x^3 + 6*x, x^3*y + x*y, 6, 0, 1, 2)
2319+
(x^3 + 6*x, (x^3 + x)*y, 6, 0, 1, 2)
23182320
23192321
sage: F = GF(2^4, 'alpha'); R.<x> = F[] # optional - sage.rings.finite_rings
23202322
sage: E = EllipticCurve(F, [1,1,0,1,0]) # optional - sage.rings.finite_rings
@@ -2325,7 +2327,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
23252327
23262328
sage: psig = two_torsion_part(E,x) # optional - sage.rings.finite_rings
23272329
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig) # optional - sage.rings.finite_rings
2328-
(x^3 + x, x^3*y + x^2 + x*y, 1, 0, 1, 2)
2330+
(x^3 + x, (x^3 + x)*y + x^2, 1, 0, 1, 2)
23292331
23302332
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1]) # optional - sage.rings.finite_rings
23312333
sage: R.<x> = GF(7)[] # optional - sage.rings.finite_rings
@@ -2337,7 +2339,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
23372339
sage: psig = two_torsion_part(E,f) # optional - sage.rings.finite_rings
23382340
sage: psig = two_torsion_part(E,f) # optional - sage.rings.finite_rings
23392341
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig) # optional - sage.rings.finite_rings
2340-
(x^7 + 5*x^6 + 2*x^5 + 6*x^4 + 3*x^3 + 5*x^2 + 6*x + 3, x^9*y - 3*x^8*y + 2*x^7*y - 3*x^3*y + 2*x^2*y + x*y - y, 1, 6, 3, 4)
2342+
(x^7 + 5*x^6 + 2*x^5 + 6*x^4 + 3*x^3 + 5*x^2 + 6*x + 3, (x^9 + 4*x^8 + 2*x^7 + 4*x^3 + 2*x^2 + x + 6)*y, 1, 6, 3, 4)
23412343
"""
23422344
# check if the polynomial really divides the two_torsion_polynomial
23432345
if self.__check and E.division_polynomial(2, x=self.__poly_ring.gen()) % psi_G != 0 :
@@ -2349,7 +2351,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
23492351
a1, a2, a3, a4, a6 = E.a_invariants()
23502352
b2, b4, _, _ = E.b_invariants()
23512353
x = self.__poly_ring.gen()
2352-
y = self.__mpoly_ring.gen(1)
2354+
y = self.__mpoly_ring.gen()
23532355

23542356
if n == 1:
23552357
x0 = -psi_G.constant_coefficient()
@@ -2430,7 +2432,7 @@ def __init_odd_kernel_polynomial(self, E, psi):
24302432
24312433
sage: R.<x> = GF(7)[] # optional - sage.rings.finite_rings
24322434
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, x+6) # optional - sage.rings.finite_rings
2433-
(x^3 + 5*x^2 + 3*x + 2, x^3*y - 3*x^2*y + x*y, 2, 6, 1, 3)
2435+
(x^3 + 5*x^2 + 3*x + 2, (x^3 + 4*x^2 + x)*y, 2, 6, 1, 3)
24342436
24352437
sage: F = GF(2^4, 'alpha'); R.<x> = F[] # optional - sage.rings.finite_rings
24362438
sage: alpha = F.gen() # optional - sage.rings.finite_rings
@@ -2444,7 +2446,7 @@ def __init_odd_kernel_polynomial(self, E, psi):
24442446
sage: R.<x> = F[] # optional - sage.rings.finite_rings
24452447
sage: f = x + alpha^2 + 1 # optional - sage.rings.finite_rings
24462448
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, f) # optional - sage.rings.finite_rings
2447-
(x^3 + (alpha^2 + 1)*x + alpha^3 + alpha^2 + alpha, x^3*y + (alpha^2 + 1)*x^2*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + 1)*x*y + (alpha^2 + alpha)*x + alpha*y + alpha, alpha^2 + alpha + 1, alpha^3 + alpha^2 + alpha, 1, 3)
2449+
(x^3 + (alpha^2 + 1)*x + alpha^3 + alpha^2 + alpha, (x^3 + (alpha^2 + 1)*x^2 + (alpha^2 + 1)*x + alpha)*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + alpha)*x + alpha, alpha^2 + alpha + 1, alpha^3 + alpha^2 + alpha, 1, 3)
24482450
24492451
sage: E = EllipticCurve(j=-262537412640768000) # optional - sage.rings.finite_rings
24502452
sage: f = E.isogenies_prime_degree()[0].kernel_polynomial() # optional - sage.rings.finite_rings
@@ -2536,11 +2538,12 @@ def __compute_omega_fast(self, E, psi, psi_pr, phi, phi_pr):
25362538
sage: fi = phi._EllipticCurveIsogeny__phi # optional - sage.rings.finite_rings
25372539
sage: fi_pr = fi.derivative() # optional - sage.rings.finite_rings
25382540
sage: phi._EllipticCurveIsogeny__compute_omega_fast(E, psi, psi_pr, fi, fi_pr) # optional - sage.rings.finite_rings
2539-
x^3*y - 3*x^2*y + x*y
2541+
(x^3 + 4*x^2 + x)*y
25402542
"""
25412543
a1 = E.a1()
25422544
a3 = E.a3()
2543-
x, y = self.__mpoly_ring.gens()
2545+
x = self.__poly_ring.gen()
2546+
y = self.__mpoly_ring.gen()
25442547

25452548
psi_2 = 2*y + a1*x + a3
25462549

@@ -2587,7 +2590,7 @@ def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
25872590
sage: fi = phi._EllipticCurveIsogeny__phi # optional - sage.rings.finite_rings
25882591
sage: fi_pr = fi.derivative() # optional - sage.rings.finite_rings
25892592
sage: phi._EllipticCurveIsogeny__compute_omega_general(E, psi, psi_pr, fi, fi_pr) # optional - sage.rings.finite_rings
2590-
x^3*y + (alpha^2 + 1)*x^2*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + 1)*x*y + (alpha^2 + alpha)*x + alpha*y + alpha
2593+
(x^3 + (alpha^2 + 1)*x^2 + (alpha^2 + 1)*x + alpha)*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + alpha)*x + alpha
25912594
25922595
A bug fixed in :trac:`7907`::
25932596
@@ -2604,7 +2607,8 @@ def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
26042607
"""
26052608
a1, a2, a3, a4, a6 = E.a_invariants()
26062609
b2, b4, _, _ = E.b_invariants()
2607-
x, y = self.__mpoly_ring.gens()
2610+
x = self.__poly_ring.gen()
2611+
y = self.__mpoly_ring.gen()
26082612

26092613
n = psi.degree()
26102614
d = 2 * n + 1
@@ -2693,7 +2697,9 @@ def __compute_via_kohel(self, xP, yP):
26932697
((x^3 - 2*x^2 + 3*x + 2)/(x^2 - 2*x + 1), (x^3*y - 3*x^2*y + x*y)/(x^3 - 3*x^2 + 3*x - 1))
26942698
"""
26952699
a = self.__phi(xP)
2696-
b = self.__omega(xP, yP)
2700+
omega0 = self.__omega[0]
2701+
omega1 = self.__omega[1]
2702+
b = omega0(xP) + omega1(xP)*yP
26972703
c = self.__psi(xP)
26982704
return a/c**2, b/c**3
26992705

0 commit comments

Comments
 (0)