Skip to content

Commit b92adb0

Browse files
Faster Kohel isogenies without bivariate polynomials
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.
1 parent 82e02a1 commit b92adb0

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
@@ -891,7 +891,7 @@ class EllipticCurveIsogeny(EllipticCurveHom):
891891
#
892892
__base_field = None
893893
__poly_ring = None # univariate in x over __base_field
894-
__mpoly_ring = None # bivariate in x, y over __base_field
894+
__mpoly_ring = None # __base_field[x][y], internal use only
895895

896896
#
897897
# Rational Maps
@@ -931,7 +931,7 @@ class EllipticCurveIsogeny(EllipticCurveHom):
931931
#
932932
__psi = None # psi polynomial
933933
__phi = None # phi polynomial
934-
__omega = None # omega polynomial
934+
__omega = None # omega polynomial, an element of k[x][y]
935935

936936

937937
#
@@ -1466,7 +1466,7 @@ def __init_algebraic_structs(self, E):
14661466
sage: phi._EllipticCurveIsogeny__poly_ring
14671467
Univariate Polynomial Ring in x over Finite Field of size 17
14681468
sage: phi._EllipticCurveIsogeny__mpoly_ring
1469-
Multivariate Polynomial Ring in x, y over Finite Field of size 17
1469+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 17
14701470
14711471
Now, calling the initialization function does nothing more::
14721472
@@ -1478,7 +1478,7 @@ def __init_algebraic_structs(self, E):
14781478
sage: phi._EllipticCurveIsogeny__poly_ring
14791479
Univariate Polynomial Ring in x over Finite Field of size 17
14801480
sage: phi._EllipticCurveIsogeny__mpoly_ring
1481-
Multivariate Polynomial Ring in x, y over Finite Field of size 17
1481+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 17
14821482
14831483
sage: E = EllipticCurve(QQ, [0,0,0,1,0])
14841484
sage: phi = EllipticCurveIsogeny(E, E((0,0)))
@@ -1490,7 +1490,7 @@ def __init_algebraic_structs(self, E):
14901490
sage: phi._EllipticCurveIsogeny__poly_ring
14911491
Univariate Polynomial Ring in x over Rational Field
14921492
sage: phi._EllipticCurveIsogeny__mpoly_ring
1493-
Multivariate Polynomial Ring in x, y over Rational Field
1493+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Rational Field
14941494
14951495
sage: F = GF(19); R.<x> = F[]
14961496
sage: E = EllipticCurve(j=GF(19)(0))
@@ -1503,14 +1503,16 @@ def __init_algebraic_structs(self, E):
15031503
sage: phi._EllipticCurveIsogeny__poly_ring
15041504
Univariate Polynomial Ring in x over Finite Field of size 19
15051505
sage: phi._EllipticCurveIsogeny__mpoly_ring
1506-
Multivariate Polynomial Ring in x, y over Finite Field of size 19
1506+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Finite Field of size 19
15071507
"""
15081508
self._domain = E
15091509
self.__base_field = E.base_ring()
15101510
self.__poly_ring = PolynomialRing(self.__base_field, ['x'])
1511-
self.__mpoly_ring = PolynomialRing(self.__base_field, ['x','y'])
1511+
self.__mpoly_ring = PolynomialRing(self.__poly_ring, ['y'])
1512+
# The fraction fields are implicitly part of the public API, being the parents
1513+
# of the rational maps.
15121514
self.__xfield = FractionField(self.__poly_ring)
1513-
self.__xyfield = FractionField(self.__mpoly_ring)
1515+
self.__xyfield = FractionField(PolynomialRing(self.__base_field, ['x', 'y']))
15141516

15151517
def __compute_codomain(self):
15161518
r"""
@@ -2046,7 +2048,7 @@ def __initialize_rational_maps_via_velu(self):
20462048
((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))
20472049
"""
20482050
x = self.__poly_ring.gen()
2049-
y = self.__mpoly_ring.gen(1)
2051+
y = self.__xyfield.gen(1)
20502052
return self.__compute_via_velu(x,y)
20512053

20522054
def __init_kernel_polynomial_velu(self):
@@ -2197,7 +2199,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
21972199
sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import two_torsion_part
21982200
sage: psig = two_torsion_part(E,x)
21992201
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2200-
(x^3 + 6*x, x^3*y + x*y, 6, 0, 1, 2)
2202+
(x^3 + 6*x, (x^3 + x)*y, 6, 0, 1, 2)
22012203
22022204
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
22032205
sage: E = EllipticCurve(F, [1,1,0,1,0])
@@ -2206,7 +2208,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
22062208
22072209
sage: psig = two_torsion_part(E,x)
22082210
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2209-
(x^3 + x, x^3*y + x^2 + x*y, 1, 0, 1, 2)
2211+
(x^3 + x, (x^3 + x)*y + x^2, 1, 0, 1, 2)
22102212
22112213
sage: E = EllipticCurve(GF(7), [0,-1,0,0,1])
22122214
sage: R.<x> = GF(7)[]
@@ -2216,7 +2218,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
22162218
sage: psig = two_torsion_part(E,f)
22172219
sage: psig = two_torsion_part(E,f)
22182220
sage: phi._EllipticCurveIsogeny__init_even_kernel_polynomial(E,psig)
2219-
(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)
2221+
(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)
22202222
"""
22212223
# check if the polynomial really divides the two_torsion_polynomial
22222224
if self.__check and E.division_polynomial(2, x=self.__poly_ring.gen()) % psi_G != 0 :
@@ -2228,7 +2230,7 @@ def __init_even_kernel_polynomial(self, E, psi_G):
22282230
a1, a2, a3, a4, a6 = E.a_invariants()
22292231
b2, b4, _, _ = E.b_invariants()
22302232
x = self.__poly_ring.gen()
2231-
y = self.__mpoly_ring.gen(1)
2233+
y = self.__mpoly_ring.gen()
22322234

