Skip to content

Instantly share code, notes, and snippets.

@defeo
Created March 10, 2017 14:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save defeo/d76b969ad27c8acf59245256d6c842bc to your computer and use it in GitHub Desktop.
Save defeo/d76b969ad27c8acf59245256d6c842bc to your computer and use it in GitHub Desktop.
Old code for supporting non-normalized isogenies in Sage
# HG changeset patch
# User Luca De Feo <luca.defeo@polytechnique.edu>
# Date 1309987774 14400
# Node ID c1ea68aa11f5be87be8503894782b7e86d0fc25b
# Parent 308b228f1089ab3ae54b36d7838697e1454d8497
Non normalized isogenies.
diff -r 308b228f1089 -r c1ea68aa11f5 sage/schemes/elliptic_curves/ell_curve_isogeny.py
--- a/sage/schemes/elliptic_curves/ell_curve_isogeny.py Thu Apr 28 17:00:51 2011 +0200
+++ b/sage/schemes/elliptic_curves/ell_curve_isogeny.py Wed Jul 06 17:29:34 2011 -0400
@@ -86,6 +86,8 @@
from sage.schemes.elliptic_curves import ell_isogeny_char_zero
+from sage.databases.db_modular_polynomials import AtkinModularPolynomialDatabase
+
#
# Private function for parsing input to determine the type of
# algorithm
@@ -511,6 +513,18 @@
rationals, then the codomain is set to be the unique global
minimum model.
+ - ``normalized`` - a boolean or an element of the base field (default:``True``).
+ This parameter is only considered when the codomain parameter
+ is not ``None``. If an element `u` of the base field is
+ given, then the isogeny `\varphi` between ``domain`` and
+ ``codomain`` is assumed to be normalized by a factor `u`,
+ i.e. the pullback `\varphi^\ast` is such that
+ `\varphi^\ast \omega_\text{codomain} = u \omega_\text{codomain}`.
+ The default ``True`` is equivalent to 1. ``False`` or 0
+ means that the normalization factor is unknown and must
+ be computed by evaluating the derivatives of the modular
+ polynomial of level ``degree`` (this may be time consuming).
+
- ``check`` (default: ``True``) checks if the input is valid to define an isogeny
EXAMPLES:
@@ -704,15 +718,23 @@
sage: phi_s.rational_maps() == phi.rational_maps()
True
- However only normalized isogenies can be constructed this
- way. So it won't find the multiplication-by-3 endomorphism::
+ However only normalized isogenies can be constructed by default.
+ So it won't find the multiplication-by-3 endomorphism::
- sage: E.isogeny(None, codomain=E,degree=9)
+ sage: E.isogeny(None, codomain=E, degree=9)
Traceback (most recent call last):
...
ValueError: The two curves are not linked by a normalized isogeny of degree 9
-
- nor the dual isogeny::
+
+ It is well known, however, that the multiplication-by-m endomorphism is
+ such that `[m]^\ast\omega = m\omega`, thus we can use the ``normalized``
+ parameter::
+
+ sage: E.isogeny(None, codomain=E, degree=9, normalized=3)
+ Isogeny of degree 9 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
+
+ Similarly, the dual isogeny is not normalized, but its normalization factor
+ is easily obtained as above::
sage: E2.isogeny(None,codomain=E,degree=5)
Traceback (most recent call last):
@@ -720,8 +742,8 @@
ValueError: The two curves are not linked by a normalized isogeny of degree 5
sage: phi.dual()
Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 - x^2 - 7820*x - 263580 over Rational Field to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field
- sage: phi.dual().is_normalized()
- False
+ sage: phi.dual().normalization_factor()
+ 5
Here an example of a construction of a endomorphisms with cyclic
kernel on a CM-curve::
@@ -839,7 +861,8 @@
# Python Special Functions
#
- def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True):
+ def __init__(self, E, kernel, codomain=None, degree=None, model=None,
+ normalized=True, check=True):
r"""
Constructor for EllipticCurveIsogeny class.
@@ -898,8 +921,15 @@
# save the domain/codomain: really used now (trac #7096)
old_domain = E
old_codomain = codomain
-
- (pre_isom, post_isom, E, codomain, kernel) = compute_sequence_of_maps(E, codomain, degree)
+
+ if not normalized:
+ u = compute_normalization_factor(E, codomain, degree)
+ else:
+ u = E.base_field()(normalized)
+
+ (pre_isom, post_isom, E, codomain,
+ kernel) = compute_sequence_of_maps(E, codomain, degree,
+ normalization_factor=u)
self.__init_algebraic_structs(E)
@@ -2913,8 +2943,8 @@
sage: phi2.get_post_isomorphism()
Generic morphism:
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83
- To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + 77*y = x^3 + 49*x + 28 over Finite Field of size 83
- Via: (u,r,s,t) = (1, 7, 42, 80)
+ To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 16 over Finite Field of size 83
+ Via: (u,r,s,t) = (1, 7, 42, 42)
"""
return self.__post_isomorphism
@@ -2976,7 +3006,30 @@
"""
self.set_post_isomorphism(WeierstrassIsomorphism(self.__E2, (-1,0,-self.__E2.a1(),-self.__E2.a3())))
- def is_normalized(self, via_formal=True, check_by_pullback=True):
+ def normalization_factor(self):
+ r"""
+ Returns the element `u` of the base field such that
+ `\varphi^\ast\omega_{\text{codomain}} = u\omega_{\text{domain}}`.
+
+ EXAMPLES::
+ sage: E = EllipticCurve('11a1')
+ sage: phi = E.isogeny(None, E, 4, normalized=2)
+ sage: phi.normalization_factor()
+ 2
+ sage: phi.dual().normalization_factor()
+ 2
+
+ sage: E = EllipticCurve(GF(17), [-2, 3, -5, 7, -11])
+ sage: R.<x> = GF(17)[]
+ sage: f = x+6
+ sage: phi = EllipticCurveIsogeny(E, f)
+ sage: phi.normalization_factor()
+ 1
+
+ """
+ return self.formal(prec=1)[1]
+
+ def is_normalized(self):
r"""
Returns ``True`` if this isogeny is normalized. An isogeny
`\varphi\colon E\to E_2` between two given Weierstrass
@@ -2985,15 +3038,6 @@
`omega_2` are the invariant differentials on `E` and `E_2`
corresponding to the given equation.
- INPUT:
-
- - ``via_formal`` - (default: ``True``) If ``True`` it simply checks if
- the leading term of the formal series is 1. Otherwise
- it uses a deprecated algorithm involving the second
- optional argument.
-
- - ``check_by_pullback`` - (default:``True``) Deprecated.
-
EXAMPLES::
sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism
@@ -3054,85 +3098,7 @@
True
"""
- # easy algorithm using the formal expansion.
- if via_formal:
- phi_formal = self.formal(prec=5)
- return phi_formal[1] == 1
-
- # this is the old algorithm. it should be deprecated.
- check_prepost_isomorphism = False
-
- f_normalized = True
-
- if (check_by_pullback):
-
- (Xmap, Ymap) = self.rational_maps()
-
- E1 = self.__E1
- E2 = self.__E2
-
- a1 = E1.a1()
- a3 = E1.a3()
-
- a1pr = E2.a1()
- a3pr = E2.a3()
-
- x = self.__x_var
- y = self.__y_var
-
- Xmap_pr = Xmap.derivative(x)
-
- domain_inv_diff = 1/(2*y + a1*x + a3)
- codomain_inv_diff = Xmap_pr/(2*Ymap + a1pr*Xmap + a3pr)
-
- inv_diff_quo = domain_inv_diff/codomain_inv_diff
-
- if (1 == inv_diff_quo):
- f_normalized = True
- else:
- # For some reason, in certain cases, when the isogeny
- # is pre or post composed with a translation the
- # resulting rational functions are too complicated for
- # sage to simplify down to a constant in this case, we
- # do some cheating by checking if the post-composition
- # by isogeny has a non 1 scaling factor
- if ( inv_diff_quo.numerator().is_constant() and (inv_diff_quo.denominator().is_constant) ):
- f_normalized = False
- else:
- check_prepost_isomorphism = True
- else:
- check_prepost_isomorphism = True
-
- # If we skip checking by the pullback of the invariant
- # differential OR if that was inconclusive We explicitly check
- # if there is a post isomorphism and if it has a non 1 scaling
- # factor or if it is a just a translation. NOTE: This only
- # works because we are using algorithms for calculating the
- # isogenies that calculate a separable normalized isogeny, if
- # this changes, this check will no longer be correct.
- #
- if (check_prepost_isomorphism):
- post_isom = self.__post_isomorphism
- if (post_isom is not None):
- if (1 == self.__base_field(post_isom.u)):
- f_post_normalized = True
- else:
- f_post_normalized = False
- else:
- f_post_normalized = True
-
- pre_isom = self.__pre_isomorphism
- if (pre_isom is not None):
- if (1 == self.__base_field(pre_isom.u)):
- f_pre_normalized = True
- else:
- f_pre_normalized = False
- else:
- f_pre_normalized = True
-
- f_normalized = f_pre_normalized and f_post_normalized
-
- return f_normalized
+ return self.normalization_factor() == 1
def dual(self):
r"""
@@ -3217,63 +3183,65 @@
"""
- if (self.__base_field.characteristic() in [2,3]):
- raise NotImplemented
-
if (self.__dual is not None):
return self.__dual
+ F = self.__base_field
+ d = self.__degree
+
# trac 7096
- (E1, E2pr, pre_isom, post_isom) = compute_intermediate_curves(self.codomain(), self.domain())
-
- F = self.__base_field
-
- d = self.__degree
-
- # trac 7096
- if F(d) == 0:
- raise NotImplementedError, "The dual isogeny is not separable, but only separable isogenies are implemented so far"
+ #if F(d) == 0:
+ # raise NotImplementedError, "The dual isogeny is not separable, but only separable isogenies are implemented so far"
+
+ u = F(d) / self.normalization_factor()
+ phi_hat = EllipticCurveIsogeny(self.codomain(), None, self.domain(),
+ d, normalized=u)
+ phi_hat.__dual = self
+ return phi_hat
- # trac 7096
- # this should take care of the case when the isogeny is not normalized.
- u = self.formal(prec=5)[1]
- isom = WeierstrassIsomorphism(E2pr, (u/F(d), 0, 0, 0))
-
- E2 = isom.codomain().codomain()
-
- pre_isom = self.__E2.isomorphism_to(E1)
- post_isom = E2.isomorphism_to(self.__E1)
-
- phi_hat = EllipticCurveIsogeny(E1, None, E2, d)
-
- phi_hat.set_pre_isomorphism(pre_isom)
- phi_hat.set_post_isomorphism(post_isom)
- phi_hat.__perform_inheritance_housekeeping()
-
- assert phi_hat.codomain() == self.domain()
-
- # trac 7096 : this adjust a posteriori the automorphism
- # on the codomain of the dual isogeny.
- # we used _a_ Weierstrass isomorphism to get to the original
- # curve, but we may have to change it my an automorphism.
- # we impose that the composition has the degree
- # as a leading coefficient in the formal expansion.
-
- phi_sc = self.formal(prec=5)[1]
- phihat_sc = phi_hat.formal(prec=5)[1]
-
- sc = phi_sc * phihat_sc/F(d)
-
- if sc != 1:
- auts = phi_hat.codomain().automorphsims()
- aut = [a for a in auts if a.u == sc]
- if len(aut) != 1:
- raise ValueError, "There is a bug in dual()."
- phi_hat.set_post_isomorphism(a[0])
-
- self.__dual = phi_hat
-
- return phi_hat
+# # trac 7096
+# (E1, E2pr, pre_isom, post_isom) = compute_intermediate_curves(self.codomain(), self.domain())
+#
+# # trac 7096
+# # this should take care of the case when the isogeny is not normalized.
+# u = self.formal(prec=5)[1]
+# isom = WeierstrassIsomorphism(E2pr, (u/F(d), 0, 0, 0))
+#
+# E2 = isom.codomain().codomain()
+#
+# pre_isom = self.__E2.isomorphism_to(E1)
+# post_isom = E2.isomorphism_to(self.__E1)
+#
+# phi_hat = EllipticCurveIsogeny(E1, None, E2, d)
+#
+# phi_hat.set_pre_isomorphism(pre_isom)
+# phi_hat.set_post_isomorphism(post_isom)
+# phi_hat.__perform_inheritance_housekeeping()
+#
+# assert phi_hat.codomain() == self.domain()
+#
+# # trac 7096 : this adjust a posteriori the automorphism
+# # on the codomain of the dual isogeny.
+# # we used _a_ Weierstrass isomorphism to get to the original
+# # curve, but we may have to change it my an automorphism.
+# # we impose that the composition has the degree
+# # as a leading coefficient in the formal expansion.
+#
+# phi_sc = self.formal(prec=5)[1]
+# phihat_sc = phi_hat.formal(prec=5)[1]
+#
+# sc = phi_sc * phihat_sc/F(d)
+#
+# if sc != 1:
+# auts = phi_hat.codomain().automorphsims()
+# aut = [a for a in auts if a.u == sc]
+# if len(aut) != 1:
+# raise ValueError, "There is a bug in dual()."
+# phi_hat.set_post_isomorphism(a[0])
+#
+# self.__dual = phi_hat
+#
+# return phi_hat
def formal(self,prec=20):
r"""
@@ -3282,33 +3250,32 @@
INPUT:
- - ``prec`` - (default = 20), the precision with which the computations
- in the formal group are carried out.
+ - ``prec`` - (default = 20), the precision with which the power series
+ is computed.
EXAMPLES::
sage: E = EllipticCurve(GF(13),[1,7])
sage: phi = E.isogeny(E(10,4))
sage: phi.formal()
- t + 12*t^13 + 2*t^17 + 8*t^19 + 2*t^21 + O(t^23)
+ t + 12*t^13 + 2*t^17 + 8*t^19 + O(t^20)
sage: E = EllipticCurve([0,1])
sage: phi = E.isogeny(E(2,3))
- sage: phi.formal(prec=10)
+ sage: phi.formal(prec=13)
t + 54*t^5 + 255*t^7 + 2430*t^9 + 19278*t^11 + O(t^13)
sage: E = EllipticCurve('11a2')
sage: R.<x> = QQ[]
sage: phi = E.isogeny(x^2 + 101*x + 12751/5)
- sage: phi.formal(prec=7)
+ sage: phi.formal(prec=10)
t - 2724/5*t^5 + 209046/5*t^7 - 4767/5*t^8 + 29200946/5*t^9 + O(t^10)
-
"""
Eh = self.__E1.formal()
f, g = self.rational_maps()
- xh = Eh.x(prec=prec)
- yh = Eh.y(prec=prec)
+ xh = Eh.x(prec=prec-3)
+ yh = Eh.y(prec=prec-3)
fh = f(xh,yh)
gh = g(xh,yh)
return -fh/gh
@@ -3470,23 +3437,28 @@
# def starks_find_r_and_t(T, Z):
-def compute_intermediate_curves(E1, E2):
+def compute_intermediate_curves(E1, E2, normalization_factor=1):
r"""
- Computes isomorphism from ``E1`` to an intermediate domain and an
- isomorphism from an intermediate codomain to ``E2``.
+ Gives canonical models for E1 and E2 and the related isomorphisms.
- Intermediate domain and intermediate codomain, are in short
- Weierstrass form.
+ If the caracteristic of the base field is not 2 or 3, these are the short
+ Weierstrass models.
- This is used so we can compute `\wp` functions from the short
- Weierstrass model more easily.
-
- The underlying field must be of characteristic not equal to 2,3.
-
+ In characteristic 3, this is the model `y^2 = x^3 + a_2 x^2 + a_4 x + a_6`
+ obtained by completing the square.
+
+ In characteristic 2 these are just ``E1`` and ``E2`` themselves.
+
+ The ``normalization_factor`` can be used to scale the model of ``E2``. If it
+ is different from 1, then the normalized model is scaled by
+ ``1/normalization_factor`` (i.e., via the isomorphism with
+ ``(u, r, s, t) = (1/normalization_factor, 0, 0, 0)``)
+
INPUT:
- ``E1`` - an elliptic curve
- ``E2`` - an elliptic curve
+ - ``normalization_factor`` - an element of the base field. Default is 1.
OUTPUT:
@@ -3514,8 +3486,8 @@
Via: (u,r,s,t) = (1, 76, 41, 3),
Generic morphism:
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83
- To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + 77*y = x^3 + 49*x + 28 over Finite Field of size 83
- Via: (u,r,s,t) = (1, 7, 42, 80))
+ To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x + 16 over Finite Field of size 83
+ Via: (u,r,s,t) = (1, 7, 42, 42))
sage: R.<x> = QQ[]
sage: K.<i> = NumberField(x^2 + 1)
@@ -3532,44 +3504,58 @@
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1
Via: (u,r,s,t) = (1, 0, 0, 0))
+
+ sage: K.<z> = GF(8, 'z')
+ sage: E = EllipticCurve([1, 0, z, z + 1, z^2])
+ sage: E1, E2, i1, i2 = compute_intermediate_curves(E, E)
+ sage: E == E1 and E == E2
+ True
+ sage: i1.is_identity() and i2.is_identity()
+ True
+
+ sage: K = GF(27, 'z')
+ sage: E = EllipticCurve(K, [1, 1, 0, 1, 1])
+ sage: compute_intermediate_curves(E, E, -1)
+ (Elliptic Curve defined by y^2 = x^3 + 2*x^2 + x + 1 over Finite Field in z of size 3^3,
+ Elliptic Curve defined by y^2 = x^3 + 2*x^2 + x + 1 over Finite Field in z of size 3^3,
+ Generic morphism:
+ From: Abelian group of points on Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + x + 1 over Finite Field in z of size 3^3
+ To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 2*x^2 + x + 1 over Finite Field in z of size 3^3
+ Via: (u,r,s,t) = (1, 0, 1, 0),
+ Generic morphism:
+ From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 2*x^2 + x + 1 over Finite Field in z of size 3^3
+ To: Abelian group of points on Elliptic Curve defined by y^2 + x*y = x^3 + x^2 + x + 1 over Finite Field in z of size 3^3
+ Via: (u,r,s,t) = (2, 0, 1, 0))
"""
- if (E1.base_ring().characteristic() in [2,3]):
- raise NotImplemented
-
- # compute the r,s,t values that clear the denominator of E1
- a1 = E1.a1()
- a2 = E1.a2()
- a3 = E1.a3()
-
- s1 = -a1/2
- r1 = (s1**2 + s1*a1 - a2)/3
- t1 = (-r1*a1 - a3)/2
-
- # compute the isomorphism from E1 to intermediate_domain
- pre_isom = WeierstrassIsomorphism(E1, (1, r1, s1, t1))
-
+ K = E1.base_ring()
+
+ if K.characteristic() == 2:
+ return (E1, E2, WeierstrassIsomorphism(E1, (1, 0, 0, 0), E1),
+ WeierstrassIsomorphism(E1, (1, 0, 0, 0), E2))
+
+ # In characteristic 3, a model of the form
+ # y^2 = x^3 + a2 x^2 + a4 x + a6
+ # is enough
+ complete_cube = K.characteristic() != 3
+
+ # Domain curve to simplified Weierstrass model
+ si = E1.isomorphism_to(E1.short_weierstrass_model(complete_cube))
+ pre_isom = WeierstrassIsomorphism(E1, (1, si.r, si.s, si.t))
intermediate_domain = pre_isom.codomain().codomain()
- # compute the r,s,t values that clear the denominator of E2
- a1pr = E2.a1()
- a2pr = E2.a2()
- a3pr = E2.a3()
-
- s2 = -a1pr/2
- r2 = (s2**2 + s2*a1pr - a2pr)/3
- t2 = (-r2*a1pr - a3pr)/2
-
- post_isom_inv = WeierstrassIsomorphism(E2, (1, r2, s2, t2))
- intermediate_codomain = post_isom_inv.codomain().codomain()
-
- post_isom = WeierstrassIsomorphism(intermediate_codomain, (1, -r2, -s2, -t2))
+ # Codomain curve to simplified Weierstrass model plus
+ # scaling by the inverse of the normalization factor
+ si = E2.isomorphism_to(E2.short_weierstrass_model(complete_cube))
+ post_isom = ~WeierstrassIsomorphism(E2, (~normalization_factor,
+ si.r, si.s, si.t))
+ intermediate_codomain = post_isom.domain().codomain()
return (intermediate_domain, intermediate_codomain, pre_isom, post_isom)
-def compute_sequence_of_maps(E1, E2, ell, algorithm=None):
+def compute_sequence_of_maps(E1, E2, ell, algorithm=None, normalization_factor=1):
r"""
Given domain ``E1`` and codomain ``E2`` such that there is a
degree ``ell`` separable normalized isogeny from ``E1`` to ``E2``,
@@ -3638,15 +3624,14 @@
Via: (u,r,s,t) = (1, 8, 48, 44),
Generic morphism:
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97
- To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + 9*y = x^3 + 83*x + 6 over Finite Field of size 97
- Via: (u,r,s,t) = (1, 89, 49, 53),
- Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97,
- Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97,
- x^5 + 67*x^4 + 13*x^3 + 35*x^2 + 77*x + 69)
+ To: Abelian group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 + 87*x + 26 over Finite Field of size 97
+ Via: (u,r,s,t) = (1, 89, 49, 49),
+ Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97,
+ Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97, x^5 + 67*x^4 + 13*x^3 + 35*x^2 + 77*x + 69)
"""
- (E1pr, E2pr, pre_isom, post_isom) = compute_intermediate_curves(E1, E2)
+ (E1pr, E2pr, pre_isom, post_isom) = compute_intermediate_curves(E1, E2, normalization_factor)
if algorithm is None or algorithm in ell_isogeny_char_zero.algorithm_names:
try:
@@ -3660,6 +3645,124 @@
return (pre_isom, post_isom, E1pr, E2pr, ker_poly)
+def _compute_normalization_factor(E1, E2, degree, f=None):
+ r"""
+ Private routine called by ``compute_normalization_factor``.
+
+ INPUT:
+
+ - ``E1`` - Elliptic curve.
+ - ``E2`` - Elliptic curve.
+ - ``degree`` - a prime, the degree of the isogeny from E1 to E2.
+ - ``f`` - (optional) a common root of Phi(X, j1) and Phi(X, j2), where
+ Phi is Atkin's modular polynomial of degree ``degree``.
+
+ OUTPUT:
+
+ field element -- the normalization factor.
+
+ """
+
+ K = E1.base_field()
+ p = K.characteristic()
+ j1 = E1.j_invariant()
+ j2 = E2.j_invariant()
+
+ if p != 0 and degree % p == 0:
+ return 0
+ elif p == 2 or p == 3:
+ # Up to isomorphism there is only one supersingular curve and
+ # any of its endomorphisms has normalization factor + or - 1
+ if j1 == 0 and j2 == 0:
+ w = WeierstrassIsomorphism(E1, None, E2)
+ return w.u
+
+ # In the ordinary case, the characteristic 2 and 3 cases behave
+ # differently.
+ elif j1 != 0 and j2 != 0:
+ # In characteristic 2, models of the form
+ # y^2 + xy = x^3 + a x^2 + b
+ # are always normalized.
+ if p == 2:
+ return E1.a1() / E2.a1()
+
+ # In characteristic 3, models
+ # y^2 = x^3 + a1 x^2 + b1,
+ # y^2 = x^3 + a2 x^2 + b2
+ # are normalized if and only if a1 = a2.
+ else:
+ return ((E1.a1()**2 + E2.a2()) / (E1.a1()**2 + E2.a2())).sqrt()
+ else:
+ raise ValueError, "Curves are not isogenous."
+
+ # In characteristic not 2 or 3, we go to the short Weierstrass model
+ # and apply Elkies' formulas
+ else:
+ E1sh = E1.short_weierstrass_model()
+
+ P = PolynomialRing(K, "F,J");
+ F, J = P.gens()
+ DB = AtkinModularPolynomialDatabase()
+ Phi = P(DB[degree])
+
+ # Since we use Atkin's modular polynomials, we need to know the common
+ # root f of Phi(X, j1) and Phi(X, j2)
+ if not f:
+ fs = Phi(F, j1).gcd(Phi(F, j2)).univariate_polynomial().roots(multiplicities=False)
+ if not fs:
+ raise ValueError, "Curves are not " + degree + "-isogenous."
+ else:
+ f = fs[0]
+
+ # Elkies' formulae
+ DF = f * Phi.derivative(F)(f, j1)
+ DJ = j1 * Phi.derivative(J)(f, j1)
+ DFst = f * Phi.derivative(F)(f, j2)
+ DJst = degree * j2 * Phi.derivative(J)(f, j2)
+ _, _, _, a4, a6 = E1sh.a_invariants()
+
+ jj = j2 / (j2 - 1728)
+
+ A4 = -K(27)/K(4) * degree**4 * (DFst**2 * DJ**2 * a6**2) / (DJst**2 * DF**2 * a4**2) * jj
+ A6 = -K(27)/K(4) * degree**6 * (DFst**3 * DJ**3 * a6**3) / (DJst**3 * DF**3 * a4**3) * jj
+
+ E2sh = EllipticCurve([A4, A6])
+
+ return WeierstrassIsomorphism(E1, None, E1sh).u * WeierstrassIsomorphism(E2sh, None, E2).u
+
+def compute_normalization_factor(E1, E2, degree):
+ r"""
+ Compute the normalization factor of the isogeny of prime degree ``degree``
+ from ``E1`` to ``E2``.
+
+ INPUT:
+
+ - ``E1`` - Elliptic curve.
+ - ``E2`` - Elliptic curve.
+ - ``degree`` - a prime, the degree of the isogeny from E1 to E2.
+
+ OUTPUT:
+
+ field element -- the normalization factor.
+
+ ..note:
+ The normalization factor is only defined up to the automorphisms of
+ ``E1`` and ``E2``.
+
+ EXAMPLES::
+
+ sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_normalization_factor
+ sage: E = EllipticCurve(GF(97), [1,0,1,1,0])
+ sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21
+ sage: phi = EllipticCurveIsogeny(E, f)
+ sage: compute_normalization_factor(E, phi.codomain(), 11)^2
+ 1
+
+ """
+ return _compute_normalization_factor(E1, E2, degree)
+
+
+
##########################################################################
# The following section is all about computing l-isogenies, where l is
# a prime. The genus 0 cases `l` = 2, 3, 5, 7 and 13 are
diff -r 308b228f1089 -r c1ea68aa11f5 sage/schemes/elliptic_curves/ell_field.py
--- a/sage/schemes/elliptic_curves/ell_field.py Thu Apr 28 17:00:51 2011 +0200
+++ b/sage/schemes/elliptic_curves/ell_field.py Wed Jul 06 17:29:34 2011 -0400
@@ -697,7 +697,8 @@
else:
return Eout
- def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True):
+ def isogeny(self, kernel, codomain=None, degree=None, model=None,
+ normalized=True, check=True):
r"""
Returns an elliptic curve isogeny from self.
@@ -747,6 +748,18 @@
rationals, then the codomain is set to be the unique
global minimum model.
+ - ``normalized`` - a boolean or an element of the base field (default:``True``).
+ This parameter is only considered when the codomain parameter
+ is not ``None``. If an element `u` of the base field is
+ given, then the isogeny `\phi` between ``domain`` and
+ ``codomain`` is assumed to be normalized by a factor `u`,
+ i.e. the pullback `\phi^\ast` is such that
+ `\phi^\ast \omega_\text{codomain} = u \omega_\text{codomain}`.
+ The default ``True`` is equivalent to 1. ``False`` or 0
+ means that the normalization factor is unknown and must
+ be computed by evaluating the derivatives of the modular
+ polynomial of level ``degree`` (this may be time consuming).
+
- ``check`` (default: True) checks if the input is valid to define an
isogeny
@@ -786,7 +799,8 @@
ValueError: The polynomial does not define a finite subgroup of the elliptic curve.
"""
- return EllipticCurveIsogeny(self, kernel, codomain, degree, model, check=check)
+ return EllipticCurveIsogeny(self, kernel, codomain, degree, model,
+ normalized, check)
def isogeny_codomain(self, kernel, degree=None):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment