@@ -1130,6 +1130,120 @@ def matrix_on_subgroup(self, domain_gens, codomain_gens=None):
1130
1130
from sage .rings .finite_rings .integer_mod_ring import Zmod
1131
1131
return matrix (Zmod (n ), [vecP , vecQ ])
1132
1132
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
+
1133
1247
1134
1248
def compare_via_evaluation (left , right ):
1135
1249
r"""
0 commit comments