Beispiel #1
0
def q_binomial(n, k, p=None):
    """
    Returns the `q`-binomial coefficient.

    If `p` is unspecified, then it defaults to using the generator `q` for
    a univariate polynomial ring over the integers.

    The q-binomials are computed as a product of cyclotomic polynomials (cf. [CH2006]_).

    REFERENCES:

    .. [CH2006] William Y.C. Chen and Qing-Hu Hou, "Factors of the Gaussian
       coefficients", Discrete Mathematics 306 (2006), 1446-1449.
       http://dx.doi.org/10.1016/j.disc.2006.03.031

    EXAMPLES::

        sage: import sage.combinat.q_analogues as q_analogues
        sage: q_analogues.q_binomial(4,2)
        q^4 + q^3 + 2*q^2 + q + 1
        sage: q_analogues.q_binomial(4,5)
        0
        sage: p = ZZ['p'].0
        sage: q_analogues.q_binomial(4,2,p)
        p^4 + p^3 + 2*p^2 + p + 1
        sage: q_analogues.q_binomial(2,3)
        0

    The `q`-analogue of ``binomial(n,k)`` is currently only defined for
    `n` a nonnegative integer, it is zero for negative k  (trac #11411)::

        sage: q_analogues.q_binomial(5, -1)
        0
    """
    if not (n in ZZ and k in ZZ):
        raise ValueError("Argument (%s, %s) must be integers." % (n, k))
    if n < 0:
        raise NotImplementedError
    if 0 <= k and k <= n:
        if p == None:
            R = ZZ["q"]
        else:
            R = ZZ[p]
        from sage.functions.all import floor

        return prod(
            R.cyclotomic_polynomial(d) for d in range(2, n + 1) if floor(n / d) != floor(k / d) + floor((n - k) / d)
        )
    else:
        return 0
def Pall_mass_density_at_odd_prime(self, p):
    """
    Returns the local representation density of a form (for
    representing itself) defined over `ZZ`, at some prime `p>2`.

    REFERENCES:
        Pall's article "The Weight of a Genus of Positive n-ary Quadratic Forms"
        appearing in Proc. Symp. Pure Math. VIII (1965), pp95--105.

    INPUT:
        `p` -- a prime number > 2.

    OUTPUT:
        a rational number.

    EXAMPLES::

        sage: Q = QuadraticForm(ZZ, 3, [1,0,0,1,0,1])
        sage: Q.Pall_mass_density_at_odd_prime(3)
        [(0, Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 1 0 0 ]
        [ * 1 0 ]
        [ * * 1 ])] [(0, 3, 8)] [8/9] 8/9
        8/9
    """
    ## Check that p is a positive prime -- unnecessary since it's done implicitly in the next step. =)
    if p<=2:
        raise TypeError, "Oops!  We need p to be a prime > 2."

    ## Step 1: Obtain a p-adic (diagonal) local normal form, and
    ## compute the invariants for each Jordan block.
    jordan_list = self.jordan_blocks_by_scale_and_unimodular(p)
    modified_jordan_list = [(a, Q.dim(), Q.det())  for (a,Q) in jordan_list]     ## List of pairs (scale, det)
    #print jordan_list
    #print modified_jordan_list

    ## Step 2: Compute the list of local masses for each Jordan block
    jordan_mass_list = []
    for (s,n,d) in modified_jordan_list:
        generic_factor = prod([1 - p**(-2*j)  for j in range(1, floor((n-1)/2)+1)])
        #print "generic factor: ", generic_factor
        if (n % 2 == 0):
            m = n/2
            generic_factor *= (1 + legendre_symbol(((-1)**m) * d, p) * p**(-m))
        #print "jordan_mass: ", generic_factor
        jordan_mass_list = jordan_mass_list + [generic_factor]

    ## Step 3: Compute the local mass $\al_p$ at p.
        MJL = modified_jordan_list
    s = len(modified_jordan_list)
    M = [sum([MJL[j][1]  for j in range(i, s)])  for i in range(s-1)]    ## Note: It's s-1 since we don't need the last M.
    #print "M = ", M
    nu = sum([M[i] * MJL[i][0] * MJL[i][1]  for i in range(s-1)]) - ZZ(sum([J[0] * J[1] * (J[1]-1)  for J in MJL]))/ZZ(2)
    p_mass = prod(jordan_mass_list)
    p_mass *= 2**(s-1) * p**nu

    print jordan_list, MJL, jordan_mass_list, p_mass

    ## Return the result
    return p_mass
Beispiel #3
0
    def test(self,do_twoy=False,up_to_M=0):
        r""" Return the number of digits we believe are correct (at least) 

        EXAMPLES::

        
            sage: G=MySubgroup(Gamma0(1))
            sage: R=mpmath.mpf(9.53369526135355755434423523592877032382125639510725198237579046413534)
            sage: F=MaassWaveformElement(G,R)    
            sage: F.test()
            7



        """
        # If we have a Gamma_0(N) we can use Hecke operators
        if(not do_twoy and (isinstance(self._space,sage.modular.arithgroup.congroup_gamma0.Gamma0_class) or \
            self._space._G.is_congruence)):
            # isinstance(self._space,sage.modular.arithgroup.congroup_sl2z.SL2Z_class_with_category))):
            if(self._space._verbose>1):
                print "Check Hecke relations!"
            er=test_Hecke_relations(2,3,self.coeffs)
            d=floor(-mpmath.log10(er))
            if(self._space._verbose>1):
                print "Hecke is ok up to ",d,"digits!"
            return d
        else:
            # Test two Y's
            nd=self._nd+5
            [M0,Y0]=find_Y_and_M(G,R,nd)
            Y1=Y0*0.95
            C1=Maassform_coeffs(self,R,Mset=M0,Yset=Y0 ,ndigs=nd )[0]
            C2=Maassform_coeffs(self,R,Mset=M0,Yset=Y1 ,ndigs=nd )[0]
            er=mpmath.mpf(0)
            for j in range(2,max(M0/2,up_to_M)):
                t=abs(C1[j]-C2[j])
                print "|C1-C2[",j,"]|=|",C1[j],'-',C2[j],'|=',t
                if(t>er):
                    er=t
            d=floor(-mpmath.log10(er))
            print "Hecke is ok up to ",d,"digits!"
            return d
Beispiel #4
0
def katz_expansions(k0,p,ellp,mdash,n):
    r"""
    Returns a list e of q-expansions, and the Eisenstein series `E_{p-1} = 1 +
    \dots`, all modulo `(p^\text{mdash},q^\text{ellp})`. The list e contains
    the elements `e_{i,s}` in the Katz expansions basis in Step 3 of Algorithm
    1 in [AGBL]_ when one takes as input to that algorithm p,m and k and define
    ``k0``, ``mdash``, n, ``ellp = ell*p`` as in Step 1.

    INPUT:

    - ``k0`` -- integer in range 0 to p-1.
    - ``p`` -- prime at least 5.
    - ``ellp,mdash,n`` -- positive integers.

    OUTPUT:

    - list of q-expansions and the Eisenstein series E_{p-1} modulo
      `(p^\text{mdash},q^\text{ellp})`.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import katz_expansions
        sage: katz_expansions(0,5,10,3,4)
        ([1 + O(q^10), q + 6*q^2 + 27*q^3 + 98*q^4 + 65*q^5 + 37*q^6 + 81*q^7 + 85*q^8 + 62*q^9 + O(q^10)],
        1 + 115*q + 35*q^2 + 95*q^3 + 20*q^4 + 115*q^5 + 105*q^6 + 60*q^7 + 25*q^8 + 55*q^9 + O(q^10))
    """
    S = Zmod(p**mdash)

    Ep1 = eisenstein_series_qexp(p-1, ellp, K=S, normalization="constant")
    E4 =  eisenstein_series_qexp(4,   ellp, K=S, normalization="constant")
    E6 =  eisenstein_series_qexp(6,   ellp, K=S, normalization="constant")

    delta = delta_qexp(ellp, K=S)
    h = delta / E6**2
    hj = delta.parent()(1)
    e = []

    # We compute negative powers of E_(p-1) successively (this saves a great
    # deal of time). The effect is that Ep1mi = Ep1 ** (-i).
    Ep1m1 = ~Ep1
    Ep1mi = 1
    for i in xrange(0,n+1):
        Wi,hj = compute_Wi(k0 + i*(p-1),p,h,hj,E4,E6)
        for bis in Wi:
            eis = p**floor(i/(p+1)) * Ep1mi * bis
            e.append(eis)
        Ep1mi = Ep1mi * Ep1m1

    return e,Ep1
def reduced_binary_form1(self):
    r"""
    Reduce the form `ax^2 + bxy+cy^2` to satisfy the reduced condition `|b| \le
    a \le c`, with `b \ge 0` if `a = c`. This reduction occurs within the
    proper class, so all transformations are taken to have determinant 1.

    EXAMPLES::

        sage: QuadraticForm(ZZ,2,[5,5,2]).reduced_binary_form1()
        (
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 2 -1 ]
        [ * 2 ]                                                            ,
        <BLANKLINE>
        [ 0 -1]
        [ 1  1]
        )
    """
    if self.dim() != 2:
        raise TypeError("This must be a binary form for now...")

    R = self.base_ring()
    interior_reduced_flag = False
    Q = deepcopy(self)
    M = matrix(R, 2, 2, [1,0,0,1])

    while not interior_reduced_flag:
        interior_reduced_flag = True

        ## Arrange for a <= c
        if Q[0,0] > Q[1,1]:
            M_new = matrix(R,2,2,[0, -1, 1, 0])
            Q = Q(M_new)
            M = M * M_new
            interior_reduced_flag = False
            #print "A"

        ## Arrange for |b| <= a
        if abs(Q[0,1]) > Q[0,0]:
            r = R(floor(round(Q[0,1]/(2*Q[0,0]))))
            M_new = matrix(R,2,2,[1, -r, 0, 1])
            Q = Q(M_new)
            M = M * M_new
            interior_reduced_flag = False
            #print "B"

    return Q, M
Beispiel #6
0
def isqrt(x):
    """
    Returns an integer square root, i.e., the floor of a square root.

    EXAMPLES::

        sage: isqrt(10)
        3
        sage: isqrt(10r)
        3
    """
    try:
        return x.isqrt()
    except AttributeError:
        from sage.functions.all import floor
        n = sage.rings.integer.Integer(floor(x))
        return n.isqrt()
Beispiel #7
0
def isqrt(x):
    """
    Returns an integer square root, i.e., the floor of a square root.

    EXAMPLES::

        sage: isqrt(10)
        3
        sage: isqrt(10r)
        3
    """
    try:
        return x.isqrt()
    except AttributeError:
        from sage.functions.all import floor
        n = sage.rings.integer.Integer(floor(x))
        return n.isqrt()
Beispiel #8
0
def reduced_binary_form1(self):
    r"""
    Reduce the form `ax^2 + bxy+cy^2` to satisfy the reduced condition `|b| \le
    a \le c`, with `b \ge 0` if `a = c`. This reduction occurs within the
    proper class, so all transformations are taken to have determinant 1.

    EXAMPLES::

        sage: QuadraticForm(ZZ,2,[5,5,2]).reduced_binary_form1()
        (
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 2 -1 ]
        [ * 2 ]                                                            ,
        <BLANKLINE>
        [ 0 -1]
        [ 1  1]
        )
    """
    if self.dim() != 2:
        raise TypeError("This must be a binary form for now...")

    R = self.base_ring()
    interior_reduced_flag = False
    Q = deepcopy(self)
    M = matrix(R, 2, 2, [1,0,0,1])

    while not interior_reduced_flag:
        interior_reduced_flag = True

        ## Arrange for a <= c
        if Q[0,0] > Q[1,1]:
            M_new = matrix(R,2,2,[0, -1, 1, 0])
            Q = Q(M_new)
            M = M * M_new
            interior_reduced_flag = False

        ## Arrange for |b| <= a
        if abs(Q[0,1]) > Q[0,0]:
            r = R(floor(round(Q[0,1]/(2*Q[0,0]))))
            M_new = matrix(R,2,2,[1, -r, 0, 1])
            Q = Q(M_new)
            M = M * M_new
            interior_reduced_flag = False

    return Q, M
Beispiel #9
0
def hecke_series_degree_bound(p, N, k, m):
    r"""
    Returns the ``Wan bound`` on the degree of the characteristic series of the
    Atkin operator on p-adic overconvergent modular forms of level
    `\Gamma_0(N)` and weight k when reduced modulo `p^m`. This bound depends
    only upon p, `k \pmod{p-1}`, and N. It uses Lemma 3.1 in [DW]_.

    INPUT:

    - ``p`` -- prime at least 5.
    - ``N`` -- positive integer not divisible by p.
    - ``k`` -- even integer.
    - ``m`` -- positive integer.

    OUTPUT:

    A non-negative integer.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import hecke_series_degree_bound
        sage: hecke_series_degree_bound(13,11,100,5)
        39

    REFERENCES:

    .. [DW] Daqing Wan, "Dimension variation of classical and p-adic modular
       forms", Invent. Math. 133, (1998) 449-463.
    """
    k0 = k % (p - 1)
    ds = [dimension_modular_forms(N, k0)]
    ms = [ds[0]]
    sum = 0
    u = 1

    ord = 0
    while ord < m:
        ds.append(dimension_modular_forms(N, k0 + u * (p - 1)))
        ms.append(ds[u] - ds[u - 1])
        sum = sum + u * ms[u]
        ord = floor(((p - 1) / (p + 1)) * sum - ds[u])
        u = u + 1

    return (ds[u - 1] - 1)
