Skip to content

Commit 38f4c2e

Browse files
author
Release Manager
committed
gh-40467: compute minimal polynomial of an isogeny & push such polynomials through other isogenies Back in #37125 we implemented Algorithms 3 and 4 from [Deuring for the People](https://ia.cr/2023/106). In this patch we add Algorithm 5, as well as a simple convenience method to compute the minimal polynomial of an isogeny. This functionality is very convenient for computing chains of isogenies whose kernel points may live in extension fields — without explicitly constructing and working in those extension fields. URL: #40467 Reported by: Lorenz Panny Reviewer(s): Giacomo Pope
2 parents 8c96045 + 5497458 commit 38f4c2e

File tree

2 files changed

+166
-0
lines changed

2 files changed

+166
-0
lines changed

src/sage/schemes/elliptic_curves/hom.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,139 @@ def divide_right(self, psi):
13641364
denom = psi.degree()
13651365
return EllipticCurveHom_fractional(numer, denom)
13661366

1367+
def minimal_polynomial(self):
1368+
r"""
1369+
Return a minimal polynomial of the kernel subgroup of this isogeny, as
1370+
defined in [EPSV2023]_, Definition 15: That is, some polynomial `f` such
1371+
that the points on the domain curve whose `x`-coordinates are roots of `f`
1372+
generate the kernel of this isogeny.
1373+
1374+
.. SEEALSO::
1375+
1376+
:meth:`EllipticCurve_field.kernel_polynomial_from_divisor()`
1377+
1378+
EXAMPLES::
1379+
1380+
sage: E = EllipticCurve(GF(419), [32, 41])
1381+
sage: phi = E.isogeny(E.lift_x(30)); phi
1382+
Isogeny of degree 7
1383+
from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1384+
to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
1385+
sage: f = phi.minimal_polynomial(); f # random -- one of x+161, x+201, x+389
1386+
x + 161
1387+
sage: f.divides(phi.kernel_polynomial())
1388+
True
1389+
sage: E.kernel_polynomial_from_divisor(f, 7) == phi.kernel_polynomial()
1390+
True
1391+
1392+
It also works for rational isogenies with irrational kernel points::
1393+
1394+
sage: E = EllipticCurve(GF(127^2), [1,0])
1395+
sage: phi = E.isogenies_prime_degree(17)[0]; phi
1396+
Isogeny of degree 17
1397+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1398+
to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1399+
sage: phi.kernel_polynomial()
1400+
x^8 + (68*z2 + 97)*x^6 + (59*z2 + 40)*x^4 + (59*z2 + 38)*x^2 + 4*z2 + 13
1401+
sage: phi.kernel_polynomial().factor()
1402+
(x^4 + (11*z2 + 32)*x^2 + 48*z2 + 70) * (x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25)
1403+
sage: phi.minimal_polynomial().factor()
1404+
x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25
1405+
"""
1406+
#FIXME This can probably be implemented better!
1407+
h = self.kernel_polynomial()
1408+
for f,_ in reversed(h.factor()):
1409+
if self.domain().kernel_polynomial_from_divisor(f, self.degree()) == h:
1410+
return f
1411+
raise ValueError('not a cyclic isogeny')
1412+
1413+
def push_subgroup(self, f):
1414+
r"""
1415+
Given a minimal polynomial (see :meth:`minimal_polynomial`) of a
1416+
subgroup `G` of the domain curve of this isogeny, return a minimal
1417+
polynomial of the image of `G` under this isogeny.
1418+
1419+
ALGORITHM: [EPSV2023]_, Algorithm 5 (``PushSubgroup``)
1420+
1421+
EXAMPLES::
1422+
1423+
sage: E = EllipticCurve(GF(419), [32, 41])
1424+
sage: K = E.lift_x(30)
1425+
sage: phi = E.isogeny(K); phi
1426+
Isogeny of degree 7
1427+
from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1428+
to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
1429+
sage: psi = E.isogeny(E.lift_x(54), algorithm='factored'); psi
1430+
Composite morphism of degree 15 = 3*5:
1431+
From: Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1432+
To: Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
1433+
sage: f = phi.minimal_polynomial(); f # random -- one of x+161, x+201, x+389
1434+
x + 161
1435+
sage: g = psi.push_subgroup(f); g # random -- one of x+148, x+333, x+249
1436+
x + 148
1437+
sage: h = psi.codomain().kernel_polynomial_from_divisor(g, phi.degree()); h
1438+
x^3 + 311*x^2 + 196*x + 44
1439+
sage: chi = psi.codomain().isogeny(h); chi
1440+
Isogeny of degree 7
1441+
from Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
1442+
to Elliptic Curve defined by y^2 = x^3 + 186*x + 37 over Finite Field of size 419
1443+
sage: (chi * psi)(K)
1444+
(0 : 1 : 0)
1445+
1446+
It also works for rational isogenies with irrational kernel points::
1447+
1448+
sage: E = EllipticCurve(GF(127^2), [1,0])
1449+
sage: phi = E.isogenies_prime_degree(13)[0]; phi
1450+
Isogeny of degree 13
1451+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1452+
to Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1453+
sage: psi = E.isogenies_prime_degree(17)[0]; psi
1454+
Isogeny of degree 17
1455+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1456+
to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1457+
sage: f_phi = phi.minimal_polynomial()
1458+
sage: g_phi = psi.push_subgroup(f_phi)
1459+
sage: h_phi = psi.codomain().kernel_polynomial_from_divisor(g_phi, phi.degree())
1460+
sage: phi_pushed = psi.codomain().isogeny(h_phi); phi_pushed
1461+
Isogeny of degree 13 from Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2 to Elliptic Curve defined by y^2 = x^3 + (110*z2+61)*x over Finite Field in z2 of size 127^2
1462+
sage: f_psi = psi.minimal_polynomial()
1463+
sage: g_psi = phi.push_subgroup(f_psi)
1464+
sage: h_psi = phi.codomain().kernel_polynomial_from_divisor(g_psi, psi.degree())
1465+
sage: psi_pushed = phi.codomain().isogeny(h_psi); psi_pushed
1466+
Isogeny of degree 17 from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2 to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1467+
sage: any(iso * psi_pushed * phi == phi_pushed * psi
1468+
....: for iso in psi_pushed.codomain().isomorphisms(phi_pushed.codomain()))
1469+
True
1470+
1471+
If the subgroup represented by `f` intersects nontrivially with the
1472+
kernel of this isogeny, the method still works correctly::
1473+
1474+
sage: E = EllipticCurve(GF(419), [1,0])
1475+
sage: phi = next(E.isogenies_degree(7)); phi
1476+
Isogeny of degree 7
1477+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
1478+
to Elliptic Curve defined by y^2 = x^3 + 285*x + 87 over Finite Field of size 419
1479+
sage: psi = next(E.isogenies_degree(21)); psi
1480+
Composite morphism of degree 21 = 7*3:
1481+
From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419
1482+
To: Elliptic Curve defined by y^2 = x^3 + 134*x + 230 over Finite Field of size 419
1483+
sage: phi.kernel_polynomial().gcd(psi.kernel_polynomial())
1484+
x^3 + 274*x^2 + 350*x + 6
1485+
sage: f = phi.minimal_polynomial()
1486+
sage: psi.push_subgroup(f)
1487+
1
1488+
"""
1489+
g = self.x_rational_map()
1490+
g1, g2 = g.numerator(), g.denominator()
1491+
gker = g2.gcd(f)
1492+
f1 = f // gker
1493+
R = f1.parent()
1494+
S = R.quotient_ring(f1)
1495+
alpha = S(g1 * g2.inverse_mod(f1))
1496+
if not alpha:
1497+
return R.one()
1498+
return alpha.minpoly()
1499+
13671500

