Skip to content

Commit b95c165

Browse files
author
Release Manager
committed
Trac #33865: direct computation of .formal()[1] for elliptic-curve morphisms
Follow-up to #33216: We can compute `.formal()[1]` directly, i.e., without going through `.formal()`. This is beneficial as the latter uses the rational maps, which can be very expensive. * Composite isogenies have multiplicative `.formal()[1]`. * Isogenies using Vélu's or Kohel's formulas are normalized, hence `.formal()[1] == 1`. We only need to account for pre- and post- isomorphism. * Weierstrass isomorphisms `(u,r,s,t)` have `.formal()[1] == u`. Same example as for #33216: {{{#!sage E = EllipticCurve(j=GF(431^2)(4)) phi = E.isogeny(2^99*E.gens()[0]) _ = phi.dual() %timeit phi._EllipticCurveIsogeny__dual = None; phi.dual() }}} Sage 9.6: {{{ 537 ms ± 6.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) }}} This branch: {{{ 294 ms ± 1.71 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) }}} URL: https://trac.sagemath.org/33865 Reported by: lorenz Ticket author(s): Lorenz Panny Reviewer(s): John Cremona
2 parents f0068fa + fb571d3 commit b95c165

File tree

6 files changed

+125
-13
lines changed

6 files changed

+125
-13
lines changed

Diff for: build/pkgs/configure/checksums.ini

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
tarball=configure-VERSION.tar.gz
2-
sha1=f14a58dd08a2daa9d8613e366de9e4c5f19d1420
3-
md5=e13c6fdf0bea7b51d864405bd24685ca
4-
cksum=1164709658
2+
sha1=6d548fec179a7c4d71f327f409320a06e0d9d35e
3+
md5=7289e1187b21d5cd30a891838410fcd0
4+
cksum=1487266202

Diff for: build/pkgs/configure/package-version.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6b96f8af0e66b45a53984772dc68db8d4b2e3e04
1+
6b0fd1ca2d493aec6beadae8b15f926c19070098

Diff for: src/sage/schemes/elliptic_curves/ell_curve_isogeny.py

+35-2
Original file line numberDiff line numberDiff line change
@@ -2768,6 +2768,39 @@ def x_rational_map(self):
27682768
self.__initialize_rational_maps()
27692769
return self.__X_coord_rational_map
27702770