Beispiel #10
0
def hecke_series_degree_bound(p,N,k,m):
    r"""
    Returns the ``Wan bound`` on the degree of the characteristic series of the
    Atkin operator on p-adic overconvergent modular forms of level
    `\Gamma_0(N)` and weight k when reduced modulo `p^m`. This bound depends
    only upon p, `k \pmod{p-1}`, and N. It uses Lemma 3.1 in [DW]_.

    INPUT:

    - ``p`` -- prime at least 5.
    - ``N`` -- positive integer not divisible by p.
    - ``k`` -- even integer.
    - ``m`` -- positive integer.

    OUTPUT:

    A non-negative integer.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import hecke_series_degree_bound
        sage: hecke_series_degree_bound(13,11,100,5)
        39

    REFERENCES:

    .. [DW] Daqing Wan, "Dimension variation of classical and p-adic modular
       forms", Invent. Math. 133, (1998) 449-463.
    """
    k0 = k % (p-1)
    ds = [dimension_modular_forms(N, k0)]
    ms = [ds[0]]
    sum = 0
    u = 1

    ord = 0
    while ord < m:
        ds.append(dimension_modular_forms(N,k0 + u*(p-1)))
        ms.append(ds[u] - ds[u-1])
        sum = sum + u*ms[u]
        ord = floor(((p-1)/(p+1))*sum - ds[u])
        u = u + 1

    return (ds[u-1] - 1)
Beispiel #11
0
    def plot_lattice(self):
        r"""
        Plot the lattice (i.e. its points in the cut-off bounds of ``self``).

        OUTPUT:

        - a plot.

        EXAMPLES::

            sage: from sage.geometry.toric_plotter import ToricPlotter
            sage: tp = ToricPlotter(dict(), 2)
            sage: print tp.plot_lattice()
            Graphics object consisting of 1 graphics primitive
        """
        if not self.show_lattice:
            # Plot the origin anyway, otherwise rays/generators may look ugly.
            return self.plot_points([self.origin])
        d = self.dimension
        extra_options = self.extra_options
        if d == 1:
            points = ((x, 0) for x in range(ceil(self.xmin),
                                            floor(self.xmax) + 1))
        elif d == 2:
            points = ((x, y) for x in range(ceil(self.xmin),
                                            floor(self.xmax) + 1)
                      for y in range(ceil(self.ymin),
                                     floor(self.ymax) + 1))
        elif d == 3:
            points = ((x, y, z) for x in range(ceil(self.xmin),
                                               floor(self.xmax) + 1)
                      for y in range(ceil(self.ymin),
                                     floor(self.ymax) + 1)
                      for z in range(ceil(self.zmin),
                                     floor(self.zmax) + 1))
        if self.mode == "round":
            r = 1.01 * self.radius  # To make sure integer values work OK.
            points = (pt for pt in points if vector(pt).norm() <= r)
        f = self.lattice_filter
        if f is not None:
            points = (pt for pt in points if f(pt))
        return self.plot_points(tuple(points))
Beispiel #12
0
    def plot_lattice(self):
        r"""
        Plot the lattice (i.e. its points in the cut-off bounds of ``self``).

        OUTPUT:

        - a plot.

        EXAMPLES::

            sage: from sage.geometry.toric_plotter import ToricPlotter
            sage: tp = ToricPlotter(dict(), 2)
            sage: print tp.plot_lattice()
            Graphics object consisting of 1 graphics primitive
        """
        if not self.show_lattice:
            # Plot the origin anyway, otherwise rays/generators may look ugly.
            return self.plot_points([self.origin])
        d = self.dimension
        extra_options = self.extra_options
        if d == 1:
            points = ((x, 0) for x in range(ceil(self.xmin), floor(self.xmax) + 1))
        elif d == 2:
            points = (
                (x, y)
                for x in range(ceil(self.xmin), floor(self.xmax) + 1)
                for y in range(ceil(self.ymin), floor(self.ymax) + 1)
            )
        elif d == 3:
            points = (
                (x, y, z)
                for x in range(ceil(self.xmin), floor(self.xmax) + 1)
                for y in range(ceil(self.ymin), floor(self.ymax) + 1)
                for z in range(ceil(self.zmin), floor(self.zmax) + 1)
            )
        if self.mode == "round":
            r = 1.01 * self.radius  # To make sure integer values work OK.
            points = (pt for pt in points if vector(pt).norm() <= r)
        f = self.lattice_filter
        if f is not None:
            points = (pt for pt in points if f(pt))
        return self.plot_points(tuple(points))
def mass__by_Siegel_densities(self, odd_algorithm="Pall", even_algorithm="Watson"):
    """
    Gives the mass of transformations (det 1 and -1).

    WARNING: THIS IS BROKEN RIGHT NOW... =(

    Optional Arguments:

    - When p > 2  --  odd_algorithm = "Pall" (only one choice for now)
    - When p = 2  --  even_algorithm = "Kitaoka" or "Watson"

    REFERENCES:

    - Nipp's Book "Tables of Quaternary Quadratic Forms".
    - Papers of Pall (only for p>2) and Watson (for `p=2` -- tricky!).
    - Siegel, Milnor-Hussemoller, Conway-Sloane Paper IV, Kitoaka (all of which
      have problems...)

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.mass__by_Siegel_densities()
        1/384
        sage: Q.mass__by_Siegel_densities() - (2^Q.dim() * factorial(Q.dim()))^(-1)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1])
        sage: Q.mass__by_Siegel_densities()
        1/48
        sage: Q.mass__by_Siegel_densities() - (2^Q.dim() * factorial(Q.dim()))^(-1)
        0

    """
    ## Setup
    n = self.dim()
    s = floor((n-1)/2)
    if n % 2 != 0:
        char_d = squarefree_part(2*self.det())   ## Accounts for the det as a QF
    else:
        char_d = squarefree_part(self.det())

    ## Form the generic zeta product
    generic_prod = ZZ(2) * (pi)**(-ZZ(n) * (n+1) / 4)
    ##########################################
    generic_prod *= (self.det())**(ZZ(n+1)/2)  ## ***** This uses the Hessian Determinant ********
    ##########################################
    #print "gp1 = ", generic_prod
    generic_prod *= prod([gamma__exact(ZZ(j)/2)  for j in range(1,n+1)])
    #print "\n---", [(ZZ(j)/2, gamma__exact(ZZ(j)/2))  for j in range(1,n+1)]
    #print "\n---", prod([gamma__exact(ZZ(j)/2)  for j in range(1,n+1)])
    #print "gp2 = ", generic_prod
    generic_prod *= prod([zeta__exact(ZZ(j))  for j in range(2, 2*s+1, 2)])
    #print "\n---", [zeta__exact(ZZ(j))  for j in range(2, 2*s+1, 2)]
    #print "\n---", prod([zeta__exact(ZZ(j))  for j in range(2, 2*s+1, 2)])
    #print "gp3 = ", generic_prod
    if (n % 2 == 0):
        generic_prod *= ZZ(1) * quadratic_L_function__exact(n/2, (-1)**(n/2) * char_d)
        #print " NEW = ", ZZ(1) * quadratic_L_function__exact(n/2, (-1)**(n/2) * char_d)
        #print
    #print "gp4 = ", generic_prod

    #print "generic_prod =", generic_prod

    ## Determine the adjustment factors
    adj_prod = 1
    for p in prime_divisors(2 * self.det()):
        ## Cancel out the generic factors
        p_adjustment = prod([1 - ZZ(p)**(-j)  for j in range(2, 2*s+1, 2)])
        if (n % 2 == 0):
            p_adjustment *= ZZ(1) * (1 - kronecker((-1)**(n/2) * char_d, p) * ZZ(p)**(-n/2))
            #print " EXTRA = ", ZZ(1) * (1 - kronecker((-1)**(n/2) * char_d, p) * ZZ(p)**(-n/2))
        #print "Factor to cancel the generic one:", p_adjustment

        ## Insert the new mass factors
        if p == 2:
            if even_algorithm == "Kitaoka":
                p_adjustment = p_adjustment / self.Kitaoka_mass_at_2()
            elif even_algorithm == "Watson":
                p_adjustment = p_adjustment / self.Watson_mass_at_2()
            else:
                raise TypeError, "There is a problem -- your even_algorithm argument is invalid.  Try again. =("
        else:
            if odd_algorithm == "Pall":
                p_adjustment = p_adjustment / self.Pall_mass_density_at_odd_prime(p)
            else:
                raise TypeError, "There is a problem -- your optional arguments are invalid.  Try again. =("

        #print "p_adjustment for p =", p, "is", p_adjustment

        ## Put them together (cumulatively)
        adj_prod *= p_adjustment

    #print "Cumulative adj_prod =", adj_prod

        ## Extra adjustment for the case of a 2-dimensional form.
    #if (n == 2):
    #    generic_prod *= 2


    ## Return the mass
    mass = generic_prod * adj_prod
    return mass
Beispiel #14
0
def theta_series_degree_2(Q, prec):
    r"""
    Compute the theta series of degree 2 for the quadratic form Q.

    INPUT:

    - ``prec`` -- an integer.

    OUTPUT:

    dictionary, where:

    - keys are `{\rm GL}_2(\ZZ)`-reduced binary quadratic forms (given as triples of
      coefficients)
    - values are coefficients

    EXAMPLES::

        sage: Q2 = QuadraticForm(ZZ, 4, [1,1,1,1, 1,0,0, 1,0, 1])
        sage: S = Q2.theta_series_degree_2(10)
        sage: S[(0,0,2)]
        24
        sage: S[(1,0,1)]
        144
        sage: S[(1,1,1)]
        192

    AUTHORS:

    - Gonzalo Tornaria (2010-03-23)

    REFERENCE:

    - Raum, Ryan, Skoruppa, Tornaria, 'On Formal Siegel Modular Forms'
      (preprint)
    """
    if Q.base_ring() != ZZ:
        raise TypeError("The quadratic form must be integral")
    if not Q.is_positive_definite():
        raise ValueError("The quadratic form must be positive definite")
    try:
        X = ZZ(prec-1)    # maximum discriminant
    except TypeError:
        raise TypeError("prec is not an integer")

    if X < -1:
        raise ValueError("prec must be >= 0")

    if X == -1:
        return {}

    V = ZZ ** Q.dim()
    H = Q.Hessian_matrix()

    t = cputime()
    max = int(floor((X+1)/4))
    v_list = (Q.vectors_by_length(max))        # assume a>0
    v_list = [[V(_) for _ in vs] for vs in v_list]  # coerce vectors into V
    verbose("Computed vectors_by_length" , t)

    # Deal with the singular part
    coeffs = {(0,0,0):ZZ(1)}
    for i in range(1,max+1):
        coeffs[(0,0,i)] = ZZ(2) * len(v_list[i])

    # Now deal with the non-singular part
    a_max = int(floor(sqrt(X/3)))
    for a in range(1, a_max + 1):
        t = cputime()
        c_max = int(floor((a*a + X)/(4*a)))
        for c in range(a, c_max + 1):
            for v1 in v_list[a]:
                v1_H = v1 * H
                def B_v1(v):
                    return v1_H * v2
                for v2 in v_list[c]:
                    b = abs(B_v1(v2))
                    if b <= a and 4*a*c-b*b <= X:
                        qf = (a,b,c)
                        count = ZZ(4) if b == 0 else ZZ(2)
                        coeffs[qf] = coeffs.get(qf, ZZ(0)) + count
        verbose("done a = %d" % a, t)

    return coeffs
