From 2fe08b6d8f7016d8cc55b91b77d517a2ad77ca67 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 11:57:14 +0100 Subject: [PATCH 01/11] Fix #6413 Compute pth multiplication coordinate maps by using isogenies. Thanks @yyyyx4 for the idea! --- .../schemes/elliptic_curves/ell_generic.py | 73 +++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 57003863b1b..3fd4df3961a 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -53,6 +53,7 @@ # **************************************************************************** import math +from sage.arith.misc import valuation import sage.rings.abc from sage.rings.finite_rings.integer_mod import mod @@ -65,6 +66,7 @@ import sage.groups.generic as generic from sage.arith.functions import lcm +from sage.functions.generalized import sgn from sage.rings.integer import Integer from sage.rings.big_oh import O from sage.rings.infinity import Infinity as oo @@ -2019,6 +2021,30 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2, force_evalu torsion_polynomial = division_polynomial + @cached_method + def multiplication_by_p_isogeny(self): + r""" + Return the multiplication-by-\(p\) isogeny. + + EXAMPLES:: + + sage: p = 23 + sage: K. = GF(p^3) + sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: phi = E.multiplication_by_p_isogeny() + sage: assert phi.degree() == p**2 + sage: P = E.random_element() + sage: assert phi(P) == P * p + """ + from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic + + K = self.base_ring() + if not isinstance(K, FiniteField_generic): + raise ValueError(f"Base ring (={K}) is not a finite field.") + + frob = self.frobenius_isogeny() + return frob.dual() * frob + def _multiple_x_numerator(self, n, x=None): r""" Return the numerator of the `x`-coordinate of the `n\th` multiple of a @@ -2333,6 +2359,20 @@ def multiplication_by_m(self, m, x_only=False): sage: my_eval = lambda f,P: [fi(P[0],P[1]) for fi in f] sage: f = E.multiplication_by_m(2) sage: assert(E(eval(f,P)) == 2*P) + + The following test shows that :trac:`6413` is indeed fixed:: + sage: p = 7 + sage: K. = GF(p^2) + sage: E = EllipticCurve(K, [a + 3, 5 - a]) + sage: k = p^2 * 3 + sage: f, g = E.multiplication_by_m(k) + sage: for _ in range(100): + ....: P = E.random_point() + ....: if P * k == 0: + ....: continue + ....: Qx = f.subs(x=P[0]) + ....: Qy = g.subs(x=P[0], y=P[1]) + ....: assert (P * k).xy() == (Qx, Qy) """ # Coerce the input m to be an integer m = Integer(m) @@ -2352,7 +2392,7 @@ def multiplication_by_m(self, m, x_only=False): return x # Grab curve invariants - a1, a2, a3, a4, a6 = self.a_invariants() + a1, _, a3, _, _ = self.a_invariants() if m == -1: if not x_only: @@ -2360,22 +2400,43 @@ def multiplication_by_m(self, m, x_only=False): else: return x - # the x-coordinate does not depend on the sign of m. The work + # Inseparable cases + # Special case of multiplication by p is easy. Kind of. + p = Integer(self.base_ring().characteristic()) + + v_p = 0 if p == 0 else valuation(m.abs(), p) + m //= p**v_p + + # the x-coordinate does not depend on the sign of m. The work # here is done by functions defined earlier: mx = (x.parent()(self._multiple_x_numerator(m.abs(), x)) / x.parent()(self._multiple_x_denominator(m.abs(), x))) + if x_only: + # slow. + if v_p > 0: + p_endo = self.multiplication_by_p_isogeny() + isog = p_endo**v_p + fx = isog.x_rational_map() + # slow. + mx = mx.subs(x=fx) # Return it if the optional parameter x_only is set. return mx - # Consideration of the invariant differential - # w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3) - # and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx - + # Consideration of the invariant differential + # w=dx/(2*y+a1*x+a3) shows that m*w = d(mx)/(2*my+a1*mx+a3) + # and hence 2*my+a1*mx+a3 = (1/m)*(2*y+a1*x+a3)*d(mx)/dx my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2 + if v_p > 0: + frob = self.frobenius_isogeny() + isog = (frob.dual() * frob)**v_p + fx, fy = isog.rational_maps() + # slow... + my = my.subs(x=fx, y=fy) + return mx, my def multiplication_by_m_isogeny(self, m): From 4273b70e4c9bfa39ec235be85b680f63195a28de Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 17:33:05 +0100 Subject: [PATCH 02/11] Fix --- src/sage/schemes/elliptic_curves/ell_generic.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 3fd4df3961a..6a0a11bf1f4 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2400,12 +2400,12 @@ def multiplication_by_m(self, m, x_only=False): else: return x - # Inseparable cases - # Special case of multiplication by p is easy. Kind of. - p = Integer(self.base_ring().characteristic()) - - v_p = 0 if p == 0 else valuation(m.abs(), p) - m //= p**v_p + # If we only require the x coordinate, it is faster to use the recursive formula + # since substituting polynomials is quite slow. + if not x_only: + p = Integer(self.base_ring().characteristic()) + v_p = 0 if p == 0 else valuation(m.abs(), p) + m //= p**v_p # the x-coordinate does not depend on the sign of m. The work # here is done by functions defined earlier: From 8ab10810a1181ee72f4904792b758096cf0baf10 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 17:32:18 +0100 Subject: [PATCH 03/11] Remove useless imports --- src/sage/schemes/elliptic_curves/ell_generic.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 6a0a11bf1f4..de0c2f67a67 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -66,10 +66,7 @@ import sage.groups.generic as generic from sage.arith.functions import lcm -from sage.functions.generalized import sgn from sage.rings.integer import Integer -from sage.rings.big_oh import O -from sage.rings.infinity import Infinity as oo from sage.rings.rational import Rational from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.rational_field import RationalField From 6a81d72d927e91c10b4ff60f9a42bfb0c45a7b6b Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 18 Jan 2024 17:46:23 +0100 Subject: [PATCH 04/11] serious fix (one character) --- src/sage/schemes/elliptic_curves/ell_generic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index de0c2f67a67..f7eaa417da3 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2410,7 +2410,6 @@ def multiplication_by_m(self, m, x_only=False): mx = (x.parent()(self._multiple_x_numerator(m.abs(), x)) / x.parent()(self._multiple_x_denominator(m.abs(), x))) - if x_only: # slow. if v_p > 0: From ac0ce58d0f2ea6b7aab6f5cdeb3f3a0ce0fc5821 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Mon, 22 Jan 2024 01:54:12 +0000 Subject: [PATCH 05/11] Remove deprecated `multiplication_by_m_isogeny` --- .../schemes/elliptic_curves/ell_generic.py | 95 ------------------- src/sage/schemes/elliptic_curves/hom.py | 15 --- .../schemes/elliptic_curves/hom_scalar.py | 3 +- 3 files changed, 1 insertion(+), 112 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index f7eaa417da3..ea3d8992e46 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2435,101 +2435,6 @@ def multiplication_by_m(self, m, x_only=False): return mx, my - def multiplication_by_m_isogeny(self, m): - r""" - Return the ``EllipticCurveIsogeny`` object associated to the - multiplication-by-`m` map on this elliptic curve. - - The resulting isogeny will - have the associated rational maps (i.e., those returned by - :meth:`multiplication_by_m`) already computed. - - NOTE: This function is currently *much* slower than the - result of ``self.multiplication_by_m()``, because - constructing an isogeny precomputes a significant amount - of information. See :trac:`7368` and :trac:`8014` for the - status of improving this situation. - - INPUT: - - - ``m`` -- a nonzero integer - - OUTPUT: - - - An ``EllipticCurveIsogeny`` object associated to the - multiplication-by-`m` map on this elliptic curve. - - EXAMPLES:: - - 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: - - Tests for :trac:`32490`:: - - sage: E = EllipticCurve(QQbar, [1,0]) # needs sage.rings.number_field - sage: E.multiplication_by_m_isogeny(1).rational_maps() # needs sage.rings.number_field - (x, y) - - :: - - sage: E = EllipticCurve_from_j(GF(31337).random_element()) # needs sage.rings.finite_rings - sage: P = E.random_point() # needs sage.rings.finite_rings - sage: [E.multiplication_by_m_isogeny(m)(P) == m*P for m in (1,2,3,5,7,9)] # needs sage.rings.finite_rings - [True, True, True, True, True, True] - - :: - - sage: E = EllipticCurve('99.a1') - sage: E.multiplication_by_m_isogeny(5) - Isogeny of degree 25 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field - sage: E.multiplication_by_m_isogeny(2).rational_maps() - ((1/4*x^4 + 33/4*x^2 - 121/2*x + 363/4)/(x^3 - 3/4*x^2 - 33/2*x + 121/4), - (-1/256*x^7 + 1/128*x^6*y - 7/256*x^6 - 3/256*x^5*y - 105/256*x^5 - 165/256*x^4*y + 1255/256*x^4 + 605/128*x^3*y - 473/64*x^3 - 1815/128*x^2*y - 10527/256*x^2 + 2541/128*x*y + 4477/32*x - 1331/128*y - 30613/256)/(1/16*x^6 - 3/32*x^5 - 519/256*x^4 + 341/64*x^3 + 1815/128*x^2 - 3993/64*x + 14641/256)) - - Test for :trac:`34727`:: - - sage: E = EllipticCurve([5,5]) - sage: E.multiplication_by_m_isogeny(-1) - Isogeny of degree 1 - from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - sage: E.multiplication_by_m_isogeny(-2) - Isogeny of degree 4 - from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - sage: E.multiplication_by_m_isogeny(-3) - Isogeny of degree 9 - from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field - sage: mu = E.multiplication_by_m_isogeny - 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() - phi = self.isogeny(torsion_poly, codomain=self) - phi._EllipticCurveIsogeny__initialize_rational_maps(precomputed_maps=(mx, my)) - - # trac 32490: using codomain=self can give a wrong isomorphism - for aut in self.automorphisms(): - psi = aut * phi - if psi.rational_maps() == (mx, my): - return psi - - assert False, 'bug in multiplication_by_m_isogeny()' - def scalar_multiplication(self, m): r""" Return the scalar-multiplication map `[m]` on this elliptic diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index fb7855993ea..627d9099237 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -227,21 +227,6 @@ def _richcmp_(self, other, op): sage: phi.dual() == psi.dual() True - :: - - 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 = identity_morphism(E) - sage: wF = identity_morphism(F) - 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)] - [False, False, False, False] - .. SEEALSO:: - :meth:`_comparison_impl` diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index 03f042fd830..37994a864d0 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -381,8 +381,7 @@ def scaling_factor(self): sage: u = phi.scaling_factor() sage: u == phi.formal()[1] True - sage: u == E.multiplication_by_m_isogeny(5).scaling_factor() - doctest:warning ... DeprecationWarning: ... + sage: u == 5 True The scaling factor lives in the base ring:: From 94fc8710e4e82207648b3d0f99a1032f974a7903 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Mon, 22 Jan 2024 04:48:44 +0000 Subject: [PATCH 06/11] Fix unbounded local variable bug --- src/sage/schemes/elliptic_curves/ell_generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index ea3d8992e46..fc8861b373c 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2399,9 +2399,9 @@ def multiplication_by_m(self, m, x_only=False): # If we only require the x coordinate, it is faster to use the recursive formula # since substituting polynomials is quite slow. + p = Integer(self.base_ring().characteristic()) + v_p = 0 if p == 0 else valuation(m.abs(), p) if not x_only: - p = Integer(self.base_ring().characteristic()) - v_p = 0 if p == 0 else valuation(m.abs(), p) m //= p**v_p # the x-coordinate does not depend on the sign of m. The work From 826ec07905d3e0827bdfba4875a04a2f36662c54 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 24 Jan 2024 02:25:18 +0000 Subject: [PATCH 07/11] =?UTF-8?q?apply=20review=20changes=20=F0=9F=93=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../elliptic_curves/ell_finite_field.py | 18 +++++ .../schemes/elliptic_curves/ell_generic.py | 65 ++++++++----------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 8d3eca74214..1a3e92aabd8 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -610,6 +610,24 @@ def cardinality(self, algorithm=None, extension_degree=1): order = cardinality # alias + @cached_method + def multiplication_by_p_isogeny(self): + r""" + Return the multiplication-by-\(p\) isogeny. + + EXAMPLES:: + + sage: p = 23 + sage: K. = GF(p^3) + sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: phi = E.multiplication_by_p_isogeny() + sage: assert phi.degree() == p**2 + sage: P = E.random_element() + sage: assert phi(P) == P * p + """ + frob = self.frobenius_isogeny() + return frob.dual() * frob + def frobenius_polynomial(self): r""" Return the characteristic polynomial of Frobenius. diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index c22e0b59bab..8caa78557ea 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2106,30 +2106,6 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2, force_evalu torsion_polynomial = division_polynomial - @cached_method - def multiplication_by_p_isogeny(self): - r""" - Return the multiplication-by-\(p\) isogeny. - - EXAMPLES:: - - sage: p = 23 - sage: K. = GF(p^3) - sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) - sage: phi = E.multiplication_by_p_isogeny() - sage: assert phi.degree() == p**2 - sage: P = E.random_element() - sage: assert phi(P) == P * p - """ - from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic - - K = self.base_ring() - if not isinstance(K, FiniteField_generic): - raise ValueError(f"Base ring (={K}) is not a finite field.") - - frob = self.frobenius_isogeny() - return frob.dual() * frob - def _multiple_x_numerator(self, n, x=None): r""" Return the numerator of the `x`-coordinate of the `n\th` multiple of a @@ -2445,7 +2421,7 @@ def multiplication_by_m(self, m, x_only=False): sage: f = E.multiplication_by_m(2) sage: assert(E(eval(f,P)) == 2*P) - The following test shows that :trac:`6413` is indeed fixed:: + The following test shows that :trac:`6413` is fixed for elliptic curves over finite fields:: sage: p = 7 sage: K. = GF(p^2) sage: E = EllipticCurve(K, [a + 3, 5 - a]) @@ -2458,15 +2434,39 @@ def multiplication_by_m(self, m, x_only=False): ....: Qx = f.subs(x=P[0]) ....: Qy = g.subs(x=P[0], y=P[1]) ....: assert (P * k).xy() == (Qx, Qy) + + However, it still fails for elliptic curves over positive characteristics fields:: + + sage: F. = FunctionField(GF(7)) + sage: E = EllipticCurve(F, [a, 1 / a]) + sage: E.multiplication_by_m(7) + Traceback (most recent call last): + ... + NotImplementedError: multiplication by integer not coprime to pis only implemented for curves over finite fields + + :: + + sage: p = 7 + sage: K. = GF(p^2) + sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: E.multiplication_by_m(p * 2)[0] == E.multiplication_by_m(p * 2, x_only=True) + True """ # Coerce the input m to be an integer m = Integer(m) + p = self.base_ring().characteristic() + if m == 0: raise ValueError("m must be a non-zero integer") if x_only: x = polygen(self.base_ring(), 'x') else: + from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic + if m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): + # TODO: Implement the correct formula? + raise NotImplementedError("multiplication by integer not coprime to p" + "is only implemented for curves over finite fields") x, y = polygens(self.base_ring(), 'x,y') # Special case of multiplication by 1 is easy. @@ -2487,8 +2487,7 @@ def multiplication_by_m(self, m, x_only=False): # If we only require the x coordinate, it is faster to use the recursive formula # since substituting polynomials is quite slow. - p = Integer(self.base_ring().characteristic()) - v_p = 0 if p == 0 else valuation(m.abs(), p) + v_p = 0 if p == 0 else valuation(m, p) if not x_only: m //= p**v_p @@ -2499,14 +2498,6 @@ def multiplication_by_m(self, m, x_only=False): / x.parent()(self._multiple_x_denominator(m.abs(), x))) if x_only: - # slow. - if v_p > 0: - p_endo = self.multiplication_by_p_isogeny() - isog = p_endo**v_p - fx = isog.x_rational_map() - # slow. - mx = mx.subs(x=fx) - # Return it if the optional parameter x_only is set. return mx # Consideration of the invariant differential @@ -2515,10 +2506,10 @@ def multiplication_by_m(self, m, x_only=False): my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2 if v_p > 0: - frob = self.frobenius_isogeny() - isog = (frob.dual() * frob)**v_p + isog = self.multiplication_by_p_isogeny()**v_p fx, fy = isog.rational_maps() # slow... + mx = mx.subs(x=fx) my = my.subs(x=fx, y=fy) return mx, my From a9b05b42111b093ffea83cb575271b2298211e9c Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 30 Jan 2024 15:06:06 +0000 Subject: [PATCH 08/11] =?UTF-8?q?apply=20review=20changes=20=F0=9F=93=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 2 +- src/sage/schemes/elliptic_curves/ell_generic.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 1a3e92aabd8..fdbd3228716 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -613,7 +613,7 @@ def cardinality(self, algorithm=None, extension_degree=1): @cached_method def multiplication_by_p_isogeny(self): r""" - Return the multiplication-by-\(p\) isogeny. + Return the multiplication-by-`p` isogeny. EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 8caa78557ea..411501469b6 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2435,14 +2435,14 @@ def multiplication_by_m(self, m, x_only=False): ....: Qy = g.subs(x=P[0], y=P[1]) ....: assert (P * k).xy() == (Qx, Qy) - However, it still fails for elliptic curves over positive characteristics fields:: + However, it still fails for elliptic curves over positive-characteristic fields:: sage: F. = FunctionField(GF(7)) sage: E = EllipticCurve(F, [a, 1 / a]) sage: E.multiplication_by_m(7) Traceback (most recent call last): ... - NotImplementedError: multiplication by integer not coprime to pis only implemented for curves over finite fields + NotImplementedError: multiplication by integer not coprime to p is only implemented for curves over finite fields :: @@ -2465,7 +2465,7 @@ def multiplication_by_m(self, m, x_only=False): from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic if m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): # TODO: Implement the correct formula? - raise NotImplementedError("multiplication by integer not coprime to p" + raise NotImplementedError("multiplication by integer not coprime to p " "is only implemented for curves over finite fields") x, y = polygens(self.base_ring(), 'x,y') From d466ecb2cd3645998f3b1d85f963fb5fcc2dc44c Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 30 Jan 2024 15:07:26 +0000 Subject: [PATCH 09/11] Revert "Remove deprecated `multiplication_by_m_isogeny`" This reverts commit ac0ce58d0f2ea6b7aab6f5cdeb3f3a0ce0fc5821. --- .../schemes/elliptic_curves/ell_generic.py | 95 +++++++++++++++++++ src/sage/schemes/elliptic_curves/hom.py | 15 +++ .../schemes/elliptic_curves/hom_scalar.py | 3 +- 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 411501469b6..6357c7a4a6c 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2514,6 +2514,101 @@ def multiplication_by_m(self, m, x_only=False): return mx, my + def multiplication_by_m_isogeny(self, m): + r""" + Return the ``EllipticCurveIsogeny`` object associated to the + multiplication-by-`m` map on this elliptic curve. + + The resulting isogeny will + have the associated rational maps (i.e., those returned by + :meth:`multiplication_by_m`) already computed. + + NOTE: This function is currently *much* slower than the + result of ``self.multiplication_by_m()``, because + constructing an isogeny precomputes a significant amount + of information. See :trac:`7368` and :trac:`8014` for the + status of improving this situation. + + INPUT: + + - ``m`` -- a nonzero integer + + OUTPUT: + + - An ``EllipticCurveIsogeny`` object associated to the + multiplication-by-`m` map on this elliptic curve. + + EXAMPLES:: + + 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: + + Tests for :trac:`32490`:: + + sage: E = EllipticCurve(QQbar, [1,0]) # needs sage.rings.number_field + sage: E.multiplication_by_m_isogeny(1).rational_maps() # needs sage.rings.number_field + (x, y) + + :: + + sage: E = EllipticCurve_from_j(GF(31337).random_element()) # needs sage.rings.finite_rings + sage: P = E.random_point() # needs sage.rings.finite_rings + sage: [E.multiplication_by_m_isogeny(m)(P) == m*P for m in (1,2,3,5,7,9)] # needs sage.rings.finite_rings + [True, True, True, True, True, True] + + :: + + sage: E = EllipticCurve('99.a1') + sage: E.multiplication_by_m_isogeny(5) + Isogeny of degree 25 from Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field to Elliptic Curve defined by y^2 + x*y + y = x^3 - x^2 - 17*x + 30 over Rational Field + sage: E.multiplication_by_m_isogeny(2).rational_maps() + ((1/4*x^4 + 33/4*x^2 - 121/2*x + 363/4)/(x^3 - 3/4*x^2 - 33/2*x + 121/4), + (-1/256*x^7 + 1/128*x^6*y - 7/256*x^6 - 3/256*x^5*y - 105/256*x^5 - 165/256*x^4*y + 1255/256*x^4 + 605/128*x^3*y - 473/64*x^3 - 1815/128*x^2*y - 10527/256*x^2 + 2541/128*x*y + 4477/32*x - 1331/128*y - 30613/256)/(1/16*x^6 - 3/32*x^5 - 519/256*x^4 + 341/64*x^3 + 1815/128*x^2 - 3993/64*x + 14641/256)) + + Test for :trac:`34727`:: + + sage: E = EllipticCurve([5,5]) + sage: E.multiplication_by_m_isogeny(-1) + Isogeny of degree 1 + from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-2) + Isogeny of degree 4 + from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-3) + Isogeny of degree 9 + from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: mu = E.multiplication_by_m_isogeny + 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() + phi = self.isogeny(torsion_poly, codomain=self) + phi._EllipticCurveIsogeny__initialize_rational_maps(precomputed_maps=(mx, my)) + + # trac 32490: using codomain=self can give a wrong isomorphism + for aut in self.automorphisms(): + psi = aut * phi + if psi.rational_maps() == (mx, my): + return psi + + assert False, 'bug in multiplication_by_m_isogeny()' + def scalar_multiplication(self, m): r""" Return the scalar-multiplication map `[m]` on this elliptic diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index a7688d3099b..db3b1918926 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -230,6 +230,21 @@ def _richcmp_(self, other, op): sage: phi.dual() == psi.dual() True + :: + + 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 = identity_morphism(E) + sage: wF = identity_morphism(F) + 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)] + [False, False, False, False] + .. SEEALSO:: - :meth:`_comparison_impl` diff --git a/src/sage/schemes/elliptic_curves/hom_scalar.py b/src/sage/schemes/elliptic_curves/hom_scalar.py index b09a538ed8c..711c1c66edf 100644 --- a/src/sage/schemes/elliptic_curves/hom_scalar.py +++ b/src/sage/schemes/elliptic_curves/hom_scalar.py @@ -381,7 +381,8 @@ def scaling_factor(self): sage: u = phi.scaling_factor() sage: u == phi.formal()[1] True - sage: u == 5 + sage: u == E.multiplication_by_m_isogeny(5).scaling_factor() + doctest:warning ... DeprecationWarning: ... True The scaling factor lives in the base ring:: From bbb27866104675060ce91dc52227316209c5223e Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 30 Jan 2024 18:44:08 +0000 Subject: [PATCH 10/11] fix mod p thing --- src/sage/schemes/elliptic_curves/ell_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 6357c7a4a6c..cd6ece02e2c 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2463,7 +2463,7 @@ def multiplication_by_m(self, m, x_only=False): x = polygen(self.base_ring(), 'x') else: from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic - if m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): + if p != 0 and m % p == 0 and not isinstance(self.base_ring(), FiniteField_generic): # TODO: Implement the correct formula? raise NotImplementedError("multiplication by integer not coprime to p " "is only implemented for curves over finite fields") From 929d0e64fa2e1daa6c167992bf271d3ca70339f8 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Sat, 10 Feb 2024 17:51:10 +0000 Subject: [PATCH 11/11] =?UTF-8?q?fix=20(possibly)=20failing=20doctests=20?= =?UTF-8?q?=F0=9F=93=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index fdbd3228716..26981f506e6 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -619,7 +619,7 @@ def multiplication_by_p_isogeny(self): sage: p = 23 sage: K. = GF(p^3) - sage: E = EllipticCurve(K, [K.random_element(), K.random_element()]) + sage: E = EllipticCurve(j=K.random_element()) sage: phi = E.multiplication_by_p_isogeny() sage: assert phi.degree() == p**2 sage: P = E.random_element() @@ -1791,13 +1791,7 @@ def twists(self): sage: p = next_prime(randrange(2,100)) sage: e = randrange(1,10) sage: F. = GF((p,e)) - sage: while True: - ....: try: - ....: E = EllipticCurve([F.random_element() for _ in range(5)]) - ....: except ArithmeticError: - ....: pass - ....: else: - ....: break + sage: E = EllipticCurve(j=F.random_element()) sage: twists1 = E.twists() sage: {sum(E1.is_isomorphic(E2) for E2 in twists1) == 1 for E1 in twists1} {True}