Skip to content

Commit f7fe82e

Browse files
committed
compute minimal polynomial of an isogeny & push such polynomials through other isogenies
1 parent 931cc5e commit f7fe82e

File tree

1 file changed

+114
-0
lines changed
  • src/sage/schemes/elliptic_curves

1 file changed

+114
-0
lines changed

src/sage/schemes/elliptic_curves/hom.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,120 @@ def matrix_on_subgroup(self, domain_gens, codomain_gens=None):
11301130
from sage.rings.finite_rings.integer_mod_ring import Zmod
11311131
return matrix(Zmod(n), [vecP, vecQ])
11321132

1133+
def minimal_polynomial(self):
1134+
r"""
1135+
Return a minimal polynomial of this isogeny, as defined in
1136+
[EPSV2023]_, Definition 15: That is, some polynomial `f`
1137+
such that the points on the domain curve whose `x`-coordinates
1138+
are roots of `f` generate the kernel of this isogeny.
1139+
1140+
.. SEEALSO::
1141+
1142+
:meth:`EllipticCurve_field.kernel_polynomial_from_divisor()`
1143+
1144+
EXAMPLES::
1145+
1146+
sage: E = EllipticCurve(GF(419), [32, 41])
1147+
sage: phi = E.isogeny(E.lift_x(30)); phi
1148+
Isogeny of degree 7
1149+
from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1150+
to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
1151+
sage: f = phi.minimal_polynomial(); f # random -- one of x+161, x+201, x+389
1152+
x + 161
1153+
sage: f.divides(phi.kernel_polynomial())
1154+
True
1155+
sage: E.kernel_polynomial_from_divisor(f, 7) == phi.kernel_polynomial()
1156+
True
1157+
1158+
It also works for rational isogenies with irrational kernel points::
1159+
1160+
sage: E = EllipticCurve(GF(127^2), [1,0])
1161+
sage: phi = E.isogenies_prime_degree(17)[0]; phi
1162+
Isogeny of degree 17
1163+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1164+
to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1165+
sage: phi.kernel_polynomial()
1166+
x^8 + (68*z2 + 97)*x^6 + (59*z2 + 40)*x^4 + (59*z2 + 38)*x^2 + 4*z2 + 13
1167+
sage: phi.kernel_polynomial().factor()
1168+
(x^4 + (11*z2 + 32)*x^2 + 48*z2 + 70) * (x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25)
1169+
sage: phi.minimal_polynomial().factor()
1170+
x^4 + (57*z2 + 65)*x^2 + 20*z2 + 25
1171+
"""
1172+
#FIXME This can probably be implemented better!
1173+
h = self.kernel_polynomial()
1174+
for f,_ in reversed(h.factor()):
1175+
if self.domain().kernel_polynomial_from_divisor(f, self.degree()) == h:
1176+
return f
1177+
raise ValueError('not a cyclic isogeny')
1178+
1179+
def push_subgroup(self, f):
1180+
r"""
1181+
Given a irreducible divisor `f` of an `l`-division polynomial on the
1182+
domain curve of this isogeny, return an irreducible polynomial `f'`
1183+
such that the subgroup defined by `f` is mapped to the subgroup
1184+
defined by `f'`. See :meth:`minimal_polynomial()`.
1185+
1186+
ALGORITHM: [EPSV2023]_, Algorithm 5 (``PushSubgroup'')
1187+
1188+
EXAMPLES::
1189+
1190+
sage: E = EllipticCurve(GF(419), [32, 41])
1191+
sage: K = E.lift_x(30)
1192+
sage: phi = E.isogeny(K); phi
1193+
Isogeny of degree 7
1194+
from Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1195+
to Elliptic Curve defined by y^2 = x^3 + 316*x + 241 over Finite Field of size 419
1196+
sage: psi = E.isogeny(E.lift_x(54), algorithm='factored'); psi
1197+
Composite morphism of degree 15 = 3*5:
1198+
From: Elliptic Curve defined by y^2 = x^3 + 32*x + 41 over Finite Field of size 419
1199+
To: Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
1200+
sage: f = phi.minimal_polynomial(); f # random -- one of x+161, x+201, x+389
1201+
x + 161
1202+
sage: g = psi.push_subgroup(f); g # random -- one of x+148, x+333, x+249
1203+
x + 148
1204+
sage: h = psi.codomain().kernel_polynomial_from_divisor(g, phi.degree()); h
1205+
x^3 + 311*x^2 + 196*x + 44
1206+
sage: chi = psi.codomain().isogeny(h); chi
1207+
Isogeny of degree 7
1208+
from Elliptic Curve defined by y^2 = x^3 + 36*x + 305 over Finite Field of size 419
1209+
to Elliptic Curve defined by y^2 = x^3 + 186*x + 37 over Finite Field of size 419
1210+
sage: (chi * psi)(K)
1211+
(0 : 1 : 0)
1212+
1213+
It also works for rational isogenies with irrational kernel points::
1214+
1215+
sage: E = EllipticCurve(GF(127^2), [1,0])
1216+
sage: phi = E.isogenies_prime_degree(13)[0]; phi
1217+
Isogeny of degree 13
1218+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1219+
to Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1220+
sage: psi = E.isogenies_prime_degree(17)[0]; psi
1221+
Isogeny of degree 17
1222+
from Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 127^2
1223+
to Elliptic Curve defined by y^2 = x^3 + (16*z2+26)*x over Finite Field in z2 of size 127^2
1224+
sage: f_phi = phi.minimal_polynomial()
1225+
sage: g_phi = psi.push_subgroup(f_phi)
1226+
sage: h_phi = psi.codomain().kernel_polynomial_from_divisor(g_phi, phi.degree())
1227+
sage: phi_pushed = psi.codomain().isogeny(h_phi); phi_pushed
1228+
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
1229+
sage: f_psi = psi.minimal_polynomial()
1230+
sage: g_psi = phi.push_subgroup(f_psi)
1231+
sage: h_psi = phi.codomain().kernel_polynomial_from_divisor(g_psi, psi.degree())
1232+
sage: psi_pushed = phi.codomain().isogeny(h_psi); psi_pushed
1233+
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
1234+
sage: any(iso * psi_pushed * phi == phi_pushed * psi
1235+
....: for iso in psi_pushed.codomain().isomorphisms(phi_pushed.codomain()))
1236+
True
1237+
"""
1238+
g = self.x_rational_map()
1239+
g1, g2 = g.numerator(), g.denominator()
1240+
gker = g2.gcd(f)
1241+
f1 = f // gker
1242+
R = f1.parent()
1243+
S = R.quotient_ring(f1)
1244+
alpha = S(g1 * g2.inverse_mod(f1))
1245+
return alpha.minpoly()
1246+
11331247

11341248
def compare_via_evaluation(left, right):
11351249
r"""

0 commit comments

Comments
 (0)