Ticket #7096: trac_7096_4.patch
| File trac_7096_4.patch, 68.5 KB (added by wuthrich, 4 years ago) |
|---|
-
doc/en/reference/plane_curves.rst
# HG changeset patch # User Chris Wuthrich <christian.wuthrich@gmail.com> # Date 1257087516 0 # Node ID c4ee267a4cf7dedd6120e8ac2d49ae7f86b02981 # Parent be27090d4a20592e2efd7a39e67a757107ce032b trac 7096 : isogenies and weierstrass_p diff -r be27090d4a20 -r c4ee267a4cf7 doc/en/reference/plane_curves.rst
a b 24 24 sage/schemes/elliptic_curves/kodaira_symbol 25 25 sage/schemes/elliptic_curves/weierstrass_morphism 26 26 sage/schemes/elliptic_curves/ell_curve_isogeny 27 sage/schemes/elliptic_curves/ell_wp 27 28 sage/schemes/elliptic_curves/period_lattice 28 29 sage/schemes/elliptic_curves/formal_group 29 30 sage/schemes/elliptic_curves/ell_tate_curve -
sage/schemes/elliptic_curves/ell_curve_isogeny.py
diff -r be27090d4a20 -r c4ee267a4cf7 sage/schemes/elliptic_curves/ell_curve_isogeny.py
a b 5 5 origin of `E_1` to the origin of `E_2`. Such a morphism is automatically a morphism of group schemes and the kernel is a finite subgroup scheme of `E_1`. 6 6 Such a subscheme can either be given by a list of generators, which have to be torsion points, or by a polynomial in the coordinate `x` of the Weierstrass equation of `E_1`. 7 7 8 The usual way to create and work with isogenies is illustrated with the following example:: 9 10 sage: k = GF(11) 11 sage: E = EllipticCurve(k,[1,1]) 12 sage: Q = E(6,5) 13 sage: phi = E.isogeny(Q) 14 sage: phi 15 Isogeny of degree 7 from Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 11 to Elliptic Curve defined by y^2 = x^3 + 7*x + 8 over Finite Field of size 11 16 sage: P = E(4,5) 17 sage: phi(P) 18 (10 : 0 : 1) 19 sage: phi.codomain() 20 Elliptic Curve defined by y^2 = x^3 + 7*x + 8 over Finite Field of size 11 21 sage: phi.rational_maps() 22 ((x^7 + 4*x^6 - 3*x^5 - 2*x^4 - 3*x^3 + 3*x^2 + x - 2)/(x^6 + 4*x^5 - 4*x^4 - 5*x^3 + 5*x^2), (x^9*y - 5*x^8*y - x^7*y + x^5*y - x^4*y - 5*x^3*y - 5*x^2*y - 2*x*y - 5*y)/(x^9 - 5*x^8 + 4*x^6 - 3*x^4 + 2*x^3)) 23 24 The functions directly accessible from an elliptic curve ``E`` over a field are ``isogeny`` and ``isogeny_codomain``. 25 26 The most useful functions that apply to isogenies are 27 28 - ``codomain`` 29 - ``degree`` 30 - ``domain`` 31 - ``dual`` 32 - ``rational_maps`` 33 - ``kernel_polynomial`` 34 35 .. Warning:: 36 37 Only cyclic isogenies are implemented (except for [2]). Some algorithms may need the isogeny to be normalized. 38 8 39 AUTHORS: 9 40 10 41 - Daniel Shumow <shumow@gmail.com>: 2009-04-19: initial version 11 42 - Chris Wuthrich : 7/09: changes: add check of input, not the full list is needed. 43 10/09: eliminating some bugs. 12 44 """ 13 45 14 46 #***************************************************************************** … … 26 58 27 59 from sage.structure.sage_object import SageObject 28 60 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing 61 from sage.rings.laurent_series_ring import LaurentSeriesRing 29 62 from sage.rings.polynomial.all import is_Polynomial 30 63 from sage.schemes.elliptic_curves.all import EllipticCurve 31 64 from sage.schemes.elliptic_curves.all import is_EllipticCurve … … 45 78 r""" 46 79 Helper function that allows the various isogeny functions to infer the algorithm type from the parameters passed in. 47 80 48 If kernelis a list of points on the EllipticCurve `E`, then we assume the algorithm to use is Velu.81 If ``kernel`` is a list of points on the EllipticCurve `E`, then we assume the algorithm to use is Velu. 49 82 50 If kernelis a list of coefficients or a univariate polynomial we try to use the Kohel's algorithms.83 If ``kernel`` is a list of coefficients or a univariate polynomial we try to use the Kohel's algorithms. 51 84 52 85 EXAMPLES: 53 86 … … 98 131 99 132 - ``kernel`` - Either a list of points in the kernel of the isogeny, or a kernel polynomial (specified as a either a univariate polynomial or a coefficient list.) 100 133 101 - ``degree`` - an integer, (default:None) optionally specified degree of the kernel. 102 134 - ``degree`` - an integer, (default:``None``) optionally specified degree of the kernel. 103 135 104 136 OUTPUT: 105 137 … … 371 403 def two_torsion_part(E, poly_ring, psi, degree): 372 404 r""" 373 405 374 Returns the greatest common divisor of ``psi`` and the 2 torsion polynomial of E.406 Returns the greatest common divisor of ``psi`` and the 2 torsion polynomial of `E`. 375 407 376 408 EXAMPLES: 377 409 … … 410 442 r""" 411 443 Class Implementing Isogenies of Elliptic Curves 412 444 413 This class implements separable, normalized isogenies of elliptic curves.445 This class implements cyclic, separable, normalized isogenies of elliptic curves. 414 446 415 447 Several different algorithms for computing isogenies are available. These include: 416 448 … … 420 452 421 453 - Kohel's Formulas: 422 454 Kohel's original formulas for computing isogenies. 423 This algorithm is selected by giving as the ``kernel`` parameter a polynomial (or a coefficient list (little endian)) which will define the kernel of the isogeny.455 This algorithm is selected by giving as the ``kernel`` parameter a monic polynomial (or a coefficient list (little endian)) which will define the kernel of the isogeny. 424 456 425 457 INPUT: 426 458 427 459 - ``E`` - an elliptic curve, the domain of the isogeny to initialize. 428 - ``kernel`` - a kernel, either a point in ``E``, a list of points in ``E``, a kernel polynomial, or ``None``. 429 If initiating from a domain/codomain, this must be set to None. 430 - ``codomain`` - an elliptic curve (default:None). If ``kernel`` is None, 431 then this must be the codomain of a separable normalized isogeny, 432 furthermore, ``degree`` must be the degree of the isogeny from ``E`` to ``codomain``. 433 If ``kernel`` is not None, then this must be isomorphic to the codomain of the 434 normalized separable isogeny defined by ``kernel``, 435 in this case, the isogeny is post composed with an isomorphism so that this parameter is the codomain. 436 - ``degree`` - an integer (default:None). 437 If ``kernel`` is None, then this is the degree of the isogeny from ``E`` to ``codomain``. 438 If ``kernel`` is not None, then this is used to determine whether or not to skip a gcd 439 of the kernel polynomial with the two torsion polynomial of ``E``. 440 - ``model`` - a string (default:None). Only supported variable is "minimal", in which case if 441 ``E`` is a curve over the rationals, then the codomain is set to be the unique global minimum model. 442 - ``check`` (default: True) checks if the input is valid to define an isogeny 460 - ``kernel`` - a kernel, either a point in ``E``, a list of points in ``E``, a monic kernel polynomial, or ``None``. 461 If initiating from a domain/codomain, this must be set to None. 462 - ``codomain`` - an elliptic curve (default:``None``). If ``kernel`` is ``None``, then this must be the codomain of a cyclic, separable, normalized isogeny, furthermore, ``degree`` must be the degree of the isogeny from ``E`` to ``codomain``. If ``kernel`` is not ``None``, then this must be isomorphic to the codomain of the cyclic normalized separable isogeny defined by ``kernel``, in this case, the isogeny is post composed with an isomorphism so that this parameter is the codomain. 463 - ``degree`` - an integer (default:``None``). 464 If ``kernel`` is ``None``, then this is the degree of the isogeny from ``E`` to ``codomain``. 465 If ``kernel`` is not ``None``, then this is used to determine whether or not to skip a gcd 466 of the kernel polynomial with the two torsion polynomial of ``E``. 467 - ``model`` - a string (default:``None``). Only supported variable is ``minimal``, in which case if 468 ``E`` is a curve over the rationals, then the codomain is set to be the unique global minimum model. 469 - ``check`` (default: ``True``) checks if the input is valid to define an isogeny 443 470 444 471 EXAMPLES: 445 472 … … 562 589 sage: phi_k.is_separable() 563 590 True 564 591 565 A more complicated example over the rationals ( with odd degree isogeny)::592 A more complicated example over the rationals (of odd degree):: 566 593 567 594 sage: E = EllipticCurve('11a1') 568 595 sage: P_list = E.torsion_points() … … 628 655 sage: phi_s.rational_maps() == phi.rational_maps() 629 656 True 630 657 658 However only cyclic normalized isogenies can be constructed this way. So it won't find the 659 isogeny [3]:: 660 661 sage: E.isogeny(None, codomain=E,degree=9) 662 Traceback (most recent call last): 663 ... 664 ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 9 665 666 Also the presumed isogeny between the domain and codomain must be normalized:: 667 668 sage: E2.isogeny(None,codomain=E,degree=5) 669 Traceback (most recent call last): 670 ... 671 ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 5 672 sage: phi.dual() 673 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 674 sage: phi.dual().is_normalized() 675 False 676 677 Here an example of a construction of a endomorphisms with cyclic kernel on a cm-curve:: 678 679 sage: K.<i> = NumberField(x^2+1) 680 sage: E = EllipticCurve(K, [1,0]) 681 sage: RK.<X> = K[] 682 sage: f = X^2 - 2/5*i + 1/5 683 sage: phi= E.isogeny(f) 684 sage: isom = phi.codomain().isomorphism_to(E) 685 sage: phi.set_post_isomorphism(isom) 686 sage: phi.codomain() == phi.domain() 687 True 688 sage: phi.rational_maps() 689 (((-4/25*i - 3/25)*x^5 + (-4/5*i + 2/5)*x^3 + x)/(x^4 + (-4/5*i + 2/5)*x^2 + (-4/25*i - 3/25)), 690 ((2/125*i - 11/125)*x^6*y + (64/125*i + 23/125)*x^4*y + (162/125*i - 141/125)*x^2*y + (-4/25*i - 3/25)*y)/(x^6 + (-6/5*i + 3/5)*x^4 + (-12/25*i - 9/25)*x^2 + (2/125*i - 11/125))) 631 691 632 692 """ 633 693 … … 635 695 # member variables 636 696 #################### 637 697 698 __check = None 638 699 # 639 700 # variables common to all algorithms 640 701 # … … 783 844 if (None == degree): 784 845 raise ValueError, "If specifying isogeny by domain and codomain, degree parameter must be set." 785 846 786 # save the domain/codomain: not really used now847 # save the domain/codomain: really used now (trac #7096) 787 848 old_domain = E 788 849 old_codomain = codomain 789 850 … … 808 869 self.set_pre_isomorphism(pre_isom) 809 870 810 871 if (None != post_isom): 811 self. set_post_isomorphism(post_isom)872 self.__set_post_isomorphism(old_codomain, post_isom) #(trac #7096) 812 873 813 874 # Inheritance house keeping 814 875 … … 923 984 sage: phi_p.__hash__() == phi_v.__hash__() 924 985 False 925 986 987 sage: E = EllipticCurve('49a3') 988 sage: R.<X> = QQ[] 989 sage: EllipticCurveIsogeny(E,X^3-13*X^2-58*X+503,check=False) 990 Isogeny of degree 7 from Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 107*x + 552 over Rational Field to Elliptic Curve defined by y^2 + x*y = x^3 - x^2 - 5252*x - 178837 over Rational Field 991 926 992 """ 927 993 928 994 if (None != self.__this_hash): … … 964 1030 sage: phi_p = EllipticCurveIsogeny(E_F17, [0,1]) 965 1031 sage: phi_p == phi_v 966 1032 False 1033 sage: E = EllipticCurve('11a1') 1034 sage: phi = E.isogeny(E(5,5)) 1035 sage: psi = E.isogeny(phi.kernel_polynomial()) 1036 sage: phi == psi 1037 True 1038 sage: phi.dual() == psi.dual() 1039 True 967 1040 968 1041 969 1042 """ … … 1466 1539 1467 1540 newE2 = codomain 1468 1541 post_isom = oldE2.isomorphism_to(newE2) 1469 1542 1470 1543 if (None != post_isom): 1471 1544 self.__set_post_isomorphism(newE2, post_isom) 1472 1545 … … 1850 1923 1851 1924 n = len(kernel_polynomial)-1 1852 1925 1926 if kernel_polynomial[-1] != 1: 1927 raise ValueError, "The kernel polynomial must be monic." 1928 1853 1929 self.__kernel_polynomial_list = kernel_polynomial 1854 1930 1855 1931 psi = 0 … … 2255 2331 # So in this case, we do some explicit casting to make sure 2256 2332 # everything comes out right 2257 2333 2258 if is_NumberField(self.__base_field) and \ 2259 (1 < self.__base_field.degree()) : 2334 if is_NumberField(self.__base_field) and (1 < self.__base_field.degree()) : 2260 2335 xP_out = self.__poly_ring(xP_out).constant_coefficient() 2261 2336 yP_out = self.__poly_ring(yP_out).constant_coefficient() 2262 2337 … … 2455 2530 r""" 2456 2531 This function returns a bool indicating whether or not this isogeny is separable. 2457 2532 2458 This function always returns true. This class only implements separable isogenies.2533 This function always returns ``True`` as currently this class only implements separable isogenies. 2459 2534 2460 2535 EXAMPLES:: 2461 2536 … … 2655 2730 def get_pre_isomorphism(self): 2656 2731 r""" 2657 2732 Returns the pre-isomorphism of this isogeny. 2658 If there has been no pre-isomorphism set, this returns None.2733 If there has been no pre-isomorphism set, this returns ``None``. 2659 2734 2660 2735 EXAMPLES:: 2661 2736 … … 2690 2765 def get_post_isomorphism(self): 2691 2766 r""" 2692 2767 Returns the post-isomorphism of this isogeny. 2693 If there has been no post-isomorphism set, this returns None.2768 If there has been no post-isomorphism set, this returns ``None``. 2694 2769 2695 2770 EXAMPLES:: 2696 2771 … … 2711 2786 sage: phi2 = EllipticCurveIsogeny(E, None, E2, 2) 2712 2787 sage: phi2.get_post_isomorphism() 2713 2788 Generic morphism: 2714 From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 832715 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 832716 Via: (u,r,s,t) = (82, 7, 41, 3)2789 From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 65*x + 69 over Finite Field of size 83 2790 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 2791 Via: (u,r,s,t) = (1, 7, 42, 80) 2717 2792 2718 2793 """ 2719 2794 return self.__post_isomorphism … … 2775 2850 """ 2776 2851 self.set_post_isomorphism(WeierstrassIsomorphism(self.__E2, (-1,0,-self.__E2.a1(),-self.__E2.a3()))) 2777 2852 2778 2779 def is_normalized(self, check_by_pullback=True): 2853 def is_normalized(self, via_formal=True, check_by_pullback=True): 2780 2854 r""" 2781 Returns true if this isogeny is normalized. 2782 2783 This code does two things. If the check_by_pullback flag is set, then the code checks that the coefficient on the pullback of the 2784 invariant differential is 1. However, in some cases (after a translation has been applied) the underlying polynomial algebra code 2785 can not sufficiently simplify the pullback expression. As such, we also cheat a little by falling back and seeing if the post isomorphism 2786 on this isogeny is a translation with no rescaling. 2855 Returns ``True`` if this isogeny is normalized. An isogeny `\varphi\colon E\to E_2` between two given Weierstrass 2856 equations is said to be normalized if the constant `c` is `1` in `\varphi*(\omega_2) = c\cdot\omega`, where `\omega` and `omega_2` 2857 are the invariant differentials on `E` and `E_2` corresponding to the given equation. 2787 2858 2788 2859 INPUT: 2789 2860 2790 - ``check_by_pullback`` - If this flag is true then the code 2791 checks the coefficient on the pullback of the invariant 2792 differential. (default:True) 2861 - ``via_formal`` - (default: ``True``) If ``True`` it simply checks if the leading 2862 term of the formal series is 1. Otherwise it uses a deprecated algorithm 2863 involving the second optional argument. 2864 2865 - ``check_by_pullback`` - (default:``True``) Deprecated. 2793 2866 2794 2867 EXAMPLES:: 2795 2868 … … 2851 2924 True 2852 2925 2853 2926 """ 2927 # easy algorithm using the formal expansion. 2928 if via_formal: 2929 phi_formal = self.formal(prec=5) 2930 return phi_formal[1] == 1 2854 2931 2932 # this is the old algorithm. it should be deprecated. 2855 2933 check_prepost_isomorphism = False 2856 2934 2857 2935 f_normalized = True … … 2880 2958 inv_diff_quo = domain_inv_diff/codomain_inv_diff 2881 2959 2882 2960 if (1 == inv_diff_quo): 2883 f_normalized 2961 f_normalized = True 2884 2962 else: 2885 2963 # For some reason, in certain cases, when the isogeny is pre or post composed with a translation 2886 2964 # the resulting rational functions are too complicated for sage to simplify down to a constant … … 2923 3001 2924 3002 return f_normalized 2925 3003 2926 2927 3004 def dual(self): 2928 3005 r""" 2929 Computes and returns the dual isogeny of this isogeny. 3006 Computes and returns the dual isogeny of this isogeny. If `\varphi\colon E \to E_2` is the given isogeny, 3007 then the dual is by definition the unique isogeny `\hat\varphi\colon E_2\to E` such that the compositions 3008 `\hat\varphi\circ\varphi` and `\varphi\circ\hat\varphi` are the multiplication `[n]` by the 3009 degree of `\varphi` on `E` and `E_2` respectively. 2930 3010 2931 3011 EXAMPLES:: 2932 3012 … … 2960 3040 sage: Xm = Xhat.subs(x=X, y=Y) 2961 3041 sage: Ym = Yhat.subs(x=X, y=Y) 2962 3042 sage: (Xm, Ym) == E.multiplication_by_m(7) 2963 False2964 sage: (Xm, -Ym) == E.multiplication_by_m(7)2965 3043 True 2966 3044 2967 3045 sage: E = EllipticCurve(GF(31), [0,0,0,1,8]) … … 2978 3056 sage: Xm = Xhat.subs(x=X, y=Y) 2979 3057 sage: Ym = Yhat.subs(x=X, y=Y) 2980 3058 sage: (Xm, Ym) == E.multiplication_by_m(5) 2981 False2982 sage: (Xm, -Ym) == E.multiplication_by_m(5)2983 3059 True 2984 3060 3061 Test (for trac ticket 7096):: 3062 3063 sage: E = EllipticCurve('11a1') 3064 sage: phi = E.isogeny(E(5,5)) 3065 sage: phi.dual().dual() == phi 3066 True 3067 3068 sage: k = GF(103) 3069 sage: E = EllipticCurve(k,[11,11]) 3070 sage: phi = E.isogeny(E(4,4)) 3071 sage: phi 3072 Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + 11*x + 11 over Finite Field of size 103 to Elliptic Curve defined by y^2 = x^3 + 25*x + 80 over Finite Field of size 103 3073 sage: from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism 3074 sage: phi.set_post_isomorphism(WeierstrassIsomorphism(phi.codomain(),(5,0,1,2))) 3075 sage: phi.dual().dual() == phi 3076 True 3077 3078 sage: E = EllipticCurve(GF(103),[1,0,0,1,-1]) 3079 sage: phi = E.isogeny(E(60,85)) 3080 sage: phi.dual() 3081 Isogeny of degree 7 from Elliptic Curve defined by y^2 + x*y = x^3 + 84*x + 34 over Finite Field of size 103 to Elliptic Curve defined by y^2 + x*y = x^3 + x + 102 over Finite Field of size 103 3082 2985 3083 """ 2986 3084 2987 3085 if (self.__base_field.characteristic() in [2,3]): … … 2989 3087 2990 3088 if (None != self.__dual): 2991 3089 return self.__dual 2992 2993 E1 = self.__intermediate_codomain2994 E2pr = self.__intermediate_domain3090 3091 # trac 7096 3092 (E1, E2pr, pre_isom, post_isom) = compute_intermediate_curves(self.codomain(), self.domain()) 2995 3093 2996 3094 F = self.__base_field 2997 3095 2998 3096 d = self.__degree 2999 3097 3000 isom = WeierstrassIsomorphism(E2pr, (1/F(d), 0, 0, 0)) 3098 # trac 7096 3099 if F(d) == 0: 3100 raise NotImplementedError, "The dual isogeny is not separable, but only separable isogenies are implemented so far" 3101 3102 # trac 7096 3103 # this should take care of the case when the isogeny is not normalized. 3104 u = self.formal(prec=5)[1] 3105 isom = WeierstrassIsomorphism(E2pr, (u/F(d), 0, 0, 0)) 3001 3106 3002 3107 E2 = isom.codomain().codomain() 3003 3108 … … 3008 3113 3009 3114 phi_hat.set_pre_isomorphism(pre_isom) 3010 3115 phi_hat.set_post_isomorphism(post_isom) 3116 phi_hat.__perform_inheritance_housekeeping() 3011 3117 3118 assert phi_hat.codomain() == self.domain() 3119 3120 # trac 7096 : this adjust a posteriori the automorphism 3121 # on the codomain of the dual isogeny. 3122 # we used _a_ Weierstrass isomorphism to get to the original 3123 # curve, but we may have to change it my an automorphism. 3124 # we impose that the composition has the degree 3125 # as a leading coefficient in the formal expansion. 3126 3127 phi_sc = self.formal(prec=5)[1] 3128 phihat_sc = phi_hat.formal(prec=5)[1] 3129 3130 sc = phi_sc * phihat_sc/F(d) 3131 3132 if sc != 1: 3133 auts = phi_hat.codomain().automorphsims() 3134 aut = [a for a in auts if a.u == sc] 3135 if len(aut) != 1: 3136 raise ValueError, "There is a bug in dual()." 3137 phi_hat.set_post_isomorphism(a[0]) 3138 3012 3139 self.__dual = phi_hat 3013 3140 3014 3141 return phi_hat 3015 3142 3143 def formal(self,prec=20): 3144 r""" 3145 Computes the formal isogeny as a power series in the variable `t=-x/y` 3146 on the domain curve. 3147 3148 INPUT: 3149 3150 - ``prec``: (default = 20), the precision with which the computations in the formal group are carried out. 3151 3152 EXAMPLES:: 3153 3154 sage: E = EllipticCurve(GF(13),[1,7]) 3155 sage: phi = E.isogeny(E(10,4)) 3156 sage: phi.formal() 3157 t + 12*t^13 + 2*t^17 + 8*t^19 + 2*t^21 + O(t^23) 3158 3159 sage: E = EllipticCurve([0,1]) 3160 sage: phi = E.isogeny(E(2,3)) 3161 sage: phi.formal(prec=10) 3162 t + 54*t^5 + 255*t^7 + 2430*t^9 + 19278*t^11 + O(t^13) 3163 3164 sage: E = EllipticCurve('11a2') 3165 sage: R.<x> = QQ[] 3166 sage: phi = E.isogeny(x^2 + 101*x + 12751/5) 3167 sage: phi.formal(prec=7) 3168 t - 2724/5*t^5 + 209046/5*t^7 - 4767/5*t^8 + 29200946/5*t^9 + O(t^10) 3169 3170 3171 """ 3172 Eh = self.__E1.formal() 3173 f, g = self.rational_maps() 3174 xh = Eh.x(prec=prec) 3175 yh = Eh.y(prec=prec) 3176 fh = f(xh,yh) 3177 gh = g(xh,yh) 3178 return -fh/gh 3016 3179 3017 3180 # 3018 3181 # Overload Morphism methods that we want to … … 3038 3201 """ 3039 3202 raise NotImplementedError 3040 3203 3041 3204 3042 3205 def is_injective(self): 3043 3206 r""" 3044 3207 Method inherited from the morphism class. 3045 Returns Trueif and only if this isogeny has trivial kernel.3208 Returns ``True`` if and only if this isogeny has trivial kernel. 3046 3209 3047 3210 EXAMPLES:: 3048 3211 … … 3073 3236 3074 3237 def is_surjective(self): 3075 3238 r""" 3076 For elliptic curve isogenies, always returns True(as a non-constant map of algebraic curves must be surjective).3239 For elliptic curve isogenies, always returns ``True`` (as a non-constant map of algebraic curves must be surjective). 3077 3240 3078 3241 EXAMPLES:: 3079 3242 … … 3163 3326 3164 3327 """ 3165 3328 raise NotImplementedError, "Numerical approximations do not make sense for Elliptic Curve Isogenies" 3329 3330 # no longer needed (trac 7096) 3331 # def starks_find_r_and_t(T, Z): 3166 3332 3167 3168 def truncated_reciprocal_quadratic(f, n): 3169 r""" 3170 Computes the truncated reciprocal of `f`, to precision `n`. 3171 This algorithm has complexity `O(dM(n))`, where `M(n)` is the cost of a multiplication and `d` is the degree of `f`. 3172 3173 INPUT: 3174 3175 - ``f`` - polynomial, to compute the truncated reciprocal of 3176 - ``n`` - integer, precision to compute reciprocal to 3177 3178 OUTPUT: 3179 3180 polynomial -- a polynomial `g`, such that `gf \equiv 1 \pmod {z^n}` 3181 3182 ALGORITHM: 3183 3184 This function uses the algorithm described in section 2.1 of [BMSS] 3185 3186 REFERENCES: 3187 3188 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 3189 3190 EXAMPLES:: 3191 3192 sage: f = 1 + x + 7*x^2 + 9*x^5 3193 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import truncated_reciprocal_quadratic 3194 sage: R.<x> = QQ[] 3195 sage: f = 1 + x + 7*x^2 + 9*x^5 3196 sage: truncated_reciprocal_quadratic(f, 5) 3197 29*x^4 + 13*x^3 - 6*x^2 - x + 1 3198 sage: (f*truncated_reciprocal_quadratic(f, 5)).quo_rem(x**5)[1] 3199 1 3200 3201 """ 3202 3203 R = f.parent() 3204 d = f.degree() 3205 3206 f_coef = f.coeffs() 3207 for j in xrange(len(f_coef), n): 3208 f_coef.append(0) 3209 3210 g_coef = [0 for j in xrange(n)] 3211 3212 g_coef[0] = g0 = 1/f.constant_coefficient() 3213 3214 for j in xrange(1,n): 3215 gj = 0 3216 for k in xrange(1,min(j+1,d+1)): 3217 gj += f_coef[k]*g_coef[j-k] 3218 gj = -g0*gj 3219 g_coef[j] = gj 3220 3221 g = R(g_coef) 3222 3223 return g 3224 3225 3226 def truncated_reciprocal_newton(f, n): 3227 r""" 3228 Computes the truncated reciprocal of `f`, to precision `n` by newton iteration. 3229 This algorithm has complexity `O(M(n))`, where `M(n)` is the cost of a multiplication. 3230 3231 INPUT: 3232 3233 - ``f`` - polynomial, to compute the truncated reciprocal of 3234 - ``n`` - integer, precision to compute reciprocal to 3235 3236 OUTPUT: 3237 3238 polynomial -- polynomial `g`, such that `gf \equiv 1 \pmod {z^n}` 3239 3240 ALGORITHM: 3241 3242 This function uses the algorithm described in section 2.1 of [BMSS] 3243 3244 REFERENCES: 3245 3246 - [BMSS] Boston, Morain, Salvy, SCHOST, "Fast Algorithms for Isogenies." 3247 3248 EXAMPLES:: 3249 3250 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import truncated_reciprocal_newton 3251 sage: R.<x> = GF(53)[] 3252 sage: f = 4 + 3*x^3 + 47*x^7 3253 sage: truncated_reciprocal_newton(f, 6) 3254 23*x^3 + 40 3255 sage: (f*truncated_reciprocal_newton(f, 6)).quo_rem(x**6)[1] 3256 1 3257 3258 """ 3259 3260 hj = 1/f.constant_coefficient() 3261 3262 if (f.is_constant()): 3263 return hj 3264 3265 z = f.variables()[0] 3266 3267 loop_condition = 2 3268 3269 while (loop_condition < 2*n): 3270 hj_next = hj*(2 - f*hj) 3271 (hj_quo, hj) = hj_next.quo_rem(z**loop_condition) 3272 loop_condition *= 2 3273 3274 (hj_quo, g) = hj.quo_rem(z**n) 3275 3276 return g 3277 3278 3279 def truncated_reciprocal(f, n, algorithm="newtoniteration"): 3280 r""" 3281 Computes the truncated reciprocal of `f`, to precision `n`. 3282 3283 INPUT: 3284 3285 - ``f`` - polynomial, to compute the truncated reciprocal of 3286 - ``n`` - integer, precision to compute reciprocal to 3287 - ``algorithm`` - string (default:"newtoniteration"), string that selects the algorithm to use. Choices are "newtoniteration" or "quadratic" 3288 3289 OUTPUT: 3290 3291 polynomial -- a polynomial `g`, such that `gf \equiv 1 \pmod {z^n}` 3292 3293 ALGORITHM: 3294 3295 "newtoniteration" algorithm has complexity `O(M(n))` and "quadratic" has algorithm complexity `O(dM(n))`. 3296 3297 EXAMPLES:: 3298 3299 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import truncated_reciprocal 3300 sage: R.<x> = GF(101)[] 3301 sage: f = 4 + x - x^2 + 50*x^3 + 91*x^5 3302 sage: truncated_reciprocal(f, 5) 3303 37*x^4 + 43*x^3 + 49*x^2 + 82*x + 76 3304 sage: (f*truncated_reciprocal(f, 5)).quo_rem(x**5)[1] 3305 1 3306 3307 """ 3308 3309 if ("quadratic"==algorithm): 3310 trunc_recip = truncated_reciprocal_quadratic(f, n) 3311 elif ("newtoniteration"==algorithm): 3312 trunc_recip = truncated_reciprocal_newton(f, n) 3313 else: 3314 raise ValueError, "Unknown algorithm for computing reciprocal" 3315 3316 return trunc_recip 3317 3318 3319 def truncated_log(f, n): 3320 r""" 3321 Computes the truncated logarithm of polynomial `f` to precision `n`. 3322 The complexity of this function is `O(M(n))`. 3323 3324 INPUT: 3325 3326 - ``f`` - polynomial, to compute the truncated logarithm of; `f(0)` must not equal 0. 3327 - ``n`` - integer, precision to compute logarithm to 3328 3329 OUTPUT: 3330 3331 polynomial -- a polynomial `g`, such that `\exp_n (g) \equiv f \pmod {z^n}` 3332 3333 ALGORITHM: 3334 3335 Uses the algorithm from section 2.2 of [BMSS]. 3336 3337 REFERENCES: 3338 3339 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 3340 3341 EXAMPLES:: 3342 3343 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import truncated_log, truncated_exp 3344 sage: R.<x> = GF(31)[] 3345 sage: f = 1 + x + 2*x^2 + 3*x^3 3346 sage: truncated_log(f, 4) 3347 3*x^3 + 2*x^2 + x 3348 sage: g = truncated_log(f, 4) 3349 sage: truncated_exp(g, 4) 3350 3*x^3 + 2*x^2 + x + 1 3351 3352 """ 3353 3354 R = f.parent() 3355 K = R.base_ring() 3356 3357 z = R.gen() 3358 3359 z_mod = z**n 3360 3361 g = 1 - f 3362 cur_pow = g 3363 3364 sum_acc = 0 3365 3366 for j in xrange(1,n): 3367 sum_acc -= K(1/j)*cur_pow 3368 cur_pow *= g 3369 cur_pow = cur_pow.quo_rem(z_mod)[1] 3370 3371 return sum_acc 3372 3373 3374 def truncated_exp_quadratic(f, n): 3375 r""" 3376 Computes the truncated exponential of `f`, to precision `n`, using a straight forward power series approximation. 3377 3378 This algorithm has complexity `O(nM(n))`, where `M(n)` is the 3379 cost of a multiplication. 3380 3381 INPUT: 3382 3383 - ``f`` - polynomial, to compute the truncated exponential of 3384 - ``n`` - integer, precision to compute reciprocal to 3385 3386 OUTPUT: 3387 3388 polynomial -- a polynomial `g`, such that `\log_n(g) \equiv f \pmod {z^n}`. 3389 3390 ALGORITHM: 3391 3392 This function uses the algorithm described in section 2.2 of [BMSS] 3393 3394 REFERENCES: 3395 3396 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 3397 3398 EXAMPLES:: 3399 3400 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import truncated_exp_quadratic 3401 sage: R.<x> = GF(127)[] 3402 sage: f = 1 + x^10 3403 sage: truncated_exp_quadratic(f, 10) 3404 31 3405 sage: truncated_exp_quadratic(f, 11) 3406 31*x^10 + 123 3407 3408 """ 3409 3410 R = f.parent() 3411 K = R.base_ring() 3412 3413 z = R.gen() 3414 3415 z_mod = z**n 3416 3417 g = R(1) 3418 cur_coef = K(1) 3419 3420 cur_pow = R(1) 3421 3422 for j in xrange(1,n): 3423 cur_coef = cur_coef/K(j) 3424 cur_pow = cur_pow * f 3425 cur_pow = cur_pow.quo_rem(z_mod)[1] 3426 g += cur_coef*cur_pow 3427 3428 return g 3429 3430 3431 def truncated_exp_fast(f, n): 3432 r""" 3433 Computes the truncated exponential of `f`, to precision `n`, using an efficient newton iteration. 3434 This algorithm has complexity `O(M(n))`, where `M(n)` is the cost of a multiplication. 3435 3436 INPUT: 3437 3438 - ``f`` - polynomial, to compute the truncated exponential of 3439 - ``n`` - integer, precision to compute reciprocal to 3440 3441 OUTPUT: 3442 3443 polynomial -- a polynomial `g`, such that `\log_n(g) \equiv f \pmod {z^n}`. 3444 3445 ALGORITHM: 3446 3447 This function uses the algorithm described in section 2.2 of [BMSS] 3448 3449 REFERENCES: 3450 3451 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 3452 3453 EXAMPLES:: 3454 3455 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import truncated_exp_fast 3456 3457 sage: R.<x> = GF(27, 'a')[] 3458 sage: f = 1 + x^2 + 2*x^3 + x^4 3459 sage: truncated_exp_fast(f, 4) 3460 x^3 + 2 3461 sage: truncated_exp_fast(f, 3) 3462 2*x^2 + 2 3463 3464 """ 3465 3466 R = f.parent() 3467 K = R.base_ring() 3468 3469 z = R.gen() 3470 3471 gi = R(1) 3472 3473 j = 0 3474 m = 0 3475 3476 while (m < n): 3477 3478 m = 2**j + 1 3479 3480 if (n < m): 3481 m = n 3482 3483 g_nexti = gi*(1 + f - truncated_log(gi, m)) 3484 gi = g_nexti.quo_rem(z**m)[1] 3485 3486 j += 1 3487 3488 return gi 3489 3490 3491 def truncated_exp(f, n, algorithm="fast"): 3492 r""" 3493 Computes the truncated exponential of `f`, to precision `n`, using an efficient newton iteration. 3494 This algorithm has complexity `O(M(n))`, where `M(n)` is the cost of a multiplication. 3495 3496 INPUT: 3497 3498 - ``f`` - polynomial, to compute the truncated exponential of 3499 - ``n`` - integer, precision to compute reciprocal to 3500 - ``algorithm`` - string (default:"fast") indicates which algorithm to use, choices are "fast" or "quadratic" 3501 3502 OUTPUT: 3503 3504 polynomial -- a polynomial `g`, such that `\log_n(g) \equiv f \pmod {z^n}`. 3505 3506 ALGORITHM: 3507 3508 algorithm "fast" uses newton iteration and has complexity O(M(n)), algorithm "quadratic" has complexity O(n*M(n)) 3509 3510 EXAMPLES:: 3511 3512 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import truncated_exp 3513 3514 sage: R.<x> = GF(29)[] 3515 sage: f = 1+x+3*x^3 3516 sage: truncated_exp(f, 3) 3517 x + 2 3518 3519 """ 3520 3521 if ("quadratic"==algorithm): 3522 trunc_exp = truncated_exp_quadratic(f, n) 3523 elif ("fast"==algorithm): 3524 trunc_exp = truncated_exp_fast(f, n) 3525 else: 3526 raise ValueError, "Unknown algorithm for computing truncated exponential." 3527 3528 return trunc_exp 3529 3530 3531 def solve_linear_differential_system(a, b, c, alpha, z, n): 3532 r""" 3533 Solves a system of linear differential equations: 3534 `af' + bf = c`, `f'(0) = \alpha` 3535 where `a`, `b`, `c`, `f`, `f'` are polynomials in variable `z`, and `f`, `f'` are computed to precision `n`. 3536 3537 EXAMPLES: 3538 3539 The following examples inherently exercises this function:: 3540 3541 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_kernel_polynomial, solve_linear_differential_system 3542 3543 sage: R.<x> = GF(47)[] 3544 sage: E = EllipticCurve(GF(47), [0,0,0,1,0]) 3545 sage: E2 = EllipticCurve(GF(47), [0,0,0,16,0]) 3546 sage: compute_isogeny_kernel_polynomial(E, E2, 4, algorithm="starks") 3547 z^3 + z 3548 3549 """ 3550 3551 z_mod = z**(n-1) 3552 3553 a_recip = truncated_reciprocal(a, n-1) 3554 3555 B = (b*a_recip).quo_rem(z_mod)[1] 3556 3557 C = (c*a_recip).quo_rem(z_mod)[1] 3558 3559 int_B = B.integral() 3560 3561 J = truncated_exp(int_B, n) 3562 3563 J_recip = truncated_reciprocal(J, n) 3564 3565 CJ = (C*J).quo_rem(z_mod)[1] 3566 3567 int_CJ = CJ.integral() 3568 3569 f = (J_recip*(alpha + int_CJ)).quo_rem(z*z_mod)[1] 3570 3571 return f 3572 3573 3574 def compute_pe_quadratic(R, A, B, ell): 3575 r""" 3576 Computes the truncated Weierstrass function of an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. 3577 Uses an algorithm that is of complexity `O(\ell^2)`. 3578 3579 Let `p` be the characteristic of the underlying field: Then we must have either `p=0`, or `p > 2\ell + 3`. 3580 3581 INPUT: 3582 3583 - ``poly_ring`` - polynomial ring, to compute the `\wp` function in (assumes that the generator is `z^2` for efficiency of storage/operations.) 3584 - ``A`` - field element corresponding to the `x` coefficient in the Weierstrass equation of an elliptic curve 3585 - ``B`` - field element corresponding to the constant coefficient in the Weierstrass equation of an elliptic curve 3586 - ``ell`` - degree of `z` to compute the truncated function to. If `p` is the characteristic of the underlying field. If `p > 0` then we must have `2\ell + 3 < p`. 3587 3588 OUTPUT: 3589 3590 polynomial -- the element in ``poly_ring`` that corresponds to the truncated function to precision `2\ell`. 3591 3592 ALGORITHM: 3593 3594 This function uses the algorithm described in section 3.2 of [BMSS]. 3595 3596 REFERENCES: 3597 3598 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 3599 3600 EXAMPLES:: 3601 3602 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_pe_quadratic 3603 3604 sage: R.<x> = GF(37)[] 3605 sage: compute_pe_quadratic(R, GF(37)(1), GF(37)(8), 7) 3606 (7*x^7 + 9*x^5 + x^4 + 20*x^3 + 22*x^2 + 1)/x 3607 3608 sage: R.<x> = QQ[] 3609 sage: compute_pe_quadratic(R, 1, 8, 7) 3610 (-16/5775*x^7 + 23902/238875*x^6 + 24/385*x^5 + 1/75*x^4 - 8/7*x^3 - 1/5*x^2 + 1)/x 3611 3612 """ 3613 3614 c = [0 for j in xrange(ell)] 3615 c[0] = -A/5 3616 c[1] = -B/7 3617 3618 Z = R.gen() 3619 pe = Z**-1 + c[0]*Z + c[1]*Z**2 3620 3621 for k in xrange(3, ell): 3622 t = 0 3623 for j in xrange(1,k-1): 3624 t += c[j-1]*c[k-2-j] 3625 ck = (3*t)/((k-2)*(2*k+3)) 3626 pe += ck*Z**k 3627 c[k-1] = ck 3628 3629 return pe 3630 3631 3632 def compute_pe_fast(poly_ring, A, B, ell): 3633 r""" 3634 Computes the truncated Weierstrass function of an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. 3635 It does this with time complexity `O(M(n))`. 3636 3637 Let `p` be the characteristic of the underlying field: Then we must have either `p=0`, or `p > 2\ell + 3`. 3638 3639 INPUT: 3640 3641 - ``poly_ring`` - polynomial ring, to compute the function in (assumes that the generator is `z^2` for efficiency of storage/operations.) 3642 - ``A`` - field element corresponding to the `x` coefficient in the Weierstrass equation of an elliptic curve 3643 - ``B`` - field element corresponding to the constant coefficient in the Weierstrass equation of an elliptic curve 3644 - ``ell`` - degree of `z` to compute the truncated function to. If `p` is the characteristic of the underlying field and `p > 0`, then we must have `2\ell + 3 < p`. 3645 3646 OUTPUT: 3647 3648 polynomial -- the element in ``poly_ring`` that corresponds to the truncated `\wp` function to precision `2\ell`. 3649 3650 ALGORITHM: 3651 3652 This function uses the algorithm described in section 3.3 of 3653 [BMSS]. 3654 3655 .. note:: 3656 3657 Occasionally this function will fail to give the right answer, 3658 it faithfully implements the above published algorithm, so 3659 compute_pe_quadratic should be used as a fallback. 3660 3661 REFERENCES: 3662 3663 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 3664 3665 EXAMPLES:: 3666 3667 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_pe_fast 3668 sage: R.<x> = QQ[] 3669 sage: compute_pe_fast(R, 1, 8, 7) 3670 (-16/5775*x^7 + 23902/238875*x^6 + 24/385*x^5 + 1/75*x^4 - 8/7*x^3 - 1/5*x^2 + 1)/x 3671 3672 sage: R.<x> = GF(37)[] 3673 sage: compute_pe_fast(R, GF(37)(1), GF(37)(8), 5) 3674 (9*x^5 + x^4 + 20*x^3 + 22*x^2 + 1)/x 3675 3676 """ 3677 3678 z = poly_ring.gen() 3679 3680 f1 = z 3681 3682 s = 2 3683 3684 # solve the nonlinear differential equation 3685 3686 n = 2*ell + 4 3687 3688 while (s < n): 3689 f1pr = f1.derivative() 3690 3691 next_s = 2*s - 1 3692 3693 if ( n < next_s): 3694 next_s = n 3695 3696 a = 2*f1pr 3697 b = -(6*B*(f1**5) + 4*A*(f1**3)) 3698 c = B*(f1**6) + A*f1**4 + 1 - (f1pr**2) 3699 3700 z_mod = z**(next_s) 3701 3702 a = a.quo_rem(z_mod)[1] 3703 b = b.quo_rem(z_mod)[1] 3704 c = c.quo_rem(z_mod)[1] 3705 3706 f2 = solve_linear_differential_system(a, b, c, 0, z, next_s) 3707 3708 f1 = f1 + f2 3709 3710 s = next_s 3711 3712 R = f1 3713 3714 3715 Q = (R**2).quo_rem(z**(2*ell+5))[1] 3716 3717 pe_denom = Q.quo_rem(z**2)[0] 3718 3719 pe_numer1 = truncated_reciprocal(pe_denom, 2*ell+1) 3720 3721 pe_numer1_list = pe_numer1.list() 3722 3723 pe_numer2 = 0 3724 3725 # now we go through and make this a polynomial in the even powers 3726 for j in xrange(ell+1): 3727 pe_numer2 = pe_numer2*z + pe_numer1_list[2*(ell - j)] 3728 3729 pe = pe_numer2/(z) 3730 3731 return pe 3732 3733 3734 def compute_pe(R, E, ell, algorithm=None): 3735 r""" 3736 Computes the truncated Weierstrass function on an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. 3737 Uses the algorithm specified by the algorithm parameter. 3738 3739 INPUT: 3740 3741 - ``R`` - polynomial ring, the ring to compute the truncated `\wp` function in (treats the generator as a power of 2 for ease of storage/use.) 3742 - ``E`` - Elliptic Curve, must be in short Weierstrass form `0 = a_1 = a_2 = a_3` 3743 - ``ell`` - precision to compute the truncated function to 3744 - ``algorithm`` - string (default:None) an algorithm identifier indicating using the "fast" or "quadratic" algorithm. If the algorithm is None, then this function determines the best algorithm to use. 3745 3746 OUTPUT: 3747 3748 polynomial - a polynomial corresponding to the truncated Weierstrass `\wp` function in ``R``. 3749 3750 EXAMPLES:: 3751 3752 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_pe 3753 sage: E = EllipticCurve(GF(37), [0,0,0,1,8]) 3754 sage: R.<x> = GF(37)[] 3755 sage: f = (x + 10) * (x + 12) * (x + 16) 3756 sage: phi = EllipticCurveIsogeny(E, f) 3757 sage: E2 = phi.codomain() 3758 sage: pe = compute_pe(R, E, 7, algorithm="quadratic") 3759 sage: pe = pe(x^2) 3760 sage: pe 3761 (7*x^14 + 9*x^10 + x^8 + 20*x^6 + 22*x^4 + 1)/x^2 3762 3763 """ 3764 Ea_inv = E.a_invariants() 3765 if ( (0,0,0) != (Ea_inv[0], Ea_inv[1], Ea_inv[2]) ): 3766 raise ValueError, "elliptic curve parameter must be in short Weierstrass form" 3767 3768 p = R.base_ring().characteristic() 3769 3770 # if the algorithm is not set, try to determine algorithm from input 3771 if (None == algorithm): 3772 if (0 == p) or (p < 2*ell + 5): 3773 algorithm = "fast" 3774 elif (p < 2*ell + 3): 3775 algorithm = "quadratic" 3776 else: 3777 raise NotImplementedError, "algorithms for computing pe function for that characteristic / precision pair not implemented." 3778 3779 A = Ea_inv[3] 3780 B = Ea_inv[4] 3781 3782 if ("quadratic"==algorithm): 3783 3784 if (0 < p) and (p < 2*ell + 3): 3785 raise ValueError, "For computing pe via quadratic algorithm, characteristic of underlying field must be greater than or equal to 2*ell + 3" 3786 3787 pe = compute_pe_quadratic(R, A, B, ell) 3788 3789 elif ("fast"==algorithm): 3790 3791 if (0 < p) and (p < 2*ell + 4): 3792 raise ValueError, "For computing pe via the fast algorithm, characteristic of underlying field must be greater than or equal to 2*ell + 4" 3793 3794 pe = compute_pe_fast(R, A, B, ell) 3795 3796 else: 3797 raise ValueError, "unknown algorithm for computing pe." 3798 3799 return pe 3800 3801 3802 def starks_find_r_and_t(T, Z): 3803 r""" 3804 Helper function for starks algorithm. 3805 3806 INPUT: 3807 3808 - ``T`` - Rational function in ``Z``. 3809 - ``Z`` - Variable of the rational function ``T``. 3810 3811 OUTPUT: 3812 3813 tuple -- `(r, t)` where `r` is the largest exponent such that the 3814 coefficient `t` of `1/Z^r` is nonzero, where `T` is regarded as 3815 a polynomial in `1/Z`. 3816 3817 EXAMPLES:: 3818 3819 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import starks_find_r_and_t 3820 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_pe 3821 sage: E = EllipticCurve(GF(37), [0,0,0,1,8]) 3822 sage: R.<x> = GF(37)[] 3823 sage: f = (x + 10) * (x + 12) * (x + 16) 3824 sage: phi = EllipticCurveIsogeny(E, f) 3825 sage: E2 = phi.codomain() 3826 sage: pe = compute_pe(R, E, 7, algorithm="quadratic") 3827 sage: pe = pe(x^2) 3828 sage: starks_find_r_and_t(pe, x) 3829 (2, 1) 3830 3831 """ 3832 Tdenom = T.denominator() 3833 Tdenom_lc = Tdenom.leading_coefficient() 3834 Tnumer = (1/Tdenom_lc)*T.numerator() 3835 3836 r = Tdenom.degree() 3837 3838 t_r = Tnumer.constant_coefficient() 3839 3840 if (0 == r) and (t_r == 0): 3841 Tnumer_quo = Tnumer 3842 Tnumer_rem = 0 3843 while (0 == Tnumer_rem): 3844 (Tnumer_quo, Tnumer_rem) = Tnumer_quo.quo_rem(Z) 3845 r -= 1 3846 r += 1 3847 t_r = Tnumer_rem 3848 3849 return (r, t_r) 3850 3851 3852 def compute_isogeny_starks(E1, E2, ell, pe_algorithm="fast"): 3333 def compute_isogeny_starks(E1, E2, ell): 3853 3334 r""" 3854 3335 Computes the degree ``ell`` isogeny between ``E1`` and ``E2`` via 3855 3336 Stark's algorithm. There must be a degree ``ell``, separable, 3856 normalized isogeny from ``E1`` to ``E2``.3337 normalized cyclic isogeny from ``E1`` to ``E2``. 3857 3338 3858 3339 INPUT: 3859 3340 … … 3878 3359 3879 3360 REFERENCES: 3880 3361 3881 - [BMSS] Boston, Morain, Salvy, S CHOST, "Fast Algorithms for Isogenies."3362 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 3882 3363 - [M09] Moody, "The Diffie-Hellman Problem and Generalization of Verheul's Theorem" 3883 3364 - [S72] Stark, "Class-numbers of complex quadratic fields." 3884 3365 … … 3891 3372 sage: phi = EllipticCurveIsogeny(E, f) 3892 3373 sage: E2 = phi.codomain() 3893 3374 sage: (isom1, isom2, E1pr, E2pr, ker_poly) = compute_sequence_of_maps(E, E2, 11) 3894 sage: compute_isogeny_starks(E1pr, E2pr, 11 , pe_algorithm="quadratic")3895 z^10 + 37*z^9 + 53*z^8 + 66*z^7 + 66*z^6 + 17*z^5 + 57*z^4 + 6*z^3 + 89*z^2 + 53*z+ 83375 sage: compute_isogeny_starks(E1pr, E2pr, 11) 3376 x^10 + 37*x^9 + 53*x^8 + 66*x^7 + 66*x^6 + 17*x^5 + 57*x^4 + 6*x^3 + 89*x^2 + 53*x + 8 3896 3377 3897 3378 sage: E = EllipticCurve(GF(37), [0,0,0,1,8]) 3898 3379 sage: R.<x> = GF(37)[] … … 3900 3381 sage: phi = EllipticCurveIsogeny(E, f) 3901 3382 sage: E2 = phi.codomain() 3902 3383 sage: compute_isogeny_starks(E, E2, 5) 3903 z^4 + 14*z^3 + z^2 + 34*z+ 213384 x^4 + 14*x^3 + x^2 + 34*x + 21 3904 3385 sage: f**2 3905 3386 x^4 + 14*x^3 + x^2 + 34*x + 21 3906 3387 … … 3909 3390 sage: f = x 3910 3391 sage: phi = EllipticCurveIsogeny(E, f) 3911 3392 sage: E2 = phi.codomain() 3912 sage: compute_isogeny_starks(E, E2, 2 , pe_algorithm="fast")3913 z3393 sage: compute_isogeny_starks(E, E2, 2) 3394 x 3914 3395 3915 3396 """ 3916 3397 3917 3398 K = E1.base_field() 3918 3919 R = PolynomialRing(K, 'z') 3920 z = R.gen() 3921 3922 S = PolynomialRing(K, 'Z') 3923 Z = S.gen() 3399 R = PolynomialRing(K, 'x') 3400 x = R.gen() 3401 3402 wp1 = E1.weierstrass_p(prec=4*ell+4) #BMSS claim 2*ell is enough, but it is not M09 3403 wp2 = E2.weierstrass_p(prec=4*ell+4) 3924 3404 3925 pe1 = compute_pe(S, E1, 2*ell, pe_algorithm) 3926 pe2 = compute_pe(S, E2, 2*ell, pe_algorithm) 3927 3405 # viewed them as power series in Z = z^2 3406 S = LaurentSeriesRing(K, 'Z') 3407 Z = S.gen() 3408 pe1 = 1/Z 3409 pe2 = 1/Z 3410 for i in xrange(2*ell+1): 3411 pe1 += wp1[2*i] * Z**i 3412 pe2 += wp2[2*i] * Z**i 3413 pe1 = pe1.add_bigoh(2*ell+2) 3414 pe2 = pe2.add_bigoh(2*ell+2) 3415 3416 #print 'wps = ',pe1 3417 #print 'wps2 = ',pe2 3418 3928 3419 n = 1 3929 3420 q = [R(1), R(0)] 3930 3421 #p = [R(0), R(1)] 3931 3422 T = pe2 3932 3423 3933 3424 while ( q[n].degree() < (ell-1) ): 3934 # print 'n=', n 3935 # print 'T = ', T 3425 #print 'n=', n 3936 3426 3937 n = n +13427 n += 1 3938 3428 a_n = 0 3939 3940 (r, t_r) = starks_find_r_and_t(T, Z)3941 3942 while ( 0 <= r ):3943 # print 'r=',r3944 # print 't_r=',t_r 3945 a_n = a_n + t_r *z**r3429 r = -T.valuation() 3430 while ( 0 <= r and T != 0): 3431 t_r = T[-r] 3432 #print ' r=',r 3433 #print ' t_r=',t_r 3434 #print ' T=',T 3435 a_n = a_n + t_r * x**r 3946 3436 T = T - t_r*pe1**r 3947 (r, t_r) = starks_find_r_and_t(T, Z) 3437 r = -T.valuation() 3438 3948 3439 3949 3440 q_n = a_n*q[n-1] + q[n-2] 3950 3441 q.append(q_n) 3442 #p_n = a_n*p[n-1] + q[n-2] 3443 #p.append(p_n) 3951 3444 3952 if (n == ell+1): 3445 if (n == ell+1 or T==0): 3446 if T.valuation()<2: 3447 raise ValueError, "The two curves are not linked by a cyclic normalized isogeny of degree %s"%ell 3448 #print 'breaks here' 3953 3449 break 3954 3450 3955 (r, t_r) = starks_find_r_and_t(T, Z) 3956 3957 Tdenom_lc = T.denominator().leading_coefficient() 3958 Tnumer = (1/Tdenom_lc)*T.numerator() 3959 3960 # print 'Tnumer before divide out=', Tnumer 3961 3962 Tdenom_next = Z**(-r) 3963 3964 # print 'r=', r 3965 3966 # print 'Tdenom_next = ', Tdenom_next 3967 3968 # compute the highest power of z**2 that divides the numerator 3969 (Tnumer, Tnumer_rem) = Tnumer.quo_rem(Tdenom_next); 3970 3971 # if (0 != Tnumer_rem): 3972 # print 'ERROR expected remainder =0 was not 0.' 3973 3974 # print 'Tnumer after divide out = ', Tnumer 3975 Tnumer_next = truncated_reciprocal(Tnumer, 2*ell) 3976 # print "recip check", Tnumer*Tnumer_next 3977 3978 # print 'Tnumer_next =', Tnumer_next 3979 # print 'Tdenom_next =', Tdenom_next 3980 3981 T = Tnumer_next/Tdenom_next 3982 3983 # print 'q_n=', q_n 3984 # print 'a_n=', a_n 3985 3986 # print q 3451 T = 1/T 3452 #print ' a_n=', a_n 3453 #print ' q_n=', q_n 3454 #print ' p_n=', p_n 3455 #print ' T = ', T 3987 3456 3988 3457 qn = q[n] 3458 #pn= p[n] 3459 #print 'final T = ', T 3460 #print ' f =', pn/qn 3461 3989 3462 qn = (1/qn.leading_coefficient())*qn 3463 #pn = (1/qn.leading_coefficient())*pn 3990 3464 3991 3465 return qn 3992 3466 3993 3994 3467 def split_kernel_polynomial(E1, ker_poly, ell): 3995 3468 r""" 3996 3469 Internal helper function for ``compute_isogeny_kernel_polynomial``. … … 4011 3484 sage: E2 = phi.codomain() 4012 3485 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_starks 4013 3486 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import split_kernel_polynomial 4014 sage: ker_poly = compute_isogeny_starks(E, E2, 7 , pe_algorithm="quadratic"); ker_poly4015 z^6 + 2*z^5 + 20*z^4 + 11*z^3 + 36*z^2 + 35*z+ 163487 sage: ker_poly = compute_isogeny_starks(E, E2, 7); ker_poly 3488 x^6 + 2*x^5 + 20*x^4 + 11*x^3 + 36*x^2 + 35*x + 16 4016 3489 sage: split_kernel_polynomial(E, ker_poly, 7) 4017 z^3 + z^2 + 28*z+ 333490 x^3 + x^2 + 28*x + 33 4018 3491 4019 3492 """ 4020 3493 … … 4033 3506 4034 3507 def compute_isogeny_kernel_polynomial(E1, E2, ell, algorithm="starks"): 4035 3508 r""" 4036 Computes the degree ``ell`` isogeny between ``E1`` and ``E2``. 4037 4038 There must be a degree ``ell``, separable, normalized isogeny from 4039 ``E1`` to ``E2``. If no algorithm is specified, this function 4040 determines the best algorithm to use. 3509 Computes the kernel polynomial of the degree ``ell`` isogeny between ``E1`` and ``E2``. 3510 There must be a degree ``ell``, cyclic, separable, normalized isogeny from 3511 ``E1`` to ``E2``. 4041 3512 4042 3513 INPUT: 4043 3514 … … 4047 3518 4048 3519 - ``ell`` - the degree of the isogeny from ``E1`` to ``E2``. 4049 3520 4050 - ``algorithm`` - string (default:"starks") if None, this function automatically determines best algorithm to use. 4051 Otherwise uses the algorithm specified by the string. Valid values are "starks" or "fastElkies" 3521 - ``algorithm`` - currently only ``starks`` (default) is implemented. 4052 3522 4053 3523 OUTPUT: 4054 3524 4055 3525 polynomial -- over the field of definition of ``E1``, ``E2``, that is the kernel polynomial of the isogeny from ``E1`` to ``E2``. 4056 3526 4057 .. note::4058 4059 When using Stark's algorithm, occasionally the fast pe4060 computation fails, so we retry with the quadratic algorithm, which works in all cases (for valid inputs.)4061 4062 3527 EXAMPLES:: 4063 3528 4064 3529 sage: from sage.schemes.elliptic_curves.ell_curve_isogeny import compute_isogeny_kernel_polynomial … … 4069 3534 sage: phi = EllipticCurveIsogeny(E, f) 4070 3535 sage: E2 = phi.codomain() 4071 3536 sage: compute_isogeny_kernel_polynomial(E, E2, 5) 4072 z^2 + 7*z+ 133537 x^2 + 7*x + 13 4073 3538 sage: f 4074 3539 x^2 + 7*x + 13 4075 3540 … … 4078 3543 sage: E = EllipticCurve(K, [0,0,0,1,0]) 4079 3544 sage: E2 = EllipticCurve(K, [0,0,0,16,0]) 4080 3545 sage: compute_isogeny_kernel_polynomial(E, E2, 4) 4081 z^3 + z3546 x^3 + x 4082 3547 4083 3548 """ 4084 3549 4085 if ("starks"==algorithm): 4086 ker_poly = compute_isogeny_starks(E1, E2, ell) 4087 elif ("fastElkies"==algorithm): 4088 raise NotImplementedError 4089 else: 4090 raise ValueError, "algorithm parameter was for an unknown algorithm." 4091 4092 # 4093 # Everything that follows here is how we get the kernel polynomial in the form we want 4094 # i.e. a separable polynomial (no repeated roots.) 4095 # 3550 ker_poly = compute_isogeny_starks(E1, E2, ell) 4096 3551 ker_poly = split_kernel_polynomial(E1, ker_poly, ell) 4097 3552 4098 # in case we catastrophically failed using the fast pe algorithm fall back to quadratic algorithm4099 # this is a bug and should be fixed, but this is a work around for now4100 if (ker_poly.degree() < ell/2) and ("starks"==algorithm):4101 ker_poly = compute_isogeny_starks(E1, E2, ell, pe_algorithm="quadratic")4102 ker_poly = split_kernel_polynomial(E1, ker_poly, ell)4103 4104 3553 return ker_poly 4105 3554 4106 3555 … … 4223 3672 Via: (u,r,s,t) = (1, -1/3, 0, 1/2), 4224 3673 Elliptic Curve defined by y^2 = x^3 - 31/3*x - 2501/108 over Rational Field, 4225 3674 Elliptic Curve defined by y^2 = x^3 - 23461/3*x - 28748141/108 over Rational Field, 4226 z^2 - 61/3*z+ 658/9)3675 x^2 - 61/3*x + 658/9) 4227 3676 4228 3677 sage: K.<i> = NumberField(x^2 + 1) 4229 3678 sage: E = EllipticCurve(K, [0,0,0,1,0]) … … 4239 3688 Via: (u,r,s,t) = (1, 0, 0, 0), 4240 3689 Elliptic Curve defined by y^2 = x^3 + x over Number Field in i with defining polynomial x^2 + 1, 4241 3690 Elliptic Curve defined by y^2 = x^3 + 16*x over Number Field in i with defining polynomial x^2 + 1, 4242 z^3 + z)3691 x^3 + x) 4243 3692 4244 3693 sage: E = EllipticCurve(GF(97), [1,0,1,1,0]) 4245 3694 sage: R.<x> = GF(97)[]; f = x^5 + 27*x^4 + 61*x^3 + 58*x^2 + 28*x + 21 … … 4256 3705 Via: (u,r,s,t) = (1, 89, 49, 53), 4257 3706 Elliptic Curve defined by y^2 = x^3 + 52*x + 31 over Finite Field of size 97, 4258 3707 Elliptic Curve defined by y^2 = x^3 + 41*x + 66 over Finite Field of size 97, 4259 z^5 + 67*z^4 + 13*z^3 + 35*z^2 + 77*z+ 69)3708 x^5 + 67*x^4 + 13*x^3 + 35*x^2 + 77*x + 69) 4260 3709 4261 3710 """ 4262 3711 -
sage/schemes/elliptic_curves/ell_field.py
diff -r be27090d4a20 -r c4ee267a4cf7 sage/schemes/elliptic_curves/ell_field.py
a b 19 19 from constructor import EllipticCurve 20 20 21 21 from ell_curve_isogeny import EllipticCurveIsogeny, isogeny_codomain_from_kernel 22 from ell_wp import weierstrass_p 22 23 23 24 class EllipticCurve_field(ell_generic.EllipticCurve_generic): 24 25 … … 630 631 631 632 """ 632 633 return isogeny_codomain_from_kernel(self, kernel, degree=None) 634 635 def weierstrass_p(self, prec=20, algorithm=None): 636 r""" 637 Computes the Weierstrass `\wp`-function of the elliptic curve. 638 639 INPUT: 640 641 - ``mprec`` - precision 642 - ``algorithm`` - string (default:``None``) an algorithm identifier indicating using the ``pari``, ``fast`` or ``quadratic`` algorithm. If the algorithm is ``None``, then this function determines the best algorithm to use. 643 644 OUTPUT: 645 646 a Laurent series in one variable `z` with coefficients in the base field `k` of `E`. 647 648 EXAMPLES:: 649 650 sage: E = EllipticCurve('11a1') 651 sage: E.weierstrass_p(prec=10) 652 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + 77531/41580*z^8 + O(z^10) 653 sage: E.weierstrass_p(prec=8) 654 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + O(z^8) 655 sage: Esh = E.short_weierstrass_model() 656 sage: Esh.weierstrass_p(prec=8) 657 z^-2 + 13392/5*z^2 + 1080432/7*z^4 + 59781888/25*z^6 + O(z^8) 658 659 sage: E.weierstrass_p(prec=8, algorithm='pari') 660 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + O(z^8) 661 sage: E.weierstrass_p(prec=8, algorithm='quadratic') 662 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + O(z^8) 663 664 sage: k = GF(101) 665 sage: E = EllipticCurve(k, [2,3]) 666 sage: E.weierstrass_p(prec=30) 667 z^-2 + 40*z^2 + 14*z^4 + 62*z^6 + 15*z^8 + 47*z^10 + 66*z^12 + 61*z^14 + 79*z^16 + 98*z^18 + 93*z^20 + 82*z^22 + 15*z^24 + 71*z^26 + 27*z^28 + O(z^30) 668 669 sage: k = GF(11) 670 sage: E = EllipticCurve(k, [1,1]) 671 sage: E.weierstrass_p(prec=6, algorithm='fast') 672 z^-2 + 2*z^2 + 3*z^4 + O(z^6) 673 sage: E.weierstrass_p(prec=7, algorithm='fast') 674 Traceback (most recent call last): 675 ... 676 ValueError: For computing the Weierstrass p-function via the fast algorithm, the characteristic (11) of the underlying field must be greater than prec + 4 = 11. 677 sage: E.weierstrass_p(prec=8 ,algorithm='pari') 678 z^-2 + 2*z^2 + 3*z^4 + 5*z^6 + O(z^8) 679 sage: E.weierstrass_p(prec=9, algorithm='pari') 680 Traceback (most recent call last): 681 ... 682 ValueError: For computing the Weierstrass p-function via pari, the characteristic (11) of the underlying field must be greater than prec + 2 = 11. 683 684 """ 685 return weierstrass_p(self, prec=prec, algorithm=algorithm) -
sage/schemes/elliptic_curves/ell_generic.py
diff -r be27090d4a20 -r c4ee267a4cf7 sage/schemes/elliptic_curves/ell_generic.py
a b 1 1 r""" 2 2 Elliptic curves over a general ring. 3 3 4 Elliptic curves are always represented by 'Weierstrass Models' with 5 five coefficients `[a_1,a_2,a_3,a_4,a_6]` in standard 6 notation. In Magma, 'Weierstrass Model' means a model with 7 a1=a2=a3=0, which is called 'Short Weierstrass Model' in Sage; 8 these only exist in characteristics other than 2 and 3. 4 Sage defines an elliptic curve over a ring `R` as a 'Weierstrass Model' with 5 five coefficients `[a_1,a_2,a_3,a_4,a_6]` in `R` given by 6 7 `y^2 + a_1 xy + a_3 y = x^3 +a_2 x^2 +a_4 x +a_6`. 8 9 Note that the (usual) scheme-theoretic definition of an elliptic curve over `R` would require the discriminant to be a unit in `R`, Sage only imposes that the discriminant is non-zero. Also, in Magma, 'Weierstrass Model' means a model with `a1=a2=a3=0`, which is called 'Short Weierstrass Model' in Sage; these do not always exist in characteristics 2 and 3. 9 10 10 11 EXAMPLES: 11 12 -
new file sage/schemes/elliptic_curves/ell_wp.py
diff -r be27090d4a20 -r c4ee267a4cf7 sage/schemes/elliptic_curves/ell_wp.py
- + 1 r""" 2 Weierstrass `\wp` function for elliptic curves. 3 4 The Weierstrass `\wp` function associated to an elliptic curve over a field `k` is a Laurent series 5 of the form 6 7 .. math:: 8 9 \wp(z) = \frac{1}{z^2} + c_2 \cdot z^2 + c_4 \cdot z^4 + \cdots. 10 11 If the field is contained in `\mathbb{C}`, then 12 this is the series expansion of the map from `\mathbb{C}` to `E(\mathbb{C})` whose kernel is the period lattice of `E`. 13 14 Over other fields, like finite fields, this still makes sense as a formal power series with coefficients in `k` - at least its first `p-2` coefficients where `p` is the characteristic of `k`. It can be defined via the formal group as `x+c` in the variable `z=\log_E(t)` for a constant `c` such that the constant term `c_0` in `\wp(z)` is zero. 15 16 EXAMPLE:: 17 18 sage: E = EllipticCurve([0,1]) 19 sage: E.weierstrass_p() 20 z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + O(z^20) 21 22 REFERENCES: 23 24 - [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 25 26 AUTHORS: 27 28 - Dan Shumov 04/09 - original implementation 29 - Chris Wuthrich 11/09 - major restructuring 30 31 """ 32 33 #***************************************************************************** 34 # Copyright (C) 2009 William Stein <wstein@gmail.com> 35 # 36 # Distributed under the terms of the GNU General Public License (GPL) 37 # 38 # http://www.gnu.org/licenses/ 39 #***************************************************************************** 40 41 #from sage.structure.sage_object import SageObject 42 #from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing 43 from sage.rings.laurent_series_ring import LaurentSeriesRing 44 from sage.rings.power_series_ring import PowerSeriesRing 45 46 #from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism 47 48 def weierstrass_p(E, prec=20, algorithm=None): 49 r""" 50 Computes the Weierstrass `\wp`-function on an elliptic curve. 51 52 INPUT: 53 54 - ``E`` - an elliptic curve 55 - ``prec`` - precision 56 - ``algorithm`` - string (default:``None``) an algorithm identifier indicating using the ``pari``, ``fast`` or ``quadratic`` algorithm. If the algorithm is ``None``, then this function determines the best algorithm to use. 57 58 OUTPUT: 59 60 a Laurent series in one variable `z` with coefficients in the base field `k` of `E`. 61 62 EXAMPLES:: 63 64 sage: E = EllipticCurve('11a1') 65 sage: E.weierstrass_p(prec=10) 66 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + 77531/41580*z^8 + O(z^10) 67 sage: E.weierstrass_p(prec=8) 68 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + O(z^8) 69 sage: Esh = E.short_weierstrass_model() 70 sage: Esh.weierstrass_p(prec=8) 71 z^-2 + 13392/5*z^2 + 1080432/7*z^4 + 59781888/25*z^6 + O(z^8) 72 73 sage: E.weierstrass_p(prec=8, algorithm='pari') 74 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + O(z^8) 75 sage: E.weierstrass_p(prec=8, algorithm='quadratic') 76 z^-2 + 31/15*z^2 + 2501/756*z^4 + 961/675*z^6 + O(z^8) 77 78 sage: k = GF(11) 79 sage: E = EllipticCurve(k, [1,1]) 80 sage: E.weierstrass_p(prec=6, algorithm='fast') 81 z^-2 + 2*z^2 + 3*z^4 + O(z^6) 82 sage: E.weierstrass_p(prec=7, algorithm='fast') 83 Traceback (most recent call last): 84 ... 85 ValueError: For computing the Weierstrass p-function via the fast algorithm, the characteristic (11) of the underlying field must be greater than prec + 4 = 11. 86 sage: E.weierstrass_p(prec=8 ,algorithm='pari') 87 z^-2 + 2*z^2 + 3*z^4 + 5*z^6 + O(z^8) 88 sage: E.weierstrass_p(prec=9, algorithm='pari') 89 Traceback (most recent call last): 90 ... 91 ValueError: For computing the Weierstrass p-function via pari, the characteristic (11) of the underlying field must be greater than prec + 2 = 11. 92 93 """ 94 Esh = E.short_weierstrass_model() 95 u = E.isomorphism_to(Esh).u 96 97 k = E.base_ring() 98 p = k.characteristic() 99 100 # if the algorithm is not set, try to determine algorithm from input 101 if (None == algorithm): 102 if (0 == p) or (p > prec+4): 103 algorithm = "fast" 104 elif p > prec + 2: 105 algorithm = "pari" 106 else: 107 raise NotImplementedError, "Currently no algorithms for computing the Weierstrass p-function for that characteristic / precision pair is implemented. Lower the precision below char(k)-2" 108 109 A = Esh.a4() 110 B = Esh.a6() 111 112 113 if ("quadratic"==algorithm): 114 115 if (0 < p) and (p < 2*prec + 3): 116 raise ValueError, "For computing the Weierstrass p-function via the quadratic algorithm, the characteristic (%s) of the underlying field must be greater than 2*prec + 3 = %s."%(p,2*prec+4) 117 118 wp = compute_wp_quadratic(k, A, B, prec) 119 R = wp.parent() 120 z = R.gen() 121 wp = wp(z*u) * u**2 122 wp = wp.add_bigoh(prec) 123 124 elif ("fast"==algorithm): 125 126 if (0 < p) and (p < prec + 5): 127 raise ValueError, "For computing the Weierstrass p-function via the fast algorithm, the characteristic (%s) of the underlying field must be greater than prec + 4 = %s."%(p,prec+4) 128 129 wp = compute_wp_fast(k, A, B, prec) 130 R = wp.parent() 131 z = R.gen() 132 wp = wp(z*u) * u**2 133 wp = wp.add_bigoh(prec) 134 135 136 elif ("pari"==algorithm): 137 138 if (0 < p) and (p < prec + 3): 139 raise ValueError, "For computing the Weierstrass p-function via pari, the characteristic (%s) of the underlying field must be greater than prec + 2 = %s."%(p,prec+2) 140 141 wp = compute_wp_pari(E, prec) 142 143 else: 144 raise ValueError, "Unknown algorithm for computing the Weierstrass p-function." 145 146 return wp 147 148 def compute_wp_pari(E,prec): 149 r""" 150 Computes the Weierstrass `\wp`-function via calling the corresponding function in pari. 151 152 EXAMPLES:: 153 154 sage: E = EllipticCurve([0,1]) 155 sage: E.weierstrass_p(algorithm='pari') 156 z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + O(z^20) 157 158 sage: E = EllipticCurve(GF(101),[5,4]) 159 sage: E.weierstrass_p(prec=30, algorithm='pari') 160 z^-2 + 100*z^2 + 86*z^4 + 34*z^6 + 50*z^8 + 82*z^10 + 45*z^12 + 70*z^14 + 33*z^16 + 87*z^18 + 33*z^20 + 36*z^22 + 45*z^24 + 40*z^26 + 12*z^28 + O(z^30) 161 162 sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_pari 163 sage: compute_wp_pari(E, prec= 20) 164 z^-2 + 100*z^2 + 86*z^4 + 34*z^6 + 50*z^8 + 82*z^10 + 45*z^12 + 70*z^14 + 33*z^16 + 87*z^18 + O(z^20) 165 166 """ 167 ep = E._pari_() 168 wpp = ep.ellwp(n=prec) 169 k = E.base_ring() 170 R = LaurentSeriesRing(k,'z') 171 z = R.gen() 172 wp = z**(-2) 173 for i in xrange(prec): 174 wp += k(wpp[i]) * z**i 175 wp = wp.add_bigoh(prec) 176 return wp 177 178 179 def compute_wp_quadratic(k, A, B, prec): 180 r""" 181 Computes the truncated Weierstrass function of an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. Uses an algorithm that is of complexity `O(prec^2)`. 182 183 Let p be the characteristic of the underlying field: Then we must have either p=0, or p > ``prec`` + 3. 184 185 INPUT: 186 187 - ``k`` - the field of definition of the curve 188 - ``A`` - and 189 - ``B`` - the coefficients of the elliptic curve 190 - ``prec`` - the precision to which we compute the series. 191 192 OUTPUT: 193 A Laurent series aproximating the Weierstrass `\wp`-function to precision ``prec``. 194 195 ALGORITHM: 196 This function uses the algorithm described in section 3.2 of [BMSS]. 197 198 REFERENCES: 199 [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies." 200 201 EXAMPLES:: 202 203 sage: E = EllipticCurve([7,0]) 204 sage: E.weierstrass_p(prec=10, algorithm='quadratic') 205 z^-2 - 7/5*z^2 + 49/75*z^6 + O(z^10) 206 207 sage: E = EllipticCurve(GF(103),[1,2]) 208 sage: E.weierstrass_p(algorithm='quadratic') 209 z^-2 + 41*z^2 + 88*z^4 + 11*z^6 + 57*z^8 + 55*z^10 + 73*z^12 + 11*z^14 + 17*z^16 + 50*z^18 + O(z^20) 210 211 sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_quadratic 212 sage: compute_wp_quadratic(E.base_ring(), E.a4(), E.a6(), prec=10) 213 z^-2 + 41*z^2 + 88*z^4 + 11*z^6 + 57*z^8 + O(z^10) 214 215 """ 216 m = prec//2 +1 217 c = [0 for j in xrange(m)] 218 c[0] = -A/5 219 c[1] = -B/7 220 221 # first Z represent z^2 222 R = LaurentSeriesRing(k,'z') #,default_prec = prec+5) 223 Z = R.gen() 224 pe = Z**-1 + c[0]*Z + c[1]*Z**2 225 226 for i in xrange(3, m): 227 t = 0 228 for j in xrange(1,i-1): 229 t += c[j-1]*c[i-2-j] 230 ci = (3*t)/((i-2)*(2*i+3)) 231 pe += ci * Z**i 232 c[i-1] = ci 233 234 return pe(Z**2).add_bigoh(prec) 235 236 def compute_wp_fast(k, A, B, m): 237 r""" 238 Computes the Weierstrass function of an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. It does this with as fast as polynomial of degree `m` can be multiplied together in the base ring, i.e. `O(M(n))` in the notation of [BMSS]. 239 240 Let `p` be the characteristic of the underlying field: Then we must have either `p=0`, or `p > m + 3`. 241 242 INPUT: 243 244 - ``k`` - the base field of the curve 245 - ``A`` - and 246 - ``B`` - as the coeffients of the short Weierstrass model `y^2 = x^3 +Ax +B`, and 247 - ``m`` - the precision to which the function is computed to. 248 249 OUTPUT: 250 251 the Weierstrass `\wp` function as a Laurent series to precision `m`. 252 253 ALGORITHM: 254 255 This function uses the algorithm described in section 3.3 of 256 [BMSS]. 257 258 EXAMPLES:: 259 260 sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_fast 261 sage: compute_wp_fast(QQ, 1, 8, 7) 262 z^-2 - 1/5*z^2 - 8/7*z^4 + 1/75*z^6 + O(z^7) 263 264 sage: k = GF(37) 265 sage: compute_wp_fast(k, k(1), k(8), 5) 266 z^-2 + 22*z^2 + 20*z^4 + O(z^5) 267 268 """ 269 R = PowerSeriesRing(k,'z',default_prec=m+5) 270 z = R.gen() 271 s = 2 272 f1 = z.add_bigoh(m+3) 273 n = 2*m + 4 274 275 # solve the nonlinear differential equation 276 while (s < n): 277 f1pr = f1.derivative() 278 next_s = 2*s - 1 279 280 a = 2*f1pr 281 b = -(6*B*(f1**5) + 4*A*(f1**3)) 282 c = B*(f1**6) + A*f1**4 + 1 - (f1pr**2) 283 284 # we should really be computing only mod z^next_s here. 285 # but we loose only a factor 2 286 f2 = solve_linear_differential_system(a, b, c, 0) 287 # sometimes we get to 0 quicker than s reaches n 288 if f2 == 0: 289 break 290 f1 = f1 + f2 291 s = next_s 292 293 R = f1 294 Q = R**2 295 pe = 1/Q 296 297 return pe 298 299 300 def solve_linear_differential_system(a, b, c, alpha): 301 r""" 302 Solves a system of linear differential equations: `af' + bf = c` and `f'(0) = \alpha` 303 where `a`, `b`, and `c` are power series in one variable and `\alpha` is a constant in the coefficient ring. 304 305 ALGORITHM: 306 307 due to Brent and Kung '78. 308 309 EXAMPLES:: 310 311 sage: from sage.schemes.elliptic_curves.ell_wp import solve_linear_differential_system 312 sage: k = GF(17) 313 sage: R.<x> = PowerSeriesRing(k) 314 sage: a = 1+x+O(x^7); b = x+O(x^7); c = 1+x^3+O(x^7); alpha = k(3) 315 sage: f = solve_linear_differential_system(a,b,c,alpha) 316 sage: f 317 3 + x + 15*x^2 + x^3 + 10*x^5 + 3*x^6 + 13*x^7 + O(x^8) 318 sage: a*f.derivative()+b*f - c 319 O(x^7) 320 sage: f(0) == alpha 321 True 322 323 """ 324 a_recip = 1/a 325 B = b * a_recip 326 C = c * a_recip 327 int_B = B.integral() 328 J = int_B.exp() 329 J_recip = 1/J 330 CJ = C * J 331 int_CJ = CJ.integral() 332 f = J_recip * (alpha + int_CJ) 333 334 return f
