diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 0e2497fbcda..e0f07c0191d 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1074,7 +1074,7 @@ def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True self.__algorithm = algorithm if algorithm == 'velu': - self.__init_from_kernel_list(kernel) + self.__init_from_kernel_gens(kernel) elif algorithm == 'kohel': self.__init_from_kernel_polynomial(kernel) else: @@ -1870,7 +1870,7 @@ def __setup_post_isomorphism(self, codomain, model): # Setup function for Velu's formula # - def __init_from_kernel_list(self, kernel_gens): + def __init_from_kernel_gens(self, kernel_gens): r""" Private function that initializes the isogeny from a list of points which generate the kernel (For Vélu's formulas.) @@ -1884,7 +1884,7 @@ def __init_from_kernel_list(self, kernel_gens): Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 4*x over Finite Field of size 7 - sage: phi._EllipticCurveIsogeny__init_from_kernel_list([E(0), E((0,0))]) + sage: phi._EllipticCurveIsogeny__init_from_kernel_gens([E(0), E((0,0))]) The following example demonstrates the necessity of avoiding any calls to P.order(), since such calls involve factoring the group order which @@ -1907,6 +1907,14 @@ def __init_from_kernel_list(self, kernel_gens): if not P.has_finite_order(): raise ValueError("given kernel contains point of infinite order") + self.__kernel_mod_sign = {} + self.__v = self.__w = 0 + + # Fast path: The kernel is given by a single generating point. + if len(kernel_gens) == 1 and kernel_gens[0]: + self.__init_from_kernel_point(kernel_gens[0]) + return + # Compute a list of points in the subgroup generated by the # points in kernel_gens. This is very naive: when finite # subgroups are implemented better, this could be simplified, @@ -1927,12 +1935,86 @@ def all_multiples(itr, terminal): self._degree = Integer(len(kernel_set)) self.__kernel_list = list(kernel_set) - self.__sort_kernel_list() + self.__init_from_kernel_list() # # Precompute the values in Velu's Formula. # - def __sort_kernel_list(self): + def __update_kernel_data(self, xQ, yQ): + r""" + Internal helper function to update some data coming from the + kernel points of this isogeny when using Vélu's formulas. + + TESTS: + + The following example inherently exercises this function:: + + sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) + sage: P = E((4,2)) + sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + """ + a1, a2, a3, a4, _ = self._domain.a_invariants() + + gxQ = (3*xQ + 2*a2)*xQ + a4 - a1*yQ + gyQ = -2*yQ - a1*xQ - a3 + + uQ = gyQ**2 + + if 2*yQ == -a1*xQ - a3: # Q is 2-torsion + vQ = gxQ + else: # Q is not 2-torsion + vQ = 2*gxQ - a1*gyQ + + self.__kernel_mod_sign[xQ] = yQ, gxQ, gyQ, vQ, uQ + + self.__v += vQ + self.__w += uQ + xQ*vQ + + def __init_from_kernel_point(self, ker): + r""" + Private function with functionality equivalent to + :meth:`__init_from_kernel_list`, but optimized for when + the kernel is given by a single point. + + TESTS: + + The following example inherently exercises this function:: + + sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) + sage: P = E((4,2)) + sage: phi = EllipticCurveIsogeny(E, P); phi # implicit doctest + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + + We check that the result is the same as for :meth:`__init_from_kernel_list`:: + + sage: psi = EllipticCurveIsogeny(E, [P, P]); psi + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 + to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 + sage: phi == psi + True + """ + self._degree = Integer(1) + + Q, prevQ = ker, self._domain(0) + + while Q and Q != -prevQ: + self.__update_kernel_data(*Q.xy()) + + if Q == -Q: + self._degree += 1 + break + + prevQ = Q + Q += ker + self._degree += 2 + + def __init_from_kernel_list(self): r""" Private function that sorts the list of points in the kernel (For Vélu's formulas). Sorts out the 2-torsion points, and @@ -1944,43 +2026,22 @@ def __sort_kernel_list(self): sage: E = EllipticCurve(GF(7), [0,0,0,-1,0]) sage: P = E((4,2)) - sage: phi = EllipticCurveIsogeny(E, P); phi + sage: phi = EllipticCurveIsogeny(E, [P,P]); phi # implicit doctest Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + 6*x over Finite Field of size 7 to Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 7 - sage: phi._EllipticCurveIsogeny__sort_kernel_list() """ - a1, a2, a3, a4, _ = self._domain.a_invariants() - - self.__kernel_mod_sign = {} - v = w = 0 - for Q in self.__kernel_list: if Q.is_zero(): continue - xQ,yQ = Q.xy() + xQ, yQ = Q.xy() if xQ in self.__kernel_mod_sign: continue - gxQ = (3*xQ + 2*a2)*xQ + a4 - a1*yQ - gyQ = -2*yQ - a1*xQ - a3 - - uQ = gyQ**2 - - if 2*yQ == -a1*xQ - a3: # Q is 2-torsion - vQ = gxQ - else: # Q is not 2-torsion - vQ = 2*gxQ - a1*gyQ - - self.__kernel_mod_sign[xQ] = yQ, gxQ, gyQ, vQ, uQ - - v += vQ - w += uQ + xQ*vQ - - self.__v, self.__w = v, w + self.__update_kernel_data(xQ, yQ) # # Velu's formula computing the codomain curve