Beispiel #15
0
def higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, modformsring,
                          bound):
    r"""
    Returns a matrix `e` of size ``ell x elldashp`` over the integers modulo
    `p^\text{mdash}`, and the Eisenstein series `E_{p-1} = 1 + .\dots \bmod
    (p^\text{mdash},q^\text{elldashp})`. The matrix e contains the coefficients
    of the elements `e_{i,s}` in the Katz expansions basis in Step 3 of
    Algorithm 2 in [AGBL]_ when one takes as input to that algorithm
    `p`,`N`,`m` and `k` and define ``k0``, ``mdash``, ``n``, ``elldash``,
    ``elldashp = ell*dashp`` as in Step 1.

    INPUT:

    - ``p`` -- prime at least 5.
    - ``N`` -- positive integer at least 2 and not divisible by p (level).
    - ``k0`` -- integer in xrange 0 to p-1.
    - ``m,mdash,elldash,elldashp`` -- positive integers.
    - ``modformsring`` -- True or False.
    - ``bound`` -- positive (even) integer.

    OUTPUT:

    - matrix and q-expansion.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import higher_level_katz_exp
        sage: e,Ep1 = higher_level_katz_exp(5,2,0,1,2,4,20,true,6)
        sage: e
        [ 1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]
        [ 0  1 18 23 19  6  9  9 17  7  3 17 12  8 22  8 11 19  1  5]
        [ 0  0  1 11 20 16  0  8  4  0 18 15 24  6 15 23  5 18  7 15]
        [ 0  0  0  1  4 16 23 13  6  5 23  5  2 16  4 18 10 23  5 15]
        sage: Ep1
        1 + 15*q + 10*q^2 + 20*q^3 + 20*q^4 + 15*q^5 + 5*q^6 + 10*q^7 +
        5*q^9 + 10*q^10 + 5*q^11 + 10*q^12 + 20*q^13 + 15*q^14 + 20*q^15 + 15*q^16 +
        10*q^17 + 20*q^18 + O(q^20)
    """
    ordr = 1 / (p + 1)
    S = Zmod(p**mdash)
    Ep1 = eisenstein_series_qexp(p - 1,
                                 prec=elldashp,
                                 K=S,
                                 normalization="constant")

    n = floor(((p + 1) / (p - 1)) * (m + 1))
    Wjs = complementary_spaces(N, p, k0, n, mdash, elldashp, elldash,
                               modformsring, bound)

    Basis = []
    for j in xrange(n + 1):
        Wj = Wjs[j]
        dimj = len(Wj)
        Ep1minusj = Ep1**(-j)
        for i in xrange(dimj):
            wji = Wj[i]
            b = p**floor(ordr * j) * wji * Ep1minusj
            Basis.append(b)

    # extract basis as a matrix

    ell = len(Basis)
    M = matrix(S, ell, elldashp)
    for i in xrange(ell):
        for j in xrange(elldashp):
            M[i, j] = Basis[i][j]

    ech_form(M, p)  # put it into echelon form

    return M, Ep1
Beispiel #16
0
 def rangea(t):
     tp0=t*wprime0
     tp1=t*wprime1
     return sorted(list(union(range(floor(xmin-_sage_const_1 -tp0-wprime0),ceil(xmax-tp0)+_sage_const_1 ),range(floor(ymin-_sage_const_1 -tp1-wprime1),ceil(ymax-tp1)+_sage_const_1 ))),key=abs)
Beispiel #17
0
def vectors_by_length(self, bound):
    """
    Returns a list of short vectors together with their values.

    This is a naive algorithm which uses the Cholesky decomposition,
    but does not use the LLL-reduction algorithm.

    INPUT:

       bound -- an integer >= 0

    OUTPUT:

        A list L of length (bound + 1) whose entry L `[i]` is a list of
        all vectors of length `i`.

    Reference: This is a slightly modified version of Cohn's Algorithm
    2.7.5 in "A Course in Computational Number Theory", with the
    increment step moved around and slightly re-indexed to allow clean
    looping.

    Note: We could speed this up for very skew matrices by using LLL
    first, and then changing coordinates back, but for our purposes
    the simpler method is efficient enough. =)

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1])
        sage: Q.vectors_by_length(5)
        [[[0, 0]],
         [[0, -1], [-1, 0]],
         [[-1, -1], [1, -1]],
         [],
         [[0, -2], [-2, 0]],
         [[-1, -2], [1, -2], [-2, -1], [2, -1]]]

    ::

        sage: Q1 = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Q1.vectors_by_length(5)
        [[[0, 0, 0, 0]],
         [[-1, 0, 0, 0]],
         [],
         [[0, -1, 0, 0]],
         [[-1, -1, 0, 0], [1, -1, 0, 0], [-2, 0, 0, 0]],
         [[0, 0, -1, 0]]]

    ::

        sage: Q = QuadraticForm(ZZ, 4, [1,1,1,1, 1,0,0, 1,0, 1])
        sage: list(map(len, Q.vectors_by_length(2)))
        [1, 12, 12]

    ::

        sage: Q = QuadraticForm(ZZ, 4, [1,-1,-1,-1, 1,0,0, 4,-3, 4])
        sage: list(map(len, Q.vectors_by_length(3)))
        [1, 3, 0, 3]
    """
    # pari uses eps = 1e-6 ; nothing bad should happen if eps is too big
    # but if eps is too small, roundoff errors may knock off some
    # vectors of norm = bound (see #7100)
    eps = RDF(1e-6)
    bound = ZZ(floor(max(bound, 0)))
    Theta_Precision = bound + eps
    n = self.dim()

    ## Make the vector of vectors which have a given value
    ## (So theta_vec[i] will have all vectors v with Q(v) = i.)
    theta_vec = [[] for i in range(bound + 1)]

    ## Initialize Q with zeros and Copy the Cholesky array into Q
    Q = self.cholesky_decomposition()

    ## 1. Initialize
    T = n * [RDF(0)]  ## Note: We index the entries as 0 --> n-1
    U = n * [RDF(0)]
    i = n - 1
    T[i] = RDF(Theta_Precision)
    U[i] = RDF(0)

    L = n * [0]
    x = n * [0]
    Z = RDF(0)

    ## 2. Compute bounds
    Z = (T[i] / Q[i][i]).sqrt(extend=False)
    L[i] = (Z - U[i]).floor()
    x[i] = (-Z - U[i]).ceil()

    done_flag = False
    Q_val_double = RDF(0)
    Q_val = 0  ## WARNING: Still need a good way of checking overflow for this value...

    ## Big loop which runs through all vectors
    while not done_flag:

        ## 3b. Main loop -- try to generate a complete vector x (when i=0)
        while (i > 0):
            #print " i = ", i
            #print " T[i] = ", T[i]
            #print " Q[i][i] = ", Q[i][i]
            #print " x[i] = ", x[i]
            #print " U[i] = ", U[i]
            #print " x[i] + U[i] = ", (x[i] + U[i])
            #print " T[i-1] = ", T[i-1]

            T[i - 1] = T[i] - Q[i][i] * (x[i] + U[i]) * (x[i] + U[i])

            #print " T[i-1] = ",  T[i-1]
            #print " x = ", x
            #print

            i = i - 1
            U[i] = 0
            for j in range(i + 1, n):
                U[i] = U[i] + Q[i][j] * x[j]

            ## Now go back and compute the bounds...
            ## 2. Compute bounds
            Z = (T[i] / Q[i][i]).sqrt(extend=False)
            L[i] = (Z - U[i]).floor()
            x[i] = (-Z - U[i]).ceil()

            # carry if we go out of bounds -- when Z is so small that
            # there aren't any integral vectors between the bounds
            # Note: this ensures T[i-1] >= 0 in the next iteration
            while (x[i] > L[i]):
                i += 1
                x[i] += 1

        ## 4. Solution found (This happens when i = 0)
        #print "-- Solution found! --"
        #print " x = ", x
        #print " Q_val = Q(x) = ", Q_val
        Q_val_double = Theta_Precision - T[0] + Q[0][0] * (x[0] + U[0]) * (
            x[0] + U[0])
        Q_val = Q_val_double.round()

        ## SANITY CHECK: Roundoff Error is < 0.001
        if abs(Q_val_double - Q_val) > 0.001:
            print(" x = ", x)
            print(" Float = ", Q_val_double, "   Long = ", Q_val)
            raise RuntimeError(
                "The roundoff error is bigger than 0.001, so we should use more precision somewhere..."
            )

        #print " Float = ", Q_val_double, "   Long = ", Q_val, "  XX "
        #print " The float value is ", Q_val_double
        #print " The associated long value is ", Q_val

        if (Q_val <= bound):
            #print " Have vector ",  x, " with value ", Q_val
            theta_vec[Q_val].append(deepcopy(x))

        ## 5. Check if x = 0, for exit condition. =)
        j = 0
        done_flag = True
        while (j < n):
            if (x[j] != 0):
                done_flag = False
            j += 1

        ## 3a. Increment (and carry if we go out of bounds)
        x[i] += 1
        while (x[i] > L[i]) and (i < n - 1):
            i += 1
            x[i] += 1

    #print " Leaving ThetaVectors()"
    return theta_vec
def Pall_mass_density_at_odd_prime(self, p):
    """
    Returns the local representation density of a form (for
    representing itself) defined over `ZZ`, at some prime `p>2`.

    REFERENCES:
        Pall's article "The Weight of a Genus of Positive n-ary Quadratic Forms"
        appearing in Proc. Symp. Pure Math. VIII (1965), pp95--105.

    INPUT:
        `p` -- a prime number > 2.

    OUTPUT:
        a rational number.

    EXAMPLES::

        sage: Q = QuadraticForm(ZZ, 3, [1,0,0,1,0,1])
        sage: Q.Pall_mass_density_at_odd_prime(3)
        [(0, Quadratic form in 3 variables over Integer Ring with coefficients:
        [ 1 0 0 ]
        [ * 1 0 ]
        [ * * 1 ])] [(0, 3, 8)] [8/9] 8/9
        8/9
    """
    ## Check that p is a positive prime -- unnecessary since it's done implicitly in the next step. =)
    if p <= 2:
        raise TypeError("Oops!  We need p to be a prime > 2.")

    ## Step 1: Obtain a p-adic (diagonal) local normal form, and
    ## compute the invariants for each Jordan block.
    jordan_list = self.jordan_blocks_by_scale_and_unimodular(p)
    modified_jordan_list = [(a, Q.dim(), Q.det()) for (a, Q) in jordan_list
                            ]  ## List of pairs (scale, det)
    #print jordan_list
    #print modified_jordan_list

    ## Step 2: Compute the list of local masses for each Jordan block
    jordan_mass_list = []
    for (s, n, d) in modified_jordan_list:
        generic_factor = prod(
            [1 - p**(-2 * j) for j in range(1,
                                            floor((n - 1) / 2) + 1)])
        #print "generic factor: ", generic_factor
        if (n % 2 == 0):
            m = n / 2
            generic_factor *= (1 + legendre_symbol(((-1)**m) * d, p) * p**(-m))
        #print "jordan_mass: ", generic_factor
        jordan_mass_list = jordan_mass_list + [generic_factor]

        ## Step 3: Compute the local mass $\al_p$ at p.
        MJL = modified_jordan_list
    s = len(modified_jordan_list)
    M = [sum([MJL[j][1] for j in range(i, s)]) for i in range(s - 1)
         ]  ## Note: It's s-1 since we don't need the last M.
    #print "M = ", M
    nu = sum([M[i] * MJL[i][0] * MJL[i][1] for i in range(s - 1)
              ]) - ZZ(sum([J[0] * J[1] * (J[1] - 1) for J in MJL])) / ZZ(2)
    p_mass = prod(jordan_mass_list)
    p_mass *= 2**(s - 1) * p**nu

    print jordan_list, MJL, jordan_mass_list, p_mass

    ## Return the result
    return p_mass
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.one() / 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
Beispiel #20
0
def berlekamp_welsh(deg, points):
    r"""
    Reconstruct polynomial with Berlekamp-Welsh algorithm.

    INPUT:

    - ``deg``    --  degree of polynomial to reconstruct.
    - ``points`` --  array of points (list of (x,y)-tuples).

    OUTPUT:

    Reconstructed polynomial.

    EXAMPLES::

        sage: from sage.crypto.smc.berlekamp_welsh import berlekamp_welsh
        sage: from sage.rings.finite_rings.constructor import FiniteField
        sage: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing

    Reconstruction with errors::

        sage: order = 2**8
        sage: F = FiniteField(order, 'a')
        sage: P = PolynomialRing(F, 'x')
        sage: n = 7
        sage: deg = 2
        sage: poly = F.fetch_int(42)
        sage: for i in range(1, deg+1): poly += F.random_element() * P.gen()**i

        sage: # evaluate polynomial at different points (shares)
        sage: points = [(F.fetch_int(i), poly(F.fetch_int(i))) for i in range(1, n+1)]
        sage: poly == berlekamp_welsh(deg, points)
        True

        sage: # introduce error
        sage: points[0] = (points[0][0], points[0][1] + F.fetch_int(9))
        sage: poly == berlekamp_welsh(deg, points)
        True

    """
    # check input vector
    F = points[0][0].parent()
    if not F.is_field():
        raise TypeError("points must be of field type.")
    for x, y in points:
        if x.parent() != F or y.parent() != F:
            raise TypeError("all points must be from same field.")
        
    # generate and solve system of linear equations
    from sage.functions.all import floor
    deg_E = floor((len(points) - (deg + 1)) / 2.)
    deg_Q = deg_E + deg
    from sage.matrix.all import Matrix
    from sage.all import vector
    sys_size = deg_Q + 1 + deg_E
    A = Matrix(F, sys_size)
    b = vector(F, sys_size)
    for n, (x, y) in enumerate(points):
        A[n] = ([x**i for i in range(deg_Q+1)]+[-y * x**i for i in range(deg_E)])
        b[n] = (y * x**deg_E)
    QE = A.solve_right(b)

    # reconstruct polynomial
    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
    P = PolynomialRing(F, 'x')
    Q = sum([coeff * P.gen()**i for i, coeff in enumerate(QE[:deg_Q+1])]);
    E = P.gen()**deg_E
    E += sum([coeff * P.gen()**i for i, coeff in enumerate(QE[deg_Q+1:])]);
    P = Q.quo_rem(E)[0]
    return P
