diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index d03346deacc..1d8837356c1 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1018,12 +1018,6 @@ def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True # Inheritance house keeping self.__perform_inheritance_housekeeping() - # over finite fields, isogenous curves have the same number - # of rational points, hence we copy over cached curve orders - if self.__base_field.is_finite(): - self._codomain._fetch_cached_order(self._domain) - self._domain._fetch_cached_order(self._codomain) - def _eval(self, P): r""" Less strict evaluation method for internal use. @@ -1157,6 +1151,16 @@ def _call_(self, P): Traceback (most recent call last): ... TypeError: (20 : 90 : 1) fails to convert into the map's domain Elliptic Curve defined by y^2 = x^3 + 7*x over Number Field in th with defining polynomial x^2 + 3, but a `pushforward` method is not properly implemented + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431), [1,0]) + sage: P, = E.gens() + sage: Q = 2^99*P; Q.order() + 27 + sage: phi = E.isogeny(3^99*P) + sage: phi(Q)._order + 27 """ if P.is_zero(): return self._codomain(0) @@ -1187,7 +1191,16 @@ def _call_(self, P): yP = self.__posti_ratl_maps[1](xP, yP) xP = self.__posti_ratl_maps[0](xP) - return self._codomain(xP, yP) + Q = self._codomain(xP, yP) + if hasattr(P, '_order') and P._order.gcd(self._degree).is_one(): + # TODO: For non-coprime degree, the order of the point + # gets reduced by a divisor of the degree when passing + # through the isogeny. We could run something along the + # lines of order_from_multiple() to determine the new + # order, but this probably shouldn't happen by default + # as it'll be detrimental to performance in some cases. + Q._order = P._order + return Q def __getitem__(self, i): r""" diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 6b64df4d075..2b38144b046 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -1024,53 +1024,6 @@ def division_field(self, l, names='t', map=False, **kwds): else: return L - def _fetch_cached_order(self, other): - r""" - This method copies the ``_order`` member from ``other`` - to ``self`` if their base field is the same and finite. - - This is used in :class:`EllipticCurveIsogeny` to keep track of - an already computed curve order: According to Tate's theorem - [Tate1966b]_, isogenous elliptic curves over a finite field - have the same number of rational points. - - EXAMPLES:: - - sage: E1 = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) - sage: E1.set_order(170141183460469231746191640949390434666) - sage: E2 = EllipticCurve(GF(2^127-1), [115649500210559831225094148253060920818, 36348294106991415644658737184600079491]) - sage: E2._fetch_cached_order(E1) - sage: E2._order - 170141183460469231746191640949390434666 - - TESTS:: - - sage: E3 = EllipticCurve(GF(17), [1,2,3,4,5]) - sage: hasattr(E3, '_order') - False - sage: E3._fetch_cached_order(E1) - Traceback (most recent call last): - ... - ValueError: curves have distinct base fields - - :: - - sage: E4 = EllipticCurve([1,2,3,4,5]) - sage: E4._fetch_cached_order(E1.change_ring(QQ)) - sage: hasattr(E4, '_order') - False - """ - if hasattr(self, '_order') or not hasattr(other, '_order'): - return - F = self.base_field() - if F != other.base_field(): - raise ValueError('curves have distinct base fields') - if not F.is_finite(): - raise ValueError('base field must be finite') - n = getattr(other, '_order', None) - if n is not None: - self._order = n - def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True, algorithm=None): r""" Return an elliptic-curve isogeny from this elliptic curve. diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 31e193a64b6..a8235585b17 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1289,6 +1289,46 @@ def set_order(self, value, num_checks=8): self._order = value + def _fetch_cached_order(self, other): + r""" + This method copies the ``_order`` member from ``other`` to + ``self``. Both curves must have the same finite base field. + + This is used in + :class:`~sage.schemes.elliptic_curves.hom.EllipticCurveHom` + to keep track of an already computed curve order: According + to Tate's theorem [Tate1966b]_, isogenous elliptic curves + over a finite field have the same number of rational points. + + EXAMPLES:: + + sage: E1 = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) + sage: E1.set_order(170141183460469231746191640949390434666) + sage: E2 = EllipticCurve(GF(2^127-1), [115649500210559831225094148253060920818, 36348294106991415644658737184600079491]) + sage: E2._fetch_cached_order(E1) + sage: E2._order + 170141183460469231746191640949390434666 + + TESTS:: + + sage: E3 = EllipticCurve(GF(17), [1,2,3,4,5]) + sage: hasattr(E3, '_order') + False + sage: E3._fetch_cached_order(E1) + Traceback (most recent call last): + ... + ValueError: curves have distinct base fields + """ + if hasattr(self, '_order') or not hasattr(other, '_order'): + return + F = self.base_field() + if F != other.base_field(): + raise ValueError('curves have distinct base fields') + n = getattr(other, '_order', None) + if n is not None: + self._order = n + + # dict to hold precomputed coefficient vectors of supersingular j values (excluding 0, 1728): supersingular_j_polynomials = {} diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 154a581bc2a..4cd98d31e00 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -559,6 +559,8 @@ def __call__(self, *args, **kwds): (ell_point.EllipticCurvePoint_field, ell_point.EllipticCurvePoint_number_field, ell_point.EllipticCurvePoint)): + if P.curve() is self: + return P # check if denominator of the point contains a factor of the # characteristic of the base ring. if so, coerce the point to # infinity. diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index bdf2c969079..e6806b7868e 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -35,6 +35,39 @@ class EllipticCurveHom(Morphism): """ Base class for elliptic-curve morphisms. """ + def __init__(self, *args, **kwds): + r""" + Constructor for elliptic-curve morphisms. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(257^2), [5,5]) + sage: P = E.lift_x(1) + sage: E.isogeny(P) # indirect doctest + Isogeny of degree 127 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 to Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2 + sage: E.isogeny(P, algorithm='factored') # indirect doctest + Composite morphism of degree 127 = 127: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2 + sage: E.isogeny(P, algorithm='velusqrt') # indirect doctest + Elliptic-curve isogeny (using Îlu) of degree 127: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + 119*x + 231 over Finite Field in z2 of size 257^2 + sage: E.montgomery_model(morphism=True) # indirect doctest + (Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2, + Elliptic-curve morphism: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2 + Via: (u,r,s,t) = (88*z2 + 253, 208*z2 + 90, 0, 0)) + """ + super().__init__(*args, **kwds) + + # Over finite fields, isogenous curves have the same number of + # rational points, hence we copy over the cached curve orders. + if isinstance(self.base_ring(), finite_field_base.FiniteField) and self.degree(): + self._codomain._fetch_cached_order(self._domain) + self._domain._fetch_cached_order(self._codomain) + def _repr_type(self): """ Return a textual representation of what kind of morphism diff --git a/src/sage/schemes/elliptic_curves/hom_composite.py b/src/sage/schemes/elliptic_curves/hom_composite.py index b2096eda5b8..7ca458dc43d 100644 --- a/src/sage/schemes/elliptic_curves/hom_composite.py +++ b/src/sage/schemes/elliptic_curves/hom_composite.py @@ -398,6 +398,16 @@ def _call_(self, P): sage: R = E.lift_x(15/4 * (a+3)) sage: psi(R) # indirect doctest (1033648757/303450 : 58397496786187/1083316500*a - 62088706165177/2166633000 : 1) + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431), [1,0]) + sage: P, = E.gens() + sage: Q = 2^99*P; Q.order() + 27 + sage: phi = E.isogeny(3^99*P, algorithm='factored') + sage: phi(Q)._order + 27 """ return _eval_factored_isogeny(self._phis, P) diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 144b5400970..8f0befbc5ca 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -896,24 +896,23 @@ def __init__(self, E, P, *, codomain=None, model=None, Q=None): if codomain is not None and model is not None: raise ValueError('cannot specify a codomain curve and model name simultaneously') - try: - self._raw_domain = E.short_weierstrass_model() - except ValueError: - raise NotImplementedError('only implemented for curves having a short Weierstrass model') - self._pre_iso = E.isomorphism_to(self._raw_domain) - try: P = E(P) except TypeError: raise ValueError('given kernel point P does not lie on E') - self._P = self._pre_iso(P) - - self._degree = self._P.order() + self._degree = P.order() if self._degree % 2 != 1 or self._degree < 9: raise NotImplementedError('only implemented for odd degrees >= 9') + try: + self._raw_domain = E.short_weierstrass_model() + except ValueError: + raise NotImplementedError('only implemented for curves having a short Weierstrass model') + self._pre_iso = E.isomorphism_to(self._raw_domain) + self._P = self._pre_iso(P) + if Q is not None: - self._Q = E(Q) + self._Q = self._pre_iso(E(Q)) EE = E else: self._Q = _point_outside_subgroup(self._P) # may extend base field diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index a9ad45f6fb2..85c673101d5 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -634,12 +634,28 @@ def __call__(self, P): (-3/4 : 3/4 : 1) sage: w(P).curve() == E.change_weierstrass_model((2,3,4,5)) True + + TESTS: + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431^2), [1,0]) + sage: i = next(a for a in E.automorphisms() if a^2 == -a^24) + sage: P,_ = E.gens() + sage: P._order + 432 + sage: i(P)._order + 432 + sage: E(i(P))._order + 432 """ if P[2] == 0: return self._codomain(0) - return self._codomain.point(baseWI.__call__(self, - tuple(P._coords)), - check=False) + res = baseWI.__call__(self, tuple(P._coords)) + Q = self._codomain.point(res, check=False) + if hasattr(P, '_order'): + Q._order = P._order + return Q def __invert__(self): r"""