Beispiel #1
0
def quadratic_L_function__numerical(n, d, num_terms=1000):
    """
    Evaluate the Dirichlet L-function (for quadratic character) numerically
    (in a very naive way).

    EXAMPLES:

    First, let us test several values for a given character::

        sage: RR = RealField(100)
        sage: for i in range(5):
        ....:     print("L({}, (-4/.)): {}".format(1+2*i, RR(quadratic_L_function__exact(1+2*i, -4)) - quadratic_L_function__numerical(RR(1+2*i),-4, 10000)))
        L(1, (-4/.)): 0.000049999999500000024999996962707
        L(3, (-4/.)): 4.99999970000003...e-13
        L(5, (-4/.)): 4.99999922759382...e-21
        L(7, (-4/.)): ...e-29
        L(9, (-4/.)): ...e-29

    This procedure fails for negative special values, as the Dirichlet
    series does not converge here::

        sage: quadratic_L_function__numerical(-3,-4, 10000)
        Traceback (most recent call last):
        ...
        ValueError: the Dirichlet series does not converge here

    Test for several characters that the result agrees with the exact
    value, to a given accuracy ::

        sage: for d in range(-20,0):  # long time (2s on sage.math 2014)
        ....:     if abs(RR(quadratic_L_function__numerical(1, d, 10000) - quadratic_L_function__exact(1, d))) > 0.001:
        ....:         print("Oops! We have a problem at d = {}: exact = {}, numerical = {}".format(d, RR(quadratic_L_function__exact(1, d)), RR(quadratic_L_function__numerical(1, d))))
    """
    # Set the correct precision if it is given (for n).
    if is_RealField(n.parent()):
        R = n.parent()
    else:
        R = RealField()

    if n < 0:
        raise ValueError('the Dirichlet series does not converge here')

    d1 = fundamental_discriminant(d)
    ans = R.zero()
    for i in range(1, num_terms):
        ans += R(kronecker_symbol(d1, i) / R(i)**n)
    return ans
Beispiel #2
0
def quadratic_L_function__numerical(n, d, num_terms=1000):
    """
    Evaluate the Dirichlet L-function (for quadratic character) numerically
    (in a very naive way).

    EXAMPLES:

    First, let us test several values for a given character::

        sage: RR = RealField(100)
        sage: for i in range(5):
        ....:     print("L({}, (-4/.)): {}".format(1+2*i, RR(quadratic_L_function__exact(1+2*i, -4)) - quadratic_L_function__numerical(RR(1+2*i),-4, 10000)))
        L(1, (-4/.)): 0.000049999999500000024999996962707
        L(3, (-4/.)): 4.99999970000003...e-13
        L(5, (-4/.)): 4.99999922759382...e-21
        L(7, (-4/.)): ...e-29
        L(9, (-4/.)): ...e-29

    This procedure fails for negative special values, as the Dirichlet
    series does not converge here::

        sage: quadratic_L_function__numerical(-3,-4, 10000)
        Traceback (most recent call last):
        ...
        ValueError: the Dirichlet series does not converge here

    Test for several characters that the result agrees with the exact
    value, to a given accuracy ::

        sage: for d in range(-20,0):  # long time (2s on sage.math 2014)
        ....:     if abs(RR(quadratic_L_function__numerical(1, d, 10000) - quadratic_L_function__exact(1, d))) > 0.001:
        ....:         print("Oops! We have a problem at d = {}: exact = {}, numerical = {}".format(d, RR(quadratic_L_function__exact(1, d)), RR(quadratic_L_function__numerical(1, d))))
    """
    # Set the correct precision if it is given (for n).
    if is_RealField(n.parent()):
        R = n.parent()
    else:
        R = RealField()

    if n < 0:
        raise ValueError('the Dirichlet series does not converge here')

    d1 = fundamental_discriminant(d)
    ans = R.zero()
    for i in range(1,num_terms):
        ans += R(kronecker_symbol(d1,i) / R(i)**n)
    return ans
