示例#1
0
    def guess(self, sequence, algorithm='sage'):
        """
        Return the minimal CFiniteSequence that generates the sequence.

        Assume the first value has index 0.

        INPUT:

        - ``sequence`` -- list of integers
        - ``algorithm`` -- string
            - 'sage' - the default is to use Sage's matrix kernel function
            - 'pari' - use Pari's implementation of LLL
            - 'bm' - use Sage's Berlekamp-Massey algorithm

        OUTPUT:

        - a CFiniteSequence, or 0 if none could be found

        With the default kernel method, trailing zeroes are chopped
        off before a guessing attempt. This may reduce the data
        below the accepted length of six values.

        EXAMPLES::

            sage: C.<x> = CFiniteSequences(QQ)
            sage: C.guess([1,2,4,8,16,32])
            C-finite sequence, generated by 1/(-2*x + 1)
            sage: r = C.guess([1,2,3,4,5])
            Traceback (most recent call last):
            ...
            ValueError: Sequence too short for guessing.

        With Berlekamp-Massey, if an odd number of values is given, the last one is dropped.
        So with an odd number of values the result may not generate the last value::

            sage: r = C.guess([1,2,4,8,9], algorithm='bm'); r
            C-finite sequence, generated by 1/(-2*x + 1)
            sage: r[0:5]
            [1, 2, 4, 8, 16]
        """
        S = self.polynomial_ring()
        if algorithm == 'bm':
            from sage.matrix.berlekamp_massey import berlekamp_massey
            if len(sequence) < 2:
                raise ValueError('Sequence too short for guessing.')
            R = PowerSeriesRing(QQ, 'x')
            if len(sequence) % 2 == 1:
                sequence = sequence[:-1]
            l = len(sequence) - 1
            denominator = S(berlekamp_massey(sequence).list()[::-1])
            numerator = R(S(sequence) * denominator, prec=l).truncate()

            return CFiniteSequence(numerator / denominator)
        elif algorithm == 'pari':
            global _gp
            if len(sequence) < 6:
                raise ValueError('Sequence too short for guessing.')
            if _gp is None:
                _gp = Gp()
                _gp("ggf(v)=local(l,m,p,q,B);l=length(v);B=floor(l/2);\
                if(B<3,return(0));m=matrix(B,B,x,y,v[x-y+B+1]);\
                q=qflll(m,4)[1];if(length(q)==0,return(0));\
                p=sum(k=1,B,x^(k-1)*q[k,1]);\
                q=Pol(Pol(vector(l,n,v[l-n+1]))*p+O(x^(B+1)));\
                if(polcoeff(p,0)<0,q=-q;p=-p);q=q/p;p=Ser(q+O(x^(l+1)));\
                for(m=1,l,if(polcoeff(p,m-1)!=v[m],return(0)));q")
            _gp.set('gf', sequence)
            _gp("gf=ggf(gf)")
            num = S(sage_eval(_gp.eval("Vec(numerator(gf))"))[::-1])
            den = S(sage_eval(_gp.eval("Vec(denominator(gf))"))[::-1])
            if num == 0:
                return 0
            else:
                return CFiniteSequence(num / den)
        else:
            from sage.matrix.constructor import matrix
            from sage.functions.other import floor, ceil
            from numpy import trim_zeros
            l = len(sequence)
            while l > 0 and sequence[l - 1] == 0:
                l -= 1
            sequence = sequence[:l]
            if l == 0:
                return 0
            if l < 6:
                raise ValueError('Sequence too short for guessing.')

            hl = ceil(ZZ(l) / 2)
            A = matrix([sequence[k:k + hl] for k in range(hl)])
            K = A.kernel()
            if K.dimension() == 0:
                return 0
            R = PolynomialRing(QQ, 'x')
            den = R(trim_zeros(K.basis()[-1].list()[::-1]))
            if den == 1:
                return 0
            offset = next((i for i, x in enumerate(sequence) if x != 0), None)
            S = PowerSeriesRing(QQ, 'x', default_prec=l - offset)
            num = S(R(sequence) * den).add_bigoh(floor(ZZ(l) / 2 +
                                                       1)).truncate()
            if num == 0 or sequence != S(num / den).list():
                return 0
            else:
                return CFiniteSequence(num / den)
    def molien_series(self,
                      chi=None,
                      return_series=True,
                      prec=20,
                      variable='t'):
        r"""
        Compute the Molien series of this finite group with respect to the
        character ``chi``. It can be returned either as a rational function
        in one variable or a power series in one variable. The base field
        must be a finite field, the rationals, or a cyclotomic field.

        Note that the base field characteristic cannot divide the group
        order (i.e., the non-modular case).

        ALGORITHM:

        For a finite group `G` in characteristic zero we construct the Molien series as

        .. MATH::

            \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\text{det}(I-tg)},

        where `I` is the identity matrix and `t` an indeterminate.

        For characteristic `p` not dividing the order of `G`, let `k` be the base field
        and `N` the order of `G`. Define `\lambda` as a primitive `N`-th root of unity over `k`
        and `\omega` as a primitive `N`-th root of unity over `\QQ`. For each `g \in G`
        define `k_i(g)` to be the positive integer such that
        `e_i = \lambda^{k_i(g)}` for each eigenvalue `e_i` of `g`. Then the Molien series
        is computed as

        .. MATH::

            \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\prod_{i=1}^n(1 - t\omega^{k_i(g)})},

        where `t` is an indeterminant. [Dec1998]_

        INPUT:

        - ``chi`` -- (default: trivial character) a linear group character of this group

        - ``return_series`` -- boolean (default: ``True``) if ``True``, then returns
          the Molien series as a power series, ``False`` as a rational function

        - ``prec`` -- integer (default: 20); power series default precision

        - ``variable`` -- string (default: ``'t'``); Variable name for the Molien series

        OUTPUT: single variable rational function or power series with integer coefficients

        EXAMPLES::

            sage: MatrixGroup(matrix(QQ,2,2,[1,1,0,1])).molien_series()
            Traceback (most recent call last):
            ...
            NotImplementedError: only implemented for finite groups
            sage: MatrixGroup(matrix(GF(3),2,2,[1,1,0,1])).molien_series()
            Traceback (most recent call last):
            ...
            NotImplementedError: characteristic cannot divide group order

        Tetrahedral Group::

            sage: K.<i> = CyclotomicField(4)
            sage: Tetra =  MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0])
            sage: Tetra.molien_series(prec=30)
            1 + t^8 + 2*t^12 + t^16 + 2*t^20 + 3*t^24 + 2*t^28 + O(t^30)
            sage: mol = Tetra.molien_series(return_series=False); mol
            (t^8 - t^4 + 1)/(t^16 - t^12 - t^4 + 1)
            sage: mol.parent()
            Fraction Field of Univariate Polynomial Ring in t over Integer Ring
            sage: chi = Tetra.character(Tetra.character_table()[1])
            sage: Tetra.molien_series(chi, prec=30, variable='u')
            u^6 + u^14 + 2*u^18 + u^22 + 2*u^26 + 3*u^30 + 2*u^34 + O(u^36)
            sage: chi = Tetra.character(Tetra.character_table()[2])
            sage: Tetra.molien_series(chi)
            t^10 + t^14 + t^18 + 2*t^22 + 2*t^26 + O(t^30)

        ::

            sage: S3 = MatrixGroup(SymmetricGroup(3))
            sage: mol = S3.molien_series(prec=10); mol
            1 + t + 2*t^2 + 3*t^3 + 4*t^4 + 5*t^5 + 7*t^6 + 8*t^7 + 10*t^8 + 12*t^9 + O(t^10)
            sage: mol.parent()
            Power Series Ring in t over Integer Ring

        Octahedral Group::

            sage: K.<v> = CyclotomicField(8)
            sage: a = v-v^3 #sqrt(2)
            sage: i = v^2
            sage: Octa = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [(1+i)/a,0, 0,(1-i)/a])
            sage: Octa.molien_series(prec=30)
            1 + t^8 + t^12 + t^16 + t^18 + t^20 + 2*t^24 + t^26 + t^28 + O(t^30)

        Icosahedral Group::

            sage: K.<v> = CyclotomicField(10)
            sage: z5 = v^2
            sage: i = z5^5
            sage: a = 2*z5^3 + 2*z5^2 + 1 #sqrt(5)
            sage: Ico = MatrixGroup([[z5^3,0, 0,z5^2], [0,1, -1,0], [(z5^4-z5)/a, (z5^2-z5^3)/a, (z5^2-z5^3)/a, -(z5^4-z5)/a]])
            sage: Ico.molien_series(prec=40)
            1 + t^12 + t^20 + t^24 + t^30 + t^32 + t^36 + O(t^40)

        ::

            sage: G = MatrixGroup(CyclicPermutationGroup(3))
            sage: chi = G.character(G.character_table()[1])
            sage: G.molien_series(chi, prec=10)
            t + 2*t^2 + 3*t^3 + 5*t^4 + 7*t^5 + 9*t^6 + 12*t^7 + 15*t^8 + 18*t^9 + 22*t^10 + O(t^11)

        ::

            sage: K = GF(5)
            sage: S = MatrixGroup(SymmetricGroup(4))
            sage: G = MatrixGroup([matrix(K,4,4,[K(y) for u in m.list() for y in u])for m in S.gens()])
            sage: G.molien_series(return_series=False)
            1/(t^10 - t^9 - t^8 + 2*t^5 - t^2 - t + 1)

        ::

            sage: i = GF(7)(3)
            sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]])
            sage: chi = G.character(G.character_table()[4])
            sage: G.molien_series(chi)
            3*t^5 + 6*t^11 + 9*t^17 + 12*t^23 + O(t^25)
        """
        if not self.is_finite():
            raise NotImplementedError("only implemented for finite groups")
        if chi is None:
            chi = self.trivial_character()
        M = self.matrix_space()
        R = FractionField(self.base_ring())
        N = self.order()
        if R.characteristic() == 0:
            P = PolynomialRing(R, variable)
            t = P.gen()
            #it is possible the character is over a larger cyclotomic field
            K = chi.values()[0].parent()
            if K.degree() != 1:
                if R.degree() != 1:
                    L = K.composite_fields(R)[0]
                else:
                    L = K
            else:
                L = R
            mol = P(0)
            for g in self:
                mol += L(chi(g)) / (M.identity_matrix() -
                                    t * g.matrix()).det().change_ring(L)
        elif R.characteristic().divides(N):
            raise NotImplementedError(
                "characteristic cannot divide group order")
        else:  #char p>0
            #find primitive Nth roots of unity over base ring and QQ
            F = cyclotomic_polynomial(N).change_ring(R)
            w = F.roots(ring=R.algebraic_closure(), multiplicities=False)[0]
            #don't need to extend further in this case since the order of
            #the roots of unity in the character divide the order of the group
            L = CyclotomicField(N, 'v')
            v = L.gen()
            #construct Molien series
            P = PolynomialRing(L, variable)
            t = P.gen()
            mol = P(0)
            for g in self:
                #construct Phi
                phi = L(chi(g))
                for e in g.matrix().eigenvalues():
                    #find power such that w**n  = e
                    n = 1
                    while w**n != e and n < N + 1:
                        n += 1
                    #raise v to that power
                    phi *= (1 - t * v**n)
                mol += P(1) / phi
        #We know the coefficients will be integers
        mol = mol.numerator().change_ring(ZZ) / mol.denominator().change_ring(
            ZZ)
        #divide by group order
        mol /= N
        if return_series:
            PS = PowerSeriesRing(ZZ, variable, default_prec=prec)
            return PS(mol)
        return mol
