示例#1
0
class WebDBDirichletOrbit(WebChar, WebDBDirichlet):
    """
    A class using data stored in the database. Currently, this is all Dirichlet
    characters with modulus up to 10000.
    """

    headers = ['Character']

    _keys = [ 'title', 'codelangs', 'type',
              'nf', 'nflabel', 'nfpol', 'modulus', 'modlabel',
              'number', 'numlabel', 'texname', 'codeinit',
              'symbol', 'codesymbol','headers',
              'previous', 'next', 'conductor',
              'condlabel', 'codecond',
              'isprimitive', 'codeisprimitive',
              'inducing','rowtruncate','ind_orbit_label',
              'indlabel', 'codeind', 'order', 'codeorder', 'parity', 'codeparity',
              'isreal', 'generators', 'codegenvalues', 'genvalues', 'logvalues',
              'groupelts', 'values', 'codeval', 'galoisorbit', 'codegaloisorbit',
              'valuefield', 'vflabel', 'vfpol', 'kerfield', 'kflabel',
              'kfpol', 'contents', 'properties', 'friends', 'coltruncate',
              'charsums', 'codegauss', 'codejacobi', 'codekloosterman',
              'orbit_label', 'orbit_index', 'isminimal', 'isorbit', 'degree']

    def __init__(self, **kwargs):
        self.type = "Dirichlet"
        self.isorbit = True
        self.modulus = kwargs.get('modulus', None)
        if self.modulus:
            self.modulus = int(self.modulus)
        self.modlabel = self.modulus
        self.number = kwargs.get('number', None)
        if self.number:
            self.number = int(self.number)
        self.numlabel = self.number
        if self.modulus:
            # Needed for Gauss sums, etc
            self.H = PariConreyGroup(self.modulus)
            if self.number:
                self.chi = ConreyCharacter(self.modulus, self.number)
        self.codelangs = ('pari', 'sage')
        self.orbit_label = kwargs.get('orbit_label', None)  # this is what the user inserted, so might be banana
        self.label = "{}.{}".format(self.modulus, self.orbit_label)
        self.orbit_data = self.get_orbit_data(self.orbit_label)  # this is the meat
        self.maxrows = 30
        self.rowtruncate = False
        self._set_galoisorbit(self.orbit_data)
        self.maxcols = 10
        self._contents = None
        self._set_groupelts()

    @lazy_attribute
    def title(self):
        return "Dirichlet character orbit {}.{}".format(self.modulus, self.orbit_label)

    def _set_galoisorbit(self, orbit_data):
        if self.modulus == 1:
            self.galoisorbit = [self._char_desc(1, mod=1,prim=True)]
            return

        upper_limit = min(self.maxrows + 1, self.degree + 1)

        if self.maxrows < self.degree + 1:
            self.rowtruncate = True
        self.galorbnums = orbit_data['galois_orbit'][:upper_limit]
        self.galoisorbit = list(
            self._char_desc(num, prim=orbit_data['is_primitive']) for num in self.galorbnums
        )

    def get_orbit_data(self, orbit_label):
        mod_and_label = "{}.{}".format(self.modulus, orbit_label)
        orbit_data =  db.char_dir_orbits.lucky(
            {'modulus': self.modulus, 'label': mod_and_label}
        )

        if orbit_data is None:
            raise ValueError

        # Since we've got this, might as well set a bunch of stuff

        self.conductor = orbit_data['conductor']
        self.order = orbit_data['order']
        self.degree = orbit_data['char_degree']
        self.isprimitive = bool_string(orbit_data['is_primitive'])
        self.isminimal = bool_string(orbit_data['is_minimal'])
        self.parity = parity_string(int(orbit_data['parity']))
        self._set_kernel_field_poly(orbit_data)
        self.ind_orbit_label = cremona_letter_code(int(orbit_data['prim_orbit_index']) - 1)
        self.inducing = "{}.{}".format(self.conductor, self.ind_orbit_label)
        return orbit_data

    def _set_kernel_field_poly(self, orbit_data):
        if 'kernel_field_poly' in orbit_data.keys():
            self.kernel_field_poly = orbit_data['kernel_field_poly']
        else:
            self.kernel_field_poly = None

    @lazy_attribute
    def friends(self):
        friendlist = []
        cglink = url_character(type=self.type, modulus=self.modulus)
        friendlist.append( ("Character group", cglink) )
        if self.type == "Dirichlet" and self.isprimitive == bool_string(True):
            friendlist.append(
                ('Sato-Tate group', '/SatoTateGroup/0.1.%d' % self.order)
            )
        if len(self.vflabel) > 0:
            friendlist.append( ("Value field", '/NumberField/' + self.vflabel) )
        if self.symbol_numerator():
            if self.symbol_numerator() > 0:
                assoclabel = '2.2.%d.1' % self.symbol_numerator()
            else:
                assoclabel = '2.0.%d.1' % -self.symbol_numerator()
            friendlist.append(("Associated quadratic field", '/NumberField/' + assoclabel))

        if self.type == "Dirichlet" and self.isprimitive == bool_string(False):
            friendlist.append(('Primitive orbit '+self.inducing,
                url_for('characters.render_Dirichletwebpage', modulus=self.conductor, orbit_label=self.ind_orbit_label)))

        return friendlist

    @lazy_attribute
    def contents(self):
        if self._contents is None:
            self._contents = []
            self._fill_contents()
        return self._contents

    def _fill_contents(self):
        for c in self.galorbnums:
            self.add_row(c)

    def add_row(self, c):
        """
        Add a row to _contents for display on the webpage.
        Each row of content takes the form
            character_name, (header..data), (several..values)
        where `header..data` is expected to be a tuple of length the same
        size as `len(headers)`, and given in the same order as in `headers`,
        and where `several..values` are the values of the character
        on self.groupelts, in order.
        """
        mod = self.modulus
        num = c
        valuepairs = db.char_dir_values.lookup(
            "{}.{}".format(mod, num),
            projection='values'
        )
        prim = self.isprimitive == bool_string(True)
        self._contents.append((
            self._char_desc(num, mod=mod, prim=prim),
            self._determine_values(valuepairs, self.order)
        ))

    def symbol_numerator(self):
        """
        chi is equal to a kronecker symbol if and only if it is real
        """
        if self.order != 2:
            return None
        if self.parity == parity_string(-1):
            return symbol_numerator(self.conductor, True)
        return symbol_numerator(self.conductor, False)

    @lazy_attribute
    def symbol(self):
        return kronecker_symbol(self.symbol_numerator())

    @lazy_attribute
    def codesymbol(self):
        m = self.symbol_numerator()
        if m:
            return { 'sage': 'kronecker_character(%i)'%m,
                     'pari': 'znchartokronecker(g,chi)'
                     }
        return None

    def _determine_values(self, valuepairs, order):
        """
        Translate the db's values into the actual values.
        """
        raw_values = [int(v) for g, v in valuepairs]
        values = [
            self._tex_value(v, order, texify=True) for v in raw_values
        ]
        return values

    def _set_groupelts(self):
        if self.modulus == 1:
            self.groupelts = [1]
        else:
            db_data = db.char_dir_values.lookup(
                "{}.{}".format(self.modulus, 1)
            )
            valuepairs = db_data['values']
            self.groupelts = [int(g) for g, v in valuepairs]
            self.groupelts[0] = -1

    @lazy_attribute
    def codeinit(self):
        self.exnum = self.galorbnums[0]
        self.exchi = ConreyCharacter(self.modulus, self.exnum)

        values_gens = db.char_dir_values.lookup(
            "{}.{}".format(self.modulus, self.exnum),
            projection='values_gens'
        )

        vals = [int(v) for g, v in values_gens]
        sage_zeta_order = self.exchi.sage_zeta_order(self.order)
        self._genvalues_for_code = get_sage_genvalues(self.modulus,
                                    self.order, vals, sage_zeta_order)

        return {
            'sage': [
                'from sage.modular.dirichlet import DirichletCharacter',
                'H = DirichletGroup({}, base_ring=CyclotomicField({}))'.format(
                    self.modulus, sage_zeta_order),
                'M = H._module',
                'chi = DirichletCharacter(H, M([{}]))'.format(
                    ','.join(str(val) for val in self._genvalues_for_code)
                ),
                'chi.galois_orbit()'
            ],
            'pari': [
                '[g,chi] = znchar(Mod(%i,%i))' % (self.exnum, self.modulus),
                'order = charorder(g,chi)',
                '[ charpow(g,chi, k % order) | k <-[1..order-1], gcd(k,order)==1 ]'
            ]
        }

    @lazy_attribute
    def codeisprimitive(self):
        return { 'sage': 'chi.is_primitive()',
                 'pari': '#znconreyconductor(g,chi)==1' }

    @lazy_attribute
    def codecond(self):
        return { 'sage': 'chi.conductor()',
                 'pari': 'znconreyconductor(g,chi)' }

    @lazy_attribute
    def codeparity(self):
        return { 'sage': 'chi.is_odd()',
                 'pari': 'zncharisodd(g,chi)' }
