Exemple #1
0
def compute_wp_pari(E,prec):
    r"""
    Computes the Weierstrass `\wp`-function via calling the corresponding function in pari.

    EXAMPLES::

        sage: E = EllipticCurve([0,1])
        sage: E.weierstrass_p(algorithm='pari')
        z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + O(z^20)

        sage: E = EllipticCurve(GF(101),[5,4])
        sage: E.weierstrass_p(prec=30, algorithm='pari')
        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)

        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_pari
        sage: compute_wp_pari(E, prec= 20)
        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)

    """
    ep = E._pari_()
    wpp = ep.ellwp(n=prec)
    k = E.base_ring()
    R = LaurentSeriesRing(k,'z')
    z = R.gen()
    wp = z**(-2)
    for i in xrange(prec):
        wp += k(wpp[i]) * z**i
    wp = wp.add_bigoh(prec)
    return wp
Exemple #2
0
def compute_wp_pari(E, prec):
    r"""
    Computes the Weierstrass `\wp`-function via calling the corresponding function in pari.

    EXAMPLES::
        
        sage: E = EllipticCurve([0,1])
        sage: E.weierstrass_p(algorithm='pari')
        z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + O(z^20)
        
        sage: E = EllipticCurve(GF(101),[5,4])
        sage: E.weierstrass_p(prec=30, algorithm='pari')
        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)

        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_pari
        sage: compute_wp_pari(E, prec= 20)
        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)

    """
    ep = E._pari_()
    wpp = ep.ellwp(n=prec)
    k = E.base_ring()
    R = LaurentSeriesRing(k, 'z')
    z = R.gen()
    wp = z**(-2)
    for i in xrange(prec):
        wp += k(wpp[i]) * z**i
    wp = wp.add_bigoh(prec)
    return wp
Exemple #3
0
def compute_wp_quadratic(k, A, B, prec):
    r"""
    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)`.

    Let p be the characteristic of the underlying field. Then we must
    have either p = 0, or p > prec + 2.

    INPUT:

     - ``k`` - the field of definition of the curve
     - ``A`` - and
     - ``B`` - the coefficients of the elliptic curve
     - ``prec`` - the precision to which we compute the series.

    OUTPUT:
    A Laurent series aproximating the Weierstrass `\wp`-function to precision ``prec``.

    ALGORITHM:
    This function uses the algorithm described in section 3.2 of [BMSS].

    REFERENCES:
    [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies."

    EXAMPLES::

        sage: E = EllipticCurve([7,0])
        sage: E.weierstrass_p(prec=10, algorithm='quadratic')
        z^-2 - 7/5*z^2 + 49/75*z^6 + O(z^10)

        sage: E = EllipticCurve(GF(103),[1,2])
        sage: E.weierstrass_p(algorithm='quadratic')
        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)

        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_quadratic
        sage: compute_wp_quadratic(E.base_ring(), E.a4(), E.a6(), prec=10)
        z^-2 + 41*z^2 + 88*z^4 + 11*z^6 + 57*z^8 + O(z^10)

    """
    m = (prec + 1) // 2
    c = [0 for j in range(m)]
    c[0] = -A / 5
    c[1] = -B / 7

    # first Z represent z^2
    R = LaurentSeriesRing(k, 'z')
    Z = R.gen()
    pe = Z**-1 + c[0] * Z + c[1] * Z**2

    for i in range(3, m):
        t = 0
        for j in range(1, i - 1):
            t += c[j - 1] * c[i - 2 - j]
        ci = (3 * t) / ((i - 2) * (2 * i + 3))
        pe += ci * Z**i
        c[i - 1] = ci

    return pe(Z**2).add_bigoh(prec)
Exemple #4
0
def compute_wp_quadratic(k, A, B, prec):
    r"""
    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)`.

    Let p be the characteristic of the underlying field. Then we must
    have either p = 0, or p > prec + 2.

    INPUT:

     - ``k`` - the field of definition of the curve
     - ``A`` - and
     - ``B`` - the coefficients of the elliptic curve
     - ``prec`` - the precision to which we compute the series.

    OUTPUT:
    A Laurent series aproximating the Weierstrass `\wp`-function to precision ``prec``.

    ALGORITHM:
    This function uses the algorithm described in section 3.2 of [BMSS].

    REFERENCES:
    [BMSS] Boston, Morain, Salvy, Schost, "Fast Algorithms for Isogenies."

    EXAMPLES::

        sage: E = EllipticCurve([7,0])
        sage: E.weierstrass_p(prec=10, algorithm='quadratic')
        z^-2 - 7/5*z^2 + 49/75*z^6 + O(z^10)

        sage: E = EllipticCurve(GF(103),[1,2])
        sage: E.weierstrass_p(algorithm='quadratic')
        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)

        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_quadratic
        sage: compute_wp_quadratic(E.base_ring(), E.a4(), E.a6(), prec=10)
        z^-2 + 41*z^2 + 88*z^4 + 11*z^6 + 57*z^8 + O(z^10)

    """
    m = (prec + 1)//2
    c = [0 for j in range(m)]
    c[0] = -A/5
    c[1] = -B/7

    # first Z represent z^2
    R = LaurentSeriesRing(k,'z')
    Z = R.gen()
    pe = Z**-1 + c[0]*Z + c[1]*Z**2

    for i in range(3, m):
        t = 0
        for j in range(1, i - 1):
            t += c[j-1]*c[i-2-j]
        ci = (3*t)/((i-2)*(2*i+3))
        pe += ci * Z**i
        c[i-1] = ci

    return pe(Z**2).add_bigoh(prec)
