Exemplo n.º 1
0
def zeta__exact(n):
    r"""
    Returns the exact value of the Riemann Zeta function

    The argument must be a critical value, namely either positive even
    or negative odd.

    See for example [Iwa1972]_, p13, Special value of `\zeta(2k)`

    EXAMPLES:

    Let us test the accuracy for negative special values::

        sage: RR = RealField(100)
        sage: for i in range(1,10):
        ....:     print("zeta({}): {}".format(1-2*i, RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i))))
        zeta(-1): 0.00000000000000000000000000000
        zeta(-3): 0.00000000000000000000000000000
        zeta(-5): 0.00000000000000000000000000000
        zeta(-7): 0.00000000000000000000000000000
        zeta(-9): 0.00000000000000000000000000000
        zeta(-11): 0.00000000000000000000000000000
        zeta(-13): 0.00000000000000000000000000000
        zeta(-15): 0.00000000000000000000000000000
        zeta(-17): 0.00000000000000000000000000000

    Let us test the accuracy for positive special values::

        sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10))
        True

    TESTS::

        sage: zeta__exact(4)
        1/90*pi^4
        sage: zeta__exact(-3)
        1/120
        sage: zeta__exact(0)
        -1/2
        sage: zeta__exact(5)
        Traceback (most recent call last):
        ...
        TypeError: n must be a critical value (i.e. even > 0 or odd < 0)

    REFERENCES:

    - [Iwa1972]_
    - [IR1990]_
    - [Was1997]_
    """
    if n < 0:
        return bernoulli(1 - n) / (n - 1)
    elif n > 1:
        if (n % 2 == 0):
            return ZZ(-1)**(n // 2 + 1) * ZZ(2)**(
                n - 1) * pi**n * bernoulli(n) / factorial(n)
        else:
            raise TypeError(
                "n must be a critical value (i.e. even > 0 or odd < 0)")
    elif n == 1:
        return infinity
    elif n == 0:
        return QQ((-1, 2))
def local_badI_density_congruence(self, p, m, Zvec=None, NZvec=None):
    """
    Finds the Bad-type I local density of Q representing `m` at `p`.
    (Assuming that p > 2 and Q is given in local diagonal form.)


    INPUT:
        Q -- quadratic form assumed to be block diagonal and `p`-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:
        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_badI_density_congruence(2, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(2, 2, None, None)
        1
        sage: Q.local_badI_density_congruence(2, 4, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 9, None, None)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_badI_density_congruence(2, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(2, 2, None, None)
        0
        sage: Q.local_badI_density_congruence(2, 4, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 2, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 9, None, None)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,3,9])
        sage: Q.local_badI_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 3, None, None)
        4/3
        sage: Q.local_badI_density_congruence(3, 6, None, None)
        4/3
        sage: Q.local_badI_density_congruence(3, 9, None, None)
        0
        sage: Q.local_badI_density_congruence(3, 18, None, None)
        0


    """
    ## DIAGNOSTIC
    verbose(" In local_badI_density_congruence with ")
    verbose(" Q is: \n" + str(self))
    verbose(" p = " + str(p))
    verbose(" m = " + str(m))
    verbose(" Zvec = " + str(Zvec))
    verbose(" NZvec = " + str(NZvec))

    ## Put the Zvec congruence condition in a standard form
    if Zvec == None:
        Zvec = []


    n = self.dim()



    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec != None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec != None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")



    ## Define the indexing set S_0, and determine if S_1 is empty:
    ## -----------------------------------------------------------
    S0 = []
    S1_empty_flag = True    ## This is used to check if we should be computing BI solutions at all!
                            ## (We should really to this earlier, but S1 must be non-zero to proceed.)

    ## Find the valuation of each variable (which will be the same over 2x2 blocks),
    ## remembering those of valuation 0 and if an entry of valuation 1 exists.
    for i in range(n):

        ## Compute the valuation of each index, allowing for off-diagonal terms
        if (self[i,i] == 0):
            if (i == 0):
                val = valuation(self[i,i+1], p)    ## Look at the term to the right
            else:
                if (i == n-1):
                    val = valuation(self[i-1,i], p)    ## Look at the term above
                else:
                    val = valuation(self[i,i+1] + self[i-1,i], p)    ## Finds the valuation of the off-diagonal term since only one isn't zero
        else:
            val = valuation(self[i,i], p)

        if (val == 0):
            S0 += [i]
        elif (val == 1):
            S1_empty_flag = False    ## Need to have a non-empty S1 set to proceed with Bad-type I reduction...





    ## Check that S1 is non-empty and p|m to proceed, otherwise return no solutions.
    if (S1_empty_flag == True) or (m % p != 0):
        return 0

    ## Check some conditions for no bad-type I solutions to exist
    if (NZvec != None) and (len(Set(S0).intersection(Set(NZvec))) != 0):
        return 0



    ## Check that the form is primitive...                     WHY DO WE NEED TO DO THIS?!?
    if (S0 == []):
        print " Using Q = " + str(self)
        print " and p = " + str(p)
        raise RuntimeError("Oops! The form is not primitive!")



    ## DIAGNOSTIC
    verbose(" m = " + str(m) + "   p = " + str(p))
    verbose(" S0 = " + str(S0))
    verbose(" len(S0) = " + str(len(S0)))



    ## Make the form Qnew for the reduction procedure:
    ## -----------------------------------------------
    Qnew = deepcopy(self)                                          ## TO DO:  DO THIS WITHOUT A copy(). =)
    for i in range(n):
        if i in S0:
            Qnew[i,i] = p * Qnew[i,i]
            if ((p == 2) and (i < n-1)):
                Qnew[i,i+1] = p * Qnew[i,i+1]
        else:
            Qnew[i,i] = Qnew[i,i] / p
            if ((p == 2) and (i < n-1)):
                Qnew[i,i+1] = Qnew[i,i+1] / p



    ## DIAGNOSTIC
    verbose("\n\n Check of Bad-type I reduction: \n")
    verbose(" Q is " + str(self))
    verbose(" Qnew is " + str(Qnew))
    verbose(" p = " + str(p))
    verbose(" m / p = " + str(m/p))
    verbose(" NZvec " + str(NZvec))



    ## Do the reduction
    Zvec_geq_1 = list(Set([i  for i in Zvec  if i not in S0]))
    if NZvec == None:
        NZvec_geq_1 = NZvec
    else:
        NZvec_geq_1 = list(Set([i  for i in NZvec  if i not in S0]))

    return QQ(p**(1 - len(S0))) * Qnew.local_good_density_congruence(p, m / p, Zvec_geq_1, NZvec_geq_1)
def local_badII_density_congruence(self, p, m, Zvec=None, NZvec=None):
    """
    Finds the Bad-type II local density of Q representing `m` at `p`.
    (Assuming that `p` > 2 and Q is given in local diagonal form.)


     INPUT:
        Q -- quadratic form assumed to be block diagonal and p-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:
        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_badII_density_congruence(2, 1, None, None)
        0
        sage: Q.local_badII_density_congruence(2, 2, None, None)
        0
        sage: Q.local_badII_density_congruence(2, 4, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 9, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 27, None, None)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,3,9,9])
        sage: Q.local_badII_density_congruence(3, 1, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 3, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 6, None, None)
        0
        sage: Q.local_badII_density_congruence(3, 9, None, None)
        4/27
        sage: Q.local_badII_density_congruence(3, 18, None, None)
        4/9

    """
    ## DIAGNOSTIC
    verbose(" In local_badII_density_congruence with ")
    verbose(" Q is: \n" + str(self))
    verbose(" p = " + str(p))
    verbose(" m = " + str(m))
    verbose(" Zvec = " + str(Zvec))
    verbose(" NZvec = " + str(NZvec))

    ## Put the Zvec congruence condition in a standard form
    if Zvec == None:
        Zvec = []


    n = self.dim()


    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec != None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec != None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")


    ## Define the indexing sets S_i:
    ## -----------------------------
    S0 = []
    S1 = []
    S2plus = []

    for i in range(n):

        ## Compute the valuation of each index, allowing for off-diagonal terms
        if (self[i,i] == 0):
            if (i == 0):
                val = valuation(self[i,i+1], p)    ## Look at the term to the right
            elif (i == n-1):
                val = valuation(self[i-1,i], p)    ## Look at the term above
            else:
                val = valuation(self[i,i+1] + self[i-1,i], p)    ## Finds the valuation of the off-diagonal term since only one isn't zero
        else:
            val = valuation(self[i,i], p)

        ## Sort the indices into disjoint sets by their valuation
        if (val == 0):
            S0 += [i]
        elif (val == 1):
            S1 += [i]
        elif (val >= 2):
            S2plus += [i]



    ## Check that S2 is non-empty and p^2 divides m to proceed, otherwise return no solutions.
    p2 = p * p
    if (S2plus == []) or (m % p2 != 0):
        return 0




    ## Check some conditions for no bad-type II solutions to exist
    if (NZvec != None) and (len(Set(S2plus).intersection(Set(NZvec))) == 0):
        return 0



    ## Check that the form is primitive...                     WHY IS THIS NECESSARY?
    if (S0 == []):
        print " Using Q = " + str(self)
        print " and p = " + str(p)
        raise RuntimeError("Oops! The form is not primitive!")




    ## DIAGNOSTIC
    verbose("\n Entering BII routine ")
    verbose(" S0 is " + str(S0))
    verbose(" S1 is " + str(S1))
    verbose(" S2plus is " + str(S2plus))
    verbose(" m = " + str(m) + "   p = " + str(p))





    ## Make the form Qnew for the reduction procedure:
    ## -----------------------------------------------
    Qnew = deepcopy(self)                                          ## TO DO:  DO THIS WITHOUT A copy(). =)
    for i in range(n):
        if i in S2plus:
            Qnew[i,i] = Qnew[i,i] / p2
            if (p == 2) and (i < n-1):
                Qnew[i,i+1] = Qnew[i,i+1] / p2

    ## DIAGNOSTIC
    verbose("\n\n Check of Bad-type II reduction: \n")
    verbose(" Q is " + str(self))
    verbose(" Qnew is " + str(Qnew))



    ## Perform the reduction formula
    Zvec_geq_2 = list(Set([i  for i in Zvec  if i in S2plus]))
    if NZvec == None:
        NZvec_geq_2 = NZvec
    else:
        NZvec_geq_2 = list(Set([i  for i in NZvec  if i in S2plus]))

    return QQ(p**(len(S2plus) + 2 - n)) \
        * (Qnew.local_density_congruence(p, m / p2, Zvec_geq_2, NZvec_geq_2) \
        - Qnew.local_density_congruence(p, m / p2, S2plus , NZvec_geq_2))
Exemplo n.º 4
0
from sage.rings.rational_field import QQ
from ore_algebra import DifferentialOperators

IVP = collections.namedtuple("IVP", ["dop", "ini"])

DiffOps_a, a, Da = DifferentialOperators(QQ, 'a')
koutschan1 = IVP(
    dop=(1315013644371957611900 * a**2 + 263002728874391522380 * a +
         13150136443719576119) * Da**3 +
    (2630027288743915223800 * a**2 + 16306169190212274387560 * a +
     1604316646133788286518) * Da**2 +
    (1315013644371957611900 * a**2 - 39881765316802329075320 * a +
     35449082663034775873349) * Da +
    (-278967152068515080896550 + 6575068221859788059500 * a),
    ini=[
        QQ(5494216492395559) / 3051757812500000000000000000000,
        QQ(6932746783438351) / 610351562500000000000000000000,
        1 / QQ(2) * QQ(1142339612827789) / 19073486328125000000000000000
    ])

