From 64865d221f38067d633933288a1af9fb61ab5af3 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 5 Nov 2021 12:38:56 +0800 Subject: [PATCH 01/11] scalar-multiplication endomorphisms --- .../en/reference/arithmetic_curves/index.rst | 1 + .../schemes/elliptic_curves/ell_generic.py | 29 + src/sage/schemes/elliptic_curves/hom.py | 8 +- .../schemes/elliptic_curves/hom_scalar.py | 529 ++++++++++++++++++ .../elliptic_curves/weierstrass_morphism.py | 17 + 5 files changed, 582 insertions(+), 2 deletions(-) create mode 100644 src/sage/schemes/elliptic_curves/hom_scalar.py diff --git a/src/doc/en/reference/arithmetic_curves/index.rst b/src/doc/en/reference/arithmetic_curves/index.rst index 9fbc0495854..167e1b7daa6 100644 --- a/src/doc/en/reference/arithmetic_curves/index.rst +++ b/src/doc/en/reference/arithmetic_curves/index.rst @@ -22,6 +22,7 @@ Maps between them sage/schemes/elliptic_curves/ell_curve_isogeny sage/schemes/elliptic_curves/isogeny_small_degree sage/schemes/elliptic_curves/hom_composite + sage/schemes/elliptic_curves/hom_scalar Elliptic curves over number fields diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 965271f7241..0cfad179daf 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2215,6 +2215,35 @@ def multiplication_by_m_isogeny(self, m): assert False, 'bug in multiplication_by_m_isogeny()' + def scalar_multiplication(self, m): + r""" + Return the scalar-multiplication map `[m]` on this elliptic + curve as a + :class:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar` + object. + + .. WARNING:: + + This method is currently experimental. It is intended to + eventually supersede :meth:`multiplication_by_m_isogeny`. + + EXAMPLES:: + + sage: E = EllipticCurve('77a1') + sage: m = E.scalar_multiplication(-7); m + doctest:warning ... + Scalar-multiplication endomorphism [-7] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field + sage: m.degree() + 49 + sage: P = E(2,3) + sage: m(P) + (-26/225 : -2132/3375 : 1) + sage: m.rational_maps() == E.multiplication_by_m(-7) + True + """ + from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + return EllipticCurveHom_scalar(self, m) + def isomorphism_to(self, other): """ Given another weierstrass model ``other`` of self, return an diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 10c7afae878..2ff47442de6 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -169,6 +169,7 @@ def degree(self): - :meth:`EllipticCurveIsogeny.degree` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.degree` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.degree` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.degree` TESTS:: @@ -189,6 +190,7 @@ def kernel_polynomial(self): - :meth:`EllipticCurveIsogeny.kernel_polynomial` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.kernel_polynomial` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.kernel_polynomial` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.kernel_polynomial` TESTS:: @@ -209,6 +211,7 @@ def dual(self): - :meth:`EllipticCurveIsogeny.dual` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.dual` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.dual` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.dual` TESTS:: @@ -231,6 +234,7 @@ def rational_maps(self): - :meth:`EllipticCurveIsogeny.rational_maps` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.rational_maps` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.rational_maps` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.rational_maps` TESTS:: @@ -252,6 +256,7 @@ def x_rational_map(self): - :meth:`EllipticCurveIsogeny.x_rational_map` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.x_rational_map` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.x_rational_map` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.x_rational_map` TESTS:: @@ -529,8 +534,7 @@ def __neg__(self): sage: psi.rational_maps() == (f, -g) True """ - a1,_,a3,_,_ = self.codomain().a_invariants() - return wm.WeierstrassIsomorphism(self.codomain(), (-1,0,-a1,-a3)) * self + return wm.negation_morphism(self.codomain()) * self @cached_method def __hash__(self): diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py new file mode 100644 index 00000000000..60672dec291 --- /dev/null +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -0,0 +1,529 @@ +r""" +Scalar-multiplication morphisms of elliptic curves + +This class provides an :class:`EllipticCurveHom` instantiation for +multiplication-by-`m` maps on elliptic curves. + +.. WARNING:: + + This module is currently considered experimental. + It may change in a future release without prior warning, or even + be removed altogether if things turn out to be unfixably broken. + +EXAMPLES: + +We can construct and evaluate scalar multiplications:: + + sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + doctest:warning ... + sage: E = EllipticCurve('77a1') + sage: phi = E.scalar_multiplication(5); phi + Scalar-multiplication endomorphism [5] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field + sage: P = E(2,3) + sage: phi(P) + (30 : 164 : 1) + +The usual :class:`EllipticCurveHom` methods are supported:: + + sage: phi.degree() + 25 + sage: phi.kernel_polynomial() + x^12 + 124/5*x^10 + 19*x^9 - 84*x^8 + 24*x^7 - 483*x^6 - 696/5*x^5 - 448*x^4 - 37*x^3 - 332*x^2 - 84*x + 47/5 + sage: phi.rational_maps() + ((x^25 - 200*x^23 - 520*x^22 + 9000*x^21 + ... + 1377010*x^3 + 20360*x^2 - 39480*x + 2209), + (10*x^36*y - 620*x^36 + 3240*x^34*y - 44880*x^34 + ... + 424927560*x*y + 226380480*x + 42986410*y + 20974090)/(1250*x^36 + 93000*x^34 + 71250*x^33 + 1991400*x^32 + ... + 1212964050*x^3 + 138715800*x^2 - 27833400*x + 1038230)) + sage: phi.dual() + Scalar-multiplication endomorphism [5] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field + sage: phi.dual() is phi + True + sage: phi.formal() + 5*t - 310*t^4 - 2496*t^5 + 10540*t^7 + ... - 38140146674516*t^20 - 46800256902400*t^21 + 522178541079910*t^22 + O(t^23) + sage: phi.is_normalized() + False + sage: phi.is_separable() + True + sage: phi.is_injective() + False + sage: phi.is_surjective() + True + +Contrary to constructing an :class:`EllipticCurveIsogeny` from +the division polynomial, :class:`EllipticCurveHom_scalar` can +deal with huge scalars very quickly:: + + sage: E = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) + sage: phi = E.scalar_multiplication(9^99); phi + Scalar-multiplication endomorphism [29512665430652752148753480226197736314359272517043832886063884637676943433478020332709411004889] of Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 170141183460469231731687303715884105727 + sage: phi(E(1,2)) + (82124533143060719620799539030695848450 : 17016022038624814655722682134021402379 : 1) + +Composition of scalar multiplications results in another scalar +multiplication:: + + sage: E = EllipticCurve(GF(19), [4,4]) + sage: phi = E.scalar_multiplication(-3); phi + Scalar-multiplication endomorphism [-3] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: psi = E.scalar_multiplication(7); psi + Scalar-multiplication endomorphism [7] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: phi * psi + Scalar-multiplication endomorphism [-21] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: psi * phi + Scalar-multiplication endomorphism [-21] of Elliptic Curve defined by y^2 = x^3 + 4*x + 4 over Finite Field of size 19 + sage: phi * psi == psi * phi + True + sage: -phi == E.scalar_multiplication(-1) * phi + True + +The zero endomorphism `[0]` is supported:: + + sage: E = EllipticCurve(GF(71), [1,1]) + sage: zero = E.scalar_multiplication(0); zero + Scalar-multiplication endomorphism [0] of Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 71 + sage: zero.is_zero() + True + sage: zero.is_injective() + False + sage: zero.is_surjective() + False + sage: zero(E.random_point()) + (0 : 1 : 0) + +Due to a bug (:trac:`6413`), retrieving multiplication-by-`m` maps +when `m` is divisible by the characteristic currently fails:: + + sage: E = EllipticCurve(GF(7), [1,0]) + sage: phi = E.scalar_multiplication(7); phi + Scalar-multiplication endomorphism [7] of Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 7 + sage: phi.rational_maps() # known bug -- #6413 + (x^49, y^49) + sage: phi.x_rational_map() + x^49 + +:: + + sage: E = EllipticCurve(GF(7), [0,1]) + sage: phi = E.scalar_multiplication(7); phi + Scalar-multiplication endomorphism [7] of Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 + sage: phi.rational_maps() # known bug -- #6413 + ((-3*x^49 - x^28 - x^7)/(x^42 - x^21 + 2), + (-x^72*y - 3*x^69*y - 3*x^66*y - x^63*y + 3*x^51*y + 2*x^48*y + 2*x^45*y + 3*x^42*y - x^9*y - 3*x^6*y - 3*x^3*y - y)/(x^63 + 2*x^42 - x^21 - 1)) + sage: phi.x_rational_map() + (4*x^49 + 6*x^28 + 6*x^7)/(x^42 + 6*x^21 + 2) + +TESTS:: + + sage: E = EllipticCurve(j = GF(65537^3).random_element()) + sage: m = randrange(-2^99, +2^99) + sage: phi = E.scalar_multiplication(m) + sage: phi.degree() == m**2 + True + sage: P = E.random_point() + sage: phi(P) == m*P + True + +AUTHORS: + +- Lorenz Panny (2021): implement :class:`EllipticCurveHom_scalar` +""" + +from sage.misc.cachefunc import cached_method +from sage.structure.richcmp import richcmp + +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic +from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, negation_morphism +from sage.schemes.elliptic_curves.hom import EllipticCurveHom + +from sage.misc.superseded import experimental_warning +experimental_warning(32826, 'EllipticCurveHom_scalar is experimental code.') + +class EllipticCurveHom_scalar(EllipticCurveHom): + + def __init__(self, E, m): + """ + Construct a scalar-multiplication map on an elliptic curve. + + TESTS:: + + sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + sage: E = EllipticCurve([1,1]) + sage: EllipticCurveHom_scalar(E, 123) + Scalar-multiplication endomorphism [123] of Elliptic Curve defined by y^2 = x^3 + x + 1 over Rational Field + """ + if not isinstance(E, EllipticCurve_generic): + raise ValueError(f'not an elliptic curve: {E}') + + self._m = ZZ(m) + + self._degree = self._m**2 + self._domain = self._codomain = E + + EllipticCurveHom.__init__(self, self._domain, self._codomain) + + # TODO: should probably be in EllipticCurveHom? + self._base_ring = self._domain.base_ring() + self._poly_ring = PolynomialRing(self._base_ring, ['x']) + self._mpoly_ring = PolynomialRing(self._base_ring, ['x','y']) + + self._rational_maps = None + + def _call_(self, P): + """ + Evaluate this scalar-multiplication map `[m]` at a point `P`, + i.e., return `[m]P`. + + TESTS:: + + sage: p = random_prime(2^22) + sage: q = p^randrange(1,5) + sage: E = EllipticCurve_from_j(GF(q).random_element()) + sage: m = randrange(-9^99, 9^99) + sage: phi = E.scalar_multiplication(m) + sage: P = E.random_point() + sage: phi(P) == m*P + True + """ + if P not in self._domain: + raise ValueError(f'{P} is not a point on {self._domain}') + return self._m * P + + def _eval(self, P): + """ + Less strict evaluation method for internal use. + + In particular, this can be used to evaluate ``self`` at a + point defined over an extension field. + + INPUT: a sequence of 3 coordinates defining a point on ``self`` + + OUTPUT: the result of evaluating ``self'' at the given point + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar + sage: E = EllipticCurve(j=Mod(1728,419)) + sage: psi = EllipticCurveHom_scalar(E, 13) + sage: P = E.change_ring(GF(419**2)).lift_x(5) + sage: P = min({P, -P}) # fix choice of y + sage: Q = psi._eval(P); Q + (134 : 210*z2 + 314 : 1) + sage: Q.curve() + Elliptic Curve defined by y^2 = x^3 + x over Finite Field in z2 of size 419^2 + """ + if self._domain.defining_polynomial()(*P): + raise ValueError(f'{P} not on {self._domain}') + return self._m * P + + + def _repr_(self): + """ + Return basic facts about this scalar multiplication as a string. + + TESTS:: + + sage: E = EllipticCurve([i,i]) + sage: E.scalar_multiplication(777) + Scalar-multiplication endomorphism [777] of Elliptic Curve defined by y^2 = x^3 + I*x + I over Number Field in I with defining polynomial x^2 + 1 with I = 1*I + """ + return f'Scalar-multiplication endomorphism [{self._m}] of {self._domain}' + + + # EllipticCurveHom methods + + @staticmethod + def _composition_impl(self, other): + """ + Helper method to compose other elliptic-curve morphisms with + :class:`EllipticCurveHom_scalar` objects. Called by + :meth:`EllipticCurveHom._composition_`. + + This method only handles composing two scalar multiplications; + all other cases are dealt with elsewhere. + + TESTS:: + + sage: E = EllipticCurve([1,2,3,4,5]) + sage: phi = E.scalar_multiplication(5) + sage: psi = E.scalar_multiplication(-7) + sage: phi * psi # implicit doctest + Scalar-multiplication endomorphism [-35] of Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Rational Field + + :: + + sage: phi._composition_impl(phi, E.automorphisms()[0]) + NotImplemented + """ + if isinstance(self, EllipticCurveHom_scalar) and isinstance(other, EllipticCurveHom_scalar): + assert self._domain == other._domain + return EllipticCurveHom_scalar(self._domain, self._m * other._m) + return NotImplemented + + def degree(self): + """ + Return the degree of this scalar-multiplication morphism. + + The map `[m]` has degree `m^2`. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(23), [0,1]) + sage: phi = E.scalar_multiplication(1111111) + sage: phi.degree() + 1234567654321 + + TESTS: + + The degree is still `m^2` even in the inseparable case:: + + sage: E = EllipticCurve(GF(23), [1,1]) + sage: E.scalar_multiplication(23).degree() + 529 + sage: E = EllipticCurve(GF(23), [0,1]) + sage: E.scalar_multiplication(23).degree() + 529 + """ + return self._degree + + def _richcmp_(self, other, op): + """ + Compare this scalar multiplication to another elliptic-curve morphism. + + .. WARNING:: + + This method sometimes calls :meth:`EllipticCurveHom._richcmp_`, + which sometimes compares :meth:`rational_maps`. Therefore, the + complexity is at least quadratic in `m` in the worst case. + + EXAMPLES:: + + sage: E = EllipticCurve([i,i]) + sage: phi = E.scalar_multiplication(-5) + sage: psi = E.scalar_multiplication(5) + sage: phi == -psi + True + + TESTS: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite + doctest:warning ... + sage: neg = WeierstrassIsomorphism(E, [-1,0,0,0]) + sage: neg_psi = EllipticCurveHom_composite.from_factors([psi,neg]) + sage: psi_neg = EllipticCurveHom_composite.from_factors([neg,psi]) + sage: phi == neg_psi == psi_neg == -psi + True + """ + if isinstance(other, EllipticCurveHom_scalar): + return richcmp((self._domain, self._m), (other._domain, other._m), op) + return EllipticCurveHom._richcmp_(self, other, op) + + def rational_maps(self): + """ + Return the pair of explicit rational maps defining this scalar + multiplication. + + ALGORITHM: :meth:`EllipticCurve_generic.multiplication_by_m` + + EXAMPLES:: + + sage: E = EllipticCurve('77a1') + sage: phi = E.scalar_multiplication(5) + sage: phi.rational_maps() + ((x^25 - 200*x^23 - 520*x^22 + ... + 368660*x^2 + 163195*x + 16456)/(25*x^24 + 1240*x^22 + 950*x^21 + ... + 20360*x^2 - 39480*x + 2209), + (10*x^36*y - 620*x^36 + 3240*x^34*y - ... + 226380480*x + 42986410*y + 20974090)/(1250*x^36 + 93000*x^34 + 71250*x^33 + ... + 138715800*x^2 - 27833400*x + 1038230)) + sage: P = (2,3) + sage: Q = tuple(r(P) for r in phi.rational_maps()); Q + (30, 164) + sage: E(Q) == 5*E(P) + True + + TESTS:: + + sage: {r.parent() for r in phi.rational_maps()} + {Fraction Field of Multivariate Polynomial Ring in x, y over Rational Field} + """ + if not self._rational_maps or None in self._rational_maps: + if not self._m: + raise ValueError('[0] is not expressible in (x,y) coordinates') + self._rational_maps = self._domain.multiplication_by_m(self._m) + return self._rational_maps + + def x_rational_map(self): + """ + Return the `x`-coordinate rational map of this scalar + multiplication. + + ALGORITHM: :meth:`EllipticCurve_generic.multiplication_by_m` + + EXAMPLES:: + + sage: E = EllipticCurve(GF(65537), [1,2,3,4,5]) + sage: phi = E.scalar_multiplication(7) + sage: phi.x_rational_map() == phi.rational_maps()[0] + True + + TESTS:: + + sage: phi.x_rational_map().parent() + Fraction Field of Univariate Polynomial Ring in x over Finite Field of size 65537 + """ + if not self._rational_maps: + if not self._m: + raise ValueError('[0] is not expressible in (x,y) coordinates') + h = self._domain.multiplication_by_m(self._m, x_only=True) + self._rational_maps = (self._mpoly_ring.fraction_field()(h), None) + f,g = map(self._poly_ring, (self._rational_maps[0].numerator(), + self._rational_maps[0].denominator())) + return f / g + + @cached_method + def kernel_polynomial(self): + r""" + Return the kernel polynomial of this scalar-multiplication map. + (When `m=0`, return `0`.) + + EXAMPLES:: + + sage: E = EllipticCurve(GF(997), [7,7,7,7,7]) + sage: phi = E.scalar_multiplication(5) + sage: phi.kernel_polynomial() + x^12 + 77*x^11 + 380*x^10 + 198*x^9 + 840*x^8 + 376*x^7 + 946*x^6 + 848*x^5 + 246*x^4 + 778*x^3 + 77*x^2 + 518*x + 28 + + :: + + sage: E = EllipticCurve(GF(997), [5,6,7,8,9]) + sage: phi = E.scalar_multiplication(11) + sage: phi.kernel_polynomial() + x^60 + 245*x^59 + 353*x^58 + 693*x^57 + 499*x^56 + 462*x^55 + 820*x^54 + 962*x^53 + ... + 736*x^7 + 939*x^6 + 429*x^5 + 267*x^4 + 116*x^3 + 770*x^2 + 491*x + 519 + + TESTS:: + + sage: E = EllipticCurve(j = GF(997^6).random_element()) + sage: m = choice([+1,-1]) * randrange(1,8) + sage: phi = E.scalar_multiplication(m) + sage: phi.kernel_polynomial() == phi.x_rational_map().denominator().monic().radical() + True + + :: + + sage: E.scalar_multiplication(randint(-10,+10)).kernel_polynomial().parent() + Univariate Polynomial Ring in x over Finite Field in z6 of size 997^6 + """ + if not self._m: + return self._poly_ring(0) + # TODO: inseparable case should be consistent with Frobenius' .kernel_polynomial() + return self._domain.division_polynomial(self._m.abs()).monic().radical() + + def dual(self): + """ + Return the dual isogeny of this scalar-multiplication map. + + This method simply returns ``self`` as scalars are self-dual. + + EXAMPLES:: + + sage: E = EllipticCurve([5,5]) + sage: phi = E.scalar_multiplication(5) + sage: phi.dual() is phi + True + """ + return self + + def is_separable(self): + """ + Determine whether this scalar-multiplication map is a + separable isogeny. (This is the case if and only if the + scalar `m` is coprime to the characteristic.) + + EXAMPLES:: + + sage: E = EllipticCurve(GF(11), [4,4]) + sage: E.scalar_multiplication(11).is_separable() + False + sage: E.scalar_multiplication(-11).is_separable() + False + sage: E.scalar_multiplication(777).is_separable() + True + sage: E.scalar_multiplication(-1).is_separable() + True + sage: E.scalar_multiplication(77).is_separable() + False + sage: E.scalar_multiplication(121).is_separable() + False + + TESTS:: + + sage: E.scalar_multiplication(0).is_separable() + Traceback (most recent call last): + ... + ValueError: [0] is not an isogeny + """ + if self._m.is_zero(): + raise ValueError('[0] is not an isogeny') + p = self._domain.base_ring().characteristic() + return p == 0 or self._m.gcd(p) == 1 + + + def is_injective(self): + """ + Determine whether this scalar multiplication defines an + injective map (over the algebraic closure). + + Equivalently, return ``True`` if and only if this scalar + multiplication is a purely inseparable isogeny. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(23), [1,0]) + sage: E.scalar_multiplication(4).is_injective() + False + sage: E.scalar_multiplication(5).is_injective() + False + sage: E.scalar_multiplication(1).is_injective() + True + sage: E.scalar_multiplication(-1).is_injective() + True + sage: E.scalar_multiplication(23).is_injective() + True + sage: E.scalar_multiplication(-23).is_injective() + True + sage: E.scalar_multiplication(0).is_injective() + False + """ + if self._m.is_zero(): + return False + p = self._domain.base_ring().characteristic() + return self._m.abs().is_power_of(p) and self._domain.is_supersingular() + + def __neg__(self): + """ + Negate this scalar-multiplication map, i.e., return `[-m]` + when this morphism equals `[m]`. + + If rational maps have been computed already, they will be + reused for the negated morphism. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(2^8), [1,0,1,0,1]) + sage: phi = E.scalar_multiplication(23) + sage: -phi + Scalar-multiplication endomorphism [-23] of Elliptic Curve defined by y^2 + x*y + y = x^3 + 1 over Finite Field in z8 of size 2^8 + + TESTS:: + + sage: E = EllipticCurve(GF(79), [7,7]) + sage: phi = E.scalar_multiplication(5) + sage: _ = phi.rational_maps() + sage: (-phi)._rational_maps + ((x^25 + 11*x^23 - 24*x^22 - ... - 7*x^2 + 34*x + 21)/(25*x^24 - 5*x^22 - 23*x^21 - ... - 11*x^2 + 36*x + 21), + (29*x^36*y + 22*x^34*y - 27*x^33*y - ... + 14*x^2*y - 33*x*y + 37*y)/(9*x^36 + 21*x^34 - 14*x^33 + ... - 26*x^2 + 18*x + 7)) + """ + result = EllipticCurveHom_scalar(self._domain, -self._m) + if self._rational_maps is not None: + w = negation_morphism(self._domain).rational_maps() + result._rational_maps = tuple(f(*w) if f is not None else None for f in self._rational_maps) + return result + diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index dc508abd6ad..4611945759f 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -866,3 +866,20 @@ def __neg__(self): urst = baseWI.__mul__(self, w).tuple() return WeierstrassIsomorphism(self._domain, urst, self._codomain) + +def negation_morphism(E): + r""" + Given an elliptic curve `E`, return the negation endomorphism + `[-1]` of `E` as a :class:`WeierstrassIsomorphism`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism + sage: E = EllipticCurve([5,6,7,8,9]) + sage: neg = negation_morphism(E) + sage: neg.rational_maps() + (x, -5*x - y - 7) + """ + a1,_,a3,_,_ = E.a_invariants() + return WeierstrassIsomorphism(E, (-1, 0, -a1, -a3)) + From a9b8eccc6c87d22478eb6d416f2882a8b64748bb Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 21 Jan 2022 15:43:38 +0800 Subject: [PATCH 02/11] move .is_separable() to child classes --- .../elliptic_curves/ell_curve_isogeny.py | 24 +++++++++++++++ src/sage/schemes/elliptic_curves/hom.py | 29 ++++++++----------- .../elliptic_curves/weierstrass_morphism.py | 15 ++++++++++ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index cfb1dfb7729..8247defa108 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -2795,6 +2795,30 @@ def kernel_polynomial(self): return self.__kernel_polynomial + def is_separable(self): + r""" + Determine whether or not this isogeny is separable. + + Since :class:`EllipticCurveIsogeny` only implements + separable isogenies, this method always returns ``True``. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) + sage: phi = EllipticCurveIsogeny(E, E((0,0))) + sage: phi.is_separable() + True + + :: + + sage: E = EllipticCurve('11a1') + sage: phi = EllipticCurveIsogeny(E, E.torsion_points()) + sage: phi.is_separable() + True + """ + return True + + def set_pre_isomorphism(self, preWI): r""" Modify this isogeny by precomposing with a Weierstrass isomorphism. diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 2ff47442de6..790c6fcce00 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -407,27 +407,22 @@ def is_separable(self): r""" Determine whether or not this morphism is separable. - .. NOTE:: - - This method currently always returns ``True`` as Sage does - not yet implement inseparable isogenies. This will probably - change in the future. - - EXAMPLES:: + Implemented by child classes. For examples, see: - sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) - sage: phi = EllipticCurveIsogeny(E, E((0,0))) - sage: phi.is_separable() - True + - :meth:`EllipticCurveIsogeny.is_separable` + - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.is_separable` + - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.is_separable` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.is_separable` - :: + TESTS:: - sage: E = EllipticCurve('11a1') - sage: phi = EllipticCurveIsogeny(E, E.torsion_points()) - sage: phi.is_separable() - True + sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom + sage: EllipticCurveHom.is_separable(None) + Traceback (most recent call last): + ... + NotImplementedError: ... """ - return True + raise NotImplementedError('children must implement') def is_surjective(self): r""" diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 4611945759f..722d305b143 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -799,6 +799,21 @@ def kernel_polynomial(self): """ return self._poly_ring(1) + def is_separable(self): + r""" + Determine whether or not this isogeny is separable. + + Since :class:`WeierstrassIsomorphism` only implements + isomorphisms, this method always returns ``True``. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(31337), [0,1]) + sage: {f.is_separable() for f in E.automorphisms()} + {True} + """ + return True + def dual(self): """ Return the dual isogeny of this isomorphism. From 253cd33939ba4d4308cc28855929b9758679c01a Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 21 Jan 2022 17:25:44 +0800 Subject: [PATCH 03/11] use {identity,negation}_morphism() throughout --- .../elliptic_curves/ell_curve_isogeny.py | 12 ++++----- src/sage/schemes/elliptic_curves/hom.py | 6 ++--- .../schemes/elliptic_curves/hom_composite.py | 6 ++--- .../schemes/elliptic_curves/hom_scalar.py | 4 +-- .../elliptic_curves/weierstrass_morphism.py | 25 ++++++++++++++++--- 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 8247defa108..3c16ec5c418 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -77,7 +77,7 @@ from sage.rings.number_field.number_field_base import is_NumberField from sage.schemes.elliptic_curves.weierstrass_morphism \ - import WeierstrassIsomorphism, isomorphisms, baseWI + import WeierstrassIsomorphism, isomorphisms, baseWI, negation_morphism from sage.sets.set import Set @@ -1272,9 +1272,7 @@ def __neg__(self): """ output = copy(self) - E2 = output.__E2 - iso = WeierstrassIsomorphism(E2, (-1,0,-E2.a1(),-E2.a3())) - output._set_post_isomorphism(iso) + output._set_post_isomorphism(negation_morphism(output.__E2)) return output # @@ -1345,8 +1343,8 @@ def __clear_cached_values(self): sage: E = EllipticCurve(QQ, [0,0,0,1,0]) sage: phi = EllipticCurveIsogeny(E, x) sage: old_ratl_maps = phi.rational_maps() - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism - sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(), (-1,0,0,0))) + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism + sage: phi.set_post_isomorphism(negation_morphism(phi.codomain())) ... sage: old_ratl_maps == phi.rational_maps() False @@ -3218,7 +3216,7 @@ def switch_sign(self): from sage.misc.superseded import deprecation deprecation(32388, 'Elliptic-curve isogenies will be immutable in a future release of Sage.' ' Use -phi instead of phi.switch_sign() to obtain the negated isogeny.') - self._set_post_isomorphism(WeierstrassIsomorphism(self.__E2, (-1,0,-self.__E2.a1(),-self.__E2.a3()))) + self._set_post_isomorphism(negation_morphism(self.__E2)) def dual(self): r""" diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 790c6fcce00..17c2fc42944 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -126,11 +126,11 @@ def _richcmp_(self, other, op): :: - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism sage: E = EllipticCurve([9,9]) sage: F = E.change_ring(GF(71)) - sage: wE = WeierstrassIsomorphism(E, (1,0,0,0)) - sage: wF = WeierstrassIsomorphism(F, (1,0,0,0)) + sage: wE = identity_morphism(E) + sage: wF = identity_morphism(F) sage: mE = E.multiplication_by_m_isogeny(1) sage: mF = F.multiplication_by_m_isogeny(1) sage: [mE == wE, mF == wF] diff --git a/src/sage/schemes/elliptic_curves/hom_composite.py b/src/sage/schemes/elliptic_curves/hom_composite.py index 4ab35b88897..2ab12f56f91 100644 --- a/src/sage/schemes/elliptic_curves/hom_composite.py +++ b/src/sage/schemes/elliptic_curves/hom_composite.py @@ -93,7 +93,7 @@ from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic from sage.schemes.elliptic_curves.hom import EllipticCurveHom from sage.schemes.elliptic_curves.ell_curve_isogeny import EllipticCurveIsogeny -from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism +from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism from sage.misc.superseded import experimental_warning experimental_warning(32744, 'EllipticCurveHom_composite is experimental code.') @@ -266,7 +266,7 @@ def __init__(self, E, kernel, codomain=None): self._phis = _compute_factored_isogeny(kernel) if not self._phis: - self._phis = [WeierstrassIsomorphism(E, (1,0,0,0))] + self._phis = [identity_morphism(E)] if codomain is not None: if not isinstance(codomain, EllipticCurve_generic): @@ -362,7 +362,7 @@ def from_factors(cls, maps, E=None, strict=True): E = phi.codomain() if not maps: - maps = (WeierstrassIsomorphism(E, (1,0,0,0)),) + maps = (identity_morphism(E),) if len(maps) == 1 and not strict: return maps[0] diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index 60672dec291..be7b54cd3ce 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -306,10 +306,10 @@ def _richcmp_(self, other, op): TESTS: - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite doctest:warning ... - sage: neg = WeierstrassIsomorphism(E, [-1,0,0,0]) + sage: neg = negation_morphism(E) sage: neg_psi = EllipticCurveHom_composite.from_factors([psi,neg]) sage: psi_neg = EllipticCurveHom_composite.from_factors([neg,psi]) sage: phi == neg_psi == psi_neg == -psi diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 722d305b143..5ca8d46e811 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -870,10 +870,10 @@ def __neg__(self): :: - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, identity_morphism sage: E = EllipticCurve(QuadraticField(-1), [1,0]) sage: t = WeierstrassIsomorphism(E, (i,0,0,0)) - sage: -t^2 == WeierstrassIsomorphism(E, (1,0,0,0)) + sage: -t^2 == identity_morphism(E) True """ a1,_,a3,_,_ = self._domain.a_invariants() @@ -882,6 +882,23 @@ def __neg__(self): return WeierstrassIsomorphism(self._domain, urst, self._codomain) +def identity_morphism(E): + r""" + Given an elliptic curve `E`, return the identity morphism + on `E` as a :class:`WeierstrassIsomorphism`. + + EXAMPLES:: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import identity_morphism + sage: E = EllipticCurve([5,6,7,8,9]) + sage: id_ = identity_morphism(E) + sage: id_.rational_maps() + (x, y) + """ + R = E.base_ring() + zero = R.zero() + return WeierstrassIsomorphism(E, (R.one(), zero, zero, zero)) + def negation_morphism(E): r""" Given an elliptic curve `E`, return the negation endomorphism @@ -895,6 +912,6 @@ def negation_morphism(E): sage: neg.rational_maps() (x, -5*x - y - 7) """ - a1,_,a3,_,_ = E.a_invariants() - return WeierstrassIsomorphism(E, (-1, 0, -a1, -a3)) + R = E.base_ring() + return WeierstrassIsomorphism(E, (-R.one(), R.zero(), -E.a1(), -E.a3())) From 83809df6c2676fd19ef119a289cd0d0a8f63318a Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 21 Jan 2022 18:57:09 +0800 Subject: [PATCH 04/11] fix :: in doctest --- src/sage/schemes/elliptic_curves/hom_scalar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index be7b54cd3ce..576186770c3 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -304,7 +304,7 @@ def _richcmp_(self, other, op): sage: phi == -psi True - TESTS: + TESTS:: sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite From 059d0724abb17be3faacb3353b9f108c532abaf1 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 26 Jan 2022 13:35:00 +0800 Subject: [PATCH 05/11] remove unused import --- src/sage/schemes/elliptic_curves/hom_scalar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index 576186770c3..f2677b286ec 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -133,7 +133,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.schemes.elliptic_curves.ell_generic import EllipticCurve_generic -from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, negation_morphism +from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism from sage.schemes.elliptic_curves.hom import EllipticCurveHom from sage.misc.superseded import experimental_warning From c1bae9ec0cc6ab0bf93f31254011024fdea88315 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 23 May 2022 15:58:57 +0800 Subject: [PATCH 06/11] complete list of EllipticCurveHom children in documentation --- src/sage/schemes/elliptic_curves/hom.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 8220c5d95de..5041332b834 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -7,8 +7,10 @@ Current implementations of elliptic-curve morphisms (child classes): -- :class:`EllipticCurveIsogeny` -- :class:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism` +- :class:`~sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny` +- :class:`~sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism` +- :class:`~sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite` +- :class:`~sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar` AUTHORS: From 838429ee7773ce30f866ac1c77a94395228cbc34 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 23 May 2022 16:25:26 +0800 Subject: [PATCH 07/11] add EllipticCurveHom_scalar.scaling_factor() --- src/sage/schemes/elliptic_curves/hom.py | 1 + .../schemes/elliptic_curves/hom_scalar.py | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 0e3a8385bd6..9ed0b48be56 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -289,6 +289,7 @@ def scaling_factor(self): - :meth:`EllipticCurveIsogeny.scaling_factor` - :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.scaling_factor` - :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.scaling_factor` + - :meth:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar.scaling_factor` TESTS:: diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index f2677b286ec..b5baca186bf 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -378,6 +378,32 @@ def x_rational_map(self): self._rational_maps[0].denominator())) return f / g + def scaling_factor(self): + r""" + Return the Weierstrass scaling factor associated to this + scalar multiplication. + + The scaling factor is the constant `u` (in the base field) + such that `\varphi^* \omega_2 = u \omega_1`, where + `\varphi: E_1\to E_2` is this morphism and `\omega_i` are + the standard Weierstrass differentials on `E_i` defined by + `\mathrm dx/(2y+a_1x+a_3)`. + + EXAMPLES:: + + sage: E = EllipticCurve('11a1') + sage: phi = E.scalar_multiplication(5) + sage: u = phi.scaling_factor() + sage: u == phi.formal()[1] + True + sage: u == E.multiplication_by_m_isogeny(5).scaling_factor() + True + + ALGORITHM: The scaling factor equals the scalar that is being + multiplied by. + """ + return self._m + @cached_method def kernel_polynomial(self): r""" From 230019e19d0b55005eb25b910f5f5f169d52782e Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 18 Oct 2022 03:32:46 +0800 Subject: [PATCH 08/11] make linter happier --- src/sage/schemes/elliptic_curves/hom_scalar.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index b5baca186bf..f59b76288cd 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -198,7 +198,7 @@ def _eval(self, P): INPUT: a sequence of 3 coordinates defining a point on ``self`` - OUTPUT: the result of evaluating ``self'' at the given point + OUTPUT: the result of evaluating ``self`` at the given point EXAMPLES:: @@ -552,4 +552,3 @@ def __neg__(self): w = negation_morphism(self._domain).rational_maps() result._rational_maps = tuple(f(*w) if f is not None else None for f in self._rational_maps) return result - From be6aedc732144e17911e00505e699ed7c48d5442 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 18 Oct 2022 03:39:42 +0800 Subject: [PATCH 09/11] fix doctest failures --- src/sage/schemes/elliptic_curves/hom_scalar.py | 6 +----- src/sage/schemes/elliptic_curves/hom_velusqrt.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index f59b76288cd..8cdbef44f9f 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -307,12 +307,8 @@ def _richcmp_(self, other, op): TESTS:: sage: from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism - sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite - doctest:warning ... sage: neg = negation_morphism(E) - sage: neg_psi = EllipticCurveHom_composite.from_factors([psi,neg]) - sage: psi_neg = EllipticCurveHom_composite.from_factors([neg,psi]) - sage: phi == neg_psi == psi_neg == -psi + sage: phi == neg*psi == psi*neg == -psi True """ if isinstance(other, EllipticCurveHom_scalar): diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 144b5400970..21e0b6c0187 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -1365,6 +1365,22 @@ def scaling_factor(self): """ return self._pre_iso.scaling_factor() * self._post_iso.scaling_factor() + def is_separable(self): + r""" + Determine whether or not this isogeny is separable. + + Since :class:`EllipticCurveHom_velusqrt` only implements + separable isogenies, this method always returns ``True``. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(17), [0,0,0,3,0]) + sage: phi = E.isogeny(E((1,2)), algorithm='velusqrt') + sage: phi.is_separable() + True + """ + return True + def _random_example_for_testing(): r""" From 79ae46846467aba747b2a94377dbd2524a6c1460 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 22 Dec 2022 13:29:11 +0800 Subject: [PATCH 10/11] less experimental; deprecate .multiplication_by_m_isogeny() --- src/sage/schemes/elliptic_curves/ell_generic.py | 14 ++++++-------- src/sage/schemes/elliptic_curves/hom.py | 5 +++-- src/sage/schemes/elliptic_curves/hom_composite.py | 8 ++++---- src/sage/schemes/elliptic_curves/hom_scalar.py | 10 +--------- src/sage/schemes/elliptic_curves/hom_velusqrt.py | 4 ++-- src/sage/schemes/projective/projective_morphism.py | 4 ++-- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 85629684d40..ad9d54dc566 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2324,6 +2324,7 @@ def multiplication_by_m_isogeny(self, m): sage: E = EllipticCurve('11a1') sage: E.multiplication_by_m_isogeny(7) + doctest:warning ... DeprecationWarning: ... Isogeny of degree 49 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field TESTS: @@ -2363,6 +2364,9 @@ def multiplication_by_m_isogeny(self, m): sage: all(mu(-m) == -mu(m) for m in (1,2,3,5,7)) True """ + from sage.misc.superseded import deprecation + deprecation(32826, 'The .multiplication_by_m_isogeny() method is superseded by .scalar_multiplication().') + mx, my = self.multiplication_by_m(m) torsion_poly = self.torsion_polynomial(abs(m)).monic() @@ -2384,16 +2388,10 @@ def scalar_multiplication(self, m): :class:`sage.schemes.elliptic_curves.hom_scalar.EllipticCurveHom_scalar` object. - .. WARNING:: - - This method is currently experimental. It is intended to - eventually supersede :meth:`multiplication_by_m_isogeny`. - EXAMPLES:: sage: E = EllipticCurve('77a1') sage: m = E.scalar_multiplication(-7); m - doctest:warning ... Scalar-multiplication endomorphism [-7] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field sage: m.degree() 49 @@ -2518,9 +2516,9 @@ def automorphisms(self, field=None): ....: continue ....: break sage: Aut = E.automorphisms() - sage: Aut[0] == E.multiplication_by_m_isogeny(1) + sage: Aut[0] == E.scalar_multiplication(1) True - sage: Aut[1] == E.multiplication_by_m_isogeny(-1) + sage: Aut[1] == E.scalar_multiplication(-1) True sage: sorted(Aut) == Aut True diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 9820cb3434b..0b3256f2608 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -182,8 +182,9 @@ def _richcmp_(self, other, op): sage: F = E.change_ring(GF(71)) sage: wE = identity_morphism(E) sage: wF = identity_morphism(F) - sage: mE = E.multiplication_by_m_isogeny(1) + sage: mE = E.scalar_multiplication(1) sage: mF = F.multiplication_by_m_isogeny(1) + doctest:warning ... DeprecationWarning: ... sage: [mE == wE, mF == wF] [True, True] sage: [a == b for a in (wE,mE) for b in (wF,mF)] @@ -746,7 +747,7 @@ def compare_via_evaluation(left, right): sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite sage: mu = EllipticCurveHom_composite.from_factors([phi, psi]) sage: from sage.schemes.elliptic_curves.hom import compare_via_evaluation - sage: compare_via_evaluation(mu, E.multiplication_by_m_isogeny(7)) + sage: compare_via_evaluation(mu, E.scalar_multiplication(7)) True .. SEEALSO:: diff --git a/src/sage/schemes/elliptic_curves/hom_composite.py b/src/sage/schemes/elliptic_curves/hom_composite.py index 331528c72f5..576107582db 100644 --- a/src/sage/schemes/elliptic_curves/hom_composite.py +++ b/src/sage/schemes/elliptic_curves/hom_composite.py @@ -350,7 +350,7 @@ def from_factors(cls, maps, E=None, strict=True): TESTS:: sage: E = EllipticCurve('4730k1') - sage: EllipticCurveHom_composite.from_factors([], E) == E.multiplication_by_m_isogeny(1) + sage: EllipticCurveHom_composite.from_factors([], E) == E.scalar_multiplication(1) True :: @@ -563,7 +563,7 @@ def _comparison_impl(left, right, op): sage: psi = phi.codomain().isogeny(phi(Q)) sage: psi = psi.codomain().isomorphism_to(E) * psi sage: comp = psi * phi - sage: mu = E.multiplication_by_m_isogeny(phi.degree()) + sage: mu = E.scalar_multiplication(phi.degree()) sage: sum(a*comp == mu for a in E.automorphisms()) 1 @@ -680,9 +680,9 @@ def dual(self): Composite morphism of degree 9 = 3^2: From: Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 28339*x + 59518 over Finite Field of size 65537 To: Elliptic Curve defined by y^2 + x*y + 3*y = x^3 + 2*x^2 + 4*x + 5 over Finite Field of size 65537 - sage: psi * phi == phi.domain().multiplication_by_m_isogeny(phi.degree()) + sage: psi * phi == phi.domain().scalar_multiplication(phi.degree()) True - sage: phi * psi == psi.domain().multiplication_by_m_isogeny(psi.degree()) + sage: phi * psi == psi.domain().scalar_multiplication(psi.degree()) True """ phis = (phi.dual() for phi in self._phis[::-1]) diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index 8cdbef44f9f..e35ca84d65d 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -4,18 +4,11 @@ This class provides an :class:`EllipticCurveHom` instantiation for multiplication-by-`m` maps on elliptic curves. -.. WARNING:: - - This module is currently considered experimental. - It may change in a future release without prior warning, or even - be removed altogether if things turn out to be unfixably broken. - EXAMPLES: We can construct and evaluate scalar multiplications:: sage: from sage.schemes.elliptic_curves.hom_scalar import EllipticCurveHom_scalar - doctest:warning ... sage: E = EllipticCurve('77a1') sage: phi = E.scalar_multiplication(5); phi Scalar-multiplication endomorphism [5] of Elliptic Curve defined by y^2 + y = x^3 + 2*x over Rational Field @@ -136,8 +129,6 @@ from sage.schemes.elliptic_curves.weierstrass_morphism import negation_morphism from sage.schemes.elliptic_curves.hom import EllipticCurveHom -from sage.misc.superseded import experimental_warning -experimental_warning(32826, 'EllipticCurveHom_scalar is experimental code.') class EllipticCurveHom_scalar(EllipticCurveHom): @@ -393,6 +384,7 @@ def scaling_factor(self): sage: u == phi.formal()[1] True sage: u == E.multiplication_by_m_isogeny(5).scaling_factor() + doctest:warning ... DeprecationWarning: ... True ALGORITHM: The scaling factor equals the scalar that is being diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index e4e51d70a37..b2813dc808d 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -1252,9 +1252,9 @@ def dual(self): To: Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2 sage: phi.dual() Isogeny of degree 11 from Elliptic Curve defined by y^2 = x^3 + 39*x + 40 over Finite Field in z2 of size 101^2 to Elliptic Curve defined by y^2 + x*y + y = x^3 + x^2 + x + 1 over Finite Field in z2 of size 101^2 - sage: phi.dual() * phi == phi.domain().multiplication_by_m_isogeny(11) + sage: phi.dual() * phi == phi.domain().scalar_multiplication(11) True - sage: phi * phi.dual() == phi.codomain().multiplication_by_m_isogeny(11) + sage: phi * phi.dual() == phi.codomain().scalar_multiplication(11) True """ # FIXME: This code fails if the degree is divisible by the characteristic. diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 30afbb10ab1..c49dfbf41ca 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -2705,7 +2705,7 @@ def projective_degrees(self): sage: k = GF(11) sage: E = EllipticCurve(k,[1,1]) sage: Q = E(6,5) - sage: phi = E.multiplication_by_m_isogeny(2) + sage: phi = E.scalar_multiplication(2) sage: mor = phi.as_morphism() sage: mor.projective_degrees() (12, 3) @@ -2747,7 +2747,7 @@ def degree(self): sage: k = GF(11) sage: E = EllipticCurve(k,[1,1]) sage: Q = E(6,5) - sage: phi = E.multiplication_by_m_isogeny(2) + sage: phi = E.scalar_multiplication(2) sage: mor = phi.as_morphism() sage: mor.degree() 4 From 884263e93974b3344cc2ba3ef808a5dcf60e123d Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 22 Dec 2022 17:43:06 +0800 Subject: [PATCH 11/11] convert scaling factor to base ring --- src/sage/schemes/elliptic_curves/hom_scalar.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index e35ca84d65d..e18e514bb35 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -387,10 +387,19 @@ def scaling_factor(self): doctest:warning ... DeprecationWarning: ... True + The scaling factor lives in the base ring:: + + sage: E = EllipticCurve(GF(101^2), [5,5]) + sage: phi = E.scalar_multiplication(123) + sage: phi.scaling_factor() + 22 + sage: phi.scaling_factor().parent() + Finite Field in z2 of size 101^2 + ALGORITHM: The scaling factor equals the scalar that is being multiplied by. """ - return self._m + return self._base_ring(self._m) @cached_method def kernel_polynomial(self): @@ -476,9 +485,7 @@ def is_separable(self): """ if self._m.is_zero(): raise ValueError('[0] is not an isogeny') - p = self._domain.base_ring().characteristic() - return p == 0 or self._m.gcd(p) == 1 - + return bool(self.scaling_factor()) def is_injective(self): """