2771+
def scaling_factor(self):
2772+
r"""
2773+
Return the Weierstrass scaling factor associated to this
2774+
elliptic-curve isogeny.
2775+
2776+
The scaling factor is the constant `u` (in the base field)
2777+
such that `\varphi^* \omega_2 = u \omega_1`, where
2778+
`\varphi: E_1\to E_2` is this isogeny and `\omega_i` are
2779+
the standard Weierstrass differentials on `E_i` defined by
2780+
`\mathrm dx/(2y+a_1x+a_3)`.
2781+
2782+
EXAMPLES::
2783+
2784+
sage: E = EllipticCurve(GF(257^2), [0,1])
2785+
sage: phi = E.isogeny(E.lift_x(240))
2786+
sage: phi.degree()
2787+
43
2788+
sage: phi.scaling_factor()
2789+
1
2790+
sage: phi.dual().scaling_factor()
2791+
43
2792+
2793+
ALGORITHM: The "inner" isogeny is normalized by construction,
2794+
so we only need to account for the scaling factors of a pre-
2795+
and post-isomorphism.
2796+
"""
2797+
sc = Integer(1)
2798+
if self.__pre_isomorphism is not None:
2799+
sc *= self.__pre_isomorphism.scaling_factor()
2800+
if self.__post_isomorphism is not None:
2801+
sc *= self.__post_isomorphism.scaling_factor()
2802+
return sc
2803+
27712804
def kernel_polynomial(self):
27722805
r"""
27732806
Return the kernel polynomial of this isogeny.
@@ -3320,7 +3353,7 @@ def dual(self):
33203353

33213354
# trac 7096
33223355
# this should take care of the case when the isogeny is not normalized.
3323-
u = self.formal(prec=2)[1]
3356+
u = self.scaling_factor()
33243357
isom = WeierstrassIsomorphism(E2pr, (u/F(d), 0, 0, 0))
33253358

33263359
E2 = isom.codomain()
@@ -3343,7 +3376,7 @@ def dual(self):
33433376
# the composition has the degree as a leading coefficient in
33443377
# the formal expansion.
33453378

3346-
phihat_sc = phi_hat.formal(prec=2)[1]
3379+
phihat_sc = phi_hat.scaling_factor()
33473380

33483381
sc = u * phihat_sc/F(d)
33493382

Diff for: src/sage/schemes/elliptic_curves/hom.py

+33-7
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,36 @@ def x_rational_map(self):
266266
raise NotImplementedError('children must implement')
267267

268268

269+
def scaling_factor(self):
270+
r"""
271+
Return the Weierstrass scaling factor associated to this
272+
elliptic-curve morphism.
273+
274+
The scaling factor is the constant `u` (in the base field)
275+
such that `\varphi^* \omega_2 = u \omega_1`, where
276+
`\varphi: E_1\to E_2` is this morphism and `\omega_i` are
277+
the standard Weierstrass differentials on `E_i` defined by
278+
`\mathrm dx/(2y+a_1x+a_3)`.
279+
280+
Implemented by child classes. For examples, see:
281+
282+
- :meth:`EllipticCurveIsogeny.scaling_factor`
283+
- :meth:`sage.schemes.elliptic_curves.weierstrass_morphism.WeierstrassIsomorphism.scaling_factor`
284+
- :meth:`sage.schemes.elliptic_curves.hom_composite.EllipticCurveHom_composite.scaling_factor`
285+
286+
TESTS::
287+
288+
sage: from sage.schemes.elliptic_curves.hom import EllipticCurveHom
289+
sage: EllipticCurveHom.scaling_factor(None)
290+
Traceback (most recent call last):
291+
...
292+
NotImplementedError: ...
293+
"""
294+
#TODO: could have a default implementation that simply
295+
# returns .formal()[1], but it seems safer to fail
296+
# visibly to make sure we would notice regressions
297+
raise NotImplementedError('children must implement')
298+
269299
def formal(self, prec=20):
270300
r"""
271301
Return the formal isogeny associated to this elliptic-curve
@@ -326,11 +356,6 @@ def is_normalized(self):
326356
`\omega_2` are the invariant differentials on `E_1` and
327357
`E_2` corresponding to the given equation.
328358
329-
ALGORITHM:
330-
331-
The method checks if the leading term of the formal series
332-
associated to this isogeny equals `1`.
333-
334359
EXAMPLES::
335360
336361
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
@@ -393,9 +418,10 @@ def is_normalized(self):
393418
sage: phi = isom * phi
394419
sage: phi.is_normalized()
395420
True
421+
422+
ALGORITHM: We check if :meth:`scaling_factor` returns `1`.
396423
"""
397-
phi_formal = self.formal(prec=5)
398-
return phi_formal[1] == 1
424+
return self.scaling_factor() == 1
399425

400426

401427
def is_separable(self):

Diff for: src/sage/schemes/elliptic_curves/hom_composite.py

+30
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,36 @@ def formal(self, prec=20):
765765
res = res(phi.formal(prec=prec))
766766
return res
767767

768+
def scaling_factor(self):
769+
r"""
770+
Return the Weierstrass scaling factor associated to this
771+
composite morphism.
772+
773+
The scaling factor is the constant `u` (in the base field)
774+
such that `\varphi^* \omega_2 = u \omega_1`, where
775+
`\varphi: E_1\to E_2` is this morphism and `\omega_i` are
776+
the standard Weierstrass differentials on `E_i` defined by
777+
`\mathrm dx/(2y+a_1x+a_3)`.
778+
779+
EXAMPLES::
780+
781+
sage: from sage.schemes.elliptic_curves.hom_composite import EllipticCurveHom_composite
782+
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
783+
sage: E = EllipticCurve(GF(65537), [1,2,3,4,5])
784+
sage: P = E.lift_x(7321)
785+
sage: phi = EllipticCurveHom_composite(E, P)
786+
sage: phi = WeierstrassIsomorphism(phi.codomain(), [7,8,9,10]) * phi
787+
sage: phi.formal()
788+
7*t + 65474*t^2 + 511*t^3 + 61316*t^4 + 20548*t^5 + 45511*t^6 + 37285*t^7 + 48414*t^8 + 9022*t^9 + 24025*t^10 + 35986*t^11 + 55397*t^12 + 25199*t^13 + 18744*t^14 + 46142*t^15 + 9078*t^16 + 18030*t^17 + 47599*t^18 + 12158*t^19 + 50630*t^20 + 56449*t^21 + 43320*t^22 + O(t^23)
789+
sage: phi.scaling_factor()
790+
7
791+
792+
ALGORITHM: The scaling factor is multiplicative under
793+
composition, so we return the product of the individual
794+
scaling factors associated to each factor.
795+
"""
796+
return prod(phi.scaling_factor() for phi in self._phis)
797+
768798
def is_injective(self):
769799
"""
770800
Determine whether this composite morphism has trivial kernel.

Diff for: src/sage/schemes/elliptic_curves/weierstrass_morphism.py

+23
Original file line numberDiff line numberDiff line change
@@ -898,3 +898,26 @@ def __neg__(self):
898898
w = baseWI(-1, 0, -a1, -a3)
899899
urst = baseWI.__mul__(self, w).tuple()
900900
return WeierstrassIsomorphism(self._domain, urst, self._codomain)
901+
902+
def scaling_factor(self):
903+
r"""
904+
Return the Weierstrass scaling factor associated to this
905+
Weierstrass isomorphism.
906+
907+
The scaling factor is the constant `u` (in the base field)
908+
such that `\varphi^* \omega_2 = u \omega_1`, where
909+
`\varphi: E_1\to E_2` is this isomorphism and `\omega_i` are
910+
the standard Weierstrass differentials on `E_i` defined by
911+
`\mathrm dx/(2y+a_1x+a_3)`.
912+
913+
EXAMPLES::
914+
915+
sage: E = EllipticCurve(QQbar, [0,1])
916+
sage: all(f.scaling_factor() == f.formal()[1] for f in E.automorphisms())
917+
True
918+
919+
ALGORITHM: The scaling factor equals the `u` component of
920+
the tuple `(u,r,s,t)` defining the isomorphism.
921+
"""
922+
return self.u
923+

0 commit comments

Comments
 (0)