y, z = PolynomialRing(QQ, ['y', 'z']).gens()
salvy1_pol = (16 * y**6 * z**2 + 8 * y**5 * z**3 + y**4 * z**4 +
              128 * y**5 * z**2 + 48 * y**4 * z**3 + 4 * y**3 * z**4 +
              32 * y**5 * z + 372 * y**4 * z**2 + 107 * y**3 * z**3 +
              6 * y**2 * z**4 + 88 * y**4 * z + 498 * y**3 * z**2 +
              113 * y**2 * z**3 + 4 * y * z**4 + 16 * y**4 + 43 * y**3 * z +
              311 * y**2 * z**2 + 57 * y * z**3 + z**4 + 24 * y**3 -
              43 * y**2 * z + 72 * y * z**2 + 11 * z**3 + 12 * y**2 -
              30 * y * z - z**2 + 2 * y)
DiffOps_z, z, Dz = DifferentialOperators(QQ, 'z')
salvy1_dop = (
def Kitaoka_mass_at_2(self):
    """
    Returns the local mass of the quadratic form when `p=2`, according
    to Theorem 5.6.3 on pp108--9 of Kitaoka's Book "The Arithmetic of
    Quadratic Forms".

    INPUT:
        none

    OUTPUT:
        a rational number > 0

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1])
        sage: Q.Kitaoka_mass_at_2()   ## WARNING:  WE NEED TO CHECK THIS CAREFULLY!
        1/2

    """
    ## Make a 0-dim'l quadratic form (for initialization purposes)
    Null_Form = copy.deepcopy(self)
    Null_Form.__init__(ZZ, 0)

    ## Step 0: Compute Jordan blocks and bounds of the scales to keep track of
    Jordan_Blocks = self.jordan_blocks_by_scale_and_unimodular(2)
    scale_list = [B[0] for B in Jordan_Blocks]
    s_min = min(scale_list)
    s_max = max(scale_list)

    ## Step 1: Compute dictionaries of the diagonal block and 2x2 block for each scale
    diag_dict = dict(
        (i, Null_Form)
        for i in range(s_min - 2, s_max + 4))  ## Initialize with the zero form
    dim2_dict = dict(
        (i, Null_Form)
        for i in range(s_min, s_max + 4))  ## Initialize with the zero form
    for (s, L) in Jordan_Blocks:
        i = 0
        while (i < L.dim() - 1) and (L[i, i + 1]
                                     == 0):  ## Find where the 2x2 blocks start
            i = i + 1
        if i < (L.dim() - 1):
            diag_dict[s] = L.extract_variables(range(i))  ## Diagonal Form
            dim2_dict[s + 1] = L.extract_variables(range(
                i, L.dim()))  ## Non-diagonal Form
        else:
            diag_dict[s] = L

    #print "diag_dict = ", diag_dict
    #print "dim2_dict = ", dim2_dict
    #print "Jordan_Blocks = ", Jordan_Blocks

    ##################  START EDITING HERE  ##################

    ## Compute q := sum of the q_j
    q = 0
    for j in range(s_min, s_max + 1):
        if diag_dict[j].dim(
        ) > 0:  ## Check that N_j is odd (i.e. rep'ns an odd #)
            if diag_dict[j + 1].dim() == 0:
                q += Jordan_Blocks[j][1].dim(
                )  ## When N_{j+1} is "even", add n_j
            else:
                q += Jordan_Blocks[j][1].dim(
                ) + 1  ## When N_{j+1} is "odd", add n_j + 1

    ## Compute P = product of the P_j
    P = QQ(1)
    for j in range(s_min, s_max + 1):
        tmp_m = dim2_dict[j].dim() / 2
        P *= prod([QQ(1) - QQ(4**(-k)) for j in range(1, tmp_m + 1)])

    ## Compute the product E := prod_j (1 / E_j)
    E = QQ(1)
    for j in range(s_min - 1, s_max + 2):
        if (diag_dict[j-1].dim() == 0) and (diag_dict[j+1].dim() == 0) and \
           ((diag_dict[j].dim() != 2) or (((diag_dict[j][0,0] - diag_dict[j][1,1]) % 4) != 0)):

            ## Deal with the complicated case:
            tmp_m = dim2_dict[j].dim() / 2
            if dim2_dict[j].is_hyperbolic(2):
                E *= 2 / (1 + 2**(-tmp_m))
            else:
                E *= 2 / (1 - 2**(-tmp_m))

        else:
            E *= 2

    ## DIAGNOSTIC
    #print "\nFinal Summary:"
    #print "nu =", nu
    #print "q = ", q
    #print "P = ", P
    #print "E = ", E

    ## Compute the exponent w
    w = QQ(0)
    for j in range(s_min, s_max + 1):
        n_j = Jordan_Blocks[j][1].dim()
        for k in range(j + 1, s_max + 1):
            n_k = Jordan_Blocks[k][1].dim()
            w += j * n_j * (n_k + QQ(n_j + 1) / 2)

    ## Step 5: Compute the local mass for the prime 2.
    mass_at_2 = (QQ(2)**(w - q)) * P * E
    return mass_at_2
Exemplo n.º 6
0
def braid_monodromy(f):
    r"""
    Compute the braid monodromy of a projection of the curve defined by a polynomial

    INPUT:

    - ``f`` -- a polynomial with two variables, over a number field with an embedding
      in the complex numbers.

    OUTPUT:

    A list of braids. The braids correspond to paths based in the same point;
    each of this paths is the conjugated of a loop around one of the points
    in the discriminant of the projection of ``f``.

    .. NOTE::

        The projection over the `x` axis is used if there are no vertical asymptotes.
        Otherwise, a linear change of variables is done to fall into the previous case.

    EXAMPLES::

        sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy
        sage: R.<x,y> = QQ[]
        sage: f = (x^2-y^3)*(x+3*y-5)
        sage: braid_monodromy(f)  # optional - sirocco
        [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
         s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1,
         s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1,
         s1*s0*s2*s0^-1*s2*s1^-1]

    """
    global roots_interval_cache
    (x, y) = f.parent().gens()
    F = f.base_ring()
    g = f.radical()
    d = g.degree(y)
    while not g.coefficient(y**d) in F:
        g = g.subs({x: x + y})
        d = g.degree(y)
    disc = discrim(g)
    V = corrected_voronoi_diagram(tuple(disc))
    G = Graph()
    for reg in V.regions().values():
        G = G.union(reg.vertex_graph())
    E = Graph()
    for reg in V.regions().values():
        if reg.rays() or reg.lines():
            E = E.union(reg.vertex_graph())
    p = next(E.vertex_iterator())
    geombasis = geometric_basis(G, E, p)
    segs = set([])
    for p in geombasis:
        for s in zip(p[:-1], p[1:]):
            if (s[1], s[0]) not in segs:
                segs.add((s[0], s[1]))
    I = QQbar.gen()
    segs = [(a[0] + I * a[1], b[0] + I * b[1]) for (a, b) in segs]
    vertices = list(set(flatten(segs)))
    tocacheverts = [(g, v) for v in vertices]
    populate_roots_interval_cache(tocacheverts)
    gfac = g.factor()
    try:
        braidscomputed = list(
            braid_in_segment([(gfac, seg[0], seg[1]) for seg in segs]))
    except ChildProcessError:  # hack to deal with random fails first time
        braidscomputed = list(
            braid_in_segment([(gfac, seg[0], seg[1]) for seg in segs]))
    segsbraids = dict()
    for braidcomputed in braidscomputed:
        seg = (braidcomputed[0][0][1], braidcomputed[0][0][2])
        beginseg = (QQ(seg[0].real()), QQ(seg[0].imag()))
        endseg = (QQ(seg[1].real()), QQ(seg[1].imag()))
        b = braidcomputed[1]
        segsbraids[(beginseg, endseg)] = b
        segsbraids[(endseg, beginseg)] = b.inverse()
    B = b.parent()
    result = []
    for path in geombasis:
        braidpath = B.one()
        for i in range(len(path) - 1):
            x0 = tuple(path[i].vector())
            x1 = tuple(path[i + 1].vector())
            braidpath = braidpath * segsbraids[(x0, x1)]
        result.append(braidpath)
    return result
Exemplo n.º 7
0
    def _closed_form(hyp):
        a, b, z = hyp.operands()
        a, b = a.operands(), b.operands()
        p, q = len(a), len(b)

        if z == 0:
            return Integer(1)
        if p == q == 0:
            return exp(z)
        if p == 1 and q == 0:
            return (1 - z) ** (-a[0])

        if p == 0 and q == 1:
            # TODO: make this require only linear time
            def _0f1(b, z):
                F12 = cosh(2 * sqrt(z))
                F32 = sinh(2 * sqrt(z)) / (2 * sqrt(z))
                if 2 * b == 1:
                    return F12
                if 2 * b == 3:
                    return F32
                if 2 * b > 3:
                    return ((b - 2) * (b - 1) / z * (_0f1(b - 2, z) -
                            _0f1(b - 1, z)))
                if 2 * b < 1:
                    return (_0f1(b + 1, z) + z / (b * (b + 1)) *
                            _0f1(b + 2, z))
                raise ValueError
            # Can evaluate 0F1 in terms of elementary functions when
            # the parameter is a half-integer
            if 2 * b[0] in ZZ and b[0] not in ZZ:
                return _0f1(b[0], z)

        # Confluent hypergeometric function
        if p == 1 and q == 1:
            aa, bb = a[0], b[0]
            if aa * 2 == 1 and bb * 2 == 3:
                t = sqrt(-z)
                return sqrt(pi) / 2 * erf(t) / t
            if a == 1 and b == 2:
                return (exp(z) - 1) / z
            n, m = aa, bb
            if n in ZZ and m in ZZ and m > 0 and n > 0:
                rf = rising_factorial
                if m <= n:
                    return (exp(z) * sum(rf(m - n, k) * (-z) ** k /
                            factorial(k) / rf(m, k) for k in
                            range(n - m + 1)))
                else:
                    T = sum(rf(n - m + 1, k) * z ** k /
                            (factorial(k) * rf(2 - m, k)) for k in
                            range(m - n))
                    U = sum(rf(1 - n, k) * (-z) ** k /
                            (factorial(k) * rf(2 - m, k)) for k in
                            range(n))
                    return (factorial(m - 2) * rf(1 - m, n) *
                            z ** (1 - m) / factorial(n - 1) *
                            (T - exp(z) * U))

        if p == 2 and q == 1:
            R12 = QQ('1/2')
            R32 = QQ('3/2')

            def _2f1(a, b, c, z):
                """
                Evaluation of 2F1(a, b; c; z), assuming a, b, c positive
                integers or half-integers
                """
                if b == c:
                    return (1 - z) ** (-a)
                if a == c:
                    return (1 - z) ** (-b)
                if a == 0 or b == 0:
                    return Integer(1)
                if a > b:
                    a, b = b, a
                if b >= 2:
                    F1 = _2f1(a, b - 1, c, z)
                    F2 = _2f1(a, b - 2, c, z)
                    q = (b - 1) * (z - 1)
                    return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 +
                            (b - c - 1) * F2) / q)
                if c > 2:
                    # how to handle this case?
                    if a - c + 1 == 0 or b - c + 1 == 0:
                        raise NotImplementedError
                    F1 = _2f1(a, b, c - 1, z)
                    F2 = _2f1(a, b, c - 2, z)
                    r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z)
                    r2 = (c - 1) * (c - 2) * (1 - z)
                    q = (a - c + 1) * (b - c + 1) * z
                    return (r1 * F1 + r2 * F2) / q

                if (a, b, c) == (R12, 1, 2):
                    return (2 - 2 * sqrt(1 - z)) / z
                if (a, b, c) == (1, 1, 2):
                    return -log(1 - z) / z
                if (a, b, c) == (1, R32, R12):
                    return (1 + z) / (1 - z) ** 2
                if (a, b, c) == (1, R32, 2):
                    return 2 * (1 / sqrt(1 - z) - 1) / z
                if (a, b, c) == (R32, 2, R12):
                    return (1 + 3 * z) / (1 - z) ** 3
                if (a, b, c) == (R32, 2, 1):
                    return (2 + z) / (2 * (sqrt(1 - z) * (1 - z) ** 2))
                if (a, b, c) == (2, 2, 1):
                    return (1 + z) / (1 - z) ** 3
                raise NotImplementedError
            aa, bb = a
            cc, = b
            if z == 1:
                return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) /
                        gamma(cc - bb))
            if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and
                aa > 0 and bb > 0 and cc > 0):
                try:
                    return _2f1(aa, bb, cc, z)
                except NotImplementedError:
                    pass
        return hyp