Exemple #5
0
    def __init__(self, f, x0, singular_data, order=None):
        r"""Initialize a PuiseuxTSeries using a set of :math:`\pi = \{\tau\}`
        data.

        Parameters
        ----------
        f, x, y : polynomial
            A plane algebraic curve.
        x0 : complex
            The x-center of the Puiseux series expansion.
        singular_data : list
            The output of :func:`singular`.
        t : variable
            The variable in which the Puiseux t series is represented.

        """
        R = f.parent()
        x, y = R.gens()
        extension_polynomial, xpart, ypart = singular_data
        L = LaurentSeriesRing(ypart.base_ring(), 't')
        t = L.gen()

        self.f = f
        self.t = t
        self._xpart = xpart
        self._ypart = ypart

        # store x-part attributes. handle the centered at infinity case
        self.x0 = x0
        if x0 == infinity:
            x0 = QQ(0)
        self.center = x0

        # extract and store information about the x-part of the puiseux series
        xpart = xpart(t, 0)
        xpartshift = xpart - x0
        ramification_index, xcoefficient = xpartshift.laurent_polynomial(
        ).dict().popitem()
        self.xcoefficient = xcoefficient
        self.ramification_index = QQ(ramification_index).numerator()
        self.xpart = xpart

        # extract and store information about the y-part of the puiseux series
        self.ypart = L(ypart(t, 0))
        self._initialize_extension(extension_polynomial)

        # determine the initial order. See the order property
        val = L(ypart(t, O(t))).prec()
        self._singular_order = 0 if val == infinity else val
        self._regular_order = self._p.degree(x)

        # extend to have at least two elements
        self.extend(nterms=1)

        # the curve, x-part, and terms output by puiseux make the puiseux
        # series unique. any mutability only adds terms
        self.__parent = self.ypart.parent()
        self._hash = hash((self.f, self.xpart, self.ypart))
Exemple #6
0
    def __init__(self, f, x0, singular_data, order=None):
        r"""Initialize a PuiseuxTSeries using a set of :math:`\pi = \{\tau\}`
        data.

        Parameters
        ----------
        f, x, y : polynomial
            A plane algebraic curve.
        x0 : complex
            The x-center of the Puiseux series expansion.
        singular_data : list
            The output of :func:`singular`.
        t : variable
            The variable in which the Puiseux t series is represented.

        """
        R = f.parent()
        x,y = R.gens()
        extension_polynomial, xpart, ypart = singular_data
        L = LaurentSeriesRing(ypart.base_ring(), 't')
        t = L.gen()

        self.f = f
        self.t = t
        self._xpart = xpart
        self._ypart = ypart

        # store x-part attributes. handle the centered at infinity case
        self.x0 = x0
        if x0 == infinity:
            x0 = QQ(0)
        self.center = x0

        # extract and store information about the x-part of the puiseux series
        xpart = xpart(t,0)
        xpartshift = xpart - x0
        ramification_index, xcoefficient = xpartshift.laurent_polynomial().dict().popitem()
        self.xcoefficient = xcoefficient
        self.ramification_index = QQ(ramification_index).numerator()
        self.xpart = xpart

        # extract and store information about the y-part of the puiseux series
        self.ypart = L(ypart(t,0))
        self._initialize_extension(extension_polynomial)

        # determine the initial order. See the order property
        val = L(ypart(t,O(t))).prec()
        self._singular_order = 0 if val == infinity else val
        self._regular_order = self._p.degree(x)

        # extend to have at least two elements
        self.extend(nterms=1)

        # the curve, x-part, and terms output by puiseux make the puiseux
        # series unique. any mutability only adds terms
        self.__parent = self.ypart.parent()
        self._hash = hash((self.f, self.xpart, self.ypart))