22332235
if n == 1:
22342236
x0 = -psi_G.constant_coefficient()
@@ -2307,7 +2309,7 @@ def __init_odd_kernel_polynomial(self, E, psi):
23072309
23082310
sage: R.<x> = GF(7)[]
23092311
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, x+6)
2310-
(x^3 + 5*x^2 + 3*x + 2, x^3*y - 3*x^2*y + x*y, 2, 6, 1, 3)
2312+
(x^3 + 5*x^2 + 3*x + 2, (x^3 + 4*x^2 + x)*y, 2, 6, 1, 3)
23112313
23122314
sage: F = GF(2^4, 'alpha'); R.<x> = F[]
23132315
sage: alpha = F.gen()
@@ -2319,7 +2321,7 @@ def __init_odd_kernel_polynomial(self, E, psi):
23192321
sage: R.<x> = F[]
23202322
sage: f = x + alpha^2 + 1
23212323
sage: phi._EllipticCurveIsogeny__init_odd_kernel_polynomial(E, f)
2322-
(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)
2324+
(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)
23232325
23242326
sage: E = EllipticCurve(j=-262537412640768000)
23252327
sage: f = E.isogenies_prime_degree()[0].kernel_polynomial()
@@ -2407,11 +2409,12 @@ def __compute_omega_fast(self, E, psi, psi_pr, phi, phi_pr):
24072409
sage: fi = phi._EllipticCurveIsogeny__phi
24082410
sage: fi_pr = fi.derivative()
24092411
sage: phi._EllipticCurveIsogeny__compute_omega_fast(E, psi, psi_pr, fi, fi_pr)
2410-
x^3*y - 3*x^2*y + x*y
2412+
(x^3 + 4*x^2 + x)*y
24112413
"""
24122414
a1 = E.a1()
24132415
a3 = E.a3()
2414-
x, y = self.__mpoly_ring.gens()
2416+
x = self.__poly_ring.gen()
2417+
y = self.__mpoly_ring.gen()
24152418

24162419
psi_2 = 2*y + a1*x + a3
24172420

@@ -2456,7 +2459,7 @@ def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
24562459
sage: fi = phi._EllipticCurveIsogeny__phi
24572460
sage: fi_pr = fi.derivative()
24582461
sage: phi._EllipticCurveIsogeny__compute_omega_general(E, psi, psi_pr, fi, fi_pr)
2459-
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
2462+
(x^3 + (alpha^2 + 1)*x^2 + (alpha^2 + 1)*x + alpha)*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + alpha)*x + alpha
24602463
24612464
A bug fixed in :trac:`7907`::
24622465
@@ -2470,7 +2473,8 @@ def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr):
24702473
"""
24712474
a1, a2, a3, a4, a6 = E.a_invariants()
24722475
b2, b4, _, _ = E.b_invariants()
2473-
x, y = self.__mpoly_ring.gens()
2476+
x = self.__poly_ring.gen()
2477+
y = self.__mpoly_ring.gen()
24742478

24752479
n = psi.degree()
24762480
d = 2 * n + 1
@@ -2559,7 +2563,9 @@ def __compute_via_kohel(self, xP, yP):
25592563
((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))
25602564
"""
25612565
a = self.__phi(xP)
2562-
b = self.__omega(xP, yP)
2566+
omega0 = self.__omega[0]
2567+
omega1 = self.__omega[1]
2568+
b = omega0(xP) + omega1(xP)*yP
25632569
c = self.__psi(xP)
25642570
return a/c**2, b/c**3
25652571

0 commit comments

Comments
 (0)