Beispiel #3
0
    def deriv_at1(self, k=None, prec=None):
        r"""
        Compute `L'(E,1)` using `k` terms of the series for `L'(E,1)`,
        under the assumption that `L(E,1) = 0`.

        The algorithm used is from Section 7.5.3 of Henri Cohen's book
        *A Course in Computational Algebraic Number Theory*.

        INPUT:

        - ``k`` -- number of terms of the series. If zero or ``None``,
          use `k = \sqrt{N}`, where `N` is the conductor.

        - ``prec`` -- numerical precision in bits. If zero or ``None``,
          use a reasonable automatic default.

        OUTPUT:

        A tuple of real numbers ``(L1, err)`` where ``L1`` is an
        approximation for `L'(E,1)` and ``err`` is a bound on the error
        in the approximation.

        .. WARNING::

            This function only makes sense if `L(E)` has positive order
            of vanishing at 1, or equivalently if `L(E,1) = 0`.

        ALGORITHM:

        - Compute the root number eps.  If it is 1, return 0.

        - Compute the Fourier coefficients `a_n`, for `n` up to and
          including `k`.

        - Compute the sum

          .. MATH::

                 2 \cdot \sum_{n=1}^{k} (a_n / n) \cdot E_1(2 \pi n/\sqrt{N}),

          where `N` is the conductor of `E`, and `E_1` is the
          exponential integral function.

        - Compute a bound on the tail end of the series, which is

          .. MATH::

                 2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}).

          For a proof see [Grigorov-Jorza-Patrascu-Patrikis-Stein].  This
          is exactly the same as the bound for the approximation to
          `L(E,1)` produced by :meth:`at1`.

        EXAMPLES::

            sage: E = EllipticCurve('37a')
            sage: E.lseries().deriv_at1()
            (0.3059866, 0.000801045)
            sage: E.lseries().deriv_at1(100)
            (0.3059997738340523018204836833216764744526377745903, 1.52493e-45)
            sage: E.lseries().deriv_at1(1000)
            (0.305999773834052301820483683321676474452637774590771998..., 2.75031e-449)

        With less numerical precision, the error is bounded by numerical accuracy::

            sage: L,err = E.lseries().deriv_at1(100, prec=64)
            sage: L,err
            (0.305999773834052302, 5.55318e-18)
            sage: parent(L)
            Real Field with 64 bits of precision
            sage: parent(err)
            Real Field with 24 bits of precision and rounding RNDU

        Rank 2 and rank 3 elliptic curves::

            sage: E = EllipticCurve('389a1')
            sage: E.lseries().deriv_at1()
            (0.0000000, 0.000000)
            sage: E = EllipticCurve((1, 0, 1, -131, 558))  # curve 59450i1
            sage: E.lseries().deriv_at1()
            (-0.00010911444, 0.142428)
            sage: E.lseries().deriv_at1(4000)
            (6.990...e-50, 1.31318e-43)
        """
        sqrtN = sqrt(self.__E.conductor())
        if k:
            k = int(k)
        else:
            k = int(ceil(sqrtN))

        if prec:
            prec = int(prec)
        else:
            # Estimate number of bits for the computation, based on error
            # estimate below (the denominator of that error is close enough
            # to 1 that we can ignore it).
            # 9.065 = 2*Pi/log(2)
            # 1.443 = 1/log(2)
            # 12 is an arbitrary extra number of bits (it is chosen
            #    such that the precision is 24 bits when the conductor
            #    equals 11 and k is the default value 4)
            prec = int(9.065 * k / sqrtN + 1.443 * log(k)) + 12
        R = RealField(prec)
        # Compute error term with bounded precision of 24 bits and
        # round towards +infinity
        Rerror = RealField(24, rnd='RNDU')

        if self.__E.root_number() == 1:
            # Order of vanishing at 1 of L(E) is even and assumed to be
            # positive, so L'(E,1) = 0.
            return (R.zero(), Rerror.zero())

        an = self.__E.anlist(k)  # list of Sage Integers
        pi = R.pi()
        sqrtN = R(self.__E.conductor()).sqrt()
        v = exp_integral.exponential_integral_1(2 * pi / sqrtN, k)

        # Compute series sum and accumulate floating point errors
        L = R.zero()
        error = Rerror.zero()
        # Sum of |an[n]|/n
        sumann = Rerror.zero()

        for n in xrange(1, k + 1):
            term = (v[n - 1] * an[n]) / n
            L += term
            error += term.epsilon(Rerror) * 5 + L.ulp(Rerror)
            sumann += Rerror(an[n].abs()) / n
        L *= 2

        # Add error term for exponential_integral_1() errors.
        # Absolute error for 2*v[i] is 4*max(1, v[0])*2^-prec
        if v[0] > 1.0:
            sumann *= Rerror(v[0])
        error += (sumann >> (prec - 2))

        # Add series error (we use (-2)/(z-1) instead of 2/(1-z)
        # because this causes 1/(1-z) to be rounded up)
        z = (-2 * pi / sqrtN).exp()
        zpow = ((-2 * (k + 1)) * pi / sqrtN).exp()
        error += ((-2) * Rerror(zpow)) / Rerror(z - 1)
        return (L, error)