def _sa_coefficients_lambda_(K, beta=0):
    r"""
    Return the coefficients `\lambda_{k, \ell}(\beta)` used in singularity analysis.

    INPUT:

    - ``K`` -- an integer.

    - ``beta`` -- (default: `0`) the order of the logarithmic
      singularity.

    OUTPUT:

    A dictionary mapping pairs of indices to rationals.

    .. SEEALSO::

        :meth:`~AsymptoticExpansionGenerators.SingularityAnalysis`

    TESTS::

        sage: from sage.rings.asymptotic.asymptotic_expansion_generators \
        ....:     import _sa_coefficients_lambda_
        sage: _sa_coefficients_lambda_(3)
        {(0, 0): 1,
         (1, 1): -1,
         (1, 2): 1/2,
         (2, 2): 1,
         (2, 3): -5/6,
         (2, 4): 1/8,
         (3, 3): -1,
         (3, 4): 13/12,
         (4, 4): 1}
        sage: _sa_coefficients_lambda_(3, beta=1)
        {(0, 0): 1,
         (1, 1): -2,
         (1, 2): 1/2,
         (2, 2): 3,
         (2, 3): -4/3,
         (2, 4): 1/8,
         (3, 3): -4,
         (3, 4): 29/12,
         (4, 4): 5}
    """
    from sage.rings.laurent_series_ring import LaurentSeriesRing
    from sage.rings.power_series_ring import PowerSeriesRing
    from sage.rings.rational_field import QQ

    V = LaurentSeriesRing(QQ, names='v', default_prec=K)
    v = V.gen()
    T = PowerSeriesRing(V, names='t', default_prec=2*K-1)
    t = T.gen()

    S = (t - (1+1/v+beta) * (1+v*t).log()).exp()
    return dict(((k + L.valuation(), ell), c)
                for ell, L in enumerate(S.list())
                for k, c in enumerate(L.list()))
Exemple #8
0
    def local_coordinates_at_infinity(self, prec=20, name='t'):
        """
        For the genus `g` hyperelliptic curve `y^2 = f(x)`, return
        `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is
        the local parameter at infinity

        INPUT:

        - ``prec`` -- desired precision of the local coordinates
        - ``name`` -- generator of the power series ring (default: ``t``)

        OUTPUT:

        `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x^g/y`
        is the local parameter at infinity

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^5-5*x^2+1)
            sage: x,y = H.local_coordinates_at_infinity(10)
            sage: x
            t^-2 + 5*t^4 - t^8 - 50*t^10 + O(t^12)
            sage: y
            t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12)

        ::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^3-x+1)
            sage: x,y = H.local_coordinates_at_infinity(10)
            sage: x
            t^-2 + t^2 - t^4 - t^6 + 3*t^8 + O(t^12)
            sage: y
            t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12)

        AUTHOR:

        - Jennifer Balakrishnan (2007-12)
        """
        g = self.genus()
        pol = self.hyperelliptic_polynomials()[0]
        K = LaurentSeriesRing(self.base_ring(), name, default_prec=prec + 2)
        t = K.gen()
        L = PolynomialRing(K, 'x')
        x = L.gen()
        i = 0
        w = (x**g / t)**2 - pol
        wprime = w.derivative(x)
        if pol.degree() == 2 * g + 1:
            x = t**-2
        else:
            x = t**-1
        for i in range((RR(log(prec + 2) / log(2))).ceil()):
            x = x - w(x) / wprime(x)
        y = x**g / t
        return x + O(t**(prec + 2)), y + O(t**(prec + 2))