13681501
def compare_via_evaluation(left, right):
13691502
r"""

src/sage/schemes/elliptic_curves/hom_composite.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,3 +1048,36 @@ def inverse_image(self, Q, /, *, all=False):
10481048
return next(self.inverse_image(Q, all=True))
10491049
except StopIteration:
10501050
raise ValueError
1051+
1052+
def push_subgroup(self, f):
1053+
r"""
1054+
Given a minimal polynomial (see :meth:`~EllipticCurveHom.minimal_polynomial`)
1055+
of a subgroup `G` of the domain curve of this isogeny, return a minimal polynomial
1056+
of the image of `G` under this isogeny.
1057+
1058+
ALGORITHM: iterative :meth:`EllipticCurveHom.push_subgroup()`
1059+
1060+
EXAMPLES::
1061+
1062+
sage: E = EllipticCurve(GF((2^61-1, 2)), [1,0])
1063+
sage: phi = next(E.isogenies_degree(7)); phi
1064+
Isogeny of degree 7
1065+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 2305843009213693951^2
1066+
to Elliptic Curve defined by y^2 = x^3 + (595688734420561721*z2+584021682365204922)*x + (2058397526093132314*z2+490140893682260802) over Finite Field in z2 of size 2305843009213693951^2
1067+
sage: psi = E.isogeny(E.lift_x(48), algorithm='factored'); psi
1068+
Composite morphism of degree 36028797018963968 = 2^55:
1069+
From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 2305843009213693951^2
1070+
To: Elliptic Curve defined by y^2 = x^3 + 938942632807894005*x + 1238942515234646252 over Finite Field in z2 of size 2305843009213693951^2
1071+
sage: f = phi.minimal_polynomial()
1072+
sage: g = psi.push_subgroup(f)
1073+
sage: h = psi.codomain().kernel_polynomial_from_divisor(g, phi.degree())
1074+
sage: chi = psi.codomain().isogeny(h); chi
1075+
Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + 938942632807894005*x + 1238942515234646252 over Finite Field in z2 of size 2305843009213693951^2 to Elliptic Curve defined by y^2 = x^3 + (1406897314822267524*z2+1659665944678449850)*x + (650305521764753329*z2+1047269804324934563) over Finite Field in z2 of size 2305843009213693951^2
1076+
sage: x = phi.kernel_polynomial().any_root()
1077+
sage: K = E.change_ring(E.base_field().extension(2)).lift_x(x)
1078+
sage: (chi * psi)._eval(K)
1079+
(0 : 1 : 0)
1080+
"""
1081+
for phi in self.factors():
1082+
f = phi.push_subgroup(f)
1083+
return f

0 commit comments

Comments
 (0)