Exemplo n.º 8
0
    def H_value(self, p, f, t, ring=None):
        """
        Return the trace of the Frobenius, computed in terms of Gauss sums
        using the hypergeometric trace formula.

        INPUT:

        - `p` -- a prime number

        - `f` -- an integer such that `q = p^f`

        - `t` -- a rational parameter

        - ``ring`` -- optional (default ``UniversalCyclotomicfield``)

        The ring could be also ``ComplexField(n)`` or ``QQbar``.

        OUTPUT:

        an integer

        .. WARNING::

            This is apparently working correctly as can be tested
            using ComplexField(70) as value ring.

            Using instead UniversalCyclotomicfield, this is much
            slower than the `p`-adic version :meth:`padic_H_value`.

        EXAMPLES:

        With values in the UniversalCyclotomicField (slow)::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
            sage: [H.H_value(3,i,-1) for i in range(1,3)]
            [0, -12]
            sage: [H.H_value(5,i,-1) for i in range(1,3)]
            [-4, 276]
            sage: [H.H_value(7,i,-1) for i in range(1,3)]  # not tested
            [0, -476]
            sage: [H.H_value(11,i,-1) for i in range(1,3)]  # not tested
            [0, -4972]
            sage: [H.H_value(13,i,-1) for i in range(1,3)]  # not tested
            [-84, -1420]

        With values in ComplexField::

            sage: [H.H_value(5,i,-1, ComplexField(60)) for i in range(1,3)]
            [-4, 276]

        REFERENCES:

        - [BeCoMe]_ (Theorem 1.3)
        - [Benasque2009]_
        """
        alpha = self._alpha
        beta = self._beta
        if 0 in alpha:
            H = self.swap_alpha_beta()
            return (H.H_value(p, f, ~t, ring))
        if ring is None:
            ring = UniversalCyclotomicField()
        t = QQ(t)
        gamma = self.gamma_array()
        q = p**f

        m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)}
        D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta)
        # also: D = (self.weight() + 1 - m[0]) // 2
        M = self.M_value()

        Fq = GF(q)
        gen = Fq.multiplicative_generator()
        zeta_q = ring.zeta(q - 1)

        tM = Fq(M / t)
        for k in range(q - 1):
            if gen**k == tM:
                teich = zeta_q**k
                break

        gauss_table = [gauss_sum(zeta_q**r, Fq) for r in range(q - 1)]

        sigma = sum(q**(D + m[0] - m[r]) *
                    prod(gauss_table[(-v * r) % (q - 1)]**gv
                         for v, gv in gamma.items()) * teich**r
                    for r in range(q - 1))
        resu = ZZ(-1)**m[0] / (1 - q) * sigma
        if not ring.is_exact():
            resu = resu.real_part().round()
        return resu
Exemplo n.º 9
0
    def padic_H_value(self, p, f, t, prec=20):
        """
        Return the `p`-adic trace of Frobenius, computed using the
        Gross-Koblitz formula.

        INPUT:

        - `p` -- a prime number

        - `f` -- an integer such that `q = p^f`

        - `t` -- a rational parameter

        - ``prec`` -- precision (optional, default 20)

        OUTPUT:

        an integer

        EXAMPLES:

        From Benasque report [Benasque2009]_, page 8::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
            sage: [H.padic_H_value(3,i,-1) for i in range(1,3)]
            [0, -12]
            sage: [H.padic_H_value(5,i,-1) for i in range(1,3)]
            [-4, 276]
            sage: [H.padic_H_value(7,i,-1) for i in range(1,3)]
            [0, -476]
            sage: [H.padic_H_value(11,i,-1) for i in range(1,3)]
            [0, -4972]

        From [Roberts2015]_ (but note conventions regarding `t`)::

            sage: H = Hyp(gamma_list=[-6,-1,4,3])
            sage: t = 189/125
            sage: H.padic_H_value(13,1,1/t)
            0

        REFERENCES:

        - [MagmaHGM]_
        """
        alpha = self._alpha
        beta = self._beta
        if 0 in alpha:
            H = self.swap_alpha_beta()
            return (H.padic_H_value(p, f, ~t, prec))
        t = QQ(t)
        gamma = self.gamma_array()
        q = p**f

        m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)}
        M = self.M_value()
        D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta)
        # also: D = (self.weight() + 1 - m[0]) // 2

        gauss_table = [
            padic_gauss_sum(r, p, f, prec, factored=True) for r in range(q - 1)
        ]

        p_ring = Zp(p, prec=prec)
        teich = p_ring.teichmuller(M / t)
        sigma = sum(q**(D + m[0] - m[r]) *
                    (-p)**(sum(gauss_table[(v * r) % (q - 1)][0] * gv
                               for v, gv in gamma.items()) // (p - 1)) *
                    prod(gauss_table[(v * r) % (q - 1)][1]**gv
                         for v, gv in gamma.items()) * teich**r
                    for r in range(q - 1))
        resu = ZZ(-1)**m[0] / (1 - q) * sigma
        return IntegerModRing(p**prec)(resu).lift_centered()
Exemplo n.º 10
0
def braid_in_segment(f, x0, x1):
    """
    Return the braid formed by the `y` roots of ``f`` when `x` moves
    from ``x0`` to ``x1``.

    INPUT:

    - ``f`` -- a polynomial in two variables
    - ``x0`` -- a complex number
    - ``x1`` -- a complex number

    OUTPUT:

    A braid.

    EXAMPLES::

        sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco
        sage: R.<x,y> = QQ[]
        sage: f = x^2 + y^3
        sage: x0 = CC(1,0)
        sage: x1 = CC(1, 0.5)
        sage: braid_in_segment(f, x0, x1) # optional - sirocco
        s1

    TESTS:

    Check that :trac:`26503` is fixed::

        sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0]
        sage: Kw.<wp> = NumberField(wp.minpoly(), embedding=wp)
        sage: R.<x, y> = Kw[]
        sage: z = -wp - 1
        sage: f = y*(y + z)*x*(x - 1)*(x - y)*(x + z*y - 1)*(x + z*y + wp)
        sage: from sage.schemes.curves import zariski_vankampen as zvk
        sage: g = f.subs({x: x + 2*y})
        sage: p1 = QQbar(sqrt(-1/3))
        sage: p2 = QQbar(1/2+sqrt(-1/3)/2)
        sage: B = zvk.braid_in_segment(g,CC(p1),CC(p2)) # optional - sirocco
        sage: B.left_normal_form()  # optional - sirocco
        (1, s5)
    """
    CC = ComplexField(64)
    (x, y) = f.variables()
    I = QQbar.gen()
    X0 = QQ(x0.real()) + I * QQ(x0.imag())
    X1 = QQ(x1.real()) + I * QQ(x1.imag())
    F0 = QQbar[y](f(X0, y))
    y0s = F0.roots(multiplicities=False)
    strands = [followstrand(f, x0, x1, CC(a)) for a in y0s]
    complexstrands = [[(a[0], CC(a[1], a[2])) for a in b] for b in strands]
    centralbraid = braid_from_piecewise(complexstrands)
    initialstrands = []
    y0aps = [c[0][1] for c in complexstrands]
    used = []
    for y0ap in y0aps:
        distances = [((y0ap - y0).norm(), y0) for y0 in y0s]
        y0 = sorted(distances)[0][1]
        if y0 in used:
            raise ValueError("different roots are too close")
        used.append(y0)
        initialstrands.append([(0, y0), (1, y0ap)])
    initialbraid = braid_from_piecewise(initialstrands)
    F1 = QQbar[y](f(X1, y))
    y1s = F1.roots(multiplicities=False)
    finalstrands = []
    y1aps = [c[-1][1] for c in complexstrands]
    used = []
    for y1ap in y1aps:
        distances = [((y1ap - y1).norm(), y1) for y1 in y1s]
        y1 = sorted(distances)[0][1]
        if y1 in used:
            raise ValueError("different roots are too close")
        used.append(y1)
        finalstrands.append([(0, y1ap), (1, y1)])
    finallbraid = braid_from_piecewise(finalstrands)
    return initialbraid * centralbraid * finallbraid
    def _adjust_bounds(self):
        r"""
        TESTS::

            sage: from surface_dynamics import FatGraphs
            sage: FatGraphs(g=0, nv_max=4, vertex_min_degree=3)
            Traceback (most recent call last):
            ...
            ValueError: infinitely many fat graphs
            sage: FatGraphs(g=0, nf_max=4, vertex_min_degree=3)
            FatGraphs(g=0, nf_min=2, nf_max=4, ne_min=1, ne_max=4, nv_min=1, nv_max=3, vertex_min_degree=3)

            sage: F = FatGraphs(g=0, nv=2, ne=1, nf=2)
            sage: F
            EmptySet
            sage: F.cardinality_and_weighted_cardinality()
            (0, 0)

            sage: FatGraphs(ne=0).list()
            [FatGraph('()', '()', '()')]
            sage: FatGraphs(ne=1).list()
            [FatGraph('(0,1)', '(0,1)', '(0)(1)'),
             FatGraph('(0)(1)', '(0,1)', '(0,1)')]
            sage: FatGraphs(ne=2).list()
            [FatGraph('(0,1,2,3)', '(0,2)(1,3)', '(0,1,2,3)'),
             FatGraph('(0,2,1)(3)', '(0,1)(2,3)', '(0,2,3)(1)'),
             FatGraph('(0,2)(1,3)', '(0,1)(2,3)', '(0,3)(1,2)'),
             FatGraph('(0,3,2,1)', '(0,1)(2,3)', '(0,2)(1)(3)'),
             FatGraph('(0,2)(1)(3)', '(0,1)(2,3)', '(0,1,2,3)')]
        """
        # variable order: v, e, f, g
        from sage.geometry.polyhedron.constructor import Polyhedron
        eqns = [(-2, 1, -1, 1, 2)]  # -2 + v - e + f + 2g = 0
        ieqs = [
            (-self._vmin, 1, 0, 0, 0),  # -vim + v >= 0
            (-self._emin, 0, 1, 0, 0),  # -emin + e >= 0
            (-self._fmin, 0, 0, 1, 0),  # -fmin + f >= 0
            (-self._gmin, 0, 0, 0, 1),  # -gmin + g >= 0
            (0, -self._vertex_min_degree, 2, 0, 0)
        ]  # -v_min_degree*v + 2e >= 0
        if self._vmax is not None:
            ieqs.append((self._vmax - 1, -1, 0, 0, 0))  # v < vmax
        if self._emax is not None:
            ieqs.append((self._emax - 1, 0, -1, 0, 0))  # e < emax
        if self._fmax is not None:
            ieqs.append((self._fmax - 1, 0, 0, -1, 0))  # f < fmax
        if self._gmax is not None:
            ieqs.append((self._gmax - 1, 0, 0, 0, -1))  # g < gmax
        P = Polyhedron(ieqs=ieqs, eqns=eqns, ambient_dim=4, base_ring=QQ)
        if P.is_empty():
            self._vmin = self._vmax = 1
            self._emin = self._emax = 0
            self._fmin = self._fmax = 1
            self._gmin = self._gmax = 0
            self._vertex_min_degree = 0
            return
        if not P.is_compact():
            raise ValueError('infinitely many fat graphs')

        half = QQ((1, 2))
        self._vmin, self._vmax = minmax([v[0] for v in P.vertices_list()])
        self._vmin = self._vmin.floor()
        self._vmax = (self._vmax + half).ceil()
        self._emin, self._emax = minmax([v[1] for v in P.vertices_list()])
        self._emin = self._emin.floor()
        self._emax = (self._emax + half).ceil()
        self._fmin, self._fmax = minmax([v[2] for v in P.vertices_list()])
        self._fmin = self._fmin.floor()
        self._fmax = (self._fmax + half).ceil()
        self._gmin, self._gmax = minmax([v[3] for v in P.vertices_list()])
        self._gmin = self._gmin.floor()
        self._gmax = (self._gmax + half).ceil()
Exemplo n.º 12
0
    def padic_H_value(self, p, f, t, prec=None):
        """
        Return the `p`-adic trace of Frobenius, computed using the
        Gross-Koblitz formula.

        INPUT:

        - `p` -- a prime number

        - `f` -- an integer such that `q = p^f`

        - `t` -- a rational parameter

        - ``prec`` -- precision (optional, default 20)

        OUTPUT:

        an integer

        EXAMPLES:

        From Benasque report [Benasque2009]_, page 8::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4))
            sage: [H.padic_H_value(3,i,-1) for i in range(1,3)]
            [0, -12]
            sage: [H.padic_H_value(5,i,-1) for i in range(1,3)]
            [-4, 276]
            sage: [H.padic_H_value(7,i,-1) for i in range(1,3)]
            [0, -476]
            sage: [H.padic_H_value(11,i,-1) for i in range(1,3)]
            [0, -4972]

        From [Roberts2015]_ (but note conventions regarding `t`)::

            sage: H = Hyp(gamma_list=[-6,-1,4,3])
            sage: t = 189/125
            sage: H.padic_H_value(13,1,1/t)
            0

        REFERENCES:

        - [MagmaHGM]_
        """
        alpha = self._alpha
        beta = self._beta
        if 0 in alpha:
            H = self.swap_alpha_beta()
            return H.padic_H_value(p, f, ~t, prec)
        t = QQ(t)
        gamma = self.gamma_array()
        q = p**f

        #        m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)}
        m = defaultdict(lambda: 0)
        for r in range(q - 1):
            u = QQ((r, q - 1))
            if u in beta:
                m[r] = beta.count(u)
        M = self.M_value()
        D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta)
        # also: D = (self.weight() + 1 - m[0]) // 2

        if prec is None:
            prec = (self.weight() * f) // 2 + ceil(log(self.degree(), p)) + 1
        # For some reason, working in Qp instead of Zp is much faster;
        # it appears to avoid some costly conversions.
        p_ring = Qp(p, prec=prec)
        teich = p_ring.teichmuller(M / t)

        gauss_table = [
            padic_gauss_sum(r,
                            p,
                            f,
                            prec,
                            factored=True,
                            algorithm='sage',
                            parent=p_ring) for r in range(q - 1)
        ]

        sigma = sum(
            ((-p)**(sum(gauss_table[(v * r) % (q - 1)][0] * gv
                        for v, gv in gamma.items()) //
                    (p - 1)) * prod(gauss_table[(v * r) % (q - 1)][1]**gv
                                    for v, gv in gamma.items()) *
             teich**r) << (f * (D + m[0] - m[r])) for r in range(q - 1))
        resu = ZZ(-1)**m[0] / (1 - q) * sigma
        return IntegerModRing(p**prec)(resu).lift_centered()
