Example #1
0
def field_pretty(label):
    d, r, D, i = label.split('.')
    if d == '1':  # Q
        return r'\(\Q\)'
    if d == '2':  # quadratic field
        D = ZZ(int(D))
        if r == '0':
            D = -D
        # Don't prettify invalid quadratic field labels
        if not is_fundamental_discriminant(D):
            return label
        return r'\(\Q(\sqrt{' + str(D if D%4 else D/4) + r'}) \)'
    if label in cycloinfo:
        return r'\(\Q(\zeta_{%d})\)' % cycloinfo[label]
    if d == '4':
        wnf = WebNumberField(label)
        subs = wnf.subfields()
        if len(subs)==3: # only for V_4 fields
            subs = [wnf.from_coeffs(string2list(str(z[0]))) for z in subs]
            # Abort if we don't know one of these fields
            if not any(z._data is None for z in subs):
                labels = [str(z.get_label()) for z in subs]
                labels = [z.split('.') for z in labels]
                # extract abs disc and signature to be good for sorting
                labels = [[integer_squarefree_part(ZZ(z[2])), - int(z[1])] for z in labels]
                labels.sort()
                # put in +/- sign
                labels = [z[0]*(-1)**(1+z[1]/2) for z in labels]
                labels = ['i' if z == -1 else r'\sqrt{%d}'% z for z in labels]
                return r'\(\Q(%s, %s)\)'%(labels[0],labels[1])
    if label in rcycloinfo:
        return r'\(\Q(\zeta_{%d})^+\)' % rcycloinfo[label]
    return label
