Esempio n. 1
0
def number_field_from_dict(d):
    r"""
    INPUT:

    - 'd' -- {'base':F,'p':p,'g':g } where p is a polynomial in the variable(s) xN with coefficients in K. (The 'x' is just a convention)

    OUTPUT:

    - 'F' -- Number field extending K with relative minimal polynomial p.
    """
    K = d['base']; p=d['relative polynomial']; g=d['gens']
    if K=='QQ':
        K = QQ
    elif isinstance(K,dict):
        K = number_field_from_dict(K)
    else:
        raise ValueError,"Could not construct number field!"
    F = NumberField(K[g](p),names=g)
    if F.absolute_degree()==1:
        F = QQ
    return F
Esempio n. 2
0
def number_field_from_dict(d):
    r"""
    INPUT:

    - 'd' -- {'base':F,'p':p,'g':g } where p is a polynomial in the variable(s) xN with coefficients in K. (The 'x' is just a convention)

    OUTPUT:

    - 'F' -- Number field extending K with relative minimal polynomial p.
    """
    K = d['base']; p=d['relative polynomial']; g=d['gens']
    if K=='QQ':
        K = QQ
    elif isinstance(K,dict):
        K = number_field_from_dict(K)
    else:
        raise ValueError,"Could not construct number field!"
    F = NumberField(K[g](p),names=g)
    if F.absolute_degree()==1:
        F = QQ
    return F
Esempio n. 3
0
def find_newform_label(level,weight,character,field,aps):
    r"""
    Find the label of the newform orbit in the database which matches the input.
    
    INPUT:
    - 'level'     -- the level,
    - 'weight'    -- the weight
    - 'character' -- the character'
    - 'field'     -- the field, given in terms of a list of integer coefficients for the absolute polynomial  
    - 'aps'       -- the coefficients - given as a dictionary of lists giving the coefficient in terms of the generator of the field as above.
    
    
    EXAMPLE:
    
    sage: find_newform_label(9,16,1,[-119880,0,1],{2:[0,1]})
    u'e'
    sage: find_newform_label(71,2,1,[-3,-4,1,1],{3:[0,-1,0]})
    u'a'
    sage: find_newform_label(71,2,1,[-3,-4,1,1],{5:[5,1,-1]})
    u'a'
    
    NOTE: We implicitly assume that the input given is correct in the sense that 
        if there is a unique orbit with a coefficient field of the same degree as the input
        then we simply return that label. (This will save a lot of time...)
        
    """
    from web_modform_space import WebModFormSpace
    from sage.all import NumberField,QQ
    M = WebModFormSpace(level=level,weight=weight,character=character)
    if M.dimension_new_cusp_forms==1:
        return 'a'
    orbits = M.hecke_orbits
    ## construct field from field input... 
    if not isinstance(field,list):
        raise ValueError,"Need to give field as a list!"
    if not isinstance(aps,dict):
        raise ValueError,"Need to give aps as a dict!"
    if field == [1]:
        NF = QQ
    else:
        NF = NumberField(QQ['x'](field),names='x')
    degree_of_input = NF.absolute_degree()
    degrees = map(lambda x:x[1].coefficient_field_degree,orbits.viewitems())
    if degrees.count(degree_of_input)==0:
        raise ValueError,"No newform with this level, weight, character and field degree!"
    if degrees.count(degree_of_input)==1:
        ## If there is a unique mathcing field we return this orbit label.
        l = filter(lambda x: x[1].coefficient_field_degree==degree_of_input,orbits.viewitems() )
        return l[0][0]
    aps_input = { p: NF(a) for p,a in aps.viewitems()}
    possible_labels = orbits.keys()
    for label,f in orbits.viewitems():
        if f.coefficient_field_degree != degree_of_input:
            possible_labels.remove(label)
            continue
        try:
            for p,ap_input in aps_input.viewitems():
                if f.coefficient_field == QQ:
                    homs = [lambda x: x]
                else:
                    homs = f.coefficient(p).parent().Hom(NF)
                for h in homs:
                    ap = h(f.coefficient(p))
                    if ap_input != ap:
                        possible_labels.remove(label)
                        raise StopIteration
        except StopIteration:
            continue
    if len(possible_labels) > 1:
        raise ArithmeticError,"Not sufficient data (or errors) to determine orbit!"
    if len(possible_labels) == 0:
        raise ArithmeticError,"Not sufficient data (or errors) to determine orbit! NO matching label found!"
    return possible_labels[0]