def theta_series_degree_2(Q, prec):
    r"""
    Compute the theta series of degree 2 for the quadratic form Q.

    INPUT:

    - ``prec`` -- an integer.

    OUTPUT:

    dictionary, where:

    - keys are `{\rm GL}_2(\ZZ)`-reduced binary quadratic forms (given as triples of
      coefficients)
    - values are coefficients

    EXAMPLES::

        sage: Q2 = QuadraticForm(ZZ, 4, [1,1,1,1, 1,0,0, 1,0, 1])
        sage: S = Q2.theta_series_degree_2(10)
        sage: S[(0,0,2)]
        24
        sage: S[(1,0,1)]
        144
        sage: S[(1,1,1)]
        192

    AUTHORS:

    - Gonzalo Tornaria (2010-03-23)

    REFERENCE:

    - Raum, Ryan, Skoruppa, Tornaria, 'On Formal Siegel Modular Forms'
      (preprint)
    """
    if Q.base_ring() != ZZ:
        raise TypeError, "The quadratic form must be integral"
    if not Q.is_positive_definite():
        raise ValueError, "The quadratic form must be positive definite"
    try:
        X = ZZ(prec - 1)  # maximum discriminant
    except TypeError:
        raise TypeError, "prec is not an integer"

    if X < -1:
        raise ValueError, "prec must be >= 0"

    if X == -1:
        return {}

    V = ZZ**Q.dim()
    H = Q.Hessian_matrix()

    t = cputime()
    max = int(floor((X + 1) / 4))
    v_list = (Q.vectors_by_length(max))  # assume a>0
    v_list = map(lambda (vs): map(V, vs), v_list)  # coerce vectors into V
    verbose("Computed vectors_by_length", t)

    # Deal with the singular part
    coeffs = {(0, 0, 0): ZZ(1)}
    for i in range(1, max + 1):
        coeffs[(0, 0, i)] = ZZ(2) * len(v_list[i])

    # Now deal with the non-singular part
    a_max = int(floor(sqrt(X / 3)))
    for a in range(1, a_max + 1):
        t = cputime()
        c_max = int(floor((a * a + X) / (4 * a)))
        for c in range(a, c_max + 1):
            for v1 in v_list[a]:
                v1_H = v1 * H

                def B_v1(v):
                    return v1_H * v2

                for v2 in v_list[c]:
                    b = abs(B_v1(v2))
                    if b <= a and 4 * a * c - b * b <= X:
                        qf = (a, b, c)
                        count = ZZ(4) if b == 0 else ZZ(2)
                        coeffs[qf] = coeffs.get(qf, ZZ(0)) + count
        verbose("done a = %d" % a, t)

    return coeffs
def reduced_binary_form(self):
    """
    Find a form which is reduced in the sense that no further binary
    form reductions can be done to reduce the original form.

    EXAMPLES::

        sage: QuadraticForm(ZZ,2,[5,5,2]).reduced_binary_form()
        (
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 2 -1 ]
        [ * 2 ]                                                            ,
        <BLANKLINE>
        [ 0 -1]
        [ 1  1]
        )
    """
    R = self.base_ring()
    n = self.dim()
    interior_reduced_flag = False
    Q = deepcopy(self)
    M = matrix(R, n, n)
    for i in range(n):
        M[i,i] = 1


    while not interior_reduced_flag:
        interior_reduced_flag = True

        #print Q

        ## Arrange for (weakly) increasing diagonal entries
        for i in range(n):
            for j in range(i+1,n):
                if Q[i,i] > Q[j,j]:
                    M_new = matrix(R,n,n)
                    for k in range(n):
                        M_new[k,k] = 1
                    M_new[i,j] = -1
                    M_new[j,i] = 1
                    M_new[i,i] = 0
                    M_new[j,j] = 1

                    Q = Q(M_new)
                    M = M * M_new
                    interior_reduced_flag = False
                    #print "A"

                ## Arrange for |b| <= a
                if abs(Q[i,j]) > Q[i,i]:
                    r = R(floor(round(Q[i,j]/(2*Q[i,i]))))

                    M_new = matrix(R,n,n)
                    for k in range(n):
                        M_new[k,k] = 1
                    M_new[i,j] = -r

                    Q = Q(M_new)
                    M = M * M_new
                    interior_reduced_flag = False
                    #print "B"

    return Q, M
Beispiel #23
0
def level1_UpGj(p,klist,m):
    r"""
    Returns a list `[A_k]` of square matrices over ``IntegerRing(p^m)``
    parameterised by the weights k in ``klist``. The matrix `A_k` is the finite
    square matrix which occurs on input p,k and m in Step 6 of Algorithm 1 in
    [AGBL]_. Notational change from paper: In Step 1 following Wan we defined
    j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by
    ``kdiv`` so that we may use j as a column index for matrices.

    INPUT:

    - ``p`` -- prime at least 5.
    - ``klist`` -- list of integers congruent modulo `(p-1)` (the weights).
    - ``m`` -- positive integer.

    OUTPUT:

    - list of square matrices.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import level1_UpGj
        sage: level1_UpGj(7,[100],5)
        [
        [    1   980  4802     0     0]
        [    0 13727 14406     0     0]
        [    0 13440  7203     0     0]
        [    0  1995  4802     0     0]
        [    0  9212 14406     0     0]
        ]
    """
    # Step 1
    t = cputime()

    k0 = klist[0] % (p-1)
    n = floor(((p+1)/(p-1)) * (m+1))
    ell = dimension_modular_forms(1, k0 + n*(p-1))
    ellp = ell*p
    mdash = m + ceil(n/(p+1))

    verbose("done step 1", t)
    t = cputime()
    # Steps 2 and 3

    e,Ep1 = katz_expansions(k0,p,ellp,mdash,n)

    verbose("done steps 2+3", t)
    t=cputime()
    # Step 4

    G = compute_G(p, Ep1)
    Alist = []

    verbose("done step 4a", t)
    t=cputime()
    for k in klist:
        k = ZZ(k) # convert to sage integer
        kdiv = k // (p-1)
        Gkdiv = G**kdiv
        u = []
        for i in xrange(0,ell):
            ei = e[i]
            ui = Gkdiv*ei
            u.append(ui)

        verbose("done step 4b", t)
        t = cputime()
        # Step 5 and computation of T in Step 6

        S = e[0][0].parent()
        T = matrix(S,ell,ell)

        for i in xrange(0,ell):
            for j in xrange(0,ell):
                T[i,j] = u[i][p*j]

        verbose("done step 5", t)
        t = cputime()
        # Step 6: solve T = AE using fact E is upper triangular.
        # Warning: assumes that T = AE (rather than pT = AE) has
        # a solution over Z/(p^mdash). This has always been the case in
        # examples computed by the author, see Note 3.1.

        A = matrix(S,ell,ell)
        verbose("solving a square matrix problem of dimension %s" % ell, t)

        for i in xrange(0,ell):
            Ti = T[i]
            for j in xrange(0,ell):
                ej = Ti.parent()([e[j][l] for l in xrange(0,ell)])
                lj = ZZ(ej[j])
                A[i,j] = S(ZZ(Ti[j])/lj)
                Ti = Ti - A[i,j]*ej

        Alist.append(MatrixSpace(Zmod(p**m),ell,ell)(A))
        verbose("done step 6", t)

    return Alist
Beispiel #24
0
def higher_level_UpGj(p,N,klist,m,modformsring,bound):
    r"""
    Returns a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)``
    parameterised by the weights k in ``klist``. The matrix `A_k` is the finite
    square matrix which occurs on input p,k,N and m in Step 6 of Algorithm 2 in
    [AGBL]_. Notational change from paper: In Step 1 following Wan we defined
    j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by
    ``kdiv`` so that we may use j as a column index for matrices.)

    INPUT:

    - ``p`` -- prime at least 5.
    - ``N`` -- integer at least 2 and not divisible by p (level).
    - ``klist`` -- list of integers all congruent modulo (p-1) (the weights).
    - ``m`` -- positive integer.
    - ``modformsring`` -- True or False.
    - ``bound`` -- (even) positive integer.

    OUTPUT:

    - list of square matrices.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import higher_level_UpGj
        sage: higher_level_UpGj(5,3,[4],2,true,6)
        [
        [ 1  0  0  0  0  0]
        [ 0  1  0  0  0  0]
        [ 0  7  0  0  0  0]
        [ 0  5 10 20  0  0]
        [ 0  7 20  0 20  0]
        [ 0  1 24  0 20  0]
        ]

    """
    t = cputime()
    # Step 1

    k0 = klist[0] % (p-1)
    n = floor(((p+1)/(p-1)) * (m+1))
    elldash = compute_elldash(p,N,k0,n)
    elldashp = elldash*p
    mdash = m + ceil(n/(p+1))

    verbose("done step 1",t)
    t = cputime()
    # Steps 2 and 3

    e,Ep1 = higher_level_katz_exp(p,N,k0,m,mdash,elldash,elldashp,modformsring,bound)
    ell = dimension(transpose(e)[0].parent())
    S = e[0,0].parent()

    verbose("done steps 2+3", t)
    t = cputime()
    # Step 4

    R = Ep1.parent()
    G = compute_G(p, Ep1)
    Alist = []

    verbose("done step 4a", t)
    t = cputime()
    for k in klist:
        k = ZZ(k) # convert to sage integer
        kdiv = k // (p-1)
        Gkdiv = G**kdiv

        T = matrix(S,ell,elldash)
        for i in xrange(ell):
            ei = R(e[i].list())
            Gkdivei = Gkdiv*ei; # act by G^kdiv
            for j in xrange(0, elldash):
                T[i,j] = Gkdivei[p*j]

        verbose("done steps 4b and 5", t)
        t = cputime()

        # Step 6: solve T = AE using fact E is upper triangular.
        # Warning: assumes that T = AE (rather than pT = AE) has
        # a solution over Z/(p^mdash). This has always been the case in
        # examples computed by the author, see Note 3.1.

        A = matrix(S,ell,ell)
        verbose("solving a square matrix problem of dimension %s" % ell)
        verbose("elldash is %s" % elldash)

        for i in xrange(0,ell):
            Ti = T[i]
            for j in xrange(0,ell):
                ej = Ti.parent()([e[j][l] for l in xrange(0,elldash)])
                ejleadpos = ej.nonzero_positions()[0]
                lj = ZZ(ej[ejleadpos])
                A[i,j] = S(ZZ(Ti[j])/lj)
                Ti = Ti - A[i,j]*ej

        Alist.append(MatrixSpace(Zmod(p**m),ell,ell)(A))
        verbose("done step 6", t)

    return Alist