示例#2
0
class WebDBDirichlet(WebDirichlet):
    """
    A base class using data stored in the database. Currently this is all
    Dirichlet characters with modulus up to 10000.
    """
    def __init__(self, **kwargs):
        self.type = "Dirichlet"
        self.modulus = kwargs.get('modulus', None)
        if self.modulus:
            self.modulus = int(self.modulus)
        self.modlabel = self.modulus
        self.number = kwargs.get('number', None)
        if self.number:
            self.number = int(self.number)
        self.numlabel = self.number
        if self.modulus:
            # Needed for Gauss sums, etc
            self.H = PariConreyGroup(self.modulus)
            if self.number:
                self.chi = ConreyCharacter(self.modulus, self.number)
        self.maxcols = 30
        self.codelangs = ('pari', 'sage')
        self._compute()

    @lazy_attribute
    def texname(self):
        return self.char2tex(self.modulus, self.number)

    def _compute(self):
        self._populate_from_db()

    def _populate_from_db(self):
        values_data = db.char_dir_values.lookup("{}.{}".format(
            self.modulus, self.number))

        self.orbit_index = int(values_data['orbit_label'].partition('.')[-1])
        # The -1 in the line below is because labels index at 1, while
        # the Cremona letter code indexes at 0
        self.orbit_label = cremona_letter_code(self.orbit_index - 1)
        self.order = int(values_data['order'])
        self.indlabel = int(values_data['prim_label'].partition('.')[-1])
        self._set_values_and_groupelts(values_data)
        self._set_generators_and_genvalues(values_data)

        orbit_data = db.char_dir_orbits.lucky({
            'modulus': self.modulus,
            'orbit_index': self.orbit_index
        })

        self.conductor = int(orbit_data['conductor'])
        self._set_isprimitive(orbit_data)
        self._set_isminimal(orbit_data)
        self._set_parity(orbit_data)
        self._set_galoisorbit(orbit_data)
        self._set_kernel_field_poly(orbit_data)

    def _set_generators_and_genvalues(self, values_data):
        """
        The char_dir_values db collection contains `values_gens`, which
        contains the generators for the unit group U(modulus) and the values
        of the character on those generators.
        """
        valuepairs = values_data['values_gens']
        if self.modulus == 1:
            self.generators = r"\(1\)"
            self.genvalues = r"\(1\)"
        else:
            gens = [int(g) for g, v in valuepairs]
            vals = [int(v) for g, v in valuepairs]
            self._genvalues_for_code = get_sage_genvalues(
                self.modulus, self.order, vals,
                self.chi.sage_zeta_order(self.order))
            self.generators = self.textuple([str(g) for g in gens])
            self.genvalues = self.textuple([self._tex_value(v) for v in vals])

    def _set_values_and_groupelts(self, values_data):
        """
        The char_dir_values db collection contains `values`, which contains
        several group elements and the corresponding values.
        """
        valuepairs = values_data['values']
        if self.modulus == 1:
            self.groupelts = [1]
            self.values = [r"\(1\)"]
        else:
            self.groupelts = [int(g) for g, v in valuepairs]
            self.groupelts[0] = -1
            raw_values = [int(v) for g, v in valuepairs]
            self.values = [
                self._tex_value(v, self.order, texify=True) for v in raw_values
            ]

    def _tex_value(self, numer, denom=None, texify=False):
        r"""
        Formats the number e**(2 pi i * numer / denom), detecting if this
        simplifies to +- 1 or +- i.

        Surround the output i MathJax `\(..\)` tags if `texify` is True.
        `denom` defaults to self.order.
        """
        if not denom:
            denom = self.order

        g = gcd(numer, denom)
        if g > 1:
            numer = numer // g
            denom = denom // g

        # Reduce mod the denominator
        numer = (numer % denom)

        if denom == 1:
            ret = '1'
        elif (numer % denom) == 0:
            ret = '1'
        elif numer == 1 and denom == 2:
            ret = '-1'
        elif numer == 1 and denom == 4:
            ret = 'i'
        elif numer == 3 and denom == 4:
            ret = '-i'
        else:
            ret = r"e\left(\frac{%s}{%s}\right)" % (numer, denom)
        if texify:
            return r"\({}\)".format(ret)
        else:
            return ret

    def _set_isprimitive(self, orbit_data):
        self.isprimitive = bool_string(orbit_data['is_primitive'])

    def _set_isminimal(self, orbit_data):
        self.isminimal = bool_string(orbit_data['is_minimal'])

    def _set_parity(self, orbit_data):
        self.parity = parity_string(int(orbit_data['parity']))

    def _set_galoisorbit(self, orbit_data):
        if self.modulus == 1:
            self.galoisorbit = [self._char_desc(1, mod=1, prim=True)]
            return
        upper_limit = min(200, self.order + 1)
        orbit = orbit_data['galois_orbit'][:upper_limit]
        self.galoisorbit = list(
            self._char_desc(num, prim=self.isprimitive) for num in orbit)

    def _set_kernel_field_poly(self, orbit_data):
        if 'kernel_field_poly' in orbit_data.keys():
            self.kernel_field_poly = orbit_data['kernel_field_poly']
        else:
            self.kernel_field_poly = None