Beispiel #4
0
    def at1(self, k=None, prec=None):
        r"""
        Compute `L(E,1)` using `k` terms of the series for `L(E,1)` as
        explained in Section 7.5.3 of Henri Cohen's book *A Course in
        Computational Algebraic Number Theory*.  If the argument `k`
        is not specified, then it defaults to `\sqrt{N}`, where `N` is
        the conductor.

        INPUT:

        - ``k`` -- number of terms of the series. If zero or ``None``,
          use `k = \sqrt{N}`, where `N` is the conductor.

        - ``prec`` -- numerical precision in bits. If zero or ``None``,
          use a reasonable automatic default.

        OUTPUT:

        A tuple of real numbers ``(L, err)`` where ``L`` is an
        approximation for `L(E,1)` and ``err`` is a bound on the error
        in the approximation.

        This function is disjoint from the PARI ``elllseries``
        command, which is for a similar purpose.  To use that command
        (via the PARI C library), simply type
        ``E.pari_mincurve().elllseries(1)``.

        ALGORITHM:

        - Compute the root number eps.  If it is -1, return 0.

        - Compute the Fourier coefficients `a_n`, for `n` up to and
          including `k`.

        - Compute the sum

          .. MATH::

              2 \cdot \sum_{n=1}^{k} \frac{a_n}{n} \cdot \exp(-2*pi*n/\sqrt{N}),

          where `N` is the conductor of `E`.

        - Compute a bound on the tail end of the series, which is

          .. MATH::

                2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}).

          For a proof see [Grigov-Jorza-Patrascu-Patrikis-Stein].

        EXAMPLES::

            sage: L, err = EllipticCurve('11a1').lseries().at1()
            sage: L, err
            (0.253804, 0.000181444)
            sage: parent(L)
            Real Field with 24 bits of precision
            sage: E = EllipticCurve('37b')
            sage: E.lseries().at1()
            (0.7257177, 0.000800697)
            sage: E.lseries().at1(100)
            (0.7256810619361527823362055410263965487367603361763, 1.52469e-45)
            sage: L,err = E.lseries().at1(100, prec=128)
            sage: L
            0.72568106193615278233620554102639654873
            sage: parent(L)
            Real Field with 128 bits of precision
            sage: err
            1.70693e-37
            sage: parent(err)
            Real Field with 24 bits of precision and rounding RNDU

        Rank 1 through 3 elliptic curves::

            sage: E = EllipticCurve('37a1')
            sage: E.lseries().at1()
            (0.0000000, 0.000000)
            sage: E = EllipticCurve('389a1')
            sage: E.lseries().at1()
            (-0.001769566, 0.00911776)
            sage: E = EllipticCurve('5077a1')
            sage: E.lseries().at1()
            (0.0000000, 0.000000)
        """
        sqrtN = sqrt(self.__E.conductor())
        if k:
            k = int(k)
        else:
            k = int(ceil(sqrtN))

        if prec:
            prec = int(prec)
        else:
            # Use the same precision as deriv_at1() below for
            # consistency
            prec = int(9.065 * k / sqrtN + 1.443 * log(k)) + 12
        R = RealField(prec)
        # Compute error term with bounded precision of 24 bits and
        # round towards +infinity
        Rerror = RealField(24, rnd='RNDU')

        if self.__E.root_number() == -1:
            return (R.zero(), Rerror.zero())

        an = self.__E.anlist(k)  # list of Sage Integers
        pi = R.pi()
        sqrtN = R(self.__E.conductor()).sqrt()

        z = (-2 * pi / sqrtN).exp()
        zpow = z
        # Compute series sum and accumulate floating point errors
        L = R.zero()
        error = Rerror.zero()

        for n in xrange(1, k + 1):
            term = (zpow * an[n]) / n
            zpow *= z
            L += term
            # We express relative error in units of epsilon, where
            # epsilon is a number divided by 2^precision.
            # Instead of multiplying the error by 2 after the loop
            # (to account for L *= 2), we already multiply it now.
            #
            # For multiplication and division, the relative error
            # in epsilons is bounded by (1+e)^n - 1, where n is the
            # number of operations (assuming exact inputs).
            # exp(x) additionally multiplies this error by abs(x) and
            # adds one epsilon. The inputs pi and sqrtN each contribute
            # another epsilon.
            # Assuming that 2*pi/sqrtN <= 2, the relative error for z is
            # 7 epsilon. This implies a relative error of (8n-1) epsilon
            # for zpow. We add 2 for the computation of term and 1/2 to
            # compensate for the approximation (1+e)^n = 1+ne.
            #
            # The error of the addition is at most half an ulp of the
            # result.
            #
            # Multiplying everything by two gives:
            error += term.epsilon(Rerror) * (16 * n + 3) + L.ulp(Rerror)
        L *= 2

        # Add series error (we use (-2)/(z-1) instead of 2/(1-z)
        # because this causes 1/(1-z) to be rounded up)
        error += ((-2) * Rerror(zpow)) / Rerror(z - 1)
        return (L, error)