Beispiel #25
0
def q_binomial(n, k, q=None, algorithm='auto'):
    r"""
    Return the `q`-binomial coefficient.

    This is also known as the Gaussian binomial coefficient, and is defined by

    .. MATH::

        \binom{n}{k}_q = \frac{(1-q^n)(1-q^{n-1}) \cdots (1-q^{n-k+1})}
        {(1-q)(1-q^2)\cdots (1-q^k)}.

    See :wikipedia:`Gaussian_binomial_coefficient`

    If `q` is unspecified, then the variable is the generator `q` for
    a univariate polynomial ring over the integers.

    INPUT:

    - ``n, k`` -- The values, `n` and `k` defined above.

    - ``q`` -- (Default: ``None``) The variable `q`; if ``None``, then use a
      default variable in `\ZZ[q]`.

    - ``algorithm`` -- (Default: ``'auto'``) The algorithm to use and can be
      one of the following:

      - ``'auto'`` -- Automatically choose the algorithm; see the algorithm
        section below
      - ``'naive'`` -- Use the naive algorithm
      - ``'cyclotomic'`` -- Use cyclotomic algorithm

    ALGORITHM:

    The naive algorithm uses the product formula. The cyclotomic
    algorithm uses a product of cyclotomic polynomials
    (cf. [CH2006]_).

    When the algorithm is set to ``'auto'``, we choose according to
    the following rules:

    - If ``q`` is a polynomial:

      When ``n`` is small or ``k`` is small with respect to ``n``, one
      uses the naive algorithm. When both ``n`` and ``k`` are big, one
      uses the cyclotomic algorithm.

    - If ``q`` is in the symbolic ring, one uses the cyclotomic algorithm.

    - Otherwise one uses the naive algorithm, unless ``q`` is a root of
      unity, then one uses the cyclotomic algorithm.

    EXAMPLES:

    By default, the variable is the generator of `\ZZ[q]`::

        sage: from sage.combinat.q_analogues import q_binomial
        sage: g = q_binomial(5,1) ; g
        q^4 + q^3 + q^2 + q + 1
        sage: g.parent()
        Univariate Polynomial Ring in q over Integer Ring

    The `q`-binomial coefficient vanishes unless `0 \leq k \leq n`::

        sage: q_binomial(4,5)
        0
        sage: q_binomial(5,-1)
        0

    Other variables can be used, given as third parameter::

        sage: p = ZZ['p'].gen()
        sage: q_binomial(4,2,p)
        p^4 + p^3 + 2*p^2 + p + 1

    The third parameter can also be arbitrary values::

        sage: q_binomial(5,1,2) == g.subs(q=2)
        True
        sage: q_binomial(5,1,1)
        5
        sage: q_binomial(4,2,-1)
        2
        sage: q_binomial(4,2,3.14)
        152.030056160000
        sage: R = GF(25, 't')
        sage: t = R.gen(0)
        sage: q_binomial(6, 3, t)
        2*t + 3

    We can also do this for more complicated objects such as matrices or
    symmetric functions::

        sage: q_binomial(4,2,matrix([[2,1],[-1,3]]))
        [ -6  84]
        [-84  78]
        sage: Sym = SymmetricFunctions(QQ)
        sage: s = Sym.schur()
        sage: q_binomial(4,1, s[2]+s[1])
        s[] + s[1] + s[1, 1] + s[1, 1, 1] + 2*s[2] + 4*s[2, 1] + 3*s[2, 1, 1]
         + 4*s[2, 2] + 3*s[2, 2, 1] + s[2, 2, 2] + 3*s[3] + 7*s[3, 1] + 3*s[3, 1, 1]
         + 6*s[3, 2] + 2*s[3, 2, 1] + s[3, 3] + 4*s[4] + 6*s[4, 1] + s[4, 1, 1]
         + 3*s[4, 2] + 3*s[5] + 2*s[5, 1] + s[6]

    TESTS:

    One checks that the first two arguments are integers::

        sage: q_binomial(1/2,1)
        Traceback (most recent call last):
        ...
        ValueError: arguments (1/2, 1) must be integers

    One checks that `n` is nonnegative::

        sage: q_binomial(-4,1)
        Traceback (most recent call last):
        ...
        ValueError: n must be nonnegative

    This also works for variables in the symbolic ring::

        sage: z = var('z')
        sage: factor(q_binomial(4,2,z))
        (z^2 + z + 1)*(z^2 + 1)

    This also works for complex roots of unity::

        sage: q_binomial(6,1,I)
        1 + I

    Check that the algorithm does not matter::

        sage: q_binomial(6,3, algorithm='naive') == q_binomial(6,3, algorithm='cyclotomic')
        True

    One more test::

        sage: q_binomial(4, 2, Zmod(6)(2), algorithm='naive')
        5

    REFERENCES:

    .. [CH2006] William Y.C. Chen and Qing-Hu Hou, "Factors of the Gaussian
       coefficients", Discrete Mathematics 306 (2006), 1446-1449.
       :doi:`10.1016/j.disc.2006.03.031`

    AUTHORS:

    - Frederic Chapoton, David Joyner and William Stein
    """
    # sanity checks
    if not( n in ZZ and k in ZZ ):
        raise ValueError("arguments (%s, %s) must be integers" % (n, k))
    if n < 0:
        raise ValueError('n must be nonnegative')
    if not(0 <= k and k <= n):
        return 0

    k = min(n-k,k) # Pick the smallest k

    # polynomiality test
    if q is None:
        from sage.rings.polynomial.polynomial_ring import polygen
        q = polygen(ZZ, name='q')
        is_polynomial = True
    else:
        from sage.rings.polynomial.polynomial_element import Polynomial
        is_polynomial = isinstance(q, Polynomial)
    from sage.symbolic.ring import SR

    # heuristic choice of the fastest algorithm
    if algorithm == 'auto':
        if is_polynomial:
            if n <= 70 or k <= n/4:
                algorithm = 'naive'
            else:
                algorithm = 'cyclo_polynomial'
        elif q in SR:
            algorithm = 'cyclo_generic'
        else:
            algorithm = 'naive'
    elif algorithm == 'cyclotomic':
        if is_polynomial:
            algorithm = 'cyclo_polynomial'
        else:
            algorithm = 'cyclo_generic'
    elif algorithm != 'naive':
        raise ValueError("invalid algorithm choice")

    # the algorithms
    try:
        if algorithm == 'naive':
            denomin = prod([1 - q**i for i in range(1, k+1)])
            if denomin == 0: # q is a root of unity, use the cyclotomic algorithm
                algorithm = 'cyclo_generic'
            else:
                numerat = prod([1 - q**i for i in range(n-k+1, n+1)])
                try:
                    return numerat//denomin
                except TypeError:
                    return numerat/denomin
        from sage.functions.all import floor
        if algorithm == 'cyclo_generic':
            from sage.rings.polynomial.cyclotomic import cyclotomic_value
            return prod(cyclotomic_value(d,q)
                        for d in range(2,n+1)
                        if floor(n/d) != floor(k/d) + floor((n-k)/d))
        if algorithm == 'cyclo_polynomial':
            R = q.parent()
            return prod(R.cyclotomic_polynomial(d)
                        for d in range(2,n+1)
                        if floor(n/d) != floor(k/d) + floor((n-k)/d))
    except (ZeroDivisionError, TypeError):
        # As a last attempt, do the computation formally and then substitute
        return q_binomial(n, k)(q)
Beispiel #26
0
def higher_level_katz_exp(p,N,k0,m,mdash,elldash,elldashp,modformsring,bound):
    r"""
    Returns a matrix `e` of size ``ell x elldashp`` over the integers modulo
    `p^\text{mdash}`, and the Eisenstein series `E_{p-1} = 1 + .\dots \bmod
    (p^\text{mdash},q^\text{elldashp})`. The matrix e contains the coefficients
    of the elements `e_{i,s}` in the Katz expansions basis in Step 3 of
    Algorithm 2 in [AGBL]_ when one takes as input to that algorithm
    `p`,`N`,`m` and `k` and define ``k0``, ``mdash``, ``n``, ``elldash``,
    ``elldashp = ell*dashp`` as in Step 1.

    INPUT:

    - ``p`` -- prime at least 5.
    - ``N`` -- positive integer at least 2 and not divisible by p (level).
    - ``k0`` -- integer in xrange 0 to p-1.
    - ``m,mdash,elldash,elldashp`` -- positive integers.
    - ``modformsring`` -- True or False.
    - ``bound`` -- positive (even) integer.

    OUTPUT:

    - matrix and q-expansion.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import higher_level_katz_exp
        sage: e,Ep1 = higher_level_katz_exp(5,2,0,1,2,4,20,true,6)
        sage: e
        [ 1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]
        [ 0  1 18 23 19  6  9  9 17  7  3 17 12  8 22  8 11 19  1  5]
        [ 0  0  1 11 20 16  0  8  4  0 18 15 24  6 15 23  5 18  7 15]
        [ 0  0  0  1  4 16 23 13  6  5 23  5  2 16  4 18 10 23  5 15]
        sage: Ep1
        1 + 15*q + 10*q^2 + 20*q^3 + 20*q^4 + 15*q^5 + 5*q^6 + 10*q^7 +
        5*q^9 + 10*q^10 + 5*q^11 + 10*q^12 + 20*q^13 + 15*q^14 + 20*q^15 + 15*q^16 +
        10*q^17 + 20*q^18 + O(q^20)
    """
    ordr = 1/(p+1)
    S = Zmod(p**mdash)
    Ep1 = eisenstein_series_qexp(p-1,prec=elldashp,K=S, normalization="constant")

    n = floor(((p+1)/(p-1))*(m+1))
    Wjs = complementary_spaces(N,p,k0,n,mdash,elldashp,elldash,modformsring,bound)

    Basis = []
    for j in xrange(n+1):
        Wj = Wjs[j]
        dimj = len(Wj)
        Ep1minusj = Ep1**(-j)
        for i in xrange(dimj):
            wji = Wj[i]
            b = p**floor(ordr*j) * wji * Ep1minusj
            Basis.append(b)

    # extract basis as a matrix

    ell = len(Basis)
    M = matrix(S,ell,elldashp)
    for i in xrange(ell):
        for j in xrange(elldashp):
            M[i,j] = Basis[i][j]

    ech_form(M,p) # put it into echelon form

    return M,Ep1
Beispiel #27
0
def higher_level_UpGj(p, N, klist, m, modformsring, bound):
    r"""
    Returns a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)``
    parameterised by the weights k in ``klist``. The matrix `A_k` is the finite
    square matrix which occurs on input p,k,N and m in Step 6 of Algorithm 2 in
    [AGBL]_. Notational change from paper: In Step 1 following Wan we defined
    j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by
    ``kdiv`` so that we may use j as a column index for matrices.)

    INPUT:

    - ``p`` -- prime at least 5.
    - ``N`` -- integer at least 2 and not divisible by p (level).
    - ``klist`` -- list of integers all congruent modulo (p-1) (the weights).
    - ``m`` -- positive integer.
    - ``modformsring`` -- True or False.
    - ``bound`` -- (even) positive integer.

    OUTPUT:

    - list of square matrices.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import higher_level_UpGj
        sage: higher_level_UpGj(5,3,[4],2,true,6)
        [
        [ 1  0  0  0  0  0]
        [ 0  1  0  0  0  0]
        [ 0  7  0  0  0  0]
        [ 0  5 10 20  0  0]
        [ 0  7 20  0 20  0]
        [ 0  1 24  0 20  0]
        ]

    """
    t = cputime()
    # Step 1

    k0 = klist[0] % (p - 1)
    n = floor(((p + 1) / (p - 1)) * (m + 1))
    elldash = compute_elldash(p, N, k0, n)
    elldashp = elldash * p
    mdash = m + ceil(n / (p + 1))

    verbose("done step 1", t)
    t = cputime()
    # Steps 2 and 3

    e, Ep1 = higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp,
                                   modformsring, bound)
    ell = dimension(transpose(e)[0].parent())
    S = e[0, 0].parent()

    verbose("done steps 2+3", t)
    t = cputime()
    # Step 4

    R = Ep1.parent()
    G = compute_G(p, Ep1)
    Alist = []

    verbose("done step 4a", t)
    t = cputime()
    for k in klist:
        k = ZZ(k)  # convert to sage integer
        kdiv = k // (p - 1)
        Gkdiv = G**kdiv

        T = matrix(S, ell, elldash)
        for i in xrange(ell):
            ei = R(e[i].list())
            Gkdivei = Gkdiv * ei
            # act by G^kdiv
            for j in xrange(0, elldash):
                T[i, j] = Gkdivei[p * j]

        verbose("done steps 4b and 5", t)
        t = cputime()

        # Step 6: solve T = AE using fact E is upper triangular.
        # Warning: assumes that T = AE (rather than pT = AE) has
        # a solution over Z/(p^mdash). This has always been the case in
        # examples computed by the author, see Note 3.1.

        A = matrix(S, ell, ell)
        verbose("solving a square matrix problem of dimension %s" % ell)
        verbose("elldash is %s" % elldash)

        for i in xrange(0, ell):
            Ti = T[i]
            for j in xrange(0, ell):
                ej = Ti.parent()([e[j][l] for l in xrange(0, elldash)])
                ejleadpos = ej.nonzero_positions()[0]
                lj = ZZ(ej[ejleadpos])
                A[i, j] = S(ZZ(Ti[j]) / lj)
                Ti = Ti - A[i, j] * ej

        Alist.append(MatrixSpace(Zmod(p**m), ell, ell)(A))
        verbose("done step 6", t)

    return Alist
 def rangea(t):
     tp0 = t*wprime0
     tp1 = t*wprime1
     return sorted(list(union(range(floor(xmin-1-tp0-wprime0),ceil(xmax-tp0)+1),range(floor(ymin-1-tp1-wprime1),ceil(ymax-tp1)+1))),key=abs)
Beispiel #29
0
def level1_UpGj(p, klist, m):
    r"""
    Returns a list `[A_k]` of square matrices over ``IntegerRing(p^m)``
    parameterised by the weights k in ``klist``. The matrix `A_k` is the finite
    square matrix which occurs on input p,k and m in Step 6 of Algorithm 1 in
    [AGBL]_. Notational change from paper: In Step 1 following Wan we defined
    j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by
    ``kdiv`` so that we may use j as a column index for matrices.

    INPUT:

    - ``p`` -- prime at least 5.
    - ``klist`` -- list of integers congruent modulo `(p-1)` (the weights).
    - ``m`` -- positive integer.

    OUTPUT:

    - list of square matrices.

    EXAMPLES::

        sage: from sage.modular.overconvergent.hecke_series import level1_UpGj
        sage: level1_UpGj(7,[100],5)
        [
        [    1   980  4802     0     0]
        [    0 13727 14406     0     0]
        [    0 13440  7203     0     0]
        [    0  1995  4802     0     0]
        [    0  9212 14406     0     0]
        ]
    """
    # Step 1
    t = cputime()

    k0 = klist[0] % (p - 1)
    n = floor(((p + 1) / (p - 1)) * (m + 1))
    ell = dimension_modular_forms(1, k0 + n * (p - 1))
    ellp = ell * p
    mdash = m + ceil(n / (p + 1))

    verbose("done step 1", t)
    t = cputime()
    # Steps 2 and 3

    e, Ep1 = katz_expansions(k0, p, ellp, mdash, n)

    verbose("done steps 2+3", t)
    t = cputime()
    # Step 4

    G = compute_G(p, Ep1)
    Alist = []

    verbose("done step 4a", t)
    t = cputime()
    for k in klist:
        k = ZZ(k)  # convert to sage integer
        kdiv = k // (p - 1)
        Gkdiv = G**kdiv
        u = []
        for i in xrange(0, ell):
            ei = e[i]
            ui = Gkdiv * ei
            u.append(ui)

        verbose("done step 4b", t)
        t = cputime()
        # Step 5 and computation of T in Step 6

        S = e[0][0].parent()
        T = matrix(S, ell, ell)

        for i in xrange(0, ell):
            for j in xrange(0, ell):
                T[i, j] = u[i][p * j]

        verbose("done step 5", t)
        t = cputime()
        # Step 6: solve T = AE using fact E is upper triangular.
        # Warning: assumes that T = AE (rather than pT = AE) has
        # a solution over Z/(p^mdash). This has always been the case in
        # examples computed by the author, see Note 3.1.

        A = matrix(S, ell, ell)
        verbose("solving a square matrix problem of dimension %s" % ell, t)

        for i in xrange(0, ell):
            Ti = T[i]
            for j in xrange(0, ell):
                ej = Ti.parent()([e[j][l] for l in xrange(0, ell)])
                lj = ZZ(ej[j])
                A[i, j] = S(ZZ(Ti[j]) / lj)
                Ti = Ti - A[i, j] * ej

        Alist.append(MatrixSpace(Zmod(p**m), ell, ell)(A))
        verbose("done step 6", t)

    return Alist