示例#3
0
def characteristic_polynomial_from_traces(traces, d, q, i, sign):
    r"""
    Given a sequence of traces `t_1, \dots, t_k`, return the
    corresponding characteristic polynomial with Weil numbers as roots.

    The characteristic polynomial is defined by the generating series

    .. MATH::

        P(T) = \exp\left(- \sum_{k\geq 1} t_k \frac{T^k}{k}\right)

    and should have the property that reciprocals of all roots have
    absolute value `q^{i/2}`.

    INPUT:

    - ``traces`` -- a list of integers `t_1, \dots, t_k`

    - ``d`` -- the degree of the characteristic polynomial

    - ``q`` -- power of a prime number

    - ``i`` -- integer, the weight in the motivic sense

    - ``sign`` -- integer, the sign

    OUTPUT:

    a polynomial

    EXAMPLES::

        sage: from sage.modular.hypergeometric_motive import characteristic_polynomial_from_traces
        sage: characteristic_polynomial_from_traces([1, 1], 1, 3, 0, -1)
        -T + 1
        sage: characteristic_polynomial_from_traces([25], 1, 5, 4, -1)
        -25*T + 1

        sage: characteristic_polynomial_from_traces([3], 2, 5, 1, 1)
        5*T^2 - 3*T + 1
        sage: characteristic_polynomial_from_traces([1], 2, 7, 1, 1)
        7*T^2 - T + 1

        sage: characteristic_polynomial_from_traces([20], 3, 29, 2, 1)
        24389*T^3 - 580*T^2 - 20*T + 1
        sage: characteristic_polynomial_from_traces([12], 3, 13, 2, -1)
        -2197*T^3 + 156*T^2 - 12*T + 1

        sage: characteristic_polynomial_from_traces([36,7620], 4, 17, 3, 1)
        24137569*T^4 - 176868*T^3 - 3162*T^2 - 36*T + 1
        sage: characteristic_polynomial_from_traces([-4,276], 4, 5, 3, 1)
        15625*T^4 + 500*T^3 - 130*T^2 + 4*T + 1
        sage: characteristic_polynomial_from_traces([4,-276], 4, 5, 3, 1)
        15625*T^4 - 500*T^3 + 146*T^2 - 4*T + 1
        sage: characteristic_polynomial_from_traces([22, 484], 4, 31, 2, -1)
        -923521*T^4 + 21142*T^3 - 22*T + 1

    TESTS::

        sage: characteristic_polynomial_from_traces([-36], 4, 17, 3, 1)
        Traceback (most recent call last):
        ...
        ValueError: not enough traces were given
    """
    if len(traces) < d // 2:
        raise ValueError('not enough traces were given')
    if i % 2 and d % 2:
        raise ValueError('i and d may not both be odd')
    t = PowerSeriesRing(QQ, 't').gen()
    ring = PolynomialRing(ZZ, 'T')

    series = sum(-api * t**(i + 1) / (i + 1) for i, api in enumerate(traces))
    series = series.O(d // 2 + 1).exp()
    coeffs = list(series)
    coeffs += [0] * max(0, d // 2 + 1 - len(coeffs))

    data = [0 for _ in range(d + 1)]
    for k in range(d // 2 + 1):
        data[k] = coeffs[k]
    for k in range(d // 2 + 1, d + 1):
        data[k] = sign * coeffs[d - k] * q**(i * (k - d / 2))
    return ring(data)
示例#4
0
    def _rank(self, K) :
        """
        Return the dimension of the level N space of given weight. 
        """
        if not K is QQ :
            raise NotImplementedError
        
        if not self.__level.is_prime() :
            raise NotImplementedError
    
        if self.__weight % 2 != 0 :
            raise NotImplementedError( "Only even weights available")
    
        N = self.__level
        
        if N == 1 :
            ## By Igusa's theorem on the generators of the graded ring
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)
                                
            dims = 1 / ((1 - t**4) * (1 - t**6) * (1 - t**10) * (1 - t**12)).add_bigoh(self.__weight + 1)
            
            return dims[self.__weight]
        if N == 2 :
            ## As in Ibukiyama, Onodera - On the graded ring of modular forms of the Siegel
            ## paramodular group of level 2, Proposition 2
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)
                                
            dims = ((1 + t**10) * (1 + t ** 12) * (1 + t**11))
            dims = dims / ((1 - t**4) * (1 - t**6) * (1 - t**8) * (1 - t**12)).add_bigoh(self.__weight + 1)
            
            return dims[self.__weight]
        if N == 3 :
            ## By Dern, Paramodular forms of degree 2 and level 3, Corollary 5.6
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)
            
            dims = ((1 + t**12) * (1 + t**8 + t**9 + t**10 + t**11 + t**19))
            dims = dims / ((1 - t**4) * (1 - t**6)**2 * (1 - t**12)).add_bigoh(self.__weight + 1)
            
            return dims[self.__weight]
        if N == 5 :
            ## By Marschner, Paramodular forms of degree 2 with particular emphasis on level t = 5,
            ## Corollary 7.3.4. PhD thesis electronically available via the library of
            ## RWTH University, Aachen, Germany  
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)
            
            dims =   t**30 + t**24 + t**23 + 2*t**22 + t**21 + 2*t**20 + t**19 + 2*t**18 \
                   + 2*t**16 + 2*t**14 + 2*t**12 + t**11 + 2*t**10 + t**9 + 2*t**8 + t**7 + t**6 + 1
            dims = dims / ((1 - t**4) * (1 - t**5) * (1 - t**6) * (1 - t**12)).add_bigoh(self.__weight + 1)
            
            return dims[self.__weight]
        
        if self.__weight == 2 :
            ## There are only cuspforms, since there is no elliptic modular form
            ## of weight 2.
            if N < 277 :
                ## Poor, Yuen - Paramodular cusp forms tells us that all forms are
                ## Gritsenko lifts
                return JacobiFormD1NN_Gamma(self.__level, 2)._rank(QQ)

            raise NotImplementedError
        elif self.__weight == 4 :
            ## This is the formula cited by Poor and Yuen in Paramodular cusp forms
            cuspidal_dim =  Integer( (N**2 - 143) / Integer(576) + N / Integer(8)
                                     + kronecker_symbol(-1, N) * (N - 12) / Integer(96)
                                     + kronecker_symbol(2, N) / Integer(8)
                                     + kronecker_symbol(3, N) / Integer(12)
                                     + kronecker_symbol(-3, N) * N / Integer(36) )
        else :
            ## This is the formula given by Ibukiyama in
            ## Relations of dimension of automorphic forms of Sp(2,R) and its compact twist Sp(2),
            ## Theorem 4
            p = N
            k = self.__weight
            
            ## This is the reversed Ibukiyama symbol [.., .., ..; ..]
            def ibukiyama_symbol(modulus, *args) :
                return args[k % modulus]

            ## if p == 2 this formula is wrong. If the weight is even it differs by
            ## -3/16 from the true dimension and if the weight is odd it differs by
            ## -1/16 from the true dimension. 
            H1 = (p**2 + 1) * (2 * k - 2) * (2 * k - 3) * (2 * k - 4) / Integer(2**9 * 3**3 * 5)
            H2 = (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**8 * 3**2) \
                 + ( (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**7 * 3)
                     if p !=2 else
                     (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**9) )
            H3 = ( ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2**4 * 3)
                   if p != 3 else
                   5 * ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2**5 * 3) )
            H4 = ( ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2**2 * 3**3)
                   if p != 2 else
                   5 * ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2**2 * 3**3) )
            H5 = ibukiyama_symbol(6, -1, -k + 1, -k + 2, 1, k - 1, k - 2) / Integer(2**2 * 3**2)
            if p % 4 == 1 :
                H6 = 5 * (2 * k - 3) * (p + 1) / Integer(2**7 * 3) + (-1)**k * (p + 1) / Integer(2**7)
            elif p % 4 == 3 :
                H6 = (2 * k - 3) * (p - 1) / Integer(2**7) + 5 * (-1)**k * (p - 1) / Integer(2**7 * 3)
            else :
                H6 = 3 * (2 * k - 3) / Integer(2**7) + 7 * (-1)**k / Integer(2**7 * 3)
            if p % 3 == 1 :
                H7 =   (2 * k - 3) * (p + 1) / Integer(2 * 3**3) \
                     + (p + 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer(2**2 * 3**3)
            elif p % 3 == 2 :
                H7 =   (2 * k - 3) * (p - 1) / Integer(2**2 * 3**3) \
                     + (p - 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer(2 * 3**3)
            else :
                H7 =   5 * (2 * k - 3) / Integer(2**2 * 3**3) \
                     + ibukiyama_symbol(3, 0, -1, 1) / Integer(3**3)
            H8 = ibukiyama_symbol(12, 1, 0, 0, -1, -1, -1, -1, 0, 0, 1, 1, 1) / Integer(2 * 3)
            H9 = ( 2 * ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(3**2)
                   if p != 2 else
                   ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(2 * 3**2) )
            H10 = (1 + kronecker_symbol(5, p)) * ibukiyama_symbol(5, 1, 0, 0, -1, 0) / Integer(5)
            H11 = (1 + kronecker_symbol(2, p)) * ibukiyama_symbol(4, 1, 0, 0, -1) / Integer(2**3)
            if p % 12 == 1 :
                H12 = ibukiyama_symbol(3, 0, 1, -1) / Integer(2 * 3)
            elif p % 12 == 11 :
                H12 = (-1)**k / Integer(2 * 3)
            elif p == 2 or p == 3 :
                H12 = (-1)**k / Integer(2**2 * 3)
            else :
                H12 = 0
                
            I1 = ibukiyama_symbol(6, 0, 1, 1, 0, -1, -1) / Integer(6)
            I2 = ibukiyama_symbol(3, -2, 1, 1) / Integer(2 * 3**2)
            if p == 3 :
                I3 = ibukiyama_symbol(3, -2, 1, 1)/ Integer(3**2)
            elif p % 3 == 1 :
                I3 = 2 * ibukiyama_symbol(3, -1, 1, 0) / Integer(3**2)
            else :
                I3 = 2 * ibukiyama_symbol(3, -1, 0, 1) / Integer(3**2)
            I4 = ibukiyama_symbol(4, -1, 1, 1, -1) / Integer(2**2)
            I5 = (-1)**k / Integer(2**3)
            I6 = (-1)**k * (2 - kronecker_symbol(-1, p)) / Integer(2**4)
            I7 = -(-1)**k * (2 * k - 3) / Integer(2**3 * 3)
            I8 = -p * (2 * k - 3) / Integer(2**4 * 3**2)
            I9 = -1 / Integer(2**3 * 3)
            I10 = (p + 1) / Integer(2**3 * 3)
            I11 = -(1 + kronecker_symbol(-1, p)) / Integer(8)
            I12 = -(1 + kronecker_symbol(-3, p)) / Integer(6)
            
            cuspidal_dim =   H1 + H2 + H3 + H4 + H5 + H6 + H7 + H8 + H9 + H10 + H11 + H12 \
                           + I1 + I2 + I3 + I4 + I5 + I6 + I7 + I8 + I9 + I10 + I11 + I12
                       

        mfs = ModularForms(1, self.__weight)
        
        return cuspidal_dim + mfs.dimension() + mfs.cuspidal_subspace().dimension()
示例#5
0
文件: rings.py 项目: yazici/sage
        def __getitem__(self, arg):
            """
            Extend this ring by one or several elements to create a polynomial
            ring, a power series ring, or an algebraic extension.

            This is a convenience method intended primarily for interactive
            use.

            .. SEEALSO::

                :func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`,
                :func:`~sage.rings.power_series_ring.PowerSeriesRing`,
                :meth:`~sage.rings.ring.Ring.extension`,
                :meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`,
                :meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`,
                :meth:`sage.structure.parent.Parent.__getitem__`

            EXAMPLES:

            We create several polynomial rings::

                sage: ZZ['x']
                Univariate Polynomial Ring in x over Integer Ring
                sage: QQ['x']
                Univariate Polynomial Ring in x over Rational Field
                sage: GF(17)['abc']
                Univariate Polynomial Ring in abc over Finite Field of size 17
                sage: GF(17)['a,b,c']
                Multivariate Polynomial Ring in a, b, c over Finite Field of size 17
                sage: GF(17)['a']['b']
                Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17

            We can create skew polynomial rings::

                sage: k.<t> = GF(5^3)
                sage: Frob = k.frobenius_endomorphism()
                sage: k['x',Frob]
                Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5

            We can also create power series rings by using double brackets::

                sage: QQ[['t']]
                Power Series Ring in t over Rational Field
                sage: ZZ[['W']]
                Power Series Ring in W over Integer Ring

                sage: ZZ[['x,y,z']]
                Multivariate Power Series Ring in x, y, z over Integer Ring
                sage: ZZ[['x','T']]
                Multivariate Power Series Ring in x, T over Integer Ring

            Use :func:`~sage.rings.fraction_field.Frac` or
            :meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain
            the fields of rational functions and Laurent series::

                sage: Frac(QQ['t'])
                Fraction Field of Univariate Polynomial Ring in t over Rational Field
                sage: Frac(QQ[['t']])
                Laurent Series Ring in t over Rational Field
                sage: QQ[['t']].fraction_field()
                Laurent Series Ring in t over Rational Field

            Note that the same syntax can be used to create number fields::

                sage: QQ[I]
                Number Field in I with defining polynomial x^2 + 1 with I = 1*I
                sage: QQ[I].coerce_embedding()
                Generic morphism:
                  From: Number Field in I with defining polynomial x^2 + 1 with I = 1*I
                  To:   Complex Lazy Field
                  Defn: I -> 1*I

            ::

                sage: QQ[sqrt(2)]
                Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095?
                sage: QQ[sqrt(2)].coerce_embedding()
                Generic morphism:
                  From: Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095?
                  To:   Real Lazy Field
                  Defn: sqrt2 -> 1.414213562373095?

            ::

                sage: QQ[sqrt(2),sqrt(3)]
                Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field

            and orders in number fields::

                sage: ZZ[I]
                Order in Number Field in I with defining polynomial x^2 + 1 with I = 1*I
                sage: ZZ[sqrt(5)]
                Order in Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?
                sage: ZZ[sqrt(2)+sqrt(3)]
                Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1 with a = 3.146264369941973?

            Embeddings are found for simple extensions (when that makes sense)::

                sage: QQi.<i> = QuadraticField(-1, 'i')
                sage: QQ[i].coerce_embedding()
                Generic morphism:
                  From: Number Field in i with defining polynomial x^2 + 1 with i = 1*I
                  To:   Complex Lazy Field
                  Defn: i -> 1*I

            TESTS:

            A few corner cases::

                sage: QQ[()]
                Multivariate Polynomial Ring in no variables over Rational Field

                sage: QQ[[]]
                Traceback (most recent call last):
                ...
                TypeError: power series rings must have at least one variable

            These kind of expressions do not work::

                sage: QQ['a,b','c']
                Traceback (most recent call last):
                ...
                ValueError: variable name 'a,b' is not alphanumeric
                sage: QQ[['a,b','c']]
                Traceback (most recent call last):
                ...
                ValueError: variable name 'a,b' is not alphanumeric

                sage: QQ[[['x']]]
                Traceback (most recent call last):
                ...
                TypeError: expected R[...] or R[[...]], not R[[[...]]]

            Extension towers are built as follows and use distinct generator names::

                sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)]
                sage: K
                Number Field in a with defining polynomial x^3 - 2 over its base field
                sage: K.base_field()
                Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field
                sage: K.base_field().base_field()
                Number Field in b with defining polynomial x^3 - 3

            Embeddings::

                sage: QQ[I](I.pyobject())
                I
                sage: a = 10^100; expr = (2*a + sqrt(2))/(2*a^2-1)
                sage: QQ[expr].coerce_embedding() is None
                False
                sage: QQ[sqrt(5)].gen() > 0
                True
                sage: expr = sqrt(2) + I*(cos(pi/4, hold=True) - sqrt(2)/2)
                sage: QQ[expr].coerce_embedding()
                Generic morphism:
                  From: Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?
                  To:   Real Lazy Field
                  Defn: a -> 1.414213562373095?
            """
            def normalize_arg(arg):
                if isinstance(arg, (tuple, list)):
                    # Allowing arbitrary iterables would create confusion, but we
                    # may want to support a few more.
                    return tuple(arg)
                elif isinstance(arg, str):
                    return tuple(arg.split(','))
                else:
                    return (arg,)

            # 1. If arg is a list, try to return a power series ring.

            if isinstance(arg, list):
                if arg == []:
                    raise TypeError("power series rings must have at least one variable")
                elif len(arg) == 1:
                    # R[["a,b"]], R[[(a,b)]]...
                    if isinstance(arg[0], list):
                        raise TypeError("expected R[...] or R[[...]], not R[[[...]]]")
                    elts = normalize_arg(arg[0])
                else:
                    elts = normalize_arg(arg)
                from sage.rings.power_series_ring import PowerSeriesRing
                return PowerSeriesRing(self, elts)

            if isinstance(arg, tuple):
                from sage.categories.morphism import Morphism
                if len(arg) == 2 and isinstance(arg[1], Morphism):
                   from sage.rings.polynomial.skew_polynomial_ring_constructor import SkewPolynomialRing
                   return SkewPolynomialRing(self, arg[1], names=arg[0])

            # 2. Otherwise, if all specified elements are algebraic, try to
            #    return an algebraic extension

            elts = normalize_arg(arg)

            try:
                minpolys = [a.minpoly() for a in elts]
            except (AttributeError, NotImplementedError, ValueError, TypeError):
                minpolys = None

            if minpolys:
                # how to pass in names?
                names = tuple(_gen_names(elts))
                if len(elts) == 1:
                    from sage.rings.all import CIF, CLF, RLF
                    elt = elts[0]
                    try:
                        iv = CIF(elt)
                    except (TypeError, ValueError):
                        emb = None
                    else:
                        # First try creating an ANRoot manually, because
                        # extension(..., embedding=CLF(expr)) (or
                        # ...QQbar(expr)) would normalize the expression in
                        # QQbar, which currently is VERY slow in many cases.
                        # This may fail when minpoly has close roots or elt is
                        # a complicated symbolic expression.
                        # TODO: Rewrite using #19362 and/or #17886 and/or
                        # #15600 once those issues are solved.
                        from sage.rings.qqbar import AlgebraicNumber, ANRoot
                        try:
                            elt = AlgebraicNumber(ANRoot(minpolys[0], iv))
                        except ValueError:
                            pass
                        # Force a real embedding when possible, to get the
                        # right ordered ring structure.
                        if (iv.imag().is_zero() or iv.imag().contains_zero()
                                                   and elt.imag().is_zero()):
                            emb = RLF(elt)
                        else:
                            emb = CLF(elt)
                        return self.extension(minpolys[0], names[0], embedding=emb)
                try:
                    # Doing the extension all at once is best, if possible...
                    return self.extension(minpolys, names)
                except (TypeError, ValueError):
                    # ...but we can also construct it iteratively
                    return reduce(lambda R, ext: R.extension(*ext), zip(minpolys, names), self)

            # 2. Otherwise, try to return a polynomial ring

            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            return PolynomialRing(self, elts)
示例#6
0
    def __init__(self,
                 base_ring,
                 num_gens,
                 name_list,
                 order='negdeglex',
                 default_prec=10,
                 sparse=False):
        """
        Initializes a multivariate power series ring.  See PowerSeriesRing
        for complete documentation.

        INPUT

            - ``base_ring`` - a commutative ring

            - ``num_gens`` - number of generators
        
            - ``name_list`` - List of indeterminate names or a single name.
                If a single name is given, indeterminates will be this name
                followed by a number from 0 to num_gens - 1.  If a list is
                given, these will be the indeterminate names and the length
                of the list must be equal to num_gens.

            - ``order`` - ordering of variables; default is
              negative degree lexicographic
            
            - ``default_prec`` - The default total-degree precision for
              elements.  The default value of default_prec is 10.
            
            - ``sparse`` - whether or not power series are sparse

        EXAMPLES::

                sage: R.<t,u,v> = PowerSeriesRing(QQ)
                sage: g = 1 + v + 3*u*t^2 - 2*v^2*t^2
                sage: g = g.add_bigoh(5); g
                1 + v + 3*t^2*u - 2*t^2*v^2 + O(t, u, v)^5
                sage: g in R
                True
        """
        order = TermOrder(order, num_gens)
        self._term_order = order
        if not base_ring.is_commutative():
            raise TypeError("Base ring must be a commutative ring.")
        n = int(num_gens)
        if n < 0:
            raise ValueError(
                "Multivariate Polynomial Rings must have more than 0 variables."
            )
        self._ngens = n
        self._has_singular = False  #cannot convert to Singular by default
        ParentWithGens.__init__(self, base_ring, name_list)
        Nonexact.__init__(self, default_prec)

        # underlying polynomial ring in which to represent elements
        self._poly_ring_ = PolynomialRing(base_ring,
                                          self.variable_names(),
                                          sparse=sparse,
                                          order=order)
        # because sometimes PowerSeriesRing_generic calls self.__poly_ring
        self._PowerSeriesRing_generic__poly_ring = self._poly_ring()

        # background univariate power series ring
        self._bg_power_series_ring = PowerSeriesRing(self._poly_ring_,
                                                     'Tbg',
                                                     sparse=sparse,
                                                     default_prec=default_prec)
        self._bg_indeterminate = self._bg_power_series_ring.gen()

        ## use the following in PowerSeriesRing_generic.__call__
        self._PowerSeriesRing_generic__power_series_class = MPowerSeries

        self._is_sparse = sparse
        self._params = (base_ring, num_gens, name_list, order, default_prec,
                        sparse)
        self._populate_coercion_lists_()
示例#7
0
    def __init__(self, base_ring, num_gens, name_list,
                 order='negdeglex', default_prec=10, sparse=False):
        """
        Initializes a multivariate power series ring.  See PowerSeriesRing
        for complete documentation.

        INPUT:

        - ``base_ring`` -- a commutative ring

        - ``num_gens`` -- number of generators

        - ``name_list`` -- List of indeterminate names or a single name.
            If a single name is given, indeterminates will be this name
            followed by a number from 0 to num_gens - 1.  If a list is
            given, these will be the indeterminate names and the length
            of the list must be equal to num_gens.

        - ``order`` -- ordering of variables; default is
          negative degree lexicographic

        - ``default_prec`` -- The default total-degree precision for
          elements.  The default value of default_prec is 10.

        - ``sparse`` -- whether or not the power series are sparse.
          The underlying polynomial ring is always sparse.

        EXAMPLES::

            sage: R.<t,u,v> = PowerSeriesRing(QQ)
            sage: g = 1 + v + 3*u*t^2 - 2*v^2*t^2
            sage: g = g.add_bigoh(5); g
            1 + v + 3*t^2*u - 2*t^2*v^2 + O(t, u, v)^5
            sage: g in R
            True

        TESTS:

        By :trac:`14084`, the multi-variate power series ring belongs to the
        category of integral domains, if the base ring does::

            sage: P = ZZ[['x','y']]
            sage: P.category()
            Category of integral domains
            sage: TestSuite(P).run()

        Otherwise, it belongs to the category of commutative rings::

            sage: P = Integers(15)[['x','y']]
            sage: P.category()
            Category of commutative rings
            sage: TestSuite(P).run()

        """
        self._term_order = order
        if not base_ring.is_commutative():
            raise TypeError("Base ring must be a commutative ring.")
        n = int(num_gens)
        if n < 0:
            raise ValueError("Multivariate Polynomial Rings must have more than 0 variables.")
        self._ngens = n
        self._has_singular = False #cannot convert to Singular by default
        # Multivariate power series rings inherit from power series rings. But
        # apparently we can not call their initialisation. Instead, initialise
        # CommutativeRing and Nonexact:
        CommutativeRing.__init__(self, base_ring, name_list, category =
                                 _IntegralDomains if base_ring in
                                 _IntegralDomains else _CommutativeRings)
        Nonexact.__init__(self, default_prec)

        # underlying polynomial ring in which to represent elements
        self._poly_ring_ = PolynomialRing(base_ring, self.variable_names(), order=order)
        # because sometimes PowerSeriesRing_generic calls self.__poly_ring
        self._PowerSeriesRing_generic__poly_ring = self._poly_ring()

        # background univariate power series ring
        self._bg_power_series_ring = PowerSeriesRing(self._poly_ring_, 'Tbg', sparse=sparse, default_prec=default_prec)
        self._bg_indeterminate = self._bg_power_series_ring.gen()

        self._is_sparse = sparse
        self._params = (base_ring, num_gens, name_list,
                         order, default_prec, sparse)
        self._populate_coercion_lists_()
示例#8
0
def compute_wp_fast(k, A, B, m):
    r"""
    Computes the Weierstrass function of an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. It does this with as fast as polynomial of degree `m` can be multiplied together in the base ring, i.e. `O(M(n))` in the notation of [BMSS].

    Let `p` be the characteristic of the underlying field: Then we must have either `p=0`, or `p > m + 3`.

    INPUT:

     - ``k`` - the base field of the curve
     - ``A`` - and
     - ``B`` - as the coeffients of the short Weierstrass model `y^2 = x^3 +Ax +B`, and
     - ``m`` - the precision to which the function is computed to.

    OUTPUT:

    the Weierstrass `\wp` function as a Laurent series to precision `m`.

    ALGORITHM:

    This function uses the algorithm described in section 3.3 of
    [BMSS].

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_fast
        sage: compute_wp_fast(QQ, 1, 8, 7)
        z^-2 - 1/5*z^2 - 8/7*z^4 + 1/75*z^6 + O(z^7)

        sage: k = GF(37)
        sage: compute_wp_fast(k, k(1), k(8), 5)
        z^-2 + 22*z^2 + 20*z^4 + O(z^5)

    """
    R = PowerSeriesRing(k, 'z', default_prec=m + 5)
    z = R.gen()
    s = 2
    f1 = z.add_bigoh(m + 3)
    n = 2 * m + 4

    # solve the nonlinear differential equation
    while (s < n):
        f1pr = f1.derivative()
        next_s = 2 * s - 1

        a = 2 * f1pr
        b = -(6 * B * (f1**5) + 4 * A * (f1**3))
        c = B * (f1**6) + A * f1**4 + 1 - (f1pr**2)

        # we should really be computing only mod z^next_s here.
        # but we loose only a factor 2
        f2 = solve_linear_differential_system(a, b, c, 0)
        # sometimes we get to 0 quicker than s reaches n
        if f2 == 0:
            break
        f1 = f1 + f2
        s = next_s

    R = f1
    Q = R**2
    pe = 1 / Q

    return pe
示例#9
0
def e_phipsi(phi,
             psi,
             k,
             t=1,
             prec=5,
             mat=Matrix([[1, 0], [0, 1]]),
             base_ring=None):
    r"""
    Computes the Eisenstein series attached to the characters psi, phi as
    defined on p129 of Diamond--Shurman hit by mat \in \SL_2(\Z)
    INPUT:
     - psi, a primitive Dirichlet character
     - phi, a primitive Dirichlet character
     - k, int -- the weight
     - t, int -- the shift
     - prec, the desired absolute precision, can be fractional. The expansion will be up to O(q_w^(floor(w*prec))), where w is the width of the cusp.
     - mat, a matrix - typically taking $i\infty$ to some other cusp
     - base_ring, a ring - the ring in which the modular forms are defined
    OUTPUT:
     - an instance of QExpansion
    """
    chi2 = phi
    chi1 = psi
    try:
        assert (QQ(chi1(-1)) * QQ(chi2(-1)) == (-1)**k)
    except AssertionError:
        print("Parity of characters must match parity of weight")
        return None
    N1 = chi1.level()
    N2 = chi2.level()
    N = t * N1 * N2
    mat2, Tn = find_correct_matrix(mat, N)
    #By construction gamma = mat2 * Tn * mat**(-1) is in Gamma0(N) so if E is our Eisenstein series we can evaluate E|mat = chi(gamma) * E|mat2*Tn.
    #Since E|mat2 has a Fourier expansion in qN, the matrix Tn acts as a a twist. The value c_gamma = chi(gamma) is calculated below.
    #The point of swapping mat with mat2 is that mat2 satisfies C|N, C>0, (A,N)=1 and N|B and our formulas for the coefficients require this condition.
    A, B, C, D = mat2.list()
    gamma = mat2 * Tn * mat**(-1)
    if base_ring == None:
        Nbig = lcm(N, euler_phi(N1 * N2))
        base_ring = CyclotomicField(Nbig, 'zeta' + str(Nbig))
        zetaNbig = base_ring.gen()
        zetaN = zetaNbig**(Nbig / N)
    else:
        zetaN = base_ring.zeta(N)
    g = gcd(t, C)
    g1 = gcd(N1 * g, C)
    g2 = gcd(N2 * g, C)
    #Resulting Eisenstein series will have Fourier expansion in q_N**(g1g2)=q_(N/gcd(N,g1g2))**(g1g2/gcd(N,g1g2))
    Q = PowerSeriesRing(base_ring, 'q' + str(N / gcd(N, g1 * g2)))
    qN = Q.gen()
    zeta_Cg = zetaN**(N / (C / g))
    zeta_tmp = zeta_Cg**(inverse_mod(-A * ZZ(t / g), C / g))
    #Calculating a few values that will be used repeatedly
    chi1bar_vals = [base_ring(chi1.bar()(i)) for i in range(N1)]
    cp_list1 = [i for i in range(N1) if gcd(i, N1) == 1]
    chi2bar_vals = [base_ring(chi2.bar()(i)) for i in range(N2)]
    cp_list2 = [i for i in range(N2) if gcd(i, N2) == 1]

    #Computation of the Fourier coefficients
    ser = O(qN**floor(prec * N / gcd(N, g1 * g2)))
    for n in range(1, ceil(prec * N / QQ(g1 * g2)) + 1):
        f = 0
        for m in divisors(n) + list(map(lambda x: -x, divisors(n))):
            a = 0
            for r1 in cp_list1:
                b = 0
                if ((C / g1) * r1 - QQ(n) / QQ(m)) % ((N1 * g) / g1) == 0:
                    for r2 in cp_list2:
                        if ((C / g2) * r2 - m) % ((N2 * g) / g2) == 0:
                            b += chi2bar_vals[r2] * zeta_tmp**(
                                (n / m - (C / g1) * r1) / ((N1 * g) / g1) *
                                (m - (C / g2) * r2) / ((N2 * g) / g2))
                    a += chi1bar_vals[r1] * b
            a *= sign(m) * m**(k - 1)
            f += a
        f *= zetaN**(inverse_mod(A, N) * (g1 * g2) / C * n)
        #The additional factor zetaN**(n*Tn[0][1]) comes from the twist by Tn
        ser += zetaN**(n * g1 * g2 * Tn[0][1]) * f * qN**(n * (
            (g1 * g2) / gcd(N, g1 * g2)))

    #zk(chi1, chi2, c)
    gauss1 = base_ring(gauss_sum_corr(chi1.bar()))
    gauss2 = base_ring(gauss_sum_corr(chi2.bar()))
    zk = 2 * (N2 * t / QQ(g2))**(k - 1) * (t / g) * gauss1 * gauss2
    #The following is a temporary fix for a bug in sage
    if base_ring == CC:
        G = DirichletGroup(N1 * N2, CC)
        G[0]  #Otherwise chi1.bar().extend(N1*N2).base_extend(CC) or chi2.bar().extend(N1*N2).base_extend(CC) will produce an error
    #Constant term
    #c_gamma comes from replacing mat with mat2.
    c_gamma = chi1bar_vals[gamma[1][1] % N1] * chi2bar_vals[gamma[1][1] % N2]
    if N1.divides(C) and ZZ(C / N1).divides(t) and gcd(t / (C / N1), N1) == 1:
        ser += (-1)**(k - 1) * gauss2 / QQ(
            N2 * (g2 / g)**(k - 1)) * chi1bar_vals[(-A * t / g) % N1] * Sk(
                chi1.bar().extend(N1 * N2).base_extend(base_ring) *
                chi2.extend(N1 * N2).base_extend(base_ring), k)
    elif k == 1 and N2.divides(C) and ZZ(C / N2).divides(t) and gcd(
            t / (C / N2), N2) == 1:
        ser += gauss1 / QQ(N1) * chi2bar_vals[(-A * t / g) % N2] * Sk(
            chi1.extend(N1 * N2).base_extend(base_ring) *
            chi2.bar().extend(N1 * N2).base_extend(base_ring), k)
    return QExpansion(
        N, k,
        2 / zk * c_gamma * ser + O(qN**floor(prec * N / gcd(N, g1 * g2))),
        N / gcd(N, g1 * g2))
示例#10
0
    def __init__(self, precision):
        self.__precision = precision

        self.__power_series_ring_ZZ = PowerSeriesRing(ZZ, 'q')
        self.__power_series_ring = PowerSeriesRing(QQ, 'q')
示例#11
0
    def _theta_factors(self, p=None):
        r"""
        Return the factor `W^\# (\theta_0, .., \theta_{2m - 1})^{\mathrm{T}}` as a list.
        The `q`-expansion is shifted by `-(m + 1)(2*m + 1) / 24` which will be compensated
        for by the eta factor.
        """
        try:
            if p is None:
                return self.__theta_factors
            else:
                P = PowerSeriesRing(GF(p), 'q')

                return [list(map(P, facs)) for facs in self.__theta_factors]

        except AttributeError:
            qexp_prec = self._qexp_precision()
            if p is None:
                PS = self.integral_power_series_ring()
            else:
                PS = PowerSeriesRing(GF(p), 'q')
            m = self.__precision.jacobi_index()

            twom = 2 * m
            frmsq = twom**2

            thetas = dict(
                ((i, j), dict()) for i in range(m + 1) for j in range(m + 1))

            ## We want to calculate \hat \theta_{j,l} = sum_r (2 m r + j)**2l q**(m r**2 + j r).

            for r in range(0, isqrt((qexp_prec - 1 + m) // m) + 2):
                for j in [0, m]:
                    fact = (twom * r + j)**2
                    coeff = 2
                    for l in range(0, m + 1):
                        thetas[(j, l)][m * r**2 + r * j] = coeff
                        coeff = coeff * fact
            thetas[(0, 0)][0] = 1

            for r in range(0, isqrt((qexp_prec - 1 + m) // m) + 2):
                for j in range(1, m):
                    fact_p = (twom * r + j)**2
                    fact_m = (twom * r - j)**2
                    coeff_p = 2
                    coeff_m = 2

                    for l in range(0, m + 1):
                        thetas[(j, l)][m * r**2 + r * j] = coeff_p
                        thetas[(j, l)][m * r**2 - r * j] = coeff_m
                        coeff_p = coeff_p * fact_p
                        coeff_m = coeff_m * fact_m

            thetas = dict(
                (k, PS(th).add_bigoh(qexp_prec)) for (k, th) in thetas.items())

            W = matrix(
                PS, m + 1,
                [thetas[(j, l)] for j in range(m + 1) for l in range(m + 1)])

            ## Since the adjoint of matrices with entries in a general ring
            ## is extremely slow for matrices of small size, we hard code the
            ## the cases `m = 2` and `m = 3`.  The expressions are obtained by
            ## computing the adjoint of a matrix with entries `w_{i,j}` in a
            ## polynomial algebra.
            if m == 2 and qexp_prec > 10**5:
                adj00 = W[1, 1] * W[2, 2] - W[2, 1] * W[1, 2]
                adj01 = -W[1, 0] * W[2, 2] + W[2, 0] * W[1, 2]
                adj02 = W[1, 0] * W[2, 1] - W[2, 0] * W[1, 1]
                adj10 = -W[0, 1] * W[2, 2] + W[2, 1] * W[0, 2]
                adj11 = W[0, 0] * W[2, 2] - W[2, 0] * W[0, 2]
                adj12 = -W[0, 0] * W[2, 1] + W[2, 0] * W[0, 1]
                adj20 = W[0, 1] * W[1, 2] - W[1, 1] * W[0, 2]
                adj21 = -W[0, 0] * W[1, 2] + W[1, 0] * W[0, 2]
                adj22 = W[0, 0] * W[1, 1] - W[1, 0] * W[0, 1]

                Wadj = matrix(PS,
                              [[adj00, adj01, adj02], [adj10, adj11, adj12],
                               [adj20, adj21, adj22]])

            elif m == 3 and qexp_prec > 10**5:
                adj00 = -W[0, 2] * W[1, 1] * W[2, 0] + W[0, 1] * W[1, 2] * W[
                    2, 0] + W[0, 2] * W[1, 0] * W[2, 1] - W[0, 0] * W[
                        1, 2] * W[2, 1] - W[0, 1] * W[1, 0] * W[2, 2] + W[
                            0, 0] * W[1, 1] * W[2, 2]
                adj01 = -W[0, 3] * W[1, 1] * W[2, 0] + W[0, 1] * W[1, 3] * W[
                    2, 0] + W[0, 3] * W[1, 0] * W[2, 1] - W[0, 0] * W[
                        1, 3] * W[2, 1] - W[0, 1] * W[1, 0] * W[2, 3] + W[
                            0, 0] * W[1, 1] * W[2, 3]
                adj02 = -W[0, 3] * W[1, 2] * W[2, 0] + W[0, 2] * W[1, 3] * W[
                    2, 0] + W[0, 3] * W[1, 0] * W[2, 2] - W[0, 0] * W[
                        1, 3] * W[2, 2] - W[0, 2] * W[1, 0] * W[2, 3] + W[
                            0, 0] * W[1, 2] * W[2, 3]
                adj03 = -W[0, 3] * W[1, 2] * W[2, 1] + W[0, 2] * W[1, 3] * W[
                    2, 1] + W[0, 3] * W[1, 1] * W[2, 2] - W[0, 1] * W[
                        1, 3] * W[2, 2] - W[0, 2] * W[1, 1] * W[2, 3] + W[
                            0, 1] * W[1, 2] * W[2, 3]

                adj10 = -W[0, 2] * W[1, 1] * W[3, 0] + W[0, 1] * W[1, 2] * W[
                    3, 0] + W[0, 2] * W[1, 0] * W[3, 1] - W[0, 0] * W[
                        1, 2] * W[3, 1] - W[0, 1] * W[1, 0] * W[3, 2] + W[
                            0, 0] * W[1, 1] * W[3, 2]
                adj11 = -W[0, 3] * W[1, 1] * W[3, 0] + W[0, 1] * W[1, 3] * W[
                    3, 0] + W[0, 3] * W[1, 0] * W[3, 1] - W[0, 0] * W[
                        1, 3] * W[3, 1] - W[0, 1] * W[1, 0] * W[3, 3] + W[
                            0, 0] * W[1, 1] * W[3, 3]
                adj12 = -W[0, 3] * W[1, 2] * W[3, 0] + W[0, 2] * W[1, 3] * W[
                    3, 0] + W[0, 3] * W[1, 0] * W[3, 2] - W[0, 0] * W[
                        1, 3] * W[3, 2] - W[0, 2] * W[1, 0] * W[3, 3] + W[
                            0, 0] * W[1, 2] * W[3, 3]
                adj13 = -W[0, 3] * W[1, 2] * W[3, 1] + W[0, 2] * W[1, 3] * W[
                    3, 1] + W[0, 3] * W[1, 1] * W[3, 2] - W[0, 1] * W[
                        1, 3] * W[3, 2] - W[0, 2] * W[1, 1] * W[3, 3] + W[
                            0, 1] * W[1, 2] * W[3, 3]

                adj20 = -W[0, 2] * W[2, 1] * W[3, 0] + W[0, 1] * W[2, 2] * W[
                    3, 0] + W[0, 2] * W[2, 0] * W[3, 1] - W[0, 0] * W[
                        2, 2] * W[3, 1] - W[0, 1] * W[2, 0] * W[3, 2] + W[
                            0, 0] * W[2, 1] * W[3, 2]
                adj21 = -W[0, 3] * W[2, 1] * W[3, 0] + W[0, 1] * W[2, 3] * W[
                    3, 0] + W[0, 3] * W[2, 0] * W[3, 1] - W[0, 0] * W[
                        2, 3] * W[3, 1] - W[0, 1] * W[2, 0] * W[3, 3] + W[
                            0, 0] * W[2, 1] * W[3, 3]
                adj22 = -W[0, 3] * W[2, 2] * W[3, 0] + W[0, 2] * W[2, 3] * W[
                    3, 0] + W[0, 3] * W[2, 0] * W[3, 2] - W[0, 0] * W[
                        2, 3] * W[3, 2] - W[0, 2] * W[2, 0] * W[3, 3] + W[
                            0, 0] * W[2, 2] * W[3, 3]
                adj23 = -W[0, 3] * W[2, 2] * W[3, 1] + W[0, 2] * W[2, 3] * W[
                    3, 1] + W[0, 3] * W[2, 1] * W[3, 2] - W[0, 1] * W[
                        2, 3] * W[3, 2] - W[0, 2] * W[2, 1] * W[3, 3] + W[
                            0, 1] * W[2, 2] * W[3, 3]

                adj30 = -W[1, 2] * W[2, 1] * W[3, 0] + W[1, 1] * W[2, 2] * W[
                    3, 0] + W[1, 2] * W[2, 0] * W[3, 1] - W[1, 0] * W[
                        2, 2] * W[3, 1] - W[1, 1] * W[2, 0] * W[3, 2] + W[
                            1, 0] * W[2, 1] * W[3, 2]
                adj31 = -W[1, 3] * W[2, 1] * W[3, 0] + W[1, 1] * W[2, 3] * W[
                    3, 0] + W[1, 3] * W[2, 0] * W[3, 1] - W[1, 0] * W[
                        2, 3] * W[3, 1] - W[1, 1] * W[2, 0] * W[3, 3] + W[
                            1, 0] * W[2, 1] * W[3, 3]
                adj32 = -W[1, 3] * W[2, 2] * W[3, 0] + W[1, 2] * W[2, 3] * W[
                    3, 0] + W[1, 3] * W[2, 0] * W[3, 2] - W[1, 0] * W[
                        2, 3] * W[3, 2] - W[1, 2] * W[2, 0] * W[3, 3] + W[
                            1, 0] * W[2, 2] * W[3, 3]
                adj33 = -W[1, 3] * W[2, 2] * W[3, 1] + W[1, 2] * W[2, 3] * W[
                    3, 1] + W[1, 3] * W[2, 1] * W[3, 2] - W[1, 1] * W[
                        2, 3] * W[3, 2] - W[1, 2] * W[2, 1] * W[3, 3] + W[
                            1, 1] * W[2, 2] * W[3, 3]

                Wadj = matrix(PS, [[adj00, adj01, adj02, adj03],
                                   [adj10, adj11, adj12, adj13],
                                   [adj20, adj21, adj22, adj23],
                                   [adj30, adj31, adj32, adj33]])
            else:
                Wadj = W.adjoint()

            theta_factors = [[Wadj[i, r] for i in range(m + 1)]
                             for r in range(m + 1)]

            if p is None:
                self.__theta_factors = theta_factors

            return theta_factors
示例#12
0
        def q_dimension(self, q=None, prec=None, use_product=False):
            r"""
            Return the `q`-dimension of ``self``.

            Let `B(\lambda)` denote a highest weight crystal. Recall that
            the degree of the `\mu`-weight space of `B(\lambda)` (under
            the principal gradation) is equal to
            `\langle \rho^{\vee}, \lambda - \mu \rangle` where
            `\langle \rho^{\vee}, \alpha_i \rangle = 1` for all `i \in I`
            (in particular, take `\rho^{\vee} = \sum_{i \in I} h_i`).

            The `q`-dimension of a highest weight crystal `B(\lambda)` is
            defined as

            .. MATH::

                \dim_q B(\lambda) := \sum_{j \geq 0} \dim(B_j) q^j,

            where `B_j` denotes the degree `j` portion of `B(\lambda)`. This
            can be expressed as the product

            .. MATH::

                \dim_q B(\lambda) = \prod_{\alpha^{\vee} \in \Delta_+^{\vee}}
                \left( \frac{1 - q^{\langle \lambda + \rho, \alpha^{\vee}
                \rangle}}{1 - q^{\langle \rho, \alpha^{\vee} \rangle}}
                \right)^{\mathrm{mult}\, \alpha},

            where `\Delta_+^{\vee}` denotes the set of positive coroots.
            Taking the limit as `q \to 1` gives the dimension of `B(\lambda)`.
            For more information, see [Kac]_ Section 10.10.

            INPUT:

            - ``q`` -- the (generic) parameter `q`

            - ``prec`` -- (default: ``None``) The precision of the power
              series ring to use if the crystal is not known to be finite
              (i.e. the number of terms returned).
              If ``None``, then the result is returned as a lazy power series.

            - ``use_product`` -- (default: ``False``) if we have a finite
              crystal and ``True``, use the product formula

            EXAMPLES::

                sage: C = crystals.Tableaux(['A',2], shape=[2,1])
                sage: qdim = C.q_dimension(); qdim
                q^4 + 2*q^3 + 2*q^2 + 2*q + 1
                sage: qdim(1)
                8
                sage: len(C) == qdim(1)
                True
                sage: C.q_dimension(use_product=True) == qdim
                True
                sage: C.q_dimension(prec=20)
                q^4 + 2*q^3 + 2*q^2 + 2*q + 1
                sage: C.q_dimension(prec=2)
                2*q + 1

                sage: R.<t> = QQ[]
                sage: C.q_dimension(q=t^2)
                t^8 + 2*t^6 + 2*t^4 + 2*t^2 + 1

                sage: C = crystals.Tableaux(['A',2], shape=[5,2])
                sage: C.q_dimension()
                q^10 + 2*q^9 + 4*q^8 + 5*q^7 + 6*q^6 + 6*q^5
                 + 6*q^4 + 5*q^3 + 4*q^2 + 2*q + 1

                sage: C = crystals.Tableaux(['B',2], shape=[2,1])
                sage: qdim = C.q_dimension(); qdim
                q^10 + 2*q^9 + 3*q^8 + 4*q^7 + 5*q^6 + 5*q^5
                 + 5*q^4 + 4*q^3 + 3*q^2 + 2*q + 1
                sage: qdim == C.q_dimension(use_product=True)
                True

                sage: C = crystals.Tableaux(['D',4], shape=[2,1])
                sage: C.q_dimension()
                q^16 + 2*q^15 + 4*q^14 + 7*q^13 + 10*q^12 + 13*q^11
                 + 16*q^10 + 18*q^9 + 18*q^8 + 18*q^7 + 16*q^6 + 13*q^5
                 + 10*q^4 + 7*q^3 + 4*q^2 + 2*q + 1

            We check with a finite tensor product::

                sage: TP = crystals.TensorProduct(C, C)
                sage: TP.cardinality()
                25600
                sage: qdim = TP.q_dimension(use_product=True); qdim # long time
                q^32 + 2*q^31 + 8*q^30 + 15*q^29 + 34*q^28 + 63*q^27 + 110*q^26
                 + 175*q^25 + 276*q^24 + 389*q^23 + 550*q^22 + 725*q^21
                 + 930*q^20 + 1131*q^19 + 1362*q^18 + 1548*q^17 + 1736*q^16
                 + 1858*q^15 + 1947*q^14 + 1944*q^13 + 1918*q^12 + 1777*q^11
                 + 1628*q^10 + 1407*q^9 + 1186*q^8 + 928*q^7 + 720*q^6
                 + 498*q^5 + 342*q^4 + 201*q^3 + 117*q^2 + 48*q + 26
                sage: qdim(1) # long time
                25600
                sage: TP.q_dimension() == qdim # long time
                True

            The `q`-dimensions of infinite crystals are returned
            as formal power series::

                sage: C = crystals.LSPaths(['A',2,1], [1,0,0])
                sage: C.q_dimension(prec=5)
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + O(q^5)
                sage: C.q_dimension(prec=10)
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6
                 + 9*q^7 + 13*q^8 + 16*q^9 + O(q^10)
                sage: qdim = C.q_dimension(); qdim
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6
                 + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + O(x^11)
                sage: qdim.compute_coefficients(15)
                sage: qdim
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6
                 + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + 27*q^11
                 + 36*q^12 + 44*q^13 + 57*q^14 + 70*q^15 + O(x^16)

            REFERENCES:

            .. [Kac] Victor G. Kac. *Infinite-dimensional Lie Algebras*.
               Third edition. Cambridge University Press, Cambridge, 1990.
            """
            from sage.rings.all import ZZ
            WLR = self.weight_lattice_realization()
            I = self.index_set()
            mg = self.highest_weight_vectors()
            max_deg = float('inf') if prec is None else prec - 1

            def iter_by_deg(gens):
                next = set(gens)
                deg = -1
                while next and deg < max_deg:
                    deg += 1
                    yield len(next)
                    todo = next
                    next = set([])
                    while todo:
                        x = todo.pop()
                        for i in I:
                            y = x.f(i)
                            if y is not None:
                                next.add(y)
                # def iter_by_deg

            from sage.categories.finite_crystals import FiniteCrystals
            if self in FiniteCrystals():
                if q is None:
                    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
                    q = PolynomialRing(ZZ, 'q').gen(0)

                if use_product:
                    # Since we are in the classical case, all roots occur with multiplicity 1
                    pos_coroots = map(lambda x: x.associated_coroot(),
                                      WLR.positive_roots())
                    rho = WLR.rho()
                    P = q.parent()
                    ret = P.zero()
                    for v in self.highest_weight_vectors():
                        hw = v.weight()
                        ret += P.prod((1 - q**(rho + hw).scalar(ac)) /
                                      (1 - q**rho.scalar(ac))
                                      for ac in pos_coroots)
                    # We do a cast since the result would otherwise live in the fraction field
                    return P(ret)

            elif prec is None:
                # If we're here, we may not be a finite crystal.
                # In fact, we're probably infinite.
                from sage.combinat.species.series import LazyPowerSeriesRing
                if q is None:
                    P = LazyPowerSeriesRing(ZZ, names='q')
                else:
                    P = q.parent()
                if not isinstance(P, LazyPowerSeriesRing):
                    raise TypeError(
                        "the parent of q must be a lazy power series ring")
                ret = P(iter_by_deg(mg))
                ret.compute_coefficients(10)
                return ret

            from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic
            if q is None:
                q = PowerSeriesRing(ZZ, 'q', default_prec=prec).gen(0)
            P = q.parent()
            ret = P.sum(c * q**deg for deg, c in enumerate(iter_by_deg(mg)))
            if ret.degree() == max_deg and isinstance(P,
                                                      PowerSeriesRing_generic):
                ret = P(ret, prec)
            return ret
def product_space(chi, k, weights=False, base_ring=None, verbose=False):
    r"""
    Computes all eisenstein series, and products of pairs of eisenstein series
    of lower weight, lying in the space of modular forms of weight $k$ and
    nebentypus $\chi$.
    INPUT:
     - chi - Dirichlet character, the nebentypus of the target space
     - k - an integer, the weight of the target space
    OUTPUT:
     - a matrix of coefficients of q-expansions, which are the products of
     Eisenstein series in M_k(chi).

    WARNING: It is only for principal chi that we know that the resulting
    space is the whole space of modular forms.
    """

    if weights == False:
        weights = srange(1, k / 2 + 1)
    weight_dict = {}
    weight_dict[-1] = [w for w in weights if w % 2]  # Odd weights
    weight_dict[1] = [w for w in weights if not w % 2]  # Even weights

    try:
        N = chi.modulus()
    except AttributeError:
        if chi.parent() == ZZ:
            N = chi
            chi = DirichletGroup(N)[0]

    Id = DirichletGroup(1)[0]
    if chi(-1) != (-1)**k:
        raise ValueError('chi(-1)!=(-1)^k')
    sturm = ModularForms(N, k).sturm_bound() + 1
    if N > 1:
        target_dim = dimension_modular_forms(chi, k)
    else:
        target_dim = dimension_modular_forms(1, k)
    D = DirichletGroup(N)
    # product_space should ideally be called over number fields. Over complex
    # numbers the exact linear algebra solutions might not exist.
    if base_ring == None:
        base_ring = CyclotomicField(euler_phi(N))

    Q = PowerSeriesRing(base_ring, 'q')
    q = Q.gen()

    d = len(D)
    prim_chars = [phi.primitive_character() for phi in D]
    divs = divisors(N)

    products = Matrix(base_ring, [])
    indexlist = []
    rank = 0
    if verbose:
        print(D)
        print('Sturm bound', sturm)
        #TODO: target_dim needs refinment in the case of weight 2.
        print('Target dimension', target_dim)
    for i in srange(0, d):  # First character
        phi = prim_chars[i]
        M1 = phi.conductor()
        for j in srange(0, d):  # Second character
            psi = prim_chars[j]
            M2 = psi.conductor()
            if not M1 * M2 in divs:
                continue
            parity = psi(-1) * phi(-1)
            for t1 in divs:
                if not M1 * M2 * t1 in divs:
                    continue
                #TODO: THE NEXT CONDITION NEEDS TO BE CORRECTED. THIS IS JUST A TEST
                if phi.bar() == psi and not (
                        k == 2):  #and i==0 and j==0 and t1==1):
                    E = eisenstein_series_at_inf(phi, psi, k, sturm, t1,
                                                 base_ring).padded_list()
                    try:
                        products.T.solve_right(vector(base_ring, E))
                    except ValueError:
                        products = Matrix(products.rows() + [E])
                        indexlist.append([k, i, j, t1])
                        rank += 1
                        if verbose:
                            print('Added ', [k, i, j, t1])
                            print('Rank is now', rank)
                        if rank == target_dim:
                            return products, indexlist
                for t in divs:
                    if not M1 * M2 * t1 * t in divs:
                        continue
                    for t2 in divs:
                        if not M1 * M2 * t1 * t2 * t in divs:
                            continue
                        for l in weight_dict[parity]:
                            if l == 1 and phi.is_odd():
                                continue
                            if i == 0 and j == 0 and (l == 2 or l == k - 2):
                                continue
                            #TODO: THE NEXT CONDITION NEEDS TO BE REMOVED. THIS IS JUST A TEST
                            if l == 2 or l == k - 2:
                                continue
                            E1 = eisenstein_series_at_inf(
                                phi, psi, l, sturm, t1 * t, base_ring)
                            E2 = eisenstein_series_at_inf(
                                phi**(-1), psi**(-1), k - l, sturm, t2 * t,
                                base_ring)
                            #If chi is non-principal this needs to be changed to be something like chi*phi^(-1) instead of phi^(-1)
                            E = (E1 * E2 + O(q**sturm)).padded_list()
                            try:
                                products.T.solve_right(vector(base_ring, E))
                            except ValueError:
                                products = Matrix(products.rows() + [E])
                                indexlist.append([l, k - l, i, j, t1, t2, t])
                                rank += 1
                                if verbose:
                                    print('Added ',
                                          [l, k - l, i, j, t1, t2, t])
                                    print('Rank', rank)
                                if rank == target_dim:
                                    return products, indexlist
    return products, indexlist
示例#14
0
def _dimension_Gamma_2(wt_range, j, group='Gamma(2)'):
    """
    Return the dict
    {(k-> partition ->  [ d(k), e(k), c(k)] for k in wt_range]},
    where d(k), e(k), c(k) are the dimensions
    of the $p$-canonical part of $M_{k,j}( \Gamma(2))$ and its subspaces of
    Non-cusp forms and Cusp forms.
    """

    partitions = [
        u'6', u'51', u'42', u'411', u'33', u'321', u'311', u'222', u'2211',
        u'21111', u'111111'
    ]

    if is_odd(j):
        dct = dict(
            (k, dict((h, [0, 0, 0]) for h in partitions)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [0, 0, 0]
        partitions.insert(0, 'All')
        return partitions, dct

    if 'Sp4(Z)' == group and 2 == j and wt_range[0] < 4:
        wt_range1 = [k for k in wt_range if k < 4]
        wt_range2 = [k for k in wt_range if k >= 4]
        #        print wt_range1, wt_range2
        if wt_range2 != []:
            headers, dct = _dimension_Gamma_2(wt_range2, j, group)
        else:
            headers, dct = ['Total', 'Non cusp', 'Cusp'], {}
        for k in wt_range1:
            dct[k] = dict([(h, 0) for h in headers])
        return headers, dct

    if j >= 2 and wt_range[0] < 4:
        raise NotImplementedError(
            'Dimensions of \(M_{k,j}\) for \(k<4\) and even \(j\ge 2\) not implemented'
        )

    query = {'sym_power': str(j), 'group': 'Gamma(2)', 'space': 'total'}
    db_total = fetch(query)
    assert db_total, '%s: Data not available' % query
    query['space'] = 'cusp'
    db_cusp = fetch(query)
    assert db_cusp, '%s: Data not available' % query

    P = PowerSeriesRing(IntegerRing(),
                        default_prec=wt_range[-1] + 1,
                        names=('t', ))
    t = P.gen()
    total = dict()
    cusp = dict()
    for p in partitions:
        total[p] = eval(db_total[p])
        cusp[p] = eval(db_cusp[p])
    # total = dict( ( p, eval(db_total[p])) for p in partitions)
    # cusp = dict( ( p, eval(db_cusp[p])) for p in partitions)

    if 'Gamma(2)' == group:
        dct = dict(
            (k,
             dict((p, [total[p][k], total[p][k] - cusp[p][k], cusp[p][k]])
                  for p in partitions)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [
                sum(dct[k][p][j] for p in dct[k]) for j in range(3)
            ]

        partitions.insert(0, 'All')
        headers = partitions

    elif 'Gamma1(2)' == group:
        ps = {
            '3': ['6', '42', '222'],
            '21': ['51', '42', '321'],
            '111': ['411', '33']
        }

        dct = dict((k,
                    dict((p, [
                        sum(total[q][k] for q in ps[p]),
                        sum(total[q][k] - cusp[q][k] for q in ps[p]),
                        sum(cusp[q][k] for q in ps[p]),
                    ]) for p in ps)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [
                sum(dct[k][p][j] for p in dct[k]) for j in range(3)
            ]

        headers = ps.keys()
        headers.sort(reverse=True)
        headers.insert(0, 'All')

    elif 'Gamma0(2)' == group:
        headers = ['Total', 'Non cusp', 'Cusp']
        ps = ['6', '42', '222']
        dct = dict((k, {
            'Total': sum(total[p][k] for p in ps),
            'Non cusp': sum(total[p][k] - cusp[p][k] for p in ps),
            'Cusp': sum(cusp[p][k] for p in ps)
        }) for k in wt_range)

    elif 'Sp4(Z)' == group:
        headers = ['Total', 'Non cusp', 'Cusp']
        p = '6'
        dct = dict((k, {
            'Total': total[p][k],
            'Non cusp': total[p][k] - cusp[p][k],
            'Cusp': cusp[p][k]
        }) for k in wt_range)
    else:
        raise NotImplemetedError('Dimension for %s not implemented' % group)

    return headers, dct
示例#15
0
文件: rings.py 项目: yunboliu27/sage
        def __getitem__(self, arg):
            """
            Extend this ring by one or several elements to create a polynomial
            ring, a power series ring, or an algebraic extension.

            This is a convenience method intended primarily for interactive
            use.

            .. SEEALSO::

                :func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`,
                :func:`~sage.rings.power_series_ring.PowerSeriesRing`,
                :meth:`~sage.rings.ring.Ring.extension`,
                :meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`,
                :meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`,
                :meth:`sage.structure.parent.Parent.__getitem__`

            EXAMPLES:

            We create several polynomial rings::

                sage: ZZ['x']
                Univariate Polynomial Ring in x over Integer Ring
                sage: QQ['x']
                Univariate Polynomial Ring in x over Rational Field
                sage: GF(17)['abc']
                Univariate Polynomial Ring in abc over Finite Field of size 17
                sage: GF(17)['a,b,c']
                Multivariate Polynomial Ring in a, b, c over Finite Field of size 17
                sage: GF(17)['a']['b']
                Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17

            We can create skew polynomial rings::

                sage: k.<t> = GF(5^3)
                sage: Frob = k.frobenius_endomorphism()
                sage: k['x',Frob]
                Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5

            We can also create power series rings by using double brackets::

                sage: QQ[['t']]
                Power Series Ring in t over Rational Field
                sage: ZZ[['W']]
                Power Series Ring in W over Integer Ring

                sage: ZZ[['x,y,z']]
                Multivariate Power Series Ring in x, y, z over Integer Ring
                sage: ZZ[['x','T']]
                Multivariate Power Series Ring in x, T over Integer Ring

            Use :func:`~sage.rings.fraction_field.Frac` or
            :meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain
            the fields of rational functions and Laurent series::

                sage: Frac(QQ['t'])
                Fraction Field of Univariate Polynomial Ring in t over Rational Field
                sage: Frac(QQ[['t']])
                Laurent Series Ring in t over Rational Field
                sage: QQ[['t']].fraction_field()
                Laurent Series Ring in t over Rational Field

            Note that the same syntax can be used to create number fields::

                sage: QQ[I]
                Number Field in I with defining polynomial x^2 + 1
                sage: QQ[sqrt(2)]
                Number Field in sqrt2 with defining polynomial x^2 - 2
                sage: QQ[sqrt(2),sqrt(3)]
                Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field

            and orders in number fields::

                sage: ZZ[I]
                Order in Number Field in I with defining polynomial x^2 + 1
                sage: ZZ[sqrt(5)]
                Order in Number Field in sqrt5 with defining polynomial x^2 - 5
                sage: ZZ[sqrt(2)+sqrt(3)]
                Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1

            TESTS:

            A few corner cases::

                sage: QQ[()]
                Multivariate Polynomial Ring in no variables over Rational Field

                sage: QQ[[]]
                Traceback (most recent call last):
                ...
                TypeError: power series rings must have at least one variable

            Some flexibility is allowed when specifying variables::

                sage: QQ["x", SR.var('y'), polygen(CC, 'z')]
                Multivariate Polynomial Ring in x, y, z over Rational Field
                sage: QQ[["x", SR.var('y'), polygen(CC, 'z')]]
                Multivariate Power Series Ring in x, y, z over Rational Field

            but more baroque expressions do not work::

                sage: QQ['a,b','c']
                Traceback (most recent call last):
                ...
                ValueError: variable name 'a,b' is not alphanumeric
                sage: QQ[['a,b','c']]
                Traceback (most recent call last):
                ...
                ValueError: variable name 'a,b' is not alphanumeric

                sage: QQ[[['x']]]
                Traceback (most recent call last):
                ...
                TypeError: expected R[...] or R[[...]], not R[[[...]]]

            Extension towers are built as follows and use distinct generator names::

                sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)]
                sage: K
                Number Field in a with defining polynomial x^3 - 2 over its base field
                sage: K.base_field()
                Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field
                sage: K.base_field().base_field()
                Number Field in b with defining polynomial x^3 - 3

            """
            def normalize_arg(arg):
                if isinstance(arg, (tuple, list)):
                    # Allowing arbitrary iterables would create confusion, but we
                    # may want to support a few more.
                    return tuple(arg)
                elif isinstance(arg, str):
                    return tuple(arg.split(','))
                else:
                    return (arg, )

            # 1. If arg is a list, try to return a power series ring.

            if isinstance(arg, list):
                if arg == []:
                    raise TypeError(
                        "power series rings must have at least one variable")
                elif len(arg) == 1:
                    # R[["a,b"]], R[[(a,b)]]...
                    if isinstance(arg[0], list):
                        raise TypeError(
                            "expected R[...] or R[[...]], not R[[[...]]]")
                    elts = normalize_arg(arg[0])
                else:
                    elts = normalize_arg(arg)
                from sage.rings.power_series_ring import PowerSeriesRing
                return PowerSeriesRing(self, elts)

            if isinstance(arg, tuple):
                from sage.categories.morphism import Morphism
                if len(arg) == 2 and isinstance(arg[1], Morphism):
                    from sage.rings.polynomial.skew_polynomial_ring_constructor import SkewPolynomialRing
                    return SkewPolynomialRing(self, arg[1], names=arg[0])

            # 2. Otherwise, if all specified elements are algebraic, try to
            #    return an algebraic extension

            elts = normalize_arg(arg)

            try:
                minpolys = [a.minpoly() for a in elts]
            except (AttributeError, NotImplementedError, ValueError,
                    TypeError):
                minpolys = None

            if minpolys:
                # how to pass in names?
                # TODO: set up embeddings
                names = tuple(_gen_names(elts))
                try:
                    # Doing the extension all at once is best, if possible...
                    return self.extension(minpolys, names)
                except (TypeError, ValueError):
                    # ...but we can also construct it iteratively
                    return reduce(lambda R, ext: R.extension(*ext),
                                  zip(minpolys, names), self)

            # 2. Otherwise, try to return a polynomial ring

            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            return PolynomialRing(self, elts)
示例#16
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)
示例#17
0
def eisenstein_series_qexp(k, prec=10, K=QQ, var='q', normalization='linear'):
    r"""
    Return the `q`-expansion of the normalized weight `k` Eisenstein series on
    `{\rm SL}_2(\ZZ)` to precision prec in the ring `K`. Three normalizations
    are available, depending on the parameter ``normalization``; the default
    normalization is the one for which the linear coefficient is 1.

    INPUT:

    - ``k`` - an even positive integer

    - ``prec`` - (default: 10) a nonnegative integer

    - ``K`` - (default: `\QQ`) a ring

    - ``var`` - (default: ``'q'``) variable name to use for q-expansion

    - ``normalization`` - (default: ``'linear'``) normalization to use. If this
      is ``'linear'``, then the series will be normalized so that the linear
      term is 1. If it is ``'constant'``, the series will be normalized to have
      constant term 1. If it is ``'integral'``, then the series will be
      normalized to have integer coefficients and no common factor, and linear
      term that is positive. Note that ``'integral'`` will work over arbitrary
      base rings, while ``'linear'`` or ``'constant'`` will fail if the
      denominator (resp. numerator) of `B_k / 2k` is invertible.

    ALGORITHM:

        We know `E_k = \text{constant} + \sum_n \sigma_{k-1}(n) q^n`. So we
        compute all the `\sigma_{k-1}(n)` simultaneously, using the fact that
        `\sigma` is multiplicative.

    EXAMPLES::

        sage: eisenstein_series_qexp(2,5)
        -1/24 + q + 3*q^2 + 4*q^3 + 7*q^4 + O(q^5)
        sage: eisenstein_series_qexp(2,0)
        O(q^0)
        sage: eisenstein_series_qexp(2,5,GF(7))
        2 + q + 3*q^2 + 4*q^3 + O(q^5)
        sage: eisenstein_series_qexp(2,5,GF(7),var='T')
        2 + T + 3*T^2 + 4*T^3 + O(T^5)

    We illustrate the use of the ``normalization`` parameter::

        sage: eisenstein_series_qexp(12, 5, normalization='integral')
        691 + 65520*q + 134250480*q^2 + 11606736960*q^3 + 274945048560*q^4 + O(q^5)
        sage: eisenstein_series_qexp(12, 5, normalization='constant')
        1 + 65520/691*q + 134250480/691*q^2 + 11606736960/691*q^3 + 274945048560/691*q^4 + O(q^5)
        sage: eisenstein_series_qexp(12, 5, normalization='linear')
        691/65520 + q + 2049*q^2 + 177148*q^3 + 4196353*q^4 + O(q^5)
        sage: eisenstein_series_qexp(12, 50, K=GF(13), normalization="constant")
        1 + O(q^50)

    TESTS:

    Test that :trac:`5102` is fixed::

        sage: eisenstein_series_qexp(10, 30, GF(17))
        15 + q + 3*q^2 + 15*q^3 + 7*q^4 + 13*q^5 + 11*q^6 + 11*q^7 + 15*q^8 + 7*q^9 + 5*q^10 + 7*q^11 + 3*q^12 + 14*q^13 + 16*q^14 + 8*q^15 + 14*q^16 + q^17 + 4*q^18 + 3*q^19 + 6*q^20 + 12*q^21 + 4*q^22 + 12*q^23 + 4*q^24 + 4*q^25 + 8*q^26 + 14*q^27 + 9*q^28 + 6*q^29 + O(q^30)

    This shows that the bug reported at :trac:`8291` is fixed::

        sage: eisenstein_series_qexp(26, 10, GF(13))
        7 + q + 3*q^2 + 4*q^3 + 7*q^4 + 6*q^5 + 12*q^6 + 8*q^7 + 2*q^8 + O(q^10)

    We check that the function behaves properly over finite-characteristic base rings::

        sage: eisenstein_series_qexp(12, 5, K = Zmod(691), normalization="integral")
        566*q + 236*q^2 + 286*q^3 + 194*q^4 + O(q^5)
        sage: eisenstein_series_qexp(12, 5, K = Zmod(691), normalization="constant")
        Traceback (most recent call last):
        ...
        ValueError: The numerator of -B_k/(2*k) (=691) must be invertible in the ring Ring of integers modulo 691
        sage: eisenstein_series_qexp(12, 5, K = Zmod(691), normalization="linear")
        q + 667*q^2 + 252*q^3 + 601*q^4 + O(q^5)

        sage: eisenstein_series_qexp(12, 5, K = Zmod(2), normalization="integral")
        1 + O(q^5)
        sage: eisenstein_series_qexp(12, 5, K = Zmod(2), normalization="constant")
        1 + O(q^5)
        sage: eisenstein_series_qexp(12, 5, K = Zmod(2), normalization="linear")
        Traceback (most recent call last):
        ...
        ValueError: The denominator of -B_k/(2*k) (=65520) must be invertible in the ring Ring of integers modulo 2

    AUTHORS:

    - William Stein: original implementation

    - Craig Citro (2007-06-01): rewrote for massive speedup

    - Martin Raum (2009-08-02): port to cython for speedup

    - David Loeffler (2010-04-07): work around an integer overflow when `k` is large

    - David Loeffler (2012-03-15): add options for alternative normalizations
      (motivated by :trac:`12043`)
    """
    ## we use this to prevent computation if it would fail anyway.
    if k <= 0 or k % 2 == 1:
        raise ValueError("k must be positive and even")

    a0 = -bernoulli(k) / (2 * k)

    if normalization == 'linear':
        a0den = a0.denominator()
        try:
            a0fac = K(1 / a0den)
        except ZeroDivisionError:
            raise ValueError(
                "The denominator of -B_k/(2*k) (=%s) must be invertible in the ring %s"
                % (a0den, K))
    elif normalization == 'constant':
        a0num = a0.numerator()
        try:
            a0fac = K(1 / a0num)
        except ZeroDivisionError:
            raise ValueError(
                "The numerator of -B_k/(2*k) (=%s) must be invertible in the ring %s"
                % (a0num, K))
    elif normalization == 'integral':
        a0fac = None
    else:
        raise ValueError(
            "Normalization (=%s) must be one of 'linear', 'constant', 'integral'"
            % normalization)

    R = PowerSeriesRing(K, var)
    if K == QQ and normalization == 'linear':
        ls = Ek_ZZ(k, prec)
        # The following is *dramatically* faster than doing the more natural
        # "R(ls)" would be:
        E = ZZ[var](ls, prec=prec, check=False).change_ring(QQ)
        if len(ls) > 0:
            E._unsafe_mutate(0, a0)
        return R(E, prec)
        # The following is an older slower alternative to the above three lines:
        #return a0fac*R(eisenstein_series_poly(k, prec).list(), prec=prec, check=False)
    else:
        # This used to work with check=False, but that can only be regarded as
        # an improbable lucky miracle. Enabling checking is a noticeable speed
        # regression; the morally right fix would be to expose FLINT's
        # fmpz_poly_to_nmod_poly command (at least for word-sized N).
        if a0fac is not None:
            return a0fac * R(
                eisenstein_series_poly(k, prec).list(), prec=prec, check=True)
        else:
            return R(eisenstein_series_poly(k, prec).list(),
                     prec=prec,
                     check=True)
示例#18
0
    def _wronskian_adjoint(self, weight_parity=0, p=None):
        r"""
        The matrix `W^\# \pmod{p}`, mentioned on page 142 of Nils Skoruppa's thesis.
        This matrix is represented by a list of lists of q-expansions.
        
        The q-expansion is shifted by `-(m + 1) (2*m + 1) / 24` in the case of even weights, and it is
        shifted by `-(m - 1) (2*m - 3) / 24` otherwise. This is compensated by the missing q-powers
        returned by _wronskian_invdeterminant.
        
        INPUT:
        
        - `p` -- A prime or ``None``.
        
        - ``weight_parity`` -- An integer (default: `0`).
        """
        try:
            if weight_parity % 2 == 0:
                wronskian_adjoint = self.__wronskian_adjoint_even
            else:
                wronskian_adjoint = self.__wronskian_adjoint_ood

            if p is None:
                return wronskian_adjoint
            else:
                P = PowerSeriesRing(GF(p), 'q')

                return [map(P, row) for row in wronskian_adjoint]

        except AttributeError:
            qexp_prec = self._qexp_precision()

            if p is None:
                PS = self.integral_power_series_ring()
            else:
                PS = PowerSeriesRing(GF(p), 'q')
            m = self.jacobi_index()

            twom = 2 * m
            frmsq = twom**2

            thetas = dict(
                ((i, j), dict()) for i in xrange(m + 1) for j in xrange(m + 1))

            ## We want to calculate \hat \theta_{j,l} = sum_r (2 m r + j)**2l q**(m r**2 + j r)
            ## in the case of even weight, and \hat \theta_{j,l} = sum_r (2 m r + j)**(2l + 1) q**(m r**2 + j r),
            ## otherwise.
            for r in xrange(isqrt((qexp_prec - 1 + m) // m) + 2):
                for j in (xrange(m + 1) if weight_parity %
                          2 == 0 else range(1, m)):
                    fact_p = (twom * r + j)**2
                    fact_m = (twom * r - j)**2
                    if weight_parity % 2 == 0:
                        coeff_p = 2
                        coeff_m = 2
                    else:
                        coeff_p = 2 * (twom * r + j)
                        coeff_m = -2 * (twom * r - j)

                    for l in (xrange(m + 1) if weight_parity %
                              2 == 0 else range(1, m)):
                        thetas[(j, l)][m * r**2 + r * j] = coeff_p
                        thetas[(j, l)][m * r**2 - r * j] = coeff_m
                        coeff_p = coeff_p * fact_p
                        coeff_m = coeff_m * fact_m
            if weight_parity % 2 == 0:
                thetas[(0, 0)][0] = 1

            thetas = dict((k, PS(th).add_bigoh(qexp_prec))
                          for (k, th) in thetas.iteritems())

            W = matrix(PS, m + 1 if weight_parity % 2 == 0 else (m - 1), [
                thetas[(j, l)] for j in (xrange(m + 1) if weight_parity %
                                         2 == 0 else range(1, m))
                for l in (xrange(m + 1) if weight_parity %
                          2 == 0 else range(1, m))
            ])

            ## Since the adjoint of matrices with entries in a general ring
            ## is extremely slow for matrices of small size, we hard code the
            ## the cases `m = 2` and `m = 3`.  The expressions are obtained by
            ## computing the adjoint of a matrix with entries `w_{i,j}` in a
            ## polynomial algebra.
            if W.nrows() == 1:
                Wadj = matrix(PS, [[1]])
            elif W.nrows() == 2:
                Wadj = matrix(PS, [[W[1, 1], -W[0, 1]], [-W[1, 0], W[0, 0]]])

            elif W.nrows() == 3 and qexp_prec > 10**5:
                adj00 = W[1, 1] * W[2, 2] - W[2, 1] * W[1, 2]
                adj01 = -W[1, 0] * W[2, 2] + W[2, 0] * W[1, 2]
                adj02 = W[1, 0] * W[2, 1] - W[2, 0] * W[1, 1]
                adj10 = -W[0, 1] * W[2, 2] + W[2, 1] * W[0, 2]
                adj11 = W[0, 0] * W[2, 2] - W[2, 0] * W[0, 2]
                adj12 = -W[0, 0] * W[2, 1] + W[2, 0] * W[0, 1]
                adj20 = W[0, 1] * W[1, 2] - W[1, 1] * W[0, 2]
                adj21 = -W[0, 0] * W[1, 2] + W[1, 0] * W[0, 2]
                adj22 = W[0, 0] * W[1, 1] - W[1, 0] * W[0, 1]

                Wadj = matrix(PS,
                              [[adj00, adj01, adj02], [adj10, adj11, adj12],
                               [adj20, adj21, adj22]])

            elif W.nrows() == 4 and qexp_prec > 10**5:
                adj00 = -W[0, 2] * W[1, 1] * W[2, 0] + W[0, 1] * W[1, 2] * W[
                    2, 0] + W[0, 2] * W[1, 0] * W[2, 1] - W[0, 0] * W[
                        1, 2] * W[2, 1] - W[0, 1] * W[1, 0] * W[2, 2] + W[
                            0, 0] * W[1, 1] * W[2, 2]
                adj01 = -W[0, 3] * W[1, 1] * W[2, 0] + W[0, 1] * W[1, 3] * W[
                    2, 0] + W[0, 3] * W[1, 0] * W[2, 1] - W[0, 0] * W[
                        1, 3] * W[2, 1] - W[0, 1] * W[1, 0] * W[2, 3] + W[
                            0, 0] * W[1, 1] * W[2, 3]
                adj02 = -W[0, 3] * W[1, 2] * W[2, 0] + W[0, 2] * W[1, 3] * W[
                    2, 0] + W[0, 3] * W[1, 0] * W[2, 2] - W[0, 0] * W[
                        1, 3] * W[2, 2] - W[0, 2] * W[1, 0] * W[2, 3] + W[
                            0, 0] * W[1, 2] * W[2, 3]
                adj03 = -W[0, 3] * W[1, 2] * W[2, 1] + W[0, 2] * W[1, 3] * W[
                    2, 1] + W[0, 3] * W[1, 1] * W[2, 2] - W[0, 1] * W[
                        1, 3] * W[2, 2] - W[0, 2] * W[1, 1] * W[2, 3] + W[
                            0, 1] * W[1, 2] * W[2, 3]

                adj10 = -W[0, 2] * W[1, 1] * W[3, 0] + W[0, 1] * W[1, 2] * W[
                    3, 0] + W[0, 2] * W[1, 0] * W[3, 1] - W[0, 0] * W[
                        1, 2] * W[3, 1] - W[0, 1] * W[1, 0] * W[3, 2] + W[
                            0, 0] * W[1, 1] * W[3, 2]
                adj11 = -W[0, 3] * W[1, 1] * W[3, 0] + W[0, 1] * W[1, 3] * W[
                    3, 0] + W[0, 3] * W[1, 0] * W[3, 1] - W[0, 0] * W[
                        1, 3] * W[3, 1] - W[0, 1] * W[1, 0] * W[3, 3] + W[
                            0, 0] * W[1, 1] * W[3, 3]
                adj12 = -W[0, 3] * W[1, 2] * W[3, 0] + W[0, 2] * W[1, 3] * W[
                    3, 0] + W[0, 3] * W[1, 0] * W[3, 2] - W[0, 0] * W[
                        1, 3] * W[3, 2] - W[0, 2] * W[1, 0] * W[3, 3] + W[
                            0, 0] * W[1, 2] * W[3, 3]
                adj13 = -W[0, 3] * W[1, 2] * W[3, 1] + W[0, 2] * W[1, 3] * W[
                    3, 1] + W[0, 3] * W[1, 1] * W[3, 2] - W[0, 1] * W[
                        1, 3] * W[3, 2] - W[0, 2] * W[1, 1] * W[3, 3] + W[
                            0, 1] * W[1, 2] * W[3, 3]

                adj20 = -W[0, 2] * W[2, 1] * W[3, 0] + W[0, 1] * W[2, 2] * W[
                    3, 0] + W[0, 2] * W[2, 0] * W[3, 1] - W[0, 0] * W[
                        2, 2] * W[3, 1] - W[0, 1] * W[2, 0] * W[3, 2] + W[
                            0, 0] * W[2, 1] * W[3, 2]
                adj21 = -W[0, 3] * W[2, 1] * W[3, 0] + W[0, 1] * W[2, 3] * W[
                    3, 0] + W[0, 3] * W[2, 0] * W[3, 1] - W[0, 0] * W[
                        2, 3] * W[3, 1] - W[0, 1] * W[2, 0] * W[3, 3] + W[
                            0, 0] * W[2, 1] * W[3, 3]
                adj22 = -W[0, 3] * W[2, 2] * W[3, 0] + W[0, 2] * W[2, 3] * W[
                    3, 0] + W[0, 3] * W[2, 0] * W[3, 2] - W[0, 0] * W[
                        2, 3] * W[3, 2] - W[0, 2] * W[2, 0] * W[3, 3] + W[
                            0, 0] * W[2, 2] * W[3, 3]
                adj23 = -W[0, 3] * W[2, 2] * W[3, 1] + W[0, 2] * W[2, 3] * W[
                    3, 1] + W[0, 3] * W[2, 1] * W[3, 2] - W[0, 1] * W[
                        2, 3] * W[3, 2] - W[0, 2] * W[2, 1] * W[3, 3] + W[
                            0, 1] * W[2, 2] * W[3, 3]

                adj30 = -W[1, 2] * W[2, 1] * W[3, 0] + W[1, 1] * W[2, 2] * W[
                    3, 0] + W[1, 2] * W[2, 0] * W[3, 1] - W[1, 0] * W[
                        2, 2] * W[3, 1] - W[1, 1] * W[2, 0] * W[3, 2] + W[
                            1, 0] * W[2, 1] * W[3, 2]
                adj31 = -W[1, 3] * W[2, 1] * W[3, 0] + W[1, 1] * W[2, 3] * W[
                    3, 0] + W[1, 3] * W[2, 0] * W[3, 1] - W[1, 0] * W[
                        2, 3] * W[3, 1] - W[1, 1] * W[2, 0] * W[3, 3] + W[
                            1, 0] * W[2, 1] * W[3, 3]
                adj32 = -W[1, 3] * W[2, 2] * W[3, 0] + W[1, 2] * W[2, 3] * W[
                    3, 0] + W[1, 3] * W[2, 0] * W[3, 2] - W[1, 0] * W[
                        2, 3] * W[3, 2] - W[1, 2] * W[2, 0] * W[3, 3] + W[
                            1, 0] * W[2, 2] * W[3, 3]
                adj33 = -W[1, 3] * W[2, 2] * W[3, 1] + W[1, 2] * W[2, 3] * W[
                    3, 1] + W[1, 3] * W[2, 1] * W[3, 2] - W[1, 1] * W[
                        2, 3] * W[3, 2] - W[1, 2] * W[2, 1] * W[3, 3] + W[
                            1, 1] * W[2, 2] * W[3, 3]

                Wadj = matrix(PS, [[adj00, adj01, adj02, adj03],
                                   [adj10, adj11, adj12, adj13],
                                   [adj20, adj21, adj22, adj23],
                                   [adj30, adj31, adj32, adj33]])
            else:
                Wadj = W.adjoint()

            if weight_parity % 2 == 0:
                wronskian_adjoint = [[Wadj[i, r] for i in xrange(m + 1)]
                                     for r in xrange(m + 1)]
            else:
                wronskian_adjoint = [[Wadj[i, r] for i in xrange(m - 1)]
                                     for r in xrange(m - 1)]

            if p is None:
                if weight_parity % 2 == 0:
                    self.__wronskian_adjoint_even = wronskian_adjoint
                else:
                    self.__wronskian_adjoint_odd = wronskian_adjoint

            return wronskian_adjoint