Beispiel #5
0
    def deriv_at1(self, k=None, prec=None):
        r"""
        Compute `L'(E,1)` using `k` terms of the series for `L'(E,1)`,
        under the assumption that `L(E,1) = 0`.

        The algorithm used is from Section 7.5.3 of Henri Cohen's book
        ``A Course in Computational Algebraic Number Theory.''

        INPUT:

        - ``k`` -- number of terms of the series. If zero or ``None``,
          use `k = \sqrt(N)`, where `N` is the conductor.

        - ``prec`` -- numerical precision in bits. If zero or ``None``,
          use a reasonable automatic default.

        OUTPUT:

        A tuple of real numbers ``(L1, err)`` where ``L1`` is an
        approximation for `L'(E,1)` and ``err`` is a bound on the error
        in the approximation.

        .. WARNING::

            This function only makes sense if `L(E)` has positive order
            of vanishing at 1, or equivalently if `L(E,1) = 0`.

        ALGORITHM:

        - Compute the root number eps.  If it is 1, return 0.

        - Compute the Fourier coefficients `a_n`, for `n` up to and
          including `k`.

        - Compute the sum

          .. MATH::

                 2 * \sum_{n=1}^{k} (a_n / n) * E_1(2 \pi n/\sqrt{N}),

          where `N` is the conductor of `E`, and `E_1` is the
          exponential integral function.

        - Compute a bound on the tail end of the series, which is

          .. MATH::

                 2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}).

          For a proof see [Grigorov-Jorza-Patrascu-Patrikis-Stein].  This
          is exactly the same as the bound for the approximation to
          `L(E,1)` produced by :meth:`at1`.

        EXAMPLES::

            sage: E = EllipticCurve('37a')
            sage: E.lseries().deriv_at1()
            (0.3059866, 0.000801045)
            sage: E.lseries().deriv_at1(100)
            (0.3059997738340523018204836833216764744526377745903, 1.52493e-45)
            sage: E.lseries().deriv_at1(1000)
            (0.305999773834052301820483683321676474452637774590771998..., 2.75031e-449)

        With less numerical precision, the error is bounded by numerical accuracy::

            sage: L,err = E.lseries().deriv_at1(100, prec=64)
            sage: L,err
            (0.305999773834052302, 5.55318e-18)
            sage: parent(L)
            Real Field with 64 bits of precision
            sage: parent(err)
            Real Field with 24 bits of precision and rounding RNDU

        Rank 2 and rank 3 elliptic curves::

            sage: E = EllipticCurve('389a1')
            sage: E.lseries().deriv_at1()
            (0.0000000, 0.000000)
            sage: E = EllipticCurve((1, 0, 1, -131, 558))  # curve 59450i1
            sage: E.lseries().deriv_at1()
            (-0.00010911444, 0.142428)
            sage: E.lseries().deriv_at1(4000)
            (6.9902290...e-50, 1.31318e-43)
        """
        sqrtN = sqrt(self.__E.conductor())
        if k:
            k = int(k)
        else:
            k = int(ceil(sqrtN))

        if prec:
            prec = int(prec)
        else:
            # Estimate number of bits for the computation, based on error
            # estimate below (the denominator of that error is close enough
            # to 1 that we can ignore it).
            # 9.065 = 2*Pi/log(2)
            # 1.443 = 1/log(2)
            # 12 is an arbitrary extra number of bits (it is chosen
            #    such that the precision is 24 bits when the conductor
            #    equals 11 and k is the default value 4)
            prec = int(9.065*k/sqrtN + 1.443*log(k)) + 12
        R = RealField(prec)
        # Compute error term with bounded precision of 24 bits and
        # round towards +infinity
        Rerror = RealField(24, rnd='RNDU')

        if self.__E.root_number() == 1:
           # Order of vanishing at 1 of L(E) is even and assumed to be
           # positive, so L'(E,1) = 0.
           return (R.zero(), Rerror.zero())

        an = self.__E.anlist(k)  # list of Sage Integers
        pi = R.pi()
        sqrtN = R(self.__E.conductor()).sqrt()
        v = exp_integral.exponential_integral_1(2*pi/sqrtN, k)

        # Compute series sum and accumulate floating point errors
        L = R.zero()
        error = Rerror.zero()
        # Sum of |an[n]|/n
        sumann = Rerror.zero()

        for n in xrange(1,k+1):
            term = (v[n-1] * an[n])/n
            L += term
            error += term.epsilon(Rerror)*5 + L.ulp(Rerror)
            sumann += Rerror(an[n].abs())/n
        L *= 2

        # Add error term for exponential_integral_1() errors.
        # Absolute error for 2*v[i] is 4*max(1, v[0])*2^-prec
        if v[0] > 1.0:
            sumann *= Rerror(v[0])
        error += (sumann >> (prec - 2))

        # Add series error (we use (-2)/(z-1) instead of 2/(1-z)
        # because this causes 1/(1-z) to be rounded up)
        z = (-2*pi/sqrtN).exp()
        zpow = ((-2*(k+1))*pi/sqrtN).exp()
        error += ((-2)*Rerror(zpow)) / Rerror(z - 1)
        return (L, error)