Beispiel #30
0
        def coproduct_iterated(self, n=1):
            r"""
            Apply ``n`` coproducts to ``self``.

            .. TODO::

                Remove dependency on ``modules_with_basis`` methods.

            EXAMPLES::

                sage: Psi = NonCommutativeSymmetricFunctions(QQ).Psi()
                sage: Psi[2,2].coproduct_iterated(0)
                Psi[2, 2]
                sage: Psi[2,2].coproduct_iterated(2)
                Psi[] # Psi[] # Psi[2, 2] + 2*Psi[] # Psi[2] # Psi[2]
                 + Psi[] # Psi[2, 2] # Psi[] + 2*Psi[2] # Psi[] # Psi[2]
                 + 2*Psi[2] # Psi[2] # Psi[] + Psi[2, 2] # Psi[] # Psi[]

            TESTS::

                sage: p = SymmetricFunctions(QQ).p()
                sage: p[5,2,2].coproduct_iterated()
                p[] # p[5, 2, 2] + 2*p[2] # p[5, 2] + p[2, 2] # p[5]
                 + p[5] # p[2, 2] + 2*p[5, 2] # p[2] + p[5, 2, 2] # p[]
                sage: p([]).coproduct_iterated(3)
                p[] # p[] # p[] # p[]

            ::

                sage: Psi = NonCommutativeSymmetricFunctions(QQ).Psi()
                sage: Psi[2,2].coproduct_iterated(0)
                Psi[2, 2]
                sage: Psi[2,2].coproduct_iterated(3)
                Psi[] # Psi[] # Psi[] # Psi[2, 2] + 2*Psi[] # Psi[] # Psi[2] # Psi[2]
                 + Psi[] # Psi[] # Psi[2, 2] # Psi[] + 2*Psi[] # Psi[2] # Psi[] # Psi[2]
                 + 2*Psi[] # Psi[2] # Psi[2] # Psi[] + Psi[] # Psi[2, 2] # Psi[] # Psi[]
                 + 2*Psi[2] # Psi[] # Psi[] # Psi[2] + 2*Psi[2] # Psi[] # Psi[2] # Psi[]
                 + 2*Psi[2] # Psi[2] # Psi[] # Psi[] + Psi[2, 2] # Psi[] # Psi[] # Psi[]

            ::

                sage: m = SymmetricFunctionsNonCommutingVariables(QQ).m()
                sage: m[[1,3],[2]].coproduct_iterated(2)
                m{} # m{} # m{{1, 3}, {2}} + m{} # m{{1}} # m{{1, 2}}
                 + m{} # m{{1, 2}} # m{{1}} + m{} # m{{1, 3}, {2}} # m{}
                 + m{{1}} # m{} # m{{1, 2}} + m{{1}} # m{{1, 2}} # m{}
                 + m{{1, 2}} # m{} # m{{1}} + m{{1, 2}} # m{{1}} # m{}
                 + m{{1, 3}, {2}} # m{} # m{}
                sage: m[[]].coproduct_iterated(3), m[[1,3],[2]].coproduct_iterated(0)
                (m{} # m{} # m{} # m{}, m{{1, 3}, {2}})
            """
            if n < 0:
                raise ValueError("cannot take fewer than 0 coproduct iterations: %s < 0" % str(n))
            if n == 0:
                return self
            if n == 1:
                return self.coproduct()
            from sage.functions.all import floor, ceil
            from sage.rings.all import Integer

            # Use coassociativity of `\Delta` to perform many coproducts simultaneously.
            fn = floor(Integer(n-1)/2); cn = ceil(Integer(n-1)/2)
            split = lambda a,b: tensor([a.coproduct_iterated(fn), b.coproduct_iterated(cn)])
            return self.coproduct().apply_multilinear_morphism(split)
def mass__by_Siegel_densities(self,
                              odd_algorithm="Pall",
                              even_algorithm="Watson"):
    """
    Gives the mass of transformations (det 1 and -1).

    WARNING: THIS IS BROKEN RIGHT NOW... =(

    Optional Arguments:

    - When p > 2  --  odd_algorithm = "Pall" (only one choice for now)
    - When p = 2  --  even_algorithm = "Kitaoka" or "Watson"

    REFERENCES:

    - Nipp's Book "Tables of Quaternary Quadratic Forms".
    - Papers of Pall (only for p>2) and Watson (for `p=2` -- tricky!).
    - Siegel, Milnor-Hussemoller, Conway-Sloane Paper IV, Kitoaka (all of which
      have problems...)

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Q.mass__by_Siegel_densities()
        1/384
        sage: Q.mass__by_Siegel_densities() - (2^Q.dim() * factorial(Q.dim()))^(-1)
        0

    ::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1])
        sage: Q.mass__by_Siegel_densities()
        1/48
        sage: Q.mass__by_Siegel_densities() - (2^Q.dim() * factorial(Q.dim()))^(-1)
        0

    """
    ## Setup
    n = self.dim()
    s = floor((n - 1) / 2)
    if n % 2 != 0:
        char_d = squarefree_part(2 *
                                 self.det())  ## Accounts for the det as a QF
    else:
        char_d = squarefree_part(self.det())

    ## Form the generic zeta product
    generic_prod = ZZ(2) * (pi)**(-ZZ(n) * (n + 1) / 4)
    ##########################################
    generic_prod *= (self.det())**(
        ZZ(n + 1) / 2)  ## ***** This uses the Hessian Determinant ********
    ##########################################
    #print "gp1 = ", generic_prod
    generic_prod *= prod([gamma__exact(ZZ(j) / 2) for j in range(1, n + 1)])
    #print "\n---", [(ZZ(j)/2, gamma__exact(ZZ(j)/2))  for j in range(1,n+1)]
    #print "\n---", prod([gamma__exact(ZZ(j)/2)  for j in range(1,n+1)])
    #print "gp2 = ", generic_prod
    generic_prod *= prod([zeta__exact(ZZ(j)) for j in range(2, 2 * s + 1, 2)])
    #print "\n---", [zeta__exact(ZZ(j))  for j in range(2, 2*s+1, 2)]
    #print "\n---", prod([zeta__exact(ZZ(j))  for j in range(2, 2*s+1, 2)])
    #print "gp3 = ", generic_prod
    if (n % 2 == 0):
        generic_prod *= ZZ(1) * quadratic_L_function__exact(
            n / 2, (-1)**(n / 2) * char_d)
        #print " NEW = ", ZZ(1) * quadratic_L_function__exact(n/2, (-1)**(n/2) * char_d)
        #print
    #print "gp4 = ", generic_prod

    #print "generic_prod =", generic_prod

    ## Determine the adjustment factors
    adj_prod = 1
    for p in prime_divisors(2 * self.det()):
        ## Cancel out the generic factors
        p_adjustment = prod([1 - ZZ(p)**(-j) for j in range(2, 2 * s + 1, 2)])
        if (n % 2 == 0):
            p_adjustment *= ZZ(1) * (1 - kronecker(
                (-1)**(n / 2) * char_d, p) * ZZ(p)**(-n / 2))
            #print " EXTRA = ", ZZ(1) * (1 - kronecker((-1)**(n/2) * char_d, p) * ZZ(p)**(-n/2))
        #print "Factor to cancel the generic one:", p_adjustment

        ## Insert the new mass factors
        if p == 2:
            if even_algorithm == "Kitaoka":
                p_adjustment = p_adjustment / self.Kitaoka_mass_at_2()
            elif even_algorithm == "Watson":
                p_adjustment = p_adjustment / self.Watson_mass_at_2()
            else:
                raise TypeError(
                    "There is a problem -- your even_algorithm argument is invalid.  Try again. =("
                )
        else:
            if odd_algorithm == "Pall":
                p_adjustment = p_adjustment / self.Pall_mass_density_at_odd_prime(
                    p)
            else:
                raise TypeError(
                    "There is a problem -- your optional arguments are invalid.  Try again. =("
                )

        #print "p_adjustment for p =", p, "is", p_adjustment

        ## Put them together (cumulatively)
        adj_prod *= p_adjustment

    #print "Cumulative adj_prod =", adj_prod

    ## Extra adjustment for the case of a 2-dimensional form.
    #if (n == 2):
    #    generic_prod *= 2

    ## Return the mass
    mass = generic_prod * adj_prod
    return mass
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
        def coproduct_iterated(self, n=1):
            r"""
            Apply ``n`` coproducts to ``self``.

            .. TODO::

                Remove dependency on ``modules_with_basis`` methods.

            EXAMPLES::

                sage: Psi = NonCommutativeSymmetricFunctions(QQ).Psi()
                sage: Psi[2,2].coproduct_iterated(0)
                Psi[2, 2]
                sage: Psi[2,2].coproduct_iterated(2)
                Psi[] # Psi[] # Psi[2, 2] + 2*Psi[] # Psi[2] # Psi[2]
                 + Psi[] # Psi[2, 2] # Psi[] + 2*Psi[2] # Psi[] # Psi[2]
                 + 2*Psi[2] # Psi[2] # Psi[] + Psi[2, 2] # Psi[] # Psi[]

            TESTS::

                sage: p = SymmetricFunctions(QQ).p()
                sage: p[5,2,2].coproduct_iterated()
                p[] # p[5, 2, 2] + 2*p[2] # p[5, 2] + p[2, 2] # p[5]
                 + p[5] # p[2, 2] + 2*p[5, 2] # p[2] + p[5, 2, 2] # p[]
                sage: p([]).coproduct_iterated(3)
                p[] # p[] # p[] # p[]

            ::

                sage: Psi = NonCommutativeSymmetricFunctions(QQ).Psi()
                sage: Psi[2,2].coproduct_iterated(0)
                Psi[2, 2]
                sage: Psi[2,2].coproduct_iterated(3)
                Psi[] # Psi[] # Psi[] # Psi[2, 2] + 2*Psi[] # Psi[] # Psi[2] # Psi[2]
                 + Psi[] # Psi[] # Psi[2, 2] # Psi[] + 2*Psi[] # Psi[2] # Psi[] # Psi[2]
                 + 2*Psi[] # Psi[2] # Psi[2] # Psi[] + Psi[] # Psi[2, 2] # Psi[] # Psi[]
                 + 2*Psi[2] # Psi[] # Psi[] # Psi[2] + 2*Psi[2] # Psi[] # Psi[2] # Psi[]
                 + 2*Psi[2] # Psi[2] # Psi[] # Psi[] + Psi[2, 2] # Psi[] # Psi[] # Psi[]

            ::

                sage: m = SymmetricFunctionsNonCommutingVariables(QQ).m()
                sage: m[[1,3],[2]].coproduct_iterated(2)
                m{} # m{} # m{{1, 3}, {2}} + m{} # m{{1}} # m{{1, 2}}
                 + m{} # m{{1, 2}} # m{{1}} + m{} # m{{1, 3}, {2}} # m{}
                 + m{{1}} # m{} # m{{1, 2}} + m{{1}} # m{{1, 2}} # m{}
                 + m{{1, 2}} # m{} # m{{1}} + m{{1, 2}} # m{{1}} # m{}
                 + m{{1, 3}, {2}} # m{} # m{}
                sage: m[[]].coproduct_iterated(3), m[[1,3],[2]].coproduct_iterated(0)
                (m{} # m{} # m{} # m{}, m{{1, 3}, {2}})
            """
            if n < 0:
                raise ValueError(
                    "cannot take fewer than 0 coproduct iterations: %s < 0" %
                    str(n))
            if n == 0:
                return self
            if n == 1:
                return self.coproduct()
            from sage.functions.all import floor, ceil
            from sage.rings.all import Integer

            # Use coassociativity of `\Delta` to perform many coproducts simultaneously.
            fn = floor(Integer(n - 1) / 2)
            cn = ceil(Integer(n - 1) / 2)
            split = lambda a, b: tensor(
                [a.coproduct_iterated(fn),
                 b.coproduct_iterated(cn)])
            return self.coproduct().apply_multilinear_morphism(split)