def _sa_coefficients_lambda_(K, beta=0):
    r"""
    Return the coefficients `\lambda_{k, \ell}(\beta)` used in singularity analysis.

    INPUT:

    - ``K`` -- an integer.

    - ``beta`` -- (default: `0`) the order of the logarithmic
      singularity.

    OUTPUT:

    A dictionary mapping pairs of indices to rationals.

    .. SEEALSO::

        :meth:`~AsymptoticExpansionGenerators.SingularityAnalysis`

    TESTS::

        sage: from sage.rings.asymptotic.asymptotic_expansion_generators \
        ....:     import _sa_coefficients_lambda_
        sage: _sa_coefficients_lambda_(3)
        {(0, 0): 1,
         (1, 1): -1,
         (1, 2): 1/2,
         (2, 2): 1,
         (2, 3): -5/6,
         (2, 4): 1/8,
         (3, 3): -1,
         (3, 4): 13/12,
         (4, 4): 1}
        sage: _sa_coefficients_lambda_(3, beta=1)
        {(0, 0): 1,
         (1, 1): -2,
         (1, 2): 1/2,
         (2, 2): 3,
         (2, 3): -4/3,
         (2, 4): 1/8,
         (3, 3): -4,
         (3, 4): 29/12,
         (4, 4): 5}
    """
    from sage.rings.laurent_series_ring import LaurentSeriesRing
    from sage.rings.power_series_ring import PowerSeriesRing
    from sage.rings.rational_field import QQ

    V = LaurentSeriesRing(QQ, names='v', default_prec=K)
    v = V.gen()
    T = PowerSeriesRing(V, names='t', default_prec=2 * K - 1)
    t = T.gen()

    S = (t - (1 + 1 / v + beta) * (1 + v * t).log()).exp()
    return dict(((k + L.valuation(), ell), c) for ell, L in enumerate(S.list())
                for k, c in enumerate(L.list()))
    def local_coordinates_at_infinity(self, prec = 20, name = 't'):
        """
        For the genus `g` hyperelliptic curve `y^2 = f(x)`, return
        `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is
        the local parameter at infinity

        INPUT:

        - ``prec`` -- desired precision of the local coordinates
        - ``name`` -- generator of the power series ring (default: ``t``)

        OUTPUT:

        `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x^g/y`
        is the local parameter at infinity

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^5-5*x^2+1)
            sage: x,y = H.local_coordinates_at_infinity(10)
            sage: x
            t^-2 + 5*t^4 - t^8 - 50*t^10 + O(t^12)
            sage: y
            t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12)

        ::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^3-x+1)
            sage: x,y = H.local_coordinates_at_infinity(10)
            sage: x
            t^-2 + t^2 - t^4 - t^6 + 3*t^8 + O(t^12)
            sage: y
            t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12)

        AUTHOR:

        - Jennifer Balakrishnan (2007-12)
        """
        g = self.genus()
        pol = self.hyperelliptic_polynomials()[0]
        K = LaurentSeriesRing(self.base_ring(), name, default_prec=prec+2)
        t = K.gen()
        L = PolynomialRing(K,'x')
        x = L.gen()
        i = 0
        w = (x**g/t)**2-pol
        wprime = w.derivative(x)
        x = t**-2
        for i in range((RR(log(prec+2)/log(2))).ceil()):
            x = x - w(x)/wprime(x)
        y = x**g/t
        return x+O(t**(prec+2)) , y+O(t**(prec+2))
Exemple #11
0
    def test_LaurentSeries_V(self):
        L = LaurentSeriesRing(QQ, 't')
        t = L.gen()

        l = 1 * t**(-3) + 2 + 3 * t**1 + 4 * t**2 + 5 * t**9
        m = LaurentSeries_V(l, 1)
        self.assertEqual(l, m)

        m = LaurentSeries_V(l, 2)
        self.assertEqual(m.exponents(), [-6, 0, 2, 4, 18])
        self.assertEqual(m.coefficients(), [1, 2, 3, 4, 5])

        m = LaurentSeries_V(l, -1)
        self.assertEqual(m.exponents(), [-9, -2, -1, 0, 3])
        self.assertEqual(m.coefficients(), [5, 4, 3, 2, 1])

        m = LaurentSeries_V(l, -3)
        self.assertEqual(m.exponents(), [-27, -6, -3, 0, 9])
        self.assertEqual(m.coefficients(), [5, 4, 3, 2, 1])
    def test_LaurentSeries_V(self):
        L = LaurentSeriesRing(QQ,'t')
        t = L.gen()

        l = 1*t**(-3) + 2 + 3*t**1 + 4*t**2 + 5*t**9
        m = LaurentSeries_V(l,1)
        self.assertEqual(l, m)

        m = LaurentSeries_V(l, 2)
        self.assertEqual(m.exponents(), [-6,0,2,4,18])
        self.assertEqual(m.coefficients(), [1,2,3,4,5])

        m = LaurentSeries_V(l, -1)
        self.assertEqual(m.exponents(), [-9,-2,-1,0,3])
        self.assertEqual(m.coefficients(), [5,4,3,2,1])

        m = LaurentSeries_V(l, -3)
        self.assertEqual(m.exponents(), [-27,-6,-3,0,9])
        self.assertEqual(m.coefficients(), [5,4,3,2,1])
Exemple #13
0
def compute_wp_pari(E,prec):
    r"""
    Computes the Weierstrass `\wp`-function with the ``ellwp`` function
    from PARI.

    EXAMPLES::

        sage: E = EllipticCurve([0,1])
        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_pari
        sage: compute_wp_pari(E, prec=20)
        z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + O(z^20)
        sage: compute_wp_pari(E, prec=30)
        z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + 3/38548055*z^22 - 4/8364927935*z^28 + O(z^30)
    """
    ep = E._pari_()
    wpp = ep.ellwp(n=prec)
    k = E.base_ring()
    R = LaurentSeriesRing(k,'z')
    z = R.gen()
    wp = z**(-2)
    for i in range(prec):
        wp += k(wpp[i]) * z**i
    wp = wp.add_bigoh(prec)
    return wp