Example #2
0
    def make_E(self):
        #print("Creating ECNF object for {}".format(self.label))
        #sys.stdout.flush()
        K = self.field.K()
        Kgen = str(K.gen())

        # a-invariants
        # NB Here we construct the ai as elements of K, which are used as follows:
        # (1) to compute the model discriminant (if not stored)
        # (2) to compute the latex equation (if not stored)
        # (3) to compute the plots under real embeddings of K
        # Of these, (2) is not needed and (1) will soon be obsolete;
        #  for (3) it would be possible to rewrite the function EC_nf_plot() not to need this.
        # Then we might also be able to avoid constructing the field K also.

        self.ainvs = parse_ainvs(K, self.ainvs)
        self.numb = str(self.number)

        # Conductor, discriminant, j-invariant

        self.cond_norm = web_latex(self.conductor_norm)

        Dnorm = self.normdisc
        self.disc = pretty_ideal(Kgen, self.disc)

        local_data = self.local_data
        local_data.sort(key=lambda ld: ld['normp'])

        badprimes = [
            pretty_ideal(Kgen, ld['p'], enclose=False) for ld in local_data
        ]
        badnorms = [ld['normp'] for ld in local_data]
        disc_ords = [ld['ord_disc'] for ld in local_data]
        mindisc_ords = [ld['ord_disc'] for ld in local_data]
        cond_ords = [ld['ord_cond'] for ld in local_data]

        if self.conductor_norm == 1:
            self.cond = r"\((1)\)"
            self.fact_cond = self.cond
            self.fact_cond_norm = '1'
        else:
            self.cond = pretty_ideal(Kgen, self.conductor_ideal)
            self.fact_cond = latex_factorization(badprimes, cond_ords)
            self.fact_cond_norm = latex_factorization(badnorms, cond_ords)

        # Assumption: the curve models stored in the database are
        # either global minimal models or minimal at all but one
        # prime, so the list here has length 0 or 1:

        self.is_minimal = (len(self.non_min_p) == 0)
        self.has_minimal_model = self.is_minimal

        if not self.is_minimal:
            non_min_p = self.non_min_p[0]
            self.non_min_prime = pretty_ideal(Kgen, non_min_p)
            ip = [ld['p'] for ld in local_data].index(non_min_p)
            disc_ords[ip] += 12
            Dnorm_factor = local_data[ip]['normp']**12

        self.disc_norm = web_latex(Dnorm)
        signDnorm = 1 if Dnorm > 0 else -1
        if Dnorm in [1, -1]:  # since the factorization of (1) displays as "1"
            self.fact_disc = self.disc
            self.fact_disc_norm = str(Dnorm)
        else:
            self.fact_disc = latex_factorization(badprimes, disc_ords)
            self.fact_disc_norm = latex_factorization(badnorms,
                                                      disc_ords,
                                                      sign=signDnorm)

        if self.is_minimal:
            Dmin_norm = Dnorm
            self.mindisc = self.disc
        else:
            Dmin_norm = Dnorm // Dnorm_factor
            self.mindisc = pretty_ideal(Kgen, self.minD)

        self.mindisc_norm = web_latex(Dmin_norm)
        if Dmin_norm in [1,
                         -1]:  # since the factorization of (1) displays as "1"
            self.fact_mindisc = self.mindisc
            self.fact_mindisc_norm = self.mindisc_norm
        else:
            self.fact_mindisc = latex_factorization(badprimes, mindisc_ords)
            self.fact_mindisc_norm = latex_factorization(badnorms,
                                                         mindisc_ords,
                                                         sign=signDnorm)

        j = self.field.parse_NFelt(self.jinv)
        self.j = web_latex(j)
        self.fact_j = None
        # See issue 1258: some j factorizations work but take too long
        # (e.g. EllipticCurve/6.6.371293.1/1.1/a/1).  Note that we do
        # store the factorization of the denominator of j and display
        # that, which is the most interesting part.

        # When the equation is stored in the database as a latex string,
        # it may have extraneous double quotes at beginning and
        # end, which we fix here.  We also strip out initial \( and \)
        # (if present) which are added in the template.
        try:
            self.equation = self.equation.replace('"', '').replace(
                r'\\(', '').replace(r'\\)', '')
        except AttributeError:
            self.equation = latex_equation(self.ainvs)

        # Images of Galois representations

        if not hasattr(self, 'galois_images'):
            #print "No Galois image data"
            self.galois_images = "?"
            self.nonmax_primes = "?"
            self.galois_data = []
        else:
            self.galois_data = [{
                'p': p,
                'image': im
            } for p, im in zip(self.nonmax_primes, self.galois_images)]

        # CM and End(E)
        self.cm_bool = "no"
        self.End = r"\(\Z\)"
        self.rational_cm = self.cm_type > 0
        if self.cm:
            self.cm_sqf = integer_squarefree_part(ZZ(self.cm))
            self.cm_bool = r"yes (\(%s\))" % self.cm
            if self.cm % 4 == 0:
                d4 = ZZ(self.cm) // 4
                self.End = r"\(\Z[\sqrt{%s}]\)" % (d4)
            else:
                self.End = r"\(\Z[(1+\sqrt{%s})/2]\)" % self.cm

        # Galois images in CM case:
        if self.cm and self.galois_images != '?':
            self.cm_ramp = [
                p for p in ZZ(self.cm).support() if p not in self.nonmax_primes
            ]
            self.cm_nramp = len(self.cm_ramp)
            if self.cm_nramp == 1:
                self.cm_ramp = self.cm_ramp[0]
            else:
                self.cm_ramp = ", ".join([str(p) for p in self.cm_ramp])

        # Sato-Tate:
        self.ST = st_display_knowl('1.2.A.1.1a' if not self.cm_type else (
            '1.2.B.2.1a' if self.cm_type < 0 else '1.2.B.1.1a'))

        # Q-curve / Base change
        try:
            qc = self.q_curve
            if qc is True:
                self.qc = "yes"
            elif qc is False:
                self.qc = "no"
            else:  # just in case
                self.qc = "not determined"
        except AttributeError:
            self.qc = "not determined"

        # Torsion
        self.ntors = web_latex(self.torsion_order)
        self.tr = len(self.torsion_structure)
        if self.tr == 0:
            self.tor_struct_pretty = "trivial"
        if self.tr == 1:
            self.tor_struct_pretty = r"\(\Z/%s\Z\)" % self.torsion_structure[0]
        if self.tr == 2:
            self.tor_struct_pretty = r"\(\Z/%s\Z\times\Z/%s\Z\)" % tuple(
                self.torsion_structure)

        self.torsion_gens = [
            web_point(parse_point(K, P)) for P in self.torsion_gens
        ]

        # BSD data
        #
        # We divide into 3 cases, based on rank_bounds [lb,ub],
        # analytic_rank ar, (lb=ngens always).  The flag
        # self.bsd_status is set to one of the following:
        #
        # "unconditional"
        #     lb=ar=ub: we always have reg but in some cases over sextic fields we do not have omega, Lvalue, sha.
        #     i.e. [lb,ar,ub] = [r,r,r]
        #
        # "conditional"
        #     lb=ar<ub: we always have reg but in some cases over sextic fields we do not have omega, Lvalue, sha.
        #     e.g. [lb,ar,ub] = [0,0,2], [1,1,3]
        #
        # "missing_gens"
        #     lb<ar<=ub
        #     e.g. [lb,ar,ub] = [0,1,1], [0,2,2], [1,2,2], [0,1,3]
        #
        # "incomplete"
        #     ar not computed.  (We can always set lb=0, ub=Infinity.)

        # Rank and bounds
        try:
            self.rk = web_latex(self.rank)
        except AttributeError:
            self.rank = None
            self.rk = "not available"

        try:
            self.rk_lb, self.rk_ub = self.rank_bounds
        except AttributeError:
            self.rk_lb = 0
            self.rk_ub = Infinity
            self.rank_bounds = "not available"

        # Analytic rank
        try:
            self.ar = web_latex(self.analytic_rank)
        except AttributeError:
            self.analytic_rank = None
            self.ar = "not available"

        # for debugging:
        assert self.rk == "not available" or (self.rk_lb == self.rank
                                              and self.rank == self.rk_ub)
        assert self.ar == "not available" or (self.rk_lb <= self.analytic_rank
                                              and
                                              self.analytic_rank <= self.rk_ub)

        self.bsd_status = "incomplete"
        if self.analytic_rank is not None:
            if self.rk_lb == self.rk_ub:
                self.bsd_status = "unconditional"
            elif self.rk_lb == self.analytic_rank:
                self.bsd_status = "conditional"
            else:
                self.bsd_status = "missing_gens"

        # Regulator only in conditional/unconditional cases, or when we know the rank:
        if self.bsd_status in ["conditional", "unconditional"]:
            if self.ar == 0:
                self.reg = web_latex(1)  # otherwise we only get 1.00000...
            else:
                try:
                    self.reg = web_latex(self.reg)
                except AttributeError:
                    self.reg = "not available"
        elif self.rk != "not available":
            self.reg = web_latex(self.reg) if self.rank else web_latex(1)
        else:
            self.reg = "not available"

        # Generators
        try:
            self.gens = [web_point(parse_point(K, P)) for P in self.gens]
        except AttributeError:
            self.gens = []

        # Global period
        try:
            self.omega = web_latex(self.omega)
        except AttributeError:
            self.omega = "not available"

        # L-value
        try:
            r = int(self.analytic_rank)
            # lhs = "L(E,1) = " if r==0 else "L'(E,1) = " if r==1 else "L^{{({})}}(E,1)/{}! = ".format(r,r)
            self.Lvalue = web_latex(self.Lvalue)
        except (TypeError, AttributeError):
            self.Lvalue = "not available"

        # Tamagawa product
        tamagawa_numbers = [ZZ(_ld['cp']) for _ld in self.local_data]
        cp_fac = [cp.factor() for cp in tamagawa_numbers]
        cp_fac = [
            latex(cp) if len(cp) < 2 else '(' + latex(cp) + ')'
            for cp in cp_fac
        ]
        if len(cp_fac) > 1:
            self.tamagawa_factors = r'\cdot'.join(cp_fac)
        else:
            self.tamagawa_factors = None
        self.tamagawa_product = web_latex(prod(tamagawa_numbers, 1))

        # Analytic Sha
        try:
            self.sha = web_latex(self.sha) + " (rounded)"
        except AttributeError:
            self.sha = "not available"

        # Local data

        # The Kodaira symbol is stored as an int in pari encoding. The
        # conversion to latex must take into account the bug (in Sage
        # 9.2) for I_m^* when m has more than one digit.

        def latex_kod(kod):
            return latex(
                KodairaSymbol(kod)) if kod > -14 else 'I_{%s}^{*}' % (-kod - 4)

        for P, NP, ld in zip(badprimes, badnorms, local_data):
            ld['p'] = P
            ld['norm'] = NP
            ld['kod'] = latex_kod(ld['kod'])

        # URLs of self and related objects:
        self.urls = {}
        # It's useful to be able to use this class out of context, when calling url_for will fail:
        try:
            self.urls['curve'] = url_for(".show_ecnf",
                                         nf=self.field_label,
                                         conductor_label=quote(
                                             self.conductor_label),
                                         class_label=self.iso_label,
                                         number=self.number)
        except RuntimeError:
            return
        self.urls['class'] = url_for(".show_ecnf_isoclass",
                                     nf=self.field_label,
                                     conductor_label=quote(
                                         self.conductor_label),
                                     class_label=self.iso_label)
        self.urls['conductor'] = url_for(".show_ecnf_conductor",
                                         nf=self.field_label,
                                         conductor_label=quote(
                                             self.conductor_label))
        self.urls['field'] = url_for(".show_ecnf1", nf=self.field_label)

        # Isogeny information

        self.one_deg = ZZ(self.class_deg).is_prime()
        isodegs = [str(d) for d in self.isodeg if d > 1]
        if len(isodegs) < 3:
            self.isodeg = " and ".join(isodegs)
        else:
            self.isodeg = " and ".join([", ".join(isodegs[:-1]), isodegs[-1]])

        sig = self.signature
        totally_real = sig[1] == 0
        imag_quadratic = sig == [0, 1]

        if totally_real:
            self.hmf_label = "-".join(
                [self.field.label, self.conductor_label, self.iso_label])
            self.urls['hmf'] = url_for('hmf.render_hmf_webpage',
                                       field_label=self.field.label,
                                       label=self.hmf_label)
            lfun_url = url_for("l_functions.l_function_ecnf_page",
                               field_label=self.field_label,
                               conductor_label=self.conductor_label,
                               isogeny_class_label=self.iso_label)
            origin_url = lfun_url.lstrip('/L/').rstrip('/')
            if sig[0] <= 2 and db.lfunc_instances.exists({'url': origin_url}):
                self.urls['Lfunction'] = lfun_url
            elif self.abs_disc**2 * self.conductor_norm < 70000:
                # we shouldn't trust the Lfun computed on the fly for large conductor
                self.urls['Lfunction'] = url_for(
                    "l_functions.l_function_hmf_page",
                    field=self.field_label,
                    label=self.hmf_label,
                    character='0',
                    number='0')

        if imag_quadratic:
            self.bmf_label = "-".join(
                [self.field.label, self.conductor_label, self.iso_label])
            self.bmf_url = url_for('bmf.render_bmf_webpage',
                                   field_label=self.field_label,
                                   level_label=self.conductor_label,
                                   label_suffix=self.iso_label)
            lfun_url = url_for("l_functions.l_function_ecnf_page",
                               field_label=self.field_label,
                               conductor_label=self.conductor_label,
                               isogeny_class_label=self.iso_label)
            origin_url = lfun_url.lstrip('/L/').rstrip('/')
            if db.lfunc_instances.exists({'url': origin_url}):
                self.urls['Lfunction'] = lfun_url

        # most of this code is repeated in isog_class.py
        # and should be refactored
        self.friends = []
        self.friends += [('Isogeny class ' + self.short_class_label,
                          self.urls['class'])]
        self.friends += [('Twists',
                          url_for('ecnf.index',
                                  field=self.field_label,
                                  jinv=rename_j(j)))]
        if totally_real and 'Lfunction' not in self.urls:
            self.friends += [('Hilbert modular form ' + self.hmf_label,
                              self.urls['hmf'])]

        if imag_quadratic:
            if "CM" in self.label:
                self.friends += [('Bianchi modular form is not cuspidal', '')]
            elif 'Lfunction' not in self.urls:
                if db.bmf_forms.label_exists(self.bmf_label):
                    self.friends += [
                        ('Bianchi modular form %s' % self.bmf_label,
                         self.bmf_url)
                    ]
                else:
                    self.friends += [
                        ('(Bianchi modular form %s)' % self.bmf_label, '')
                    ]

        self.properties = [('Label', self.label)]

        # Plot
        if K.signature()[0]:
            self.plot = encode_plot(
                EC_nf_plot(K, self.ainvs, self.field.generator_name()))
            self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format(
                self.plot)
            self.properties += [(None, self.plot_link)]
        self.properties += [('Base field', self.field.field_pretty())]

        self.properties += [
            ('Conductor', self.cond),
            ('Conductor norm', self.cond_norm),
            # See issue #796 for why this is hidden (can be very large)
            # ('j-invariant', self.j),
            ('CM', self.cm_bool)
        ]

        if self.base_change:
            self.base_change = [
                lab for lab in self.base_change if '?' not in lab
            ]
            self.properties += [
                ('Base change',
                 'yes: %s' % ','.join([str(lab) for lab in self.base_change]))
            ]
        else:
            self.base_change = []  # in case it was False instead of []
            self.properties += [('Base change', 'no')]
        self.properties += [('Q-curve', self.qc)]

        r = self.rk
        if r == "?":
            r = self.rk_bnds
        self.properties += [
            ('Torsion order', self.ntors),
            ('Rank', r),
        ]

        for E0 in self.base_change:
            self.friends += [(r'Base change of %s /\(\Q\)' % E0,
                              url_for("ec.by_ec_label", label=E0))]

        self._code = None  # will be set if needed by get_code()

        self.downloads = [('All stored data to text',
                           url_for(".download_ECNF_all",
                                   nf=self.field_label,
                                   conductor_label=quote(self.conductor_label),
                                   class_label=self.iso_label,
                                   number=self.number))]
        for lang in [["Magma", "magma"], ["GP", "gp"], ["SageMath", "sage"]]:
            self.downloads.append(
                ('Code to {}'.format(lang[0]),
                 url_for(".ecnf_code_download",
                         nf=self.field_label,
                         conductor_label=quote(self.conductor_label),
                         class_label=self.iso_label,
                         number=self.number,
                         download_type=lang[1])))
        self.downloads.append(
            ('Underlying data', url_for(".ecnf_data", label=self.label)))

        if 'Lfunction' in self.urls:
            Lfun = get_lfunction_by_url(
                self.urls['Lfunction'].lstrip('/L').rstrip('/'),
                projection=['degree', 'trace_hash', 'Lhash'])
            if Lfun is None:
                self.friends += [('L-function not available', "")]
            else:
                instances = get_instances_by_Lhash_and_trace_hash(
                    Lfun['Lhash'], Lfun['degree'], Lfun.get('trace_hash'))
                exclude = {
                    elt[1].rstrip('/').lstrip('/')
                    for elt in self.friends if elt[1]
                }
                self.friends += names_and_urls(instances, exclude=exclude)
                self.friends += [('L-function', self.urls['Lfunction'])]
        else:
            self.friends += [('L-function not available', "")]