Exemplo n.º 13
0
def Min(Fun, p, ubRes, conj, all_orbits=False):
    r"""
    Local loop for Affine_minimal, where we check minimality at the prime p.

    First we bound the possible k in our transformations A = zp^k + b.
    See Theorems 3.3.2 and 3.3.3 in [Molnar]_.

    INPUT:

    - ``Fun`` -- a dynamical system on projective space

    - ``p`` - a prime

    - ``ubRes`` -- integer, the upper bound needed for Th. 3.3.3 in [Molnar]_

    - ``conj`` -- a 2x2 matrix keeping track of the conjugation

    - ``all_orbits`` -- boolean; whether or not to use ``==`` in the
      inequalities to find all orbits

    OUTPUT:

    - boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise

    - a dynamical system on projective space minimal at ``p``

    EXAMPLES::

        sage: P.<x,y> = ProjectiveSpace(QQ, 1)
        sage: f = DynamicalSystem_projective([149*x^2 + 39*x*y + y^2, -8*x^2 + 137*x*y + 33*y^2])
        sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import Min
        sage: Min(f, 3, -27000000, matrix(QQ,[[1, 0],[0, 1]]))
        [
        Dynamical System of Projective Space of dimension 1 over Rational Field
          Defn: Defined on coordinates by sending (x : y) to
                (157*x^2 + 72*x*y + 3*y^2 : -24*x^2 + 121*x*y + 54*y^2)        ,
        <BLANKLINE>
        [3 1]
        [0 1]
        ]
    """
    d = Fun.degree()
    AffFun = Fun.dehomogenize(1)
    R = AffFun.coordinate_ring()
    if R.is_field():
        #want the polynomial ring not the fraction field
        R = R.ring()
    F = R(AffFun[0].numerator())
    G = R(AffFun[0].denominator())
    dG = G.degree()
    # all_orbits scales bounds for >= and <= if searching for orbits instead of min model
    if dG > (d+1)/2:
        lowerBound = (-2*(G[dG]).valuation(p)/(2*dG - d + 1) + 1).floor() - int(all_orbits)
    else:
        lowerBound = (-2*(F[d]).valuation(p)/(d-1) + 1).floor() - int(all_orbits)
    upperBound = 2*(ubRes.valuation(p)) + int(all_orbits)

    if upperBound < lowerBound:
        # There are no possible transformations to reduce the resultant.
        if not all_orbits:
            return [Fun, conj]
        return []
    # Looping over each possible k, we search for transformations to reduce
    # the resultant of F/G
    all_found = []
    k = lowerBound
    Qb = PolynomialRing(QQ,'b')
    b = Qb.gen(0)
    Q = PolynomialRing(Qb,'z')
    z = Q.gen(0)
    while k <= upperBound:
        A = (p**k)*z + b
        Ft = Q(F(A) - b*G(A))
        Gt = Q((p**k)*G(A))
        Fcoeffs = Ft.coefficients(sparse=False)
        Gcoeffs = Gt.coefficients(sparse=False)
        coeffs = Fcoeffs + Gcoeffs
        RHS = (d + 1) * k / 2
        # If there is some b such that Res(phi^A) < Res(phi), we must have
        # ord_p(c) > RHS for each c in coeffs.
        # Make sure constant coefficients in coeffs satisfy the inequality.
        if all(QQ(c).valuation(p) > RHS - int(all_orbits)
               for c in coeffs if c.degree() == 0):
            # Constant coefficients in coeffs have large enough valuation, so
            # check the rest. We start by checking if simply picking b=0 works.
            if all(c(0).valuation(p) > RHS - int(all_orbits) for c in coeffs):
                # A = z*p^k satisfies the inequalities, and F/G is not minimal
                # "Conjugating by", p,"^", k, "*z +", 0
                newconj = matrix(QQ, 2, 2, [p**k, 0, 0, 1])
                minFun = Fun.conjugate(newconj)
                minFun.normalize_coordinates()
                if not all_orbits:
                    return [minFun, conj*newconj]
                all_found.append([p, k, 0])

            # Otherwise we search if any value of b will work. We start by
            # finding a minimum bound on the valuation of b that is necessary.
            # See Theorem 3.3.5 in [Molnar, M.Sc. thesis].
            bval = max(bCheck(coeff, RHS, p, b) for coeff in coeffs if coeff.degree() > 0)

            # We scale the coefficients in coeffs, so that we may assume
            # ord_p(b) is at least 0
            scaledCoeffs = [coeff(b*(p**bval)) for coeff in coeffs]

            # We now scale the inequalities, ord_p(coeff) > RHS, so that
            # coeff is in ZZ[b]
            scale = QQ(max(coeff.denominator() for coeff in scaledCoeffs))
            normalizedCoeffs = [coeff * scale for coeff in scaledCoeffs]
            scaleRHS = RHS + scale.valuation(p)

            # We now search for integers that satisfy the inequality
            # ord_p(coeff) > RHS. See Lemma 3.3.6 in [Molnar, M.Sc. thesis].
            bound = (scaleRHS + 1 - int(all_orbits)).floor()
            all_blift = blift(normalizedCoeffs, bound, p, k, all_orbits=all_orbits)

            # If bool is true after lifting, we have a solution b, and F/G
            # is not minimal.
            for boolval, sol in all_blift:
                if boolval:
                    #Rescale, conjugate and return new map
                    bsol = QQ(sol * (p**bval))
                    #only add 'minimal orbit element'
                    while bsol.abs() >= p**k:
                        if bsol < 0:
                            bsol += p**k
                        else:
                            bsol -= p**k
                    #"Conjugating by ", p,"^", k, "*z +", bsol
                    newconj = matrix(QQ, 2, 2, [p**k, bsol, 0, 1])
                    minFun = Fun.conjugate(newconj)

                    minFun.normalize_coordinates()
                    if not all_orbits:
                        return [minFun, conj*newconj]
                    if [p,k,bsol] not in all_found:
                        all_found.append([p, k, bsol])
        k = k + 1
    if not all_orbits:
        return [Fun, conj]
    return all_found
Exemplo n.º 14
0
def blift(LF, Li, p, k, S=None, all_orbits=False):
    r"""
    Search for a solution to the given list of inequalities.

    If found, lift the solution to
    an appropriate valuation. See Lemma 3.3.6 in [Molnar]_

    INPUT:

    - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients)

    - ``Li`` -- an integer, the bound on coefficients

    - ``p`` -- a prime

    - ``k`` -- the scaling factor that makes the solution a ``p``-adic integer

    - ``S`` -- polynomial ring to use

    - ``all_orbits`` -- boolean; whether or not to use ``==`` in the
      inequalities to find all orbits

    OUTPUT:

    - boolean -- whether or not the lift is successful

    - integer -- the lift

    EXAMPLES::

        sage: R.<b> = PolynomialRing(QQ)
        sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import blift
        sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341,\
        ....: -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3, 2)
        [[True, 4]]
    """

    P = LF[0].parent()
    #Determine which inequalities are trivial, and scale the rest, so that we only lift
    #as many times as needed.
    keepScaledIneqs = [scale(P(coeff),Li,p) for coeff in LF if coeff != 0]
    keptVals = [i[2] for i in keepScaledIneqs if i[0]]
    if keptVals:
        # Determine the valuation to lift until.
        # liftval = max(keptVals)
        pass
    else:
        #All inequalities are satisfied.
        if all_orbits:
            return [[True, t] for t in range(p)]
        return [[True, 1]]
    if S is None:
        S = PolynomialRing(Zmod(p), 'b')
    keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]]
    #We need a solution for each polynomial on the left hand side of the inequalities,
    #so we need only find a solution for their gcd.
    g = gcd(keptScaledIneqs)
    rts = g.roots(multiplicities=False)
    good = []
    for r in rts:
        #Recursively try to lift each root
        r_initial = QQ(r)
        newInput = P([r_initial, p])
        LG = [F(newInput) for F in LF]
        new_good = blift(LG, Li, p, k, S=S)
        for lift,lifted in new_good:
            if lift:
                #Lift successful.
                if not all_orbits:
                    return [[True, r_initial + p*lifted]]

                #only need up to SL(2,ZZ) equivalence
                #this helps control the size of the resulting coefficients
                if r_initial + p*lifted < p**k:
                    good.append([True, r_initial + p*lifted])
                else:
                    new_r = r_initial + p*lifted - p**k
                    while new_r > p**k:
                        new_r -= p**k
                    if [True, new_r] not in good:
                        good.append([True, new_r])
    if good:
        return good
    #Lift non successful.
    return [[False,0]]