Beispiel #6
0
    def at1(self, k=None, prec=None):
        r"""
        Compute `L(E,1)` using `k` terms of the series for `L(E,1)` as
        explained in Section 7.5.3 of Henri Cohen's book "A Course in
        Computational Algebraic Number Theory".  If the argument `k`
        is not specified, then it defaults to `\sqrt(N)`, where `N` is
        the conductor.

        INPUT:

        - ``k`` -- number of terms of the series. If zero or ``None``,
          use `k = \sqrt(N)`, where `N` is the conductor.

        - ``prec`` -- numerical precision in bits. If zero or ``None``,
          use a reasonable automatic default.

        OUTPUT:

        A tuple of real numbers ``(L, err)`` where ``L`` is an
        approximation for `L(E,1)` and ``err`` is a bound on the error
        in the approximation.

        This function is disjoint from the PARI ``elllseries``
        command, which is for a similar purpose.  To use that command
        (via the PARI C library), simply type
        ``E.pari_mincurve().elllseries(1)``.

        ALGORITHM:

        - Compute the root number eps.  If it is -1, return 0.

        - Compute the Fourier coefficients `a_n`, for `n` up to and
          including `k`.

        - Compute the sum

          .. MATH::

              2 * sum_{n=1}^{k} (a_n / n) * exp(-2*pi*n/Sqrt(N)),

          where `N` is the conductor of `E`.

        - Compute a bound on the tail end of the series, which is

          .. MATH::

                 2 e^{-2 \pi (k+1) / \sqrt{N}} / (1 - e^{-2 \pi/\sqrt{N}}).

          For a proof see [Grigov-Jorza-Patrascu-Patrikis-Stein].

        EXAMPLES::

            sage: L, err = EllipticCurve('11a1').lseries().at1()
            sage: L, err
            (0.253804, 0.000181444)
            sage: parent(L)
            Real Field with 24 bits of precision
            sage: E = EllipticCurve('37b')
            sage: E.lseries().at1()
            (0.7257177, 0.000800697)
            sage: E.lseries().at1(100)
            (0.7256810619361527823362055410263965487367603361763, 1.52469e-45)
            sage: L,err = E.lseries().at1(100, prec=128)
            sage: L
            0.72568106193615278233620554102639654873
            sage: parent(L)
            Real Field with 128 bits of precision
            sage: err
            1.70693e-37
            sage: parent(err)
            Real Field with 24 bits of precision and rounding RNDU

        Rank 1 through 3 elliptic curves::

            sage: E = EllipticCurve('37a1')
            sage: E.lseries().at1()
            (0.0000000, 0.000000)
            sage: E = EllipticCurve('389a1')
            sage: E.lseries().at1()
            (-0.001769566, 0.00911776)
            sage: E = EllipticCurve('5077a1')
            sage: E.lseries().at1()
            (0.0000000, 0.000000)
        """
        sqrtN = sqrt(self.__E.conductor())
        if k:
            k = int(k)
        else:
            k = int(ceil(sqrtN))

        if prec:
            prec = int(prec)
        else:
            # Use the same precision as deriv_at1() below for
            # consistency
            prec = int(9.065*k/sqrtN + 1.443*log(k)) + 12
        R = RealField(prec)
        # Compute error term with bounded precision of 24 bits and
        # round towards +infinity
        Rerror = RealField(24, rnd='RNDU')

        if self.__E.root_number() == -1:
           return (R.zero(), Rerror.zero())

        an = self.__E.anlist(k)  # list of Sage Integers
        pi = R.pi()
        sqrtN = R(self.__E.conductor()).sqrt()

        z = (-2*pi/sqrtN).exp()
        zpow = z
        # Compute series sum and accumulate floating point errors
        L = R.zero()
        error = Rerror.zero()

        for n in xrange(1,k+1):
            term = (zpow * an[n])/n
            zpow *= z
            L += term
            # We express relative error in units of epsilon, where
            # epsilon is a number divided by 2^precision.
            # Instead of multiplying the error by 2 after the loop
            # (to account for L *= 2), we already multiply it now.
            #
            # For multiplication and division, the relative error
            # in epsilons is bounded by (1+e)^n - 1, where n is the
            # number of operations (assuming exact inputs).
            # exp(x) additionally multiplies this error by abs(x) and
            # adds one epsilon. The inputs pi and sqrtN each contribute
            # another epsilon.
            # Assuming that 2*pi/sqrtN <= 2, the relative error for z is
            # 7 epsilon. This implies a relative error of (8n-1) epsilon
            # for zpow. We add 2 for the computation of term and 1/2 to
            # compensate for the approximation (1+e)^n = 1+ne.
            #
            # The error of the addition is at most half an ulp of the
            # result.
            #
            # Multiplying everything by two gives:
            error += term.epsilon(Rerror)*(16*n + 3) + L.ulp(Rerror)
        L *= 2

        # Add series error (we use (-2)/(z-1) instead of 2/(1-z)
        # because this causes 1/(1-z) to be rounded up)
        error += ((-2)*Rerror(zpow)) / Rerror(z - 1)
        return (L, error)