Beispiel #34
0
def q_binomial(n, k, q=None, algorithm='auto'):
    r"""
    Return the `q`-binomial coefficient.

    This is also known as the Gaussian binomial coefficient, and is defined by

    .. MATH::

        \binom{n}{k}_q = \frac{(1-q^n)(1-q^{n-1}) \cdots (1-q^{n-k+1})}
        {(1-q)(1-q^2)\cdots (1-q^k)}.

    See :wikipedia:`Gaussian_binomial_coefficient`

    If `q` is unspecified, then the variable is the generator `q` for
    a univariate polynomial ring over the integers.

    INPUT:

    - ``n, k`` -- The values, `n` and `k` defined above.

    - ``q`` -- (Default: ``None``) The variable `q`; if ``None``, then use a
      default variable in `\ZZ[q]`.

    - ``algorithm`` -- (Default: ``'auto'``) The algorithm to use and can be
      one of the following:

      - ``'auto'`` -- Automatically choose the algorithm; see the algorithm
        section below
      - ``'naive'`` -- Use the naive algorithm
      - ``'cyclotomic'`` -- Use cyclotomic algorithm

    ALGORITHM:

    The naive algorithm uses the product formula. The cyclotomic
    algorithm uses a product of cyclotomic polynomials
    (cf. [CH2006]_).

    When the algorithm is set to ``'auto'``, we choose according to
    the following rules:

    - If ``q`` is a polynomial:

      When ``n`` is small or ``k`` is small with respect to ``n``, one
      uses the naive algorithm. When both ``n`` and ``k`` are big, one
      uses the cyclotomic algorithm.

    - If ``q`` is in the symbolic ring, one uses the cyclotomic algorithm.

    - Otherwise one uses the naive algorithm, unless ``q`` is a root of
      unity, then one uses the cyclotomic algorithm.

    EXAMPLES:

    By default, the variable is the generator of `\ZZ[q]`::

        sage: from sage.combinat.q_analogues import q_binomial
        sage: g = q_binomial(5,1) ; g
        q^4 + q^3 + q^2 + q + 1
        sage: g.parent()
        Univariate Polynomial Ring in q over Integer Ring

    The `q`-binomial coefficient vanishes unless `0 \leq k \leq n`::

        sage: q_binomial(4,5)
        0
        sage: q_binomial(5,-1)
        0

    Other variables can be used, given as third parameter::

        sage: p = ZZ['p'].gen()
        sage: q_binomial(4,2,p)
        p^4 + p^3 + 2*p^2 + p + 1

    The third parameter can also be arbitrary values::

        sage: q_binomial(5,1,2) == g.subs(q=2)
        True
        sage: q_binomial(5,1,1)
        5
        sage: q_binomial(4,2,-1)
        2
        sage: q_binomial(4,2,3.14)
        152.030056160000
        sage: R = GF(25, 't')
        sage: t = R.gen(0)
        sage: q_binomial(6, 3, t)
        2*t + 3

    We can also do this for more complicated objects such as matrices or
    symmetric functions::

        sage: q_binomial(4,2,matrix([[2,1],[-1,3]]))
        [ -6  84]
        [-84  78]
        sage: Sym = SymmetricFunctions(QQ)
        sage: s = Sym.schur()
        sage: q_binomial(4,1, s[2]+s[1])
        s[] + s[1] + s[1, 1] + s[1, 1, 1] + 2*s[2] + 4*s[2, 1] + 3*s[2, 1, 1]
         + 4*s[2, 2] + 3*s[2, 2, 1] + s[2, 2, 2] + 3*s[3] + 7*s[3, 1] + 3*s[3, 1, 1]
         + 6*s[3, 2] + 2*s[3, 2, 1] + s[3, 3] + 4*s[4] + 6*s[4, 1] + s[4, 1, 1]
         + 3*s[4, 2] + 3*s[5] + 2*s[5, 1] + s[6]

    TESTS:

    One checks that the first two arguments are integers::

        sage: q_binomial(1/2,1)
        Traceback (most recent call last):
        ...
        ValueError: arguments (1/2, 1) must be integers

    One checks that `n` is nonnegative::

        sage: q_binomial(-4,1)
        Traceback (most recent call last):
        ...
        ValueError: n must be nonnegative

    This also works for variables in the symbolic ring::

        sage: z = var('z')
        sage: factor(q_binomial(4,2,z))
        (z^2 + z + 1)*(z^2 + 1)

    This also works for complex roots of unity::

        sage: q_binomial(6,1,I)
        1 + I

    Check that the algorithm does not matter::

        sage: q_binomial(6,3, algorithm='naive') == q_binomial(6,3, algorithm='cyclotomic')
        True

    One more test::

        sage: q_binomial(4, 2, Zmod(6)(2), algorithm='naive')
        5

    REFERENCES:

    .. [CH2006] William Y.C. Chen and Qing-Hu Hou, "Factors of the Gaussian
       coefficients", Discrete Mathematics 306 (2006), 1446-1449.
       :doi:`10.1016/j.disc.2006.03.031`

    AUTHORS:

    - Frederic Chapoton, David Joyner and William Stein
    """
    # sanity checks
    if not (n in ZZ and k in ZZ):
        raise ValueError("arguments (%s, %s) must be integers" % (n, k))
    if n < 0:
        raise ValueError('n must be nonnegative')
    if not (0 <= k and k <= n):
        return 0

    k = min(n - k, k)  # Pick the smallest k

    # polynomiality test
    if q is None:
        from sage.rings.polynomial.polynomial_ring import polygen
        q = polygen(ZZ, name='q')
        is_polynomial = True
    else:
        from sage.rings.polynomial.polynomial_element import Polynomial
        is_polynomial = isinstance(q, Polynomial)
    from sage.symbolic.ring import SR

    # heuristic choice of the fastest algorithm
    if algorithm == 'auto':
        if is_polynomial:
            if n <= 70 or k <= n / 4:
                algorithm = 'naive'
            else:
                algorithm = 'cyclo_polynomial'
        elif q in SR:
            algorithm = 'cyclo_generic'
        else:
            algorithm = 'naive'
    elif algorithm == 'cyclotomic':
        if is_polynomial:
            algorithm = 'cyclo_polynomial'
        else:
            algorithm = 'cyclo_generic'
    elif algorithm != 'naive':
        raise ValueError("invalid algorithm choice")

    # the algorithms
    try:
        if algorithm == 'naive':
            denomin = prod([1 - q**i for i in range(1, k + 1)])
            if denomin == 0:  # q is a root of unity, use the cyclotomic algorithm
                algorithm = 'cyclo_generic'
            else:
                numerat = prod([1 - q**i for i in range(n - k + 1, n + 1)])
                try:
                    return numerat // denomin
                except TypeError:
                    return numerat / denomin
        from sage.functions.all import floor
        if algorithm == 'cyclo_generic':
            from sage.rings.polynomial.cyclotomic import cyclotomic_value
            return prod(
                cyclotomic_value(d, q) for d in range(2, n + 1)
                if floor(n / d) != floor(k / d) + floor((n - k) / d))
        if algorithm == 'cyclo_polynomial':
            R = q.parent()
            return prod(
                R.cyclotomic_polynomial(d) for d in range(2, n + 1)
                if floor(n / d) != floor(k / d) + floor((n - k) / d))
    except (ZeroDivisionError, TypeError):
        # As a last attempt, do the computation formally and then substitute
        return q_binomial(n, k)(q)
def vectors_by_length(self, bound):
    """
    Returns a list of short vectors together with their values.

    This is a naive algorithm which uses the Cholesky decomposition,
    but does not use the LLL-reduction algorithm.

    INPUT:
       bound -- an integer >= 0

    OUTPUT:
        A list L of length (bound + 1) whose entry L `[i]` is a list of
        all vectors of length `i`.

    Reference: This is a slightly modified version of Cohn's Algorithm
    2.7.5 in "A Course in Computational Number Theory", with the
    increment step moved around and slightly re-indexed to allow clean
    looping.

    Note: We could speed this up for very skew matrices by using LLL
    first, and then changing coordinates back, but for our purposes
    the simpler method is efficient enough. =)

    EXAMPLES::

        sage: Q = DiagonalQuadraticForm(ZZ, [1,1])
        sage: Q.vectors_by_length(5)
        [[[0, 0]],
         [[0, -1], [-1, 0]],
         [[-1, -1], [1, -1]],
         [],
         [[0, -2], [-2, 0]],
         [[-1, -2], [1, -2], [-2, -1], [2, -1]]]

    ::

        sage: Q1 = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Q1.vectors_by_length(5)
        [[[0, 0, 0, 0]],
         [[-1, 0, 0, 0]],
         [],
         [[0, -1, 0, 0]],
         [[-1, -1, 0, 0], [1, -1, 0, 0], [-2, 0, 0, 0]],
         [[0, 0, -1, 0]]]

    ::

        sage: Q = QuadraticForm(ZZ, 4, [1,1,1,1, 1,0,0, 1,0, 1])
        sage: map(len, Q.vectors_by_length(2))
        [1, 12, 12]

    ::

        sage: Q = QuadraticForm(ZZ, 4, [1,-1,-1,-1, 1,0,0, 4,-3, 4])
        sage: map(len, Q.vectors_by_length(3))
        [1, 3, 0, 3]
    """
    # pari uses eps = 1e-6 ; nothing bad should happen if eps is too big
    # but if eps is too small, roundoff errors may knock off some
    # vectors of norm = bound (see #7100)
    eps = RDF(1e-6)
    bound = ZZ(floor(max(bound, 0)))
    Theta_Precision = bound + eps
    n = self.dim()

    ## Make the vector of vectors which have a given value
    ## (So theta_vec[i] will have all vectors v with Q(v) = i.)
    theta_vec = [[] for i in range(bound + 1)]

    ## Initialize Q with zeros and Copy the Cholesky array into Q
    Q = self.cholesky_decomposition()


    ## 1. Initialize
    T = n * [RDF(0)]    ## Note: We index the entries as 0 --> n-1
    U = n * [RDF(0)]
    i = n-1
    T[i] = RDF(Theta_Precision)
    U[i] = RDF(0)

    L = n * [0]
    x = n * [0]
    Z = RDF(0)

    ## 2. Compute bounds
    Z = (T[i] / Q[i][i]).sqrt(extend=False)
    L[i] = ( Z - U[i]).floor()
    x[i] = (-Z - U[i]).ceil()

    done_flag = False
    Q_val_double = RDF(0)
    Q_val = 0 ## WARNING: Still need a good way of checking overflow for this value...

    ## Big loop which runs through all vectors
    while not done_flag:

        ## 3b. Main loop -- try to generate a complete vector x (when i=0)
        while (i > 0):
            #print " i = ", i
            #print " T[i] = ", T[i]
            #print " Q[i][i] = ", Q[i][i]
            #print " x[i] = ", x[i]
            #print " U[i] = ", U[i]
            #print " x[i] + U[i] = ", (x[i] + U[i])
            #print " T[i-1] = ", T[i-1]

            T[i-1] = T[i] - Q[i][i] * (x[i] + U[i]) * (x[i] + U[i])

            #print " T[i-1] = ",  T[i-1]
            #print " x = ", x
            #print

            i = i - 1
            U[i] = 0
            for j in range(i+1, n):
                U[i] = U[i] + Q[i][j] * x[j]

            ## Now go back and compute the bounds...
            ## 2. Compute bounds
            Z = (T[i] / Q[i][i]).sqrt(extend=False)
            L[i] = ( Z - U[i]).floor()
            x[i] = (-Z - U[i]).ceil()

            # carry if we go out of bounds -- when Z is so small that
            # there aren't any integral vectors between the bounds
            # Note: this ensures T[i-1] >= 0 in the next iteration
            while (x[i] > L[i]):
                i += 1
                x[i] += 1

        ## 4. Solution found (This happens when i = 0)
        #print "-- Solution found! --"
        #print " x = ", x
        #print " Q_val = Q(x) = ", Q_val
        Q_val_double = Theta_Precision - T[0] + Q[0][0] * (x[0] + U[0]) * (x[0] + U[0])
        Q_val = Q_val_double.round()

        ## SANITY CHECK: Roundoff Error is < 0.001
        if abs(Q_val_double -  Q_val) > 0.001:
            print " x = ", x
            print " Float = ", Q_val_double, "   Long = ", Q_val
            raise RuntimeError("The roundoff error is bigger than 0.001, so we should use more precision somewhere...")

        #print " Float = ", Q_val_double, "   Long = ", Q_val, "  XX "
        #print " The float value is ", Q_val_double
        #print " The associated long value is ", Q_val

        if (Q_val <= bound):
            #print " Have vector ",  x, " with value ", Q_val
            theta_vec[Q_val].append(deepcopy(x))


        ## 5. Check if x = 0, for exit condition. =)
        j = 0
        done_flag = True
        while (j < n):
            if (x[j] != 0):
                done_flag = False
            j += 1


        ## 3a. Increment (and carry if we go out of bounds)
        x[i] += 1
        while (x[i] > L[i]) and (i < n-1):
            i += 1
            x[i] += 1


    #print " Leaving ThetaVectors()"
    return theta_vec