Exemplo n.º 15
0
def roots_interval(f, x0):
    """
    Find disjoint intervals that isolate the roots of a polynomial for a fixed
    value of the first variable.

    INPUT:

    - ``f`` -- a bivariate squarefree polynomial
    - ``x0`` -- a value where the first coordinate will be fixed

    The intervals are taken as big as possible to be able to detect when two
    approximate roots of `f(x_0, y)` correspond to the same exact root.

    The result is given as a dictionary, where the keys are approximations to the roots
    with rational real and imaginary parts, and the values are intervals containing them.

    EXAMPLES::

        sage: from sage.schemes.curves.zariski_vankampen import roots_interval
        sage: R.<x,y> = QQ[]
        sage: f = y^3 - x^2
        sage: ri = roots_interval(f, 1)
        sage: ri
        {-138907099/160396102*I - 1/2: -1.? - 1.?*I,
         138907099/160396102*I - 1/2: -1.? + 1.?*I,
         1: 1.? + 0.?*I}
        sage: [r.endpoints() for r in ri.values()]
        [(0.566987298107781 - 0.433012701892219*I,
          1.43301270189222 + 0.433012701892219*I,
          0.566987298107781 + 0.433012701892219*I,
          1.43301270189222 - 0.433012701892219*I),
         (-0.933012701892219 - 1.29903810567666*I,
          -0.0669872981077806 - 0.433012701892219*I,
          -0.933012701892219 - 0.433012701892219*I,
          -0.0669872981077806 - 1.29903810567666*I),
         (-0.933012701892219 + 0.433012701892219*I,
          -0.0669872981077806 + 1.29903810567666*I,
          -0.933012701892219 + 1.29903810567666*I,
          -0.0669872981077806 + 0.433012701892219*I)]

    """
    x, y = f.parent().gens()
    I = QQbar.gen()
    fx = QQbar[y](f.subs({x: QQ(x0.real()) + I * QQ(x0.imag())}))
    roots = fx.roots(QQbar, multiplicities=False)
    result = {}
    for i in range(len(roots)):
        r = roots[i]
        prec = 53
        IF = ComplexIntervalField(prec)
        CF = ComplexField(prec)
        divisor = 4
        diam = min((CF(r) - CF(r0)).abs()
                   for r0 in roots[:i] + roots[i + 1:]) / divisor
        envelop = IF(diam) * IF((-1, 1), (-1, 1))
        while not newton(fx, r, r + envelop) in r + envelop:
            prec += 53
            IF = ComplexIntervalField(prec)
            CF = ComplexField(prec)
            divisor *= 2
            diam = min([(CF(r) - CF(r0)).abs()
                        for r0 in roots[:i] + roots[i + 1:]]) / divisor
            envelop = IF(diam) * IF((-1, 1), (-1, 1))
        qapr = QQ(CF(r).real()) + QQbar.gen() * QQ(CF(r).imag())
        if qapr not in r + envelop:
            raise ValueError("Could not approximate roots with exact values")
        result[qapr] = r + envelop
    return result
Exemplo n.º 16
0
 def __init__(self):
     self.count = ZZ(0)
     self.weighted_count = QQ(0)
Exemplo n.º 17
0
def braid_in_segment(g, x0, x1):
    """
    Return the braid formed by the `y` roots of ``f`` when `x` moves
    from ``x0`` to ``x1``.

    INPUT:

    - ``g`` -- a polynomial factorization in two variables
    - ``x0`` -- a complex number
    - ``x1`` -- a complex number

    OUTPUT:

    A braid.

    EXAMPLES::

        sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco
        sage: R.<x,y> = QQ[]
        sage: f = x^2 + y^3
        sage: x0 = CC(1,0)
        sage: x1 = CC(1, 0.5)
        sage: braid_in_segment(f.factor(), x0, x1) # optional - sirocco
        s1

    TESTS:

    Check that :trac:`26503` is fixed::

        sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0]
        sage: Kw.<wp> = NumberField(wp.minpoly(), embedding=wp)
        sage: R.<x, y> = Kw[]
        sage: z = -wp - 1
        sage: f = y*(y + z)*x*(x - 1)*(x - y)*(x + z*y - 1)*(x + z*y + wp)
        sage: from sage.schemes.curves import zariski_vankampen as zvk
        sage: g = f.subs({x: x + 2*y})
        sage: p1 = QQbar(sqrt(-1/3))
        sage: p2 = QQbar(1/2+sqrt(-1/3)/2)
        sage: B = zvk.braid_in_segment(g.factor(),CC(p1),CC(p2)) # optional - sirocco
        sage: B  # optional - sirocco
        s5*s3^-1

    """
    (x, y) = g.value().parent().gens()
    I = QQbar.gen()
    X0 = QQ(x0.real()) + I * QQ(x0.imag())
    X1 = QQ(x1.real()) + I * QQ(x1.imag())
    intervals = {}
    precision = {}
    y0s = []
    for (f, naux) in g:
        if f.variables() == (y, ):
            F0 = QQbar[y](f.base_ring()[y](f))
        else:
            F0 = QQbar[y](f(X0, y))
        y0sf = F0.roots(multiplicities=False)
        y0s += list(y0sf)
        precision[f] = 53
        while True:
            CIFp = ComplexIntervalField(precision[f])
            intervals[f] = [r.interval(CIFp) for r in y0sf]
            if not any(
                    a.overlaps(b)
                    for a, b in itertools.combinations(intervals[f], 2)):
                break
            precision[f] *= 2
    strands = [
        followstrand(f[0], [p[0] for p in g if p[0] != f[0]], x0, x1,
                     i.center(), precision[f[0]]) for f in g
        for i in intervals[f[0]]
    ]
    complexstrands = [[(QQ(a[0]), QQ(a[1]), QQ(a[2])) for a in b]
                      for b in strands]
    centralbraid = braid_from_piecewise(complexstrands)
    initialstrands = []
    finalstrands = []
    initialintervals = roots_interval_cached(g.value(), X0)
    finalintervals = roots_interval_cached(g.value(), X1)
    for cs in complexstrands:
        ip = cs[0][1] + I * cs[0][2]
        fp = cs[-1][1] + I * cs[-1][2]
        matched = 0
        for center, interval in initialintervals.items():
            if ip in interval:
                initialstrands.append([(0, center.real(), center.imag()),
                                       (1, cs[0][1], cs[0][2])])
                matched += 1
        if matched == 0:
            raise ValueError("unable to match braid endpoint with root")
        if matched > 1:
            raise ValueError("braid endpoint mathes more than one root")
        matched = 0
        for center, interval in finalintervals.items():
            if fp in interval:
                finalstrands.append([(0, cs[-1][1], cs[-1][2]),
                                     (1, center.real(), center.imag())])
                matched += 1
        if matched == 0:
            raise ValueError("unable to match braid endpoint with root")
        if matched > 1:
            raise ValueError("braid endpoint mathes more than one root")
    initialbraid = braid_from_piecewise(initialstrands)
    finalbraid = braid_from_piecewise(finalstrands)

    return initialbraid * centralbraid * finalbraid
Exemplo n.º 18
0
 def __call__(self, cm, aut):
     self.count += ZZ(1)
     self.weighted_count += QQ(
         (1, (1 if aut is None else aut.group_cardinality())))
Exemplo n.º 19
0
def is_anisotropic(self, p):
    """
    Checks if the quadratic form is anisotropic over the p-adic numbers `Q_p`.

    INPUT:

        `p` -- a prime number > 0

    OUTPUT:

        boolean

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1])
        sage: Q.is_anisotropic(2)
        True
        sage: Q.is_anisotropic(3)
        True
        sage: Q.is_anisotropic(5)
        False

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,-1])
        sage: Q.is_anisotropic(2)
        False
        sage: Q.is_anisotropic(3)
        False
        sage: Q.is_anisotropic(5)
        False

    ::

        sage: [DiagonalQuadraticForm(ZZ, [1, -least_quadratic_nonresidue(p)]).is_anisotropic(p)  for p in prime_range(3, 30)]
        [True, True, True, True, True, True, True, True, True]

    ::

        sage: [DiagonalQuadraticForm(ZZ, [1, -least_quadratic_nonresidue(p), p, -p*least_quadratic_nonresidue(p)]).is_anisotropic(p)  for p in prime_range(3, 30)]
        [True, True, True, True, True, True, True, True, True]

    """
    n = self.dim()
    D = self.det()

    ## TO DO: Should check that p is prime

    if (n >= 5):
        return False

    if (n == 4):
        return (QQ(D).is_padic_square(p)
                and (self.hasse_invariant(p) == -hilbert_symbol(-1, -1, p)))

    if (n == 3):
        return (self.hasse_invariant(p) != hilbert_symbol(-1, -D, p))

    if (n == 2):
        return (not QQ(-D).is_padic_square(p))

    if (n == 1):
        return (self[0, 0] != 0)

    raise NotImplementedError(
        "Oops!  We haven't established a convention for 0-dim'l quadratic forms... =("
    )