Exemple #14
0
def compute_wp_pari(E, prec):
    r"""
    Computes the Weierstrass `\wp`-function with the ``ellwp`` function
    from PARI.

    EXAMPLES::

        sage: E = EllipticCurve([0,1])
        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_pari
        sage: compute_wp_pari(E, prec=20)
        z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + O(z^20)
        sage: compute_wp_pari(E, prec=30)
        z^-2 - 1/7*z^4 + 1/637*z^10 - 1/84721*z^16 + 3/38548055*z^22 - 4/8364927935*z^28 + O(z^30)
    """
    ep = E.__pari__()
    wpp = ep.ellwp(n=prec)
    k = E.base_ring()
    R = LaurentSeriesRing(k, 'z')
    z = R.gen()
    wp = z**(-2)
    for i in range(prec):
        wp += k(wpp[i]) * z**i
    wp = wp.add_bigoh(prec)
    return wp
Exemple #15
0
    def coleman(self,t1,t2,E=None,method='moments',mult=False,delta=-1,level=0):
        r"""
        If ``self`` is a `p`-adic automorphic form that corresponds to a rigid modular form,
        then this computes the coleman integral of this form between two points on
        the boundary `\PP^1(\QQ_p)` of the `p`-adic upper half plane.

        INPUT:

         - ``t1``, ``t2`` - elements of `\PP^1(\QQ_p)` (the endpoints of integration)

         - ``E`` - (Default: None). If specified, will not compute the covering adapted
            to ``t1`` and ``t2`` and instead use the given one. In that case, ``E``
            should be a list of matrices corresponding to edges describing the open
            balls to be considered.

         - ``method`` - string (Default: 'moments'). Tells which algorithm to use 
           (alternative is 'riemann_sum', which is unsuitable for computations 
           requiring high precision)

         - ``mult`` - boolean (Default: False). Whether to use the multiplicative version.

         - ``delta`` - integer (Default: -1)

         - ``level`` - integer (Default: 0)

        OUTPUT:

          The result of the coleman integral

        EXAMPLES:

        This example illustrates ...

        ::

        TESTS:

        ::
            sage: p = 7
            sage: lev = 2
            sage: prec = 20
            sage: X = BTQuotient(p,lev, use_magma = True) # optional - magma
            sage: k = 2 # optional - magma
            sage: M = HarmonicCocycles(X,k,prec) # optional - magma
            sage: B = M.basis() # optional - magma
            sage: f = 3*B[0] # optional - magma
            sage: MM = pAutomorphicForms(X,k,prec,overconvergent=True) # optional - magma
            sage: D = -11 # optional - magma
            sage: X.is_admissible(D) # optional - magma
            True
            sage: K.<a> = QuadraticField(D) # optional - magma
            sage: CM = X.get_CM_points(D,prec=prec) # optional - magma
            sage: Kp = CM[0].parent() # optional - magma
            sage: P=CM[0] # optional - magma
            sage: Q=P.trace()-P # optional - magma
            sage: F=MM.lift(f, verbose = False) # long time optional - magma
            sage: J0=F.coleman(P,Q,mult=True) # long time optional - magma
            sage: E=EllipticCurve([1,0,1,4,-6]) # optional - magma
            sage: T=E.tate_curve(p) # optional - magma
            sage: xx,yy=getcoords(T,J0,prec) # long time optional -magma
            sage: P = E.base_extend(K).lift_x(algdep(xx,1).roots(QQ)[0][0]); P # long time optional - magma
            (7/11 : 58/121*a - 9/11 : 1)

            sage: p = 13 # optional - magma
            sage: lev = 2 # optional - magma
            sage: prec = 20 # optional - magma
            sage: Y = BTQuotient(p,lev, use_magma = True) # optional - magma
            sage: k = 2 # optional - magma
            sage: M=HarmonicCocycles(Y,k,prec) # optional - magma
            sage: B=M.basis() # optional - magma

            sage: f = B[1] # optional - magma
            sage: g = -4*B[0]+3*B[1] # optional - magma
            sage: MM = pAutomorphicForms(Y,k,prec,overconvergent=True) # optional - magma
            sage: D = -11 # optional - magma
            sage: Y.is_admissible(D) # optional - magma
            True
            sage: K.<a> = QuadraticField(D) # optional - magma
            sage: CM = Y.get_CM_points(D,prec=prec) # optional - magma
            sage: Kp = parent(CM[0]) # optional - magma
            sage: P = CM[0] # optional - magma
            sage: Q = P.trace()-P # optional - magma
            sage: F = MM.lift(f, verbose = False) # long time optional - magma
            sage: J11 = F.coleman(P,Q,mult = True) # long time optional - magma
            sage: E = EllipticCurve('26a2') # optional - magma
            sage: T = E.tate_curve(p) # optional - magma
            sage: xx,yy = getcoords(T,J11,prec) # long time optional - magma
            sage: HP = E.base_extend(K).lift_x(algdep(xx,1).roots(QQ)[0][0]); HP # long time optional - magma
            (-137/11 : 2/121*a + 63/11 : 1)

        AUTHORS:

        - Cameron Franc (2012-02-20)
        - Marc Masdeu (2012-02-20)
        """
        if(mult and delta>=0):
            raise NotImplementedError, "Need to figure out how to implement the multiplicative part."
        p=self._parent._X._p
        K=t1.parent()
        R=PolynomialRing(K,'x')
        x=R.gen()
        R1=LaurentSeriesRing(K,'r1')
        r1=R1.gen()
        if(E is None):
            E=self._parent._X._BT.find_covering(t1,t2)
            # print 'Got %s open balls.'%len(E)
        value=0
        ii=0
        value_exp=K(1)
        if(method=='riemann_sum'):
            R1.set_default_prec(self._parent._U.weight()+1)
            for e in E:
                ii+=1
                b=e[0,1]
                d=e[1,1]
                y=(b-d*t1)/(b-d*t2)
                poly=R1(y.log()) #R1(our_log(y))
                c_e=self.evaluate(e)
                new=c_e.evaluate(poly)
                value+=new
                if mult:
                    value_exp *= K.teichmuller(y)**Integer(c_e[0].rational_reconstruction())

        elif(method=='moments'):
            R1.set_default_prec(self._parent._U.dimension())
            for e in E:
                ii+=1
                f=(x-t1)/(x-t2)
                a,b,c,d=e.list()
                y0=f(R1([b,a])/R1([d,c])) #f( (ax+b)/(cx+d) )
                y0=p**(-y0(0).valuation())*y0
                mu=K.teichmuller(y0(0))
                y=y0/mu-1
                poly=R1(0)
                ypow=y
                for jj in range(1,R1.default_prec()+10):
                    poly+=(-1)**(jj+1)*ypow/jj
                    ypow*=y
                if(delta>=0):
                    poly*=((r1-t1)**delta*(r1-t2)**(self._parent._n-delta))
                c_e=self.evaluate(e)
                new=c_e.evaluate(poly)
                value+=new
                if mult:
                    value_exp *= K.teichmuller((b-d*t1)/(b-d*t2))**Integer(c_e[0].rational_reconstruction())

        else:
            print 'The available methods are either "moments" or "riemann_sum". The latter is only provided for consistency check, and should not be used in practice.'
            return False
        if mult:
            return value_exp * value.exp()
        return value
    def coleman(self,t1,t2,poly = None, E = None,method = 'moments',mult = False,level = 0, branch = 0):
        r"""
        If ``self`` is a `p`-adic automorphic form that corresponds to a rigid modular form,
        then this computes the coleman integral of this form between two points on
        the boundary `\PP^1(\QQ_p)` of the `p`-adic upper half plane.

        INPUT:

        - ``t1``, ``t2`` - elements of `\PP^1(\QQ_p)` (the endpoints of integration)

        - ``E`` - (Default: None). If specified, will not compute the covering adapted
           to ``t1`` and ``t2`` and instead use the given one. In that case, ``E``
           should be a list of matrices corresponding to edges describing the open
           balls to be considered.

        - ``method`` - string (Default: 'moments'). Tells which algorithm to use
          (alternative is 'riemann_sum', which is unsuitable for computations
          requiring high precision)

        - ``mult`` - boolean (Default: False). Whether to use the multiplicative version.

        - ``poly`` - polynomial (Default: None)

        - ``level`` - integer (Default: 0)

        OUTPUT:

          The result of the coleman integral

        EXAMPLES::

            sage: p = 7
            sage: lev = 2
            sage: prec = 20
            sage: X = BTQuotient(p,lev, use_magma = True) # optional - magma
            sage: k = 2 # optional - magma
            sage: M = HarmonicCocycles(X,k,prec) # optional - magma
            sage: B = M.basis() # optional - magma
            sage: f = 3*M.decomposition()[0].gen(0) # optional - magma
            sage: MM = pAutomorphicForms(X,k,prec,overconvergent = True) # optional - magma
            sage: D = -11 # optional - magma
            sage: X.is_admissible(D) # optional - magma
            True
            sage: K.<a> = QuadraticField(D) # optional - magma
            sage: CM = X.get_CM_points(D,prec = prec) # optional - magma
            sage: P = CM[0] # optional - magma
            sage: Q = P.trace()-P # optional - magma
            sage: F = MM.lift(f, verbose = False) # long time optional - magma
            sage: J0 = F.coleman(P,Q,mult = True) # long time optional - magma
            sage: E = EllipticCurve([1,0,1,4,-6]) # optional - magma
            sage: T = E.tate_curve(p) # optional - magma
            sage: xx,yy = getcoords(T,J0,prec) # long time optional -magma
            sage: P = E.base_extend(K).lift_x(algdep(xx,1).roots(QQ)[0][0]); P # long time optional - magma
            (7/11 : 58/121*a - 9/11 : 1)

            sage: p = 13 # optional - magma
            sage: lev = 2 # optional - magma
            sage: prec = 20 # optional - magma
            sage: Y = BTQuotient(p,lev, use_magma = True) # optional - magma
            sage: k = 2 # optional - magma
            sage: M = HarmonicCocycles(Y,k,prec) # optional - magma
            sage: B = M.basis() # optional - magma
            sage: f = M.decomposition()[1].gen(0) # optional - magma
            sage: g = M.decomposition()[0].gen(0) # optional - magma
            sage: MM = pAutomorphicForms(Y,k,prec,overconvergent = True) # optional - magma
            sage: D = -11 # optional - magma
            sage: Y.is_admissible(D) # optional - magma
            True
            sage: K.<a> = QuadraticField(D) # optional - magma
            sage: CM = Y.get_CM_points(D,prec = prec) # optional - magma
            sage: P = CM[0] # optional - magma
            sage: Q = P.trace()-P # optional - magma
            sage: F = MM.lift(f, verbose = False) # long time optional - magma
            sage: J11 = F.coleman(P,Q,mult = True) # long time optional - magma
            sage: E = EllipticCurve('26a2') # optional - magma
            sage: T = E.tate_curve(p) # optional - magma
            sage: xx,yy = getcoords(T,J11,prec) # long time optional - magma
            sage: HP = E.base_extend(K).lift_x(algdep(xx,1).roots(QQ)[0][0]); HP # long time optional - magma
            (-137/11 : 2/121*a + 63/11 : 1)

        AUTHORS:

        - Cameron Franc (2012-02-20)
        - Marc Masdeu (2012-02-20)
        """
        p = self.parent().prime()
        K = t1.parent()
        if mult:
            branch = 0

        R = PolynomialRing(K,'x')
        x = R.gen()
        R1 = LaurentSeriesRing(K,'r1')
        r1 = R1.gen()
        if E is None:
            E = self.parent()._source._BT.find_covering(t1,t2,level)
            print 'Got %s open balls.'%len(E)
        value = K(0)
        ii = 0
        value_exp = K(1)
        wt = self.parent()._U.weight()
        dim = self.parent()._U.dimension()
        if(method == 'riemann_sum'):
            R1.set_default_prec(wt+1)
            for e in E:
                ii += 1
                b = e[0,1]
                d = e[1,1]
                y = (b-d*t1)/(b-d*t2)
                pol = R1(y.log(branch = branch))
                c_e = self.evaluate(e)
                new = c_e.evaluate(pol)
                value += new
                if mult:
                    value_exp  *=  K.teichmuller(y)**Integer(c_e[0].rational_reconstruction())

        elif method == 'moments':
            R1.set_default_prec(dim)
            for e in E:
                ii += 1
                f = (x-t1)/(x-t2)
                a,b,c,d = e.list()
                y0 = f((a*r1+b)/(c*r1+d))
                pol = y0(0).log(branch = branch)+((y0.derivative()/y0).integral())
                if poly is not None:
                    pol *= poly(r1) #((r1-t1)**delta*(r1-t2)**(self.parent()._n-delta))
                c_e = self.evaluate(e)
                new = c_e.evaluate(pol)
                value += new
                if mult:
                    base = ((b-d*t1)/(b-d*t2))
                    pwr = ZZ(c_e[0].rational_reconstruction())
                    value_exp  *=  base**pwr
        else:
            print 'The available methods are either "moments" or "riemann_sum". The latter is only provided for consistency check, and should not be used in practice.'
            return False
        if mult:
            a = value_exp.valuation(p)
            print 'a=',a
            return p**a*K.teichmuller(p**(-a)*value_exp) * value.exp(prec = 2*dim) +O(p**(dim+a))
        return value +O(p**(dim+value.valuation(p)))