Beispiel #36
0
def reduced_binary_form(self):
    """
    Find a form which is reduced in the sense that no further binary
    form reductions can be done to reduce the original form.

    EXAMPLES::

        sage: QuadraticForm(ZZ,2,[5,5,2]).reduced_binary_form()
        (Quadratic form in 2 variables over Integer Ring with coefficients: 
        [ 2 -1 ]
        [ * 2 ]
        ,
        [ 0 -1]
        [ 1  1])
    
    """
    R = self.base_ring()
    n = self.dim()
    interior_reduced_flag = False
    Q = deepcopy(self)
    M = matrix(R, n, n)
    for i in range(n):
        M[i, i] = 1

    while (interior_reduced_flag == False):
        interior_reduced_flag = True

        #print Q

        ## Arrange for (weakly) increasing diagonal entries
        for i in range(n):
            for j in range(i + 1, n):
                if Q[i, i] > Q[j, j]:
                    M_new = matrix(R, n, n)
                    for k in range(n):
                        M_new[k, k] = 1
                    M_new[i, j] = -1
                    M_new[j, i] = 1
                    M_new[i, i] = 0
                    M_new[j, j] = 1

                    Q = Q(M_new)
                    M = M * M_new
                    interior_reduced_flag = False
                    #print "A"

                ## Arrange for |b| <= a
                if abs(Q[i, j]) > Q[i, i]:
                    r = R(floor(round(Q[i, j] / (2 * Q[i, i]))))

                    M_new = matrix(R, n, n)
                    for k in range(n):
                        M_new[k, k] = 1
                    M_new[i, j] = -r

                    Q = Q(M_new)
                    M = M * M_new
                    interior_reduced_flag = False
                    #print "B"

    return Q, M
def theta_by_cholesky(self, q_prec):
    r"""
    Uses the real Cholesky decomposition to compute (the `q`-expansion of) the
    theta function of the quadratic form as a power series in `q` with terms
    correct up to the power `q^{\text{q\_prec}}`. (So its error is `O(q^
    {\text{q\_prec} + 1})`.)

    REFERENCE:
        From Cohen's "A Course in Computational Algebraic Number Theory" book,
        p 102.

    EXAMPLES::

        ## Check the sum of 4 squares form against Jacobi's formula
        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Theta = Q.theta_by_cholesky(10)
        sage: Theta
        1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10
        sage: Expected =  [1] + [8*sum([d for d in divisors(n) if d%4 != 0])  for n in range(1,11)]
        sage: Expected
        [1, 8, 24, 32, 24, 48, 96, 64, 24, 104, 144]
        sage: Theta.list() == Expected
        True

    ::

        ## Check the form x^2 + 3y^2 + 5z^2 + 7w^2 represents everything except 2 and 22.
        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Theta = Q.theta_by_cholesky(50)
        sage: Theta_list = Theta.list()
        sage: [m  for m in range(len(Theta_list))  if Theta_list[m] == 0]
        [2, 22]

    """
    ## RAISE AN ERROR -- This routine is deprecated!
    #raise NotImplementedError, "This routine is deprecated.  Try theta_series(), which uses theta_by_pari()."

    n = self.dim()
    theta = [0 for i in range(q_prec + 1)]
    PS = PowerSeriesRing(ZZ, 'q')

    bit_prec = 53  ## TO DO: Set this precision to reflect the appropriate roundoff
    Cholesky = self.cholesky_decomposition(
        bit_prec
    )  ## error estimate, to be confident through our desired q-precision.
    Q = Cholesky  ##  <----  REDUNDANT!!!
    R = RealField(bit_prec)
    half = R(0.5)

    ## 1. Initialize
    i = n - 1
    T = [R(0) for j in range(n)]
    U = [R(0) for j in range(n)]
    T[i] = R(q_prec)
    U[i] = 0
    L = [0 for j in range(n)]
    x = [0 for j in range(n)]

    ## 2. Compute bounds
    #Z = sqrt(T[i] / Q[i,i])      ## IMPORTANT NOTE: sqrt preserves the precision of the real number it's given... which is not so good... =|
    #L[i] = floor(Z - U[i])       ## Note: This is a Sage Integer
    #x[i] = ceil(-Z - U[i]) - 1   ## Note: This is a Sage Integer too

    done_flag = False
    from_step4_flag = False
    from_step3_flag = True  ## We start by pretending this, since then we get to run through 2 and 3a once. =)

    #double Q_val_double;
    #unsigned long Q_val;                 // WARNING: Still need a good way of checking overflow for this value...

    ## Big loop which runs through all vectors
    while (done_flag == False):

        ## Loop through until we get to i=1 (so we defined a vector x)
        while from_step3_flag or from_step4_flag:  ## IMPORTANT WARNING:  This replaces a do...while loop, so it may have to be adjusted!

            ## Go to directly to step 3 if we're coming from step 4, otherwise perform step 2.
            if from_step4_flag:
                from_step4_flag = False
            else:
                ## 2. Compute bounds
                from_step3_flag = False
                Z = sqrt(T[i] / Q[i, i])
                L[i] = floor(Z - U[i])
                x[i] = ceil(-Z - U[i]) - 1

            ## 3a. Main loop

            ## DIAGNOSTIC
            #print
            #print "  L = ", L
            #print "  x = ", x

            x[i] += 1
            while (x[i] > L[i]):

                ## DIAGNOSTIC
                #print "  x = ", x

                i += 1
                x[i] += 1

            ## 3b. Main loop
            if (i > 0):
                from_step3_flag = True

                ## DIAGNOSTIC
                #print " i = " + str(i)
                #print " T[i] = " + str(T[i])
                #print " Q[i,i] = " + str(Q[i,i])
                #print " x[i] = " + str(x[i])
                #print " U[i] = " + str(U[i])
                #print " x[i] + U[i] = " + str(x[i] + U[i])
                #print " T[i-1] = " + str(T[i-1])

                T[i - 1] = T[i] - Q[i, i] * (x[i] + U[i]) * (x[i] + U[i])

                # DIAGNOSTIC
                #print " T[i-1] = " + str(T[i-1])
                #print

                i += -1
                U[i] = 0
                for j in range(i + 1, n):
                    U[i] += Q[i, j] * x[j]

        ## 4. Solution found (This happens when i=0)
        from_step4_flag = True
        Q_val_double = q_prec - T[0] + Q[0, 0] * (x[0] + U[0]) * (x[0] + U[0])
        Q_val = floor(
            Q_val_double + half
        )  ## Note: This rounds the value up, since the "round" function returns a float, but floor returns integer.

        ## DIAGNOSTIC
        #print " Q_val_double = ",  Q_val_double
        #print " Q_val = ",  Q_val
        #raise RuntimeError

        ## OPTIONAL SAFETY CHECK:
        eps = 0.000000001
        if (abs(Q_val_double - Q_val) > eps):
            raise RuntimeError, "Oh No!  We have a problem with the floating point precision... \n" \
                + " Q_val_double = " + str(Q_val_double) + "\n" \
                + " Q_val = " + str(Q_val) + "\n" \
                + " x = " + str(x) + "\n"

        ## DIAGNOSTIC
        #print " The float value is " + str(Q_val_double)
        #print " The associated long value is " + str(Q_val)
        #print

        if (Q_val <= q_prec):
            theta[Q_val] += 2

        ## 5. Check if x = 0, for exit condition. =)
        done_flag = True
        for j in range(n):
            if (x[j] != 0):
                done_flag = False

    ## Set the value: theta[0] = 1
    theta[0] = 1

    ## DIAGNOSTIC
    #print "Leaving ComputeTheta \n"

    ## Return the series, truncated to the desired q-precision
    return PS(theta)
Beispiel #38
0
def theta_by_cholesky(self, q_prec):
    r"""
    Uses the real Cholesky decomposition to compute (the `q`-expansion of) the
    theta function of the quadratic form as a power series in `q` with terms
    correct up to the power `q^{\text{q\_prec}}`. (So its error is `O(q^
    {\text{q\_prec} + 1})`.)

    REFERENCE:

        From Cohen's "A Course in Computational Algebraic Number Theory" book,
        p 102.

    EXAMPLES::

        ## Check the sum of 4 squares form against Jacobi's formula
        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Theta = Q.theta_by_cholesky(10)
        sage: Theta
        1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10
        sage: Expected =  [1] + [8*sum([d for d in divisors(n) if d%4 != 0])  for n in range(1,11)]
        sage: Expected
        [1, 8, 24, 32, 24, 48, 96, 64, 24, 104, 144]
        sage: Theta.list() == Expected
        True

    ::

        ## Check the form x^2 + 3y^2 + 5z^2 + 7w^2 represents everything except 2 and 22.
        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Theta = Q.theta_by_cholesky(50)
        sage: Theta_list = Theta.list()
        sage: [m  for m in range(len(Theta_list))  if Theta_list[m] == 0]
        [2, 22]

    """
    ## RAISE AN ERROR -- This routine is deprecated!
    #raise NotImplementedError, "This routine is deprecated.  Try theta_series(), which uses theta_by_pari()."


    n = self.dim()
    theta = [0 for i in range(q_prec+1)]
    PS = PowerSeriesRing(ZZ, 'q')

    bit_prec = 53                                       ## TO DO: Set this precision to reflect the appropriate roundoff
    Cholesky = self.cholesky_decomposition(bit_prec)     ## error estimate, to be confident through our desired q-precision.
    Q = Cholesky      ##  <----  REDUNDANT!!!
    R = RealField(bit_prec)
    half = R(0.5)



    ## 1. Initialize
    i = n - 1
    T = [R(0)  for j in range(n)]
    U = [R(0)  for j in range(n)]
    T[i] = R(q_prec)
    U[i] = 0
    L = [0 for j in range (n)]
    x = [0 for j in range (n)]


    ## 2. Compute bounds
    #Z = sqrt(T[i] / Q[i,i])      ## IMPORTANT NOTE: sqrt preserves the precision of the real number it's given... which is not so good... =|
    #L[i] = floor(Z - U[i])       ## Note: This is a Sage Integer
    #x[i] = ceil(-Z - U[i]) - 1   ## Note: This is a Sage Integer too


    done_flag = False
    from_step4_flag = False
    from_step3_flag = True        ## We start by pretending this, since then we get to run through 2 and 3a once. =)

    #double Q_val_double;
    #unsigned long Q_val;                 // WARNING: Still need a good way of checking overflow for this value...



    ## Big loop which runs through all vectors
    while (done_flag == False):

        ## Loop through until we get to i=1 (so we defined a vector x)
        while from_step3_flag or from_step4_flag:              ## IMPORTANT WARNING:  This replaces a do...while loop, so it may have to be adjusted!

            ## Go to directly to step 3 if we're coming from step 4, otherwise perform step 2.
            if from_step4_flag:
                from_step4_flag = False
            else:
                ## 2. Compute bounds
                from_step3_flag = False
                Z = sqrt(T[i] / Q[i,i])
                L[i] = floor(Z - U[i])
                x[i] = ceil(-Z - U[i]) - 1



            ## 3a. Main loop

            ## DIAGNOSTIC
            #print
            #print "  L = ", L
            #print "  x = ", x

            x[i] += 1
            while (x[i] > L[i]):

                ## DIAGNOSTIC
                #print "  x = ", x

                i += 1
                x[i] += 1


            ## 3b. Main loop
            if (i > 0):
                from_step3_flag = True

                ## DIAGNOSTIC
                #print " i = " + str(i)
                #print " T[i] = " + str(T[i])
                #print " Q[i,i] = " + str(Q[i,i])
                #print " x[i] = " + str(x[i])
                #print " U[i] = " + str(U[i])
                #print " x[i] + U[i] = " + str(x[i] + U[i])
                #print " T[i-1] = " + str(T[i-1])

                T[i-1] = T[i] - Q[i,i] * (x[i] + U[i]) * (x[i] + U[i])

                # DIAGNOSTIC
                #print " T[i-1] = " + str(T[i-1])
                #print

                i += - 1
                U[i] = 0
                for j in range(i+1, n):
                    U[i] += Q[i,j] * x[j]



        ## 4. Solution found (This happens when i=0)
        from_step4_flag = True
        Q_val_double = q_prec - T[0] + Q[0,0] * (x[0] + U[0]) * (x[0] + U[0])
        Q_val = floor(Q_val_double + half)        ## Note: This rounds the value up, since the "round" function returns a float, but floor returns integer.



        ## DIAGNOSTIC
        #print " Q_val_double = ",  Q_val_double
        #print " Q_val = ",  Q_val
        #raise RuntimeError


        ## OPTIONAL SAFETY CHECK:
        eps = 0.000000001
        if (abs(Q_val_double - Q_val) > eps):
            raise RuntimeError("Oh No!  We have a problem with the floating point precision... \n" \
                + " Q_val_double = " + str(Q_val_double) + "\n" \
                + " Q_val = " + str(Q_val) + "\n" \
                + " x = " + str(x) + "\n")


        ## DIAGNOSTIC
        #print " The float value is " + str(Q_val_double)
        #print " The associated long value is " + str(Q_val)
        #print

        if (Q_val <= q_prec):
            theta[Q_val] += 2

        ## 5. Check if x = 0, for exit condition. =)
        done_flag = True
        for j in range(n):
            if (x[j] != 0):
                done_flag = False


    ## Set the value: theta[0] = 1
    theta[0] = 1

    ## DIAGNOSTIC
    #print "Leaving ComputeTheta \n"


    ## Return the series, truncated to the desired q-precision
    return PS(theta)