Exemplo n.º 20
0
def bch_iterator(X=None, Y=None):
    r"""
    A generator function which returns successive terms of the
    Baker-Campbell-Hausdorff formula.

    INPUT:

    - ``X`` -- (optional) an element of a Lie algebra
    - ``Y`` -- (optional) an element of a Lie algebra

    The BCH formula is an expression for `\log(\exp(X)\exp(Y))` as a sum of Lie
    brackets of ``X`` and ``Y`` with rational coefficients. In arbitrary Lie
    algebras, the infinite sum is only guaranteed to converge for ``X`` and
    ``Y`` close to zero.

    If the elements ``X`` and ``Y`` are not given, then the iterator will
    return successive terms of the abstract BCH formula, i.e., the BCH formula
    for the generators of the free Lie algebra on 2 generators.

    If the Lie algebra containing ``X`` and ``Y`` is not nilpotent, the
    iterator will output infinitely many elements. If the Lie algebra is
    nilpotent, the number of elements outputted is equal to the nilpotency step.

    EXAMPLES:

    The terms of the abstract BCH formula up to fifth order brackets::

        sage: from sage.algebras.lie_algebras.bch import bch_iterator
        sage: bch = bch_iterator()
        sage: next(bch)
        X + Y
        sage: next(bch)
        1/2*[X, Y]
        sage: next(bch)
        1/12*[X, [X, Y]] + 1/12*[[X, Y], Y]
        sage: next(bch)
        1/24*[X, [[X, Y], Y]]
        sage: next(bch)
        -1/720*[X, [X, [X, [X, Y]]]] + 1/180*[X, [X, [[X, Y], Y]]]
        + 1/360*[[X, [X, Y]], [X, Y]] + 1/180*[X, [[[X, Y], Y], Y]]
        + 1/120*[[X, Y], [[X, Y], Y]] - 1/720*[[[[X, Y], Y], Y], Y]

    For nilpotent Lie algebras the BCH formula only has finitely many terms::

        sage: L = LieAlgebra(QQ, 2, step=3)
        sage: L.inject_variables()
        Defining X_1, X_2, X_12, X_112, X_122
        sage: [Z for Z in bch_iterator(X_1, X_2)]
        [X_1 + X_2, 1/2*X_12, 1/12*X_112 + 1/12*X_122]
        sage: [Z for Z in bch_iterator(X_1 + X_2, X_12)]
        [X_1 + X_2 + X_12, 1/2*X_112 - 1/2*X_122, 0]

    The elements ``X`` and ``Y`` don't need to be elements of the same Lie
    algebra if there is a coercion from one to the other::

        sage: L = LieAlgebra(QQ, 3, step=2)
        sage: L.inject_variables()
        Defining X_1, X_2, X_3, X_12, X_13, X_23
        sage: S = L.subalgebra(X_1, X_2)
        sage: bch1 = [Z for Z in bch_iterator(S(X_1), S(X_2))]; bch1
        [X_1 + X_2, 1/2*X_12]
        sage: bch1[0].parent() == S
        True
        sage: bch2 = [Z for Z in bch_iterator(S(X_1), X_3)]; bch2
        [X_1 + X_3, 1/2*X_13]
        sage: bch2[0].parent() == L
        True

    The BCH formula requires a coercion from the rationals::

        sage: L.<X,Y,Z> = LieAlgebra(ZZ, 2, step=2)
        sage: bch = bch_iterator(X, Y); next(bch)
        Traceback (most recent call last):
        ...
        TypeError: the BCH formula is not well defined since Integer Ring has no coercion from Rational Field

    TESTS:

    Compare to the BCH formula up to degree 5 given by wikipedia::

        sage: from sage.algebras.lie_algebras.bch import bch_iterator
        sage: bch = bch_iterator()
        sage: L.<X,Y> = LieAlgebra(QQ)
        sage: L = L.Lyndon()
        sage: computed_BCH = L.sum(next(bch) for k in range(5))
        sage: wikiBCH = X + Y + 1/2*L[X,Y] + 1/12*(L[X,[X,Y]] + L[Y,[Y,X]])
        sage: wikiBCH += -1/24*L[Y,[X,[X,Y]]]
        sage: wikiBCH += -1/720*(L[Y,[Y,[Y,[Y,X]]]] + L[X,[X,[X,[X,Y]]]])
        sage: wikiBCH += 1/360*(L[X,[Y,[Y,[Y,X]]]] + L[Y,[X,[X,[X,Y]]]])
        sage: wikiBCH += 1/120*(L[Y,[X,[Y,[X,Y]]]] + L[X,[Y,[X,[Y,X]]]])
        sage: computed_BCH == wikiBCH
        True

    ALGORITHM:

    The BCH formula `\log(\exp(X)\exp(Y)) = \sum_k Z_k` is computed starting
    from `Z_1 = X + Y`, by the recursion

    .. MATH::

        (m+1)Z_{m+1} =  \frac{1}{2}[X - Y, Z_m]
        + \sum_{2\leq 2p \leq m}\frac{B_{2p}}{(2p)!}\sum_{k_1+\cdots+k_{2p}=m}
        [Z_{k_1}, [\cdots [Z_{k_{2p}}, X + Y]\cdots],

    where `B_{2p}` are the Bernoulli numbers, see Lemma 2.15.3. in [Var1984]_.

    .. WARNING::

        The time needed to compute each successive term increases exponentially.
        For example on one machine iterating through `Z_{11},...,Z_{18}` for a
        free Lie algebra, computing each successive term took 4-5 times longer,
        going from 0.1s for `Z_{11}` to 21 minutes for `Z_{18}`.
    """
    if X is None or Y is None:
        L = LieAlgebra(QQ, ['X', 'Y']).Lyndon()
        X, Y = L.lie_algebra_generators()
    else:
        X, Y = canonical_coercion(X, Y)
        L = X.parent()

    R = L.base_ring()
    if not R.has_coerce_map_from(QQ):
        raise TypeError("the BCH formula is not well defined since %s "
                        "has no coercion from %s" % (R, QQ))

    xdif = X - Y
    Z = [0, X + Y]  # 1-based indexing for convenience
    m = 1
    yield Z[1]

    while True:
        m += 1
        if L in LieAlgebras.Nilpotent and m > L.step():
            return

        # apply the recursion formula of [Var1984]
        Zm = ~QQ(2 * m) * xdif.bracket(Z[-1])
        for p in range(1, (m - 1) // 2 + 1):
            partitions = IntegerListsLex(m - 1, length=2 * p, min_part=1)
            coeff = bernoulli(2 * p) / QQ(m * factorial(2 * p))
            for kvec in partitions:
                W = Z[1]
                for k in kvec:
                    W = Z[k].bracket(W)
                Zm += coeff * W

        Z.append(Zm)
        yield Zm
def jordan_blocks_by_scale_and_unimodular(self, p, safe_flag=True):
    r"""
    Return a list of pairs `(s_i, L_i)` where `L_i` is a maximal
    `p^{s_i}`-unimodular Jordan component which is further decomposed into
    block diagonals of block size `\le 2`.

    For each `L_i` the 2x2 blocks are listed after the 1x1 blocks
    (which follows from the convention of the
    :meth:`local_normal_form` method).

    .. NOTE::

        The decomposition of each `L_i` into smaller blocks is not unique!

    The ``safe_flag`` argument allows us to select whether we want a copy of
    the output, or the original output.  By default ``safe_flag = True``, so we
    return a copy of the cached information.  If this is set to ``False``, then
    the routine is much faster but the return values are vulnerable to being
    corrupted by the user.

    INPUT:

    - `p` -- a prime number > 0.

    OUTPUT:

    A list of pairs `(s_i, L_i)` where:

    - `s_i` is an integer,
    - `L_i` is a block-diagonal unimodular quadratic form over `\ZZ_p`.

    .. note::

        These forms `L_i` are defined over the `p`-adic integers, but by a
        matrix over `\ZZ` (or `\QQ`?).

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,9,5,7])
        sage: Q.jordan_blocks_by_scale_and_unimodular(3)
        [(0, Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 1 0 0 ]
        [ * 5 0 ]
        [ * * 7 ]), (2, Quadratic form in 1 variables over Integer Ring with coefficients:
        [ 1 ])]

    ::

        sage: Q2 = QuadraticForm(ZZ, 2, [1,1,1])
        sage: Q2.jordan_blocks_by_scale_and_unimodular(2)
        [(-1, Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 2 2 ]
        [ * 2 ])]
        sage: Q = Q2 + Q2.scale_by_factor(2)
        sage: Q.jordan_blocks_by_scale_and_unimodular(2)
        [(-1, Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 2 2 ]
        [ * 2 ]), (0, Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 2 2 ]
        [ * 2 ])]
    """
    ## Try to use the cached result
    try:
        if safe_flag:
            return copy.deepcopy(self.__jordan_blocks_by_scale_and_unimodular_dict[p])
        else:
            return self.__jordan_blocks_by_scale_and_unimodular_dict[p]
    except Exception:
        ## Initialize the global dictionary if it doesn't exist
        if not hasattr(self, '__jordan_blocks_by_scale_and_unimodular_dict'):
            self.__jordan_blocks_by_scale_and_unimodular_dict = {}


    ## Deal with zero dim'l forms
    if self.dim() == 0:
        return []


    ## Find the Local Normal form of Q at p
    Q1 = self.local_normal_form(p)


    ## Parse this into Jordan Blocks
    n = Q1.dim()
    tmp_Jordan_list = []
    i = 0
    start_ind = 0
    if (n >= 2) and (Q1[0,1] != 0):
        start_scale = valuation(Q1[0,1], p) - 1
    else:
        start_scale = valuation(Q1[0,0], p)

    while (i < n):

        ## Determine the size of the current block
        if (i == n-1) or (Q1[i,i+1] == 0):
            block_size = 1
        else:
            block_size = 2

        ## Determine the valuation of the current block
        if block_size == 1:
            block_scale = valuation(Q1[i,i], p)
        else:
            block_scale = valuation(Q1[i,i+1], p) - 1

        ## Process the previous block if the valuation increased
        if block_scale > start_scale:
            tmp_Jordan_list += [(start_scale, Q1.extract_variables(range(start_ind, i)).scale_by_factor(ZZ(1) / (QQ(p)**(start_scale))))]
            start_ind = i
            start_scale = block_scale

        ## Increment the index
        i += block_size

    ## Add the last block
    tmp_Jordan_list += [(start_scale, Q1.extract_variables(range(start_ind, n)).scale_by_factor(ZZ(1) / QQ(p)**(start_scale)))]


    ## Cache the result
    self.__jordan_blocks_by_scale_and_unimodular_dict[p] = tmp_Jordan_list

    ## Return the result
    return tmp_Jordan_list
Exemplo n.º 22
0
def Min(Fun, p, ubRes, conj):
    r"""
    Local loop for Affine_minimal, where we check minimality at the prime p.

    First we bound the possible k in our transformations A = zp^k + b.
    See Theorems 3.3.2 and 3.3.3 in [Molnar]_.

    INPUT:

    - ``Fun`` -- a projective space morphisms.

    - ``p`` - a prime.

    - ``ubRes`` -- integer, the upper bound needed for Th. 3.3.3 in [Molnar]_.

    - ``conj`` -- a 2x2 matrix keeping track of the conjugation.

    OUTPUT:

    - Boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise.

    - a projective morphism minimal at ``p``.

    EXAMPLES::

        sage: P.<x,y> = ProjectiveSpace(QQ, 1)
        sage: H = End(P)
        sage: f = H([149*x^2 + 39*x*y + y^2, -8*x^2 + 137*x*y + 33*y^2])
        sage: from sage.schemes.projective.endPN_minimal_model import Min
        sage: Min(f, 3, -27000000, matrix(QQ,[[1, 0],[0, 1]]))
        (
        Scheme endomorphism of Projective Space of dimension 1 over Rational
        Field
          Defn: Defined on coordinates by sending (x : y) to
                (181*x^2 + 313*x*y + 81*y^2 : -24*x^2 + 73*x*y + 151*y^2)
        ,
        [3 4]
        [0 1]
        )
    """
    d = Fun.degree()
    AffFun = Fun.dehomogenize(1)
    R = AffFun.coordinate_ring()
    if R.is_field():
        #want the polynomial ring not the fraction field
        R = R.ring()
    F = R(AffFun[0].numerator())
    G = R(AffFun[0].denominator())
    dG = G.degree()
    if dG > (d + 1) / 2:
        lowerBound = (-2 * (G[dG]).valuation(p) / (2 * dG - d + 1) + 1).floor()
    else:
        lowerBound = (-2 * (F[d]).valuation(p) / (d - 1) + 1).floor()
    upperBound = 2 * (ubRes.valuation(p))

    if upperBound < lowerBound:
        #There are no possible transformations to reduce the resultant.
        return Fun, conj
    else:
        #Looping over each possible k, we search for transformations to reduce the
        #resultant of F/G
        k = lowerBound
        Qb = PolynomialRing(QQ, 'b')
        b = Qb.gen(0)
        Q = PolynomialRing(Qb, 'z')
        z = Q.gen(0)
        while k <= upperBound:
            A = (p**k) * z + b
            Ft = Q(F(A) - b * G(A))
            Gt = Q((p**k) * G(A))
            Fcoeffs = Ft.coefficients(sparse=False)
            Gcoeffs = Gt.coefficients(sparse=False)
            coeffs = Fcoeffs + Gcoeffs
            RHS = (d + 1) * k / 2
            #If there is some b such that Res(phi^A) < Res(phi), we must have ord_p(c) >
            #RHS for each c in coeffs.
            #Make sure constant coefficients in coeffs satisfy the inequality.
            if all(
                    QQ(c).valuation(p) > RHS for c in coeffs
                    if c.degree() == 0):
                #Constant coefficients in coeffs have large enough valuation, so check
                #the rest. We start by checking if simply picking b=0 works
                if all(c(0).valuation(p) > RHS for c in coeffs):
                    #A = z*p^k satisfies the inequalities, and F/G is not minimal
                    #"Conjugating by", p,"^", k, "*z +", 0
                    newconj = matrix(QQ, 2, 2, [p**k, 0, 0, 1])
                    minFun = Fun.conjugate(newconj)
                    conj = conj * newconj
                    minFun.normalize_coordinates()
                    return minFun, conj

                #Otherwise we search if any value of b will work. We start by finding a
                #minimum bound on the valuation of b that is necessary. See Theorem 3.3.5
                #in [Molnar, M.Sc. thesis].
                bval = max([
                    bCheck(coeff, RHS, p, b) for coeff in coeffs
                    if coeff.degree() > 0
                ])

                #We scale the coefficients in coeffs, so that we may assume ord_p(b) is
                #at least 0
                scaledCoeffs = [coeff(b * (p**bval)) for coeff in coeffs]

                #We now scale the inequalities, ord_p(coeff) > RHS, so that coeff is in
                #ZZ[b]
                scale = QQ(max([coeff.denominator()
                                for coeff in scaledCoeffs]))
                normalizedCoeffs = [coeff * scale for coeff in scaledCoeffs]
                scaleRHS = RHS + scale.valuation(p)

                #We now search for integers that satisfy the inequality ord_p(coeff) >
                #RHS. See Lemma 3.3.6 in [Molnar, M.Sc. thesis].
                bound = (scaleRHS + 1).floor()
                bool, sol = blift(normalizedCoeffs, bound, p)

                #If bool is true after lifting, we have a solution b, and F/G is not
                #minimal.
                if bool:
                    #Rescale, conjugate and return new map
                    bsol = QQ(sol * (p**bval))
                    #"Conjugating by ", p,"^", k, "*z +", bsol
                    newconj = matrix(QQ, 2, 2, [p**k, bsol, 0, 1])
                    minFun = Fun.conjugate(newconj)
                    conj = conj * newconj

                    minFun.normalize_coordinates()
                    return minFun, conj
            k = k + 1
        return Fun, conj
def Watson_mass_at_2(self):
    """
    Returns the local mass of the quadratic form when `p=2`, according
    to Watson's Theorem 1 of "The 2-adic density of a quadratic form"
    in Mathematika 23 (1976), pp 94--106.

    INPUT:
        none

    OUTPUT:
        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1])
        sage: Q.Watson_mass_at_2()               ## WARNING:  WE NEED TO CHECK THIS CAREFULLY!
        384

    """
    ## Make a 0-dim'l quadratic form (for initialization purposes)
    Null_Form = copy.deepcopy(self)
    Null_Form.__init__(ZZ, 0)

    ## Step 0: Compute Jordan blocks and bounds of the scales to keep track of
    Jordan_Blocks = self.jordan_blocks_by_scale_and_unimodular(2)
    scale_list = [B[0] for B in Jordan_Blocks]
    s_min = min(scale_list)
    s_max = max(scale_list)

    ## Step 1: Compute dictionaries of the diagonal block and 2x2 block for each scale
    diag_dict = dict(
        (i, Null_Form)
        for i in range(s_min - 2, s_max + 4))  ## Initialize with the zero form
    dim2_dict = dict(
        (i, Null_Form)
        for i in range(s_min, s_max + 4))  ## Initialize with the zero form
    for (s, L) in Jordan_Blocks:
        i = 0
        while (i < L.dim() - 1) and (L[i, i + 1]
                                     == 0):  ## Find where the 2x2 blocks start
            i = i + 1
        if i < (L.dim() - 1):
            diag_dict[s] = L.extract_variables(range(i))  ## Diagonal Form
            dim2_dict[s + 1] = L.extract_variables(range(
                i, L.dim()))  ## Non-diagonal Form
        else:
            diag_dict[s] = L

    #print "diag_dict = ", diag_dict
    #print "dim2_dict = ", dim2_dict
    #print "Jordan_Blocks = ", Jordan_Blocks

    ## Step 2: Compute three dictionaries of invariants (for n_j, m_j, nu_j)
    n_dict = dict((j, 0) for j in range(s_min + 1, s_max + 2))
    m_dict = dict((j, 0) for j in range(s_min, s_max + 4))
    for (s, L) in Jordan_Blocks:
        n_dict[s + 1] = L.dim()
        if diag_dict[s].dim() == 0:
            m_dict[s + 1] = ZZ(1) / ZZ(2) * L.dim()
        else:
            m_dict[s + 1] = floor(ZZ(L.dim() - 1) / ZZ(2))
            #print " ==>", ZZ(L.dim() - 1) / ZZ(2), floor(ZZ(L.dim() - 1) / ZZ(2))

    nu_dict = dict((j, n_dict[j + 1] - 2 * m_dict[j + 1])
                   for j in range(s_min, s_max + 1))
    nu_dict[s_max + 1] = 0

    #print "n_dict = ", n_dict
    #print "m_dict = ", m_dict
    #print "nu_dict = ", nu_dict

    ## Step 3: Compute the e_j dictionary
    eps_dict = {}
    for j in range(s_min, s_max + 3):
        two_form = (diag_dict[j - 2] + diag_dict[j] +
                    dim2_dict[j]).scale_by_factor(2)
        j_form = (two_form + diag_dict[j - 1]).base_change_to(
            IntegerModRing(4))

        if j_form.dim() == 0:
            eps_dict[j] = 1
        else:
            iter_vec = [4] * j_form.dim()
            alpha = sum([True for x in mrange(iter_vec) if j_form(x) == 0])
            beta = sum([True for x in mrange(iter_vec) if j_form(x) == 2])
            if alpha > beta:
                eps_dict[j] = 1
            elif alpha == beta:
                eps_dict[j] = 0
            else:
                eps_dict[j] = -1

    #print "eps_dict = ", eps_dict

    ## Step 4: Compute the quantities nu, q, P, E for the local mass at 2
    nu = sum([j * n_dict[j] * (ZZ(n_dict[j] + 1) / ZZ(2) + \
              sum([n_dict[r]  for r in range(j+1, s_max+2)]))  for j in range(s_min+1, s_max+2)])
    q = sum([
        sgn(nu_dict[j - 1] * (n_dict[j] + sgn(nu_dict[j])))
        for j in range(s_min + 1, s_max + 2)
    ])
    P = prod([
        prod([1 - QQ(4)**(-i) for i in range(1, m_dict[j] + 1)])
        for j in range(s_min + 1, s_max + 2)
    ])
    E = prod([
        ZZ(1) / ZZ(2) * (1 + eps_dict[j] * QQ(2)**(-m_dict[j]))
        for j in range(s_min, s_max + 3)
    ])

    #print "\nFinal Summary:"
    #print "nu =", nu
    #print "q = ", q
    #print "P = ", P
    #print "E = ", E

    ## Step 5: Compute the local mass for the prime 2.
    mass_at_2 = QQ(2)**(nu - q) * P / E
    return mass_at_2
Exemplo n.º 24
0
 def screen_to_math_coordinates(self, x, y):
     x = QQ(x)
     y = QQ(y)
     return self.vector_space()(((self._field(x) - self._tx) / self._s,
                                 (-self._field(y) + self._ty) / self._s))
def local_good_density_congruence_even(self, m, Zvec, NZvec):
    """
    Finds the Good-type local density of Q representing `m` at `p=2`.
    (Assuming Q is given in local diagonal form.)


    The additional congruence condition arguments Zvec and NZvec can
    be either a list of indices or None.  Zvec = [] is equivalent to
    Zvec = None which both impose no additional conditions, but NZvec
    = [] returns no solutions always while NZvec = None imposes no
    additional condition.

    WARNING: Here the indices passed in Zvec and NZvec represent
    indices of the solution vector `x` of Q(`x`) = `m (mod p^k)`, and *not*
    the Jordan components of Q.  They therefore are required (and
    assumed) to include either all or none of the indices of a given
    Jordan component of Q.  This is only important when `p=2` since
    otherwise all Jordan blocks are 1x1, and so there the indices and
    Jordan blocks coincide.

    TO DO: Add type checking for Zvec, NZvec, and that Q is in local
    normal form.


    INPUT:
        Q -- quadratic form assumed to be block diagonal and 2-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:
        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_good_density_congruence_even(1, None, None)
        1

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_good_density_congruence_even(1, None, None)
        1
        sage: Q.local_good_density_congruence_even(2, None, None)
        3/2
        sage: Q.local_good_density_congruence_even(3, None, None)
        1
        sage: Q.local_good_density_congruence_even(4, None, None)
        1/2

    ::

        sage: Q = QuadraticForm(ZZ, 4, range(10))
        sage: Q[0,0] = 5
        sage: Q[1,1] = 10
        sage: Q[2,2] = 15
        sage: Q[3,3] = 20
        sage: Q
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 5 1 2 3 ]
        [ * 10 5 6 ]
        [ * * 15 8 ]
        [ * * * 20 ]
        sage: Q.theta_series(20)
        1 + 2*q^5 + 2*q^10 + 2*q^14 + 2*q^15 + 2*q^16 + 2*q^18 + O(q^20)
        sage: Q.local_normal_form(2)
        Quadratic form in 4 variables over Integer Ring with coefficients:
        [ 0 1 0 0 ]
        [ * 0 0 0 ]
        [ * * 0 1 ]
        [ * * * 0 ]
        sage: Q.local_good_density_congruence_even(1, None, None)
        3/4
        sage: Q.local_good_density_congruence_even(2, None, None)
        1
        sage: Q.local_good_density_congruence_even(5, None, None)
        3/4

    """
    n = self.dim()

    ## Put the Zvec congruence condition in a standard form
    if Zvec == None:
        Zvec = []


    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec != None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec != None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")



    ## Find the indices of x for which the associated Jordan blocks are non-zero mod 8    TO DO: Move this to special Jordan block code separately!
    ## -------------------------------------------------------------------------------
    Not8vec = []
    for i in range(n):

        ## DIAGNOSTIC
        verbose(" i = " + str(i))
        verbose(" n = " + str(n))
        verbose(" Not8vec = " + str(Not8vec))

        nz_flag = False

        ## Check if the diagonal entry isn't divisible 8
        if  ((self[i,i] % 8) != 0):
            nz_flag = True

        ## Check appropriate off-diagonal entries aren't divisible by 8
        else:

            ## Special check for first off-diagonal entry
            if ((i == 0) and ((self[i,i+1] % 8) != 0)):
                nz_flag = True

            ## Special check for last off-diagonal entry
            elif ((i == n-1) and ((self[i-1,i] % 8) != 0)):
                nz_flag = True

            ## Check for the middle off-diagonal entries
            else:
                if ( (i > 0)  and  (i < n-1)  and  (((self[i,i+1] % 8) != 0) or ((self[i-1,i] % 8) != 0)) ):
                    nz_flag = True

        ## Remember the (vector) index if it's not part of a Jordan block of norm divisible by 8
        if (nz_flag == True):
            Not8vec += [i]



    ## Compute the number of Good-type solutions mod 8:
    ## ------------------------------------------------

    ## Setup the indexing sets for additional zero congruence solutions
    Q_Not8 = self.extract_variables(Not8vec)
    Not8 = Set(Not8vec)
    Is8 = Set(range(n)) - Not8

    Z = Set(Zvec)
    Z_Not8 = Not8.intersection(Z)
    Z_Is8 = Is8.intersection(Z)
    Is8_minus_Z = Is8 - Z_Is8


    ## DIAGNOSTIC
    verbose("Z = " + str(Z))
    verbose("Z_Not8 = " + str(Z_Not8))
    verbose("Z_Is8 = " + str(Z_Is8))
    verbose("Is8_minus_Z = " + str(Is8_minus_Z))


    ## Take cases on the existence of additional non-zero congruence conditions (mod 2)
    if NZvec == None:
        total = (4 ** len(Z_Is8)) * (8 ** len(Is8_minus_Z)) \
            * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(Z_Not8), None)
    else:
        ZNZ = Z + Set(NZvec)
        ZNZ_Not8 = Not8.intersection(ZNZ)
        ZNZ_Is8 = Is8.intersection(ZNZ)
        Is8_minus_ZNZ = Is8 - ZNZ_Is8

        ## DIAGNOSTIC
        verbose("ZNZ = " + str(ZNZ))
        verbose("ZNZ_Not8 = " + str(ZNZ_Not8))
        verbose("ZNZ_Is8 = " + str(ZNZ_Is8))
        verbose("Is8_minus_ZNZ = " + str(Is8_minus_ZNZ))

        total = (4 ** len(Z_Is8)) * (8 ** len(Is8_minus_Z)) \
            * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(Z_Not8), None) \
            - (4 ** len(ZNZ_Is8)) * (8 ** len(Is8_minus_ZNZ)) \
            * Q_Not8.count_congruence_solutions__good_type(2, 3, m, list(ZNZ_Not8), None)


    ## DIAGNOSTIC
    verbose("total = " + str(total))


    ## Return the associated Good-type representation density
    good_density = QQ(total) / 8**(n-1)
    return good_density