def epsinv(F, target, prec=53, target_tol=0.001, z=None, emb=None):
    """
    Compute a bound on the hyperbolic distance.

    The true minimum will be within the computed bound.
    It is computed as the inverse of epsilon_F from [HS2018]_.
    
    INPUT:

    - ``F`` -- binary form of degree at least 3 with no multiple roots

    - ``target`` --  positive real number. The value we want to attain, i.e.,
      the value we are taking the inverse of

    - ``prec``-- positive integer. precision to use in CC

    - ``target_tol`` -- positive real number. The tolerance with which we
      attain the target value.

    - ``z`` -- complex number. ``z_0`` covariant for F.

    - ``emb`` -- embedding into CC

    OUTPUT: a real number delta satisfying  target + target_tol > eps_F(delta) > target.

    EXAMPLES::

        sage: from sage.rings.polynomial.binary_form_reduce import epsinv
        sage: R.<x,y> = QQ[]
        sage: epsinv(-2*x^3 + 2*x^2*y + 3*x*y^2 + 127*y^3, 31.5022020249597) # tol 1e-12
        4.02520895942207       
    """
    def coshdelta(z):
        #The cosh of the hyperbolic distance from z = t+uj to j
        return (z.norm() + 1) / (2 * z.imag())

    def RQ(delta):
        # this is the quotient R(F_0,z)/R(F_0,z(F)) for a generic z
        # at distance delta from j. See Lemma 4.2 in [HS2018].
        cd = cosh(delta).n(prec=prec)
        sd = sinh(delta).n(prec=prec)
        return prod(
            [cd + (cost * phi[0] + sint * phi[1]) * sd for phi in phis])

    def epsF(delta):
        pol = RQ(delta)  #get R quotient in terms of z
        S = PolynomialRing(C, 'v')
        g = S([(i - d) * pol[i - d]
               for i in range(2 * d + 1)])  # take derivative
        drts = [
            e for e in g.roots(ring=C, multiplicities=False)
            if (e.norm() - 1).abs() < 0.1
        ]
        # find min
        return min([pol(r / r.abs()).real() for r in drts])

    C = ComplexField(prec=prec)
    R = F.parent()
    d = F.degree()
    if z is None:
        z, th = covariant_z0(F, prec=prec, emb=emb)
    else:  #need to do our own input checking
        if R.ngens() != 2 or any(sum(t) != d for t in F.exponents()):
            raise TypeError('must be a binary form')
        if d < 3:
            raise ValueError('must be at least degree 3')

    f = F.subs({R.gen(1): 1}).univariate_polynomial()
    #now we have a single variable polynomial
    if (max([ex for p,ex in f.roots(ring=C)]) >= QQ(d)/2)\
      or (f.degree() < QQ(d)/2):
        raise ValueError('cannot have root with multiplicity >= deg(F)/2')

    R = RealField(prec=prec)
    PR = PolynomialRing(R, 't')
    t = PR.gen(0)
    # compute phi_1, ..., phi_k
    # first find F_0 and its roots
    # this change of variables on f moves z(f) to j, i.e. produces F_0
    rts = f(z.imag() * t + z.real()).roots(ring=C)
    phis = []  # stereographic projection of roots
    for r, e in rts:
        phis.extend(
            [[2 * r.real() / (r.norm() + 1), (r.norm() - 1) / (r.norm() + 1)]])
    if d != f.degree():  # include roots at infinity
        phis.extend([(d - f.degree()) * [0, 1]])

    # for writing RQ in terms of generic z to minimize
    LC = LaurentSeriesRing(C, 'u', default_prec=2 * d + 2)
    u = LC.gen(0)
    cost = (u + u**(-1)) / 2
    sint = (u - u**(-1)) / (2 * C.gen(0))

    # first find an interval containing the desired value
    # then use regula falsi on log eps_F
    # d -> delta value in interval [0,1]
    # v in value in interval [1,epsF(1)]
    dl = R(0.0)
    vl = R(1.0)
    du = R(1.0)
    vu = epsF(du)
    while vu < target:
        # compute the next value of epsF for delta = 2*delta
        dl = du
        vl = vu
        du *= 2
        vu = epsF(du)
    # now dl < delta <= du
    logt = target.log()
    l2 = (vu.log() - logt).n(prec=prec)
    l1 = (vl.log() - logt).n(prec=prec)
    dn = (dl * l2 - du * l1) / (l2 - l1)
    vn = epsF(dn)
    dl = du
    vl = vu
    du = dn
    vu = vn
    while (du - dl).abs() >= target_tol or max(vl, vu) < target:
        l2 = (vu.log() - logt).n(prec=prec)
        l1 = (vl.log() - logt).n(prec=prec)
        dn = (dl * l2 - du * l1) / (l2 - l1)
        vn = epsF(dn)
        dl = du
        vl = vu
        du = dn
        vu = vn
    return max(dl, du)