def w(self, prec=20): r""" The formal group power series w. INPUT: - ``prec`` - integer (default 20) OUTPUT: a power series with given precision DETAILS: Return the formal power series .. math:: w(t) = t^3 + a_1 t^4 + (a_2 + a_1^2) t^5 + \cdots to precision `O(t^{prec})` of Proposition IV.1.1 of [Silverman AEC1]. This is the formal expansion of `w = -1/y` about the formal parameter `t = -x/y` at `\\infty`. The result is cached, and a cached version is returned if possible. .. warning:: The resulting power series will have precision prec, but its parent PowerSeriesRing will have default precision 20 (or whatever the default default is). ALGORITHM: Uses Newton's method to solve the elliptic curve equation at the origin. Complexity is roughly `O(M(n))` where `n` is the precision and `M(n)` is the time required to multiply polynomials of length `n` over the coefficient ring of `E`. AUTHOR: - David Harvey (2006-09-09): modified to use Newton's method instead of a recurrence formula. EXAMPLES:: sage: e = EllipticCurve([0, 0, 1, -1, 0]) sage: e.formal_group().w(10) t^3 + t^6 - t^7 + 2*t^9 + O(t^10) Check that caching works:: sage: e = EllipticCurve([3, 2, -4, -2, 5]) sage: e.formal_group().w(20) t^3 + 3*t^4 + 11*t^5 + 35*t^6 + 101*t^7 + 237*t^8 + 312*t^9 - 949*t^10 - 10389*t^11 - 57087*t^12 - 244092*t^13 - 865333*t^14 - 2455206*t^15 - 4366196*t^16 + 6136610*t^17 + 109938783*t^18 + 688672497*t^19 + O(t^20) sage: e.formal_group().w(7) t^3 + 3*t^4 + 11*t^5 + 35*t^6 + O(t^7) sage: e.formal_group().w(35) t^3 + 3*t^4 + 11*t^5 + 35*t^6 + 101*t^7 + 237*t^8 + 312*t^9 - 949*t^10 - 10389*t^11 - 57087*t^12 - 244092*t^13 - 865333*t^14 - 2455206*t^15 - 4366196*t^16 + 6136610*t^17 + 109938783*t^18 + 688672497*t^19 + 3219525807*t^20 + 12337076504*t^21 + 38106669615*t^22 + 79452618700*t^23 - 33430470002*t^24 - 1522228110356*t^25 - 10561222329021*t^26 - 52449326572178*t^27 - 211701726058446*t^28 - 693522772940043*t^29 - 1613471639599050*t^30 - 421817906421378*t^31 + 23651687753515182*t^32 + 181817896829144595*t^33 + 950887648021211163*t^34 + O(t^35) """ prec = max(prec, 0) k = self.curve().base_ring() try: # Try cached version w = self.__w cached_prec = w.prec() R = w.parent() except AttributeError: # No cached version available R = rings.PowerSeriesRing(k, "t") w = R([k(0), k(0), k(0), k(1)], 4) cached_prec = 4 self.__w = w if prec < cached_prec: return R(w, prec) # We use the following iteration, which doubles the precision # at each step: # # z^3 - a_3 w^2 - a_4 z w^2 - 2 a_6 w^3 # w' = ----------------------------------------------------- # 1 - a_1 z - a_2 z^2 - 2 a_3 w - 2 a_4 z w - 3 a_6 w^2 a1, a2, a3, a4, a6 = self.curve().ainvs() current_prec = cached_prec w = w.truncate() # work with polynomials instead of power series numerator_const = w.parent()([0, 0, 0, 1]) # z^3 denominator_const = w.parent()([1, -a1, -a2]) # 1 - a_1 z - a_2 z^2 last_prec = 0 for next_prec in misc.newton_method_sizes(prec): if next_prec > current_prec: if w.degree() - 1 > last_prec: # Here it's best to throw away some precision to get us # in sync with the sizes recommended by # newton_method_sizes(). This is especially counter- # intuitive when we throw away almost half of our # cached data! # todo: this might not actually be true, depending on # the overhead of truncate(), which is currently very # high e.g. for NTL based polynomials (but this is # slated to be fixed...) w = w.truncate(last_prec) w_squared = w.square() w_cubed = (w_squared * w).truncate(next_prec) numerator = numerator_const \ - a3 * w_squared \ - a4 * w_squared.shift(1) \ - (2*a6) * w_cubed denominator = denominator_const \ - (2*a3) * w \ - (2*a4) * w.shift(1) \ - (3*a6) * w_squared # todo: this is quite inefficient, because it gets # converted to a power series, then the power series # inversion works with polynomials again, and then # it gets converted *back* to a power series, and # then we convert it to a polynomial again! That's four # useless conversions!!! inverse = ~R(denominator, prec=next_prec) inverse = inverse.truncate(next_prec) w = (numerator * inverse).truncate(next_prec) last_prec = next_prec # convert back to power series w = R(w, prec) self.__w = (prec, w) return self.__w[1]
def w(self, prec=20): r""" The formal group power series w. INPUT: - ``prec`` - integer (default 20) OUTPUT: a power series with given precision DETAILS: Return the formal power series .. MATH:: w(t) = t^3 + a_1 t^4 + (a_2 + a_1^2) t^5 + \cdots to precision `O(t^{prec})` of Proposition IV.1.1 of [Silverman AEC1]. This is the formal expansion of `w = -1/y` about the formal parameter `t = -x/y` at `\\infty`. The result is cached, and a cached version is returned if possible. .. warning:: The resulting power series will have precision prec, but its parent PowerSeriesRing will have default precision 20 (or whatever the default default is). ALGORITHM: Uses Newton's method to solve the elliptic curve equation at the origin. Complexity is roughly `O(M(n))` where `n` is the precision and `M(n)` is the time required to multiply polynomials of length `n` over the coefficient ring of `E`. AUTHOR: - David Harvey (2006-09-09): modified to use Newton's method instead of a recurrence formula. EXAMPLES:: sage: e = EllipticCurve([0, 0, 1, -1, 0]) sage: e.formal_group().w(10) t^3 + t^6 - t^7 + 2*t^9 + O(t^10) Check that caching works:: sage: e = EllipticCurve([3, 2, -4, -2, 5]) sage: e.formal_group().w(20) t^3 + 3*t^4 + 11*t^5 + 35*t^6 + 101*t^7 + 237*t^8 + 312*t^9 - 949*t^10 - 10389*t^11 - 57087*t^12 - 244092*t^13 - 865333*t^14 - 2455206*t^15 - 4366196*t^16 + 6136610*t^17 + 109938783*t^18 + 688672497*t^19 + O(t^20) sage: e.formal_group().w(7) t^3 + 3*t^4 + 11*t^5 + 35*t^6 + O(t^7) sage: e.formal_group().w(35) t^3 + 3*t^4 + 11*t^5 + 35*t^6 + 101*t^7 + 237*t^8 + 312*t^9 - 949*t^10 - 10389*t^11 - 57087*t^12 - 244092*t^13 - 865333*t^14 - 2455206*t^15 - 4366196*t^16 + 6136610*t^17 + 109938783*t^18 + 688672497*t^19 + 3219525807*t^20 + 12337076504*t^21 + 38106669615*t^22 + 79452618700*t^23 - 33430470002*t^24 - 1522228110356*t^25 - 10561222329021*t^26 - 52449326572178*t^27 - 211701726058446*t^28 - 693522772940043*t^29 - 1613471639599050*t^30 - 421817906421378*t^31 + 23651687753515182*t^32 + 181817896829144595*t^33 + 950887648021211163*t^34 + O(t^35) """ prec = max(prec, 0) k = self.curve().base_ring() try: # Try cached version w = self.__w cached_prec = w.prec() R = w.parent() except AttributeError: # No cached version available R = rings.PowerSeriesRing(k, "t") w = R([k(0), k(0), k(0), k(1)], 4) cached_prec = 4 self.__w = w if prec < cached_prec: return R(w, prec) # We use the following iteration, which doubles the precision # at each step: # # z^3 - a_3 w^2 - a_4 z w^2 - 2 a_6 w^3 # w' = ----------------------------------------------------- # 1 - a_1 z - a_2 z^2 - 2 a_3 w - 2 a_4 z w - 3 a_6 w^2 a1, a2, a3, a4, a6 = self.curve().ainvs() current_prec = cached_prec w = w.truncate() # work with polynomials instead of power series numerator_const = w.parent()([0, 0, 0, 1]) # z^3 denominator_const = w.parent()([1, -a1, -a2]) # 1 - a_1 z - a_2 z^2 last_prec = 0 for next_prec in misc.newton_method_sizes(prec): if next_prec > current_prec: if w.degree() - 1 > last_prec: # Here it's best to throw away some precision to get us # in sync with the sizes recommended by # newton_method_sizes(). This is especially counter- # intuitive when we throw away almost half of our # cached data! # todo: this might not actually be true, depending on # the overhead of truncate(), which is currently very # high e.g. for NTL based polynomials (but this is # slated to be fixed...) w = w.truncate(last_prec) w_squared = w.square() w_cubed = (w_squared * w).truncate(next_prec) numerator = numerator_const \ - a3 * w_squared \ - a4 * w_squared.shift(1) \ - (2*a6) * w_cubed denominator = denominator_const \ - (2*a3) * w \ - (2*a4) * w.shift(1) \ - (3*a6) * w_squared # todo: this is quite inefficient, because it gets # converted to a power series, then the power series # inversion works with polynomials again, and then # it gets converted *back* to a power series, and # then we convert it to a polynomial again! That's four # useless conversions!!! inverse = ~R(denominator, prec=next_prec) inverse = inverse.truncate(next_prec) w = (numerator * inverse).truncate(next_prec) last_prec = next_prec # convert back to power series w = R(w, prec) self.__w = (prec, w) return self.__w[1]