Exemplo n.º 26
0
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)
        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)
    if F.base_ring() != C:
        if emb is None:
            compF = F.change_ring(C)
        else:
            compF = F.change_ring(emb)
    else:
        compF = F

    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
    x = f.parent().gen()
    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)
def local_good_density_congruence_odd(self, p, m, Zvec, NZvec):
    """
    Finds the Good-type local density of Q representing `m` at `p`.
    (Assuming that `p` > 2 and Q is given in local diagonal form.)

    The additional congruence condition arguments Zvec and NZvec can
    be either a list of indices or None.  Zvec = [] is equivalent to
    Zvec = None which both impose no additional conditions, but NZvec
    = [] returns no solutions always while NZvec = None imposes no
    additional condition.

    TO DO: Add type checking for Zvec, NZvec, and that Q is in local
    normal form.

    INPUT:
        Q -- quadratic form assumed to be diagonal and p-integral

        `p` -- a prime number

        `m` -- an integer

        Zvec, NZvec -- non-repeating lists of integers in range(self.dim()) or None

    OUTPUT:
        a rational number

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,2,3])
        sage: Q.local_good_density_congruence_odd(3, 1, None, None)
        2/3

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.local_good_density_congruence_odd(3, 1, None, None)
        8/9

    """
    n = self.dim()

    ## Put the Zvec congruence condition in a standard form
    if Zvec == None:
        Zvec = []


    ## Sanity Check on Zvec and NZvec:
    ## -------------------------------
    Sn = Set(range(n))
    if (Zvec != None) and (len(Set(Zvec) + Sn) > n):
        raise RuntimeError("Zvec must be a subset of {0, ..., n-1}.")
    if (NZvec != None) and (len(Set(NZvec) + Sn) > n):
        raise RuntimeError("NZvec must be a subset of {0, ..., n-1}.")



    ## Assuming Q is diagonal, find the indices of the p-unit (diagonal) entries
    UnitVec = [i  for i in range(n)  if (self[i,i] % p) != 0]
    NonUnitVec = list(Set(range(n)) - Set(UnitVec))


    ## Take cases on the existence of additional non-zero congruence conditions (mod p)
    UnitVec_minus_Zvec = list(Set(UnitVec) - Set(Zvec))
    NonUnitVec_minus_Zvec = list(Set(NonUnitVec) - Set(Zvec))
    Q_Unit_minus_Zvec = self.extract_variables(UnitVec_minus_Zvec)

    if (NZvec == None):
        if m % p != 0:
            total = Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(p, m) * p**len(NonUnitVec_minus_Zvec)          ## m != 0 (mod p)
        else:
            total = (Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(p, m) - 1) * p**len(NonUnitVec_minus_Zvec)    ## m == 0 (mod p)

    else:
        UnitVec_minus_ZNZvec = list(Set(UnitVec) - (Set(Zvec) + Set(NZvec)))
        NonUnitVec_minus_ZNZvec = list(Set(NonUnitVec) - (Set(Zvec) + Set(NZvec)))
        Q_Unit_minus_ZNZvec = self.extract_variables(UnitVec_minus_ZNZvec)

        if m % p != 0:         ## m != 0 (mod p)
            total = Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(p, m) * p**len(NonUnitVec_minus_Zvec) \
                    - Q_Unit_minus_ZNZvec.count_modp_solutions__by_Gauss_sum(p, m) * p**len(NonUnitVec_minus_ZNZvec)
        else:                  ## m == 0 (mod p)
            total = (Q_Unit_minus_Zvec.count_modp_solutions__by_Gauss_sum(p, m) - 1) * p**len(NonUnitVec_minus_Zvec) \
                    - (Q_Unit_minus_ZNZvec.count_modp_solutions__by_Gauss_sum(p, m) - 1) * p**len(NonUnitVec_minus_ZNZvec)

    ## Return the Good-type representation density
    good_density = QQ(total) / p**(n-1)
    return good_density