Esempio n. 4
0
class WebNewForm(WebObject, CachedRepresentation):

    _key = ['level', 'weight', 'character', 'label', 'version']
    _file_key = ['hecke_orbit_label','version']
    _file_key_multi = ['prec']
    if emf_version > 1.3:
        _collection_name = 'webnewforms2'
    else:
        _collection_name = 'webnewforms'

    def __init__(self, level=1, weight=12, character=1, label='a', prec=0, parent=None, update_from_db=True,**kwargs):
        emf_logger.debug("In WebNewForm {0}".format((level,weight,character,label,parent,update_from_db)))
        if isinstance(level,basestring) or kwargs.has_key('hecke_orbit_label'):
            hecke_orbit_label = kwargs.get('hecke_orbit_label', level)
            level,weight,character,label = parse_newform_label(hecke_orbit_label)
        self._reduction = (type(self),(level,weight,character,label),{'parent':parent,'update_from_db':update_from_db})
        if isinstance(character, WebChar):
            character_number = character.number
        else:
            character_number = character
            character = None if parent is None else parent.character
            if not isinstance(label,basestring):
                if isinstance(label,(int,Integer)):
                    label = cremona_letter_code(label)
                else:
                    raise ValueError,"Need label either string or integer! We got:{0}".format(label)

        emf_logger.debug("Before init properties 0")
        self._properties = WebProperties(
            WebInt('level', value=level),
            WebInt('weight', value=weight),
            WebCharProperty('character', modulus=level,
                            number=character_number,
                            value = character,
                            include_in_update = True if character is None
                            else False),
            WebStr('character_naming_scheme', value='Conrey'),
            WebStr('sage_version', value=''),
            WebStr('hecke_orbit_label', default_value=newform_label(level, weight, character_number, label)),
            WebStr('label', default_value=label),
            WebInt('dimension'),
            WebqExp('q_expansion'),
            WebCoeffs('_coefficients'),
            WebDict('_embeddings'),
            WebInt('prec',value=0, save_to_db=False, save_to_fs=True), 
            WebNumberField('base_ring'),
            WebNumberField('coefficient_field'),
            WebInt('coefficient_field_degree'),
            WebList('twist_info', required = False),
            WebInt('is_cm', required = False),
            WebInt('cm_disc', required = False, default_value=0),
            WebDict('_cm_values',required=False),
            WebBool('is_cuspidal',default_value=True),
            WebDict('satake', required=False),
            WebDict('_atkin_lehner_eigenvalues', required=False),
            WebBool('is_rational'),
            WebPoly('absolute_polynomial'),
            WebFloat('version', value=float(emf_version), save_to_fs=True),
            WebDict('explicit_formulas',required=False),
            WebDate('creation_date',value=None),
            WebModFormSpaceProperty('parent', value=parent,
                                    level = level,
                                    weight = weight,
                                    character = character_number,
                                    update_hecke_orbits=False,
                                    update_from_db=update_from_db)
#                                    include_in_update = True if parent is None
#                                    else False),
            )

        self._add_to_fs_query = {'prec': {'$gt': int(prec-1)}}

        super(WebNewForm, self).__init__(
            update_from_db=update_from_db,
            **kwargs
            )

        self._add_to_fs_query = {'prec': {'$gt': int(self.prec-1)}}
        
        # We're setting the WebEigenvalues property after calling __init__ of the base class
        # because it will set hecke_orbit_label from the db first

        ## 
        ## We don't init the eigenvalues (since E*v is slow)
        ## unless we (later) request a coefficient which is not
        ## in self._coefficients
        
        self.eigenvalues = WebEigenvalues(self.hecke_orbit_label, prec = self.prec, \
                                              init_dynamic_properties=False, \
                                              update_from_db = False)

        self.make_code_snippets()

    def update_from_db(self, ignore_precision = False, ignore_precision_if_failed = True, **kwargs):
        # this finds the (file) record with the
        # lowest precision (=smallest record)
        # above or equal to self.prec
        if not ignore_precision:
            self._add_to_fs_query = {'prec': {'$gt': int(self.prec-1)}}
            self._sort_files = [('prec', pymongo.ASCENDING)]
        else:
            # However, if ignore_precision is True,
            # then we just ignore this field
            # This is for compatibility reasons
            # as older versions did not have the prec stored in the fs
            file_key_multi = self._file_key_multi
            self._file_key_multi = None
            self._add_to_fs_query = None
            self._sort_files = []
        super(WebNewForm,self).update_from_db(**kwargs)
        if not self.has_updated() and ignore_precision_if_failed:
            self.update_from_db(ignore_precision = True, ignore_precision_if_failed = False)
        if ignore_precision:
            # restore file_key_multi
            self._file_key_multi = file_key_multi
            self._sort_files = [('prec', pymongo.ASCENDING)]
        #remember the precision we just got from the db for queries
        self._add_to_fs_query = {'prec': {'$gt': int(self.prec-1)}}

    def __repr__(self):
        if self.dimension == 0:
            s = "Zero "
        else:
            s = ""
        s = "WebNewform in S_{0}({1},chi_{2}) with label {3}".format(self.weight,self.level,self.character.number,self.label)
        return s

    def init_dynamic_properties(self):
        if self.q_expansion is not None:
            if not self.q_expansion.prec >= self.prec:
                self._properties['q_expansion'].set_from_coefficients(self._coefficients)
            self._properties['q_expansion'].maxprec = self.prec
        
    def q_expansion_latex(self, prec=None, name=None):
        return self._properties['q_expansion'].latex(prec, name, keepzeta=True)

    def value(self, z, embedding=0):
        if self.prec == 0:
            return 0
        else:
            q = exp(2*CC.pi()*CC(0,1)*z)
            return sum(self.coefficient_embedding(n,embedding)*q**n for n in range(self.prec))
            
    
    def coefficient(self, n):
        r"""
          Return coefficient nr. n
        """
        #emf_logger.debug("In coefficient: n={0}".format(n))
        if n==0:
            if self.is_cuspidal:
                return 0
        c = self._coefficients.get(n, None)
        if c is None:
            c = self.coefficients([n])[0] 
        return c

    def first_nonvanishing_coefficient(self, return_index = True):
        r"""
         Return the first Fourier coefficient of self
         of index >1 that does not vanish.
         if return_index is True, we also return the index of that coefficient
        """
        return self._coefficients.first_nonvanishing_coefficient(return_index=return_index)

    def first_nonvanishing_coefficient_norm(self):
        return self._coefficients.first_nonvanishing_coefficient_norm()

    def first_nonvanishing_coefficient_trace(self):
        return self._coefficients.first_nonvanishing_coefficient_trace()

    def complexity_of_first_nonvanishing_coefficients(self, number_of_coefficients=4):
        return self._coefficients.coefficient_complexity(number_of_coefficients)

    def coefficient_embeddings(self, n):
        r"""
          Return all emneddings of the coefficient a(n) of self.
        """
        if not 'values' in self._embeddings:
            raise ValueError('We do not have any embeddings. for coefficient a({})'.format(n))
        else:
            if n < self.prec:
                return self._embeddings['values'][n]
            else:
                raise ValueError('We do not have coefficient a({})'.format(n))

    def coefficient_embedding(self,n,i):
        r"""
        Return the i-th complex embedding of coefficient C(n).
        Note that if it is not in the dictionary we compute the embedding (but not the coefficient).
        """
        if not 'values' in self._embeddings:
            self._embeddings['values'] = {}
        if not 'bitprec' in self._embeddings:
            self._embeddings['bitprec'] = {}
        embc = self._embeddings['values'].get(n,None)
        bitprec = self._embeddings['bitprec']
        if embc is None:
            c = self.coefficient(n)
            if hasattr(c,"complex_embeddings"):
                embc = c.complex_embeddings(bitprec)
            else:
                embc = [ComplexField(bitprec)(c) for x in range(self.coefficient_field_degree)]
            self._embeddings['values'][n]=embc
        else:
            if len(embc) < self.coefficient_field_degree:
                embc = [embc[0] for x in range(self.coefficient_field_degree)]
                self._embeddings['values'][n]=embc
        if i > len(embc):
            raise ValueError,"Embedding nr. {0} does not exist of a number field of degree {1},embc={2}".format(i,self.coefficient_field_degree,embc)
        return embc[i]
        
        
    def coefficients(self, nrange=range(1, 10), save_to_db=False):
        r"""
         Gives the coefficients in a range.
         We assume that the self._ap containing Hecke eigenvalues
         are stored.
        """
        if len(nrange) == 0:
            return []
        if not isinstance(nrange, list):
            M = nrange
            nrange = range(0, M)
        if len(nrange) > 1:
            emf_logger.debug("getting coeffs in range {0}--{1}".format(nrange[0],nrange[-1]))
        else:
            emf_logger.debug("getting coeffs in range {0}--{0}".format(nrange[0]))
        res = []
        recompute = False
        for n in nrange:
            c = self._coefficients.get(n, None)
            #emf_logger.debug("c({0}) in self._coefficients={1}".format(n,c))            
            if c is None:
                if n == 0 and self.is_cuspidal:
                    c = 0
                else:
                    recompute = True
                    c = self.coefficient_n_recursive(n)
                    self._coefficients[n] = c
            res.append(c)
        if recompute and save_to_db:
            self.save_to_db(update=True)
        return res
       
    def coefficient_n_recursive(self, n):
        r"""
          Reimplement the recursive algorithm in sage modular/hecke/module.py
          We do this because of a bug in sage with .eigenvalue()
        """
        from sage.all import factor
        ev = self.eigenvalues

        c2 = self._coefficients.get(2)
        if c2 is not None:
            K = c2.parent()
        else:
            if ev.max_coefficient_in_db() >= 2:
                if not ev.has_eigenvalue(2):
                    ev.init_dynamic_properties()
            else:
                raise StopIteration,"Newform does not have eigenvalue a(2)!"
            self._coefficients[2]=ev[2]
            K = ev[2].parent()
        prod = K(1)
        if K.absolute_degree()>1 and K.is_relative():
            KZ = K.base_field()
        else:
            KZ = K
        #emf_logger.debug("K= {0}".format(K))
        F = factor(n)
        for p, r in F:
            #emf_logger.debug("parent_char_val[{0}]={1}".format(p,self.parent.character_used_in_computation.value(p)))
            #emf_logger.debug("char_val[{0}]={1}".format(p,self.character.value(p)))
            (p, r) = (int(p), int(r))
            pr = p**r
            cp = self._coefficients.get(p)
            if cp is None:
                if ev.has_eigenvalue(p):
                    cp = ev[p]
                elif ev.max_coefficient_in_db() >= p:
                    ev.init_dynamic_properties()
                    cp = ev[p]
            #emf_logger.debug("c{0} = {1}, parent={2}".format(p,cp,cp.parent()))
            if cp is None:
                raise IndexError,"p={0} is outside the range of computed primes (primes up to {1})! for label:{2}".format(p,max(ev.primes()),self.label)
            if self._coefficients.get(pr) is None:
                if r == 1:
                    c = cp
                else:
                    # a_{p^r} := a_p * a_{p^{r-1}} - eps(p)p^{k-1} a_{p^{r-2}}
                    apr1 = self.coefficient_n_recursive(pr//p)
                    #ap = self.coefficient_n_recursive(p)
                    apr2 = self.coefficient_n_recursive(pr//(p*p))
                    val = self.character.value(p)
                    if val == 0:
                        c = cp*apr1
                    else:
                        eps = KZ(val)
                        c = cp*apr1 - eps*(p**(self.weight-1)) * apr2
                    #emf_logger.debug("c({0})={1}".format(pr,c))
                            #ev[pr]=c
                self._coefficients[pr]=c
            try:
                prod *= K(self._coefficients[pr])
            except:
                if hasattr(self._coefficients[pr],'vector'):
                    if len(self._coefficients[pr].vector()) == len(K.power_basis()):
                        prod *= K(self._coefficients[pr].vector())
                    else:
                        emf_logger.debug("vec={0}".format(self._coefficients[pr].vector()))
                        raise ArithmeticError,"Wrong size of vectors!"
                else:
                    raise ArithmeticError,"Can not compute product of coefficients!"
            
        return prod

    def available_precs(self):
        r"""
        The precision is the number of computed Fourier coefficients.
        We have several records in the database for each newform, 
        each in a different precision.
        This method returns a list of the precisions that are available in the database for this newform.
        """
        files = self.get_file_list()
        try:
            return [x['prec'] for x in files]
        except KeyError:
            #backwards compatibility
            try:
                return [self.get_db_record()['prec']]
            except KeyError:
                return [self.prec]

    def max_available_prec(self):
        try:
            ps = self.available_precs()
        except IndexError:
            ps = [0]
        return max(ps)

    def delete_file_with_prec(self, prec):
        files = self.get_file_list({'prec': int(prec)})
        for f in files:
            self._files.delete(f['_id'])

    def max_cn(self):
        r"""
        The largest N for which we are sure that we can compute a(n) for all 1<=n<=N
        """
        return self.eigenvalues.max_coefficient_in_db()

        #if self.eigenvalues.primes()==[]:
        #    return 1
        #return max(self.eigenvalues.primes()) + 1

    def atkin_lehner_eigenvalue(self, Q):
        r""" Return the Atkin-Lehner eigenvalues of self
        corresponding to Q|N
        """
        if not (self.character.is_trivial() or self.character.order == 2):
            return None
        
        l = self.atkin_lehner_eigenvalues()
        return l.get(Q)

    def atkin_lehner_eigenvalues(self):
        r""" Return the Atkin-Lehner eigenvalues of self.

           EXAMPLES::

           sage: get_atkin_lehner_eigenvalues(4,14,0)
           '{2: 1, 14: 1, 7: 1}'
           sage: get_atkin_lehner_eigenvalues(4,14,1)
           '{2: -1, 14: 1, 7: -1}'

        """
        if not (self.character.is_trivial() or self.character.order == 2):
            return None
        
        if(len(self._atkin_lehner_eigenvalues.keys()) > 0):
            return self._atkin_lehner_eigenvalues

    def atkin_lehner_eigenvalues_for_all_cusps(self):
        r"""
        """
        self._atkin_lehner_eigenvalues_at_cusps = {}
        G =Gamma0(self.level)
        for c in Gamma0(self.level).cusps():
            aev = self.atkin_lehner_eigenvalues()
            if aev is None:
                self._atkin_lehner_eigenvalues_at_cusps = None
            else:
                for Q,ev in aev.items():
                    if G.are_equivalent(c,Q/self.level):
                        self._atkin_lehner_eigenvalues_at_cusps[c] = Q,ev
        return self._atkin_lehner_eigenvalues_at_cusps
        
    def url(self):
        return url_for('emf.render_elliptic_modular_forms', level=self.level, weight=self.weight, character=self.character.number, label=self.label)

    def create_small_record(self, min_prec=10, want_prec=100, max_length = 5242880, max_height_qexp = default_max_height):
        ### creates a duplicate record (fs) of this webnewform
        ### with lower precision to load faster on the web
        ### we aim to have at most max_length bytes
        ### but at least min_prec coefficients and we desire to have want_prec
        if min_prec>=self.prec:
            raise ValueError("Need higher precision, self.prec = {}".format(self.prec))
        if not hasattr(self, '_file_record_length'):
            self.update_from_db()
        l = self._file_record_length
            
        if l > max_length or self.prec > want_prec:
            nl = float(l)/float(self.prec)*float(want_prec)
            if nl > max_length:
                prec = max([floor(float(self.prec)/float(l)*float(max_length)), min_prec])
            else:
                prec = want_prec
            emf_logger.debug("Creating a new record with prec = {}".format(prec))
            self.prec = prec
            include_coeffs = self.complexity_of_first_nonvanishing_coefficients() <= default_max_height
            if include_coeffs:
                self.q_expansion = self.q_expansion.truncate_powerseries(prec)
                self._coefficients = {n:c for n,c in self._coefficients.iteritems() if n<prec}
            else:
                self.q_expansion = self.q_expansion.truncate_powerseries(1)
                self._coefficients = {}
                self.prec = 0
                self.coefficient_field = NumberField(self.absolute_polynomial, names=str(self.coefficient_field.gen()))
            self._embeddings['values'] = {n:c for n,c in self._embeddings['values'].iteritems() if n<prec}
            self._embeddings['prec'] = prec
            self.save_to_db()

    def download_to_sage(self, prec=None):
        r"""
        Minimal version for now to download to sage.
        Does not work for high values of prec for large degree number fields (timeout).
        """
        if prec is None:
            prec = self.prec
        s = "var('x')\n"
        if self.base_ring.absolute_degree() > 1:
            s += "K.<{brgen}>=NumberField({crpol})\n".format(brgen=str(self.base_ring.gen()), crpol=self.base_ring.polynomial().change_variable_name('x'))
        if self.coefficient_field.is_absolute():
            if self.coefficient_field.absolute_degree() > 1:
                s +=  "L.<{cfgen}> = NumberField({cfpol})\n".format(
                    cfgen=str(self.coefficient_field.gen()), cfpol=self.absolute_polynomial
                  )
        elif self.coefficient_field.relative_degree() > 1:
            s += "y = polygen(K)\n"
            s +=  "L.<{cfgen}> = NumberField({cfpol})\n".format(
                  cfgen=str(self.coefficient_field.gen()), cfpol=self.coefficient_field.relative_polynomial().change_variable_name('y')
                  )
        s = s + "D = DirichletGroup({N})\n".format(
            N = self.level
            )
        C = self.character.sage_character.parent()
        s = s + "f = {{'coefficients': {coeffs}, 'level' : {level}, 'weight': {weight}, 'character': D.Element(D,vector({elt})), 'label': '{label}','dimension': {dim}, 'is_cm': {cm} , 'cm_discriminant': {cm_disc}, 'atkin_lehner': {al}, 'explicit_formulas': {ep}}}".format(coeffs = self.coefficients(range(prec)),
            level=self.level, weight=self.weight, elt = list(self.character.sage_character.element()), label=self.hecke_orbit_label, dim=self.dimension, cm=self.is_cm, cm_disc=None if not self.is_cm else self.cm_disc , al=self.atkin_lehner_eigenvalues(),
            ep = self.explicit_formulas
            )
        s = s + "\n\n#EXAMPLE\n"
        s = s + "#sage: f['coefficients'][7]\n#{}\n".format(self.coefficient(7))
        s = s + "#sage: f['character']\n#{}".format(self.character.sage_character)
        emf_logger.debug("Generated sage file for {}".format(self.hecke_orbit_label))
        return s

    def sage_newform_number(self):
        ##store this in the db!!
        return orbit_index_from_label(self.label)

    def make_code_snippets(self):
        self.code = deepcopy(self.parent.code)
        self.code['show'] = {'sage':''}
        # Fill in placeholders for this specific newform:
        self.code['f']['sage'] = self.code['f']['sage'].format(newform_number=self.sage_newform_number())

        #self.code['f']['sage'] = self.code['f']['sage'].split("\n")
        # remove final empty line
        if len(self.code['f']['sage'][-1])==0:
            self.code['f']['sage'] = self.code['f']['sage'][:-1]

    def dump_coefficients(self, prec):
        if prec is None:
            prec = self.prec
        return dumps(self.coefficients(range(prec)))
Esempio n. 5
0
class WebNewForm(WebObject, CachedRepresentation):

    _key = ['level', 'weight', 'character', 'label', 'version']
    _file_key = ['hecke_orbit_label', 'version']
    _file_key_multi = ['prec']
    if emf_version > 1.3:
        _collection_name = 'webnewforms2'
    else:
        _collection_name = 'webnewforms'

    def __init__(self,
                 level=1,
                 weight=12,
                 character=1,
                 label='a',
                 prec=0,
                 parent=None,
                 update_from_db=True,
                 **kwargs):
        emf_logger.debug("In WebNewForm {0}".format(
            (level, weight, character, label, parent, update_from_db)))
        if isinstance(level,
                      basestring) or kwargs.has_key('hecke_orbit_label'):
            hecke_orbit_label = kwargs.get('hecke_orbit_label', level)
            level, weight, character, label = parse_newform_label(
                hecke_orbit_label)
        self._reduction = (type(self), (level, weight, character, label), {
            'parent': parent,
            'update_from_db': update_from_db
        })
        if isinstance(character, WebChar):
            character_number = character.number
        else:
            character_number = character
            character = None if parent is None else parent.character
            if not isinstance(label, basestring):
                if isinstance(label, (int, Integer)):
                    label = cremona_letter_code(label)
                else:
                    raise ValueError, "Need label either string or integer! We got:{0}".format(
                        label)

        emf_logger.debug("Before init properties 0")
        self._properties = WebProperties(
            WebInt('level', value=level), WebInt('weight', value=weight),
            WebCharProperty(
                'character',
                modulus=level,
                number=character_number,
                value=character,
                include_in_update=True if character is None else False),
            WebStr('character_naming_scheme', value='Conrey'),
            WebStr('sage_version', value=''),
            WebStr('hecke_orbit_label',
                   default_value=newform_label(level, weight, character_number,
                                               label)),
            WebStr('label', default_value=label), WebInt('dimension'),
            WebqExp('q_expansion'), WebCoeffs('_coefficients'),
            WebDict('_embeddings'),
            WebInt('prec', value=0, save_to_db=False, save_to_fs=True),
            WebNumberField('base_ring'), WebNumberField('coefficient_field'),
            WebInt('coefficient_field_degree'),
            WebList('twist_info', required=False),
            WebInt('is_cm', required=False),
            WebInt('cm_disc', required=False, default_value=0),
            WebDict('_cm_values', required=False),
            WebBool('is_cuspidal', default_value=True),
            WebDict('satake', required=False),
            WebDict('_atkin_lehner_eigenvalues', required=False),
            WebBool('is_rational'), WebPoly('absolute_polynomial'),
            WebFloat('version', value=float(emf_version), save_to_fs=True),
            WebDict('explicit_formulas', required=False),
            WebDate('creation_date', value=None),
            WebModFormSpaceProperty('parent',
                                    value=parent,
                                    level=level,
                                    weight=weight,
                                    character=character_number,
                                    update_hecke_orbits=False,
                                    update_from_db=update_from_db)
            #                                    include_in_update = True if parent is None
            #                                    else False),
        )

        self._add_to_fs_query = {'prec': {'$gt': int(prec - 1)}}

        super(WebNewForm, self).__init__(update_from_db=update_from_db,
                                         **kwargs)

        self._add_to_fs_query = {'prec': {'$gt': int(self.prec - 1)}}

        # We're setting the WebEigenvalues property after calling __init__ of the base class
        # because it will set hecke_orbit_label from the db first

        ##
        ## We don't init the eigenvalues (since E*v is slow)
        ## unless we (later) request a coefficient which is not
        ## in self._coefficients

        self.eigenvalues = WebEigenvalues(self.hecke_orbit_label, prec = self.prec, \
                                              init_dynamic_properties=False, \
                                              update_from_db = False)

        self.make_code_snippets()

    def update_from_db(self,
                       ignore_precision=False,
                       ignore_precision_if_failed=True,
                       **kwargs):
        # this finds the (file) record with the
        # lowest precision (=smallest record)
        # above or equal to self.prec
        if not ignore_precision:
            self._add_to_fs_query = {'prec': {'$gt': int(self.prec - 1)}}
            self._sort_files = [('prec', pymongo.ASCENDING)]
        else:
            # However, if ignore_precision is True,
            # then we just ignore this field
            # This is for compatibility reasons
            # as older versions did not have the prec stored in the fs
            file_key_multi = self._file_key_multi
            self._file_key_multi = None
            self._add_to_fs_query = None
            self._sort_files = []
        super(WebNewForm, self).update_from_db(**kwargs)
        if not self.has_updated() and ignore_precision_if_failed:
            self.update_from_db(ignore_precision=True,
                                ignore_precision_if_failed=False)
        if ignore_precision:
            # restore file_key_multi
            self._file_key_multi = file_key_multi
            self._sort_files = [('prec', pymongo.ASCENDING)]
        #remember the precision we just got from the db for queries
        self._add_to_fs_query = {'prec': {'$gt': int(self.prec - 1)}}

    def __repr__(self):
        if self.dimension == 0:
            s = "Zero "
        else:
            s = ""
        s = "WebNewform in S_{0}({1},chi_{2}) with label {3}".format(
            self.weight, self.level, self.character.number, self.label)
        return s

    def init_dynamic_properties(self):
        if self.q_expansion is not None:
            if not self.q_expansion.prec >= self.prec:
                self._properties['q_expansion'].set_from_coefficients(
                    self._coefficients)
            self._properties['q_expansion'].maxprec = self.prec

    def q_expansion_latex(self, prec=None, name=None):
        return self._properties['q_expansion'].latex(prec, name, keepzeta=True)

    def value(self, z, embedding=0):
        if self.prec == 0:
            return 0
        else:
            q = exp(2 * CC.pi() * CC(0, 1) * z)
            return sum(
                self.coefficient_embedding(n, embedding) * q**n
                for n in range(self.prec))

    def coefficient(self, n):
        r"""
          Return coefficient nr. n
        """
        #emf_logger.debug("In coefficient: n={0}".format(n))
        if n == 0:
            if self.is_cuspidal:
                return 0
        c = self._coefficients.get(n, None)
        if c is None:
            c = self.coefficients([n])[0]
        return c

    def first_nonvanishing_coefficient(self, return_index=True):
        r"""
         Return the first Fourier coefficient of self
         of index >1 that does not vanish.
         if return_index is True, we also return the index of that coefficient
        """
        return self._coefficients.first_nonvanishing_coefficient(
            return_index=return_index)

    def first_nonvanishing_coefficient_norm(self):
        return self._coefficients.first_nonvanishing_coefficient_norm()

    def first_nonvanishing_coefficient_trace(self):
        return self._coefficients.first_nonvanishing_coefficient_trace()

    def complexity_of_first_nonvanishing_coefficients(self,
                                                      number_of_coefficients=4
                                                      ):
        return self._coefficients.coefficient_complexity(
            number_of_coefficients)

    def coefficient_embeddings(self, n):
        r"""
          Return all emneddings of the coefficient a(n) of self.
        """
        if not 'values' in self._embeddings:
            raise ValueError(
                'We do not have any embeddings. for coefficient a({})'.format(
                    n))
        else:
            if n < self.prec:
                return self._embeddings['values'][n]
            else:
                raise ValueError('We do not have coefficient a({})'.format(n))

    def coefficient_embedding(self, n, i):
        r"""
        Return the i-th complex embedding of coefficient C(n).
        Note that if it is not in the dictionary we compute the embedding (but not the coefficient).
        """
        if not 'values' in self._embeddings:
            self._embeddings['values'] = {}
        if not 'bitprec' in self._embeddings:
            self._embeddings['bitprec'] = {}
        embc = self._embeddings['values'].get(n, None)
        bitprec = self._embeddings['bitprec']
        if embc is None:
            c = self.coefficient(n)
            if hasattr(c, "complex_embeddings"):
                embc = c.complex_embeddings(bitprec)
            else:
                embc = [
                    ComplexField(bitprec)(c)
                    for x in range(self.coefficient_field_degree)
                ]
            self._embeddings['values'][n] = embc
        else:
            if len(embc) < self.coefficient_field_degree:
                embc = [embc[0] for x in range(self.coefficient_field_degree)]
                self._embeddings['values'][n] = embc
        if i > len(embc):
            raise ValueError, "Embedding nr. {0} does not exist of a number field of degree {1},embc={2}".format(
                i, self.coefficient_field_degree, embc)
        return embc[i]

    def coefficients(self, nrange=range(1, 10), save_to_db=False):
        r"""
         Gives the coefficients in a range.
         We assume that the self._ap containing Hecke eigenvalues
         are stored.
        """
        if len(nrange) == 0:
            return []
        if not isinstance(nrange, list):
            M = nrange
            nrange = range(0, M)
        if len(nrange) > 1:
            emf_logger.debug("getting coeffs in range {0}--{1}".format(
                nrange[0], nrange[-1]))
        else:
            emf_logger.debug("getting coeffs in range {0}--{0}".format(
                nrange[0]))
        res = []
        recompute = False
        for n in nrange:
            c = self._coefficients.get(n, None)
            #emf_logger.debug("c({0}) in self._coefficients={1}".format(n,c))
            if c is None:
                if n == 0 and self.is_cuspidal:
                    c = 0
                else:
                    recompute = True
                    c = self.coefficient_n_recursive(n)
                    self._coefficients[n] = c
            res.append(c)
        if recompute and save_to_db:
            self.save_to_db(update=True)
        return res

    def coefficient_n_recursive(self, n):
        r"""
          Reimplement the recursive algorithm in sage modular/hecke/module.py
          We do this because of a bug in sage with .eigenvalue()
        """
        from sage.all import factor
        ev = self.eigenvalues

        c2 = self._coefficients.get(2)
        if c2 is not None:
            K = c2.parent()
        else:
            if ev.max_coefficient_in_db() >= 2:
                if not ev.has_eigenvalue(2):
                    ev.init_dynamic_properties()
            else:
                raise StopIteration, "Newform does not have eigenvalue a(2)!"
            self._coefficients[2] = ev[2]
            K = ev[2].parent()
        prod = K(1)
        if K.absolute_degree() > 1 and K.is_relative():
            KZ = K.base_field()
        else:
            KZ = K
        #emf_logger.debug("K= {0}".format(K))
        F = factor(n)
        for p, r in F:
            #emf_logger.debug("parent_char_val[{0}]={1}".format(p,self.parent.character_used_in_computation.value(p)))
            #emf_logger.debug("char_val[{0}]={1}".format(p,self.character.value(p)))
            (p, r) = (int(p), int(r))
            pr = p**r
            cp = self._coefficients.get(p)
            if cp is None:
                if ev.has_eigenvalue(p):
                    cp = ev[p]
                elif ev.max_coefficient_in_db() >= p:
                    ev.init_dynamic_properties()
                    cp = ev[p]
            #emf_logger.debug("c{0} = {1}, parent={2}".format(p,cp,cp.parent()))
            if cp is None:
                raise IndexError, "p={0} is outside the range of computed primes (primes up to {1})! for label:{2}".format(
                    p, max(ev.primes()), self.label)
            if self._coefficients.get(pr) is None:
                if r == 1:
                    c = cp
                else:
                    # a_{p^r} := a_p * a_{p^{r-1}} - eps(p)p^{k-1} a_{p^{r-2}}
                    apr1 = self.coefficient_n_recursive(pr // p)
                    #ap = self.coefficient_n_recursive(p)
                    apr2 = self.coefficient_n_recursive(pr // (p * p))
                    val = self.character.value(p)
                    if val == 0:
                        c = cp * apr1
                    else:
                        eps = KZ(val)
                        c = cp * apr1 - eps * (p**(self.weight - 1)) * apr2
                    #emf_logger.debug("c({0})={1}".format(pr,c))
                    #ev[pr]=c
                self._coefficients[pr] = c
            try:
                prod *= K(self._coefficients[pr])
            except:
                if hasattr(self._coefficients[pr], 'vector'):
                    if len(self._coefficients[pr].vector()) == len(
                            K.power_basis()):
                        prod *= K(self._coefficients[pr].vector())
                    else:
                        emf_logger.debug("vec={0}".format(
                            self._coefficients[pr].vector()))
                        raise ArithmeticError, "Wrong size of vectors!"
                else:
                    raise ArithmeticError, "Can not compute product of coefficients!"

        return prod

    def available_precs(self):
        r"""
        The precision is the number of computed Fourier coefficients.
        We have several records in the database for each newform, 
        each in a different precision.
        This method returns a list of the precisions that are available in the database for this newform.
        """
        files = self.get_file_list()
        try:
            return [x['prec'] for x in files]
        except KeyError:
            #backwards compatibility
            try:
                return [self.get_db_record()['prec']]
            except KeyError:
                return [self.prec]

    def max_available_prec(self):
        try:
            ps = self.available_precs()
        except IndexError:
            ps = [0]
        return max(ps)

    def delete_file_with_prec(self, prec):
        files = self.get_file_list({'prec': int(prec)})
        for f in files:
            self._files.delete(f['_id'])

    def max_cn(self):
        r"""
        The largest N for which we are sure that we can compute a(n) for all 1<=n<=N
        """
        return self.eigenvalues.max_coefficient_in_db()

        #if self.eigenvalues.primes()==[]:
        #    return 1
        #return max(self.eigenvalues.primes()) + 1

    def atkin_lehner_eigenvalue(self, Q):
        r""" Return the Atkin-Lehner eigenvalues of self
        corresponding to Q|N
        """
        if not (self.character.is_trivial() or self.character.order == 2):
            return None

        l = self.atkin_lehner_eigenvalues()
        return l.get(Q)

    def atkin_lehner_eigenvalues(self):
        r""" Return the Atkin-Lehner eigenvalues of self.

           EXAMPLES::

           sage: get_atkin_lehner_eigenvalues(4,14,0)
           '{2: 1, 14: 1, 7: 1}'
           sage: get_atkin_lehner_eigenvalues(4,14,1)
           '{2: -1, 14: 1, 7: -1}'

        """
        if not (self.character.is_trivial() or self.character.order == 2):
            return None

        if (len(self._atkin_lehner_eigenvalues.keys()) > 0):
            return self._atkin_lehner_eigenvalues

    def atkin_lehner_eigenvalues_for_all_cusps(self):
        r"""
        """
        self._atkin_lehner_eigenvalues_at_cusps = {}
        G = Gamma0(self.level)
        for c in Gamma0(self.level).cusps():
            aev = self.atkin_lehner_eigenvalues()
            if aev is None:
                self._atkin_lehner_eigenvalues_at_cusps = None
            else:
                for Q, ev in aev.items():
                    if G.are_equivalent(c, Q / self.level):
                        self._atkin_lehner_eigenvalues_at_cusps[c] = Q, ev
        return self._atkin_lehner_eigenvalues_at_cusps

    def url(self):
        return url_for('emf.render_elliptic_modular_forms',
                       level=self.level,
                       weight=self.weight,
                       character=self.character.number,
                       label=self.label)

    def create_small_record(self,
                            min_prec=10,
                            want_prec=100,
                            max_length=5242880,
                            max_height_qexp=default_max_height):
        ### creates a duplicate record (fs) of this webnewform
        ### with lower precision to load faster on the web
        ### we aim to have at most max_length bytes
        ### but at least min_prec coefficients and we desire to have want_prec
        if min_prec >= self.prec:
            raise ValueError("Need higher precision, self.prec = {}".format(
                self.prec))
        if not hasattr(self, '_file_record_length'):
            self.update_from_db()
        l = self._file_record_length

        if l > max_length or self.prec > want_prec:
            nl = float(l) / float(self.prec) * float(want_prec)
            if nl > max_length:
                prec = max([
                    floor(float(self.prec) / float(l) * float(max_length)),
                    min_prec
                ])
            else:
                prec = want_prec
            emf_logger.debug(
                "Creating a new record with prec = {}".format(prec))
            self.prec = prec
            include_coeffs = self.complexity_of_first_nonvanishing_coefficients(
            ) <= default_max_height
            if include_coeffs:
                self.q_expansion = self.q_expansion.truncate_powerseries(prec)
                self._coefficients = {
                    n: c
                    for n, c in self._coefficients.iteritems() if n < prec
                }
            else:
                self.q_expansion = self.q_expansion.truncate_powerseries(1)
                self._coefficients = {}
                self.prec = 0
                self.coefficient_field = NumberField(
                    self.absolute_polynomial,
                    names=str(self.coefficient_field.gen()))
            self._embeddings['values'] = {
                n: c
                for n, c in self._embeddings['values'].iteritems() if n < prec
            }
            self._embeddings['prec'] = prec
            self.save_to_db()

    def download_to_sage(self, prec=None):
        r"""
        Minimal version for now to download to sage.
        Does not work for high values of prec for large degree number fields (timeout).
        """
        if prec is None:
            prec = self.prec
        s = "var('x')\n"
        if self.base_ring.absolute_degree() > 1:
            s += "K.<{brgen}>=NumberField({crpol})\n".format(
                brgen=str(self.base_ring.gen()),
                crpol=self.base_ring.polynomial().change_variable_name('x'))
        if self.coefficient_field.is_absolute():
            if self.coefficient_field.absolute_degree() > 1:
                s += "L.<{cfgen}> = NumberField({cfpol})\n".format(
                    cfgen=str(self.coefficient_field.gen()),
                    cfpol=self.absolute_polynomial)
        elif self.coefficient_field.relative_degree() > 1:
            s += "y = polygen(K)\n"
            s += "L.<{cfgen}> = NumberField({cfpol})\n".format(
                cfgen=str(self.coefficient_field.gen()),
                cfpol=self.coefficient_field.relative_polynomial(
                ).change_variable_name('y'))
        s = s + "D = DirichletGroup({N})\n".format(N=self.level)
        s = s + "f = {{'coefficients': {coeffs}, 'level' : {level}, 'weight': {weight}, 'character': D.Element(D,vector({elt})), 'label': '{label}','dimension': {dim}, 'is_cm': {cm} , 'cm_discriminant': {cm_disc}, 'atkin_lehner': {al}, 'explicit_formulas': {ep}}}".format(
            coeffs=self.coefficients(range(prec)),
            level=self.level,
            weight=self.weight,
            elt=list(self.character.sage_character.element()),
            label=self.hecke_orbit_label,
            dim=self.dimension,
            cm=self.is_cm,
            cm_disc=None if not self.is_cm else self.cm_disc,
            al=self.atkin_lehner_eigenvalues(),
            ep=self.explicit_formulas)
        s = s + "\n\n#EXAMPLE\n"
        s = s + "#sage: f['coefficients'][7]\n#{}\n".format(
            self.coefficient(7))
        s = s + "#sage: f['character']\n#{}".format(
            self.character.sage_character)
        emf_logger.debug("Generated sage file for {}".format(
            self.hecke_orbit_label))
        return s

    def sage_newform_number(self):
        ##store this in the db!!
        return orbit_index_from_label(self.label)

    def make_code_snippets(self):
        self.code = deepcopy(self.parent.code)
        self.code['show'] = {'sage': ''}
        # Fill in placeholders for this specific newform:
        self.code['f']['sage'] = self.code['f']['sage'].format(
            newform_number=self.sage_newform_number())

        #self.code['f']['sage'] = self.code['f']['sage'].split("\n")
        # remove final empty line
        if len(self.code['f']['sage'][-1]) == 0:
            self.code['f']['sage'] = self.code['f']['sage'][:-1]

    def dump_coefficients(self, prec):
        if prec is None:
            prec = self.prec
        return dumps(self.coefficients(range(prec)))
Esempio n. 6
0
def find_newform_label(level, weight, character, field, aps):
    r"""
    Find the label of the newform orbit in the database which matches the input.
    
    INPUT:
    - 'level'     -- the level,
    - 'weight'    -- the weight
    - 'character' -- the character'
    - 'field'     -- the field, given in terms of a list of integer coefficients for the absolute polynomial  
    - 'aps'       -- the coefficients - given as a dictionary of lists giving the coefficient in terms of the generator of the field as above.
    
    
    EXAMPLE:
    
    sage: find_newform_label(9,16,1,[-119880,0,1],{2:[0,1]})
    u'e'
    sage: find_newform_label(71,2,1,[-3,-4,1,1],{3:[0,-1,0]})
    u'a'
    sage: find_newform_label(71,2,1,[-3,-4,1,1],{5:[5,1,-1]})
    u'a'
    
    NOTE: We implicitly assume that the input given is correct in the sense that 
        if there is a unique orbit with a coefficient field of the same degree as the input
        then we simply return that label. (This will save a lot of time...)
        
    """
    from web_modform_space import WebModFormSpace
    from sage.all import NumberField, QQ
    M = WebModFormSpace(level=level, weight=weight, character=character)
    if M.dimension_new_cusp_forms == 1:
        return 'a'
    orbits = M.hecke_orbits
    ## construct field from field input...
    if not isinstance(field, list):
        raise ValueError, "Need to give field as a list!"
    if not isinstance(aps, dict):
        raise ValueError, "Need to give aps as a dict!"
    if field == [1]:
        NF = QQ
    else:
        NF = NumberField(QQ['x'](field), names='x')
    degree_of_input = NF.absolute_degree()
    degrees = map(lambda x: x[1].coefficient_field_degree, orbits.viewitems())
    if degrees.count(degree_of_input) == 0:
        raise ValueError, "No newform with this level, weight, character and field degree!"
    if degrees.count(degree_of_input) == 1:
        ## If there is a unique mathcing field we return this orbit label.
        l = filter(lambda x: x[1].coefficient_field_degree == degree_of_input,
                   orbits.viewitems())
        return l[0][0]
    aps_input = {p: NF(a) for p, a in aps.viewitems()}
    possible_labels = orbits.keys()
    for label, f in orbits.viewitems():
        if f.coefficient_field_degree != degree_of_input:
            possible_labels.remove(label)
            continue
        try:
            for p, ap_input in aps_input.viewitems():
                if f.coefficient_field == QQ:
                    homs = [lambda x: x]
                else:
                    homs = f.coefficient(p).parent().Hom(NF)
                for h in homs:
                    ap = h(f.coefficient(p))
                    if ap_input != ap:
                        possible_labels.remove(label)
                        raise StopIteration
        except StopIteration:
            continue
    if len(possible_labels) > 1:
        raise ArithmeticError, "Not sufficient data (or errors) to determine orbit!"
    if len(possible_labels) == 0:
        raise ArithmeticError, "Not sufficient data (or errors) to determine orbit! NO matching label found!"
    return possible_labels[0]