Example #3
0
    def make_curve(self):
        data = self.data = {}
        lmfdb_label = self.lmfdb_label

        # Some data fields of self are just those from the database.
        # These only need some reformatting.

        data['ainvs'] =  self.ainvs
        data['conductor'] = N = self.conductor
        data['j_invariant'] = QQ(tuple(self.jinv))
        data['j_inv_factor'] = latex(0)
        if data['j_invariant']: # don't factor 0
            data['j_inv_factor'] = latex(data['j_invariant'].factor())
        data['j_inv_latex'] = web_latex(data['j_invariant'])
        data['faltings_height'] = RR(self.faltings_height)
        data['stable_faltings_height'] = RR(self.stable_faltings_height)

        # retrieve local reduction data from table ec_localdata:

        self.local_data = local_data = list(db.ec_localdata.search({"lmfdb_label": lmfdb_label}))
        for ld in local_data:
            if ld['kodaira_symbol'] <= -14:
                # Work around bug in Sage's latex
                ld['kod'] = 'I_{%s}^{*}' % (-ld['kodaira_symbol'] - 4)
            else:
                ld['kod'] = latex(KodairaSymbol(ld['kodaira_symbol']))

        Nfac = Factorization([(ZZ(ld['prime']),ld['conductor_valuation']) for ld in local_data])
        Dfac = Factorization([(ZZ(ld['prime']),ld['discriminant_valuation']) for ld in local_data], unit=ZZ(self.signD))
        data['disc_factor'] = latex(Dfac)
        data['disc'] = D = Dfac.value()
        data['cond_factor'] =latex(Nfac)
        data['disc_latex'] = web_latex(D)
        data['cond_latex'] = web_latex(N)

        # retrieve data about MW rank, generators, heights and
        # torsion, leading term of L-function & other BSD data from
        # table ec_mwbsd:

        self.make_mwbsd()

        # latex equation:

        latexeqn = latex_equation(self.ainvs)
        data['equation'] = raw_typeset(unlatex(latexeqn), latexeqn)

        # minimal quadratic twist:

        data['minq_D'] = minqD = self.min_quad_twist_disc
        data['minq_label'] = db.ec_curvedata.lucky({'ainvs': self.min_quad_twist_ainvs},
                                                   projection = 'lmfdb_label' if self.label_type=='LMFDB' else 'Clabel')
        data['minq_info'] = '(itself)' if minqD==1 else '(by {})'.format(minqD)

        # modular degree:

        try:
            data['degree'] = ZZ(self.degree) # convert None to 0
        except AttributeError: # if not computed, db has Null and the attribute is missing
            data['degree'] = 0 # invalid, but will be displayed nicely

        # coefficients of modular form / L-series:

        classdata = db.ec_classdata.lookup(self.lmfdb_iso)
        data['an'] = classdata['anlist']
        data['ap'] = classdata['aplist']

        # mod-p Galois images:

        data['galois_data'] = list(db.ec_galrep.search({'lmfdb_label': lmfdb_label}))
        # CM and Endo ring:

        data['CMD'] = self.cm
        data['CM'] = "no"
        data['EndE'] = r"\(\Z\)"
        if self.cm:
            data['cm_ramp'] = [p for p in ZZ(self.cm).support() if p not in self.nonmax_primes]
            data['cm_nramp'] = len(data['cm_ramp'])
            if data['cm_nramp']==1:
                data['cm_ramp'] = data['cm_ramp'][0]
            else:
                data['cm_ramp'] = ", ".join(str(p) for p in data['cm_ramp'])
            data['cm_sqf'] = integer_squarefree_part(ZZ(self.cm))

            data['CM'] = r"yes (\(D=%s\))" % data['CMD']
            if data['CMD']%4==0:
                d4 = ZZ(data['CMD'])//4
                data['EndE'] = r"\(\Z[\sqrt{%s}]\)" % d4
            else:
                data['EndE'] = r"\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD']
            data['ST'] = display_knowl('st_group.data', title=r"$N(\mathrm{U}(1))$", kwargs={'label':'1.2.B.2.1a'})
        else:
            data['ST'] = display_knowl('st_group.data', title=r"$\mathrm{SU}(2)$", kwargs={'label':'1.2.A.1.1a'})
        # Isogeny degrees:

        cond, iso, num = split_lmfdb_label(lmfdb_label)
        self.class_deg  = classdata['class_deg']
        self.one_deg = ZZ(self.class_deg).is_prime()
        isodegs = [str(d) for d in self.isogeny_degrees if d>1]
        if len(isodegs)<3:
            data['isogeny_degrees'] = " and ".join(isodegs)
        else:
            data['isogeny_degrees'] = " and ".join([", ".join(isodegs[:-1]),isodegs[-1]])

        # Optimality

        # The optimal curve in the class is the curve whose Cremona
        # label ends in '1' except for '990h' which was labelled
        # wrongly long ago. This is proved for N up to
        # OPTIMALITY_BOUND (and when there is only one curve in an
        # isogeny class, obviously) and expected for all N.

        # Column 'optimality' is 1 for certainly optimal curves, 0 for
        # certainly non-optimal curves, and is n>1 if the curve is one
        # of n in the isogeny class which may be optimal given current
        # knowledge.

        # Column "manin_constant' is the correct Manin constant
        # assuming that the optimal curve in the class is known, or
        # otherwise if it is the curve with (Cremona) number 1.

        # The code here allows us to update the display correctly by
        # changing one line in this file (defining OPTIMALITY_BOUND)
        # without changing the data.

        data['optimality_bound'] = OPTIMALITY_BOUND
        self.cremona_bound = CREMONA_BOUND
        if N<CREMONA_BOUND:
            data['manin_constant'] = self.manin_constant # (conditional on data['optimality_known'])
        else:
            data['manin_constant'] = 0 # (meaning not available)

        if N<OPTIMALITY_BOUND:

            data['optimality_code'] = int(self.Cnumber == (3 if self.Ciso=='990h' else 1))
            data['optimality_known'] = True
            data['manin_known'] = True
            if self.label_type=='Cremona':
                data['optimal_label'] = '990h3' if self.Ciso=='990h' else self.Ciso+'1'
            else:
                data['optimal_label'] = '990.i3' if self.lmfdb_iso=='990.i' else self.lmfdb_iso+'1'

        elif N<CREMONA_BOUND:

            data['optimality_code'] = self.optimality
            data['optimality_known'] = (self.optimality < 2)

            if self.optimality==1:
                data['manin_known'] = True
                data['optimal_label'] = self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label
            else:
                if self.Cnumber==1:
                    data['manin_known'] = False
                    data['optimal_label'] = self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label
                else:
                    # find curve #1 in this class and its optimailty code:
                    opt_curve = db.ec_curvedata.lucky({'Ciso': self.Ciso, 'Cnumber': 1},
                                                   projection=['Clabel','lmfdb_label','optimality'])
                    data['manin_known'] = (opt_curve['optimality']==1)
                    data['optimal_label'] = opt_curve['Clabel' if self.label_type == 'Cremona' else 'lmfdb_label']

        else:
            data['optimality_code'] = None
            data['optimality_known'] = False
            data['manin_known'] = False
            data['optimal_label'] = ''

        # p-adic data:

        data['p_adic_primes'] = [p for i,p in enumerate(prime_range(5, 100))
                                 if (N*data['ap'][i]) %p !=0]

        data['p_adic_data_exists'] = False
        if data['optimality_code']==1:
            data['p_adic_data_exists'] = db.ec_padic.exists({'lmfdb_iso': self.lmfdb_iso})

        # Iwasawa data (where present)

        self.make_iwasawa()

        # Torsion growth data (where present)

        self.make_torsion_growth()

        # Newform

        rawnewform =  str(PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True))
        data['newform'] =  raw_typeset(rawnewform, web_latex(PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True)))
        data['newform_label'] = self.newform_label = ".".join( [str(cond), str(2), 'a', iso] )
        self.newform_link = url_for("cmf.by_url_newform_label", level=cond, weight=2, char_orbit_label='a', hecke_orbit=iso)
        self.newform_exists_in_db = db.mf_newforms.label_exists(self.newform_label)
        self._code = None

        if self.label_type == 'Cremona':
            self.class_url = url_for(".by_ec_label", label=self.Ciso)
            self.class_name = self.Ciso
        else:
            self.class_url = url_for(".by_ec_label", label=self.lmfdb_iso)
            self.class_name = self.lmfdb_iso
        data['class_name'] = self.class_name
        data['Cnumber'] = self.Cnumber if N<CREMONA_BOUND else None

        self.friends = [
            ('Isogeny class ' + self.class_name, self.class_url),
            ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_ec_label", label=data['minq_label'])),
            ('All twists ', url_for(".rational_elliptic_curves", jinv=data['j_invariant']))]

        lfun_url = url_for("l_functions.l_function_ec_page", conductor_label = N, isogeny_class_label = iso)
        origin_url = lfun_url.lstrip('/L/').rstrip('/')

        if db.lfunc_instances.exists({'url':origin_url}):
            self.friends += [('L-function', lfun_url)]
        else:
            self.friends += [('L-function not available', "")]

        if not self.cm:
            if N<=300:
                self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', conductor = N, isogeny = iso))]
            if N<=50:
                self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', conductor = N, isogeny = iso))]
        if self.newform_exists_in_db:
            self.friends += [('Modular form ' + self.newform_label, self.newform_link)]

        self.downloads = [('q-expansion to text', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)),
                          ('All stored data to text', url_for(".download_EC_all", label=self.lmfdb_label)),
                          ('Code to Magma', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')),
                          ('Code to SageMath', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')),
                          ('Code to GP', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp')),
                          ('Underlying data', url_for(".EC_data", label=self.lmfdb_label)),
        ]

        try:
            self.plot = encode_plot(self.E.plot())
        except AttributeError:
            self.plot = encode_plot(EllipticCurve(data['ainvs']).plot())


        self.plot_link = '<a href="{0}"><img src="{0}" width="200" height="150"/></a>'.format(self.plot)
        self.properties = [('Label', self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label),
                           (None, self.plot_link),
                           ('Conductor', prop_int_pretty(data['conductor'])),
                           ('Discriminant', prop_int_pretty(data['disc'])),
                           ('j-invariant', '%s' % data['j_inv_latex']),
                           ('CM', '%s' % data['CM']),
                           ('Rank', 'unknown' if self.mwbsd['rank'] == '?' else prop_int_pretty(self.mwbsd['rank'])),
                           ('Torsion structure', (r'\(%s\)' % self.mwbsd['tor_struct']) if self.mwbsd['tor_struct'] else 'trivial'),
                           ]

        if self.label_type == 'Cremona':
            self.title = "Elliptic curve with Cremona label {} (LMFDB label {})".format(self.Clabel, self.lmfdb_label)
        elif N<CREMONA_BOUND:
            self.title = "Elliptic curve with LMFDB label {} (Cremona label {})".format(self.lmfdb_label, self.Clabel)
        else:
            self.title = "Elliptic curve with LMFDB label {}".format(self.lmfdb_label)

        self.bread = [('Elliptic curves', url_for("ecnf.index")),
                           (r'$\Q$', url_for(".rational_elliptic_curves")),
                           ('%s' % N, url_for(".by_conductor", conductor=N)),
                           ('%s' % iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)),
                           ('%s' % num,' ')]
Example #4
0
    def make_class(self):
        # Extract the size of the isogeny class from the database
        classdata = db.ec_classdata.lucky({'lmfdb_iso': self.lmfdb_iso})
        self.class_size = ncurves = classdata['class_size']

        # Create a list of the curves in the class from the database
        number_key = 'Cnumber' if self.label_type == 'Cremona' else 'lmfdb_number'
        self.curves = [
            db.ec_curvedata.lucky({
                'lmfdb_iso': self.lmfdb_iso,
                number_key: i + 1
            }) for i in range(ncurves)
        ]

        # Set optimality flags.  The optimal curve is conditionally
        # number 1 except in one case which is labeled differently in
        # the Cremona tables.  We know which curve is optimal iff the
        # optimality code for curve #1 is 1 (except for class 990h).

        # Note that self is actually an elliptic curve, with number=1.

        # The code here allows us to update the display correctly by
        # changing one line in this file (defining OPTIMALITY_BOUND)
        # without changing the data.

        self.cremona_bound = CREMONA_BOUND
        self.optimality_bound = OPTIMALITY_BOUND
        self.optimality_known = (self.conductor < OPTIMALITY_BOUND) or (
            (self.conductor < CREMONA_BOUND) and ((self.optimality == 1) or
                                                  (self.Ciso == '990h')))
        self.optimal_label = self.Clabel if self.label_type == 'Cremona' else self.lmfdb_label

        if self.conductor < OPTIMALITY_BOUND:
            for c in self.curves:
                c['optimal'] = (c['Cnumber'] == (3 if self.Ciso == '990h' else
                                                 1))
                c['optimality_known'] = True
        elif self.conductor < CREMONA_BOUND:
            for c in self.curves:
                c['optimal'] = (c['optimality'] > 0
                                )  # this curve possibly optimal
                c['optimality_known'] = (c['optimality'] == 1
                                         )  # this curve certainly optimal
        else:
            for c in self.curves:
                c['optimal'] = None
                c['optimality_known'] = False
        for c in self.curves:
            c['ai'] = c['ainvs']
            c['curve_url_lmfdb'] = url_for(".by_ec_label",
                                           label=c['lmfdb_label'])
            c['curve_url_cremona'] = url_for(
                ".by_ec_label",
                label=c['Clabel']) if self.conductor < CREMONA_BOUND else "N/A"
            if self.label_type == 'Cremona':
                c['curve_label'] = c['Clabel']
                _, c_iso, c_number = split_cremona_label(c['Clabel'])
            else:
                c['curve_label'] = c['lmfdb_label']
                _, c_iso, c_number = split_lmfdb_label(c['lmfdb_label'])
            c['short_label'] = "{}{}".format(c_iso, c_number)
            c['FH'] = RealField(20)(c['faltings_height'])
            c['j_inv'] = QQ(tuple(
                c['jinv']))  # convert [num,den] to rational for display
            c['disc'] = c['signD'] * c['absD']

        from sage.matrix.all import Matrix
        M = classdata['isogeny_matrix']

        # permute rows/cols to match labelling: the rows/cols in the
        # ec_classdata table are with respect to LMFDB ordering.
        if self.label_type == 'Cremona':
            perm = lambda i: next(c for c in self.curves
                                  if c['Cnumber'] == i + 1)['lmfdb_number'] - 1
            M = [[M[perm(i)][perm(j)] for i in range(ncurves)]
                 for j in range(ncurves)]

        M = Matrix(M)

        self.isogeny_matrix_str = latex(M)

        # Create isogeny graph with appropriate vertex labels:

        self.graph = make_graph(M, [c['short_label'] for c in self.curves])
        P = self.graph.plot(edge_labels=True, vertex_size=1000)
        self.graph_img = encode_plot(P)
        self.graph_link = '<img src="%s" width="200" height="150"/>' % self.graph_img

        self.newform = raw_typeset(
            PowerSeriesRing(QQ, 'q')(classdata['anlist'], 20, check=True))
        self.newform_label = ".".join(
            [str(self.conductor),
             str(2), 'a', self.iso_label])
        self.newform_exists_in_db = db.mf_newforms.label_exists(
            self.newform_label)
        if self.newform_exists_in_db:
            char_orbit, hecke_orbit = self.newform_label.split('.')[2:]
            self.newform_link = url_for("cmf.by_url_newform_label",
                                        level=self.conductor,
                                        weight=2,
                                        char_orbit_label=char_orbit,
                                        hecke_orbit=hecke_orbit)

        self.lfunction_link = url_for("l_functions.l_function_ec_page",
                                      conductor_label=self.conductor,
                                      isogeny_class_label=self.iso_label)

        self.friends = [('L-function', self.lfunction_link)]

        if self.cm:
            # set CM field for Properties box.
            D = integer_squarefree_part(ZZ(self.cm))
            coeffs = [(1 - D) // 4, -1, 1] if D % 4 == 1 else [-D, 0, 1]
            lab = db.nf_fields.lucky({'coeffs': coeffs}, projection='label')
            self.CMfield = field_pretty(lab)
        else:
            self.CMfield = "no"
            if self.conductor <= 300:
                self.friends += [('Symmetric square L-function',
                                  url_for("l_functions.l_function_ec_sym_page",
                                          power='2',
                                          conductor=self.conductor,
                                          isogeny=self.iso_label))]
            if self.conductor <= 50:
                self.friends += [('Symmetric cube L-function',
                                  url_for("l_functions.l_function_ec_sym_page",
                                          power='3',
                                          conductor=self.conductor,
                                          isogeny=self.iso_label))]
        if self.newform_exists_in_db:
            self.friends += [('Modular form ' + self.newform_label,
                              self.newform_link)]

        if self.label_type == 'Cremona':
            self.title = "Elliptic curve isogeny class with Cremona label {} (LMFDB label {})".format(
                self.Ciso, self.lmfdb_iso)
        elif self.conductor < CREMONA_BOUND:
            self.title = "Elliptic curve isogeny class with LMFDB label {} (Cremona label {})".format(
                self.lmfdb_iso, self.Ciso)
        else:
            self.title = "Elliptic curve isogeny class with LMFDB label {}".format(
                self.lmfdb_iso)

        self.properties = [
            ('Label',
             self.Ciso if self.label_type == 'Cremona' else self.lmfdb_iso),
            ('Number of curves', prop_int_pretty(ncurves)),
            ('Conductor', prop_int_pretty(self.conductor)),
            ('CM', '%s' % self.CMfield), ('Rank', prop_int_pretty(self.rank))
        ]
        if ncurves > 1:
            self.properties += [('Graph', ''), (None, self.graph_link)]

        self.downloads = [('q-expansion to text',
                           url_for(".download_EC_qexp",
                                   label=self.iso_label,
                                   limit=1000)),
                          ('All stored data to text',
                           url_for(".download_EC_all", label=self.iso_label))]

        self.bread = [('Elliptic curves', url_for("ecnf.index")),
                      (r'$\Q$', url_for(".rational_elliptic_curves")),
                      ('%s' % self.conductor,
                       url_for(".by_conductor", conductor=self.conductor)),
                      ('%s' % self.iso_label, ' ')]
        self.code = {}
        self.code['show'] = {'sage': ''}  # use default show names
        self.code['class'] = {
            'sage':
            'E = EllipticCurve("%s1")\n' % (self.iso_label) +
            'E.isogeny_class()\n'
        }
        self.code['curves'] = {'sage': 'E.isogeny_class().curves'}
        self.code['rank'] = {'sage': 'E.rank()'}
        self.code['q_eigenform'] = {'sage': 'E.q_eigenform(10)'}
        self.code['matrix'] = {'sage': 'E.isogeny_class().matrix()'}
        self.code['plot'] = {
            'sage': 'E.isogeny_graph().plot(edge_labels=True)'
        }
Example #5
0
 LinkCol("lmfdb_iso", "ec.q.lmfdb_label", "Class", lambda label: url_for(".by_ec_label", label=label),
         default=True, align="center", short_title="LMFDB class label"),
 MultiProcessedCol("cremona_iso", "ec.q.cremona_label", "Cremona class",
                   ["Ciso", "conductor"],
                   lambda label, conductor: '<a href="%s">%s</a>' % (url_for(".by_ec_label", label=label), label) if conductor < CREMONA_BOUND else " - ",
                   align="center", short_title="Cremona class label"),
 MathCol("class_size", "ec.isogeny_class", "Class size", align="center", default=lambda info: info.get("class_size") or info.get("optimal") == "on"),
 MathCol("class_deg", "ec.isogeny_class_degree", "Class degree", align="center", default=lambda info: info.get("class_deg")),
 ProcessedCol("conductor", "ec.q.conductor", "Conductor", lambda v: web_latex_factored_integer(ZZ(v)), default=True, align="center"),
 MultiProcessedCol("disc", "ec.discriminant", "Discriminant", ["signD", "absD"], lambda s, a: web_latex_factored_integer(s*ZZ(a)),
                   default=lambda info: info.get("discriminant"), align="center"),
 MathCol("rank", "ec.rank", "Rank", default=True),
 ProcessedCol("torsion_structure", "ec.torsion_subgroup", "Torsion",
              lambda tors: r"\oplus".join([r"\Z/%s\Z"%n for n in tors]) if tors else r"\mathsf{trivial}", default=True, mathmode=True, align="center"),
 ProcessedCol("geom_end_alg", "ag.endomorphism_algebra", r"$\textrm{End}^0(E_{\overline\Q})$",
              lambda v: r"$\Q$" if not v else r"$\Q(\sqrt{%d})$"%(integer_squarefree_part(v)),
              short_title="Qbar-end algebra", align="center", orig="cm"),
 ProcessedCol("cm_discriminant", "ec.complex_multiplication", "CM", lambda v: "" if v == 0 else v,
              short_title="CM discriminant", mathmode=True, align="center", default=True, orig="cm"),
 ProcessedCol("sato_tate_group", "st_group.definition", "Sato-Tate", lambda v: st_display_knowl('1.2.A.1.1a' if v==0 else '1.2.B.2.1a'),
              short_title="Sato-Tate group", align="center", orig="cm"),
 CheckCol("semistable", "ec.reduction", "Semistable"),
 CheckCol("potential_good_reduction", "ec.reduction", "Potentially good"),
 ProcessedCol("nonmax_primes", "ec.maximal_elladic_galois_rep", r"Nonmax $\ell$", lambda primes: ", ".join([str(p) for p in primes]),
              default=lambda info: info.get("nonmax_primes"), short_title="nonmaximal primes", mathmode=True, align="center"),
 ProcessedCol("elladic_images", "ec.galois_rep_elladic_image", r"$\ell$-adic images", lambda v: ", ".join([display_knowl('gl2.subgroup_data', title=s, kwargs={'label':s}) for s in v]),
              short_title="ℓ-adic images", default=lambda info: info.get("nonmax_primes") or info.get("galois_image"), align="center"),
 ProcessedCol("modell_images", "ec.galois_rep_modell_image", r"mod-$\ell$ images", lambda v: ", ".join([display_knowl('gl2.subgroup_data', title=s, kwargs={'label':s}) for s in v]),
              short_title="mod-ℓ images", default=lambda info: info.get("nonmax_primes") or info.get("galois_image"), align="center"),
 ProcessedCol("regulator", "ec.regulator", "Regulator", lambda v: str(v)[:11], mathmode=True),
 MathCol("sha", "ec.analytic_sha_order", r"$Ш_{\textrm{an}}$", short_title="analytic Ш"),