Exemplo n.º 28
0
def gamma__exact(n):
    """
    Evaluates the exact value of the `\Gamma` function at an integer or
    half-integer argument.

    EXAMPLES::

        sage: gamma__exact(4)
        6
        sage: gamma__exact(3)
        2
        sage: gamma__exact(2)
        1
        sage: gamma__exact(1)
        1

        sage: gamma__exact(1/2)
        sqrt(pi)
        sage: gamma__exact(3/2)
        1/2*sqrt(pi)
        sage: gamma__exact(5/2)
        3/4*sqrt(pi)
        sage: gamma__exact(7/2)
        15/8*sqrt(pi)

        sage: gamma__exact(-1/2)
        -2*sqrt(pi)
        sage: gamma__exact(-3/2)
        4/3*sqrt(pi)
        sage: gamma__exact(-5/2)
        -8/15*sqrt(pi)
        sage: gamma__exact(-7/2)
        16/105*sqrt(pi)

    TESTS::

        sage: gamma__exact(1/3)
        Traceback (most recent call last):
        ...
        TypeError: you must give an integer or half-integer argument
    """
    from sage.all import sqrt
    # SANITY CHECK
    if (not n in QQ) or (denominator(n) > 2):
        raise TypeError("you must give an integer or half-integer argument")

    if denominator(n) == 1:
        if n <= 0:
            return infinity
        if n > 0:
            return factorial(n - 1)
    else:
        ans = QQ(1)
        while (n != QQ(1) / 2):
            if n < 0:
                ans *= QQ(1) / n
                n += 1
            elif n > 0:
                n += -1
                ans *= n

        ans *= sqrt(pi)
        return ans
Exemplo n.º 29
0
    def _find_scaling_period(self):
        r"""
        Uses the integral period map of the modular symbol implementation in sage
        in order to determine the scaling. The resulting modular symbol is correct
        only for the `X_0`-optimal curve, at least up to a possible factor +- a
        power of 2.

        EXAMPLES::

            sage: E = EllipticCurve('11a1')
            sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period')
            sage: m._e
            (1/5, 1)
            sage: E = EllipticCurve('11a2')
            sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period')
            sage: m._e
            (1, 5)
            sage: E = EllipticCurve('121b2')
            sage: m = sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1,normalize='period')
            sage: m._e
            (0, 11/2, 0, 11/2, 11/2, 0, 0, -3, 2, 1/2, -1, 3/2)

        TESTS::

            sage: E = EllipticCurve('19a1')
            sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none')
            sage: m._find_scaling_period()
            sage: m._scaling
            1

            sage: E = EllipticCurve('19a2')
            sage: m = E.modular_symbol(sign=+1, implementation='sage', normalize='none')
            sage: m._scaling
            1
            sage: m._find_scaling_period()
            sage: m._scaling
            3
        """
        P = self._modsym.integral_period_mapping()
        self._e = P.matrix().transpose().row(0)
        self._e /= 2
        E = self._E
        try:
            crla = parse_cremona_label(E.label())
        except RuntimeError:  # raised when curve is outside of the table
            print(
                "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by a rational number."
            )
            self._scaling = 1
        else:
            cr0 = Integer(crla[0]).str() + crla[1] + '1'
            E0 = EllipticCurve(cr0)
            if self._sign == 1:
                q = E0.period_lattice().basis()[0] / E.period_lattice().basis(
                )[0]
            else:
                q = E0.period_lattice().basis()[1].imag() / E.period_lattice(
                ).basis()[1].imag()
                if E0.real_components() == 1:
                    q *= 2
                if E.real_components() == 1:
                    q /= 2
            q = QQ(int(round(q * 200))) / 200
            verbose('scale modular symbols by %s' % q)
            self._scaling = q
        c = self(0)  #  required, to change base point from oo to 0
        if c < 0:
            c *= -1
            self._scaling *= -1
        self._at_zero = c
        self._e *= self._scaling
Exemplo n.º 30
0
 def extract(cls, obj):
     """
     Takes an object extracted by the json parser and decodes the
     special-formating dictionaries used to store special types.
     """
     if isinstance(obj, dict) and 'data' in obj:
         if len(obj) == 2 and '__ComplexList__' in obj:
             return [complex(*v) for v in obj['data']]
         elif len(obj) == 2 and '__QQList__' in obj:
             return [QQ(tuple(v)) for v in obj['data']]
         elif len(obj) == 3 and '__NFList__' in obj and 'base' in obj:
             base = cls.extract(obj['base'])
             return [cls._extract(base, c) for c in obj['data']]
         elif len(obj) == 2 and '__IntDict__' in obj:
             return {Integer(k): cls.extract(v) for k, v in obj['data']}
         elif len(obj) == 3 and '__Vector__' in obj and 'base' in obj:
             base = cls.extract(obj['base'])
             return vector([cls._extract(base, v) for v in obj['data']])
         elif len(obj) == 2 and '__Rational__' in obj:
             return Rational(*obj['data'])
         elif len(obj) == 3 and '__RealLiteral__' in obj and 'prec' in obj:
             return LmfdbRealLiteral(RealField(obj['prec']), obj['data'])
         elif len(obj) == 2 and '__complex__' in obj:
             return complex(*obj['data'])
         elif len(obj) == 3 and '__Complex__' in obj and 'prec' in obj:
             return ComplexNumber(ComplexField(obj['prec']), *obj['data'])
         elif len(obj) == 3 and '__NFElt__' in obj and 'parent' in obj:
             return cls._extract(cls.extract(obj['parent']), obj['data'])
         elif len(obj) == 3 and ('__NFRelative__' in obj or '__NFAbsolute__'
                                 in obj) and 'vname' in obj:
             poly = cls.extract(obj['data'])
             return NumberField(poly, name=obj['vname'])
         elif len(obj) == 2 and '__NFCyclotomic__' in obj:
             return CyclotomicField(obj['data'])
         elif len(obj) == 2 and '__IntegerRing__' in obj:
             return ZZ
         elif len(obj) == 2 and '__RationalField__' in obj:
             return QQ
         elif len(
                 obj) == 3 and '__RationalPoly__' in obj and 'vname' in obj:
             return QQ[obj['vname']]([QQ(tuple(v)) for v in obj['data']])
         elif len(
                 obj
         ) == 4 and '__Poly__' in obj and 'vname' in obj and 'base' in obj:
             base = cls.extract(obj['base'])
             return base[obj['vname']](
                 [cls._extract(base, c) for c in obj['data']])
         elif len(
                 obj
         ) == 5 and '__PowerSeries__' in obj and 'vname' in obj and 'base' in obj and 'prec' in obj:
             base = cls.extract(obj['base'])
             prec = infinity if obj['prec'] == 'inf' else int(obj['prec'])
             return base[[obj['vname']
                          ]]([cls._extract(base, c) for c in obj['data']],
                             prec=prec)
         elif len(obj) == 2 and '__date__' in obj:
             return datetime.datetime.strptime(obj['data'],
                                               "%Y-%m-%d").date()
         elif len(obj) == 2 and '__time__' in obj:
             return datetime.datetime.strptime(obj['data'],
                                               "%H:%M:%S.%f").time()
         elif len(obj) == 2 and '__datetime__' in obj:
             return datetime.datetime.strptime(obj['data'],
                                               "%Y-%m-%d %H:%M:%S.%f")
     return obj