Example #1
0
    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]
Example #2
0
    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]