Ejemplo n.º 1
0
def make_extra_data(label, number, ainvs, gens):
    """Given a curve label (and number, as some data is only stored wih
    curve number 1 in each class) and its ainvs and gens, returns a
    dict with which to update the entry.

    Extra items computed here:
    'equation': latex string of curve's equation
    'signD': sign of discriminant
    'local_data': list of dicts, one item for each bad prime
    'min_quad_twist': dict holding curve's min quadratic twist and the twisting discriminant
    'heights': list of heights of gens

    and for curve #1 in a class only:

    'aplist': list of a_p for p<100
    'anlist': list of a_n for n<=20

    """
    E = EllipticCurve(parse_ainvs(ainvs))
    data = {}
    # convert from a list of strings to a single string, e.g. from ['0','0','0','1','1'] to '[0,0,0,1,1]'
    data['equation'] = web_latex(E)
    data['signD'] = int(E.discriminant().sign())
    data['local_data'] = [{
        'p':
        int(ld.prime().gen()),
        'ord_cond':
        int(ld.conductor_valuation()),
        'ord_disc':
        int(ld.discriminant_valuation()),
        'ord_den_j':
        int(max(0, -(E.j_invariant().valuation(ld.prime().gen())))),
        'red':
        int(ld.bad_reduction_type()),
        'rootno':
        int(E.root_number(ld.prime().gen())),
        'kod':
        web_latex(ld.kodaira_symbol()).replace('$', ''),
        'cp':
        int(ld.tamagawa_number())
    } for ld in E.local_data()]
    Etw, Dtw = E.minimal_quadratic_twist()
    if Etw.conductor() == E.conductor():
        data['min_quad_twist'] = {'label': label, 'disc': int(1)}
    else:
        minq_ainvs = ''.join(['['] + [str(c) for c in Etw.ainvs()] + [']'])
        r = curves.find_one({
            'jinv': str(E.j_invariant()),
            'ainvs': minq_ainvs
        })
        minq_label = "" if r is None else r['label']
        data['min_quad_twist'] = {'label': minq_label, 'disc': int(Dtw)}
    from lmfdb.elliptic_curves.web_ec import parse_points
    gens = [E(g) for g in parse_points(gens)]
    data['heights'] = [float(P.height()) for P in gens]
    if number == 1:
        data['aplist'] = E.aplist(100, python_ints=True)
        data['anlist'] = E.anlist(20, python_ints=True)
    return data
Ejemplo n.º 2
0
def make_extra_data(label, number, ainvs, gens):
    """
    C is a database elliptic curve entry.  Returns a dict with which to update the entry.

    Data fields needed in C already: 'ainvs', 'lmfdb_label', 'gens', 'number'
    """
    E = EllipticCurve([int(a) for a in ainvs])
    data = {}
    # convert from a list of strings to a single string, e.g. from ['0','0','0','1','1'] to '[0,0,0,1,1]'
    data['xainvs'] = ''.join(['[', ','.join(ainvs), ']'])
    data['equation'] = web_latex(E)
    data['signD'] = int(E.discriminant().sign())
    data['local_data'] = [{
        'p':
        int(ld.prime().gen()),
        'ord_cond':
        int(ld.conductor_valuation()),
        'ord_disc':
        int(ld.discriminant_valuation()),
        'ord_den_j':
        int(max(0, -(E.j_invariant().valuation(ld.prime().gen())))),
        'red':
        int(ld.bad_reduction_type()),
        'rootno':
        int(E.root_number(ld.prime().gen())),
        'kod':
        web_latex(ld.kodaira_symbol()).replace('$', ''),
        'cp':
        int(ld.tamagawa_number())
    } for ld in E.local_data()]
    Etw, Dtw = E.minimal_quadratic_twist()
    if Etw.conductor() == E.conductor():
        data['min_quad_twist'] = {'label': label, 'disc': int(1)}
    else:
        # Later this should be changed to look for xainvs but now all curves have ainvs
        minq_ainvs = [str(c) for c in Etw.ainvs()]
        r = curves.find_one({
            'jinv': str(E.j_invariant()),
            'ainvs': minq_ainvs
        })
        minq_label = "" if r is None else r['label']
        data['min_quad_twist'] = {'label': minq_label, 'disc': int(Dtw)}
    from lmfdb.elliptic_curves.web_ec import parse_points
    gens = [E(g) for g in parse_points(gens)]
    data['heights'] = [float(P.height()) for P in gens]
    if number == 1:
        data['aplist'] = E.aplist(100, python_ints=True)
        data['anlist'] = E.anlist(20, python_ints=True)
    return data
Ejemplo n.º 3
0
def make_extra_data(label,number,ainvs,gens):
    """Given a curve label (and number, as some data is only stored wih
    curve number 1 in each class) and its ainvs and gens, returns a
    dict with which to update the entry.

    Extra items computed here:
    'equation': latex string of curve's equation
    'signD': sign of discriminant
    'local_data': list of dicts, one item for each bad prime
    'min_quad_twist': dict holding curve's min quadratic twist and the twisting discriminant
    'heights': list of heights of gens

    and for curve #1 in a class only:

    'aplist': list of a_p for p<100
    'anlist': list of a_n for n<=20

    """
    E = EllipticCurve(parse_ainvs(ainvs))
    data = {}
    # convert from a list of strings to a single string, e.g. from ['0','0','0','1','1'] to '[0,0,0,1,1]'
    data['equation'] = web_latex(E)
    data['signD'] = int(E.discriminant().sign())
    data['local_data'] = [{'p': int(ld.prime().gen()),
                           'ord_cond':int(ld.conductor_valuation()),
                           'ord_disc':int(ld.discriminant_valuation()),
                           'ord_den_j':int(max(0,-(E.j_invariant().valuation(ld.prime().gen())))),
                           'red':int(ld.bad_reduction_type()),
                           'rootno':int(E.root_number(ld.prime().gen())),
                           'kod':web_latex(ld.kodaira_symbol()).replace('$',''),
                           'cp':int(ld.tamagawa_number())}
                          for ld in E.local_data()]
    Etw, Dtw = E.minimal_quadratic_twist()
    if Etw.conductor()==E.conductor():
        data['min_quad_twist'] = {'label':label, 'disc':int(1)}
    else:
        minq_ainvs = ''.join(['['] + [str(c) for c in Etw.ainvs()] + [']'])
        r = curves.find_one({'jinv':str(E.j_invariant()), 'ainvs':minq_ainvs})
        minq_label = "" if r is None else r['label']
        data['min_quad_twist'] = {'label':minq_label, 'disc':int(Dtw)}
    from lmfdb.elliptic_curves.web_ec import parse_points
    gens = [E(g) for g in parse_points(gens)]
    data['heights'] = [float(P.height()) for P in gens]
    if number==1:
        data['aplist'] = E.aplist(100,python_ints=True)
        data['anlist'] = E.anlist(20,python_ints=True)
    return data
Ejemplo n.º 4
0
def make_extra_data(label,number,ainvs,gens):
    """
    C is a database elliptic curve entry.  Returns a dict with which to update the entry.

    Data fields needed in C already: 'ainvs', 'lmfdb_label', 'gens', 'number'
    """
    E = EllipticCurve([int(a) for a in ainvs])
    data = {}
    # convert from a list of strings to a single string, e.g. from ['0','0','0','1','1'] to '[0,0,0,1,1]'
    data['xainvs'] = ''.join(['[',','.join(ainvs),']'])
    data['equation'] = web_latex(E)
    data['signD'] = int(E.discriminant().sign())
    data['local_data'] = [{'p': int(ld.prime().gen()),
                           'ord_cond':int(ld.conductor_valuation()),
                           'ord_disc':int(ld.discriminant_valuation()),
                           'ord_den_j':int(max(0,-(E.j_invariant().valuation(ld.prime().gen())))),
                           'red':int(ld.bad_reduction_type()),
                           'rootno':int(E.root_number(ld.prime().gen())),
                           'kod':web_latex(ld.kodaira_symbol()).replace('$',''),
                           'cp':int(ld.tamagawa_number())}
                          for ld in E.local_data()]
    Etw, Dtw = E.minimal_quadratic_twist()
    if Etw.conductor()==E.conductor():
        data['min_quad_twist'] = {'label':label, 'disc':int(1)}
    else:
        # Later this should be changed to look for xainvs but now all curves have ainvs
        minq_ainvs = [str(c) for c in Etw.ainvs()]
        r = curves.find_one({'jinv':str(E.j_invariant()), 'ainvs':minq_ainvs})
        minq_label = "" if r is None else r['label']
        data['min_quad_twist'] = {'label':minq_label, 'disc':int(Dtw)}
    from lmfdb.elliptic_curves.web_ec import parse_points
    gens = [E(g) for g in parse_points(gens)]
    data['heights'] = [float(P.height()) for P in gens]
    if number==1:
        data['aplist'] = E.aplist(100,python_ints=True)
        data['anlist'] = E.anlist(20,python_ints=True)
    return data
Ejemplo n.º 5
0
def render_curve_webpage_by_label(label):
    C = lmfdb.base.getDBConnection()
    data = C.elliptic_curves.curves.find_one({'lmfdb_label': label})
    if data is None:
        return elliptic_curve_jump_error(label, {})
    info = {}
    ainvs = [int(a) for a in data['ainvs']]
    E = EllipticCurve(ainvs)
    cremona_label = data['label']
    lmfdb_label = data['lmfdb_label']
    N = ZZ(data['conductor'])
    cremona_iso_class = data['iso']  # eg '37a'
    lmfdb_iso_class = data['lmfdb_iso']  # eg '37.a'
    rank = data['rank']
    try:
        j_invariant = QQ(str(data['jinv']))
    except KeyError:
        j_invariant = E.j_invariant()
    if j_invariant == 0:
        j_inv_factored = latex(0)
    else:
        j_inv_factored = latex(j_invariant.factor())
    jinv = unicode(str(j_invariant))
    CMD = 0
    CM = "no"
    EndE = "\(\Z\)"
    if E.has_cm():
        CMD = E.cm_discriminant()
        CM = "yes (\(%s\))" % CMD
        if CMD % 4 == 0:
            d4 = ZZ(CMD) // 4
            # r = d4.squarefree_part()
            # f = (d4//r).isqrt()
            # f="" if f==1 else str(f)
            # EndE = "\(\Z[%s\sqrt{%s}]\)"%(f,r)
            EndE = "\(\Z[\sqrt{%s}]\)" % (d4)
        else:
            EndE = "\(\Z[(1+\sqrt{%s})/2]\)" % CMD

    # plot=E.plot()
    discriminant = E.discriminant()
    xintpoints_projective = [
        E.lift_x(x)
        for x in xintegral_point(data['x-coordinates_of_integral_points'])
    ]
    xintpoints = proj_to_aff(xintpoints_projective)
    if 'degree' in data:
        modular_degree = data['degree']
    else:
        try:
            modular_degree = E.modular_degree()
        except RuntimeError:
            modular_degree = 0  # invalid, will be displayed nicely

    G = E.torsion_subgroup().gens()
    E_pari = E.pari_curve(prec=200)
    from sage.libs.pari.all import PariError
    try:
        minq = E.minimal_quadratic_twist()[0]
    except PariError:  # this does occur with 164411a1
        print "PariError computing minimal quadratic twist of elliptic curve %s" % lmfdb_label
        minq = E
    if E == minq:
        minq_label = lmfdb_label
    else:
        minq_ainvs = [str(c) for c in minq.ainvs()]
        minq_label = C.elliptic_curves.curves.find_one({'ainvs': minq_ainvs
                                                        })['lmfdb_label']


# We do not just do the following, as Sage's installed database
# might not have all the curves in the LMFDB database.
# minq_label = E.minimal_quadratic_twist()[0].label()

    if 'gens' in data:
        generator = parse_gens(data['gens'])
    if len(G) == 0:
        tor_struct = '\mathrm{Trivial}'
        tor_group = '\mathrm{Trivial}'
    else:
        tor_group = ' \\times '.join(['\Z/{%s}\Z' % a.order() for a in G])
    if 'torsion_structure' in data:
        info['tor_structure'] = ' \\times '.join(
            ['\Z/{%s}\Z' % int(a) for a in data['torsion_structure']])
    else:
        info['tor_structure'] = tor_group

    def trim_galois_image_code(s):
        return s[2:] if s[1].isdigit() else s[1:]

    if 'galois_images' in data:
        galois_images = data['galois_images']
        galois_images = [trim_galois_image_code(s) for s in galois_images]
        non_surjective_primes = data['non-surjective_primes']

    galois_data = [{
        'p': p,
        'image': im
    } for p, im in zip(non_surjective_primes, galois_images)]

    info.update(data)
    if rank >= 2:
        lder_tex = "L%s(E,1)" % ("^{(" + str(rank) + ")}")
    elif rank == 1:
        lder_tex = "L%s(E,1)" % ("'" * rank)
    else:
        assert rank == 0
        lder_tex = "L(E,1)"
    info['Gamma0optimal'] = (cremona_label[-1] == '1'
                             if cremona_iso_class != '990h' else
                             cremona_label[-1] == '3')
    info['modular_degree'] = modular_degree
    p_adic_data_exists = (C.elliptic_curves.padic_db.find({
        'lmfdb_iso':
        lmfdb_iso_class
    }).count()) > 0 and info['Gamma0optimal']

    # Local data
    local_data = []
    for p in N.prime_factors():
        local_info = E.local_data(p, algorithm="generic")
        local_data.append({
            'p':
            p,
            'tamagawa_number':
            local_info.tamagawa_number(),
            'kodaira_symbol':
            web_latex(local_info.kodaira_symbol()).replace('$', ''),
            'reduction_type':
            local_info.bad_reduction_type()
        })

    mod_form_iso = lmfdb_label_regex.match(lmfdb_iso_class).groups()[1]

    tamagawa_numbers = [
        E.local_data(p, algorithm="generic").tamagawa_number()
        for p in N.prime_factors()
    ]
    # if we use E.tamagawa_numbers() it calls E.local_data(p) which
    # crashes on some curves e.g. 164411a1
    info.update({
        'conductor':
        N,
        'disc_factor':
        latex(discriminant.factor()),
        'j_invar_factor':
        j_inv_factored,
        'label':
        lmfdb_label,
        'cremona_label':
        cremona_label,
        'iso_class':
        lmfdb_iso_class,
        'cremona_iso_class':
        cremona_iso_class,
        'equation':
        web_latex(E),
        #'f': ajax_more(E.q_eigenform, 10, 20, 50, 100, 250),
        'f':
        web_latex(E.q_eigenform(10)),
        'generators':
        ', '.join(web_latex(g) for g in generator) if 'gens' in data else ' ',
        'lder':
        lder_tex,
        'p_adic_primes': [
            p for p in sage.all.prime_range(5, 100)
            if E.is_ordinary(p) and not p.divides(N)
        ],
        'p_adic_data_exists':
        p_adic_data_exists,
        'ainvs':
        format_ainvs(data['ainvs']),
        'CM':
        CM,
        'CMD':
        CMD,
        'EndE':
        EndE,
        'tamagawa_numbers':
        r' \cdot '.join(str(sage.all.factor(c)) for c in tamagawa_numbers),
        'local_data':
        local_data,
        'cond_factor':
        latex(N.factor()),
        'galois_data':
        galois_data,
        'xintegral_points':
        ', '.join(web_latex(P) for P in xintpoints),
        'tor_gens':
        ', '.join(web_latex(eval(g))
                  for g in data['torsion_generators']) if False else ', '.join(
                      web_latex(P.element().xy()) for P in list(G))
    })
    info['friends'] = [('Isogeny class ' + lmfdb_iso_class,
                        url_for(".by_ec_label", label=lmfdb_iso_class)),
                       ('Minimal quadratic twist ' + minq_label,
                        url_for(".by_ec_label", label=minq_label)),
                       ('All twists ',
                        url_for(".rational_elliptic_curves", jinv=jinv)),
                       ('L-function',
                        url_for("l_functions.l_function_ec_page",
                                label=lmfdb_label)),
                       ('Symmetric square L-function',
                        url_for("l_functions.l_function_ec_sym_page",
                                power='2',
                                label=lmfdb_iso_class)),
                       ('Symmetric 4th power L-function',
                        url_for("l_functions.l_function_ec_sym_page",
                                power='4',
                                label=lmfdb_iso_class))]

    info['friends'].append(
        ('Modular form ' + lmfdb_iso_class.replace('.', '.2'),
         url_for("emf.render_elliptic_modular_forms",
                 level=int(N),
                 weight=2,
                 character=0,
                 label=mod_form_iso)))

    info['downloads'] = [('Download coeffients of q-expansion',
                          url_for(".download_EC_qexp",
                                  label=lmfdb_label,
                                  limit=100)),
                         ('Download all stored data',
                          url_for(".download_EC_all", label=lmfdb_label))]

    # info['learnmore'] = [('Elliptic Curves', url_for(".not_yet_implemented"))]
    # info['plot'] = image_src(plot)
    info['plot'] = url_for('.plot_ec', label=lmfdb_label)

    properties2 = [('Label', '%s' % lmfdb_label),
                   (None, '<img src="%s" width="200" height="150"/>' %
                    url_for('.plot_ec', label=lmfdb_label)),
                   ('Conductor', '\(%s\)' % N),
                   ('Discriminant', '\(%s\)' % discriminant),
                   ('j-invariant', '%s' % web_latex(j_invariant)),
                   ('CM', '%s' % CM), ('Rank', '\(%s\)' % rank),
                   ('Torsion Structure', '\(%s\)' % tor_group)]
    # properties.extend([ "prop %s = %s<br/>" % (_,_*1923) for _ in range(12) ])
    credit = 'John Cremona and Andrew Sutherland'
    if info['label'] == info['cremona_label']:
        t = "Elliptic Curve %s" % info['label']
    else:
        t = "Elliptic Curve %s (Cremona label %s)" % (info['label'],
                                                      info['cremona_label'])

    bread = [('Elliptic Curves ', url_for(".rational_elliptic_curves")),
             ('Elliptic curves %s' % lmfdb_label, ' ')]

    return render_template("curve.html",
                           properties2=properties2,
                           credit=credit,
                           bread=bread,
                           title=t,
                           info=info,
                           friends=info['friends'],
                           downloads=info['downloads'])
Ejemplo n.º 6
0
def curves(line, verbose=False):
    r""" Parses one line from a curves file.  Returns the label and a dict
    containing fields with keys 'field_label', 'degree', 'signature',
    'abs_disc', 'label', 'short_label', conductor_label',
    'conductor_ideal', 'conductor_norm', 'iso_label', 'iso_nlabel',
    'number', 'ainvs', 'jinv', 'cm', 'q_curve', 'base_change',
    'torsion_order', 'torsion_structure', 'torsion_gens'; and (added
    May 2016): 'equation', 'local_data', 'non_min_p', 'minD'

    Input line fields (13):

    field_label conductor_label iso_label number conductor_ideal conductor_norm a1 a2 a3 a4 a6 cm base_change

    Sample input line:

    2.0.4.1 65.18.1 a 1 [65,18,1] 65 1,1 1,1 0,1 -1,1 -1,0 0 0
    """
    # Parse the line and form the full label:
    data = split(line)
    if len(data) != 13:
        print "line %s does not have 13 fields, skipping" % line
    field_label = data[0]  # string
    IQF_flag = field_label.split(".")[:2] == ['2', '0']
    K = nf_lookup(field_label) if IQF_flag else None
    conductor_label = data[1]  # string
    # convert label (does nothing except for imaginary quadratic)
    conductor_label = convert_conductor_label(field_label, conductor_label)
    iso_label = data[2]  # string
    iso_nlabel = numerify_iso_label(iso_label)  # int
    number = int(data[3])  # int
    short_class_label = "%s-%s" % (conductor_label, iso_label)
    short_label = "%s%s" % (short_class_label, str(number))
    class_label = "%s-%s" % (field_label, short_class_label)
    label = "%s-%s" % (field_label, short_label)

    conductor_ideal = data[4]  # string
    conductor_norm = int(data[5])  # int
    ainvs = ";".join(data[6:11])  # one string joining 5 NFelt strings
    cm = data[11]  # int or '?'
    if cm != '?':
        cm = int(cm)

    # Create the field and curve to compute the j-invariant:
    dummy, deg, sig, abs_disc = field_data(field_label)
    K = nf_lookup(field_label)
    #print("Field %s created, gen_name = %s" % (field_label,str(K.gen())))
    ainvsK = parse_ainvs(K, ainvs)  # list of K-elements
    E = EllipticCurve(ainvsK)
    #print("{} created with disc = {}, N(disc)={}".format(E,K.ideal(E.discriminant()).factor(),E.discriminant().norm().factor()))
    j = E.j_invariant()
    jinv = NFelt(j)
    if cm == '?':
        cm = get_cm(j)
        if cm:
            print "cm=%s for j=%s" % (cm, j)

    q_curve = data[12]  # 0, 1 or ?.  If unknown we'll determine this below.
    if q_curve in ['0', '1']:  # already set -- easy
        q_curve = bool(int(q_curve))
    else:
        try:
            q_curve = is_Q_curve(E)
        except NotImplementedError:
            q_curve = '?'

    # Here we should check that the conductor of the constructed curve
    # agrees with the input conductor.
    N = ideal_from_string(K, conductor_ideal)
    NE = E.conductor()
    if N == "wrong" or N != NE:
        print(
            "Wrong conductor ideal {} for label {}, using actual conductor {} instead"
            .format(conductor_ideal, label, NE))
        conductor_ideal = ideal_to_string(NE)
        N = NE

    # get torsion order, structure and generators:
    torgroup = E.torsion_subgroup()
    ntors = int(torgroup.order())
    torstruct = [int(n) for n in list(torgroup.invariants())]
    torgens = [point_string(P.element()) for P in torgroup.gens()]

    # get label of elliptic curve over Q for base_change cases (a
    # subset of Q-curves)

    if True:  # q_curve: now we have not precomputed Q-curve status
        # but still want to test for base change!
        if verbose:
            print("testing {} for base-change...".format(label))
        E1list = E.descend_to(QQ)
        if len(E1list):
            base_change = [cremona_to_lmfdb(E1.label()) for E1 in E1list]
            if verbose:
                print "%s is base change of %s" % (label, base_change)
        else:
            base_change = []
            # print "%s is a Q-curve, but not base-change..." % label
    else:
        base_change = []

    # NB if this is not a global minimal model then local_data may
    # include a prime at which we have good reduction.  This causes no
    # problems except that the bad_reduction_type is then None which
    # cannot be converted to an integer.  The bad reduction types are
    # coded as (Sage) integers in {-1,0,1}.
    local_data = [{
        'p':
        ideal_to_string(ld.prime()),
        'normp':
        str(ld.prime().norm()),
        'ord_cond':
        int(ld.conductor_valuation()),
        'ord_disc':
        int(ld.discriminant_valuation()),
        'ord_den_j':
        int(max(0, -(E.j_invariant().valuation(ld.prime())))),
        'red':
        None
        if ld.bad_reduction_type() == None else int(ld.bad_reduction_type()),
        'kod':
        web_latex(ld.kodaira_symbol()).replace('$', ''),
        'cp':
        int(ld.tamagawa_number())
    } for ld in E.local_data()]

    non_minimal_primes = [ideal_to_string(P) for P in E.non_minimal_primes()]
    minD = ideal_to_string(E.minimal_discriminant_ideal())

    edata = {
        'field_label': field_label,
        'degree': deg,
        'signature': sig,
        'abs_disc': abs_disc,
        'class_label': class_label,
        'short_class_label': short_class_label,
        'label': label,
        'short_label': short_label,
        'conductor_label': conductor_label,
        'conductor_ideal': conductor_ideal,
        'conductor_norm': conductor_norm,
        'iso_label': iso_label,
        'iso_nlabel': iso_nlabel,
        'number': number,
        'ainvs': ainvs,
        'jinv': jinv,
        'cm': cm,
        'q_curve': q_curve,
        'base_change': base_change,
        'torsion_order': ntors,
        'torsion_structure': torstruct,
        'torsion_gens': torgens,
        'equation': web_latex(E),
        'local_data': local_data,
        'minD': minD,
        'non_min_p': non_minimal_primes,
    }

    return label, edata
Ejemplo n.º 7
0
class WebEC(object):
    """
    Class for an elliptic curve over Q
    """
    def __init__(self, dbdata):
        """
        Arguments:

            - dbdata: the data from the database
        """
        logger.debug("Constructing an instance of ECisog_class")
        self.__dict__.update(dbdata)
        # Next lines because the hyphens make trouble
        self.xintcoords = parse_list(dbdata['x-coordinates_of_integral_points'])
        self.non_surjective_primes = dbdata['non-surjective_primes']
        # Next lines because the python identifiers cannot start with 2
        self.twoadic_index = dbdata['2adic_index']
        self.twoadic_log_level = dbdata['2adic_log_level']
        self.twoadic_gens = dbdata['2adic_gens']
        self.twoadic_label = dbdata['2adic_label']
        # All other fields are handled here
        self.make_curve()

    @staticmethod
    def by_label(label):
        """
        Searches for a specific elliptic curve in the curves
        collection by its label, which can be either in LMFDB or
        Cremona format.
        """
        try:
            N, iso, number = split_lmfdb_label(label)
            data = db_ec().find_one({"lmfdb_label" : label})
        except AttributeError:
            try:
                N, iso, number = split_cremona_label(label)
                data = db_ec().find_one({"label" : label})
            except AttributeError:
                return "Invalid label" # caller must catch this and raise an error

        if data:
            return WebEC(data)
        return "Curve not found" # caller must catch this and raise an error

    def make_curve(self):
        # To start with the data fields of self are just those from
        # the database.  We need to reformat these, construct the
        # actual elliptic curve E, and compute some further (easy)
        # data about it.
        #

        # Weierstrass equation

        data = self.data = {}
        data['ainvs'] = [int(ai) for ai in self.ainvs]
        self.E = EllipticCurve(data['ainvs'])
        data['equation'] = web_latex(self.E)

        # conductor, j-invariant and discriminant

        data['conductor'] = N = ZZ(self.conductor)
        bad_primes = N.prime_factors()
        try:
            data['j_invariant'] = QQ(str(self.jinv))
        except KeyError:
            data['j_invariant'] = self.E.j_invariant()
        data['j_inv_factor'] = latex(0)
        if data['j_invariant']:
            data['j_inv_factor'] = latex(data['j_invariant'].factor())
        data['j_inv_str'] = unicode(str(data['j_invariant']))
        data['j_inv_latex'] = web_latex(data['j_invariant'])
        data['disc'] = D = self.E.discriminant()
        data['disc_latex'] = web_latex(data['disc'])
        data['disc_factor'] = latex(data['disc'].factor())
        data['cond_factor'] =latex(N.factor())
        data['cond_latex'] = web_latex(N)

        # CM and endomorphism ring

        data['CMD'] = self.cm
        data['CM'] = "no"
        data['EndE'] = "\(\Z\)"
        if self.cm:
            data['CM'] = "yes (\(D=%s\))" % data['CMD']
            if data['CMD']%4==0:
                d4 = ZZ(data['CMD'])//4
                data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4
            else:
                data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD']

        # modular degree

        try:
            data['degree'] = self.degree
        except AttributeError:
            try:
                data['degree'] = self.E.modular_degree()
            except RuntimeError:
                data['degree']  # invalid, but will be displayed nicely

        # Minimal quadratic twist

        E_pari = self.E.pari_curve()
        from sage.libs.pari.all import PariError
        try:
            minq, minqD = self.E.minimal_quadratic_twist()
        except PariError:  # this does occur with 164411a1
            ec.debug("PariError computing minimal quadratic twist of elliptic curve %s" % lmfdb_label)
            minq = self.E
            minqD = 1
        data['minq_D'] = minqD
        if self.E == minq:
            data['minq_label'] = self.lmfdb_label
            data['minq_info'] = '(itself)'
        else:
            minq_ainvs = [str(c) for c in minq.ainvs()]
            data['minq_label'] = db_ec().find_one({'ainvs': minq_ainvs})['lmfdb_label']
            data['minq_info'] = '(by %s)' % minqD

        minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label'])

        # rational and integral points

        mw = self.mw = {}

        xintpoints_projective = [self.E.lift_x(x) for x in self.xintcoords]
        xintpoints = [P.xy() for P in xintpoints_projective]
        mw['int_points'] = ', '.join(web_latex(P) for P in xintpoints)

        # Generators of infinite order

        mw['rank'] = self.rank
        try:
            self.generators = [self.E(g) for g in parse_points(self.gens)]
            mw['generators'] = [web_latex(P.xy()) for P in self.generators]
            mw['heights'] = [P.height() for P in self.generators]
        except AttributeError:
            mw['generators'] = ''
            mw['heights'] = []

        # Torsion subgroup: order, structure, generators

        mw['tor_order'] = self.torsion
        tor_struct = [int(c) for c in self.torsion_structure]
        if mw['tor_order'] == 1:
            mw['tor_struct'] = '\mathrm{Trivial}'
            mw['tor_gens'] = ''
        else:
            mw['tor_struct'] = ' \\times '.join(['\Z/{%s}\Z' % n
                                                 for n in tor_struct])
            mw['tor_gens'] = ', '.join(web_latex(self.E(g).xy()) for g in parse_points(self.torsion_generators))

        # Images of Galois representations

        try:
            data['galois_images'] = [trim_galois_image_code(s) for s in self.galois_images]
            data['non_surjective_primes'] = self.non_surjective_primes
        except AttributeError:
            #print "No Galois image data"
            data['galois_images'] = []
            data['non_surjective_primes'] = []

        data['galois_data'] = [{'p': p,'image': im }
                               for p,im in zip(data['non_surjective_primes'],
                                               data['galois_images'])]

        if self.twoadic_gens:
            from sage.matrix.all import Matrix
            data['twoadic_gen_matrices'] = ','.join([latex(Matrix(2,2,M)) for M in self.twoadic_gens])
            data['twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html"
        # Leading term of L-function & BSD data

        bsd = self.bsd = {}

        r = self.rank
        if r >= 2:
            bsd['lder_name'] = "L^{(%s)}(E,1)/%s!" % (r,r)
        elif r:
            bsd['lder_name'] = "L'(E,1)"
        else:
            bsd['lder_name'] = "L(E,1)"

        bsd['reg'] = self.regulator
        bsd['omega'] = self.real_period
        bsd['sha'] = int(0.1+self.sha_an)
        bsd['lder'] = self.special_value

        # 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)

        if self.iso == '990h':
            data['Gamma0optimal'] = bool(self.number == 3)
        else:
            data['Gamma0optimal'] = bool(self.number == 1)


        data['p_adic_data_exists'] = False
        if data['Gamma0optimal']:
            data['p_adic_data_exists'] = (padic_db().find({'lmfdb_iso': self.lmfdb_iso}).count()) > 0
        data['p_adic_primes'] = [p for p in sage.all.prime_range(5, 100)
                                 if self.E.is_ordinary(p) and not p.divides(N)]

        # Local data

        local_data = self.local_data = []
        # if we use E.tamagawa_numbers() it calls E.local_data(p) which
        # used to crash on some curves e.g. 164411a1
        tamagawa_numbers = []
        for p in bad_primes:
            local_info = self.E.local_data(p, algorithm="generic")
            local_data_p = {}
            local_data_p['p'] = p
            local_data_p['tamagawa_number'] = local_info.tamagawa_number()
            tamagawa_numbers.append(ZZ(local_info.tamagawa_number()))
            local_data_p['kodaira_symbol'] = web_latex(local_info.kodaira_symbol()).replace('$', '')
            local_data_p['reduction_type'] = local_info.bad_reduction_type()
            local_data_p['ord_cond'] = local_info.conductor_valuation()
            local_data_p['ord_disc'] = local_info.discriminant_valuation()
            local_data_p['ord_den_j'] = max(0,-self.E.j_invariant().valuation(p))
            local_data.append(local_data_p)

        if len(bad_primes)>1:
            bsd['tamagawa_factors'] = r' \cdot '.join(str(c.factor()) for c in tamagawa_numbers)
        else:
            bsd['tamagawa_factors'] = ''
        bsd['tamagawa_product'] = sage.misc.all.prod(tamagawa_numbers)

        cond, iso, num = split_lmfdb_label(self.lmfdb_label)
        data['newform'] =  web_latex(self.E.q_eigenform(10))

        self.make_code_snippets()

        self.friends = [
            ('Isogeny class ' + self.lmfdb_iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)),
            ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number)),
            ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)),
            ('L-function', url_for("l_functions.l_function_ec_page", label=self.lmfdb_label)),
            ('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso)),
            ('Symmetric 4th power L-function', url_for("l_functions.l_function_ec_sym_page", power='4', label=self.lmfdb_iso)),
            ('Modular form ' + self.lmfdb_iso.replace('.', '.2'), url_for("emf.render_elliptic_modular_forms", level=int(N), weight=2, character=0, label=iso))]

        self.downloads = [('Download coeffients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=100)),
                          ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label))]

        self.plot = encode_plot(self.E.plot())
        self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot
        self.properties = [('Label', self.lmfdb_label),
                           (None, self.plot_link),
                           ('Conductor', '\(%s\)' % data['conductor']),
                           ('Discriminant', '\(%s\)' % data['disc']),
                           ('j-invariant', '%s' % data['j_inv_latex']),
                           ('CM', '%s' % data['CM']),
                           ('Rank', '\(%s\)' % mw['rank']),
                           ('Torsion Structure', '\(%s\)' % mw['tor_struct'])
                           ]

        self.title = "Elliptic Curve %s (Cremona label %s)" % (self.lmfdb_label, self.label)

        self.bread = [('Elliptic Curves', url_for("ecnf.index")),
                           ('$\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,' ')]

    def make_code_snippets(self):
        # read in code.yaml from current directory:

        _curdir = os.path.dirname(os.path.abspath(__file__))
        self.code =  yaml.load(open(os.path.join(_curdir, "code.yaml")))

        # Fill in placeholders for this specific curve:

        for lang in ['sage', 'pari', 'magma']:
            self.code['curve'][lang] = self.code['curve'][lang] % (self.data['ainvs'],self.label)

        for k in self.code:
            if k != 'prompt':
                for lang in self.code[k]:
                    self.code[k][lang] = self.code[k][lang].split("\n")
                    # remove final empty line
                    if len(self.code[k][lang][-1])==0:
                        self.code[k][lang] = self.code[k][lang][:-1]
Ejemplo n.º 8
0
class WebEC(object):
    """
    Class for an elliptic curve over Q
    """
    def __init__(self, dbdata):
        """
        Arguments:

            - dbdata: the data from the database
        """
        logger.debug("Constructing an instance of WebEC")
        self.__dict__.update(dbdata)
        # Next lines because the hyphens make trouble
        self.xintcoords = split_list(
            dbdata['x-coordinates_of_integral_points'])
        self.non_surjective_primes = dbdata['non-surjective_primes']
        # Next lines because the python identifiers cannot start with 2
        self.twoadic_index = dbdata['2adic_index']
        self.twoadic_log_level = dbdata['2adic_log_level']
        self.twoadic_gens = dbdata['2adic_gens']
        self.twoadic_label = dbdata['2adic_label']
        # All other fields are handled here
        self.make_curve()

    @staticmethod
    def by_label(label):
        """
        Searches for a specific elliptic curve in the curves
        collection by its label, which can be either in LMFDB or
        Cremona format.
        """
        try:
            N, iso, number = split_lmfdb_label(label)
            data = db_ec().find_one({"lmfdb_label": label})
        except AttributeError:
            try:
                N, iso, number = split_cremona_label(label)
                data = db_ec().find_one({"label": label})
            except AttributeError:
                return "Invalid label"  # caller must catch this and raise an error

        if data:
            return WebEC(data)
        return "Curve not found"  # caller must catch this and raise an error

    def make_curve(self):
        # To start with the data fields of self are just those from
        # the database.  We need to reformat these.

        # Old version: required constructing the actual elliptic curve
        # E, and computing some further data about it.

        # New version (May 2016): extra data fields now in the
        # database so we do not have to construct the curve or do any
        # computation with it on the fly.  As a failsafe the old way
        # is still included.

        data = self.data = {}
        try:
            data['ainvs'] = [int(c) for c in self.xainvs[1:-1].split(',')]
        except AttributeError:
            data['ainvs'] = [int(ai) for ai in self.ainvs]
        data['conductor'] = N = ZZ(self.conductor)
        data['j_invariant'] = QQ(str(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_str'] = unicode(str(data['j_invariant']))
        data['j_inv_latex'] = web_latex(data['j_invariant'])
        mw = self.mw = {}
        mw['rank'] = self.rank
        mw['int_points'] = ''
        if self.xintcoords:
            a1, a2, a3, a4, a6 = [ZZ(a) for a in data['ainvs']]

            def lift_x(x):
                f = ((x + a2) * x + a4) * x + a6
                b = (a1 * x + a3)
                d = (b * b + 4 * f).sqrt()
                return (x, (-b + d) / 2)

            mw['int_points'] = ', '.join(
                web_latex(lift_x(x)) for x in self.xintcoords)

        mw['generators'] = ''
        mw['heights'] = []
        if self.gens:
            mw['generators'] = [
                web_latex(tuple(P)) for P in parse_points(self.gens)
            ]

        mw['tor_order'] = self.torsion
        tor_struct = [int(c) for c in self.torsion_structure]
        if mw['tor_order'] == 1:
            mw['tor_struct'] = '\mathrm{Trivial}'
            mw['tor_gens'] = ''
        else:
            mw['tor_struct'] = ' \\times '.join(
                ['\Z/{%s}\Z' % n for n in tor_struct])
            mw['tor_gens'] = ', '.join(
                web_latex(tuple(P))
                for P in parse_points(self.torsion_generators))

        # try to get all the data we need from the database entry (now in self)
        try:
            data['equation'] = self.equation
            local_data = self.local_data
            D = self.signD * prod(
                [ld['p']**ld['ord_disc'] for ld in local_data])
            data['disc'] = D
            Nfac = Factorization([(ZZ(ld['p']), ld['ord_cond'])
                                  for ld in local_data])
            Dfac = Factorization([(ZZ(ld['p']), ld['ord_disc'])
                                  for ld in local_data],
                                 unit=ZZ(self.signD))

            data['minq_D'] = minqD = self.min_quad_twist['disc']
            minq_label = self.min_quad_twist['label']
            data['minq_label'] = db_ec().find_one(
                {'label': minq_label}, ['lmfdb_label'])['lmfdb_label']
            data['minq_info'] = '(itself)' if minqD == 1 else '(by %s)' % minqD
            try:
                data['degree'] = self.degree
            except AttributeError:
                data['degree'] = 0  # invalid, but will be displayed nicely
            mw['heights'] = self.heights
            if self.number == 1:
                data['an'] = self.anlist
                data['ap'] = self.aplist
            else:
                r = db_ec().find_one({
                    'lmfdb_iso': self.lmfdb_iso,
                    'number': 1
                }, ['anlist', 'aplist'])
                data['an'] = r['anlist']
                data['ap'] = r['aplist']

        # otherwise fall back to computing it from the curve
        except AttributeError:
            print("Falling back to constructing E")
            self.E = EllipticCurve(data['ainvs'])
            data['equation'] = web_latex(self.E)
            data['disc'] = D = self.E.discriminant()
            Nfac = N.factor()
            Dfac = D.factor()
            bad_primes = [p for p, e in Nfac]
            try:
                data['degree'] = self.degree
            except AttributeError:
                try:
                    data['degree'] = self.E.modular_degree()
                except RuntimeError:
                    data['degree'] = 0  # invalid, but will be displayed nicely
            minq, minqD = self.E.minimal_quadratic_twist()
            data['minq_D'] = minqD
            if minqD == 1:
                data['minq_label'] = self.lmfdb_label
                data['minq_info'] = '(itself)'
            else:
                # This relies on the minimal twist being in the
                # database, which is true when the database only
                # contains the Cremona database.  It would be a good
                # idea if, when the database is extended, we ensured
                # that for any curve included, all twists of smaller
                # conductor are also included.
                minq_ainvs = [str(c) for c in minq.ainvs()]
                data['minq_label'] = db_ec().find_one(
                    {
                        'jinv': str(self.E.j_invariant()),
                        'ainvs': minq_ainvs
                    }, ['lmfdb_label'])['lmfdb_label']
                data['minq_info'] = '(by %s)' % minqD

            if self.gens:
                self.generators = [self.E(g) for g in parse_points(self.gens)]
                mw['heights'] = [P.height() for P in self.generators]

            data['an'] = self.E.anlist(20, python_ints=True)
            data['ap'] = self.E.aplist(100, python_ints=True)
            self.local_data = local_data = []
            for p in bad_primes:
                ld = self.E.local_data(p, algorithm="generic")
                local_data_p = {}
                local_data_p['p'] = p
                local_data_p['cp'] = ld.tamagawa_number()
                local_data_p['kod'] = web_latex(ld.kodaira_symbol()).replace(
                    '$', '')
                local_data_p['red'] = ld.bad_reduction_type()
                rootno = -ld.bad_reduction_type()
                if rootno == 0:
                    rootno = self.E.root_number(p)
                local_data_p['rootno'] = rootno
                local_data_p['ord_cond'] = ld.conductor_valuation()
                local_data_p['ord_disc'] = ld.discriminant_valuation()
                local_data_p['ord_den_j'] = max(
                    0, -self.E.j_invariant().valuation(p))
                local_data.append(local_data_p)

        # If we got the data from the database, the root numbers may
        # not have been stored there, so we have to compute them.  If
        # there are additive primes this means constructing the curve.
        for ld in self.local_data:
            if not 'rootno' in ld:
                rootno = -ld['red']
                if rootno == 0:
                    try:
                        E = self.E
                    except AttributeError:
                        self.E = E = EllipticCurve(data['ainvs'])
                    rootno = E.root_number(ld['p'])
                ld['rootno'] = rootno

        minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label'])

        data['disc_factor'] = latex(Dfac)
        data['cond_factor'] = latex(Nfac)
        data['disc_latex'] = web_latex(D)
        data['cond_latex'] = web_latex(N)

        data['CMD'] = self.cm
        data['CM'] = "no"
        data['EndE'] = "\(\Z\)"
        if self.cm:
            data['CM'] = "yes (\(D=%s\))" % data['CMD']
            if data['CMD'] % 4 == 0:
                d4 = ZZ(data['CMD']) // 4
                data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4
            else:
                data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD']
            data['ST'] = st_link_by_name(1, 2, 'N(U(1))')
        else:
            data['ST'] = st_link_by_name(1, 2, 'SU(2)')

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

        try:
            data['galois_images'] = [
                trim_galois_image_code(s) for s in self.galois_images
            ]
            data['non_surjective_primes'] = self.non_surjective_primes
        except AttributeError:
            #print "No Galois image data"
            data['galois_images'] = []
            data['non_surjective_primes'] = []

        data['galois_data'] = [{
            'p': p,
            'image': im
        } for p, im in zip(data['non_surjective_primes'],
                           data['galois_images'])]

        cond, iso, num = split_lmfdb_label(self.lmfdb_label)
        self.class_url = url_for(".by_double_iso_label",
                                 conductor=N,
                                 iso_label=iso)
        self.ncurves = db_ec().count({'lmfdb_iso': self.lmfdb_iso})
        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]])

        if self.twoadic_gens:
            from sage.matrix.all import Matrix
            data['twoadic_gen_matrices'] = ','.join(
                [latex(Matrix(2, 2, M)) for M in self.twoadic_gens])
            data[
                'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html"

        # Leading term of L-function & BSD data
        bsd = self.bsd = {}
        r = self.rank
        if r >= 2:
            bsd['lder_name'] = "L^{(%s)}(E,1)/%s!" % (r, r)
        elif r:
            bsd['lder_name'] = "L'(E,1)"
        else:
            bsd['lder_name'] = "L(E,1)"

        bsd['reg'] = self.regulator
        bsd['omega'] = self.real_period
        bsd['sha'] = int(0.1 + self.sha_an)
        bsd['lder'] = self.special_value

        # 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)

        if self.iso == '990h':
            data['Gamma0optimal'] = bool(self.number == 3)
        else:
            data['Gamma0optimal'] = bool(self.number == 1)

        data['p_adic_data_exists'] = False
        if data['Gamma0optimal']:
            data['p_adic_data_exists'] = (padic_db().find({
                'lmfdb_iso':
                self.lmfdb_iso
            }).count()) > 0

        tamagawa_numbers = [ZZ(ld['cp']) for ld in 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
        ]
        bsd['tamagawa_factors'] = r'\cdot'.join(cp_fac)
        bsd['tamagawa_product'] = prod(tamagawa_numbers)

        data['newform'] = web_latex(
            PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True))
        data['newform_label'] = self.newform_label = newform_label(
            cond, 2, 1, iso)
        self.newform_link = url_for("emf.render_elliptic_modular_forms",
                                    level=cond,
                                    weight=2,
                                    character=1,
                                    label=iso)
        self.newform_exists_in_db = is_newform_in_db(self.newform_label)
        self._code = None

        self.class_url = url_for(".by_double_iso_label",
                                 conductor=N,
                                 iso_label=iso)
        self.friends = [('Isogeny class ' + self.lmfdb_iso, self.class_url),
                        ('Minimal quadratic twist %s %s' %
                         (data['minq_info'], data['minq_label']),
                         url_for(".by_triple_label",
                                 conductor=minq_N,
                                 iso_label=minq_iso,
                                 number=minq_number)),
                        ('All twists ',
                         url_for(".rational_elliptic_curves", jinv=self.jinv)),
                        ('L-function',
                         url_for("l_functions.l_function_ec_page",
                                 label=self.lmfdb_label))]
        if not self.cm:
            if N <= 300:
                self.friends += [('Symmetric square L-function',
                                  url_for("l_functions.l_function_ec_sym_page",
                                          power='2',
                                          label=self.lmfdb_iso))]
            if N <= 50:
                self.friends += [('Symmetric cube L-function',
                                  url_for("l_functions.l_function_ec_sym_page",
                                          power='3',
                                          label=self.lmfdb_iso))]
        if self.newform_exists_in_db:
            self.friends += [('Modular form ' + self.newform_label,
                              self.newform_link)]

        self.downloads = [('Download coefficients of q-expansion',
                           url_for(".download_EC_qexp",
                                   label=self.lmfdb_label,
                                   limit=1000)),
                          ('Download all stored data',
                           url_for(".download_EC_all",
                                   label=self.lmfdb_label)),
                          ('Download Magma code',
                           url_for(".ec_code_download",
                                   conductor=cond,
                                   iso=iso,
                                   number=num,
                                   label=self.lmfdb_label,
                                   download_type='magma')),
                          ('Download Sage code',
                           url_for(".ec_code_download",
                                   conductor=cond,
                                   iso=iso,
                                   number=num,
                                   label=self.lmfdb_label,
                                   download_type='sage')),
                          ('Download GP code',
                           url_for(".ec_code_download",
                                   conductor=cond,
                                   iso=iso,
                                   number=num,
                                   label=self.lmfdb_label,
                                   download_type='gp'))]

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

        self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot
        self.properties = [('Label', self.lmfdb_label), (None, self.plot_link),
                           ('Conductor', '\(%s\)' % data['conductor']),
                           ('Discriminant', '\(%s\)' % data['disc']),
                           ('j-invariant', '%s' % data['j_inv_latex']),
                           ('CM', '%s' % data['CM']),
                           ('Rank', '\(%s\)' % mw['rank']),
                           ('Torsion Structure', '\(%s\)' % mw['tor_struct'])]

        self.title = "Elliptic Curve %s (Cremona label %s)" % (
            self.lmfdb_label, self.label)

        self.bread = [('Elliptic Curves', url_for("ecnf.index")),
                      ('$\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, ' ')]

    def code(self):
        if self._code == None:
            self.make_code_snippets()
        return self._code

    def make_code_snippets(self):
        # read in code.yaml from current directory:

        _curdir = os.path.dirname(os.path.abspath(__file__))
        self._code = yaml.load(open(os.path.join(_curdir, "code.yaml")))

        # Fill in placeholders for this specific curve:

        for lang in ['sage', 'pari', 'magma']:
            self._code['curve'][lang] = self._code['curve'][lang] % (
                self.data['ainvs'], self.label)
        return
        for k in self._code:
            if k != 'prompt':
                for lang in self._code[k]:
                    self._code[k][lang] = self._code[k][lang].split("\n")
                    # remove final empty line
                    if len(self._code[k][lang][-1]) == 0:
                        self._code[k][lang] = self._code[k][lang][:-1]
Ejemplo n.º 9
0
class WebEC(object):
    """
    Class for an elliptic curve over Q
    """
    def __init__(self, dbdata):
        """
        Arguments:

            - dbdata: the data from the database
        """
        logger.info("Constructing an instance of ECisog_class")
        self.__dict__.update(dbdata)
        # Next lines because the hyphens make trouble
        self.xintcoords = parse_list(dbdata['x-coordinates_of_integral_points'])
        self.non_surjective_primes = dbdata['non-surjective_primes']
        self.make_curve()

    @staticmethod
    def by_label(label):
        """
        Searches for a specific elliptic curve in the curves
        collection by its label, which can be either in LMFDB or
        Cremona format.
        """
        print "curve label = %s" % label
        try:
            N, iso, number = lmfdb_label_regex.match(label).groups()
            data = db_ec().find_one({"lmfdb_label" : label})
        except AttributeError:
            try:
                N, iso, number = cremona_label_regex.match(label).groups()
                data = db_ec().find_one({"label" : label})
            except AttributeError:
                return "Invalid label" # caller must catch this and raise an error

        if data:
            return WebEC(data)
        return "Curve not found" # caller must catch this and raise an error

    def make_curve(self):
        # To start with the data fields of self are just those from
        # the database.  We need to reformat these, construct the
        # actual elliptic curve E, and compute some further (easy)
        # data about it.
        #

        # Weierstrass equation

        data = self.data = {}
        data['ainvs'] = [int(ai) for ai in self.ainvs]
        self.E = EllipticCurve(data['ainvs'])
        data['equation'] = web_latex(self.E)

        # conductor, j-invariant and discriminant

        data['conductor'] = N = ZZ(self.conductor)
        bad_primes = N.prime_factors()
        try:
            data['j_invariant'] = QQ(str(self.jinv))
        except KeyError:
            data['j_invariant'] = self.E.j_invariant()
        data['j_inv_factor'] = latex(0)
        if data['j_invariant']:
            data['j_inv_factor'] = latex(data['j_invariant'].factor())
        data['j_inv_str'] = unicode(str(data['j_invariant']))
        data['j_inv_latex'] = web_latex(data['j_invariant'])
        data['disc'] = self.E.discriminant()
        data['disc_latex'] = web_latex(data['disc'])
        data['disc_factor'] = latex(data['disc'].factor())
        data['cond_factor'] =latex(N.factor())
        data['cond_latex'] = web_latex(N)

        # CM and endomorphism ring

        data['CMD'] = 0
        data['CM'] = "no"
        data['EndE'] = "\(\Z\)"
        if self.E.has_cm():
            data['CMD'] = self.E.cm_discriminant()
            data['CM'] = "yes (\(D=%s\))" % data['CMD']
            if data['CMD']%4==0:
                d4 = ZZ(data['CMD'])//4
                data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4
            else:
                data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD']

        # modular degree

        try:
            data['degree'] = self.degree
        except AttributeError:
            try:
                data['degree'] = self.E.modular_degree()
            except RuntimeError:
                data['degree']  # invalid, but will be displayed nicely

        # Minimal quadratic twist

        E_pari = self.E.pari_curve(prec=200)
        from sage.libs.pari.all import PariError
        try:
            minq = self.E.minimal_quadratic_twist()[0]
        except PariError:  # this does occur with 164411a1
            ec.debug("PariError computing minimal quadratic twist of elliptic curve %s" % lmfdb_label)
            minq = self.E
        if self.E == minq:
            data['minq_label'] = self.lmfdb_label
        else:
            minq_ainvs = [str(c) for c in minq.ainvs()]
            data['minq_label'] = db_ec().find_one({'ainvs': minq_ainvs})['lmfdb_label']

        # rational and integral points

        mw = self.mw = {}

        xintpoints_projective = [self.E.lift_x(x) for x in self.xintcoords]
        xintpoints = [P.xy() for P in xintpoints_projective]
        mw['int_points'] = ', '.join(web_latex(P) for P in xintpoints)

        # Generators of infinite order

        mw['rank'] = self.rank
        try:
            mw['generators'] = ', '.join(web_latex(self.E(g).xy()) for g in parse_points(self.gens))
        except AttributeError:
            mw['generators'] = ''

        # Torsion subgroup: order, structure, generators

        mw['tor_order'] = self.torsion
        tor_struct = [int(c) for c in self.torsion_structure]
        if mw['tor_order'] == 1:
            mw['tor_struct'] = '\mathrm{Trivial}'
            mw['tor_gens'] = ''
        else:
            mw['tor_struct'] = ' \\times '.join(['\Z/{%s}\Z' % n
                                                 for n in tor_struct])
            mw['tor_gens'] = ', '.join(web_latex(self.E(g).xy()) for g in parse_points(self.torsion_generators))

        # Images of Galois representations

        try:
            data['galois_images'] = [trim_galois_image_code(s) for s in self.galois_images]
            data['non_surjective_primes'] = self.non_surjective_primes
        except AttributeError:
            print "No Galois image data"
            data['galois_images'] = []
            data['non_surjective_primes'] = []

        data['galois_data'] = [{'p': p,'image': im }
                               for p,im in zip(data['non_surjective_primes'],
                                               data['galois_images'])]

        # Leading term of L-function & BSD data

        bsd = self.bsd = {}

        if mw['rank'] >= 2:
            bsd['lder_name'] = "L^{(%s)}(E,1)" % mw['rank']
        elif mw['rank']:
            bsd['lder_name'] = "L'(E,1)"
        else:
            bsd['lder_name'] = "L(E,1)"

        bsd['reg'] = self.regulator
        bsd['omega'] = self.real_period
        bsd['sha'] = int(0.1+self.sha_an)
        bsd['lder'] = self.special_value

        # 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)

        if self.iso == '990h':
            data['Gamma0optimal'] = bool(self.number == 3)
        else:
            data['Gamma0optimal'] = bool(self.number == 1)


        data['p_adic_data_exists'] = False
        if data['Gamma0optimal']:
            data['p_adic_data_exists'] = (padic_db().find({'lmfdb_iso': self.lmfdb_iso}).count()) > 0
        data['p_adic_primes'] = [p for p in sage.all.prime_range(5, 100)
                                 if self.E.is_ordinary(p) and not p.divides(N)]

        # Local data

        local_data = self.local_data = []
        # if we use E.tamagawa_numbers() it calls E.local_data(p) which
        # crashes on some curves e.g. 164411a1
        tamagawa_numbers = []
        for p in bad_primes:
            local_info = self.E.local_data(p, algorithm="generic")
            local_data_p = {}
            local_data_p['p'] = p
            local_data_p['tamagawa_number'] = local_info.tamagawa_number()
            tamagawa_numbers.append(ZZ(local_info.tamagawa_number()))
            local_data_p['kodaira_symbol'] = web_latex(local_info.kodaira_symbol()).replace('$', '')
            local_data_p['reduction_type'] = local_info.bad_reduction_type()
            local_data.append(local_data_p)

        bsd['tamagawa_factors'] = r' \cdot '.join(str(c.factor()) for c in tamagawa_numbers)
        bsd['tamagawa_product'] = sage.misc.all.prod(tamagawa_numbers)

        mod_form_iso = lmfdb_label_regex.match(self.lmfdb_iso).groups()[1]
        data['newform'] =  web_latex(self.E.q_eigenform(10))

        self.friends = [
            ('Isogeny class ' + self.lmfdb_iso, url_for(".by_ec_label", label=self.lmfdb_iso)),
            ('Minimal quadratic twist ' + data['minq_label'], url_for(".by_ec_label", label=data['minq_label'])),
            ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)),
            ('L-function', url_for("l_functions.l_function_ec_page", label=self.lmfdb_label)),
            ('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso)),
            ('Symmetric 4th power L-function', url_for("l_functions.l_function_ec_sym_page", power='4', label=self.lmfdb_iso)),
            ('Modular form ' + self.lmfdb_iso.replace('.', '.2'), url_for("emf.render_elliptic_modular_forms", level=int(N), weight=2, character=0, label=mod_form_iso))]

        self.downloads = [('Download coeffients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=100)),
                          ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label))]

        self.plot = encode_plot(self.E.plot())
        self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot
        self.properties = [('Label', self.lmfdb_label),
                           (None, self.plot_link),
                           ('Conductor', '\(%s\)' % data['conductor']),
                           ('Discriminant', '\(%s\)' % data['disc']),
                           ('j-invariant', '%s' % data['j_inv_latex']),
                           ('CM', '%s' % data['CM']),
                           ('Rank', '\(%s\)' % mw['rank']),
                           ('Torsion Structure', '\(%s\)' % mw['tor_struct'])
                           ]

        if self.lmfdb_iso == self.iso:
            self.title = "Elliptic Curve %s" % self.lmfdb_label
        else:
            self.title = "Elliptic Curve %s (Cremona label %s)" % (self.lmfdb_label, self.label)

        self.bread = [('Elliptic Curves ', url_for(".rational_elliptic_curves")), ('isogeny class %s' % self.lmfdb_iso, ' ')]
Ejemplo n.º 10
0
class WebEC(object):
    """
    Class for an elliptic curve over Q
    """

    def __init__(self, dbdata):
        """
        Arguments:

            - dbdata: the data from the database
        """
        logger.debug("Constructing an instance of ECisog_class")
        self.__dict__.update(dbdata)
        # Next lines because the hyphens make trouble
        self.xintcoords = split_list(dbdata["x-coordinates_of_integral_points"])
        self.non_surjective_primes = dbdata["non-surjective_primes"]
        # Next lines because the python identifiers cannot start with 2
        self.twoadic_index = dbdata["2adic_index"]
        self.twoadic_log_level = dbdata["2adic_log_level"]
        self.twoadic_gens = dbdata["2adic_gens"]
        self.twoadic_label = dbdata["2adic_label"]
        # All other fields are handled here
        self.make_curve()

    @staticmethod
    def by_label(label):
        """
        Searches for a specific elliptic curve in the curves
        collection by its label, which can be either in LMFDB or
        Cremona format.
        """
        try:
            N, iso, number = split_lmfdb_label(label)
            data = db_ec().find_one({"lmfdb_label": label})
        except AttributeError:
            try:
                N, iso, number = split_cremona_label(label)
                data = db_ec().find_one({"label": label})
            except AttributeError:
                return "Invalid label"  # caller must catch this and raise an error

        if data:
            return WebEC(data)
        return "Curve not found"  # caller must catch this and raise an error

    def make_curve(self):
        # To start with the data fields of self are just those from
        # the database.  We need to reformat these, construct the
        # actual elliptic curve E, and compute some further (easy)
        # data about it.
        #

        # Weierstrass equation

        data = self.data = {}
        data["ainvs"] = [int(ai) for ai in self.ainvs]
        self.E = EllipticCurve(data["ainvs"])
        data["equation"] = web_latex(self.E)

        # conductor, j-invariant and discriminant

        data["conductor"] = N = ZZ(self.conductor)
        bad_primes = N.prime_factors()
        try:
            data["j_invariant"] = QQ(str(self.jinv))
        except KeyError:
            data["j_invariant"] = self.E.j_invariant()
        data["j_inv_factor"] = latex(0)
        if data["j_invariant"]:
            data["j_inv_factor"] = latex(data["j_invariant"].factor())
        data["j_inv_str"] = unicode(str(data["j_invariant"]))
        data["j_inv_latex"] = web_latex(data["j_invariant"])
        data["disc"] = D = self.E.discriminant()
        data["disc_latex"] = web_latex(data["disc"])
        data["disc_factor"] = latex(data["disc"].factor())
        data["cond_factor"] = latex(N.factor())
        data["cond_latex"] = web_latex(N)

        # CM and endomorphism ring

        data["CMD"] = self.cm
        data["CM"] = "no"
        data["EndE"] = "\(\Z\)"
        if self.cm:
            data["CM"] = "yes (\(D=%s\))" % data["CMD"]
            if data["CMD"] % 4 == 0:
                d4 = ZZ(data["CMD"]) // 4
                data["EndE"] = "\(\Z[\sqrt{%s}]\)" % d4
            else:
                data["EndE"] = "\(\Z[(1+\sqrt{%s})/2]\)" % data["CMD"]
            data["ST"] = '<a href="%s">$%s$</a>' % (url_for("st.by_label", label="1.2.N(U(1))"), "N(\\mathrm{U}(1))")
        else:
            data["ST"] = '<a href="%s">$%s$</a>' % (url_for("st.by_label", label="1.2.SU(2)"), "\\mathrm{SU}(2)")

        # modular degree

        try:
            data["degree"] = self.degree
        except AttributeError:
            try:
                data["degree"] = self.E.modular_degree()
            except RuntimeError:
                data["degree"]  # invalid, but will be displayed nicely

        # Minimal quadratic twist

        E_pari = self.E.pari_curve()
        from sage.libs.pari.all import PariError

        try:
            minq, minqD = self.E.minimal_quadratic_twist()
        except PariError:  # this does occur with 164411a1
            ec.debug("PariError computing minimal quadratic twist of elliptic curve %s" % lmfdb_label)
            minq = self.E
            minqD = 1
        data["minq_D"] = minqD
        if self.E == minq:
            data["minq_label"] = self.lmfdb_label
            data["minq_info"] = "(itself)"
        else:
            minq_ainvs = [str(c) for c in minq.ainvs()]
            data["minq_label"] = db_ec().find_one({"jinv": str(self.E.j_invariant()), "ainvs": minq_ainvs})[
                "lmfdb_label"
            ]
            data["minq_info"] = "(by %s)" % minqD

        minq_N, minq_iso, minq_number = split_lmfdb_label(data["minq_label"])

        # rational and integral points

        mw = self.mw = {}

        xintpoints_projective = [self.E.lift_x(x) for x in self.xintcoords]
        xintpoints = [P.xy() for P in xintpoints_projective]
        mw["int_points"] = ", ".join(web_latex(P) for P in xintpoints)

        # Generators of infinite order

        mw["rank"] = self.rank
        try:
            self.generators = [self.E(g) for g in parse_points(self.gens)]
            mw["generators"] = [web_latex(P.xy()) for P in self.generators]
            mw["heights"] = [P.height() for P in self.generators]
        except AttributeError:
            mw["generators"] = ""
            mw["heights"] = []

        # Torsion subgroup: order, structure, generators

        mw["tor_order"] = self.torsion
        tor_struct = [int(c) for c in self.torsion_structure]
        if mw["tor_order"] == 1:
            mw["tor_struct"] = "\mathrm{Trivial}"
            mw["tor_gens"] = ""
        else:
            mw["tor_struct"] = " \\times ".join(["\Z/{%s}\Z" % n for n in tor_struct])
            mw["tor_gens"] = ", ".join(web_latex(self.E(g).xy()) for g in parse_points(self.torsion_generators))

        # Images of Galois representations

        try:
            data["galois_images"] = [trim_galois_image_code(s) for s in self.galois_images]
            data["non_surjective_primes"] = self.non_surjective_primes
        except AttributeError:
            # print "No Galois image data"
            data["galois_images"] = []
            data["non_surjective_primes"] = []

        data["galois_data"] = [
            {"p": p, "image": im} for p, im in zip(data["non_surjective_primes"], data["galois_images"])
        ]

        if self.twoadic_gens:
            from sage.matrix.all import Matrix

            data["twoadic_gen_matrices"] = ",".join([latex(Matrix(2, 2, M)) for M in self.twoadic_gens])
            data["twoadic_rouse_url"] = ROUSE_URL_PREFIX + self.twoadic_label + ".html"
        # Leading term of L-function & BSD data

        bsd = self.bsd = {}

        r = self.rank
        if r >= 2:
            bsd["lder_name"] = "L^{(%s)}(E,1)/%s!" % (r, r)
        elif r:
            bsd["lder_name"] = "L'(E,1)"
        else:
            bsd["lder_name"] = "L(E,1)"

        bsd["reg"] = self.regulator
        bsd["omega"] = self.real_period
        bsd["sha"] = int(0.1 + self.sha_an)
        bsd["lder"] = self.special_value

        # 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)

        if self.iso == "990h":
            data["Gamma0optimal"] = bool(self.number == 3)
        else:
            data["Gamma0optimal"] = bool(self.number == 1)

        data["p_adic_data_exists"] = False
        if data["Gamma0optimal"]:
            data["p_adic_data_exists"] = (padic_db().find({"lmfdb_iso": self.lmfdb_iso}).count()) > 0
        data["p_adic_primes"] = [p for p in sage.all.prime_range(5, 100) if self.E.is_ordinary(p) and not p.divides(N)]

        # Local data

        local_data = self.local_data = []
        # if we use E.tamagawa_numbers() it calls E.local_data(p) which
        # used to crash on some curves e.g. 164411a1
        tamagawa_numbers = []
        for p in bad_primes:
            local_info = self.E.local_data(p, algorithm="generic")
            local_data_p = {}
            local_data_p["p"] = p
            local_data_p["tamagawa_number"] = local_info.tamagawa_number()
            tamagawa_numbers.append(ZZ(local_info.tamagawa_number()))
            local_data_p["kodaira_symbol"] = web_latex(local_info.kodaira_symbol()).replace("$", "")
            local_data_p["reduction_type"] = local_info.bad_reduction_type()
            local_data_p["ord_cond"] = local_info.conductor_valuation()
            local_data_p["ord_disc"] = local_info.discriminant_valuation()
            local_data_p["ord_den_j"] = max(0, -self.E.j_invariant().valuation(p))
            local_data.append(local_data_p)

        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]
        bsd["tamagawa_factors"] = r"\cdot".join(cp_fac)
        bsd["tamagawa_product"] = sage.misc.all.prod(tamagawa_numbers)

        cond, iso, num = split_lmfdb_label(self.lmfdb_label)
        data["newform"] = web_latex(self.E.q_eigenform(10))
        self.newform_label = newform_label(cond, 2, 1, iso)
        self.newform_link = url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso)
        newform_exists_in_db = is_newform_in_db(self.newform_label)
        self._code = None

        self.friends = [
            ("Isogeny class " + self.lmfdb_iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)),
            (
                "Minimal quadratic twist %s %s" % (data["minq_info"], data["minq_label"]),
                url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number),
            ),
            ("All twists ", url_for(".rational_elliptic_curves", jinv=self.jinv)),
            ("L-function", url_for("l_functions.l_function_ec_page", label=self.lmfdb_label)),
        ]
        if not self.cm:
            if N <= 300:
                self.friends += [
                    (
                        "Symmetric square L-function",
                        url_for("l_functions.l_function_ec_sym_page", power="2", label=self.lmfdb_iso),
                    )
                ]
            if N <= 50:
                self.friends += [
                    (
                        "Symmetric cube L-function",
                        url_for("l_functions.l_function_ec_sym_page", power="3", label=self.lmfdb_iso),
                    )
                ]
        if newform_exists_in_db:
            self.friends += [("Modular form " + self.newform_label, self.newform_link)]

        self.downloads = [
            ("Download coefficients of q-expansion", url_for(".download_EC_qexp", label=self.lmfdb_label, limit=100)),
            ("Download all stored data", url_for(".download_EC_all", label=self.lmfdb_label)),
            (
                "Download Magma code",
                url_for(
                    ".ec_code_download",
                    conductor=cond,
                    iso=iso,
                    number=num,
                    label=self.lmfdb_label,
                    download_type="magma",
                ),
            ),
            (
                "Download Sage code",
                url_for(
                    ".ec_code_download",
                    conductor=cond,
                    iso=iso,
                    number=num,
                    label=self.lmfdb_label,
                    download_type="sage",
                ),
            ),
            (
                "Download GP code",
                url_for(
                    ".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type="gp"
                ),
            ),
        ]

        self.plot = encode_plot(self.E.plot())
        self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot
        self.properties = [
            ("Label", self.lmfdb_label),
            (None, self.plot_link),
            ("Conductor", "\(%s\)" % data["conductor"]),
            ("Discriminant", "\(%s\)" % data["disc"]),
            ("j-invariant", "%s" % data["j_inv_latex"]),
            ("CM", "%s" % data["CM"]),
            ("Rank", "\(%s\)" % mw["rank"]),
            ("Torsion Structure", "\(%s\)" % mw["tor_struct"]),
        ]

        self.title = "Elliptic Curve %s (Cremona label %s)" % (self.lmfdb_label, self.label)

        self.bread = [
            ("Elliptic Curves", url_for("ecnf.index")),
            ("$\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, " "),
        ]

    def code(self):
        if self._code == None:
            self.make_code_snippets()
        return self._code

    def make_code_snippets(self):
        # read in code.yaml from current directory:

        _curdir = os.path.dirname(os.path.abspath(__file__))
        self._code = yaml.load(open(os.path.join(_curdir, "code.yaml")))

        # Fill in placeholders for this specific curve:

        for lang in ["sage", "pari", "magma"]:
            self._code["curve"][lang] = self._code["curve"][lang] % (self.data["ainvs"], self.label)

        for k in self._code:
            if k != "prompt":
                for lang in self._code[k]:
                    self._code[k][lang] = self._code[k][lang].split("\n")
                    # remove final empty line
                    if len(self._code[k][lang][-1]) == 0:
                        self._code[k][lang] = self._code[k][lang][:-1]
Ejemplo n.º 11
0
def curves(line, verbose=False):
    r""" Parses one line from a curves file.  Returns the label and a dict
    containing fields with keys 'field_label', 'degree', 'signature',
    'abs_disc', 'label', 'short_label', conductor_label',
    'conductor_ideal', 'conductor_norm', 'iso_label', 'iso_nlabel',
    'number', 'ainvs', 'jinv', 'cm', 'q_curve', 'base_change',
    'torsion_order', 'torsion_structure', 'torsion_gens'; and (added
    May 2016): 'equation', 'local_data', 'non_min_p', 'minD'

    Input line fields (13):

    field_label conductor_label iso_label number conductor_ideal conductor_norm a1 a2 a3 a4 a6 cm base_change

    Sample input line:

    2.0.4.1 65.18.1 a 1 [65,18,1] 65 1,1 1,1 0,1 -1,1 -1,0 0 0
    """
    # Parse the line and form the full label:
    data = split(line)
    if len(data) != 13:
        print "line %s does not have 13 fields, skipping" % line
    field_label = data[0]       # string
    IQF_flag = field_label.split(".")[:2] == ['2','0']
    K = nf_lookup(field_label) if IQF_flag else None
    conductor_label = data[1]   # string
    # convert label (does nothing except for imaginary quadratic)
    conductor_label = convert_conductor_label(field_label, conductor_label)
    iso_label = data[2]         # string
    iso_nlabel = numerify_iso_label(iso_label)         # int
    number = int(data[3])       # int
    short_class_label = "%s-%s" % (conductor_label, iso_label)
    short_label = "%s%s" % (short_class_label, str(number))
    class_label = "%s-%s" % (field_label, short_class_label)
    label = "%s-%s" % (field_label, short_label)

    conductor_ideal = data[4]     # string
    conductor_norm = int(data[5]) # int
    ainvs = ";".join(data[6:11])  # one string joining 5 NFelt strings
    cm = data[11]                 # int or '?'
    if cm != '?':
        cm = int(cm)
    q_curve = (data[12] == '1')   # bool

    # Create the field and curve to compute the j-invariant:
    dummy, deg, sig, abs_disc = field_data(field_label)
    K = nf_lookup(field_label)
    #print("Field %s created, gen_name = %s" % (field_label,str(K.gen())))
    ainvsK = parse_ainvs(K,ainvs)  # list of K-elements
    E = EllipticCurve(ainvsK)
    #print("{} created with disc = {}, N(disc)={}".format(E,K.ideal(E.discriminant()).factor(),E.discriminant().norm().factor()))
    j = E.j_invariant()
    jinv = NFelt(j)
    if cm == '?':
        cm = get_cm(j)
        if cm:
            print "cm=%s for j=%s" % (cm, j)

    # Here we should check that the conductor of the constructed curve
    # agrees with the input conductor.
    N = ideal_from_string(K,conductor_ideal)
    NE = E.conductor()
    if N=="wrong" or N!=NE:
        print("Wrong conductor ideal {} for label {}, using actual conductor {} instead".format(conductor_ideal,label,NE))
        conductor_ideal = ideal_to_string(NE)
        N = NE

    # get torsion order, structure and generators:
    torgroup = E.torsion_subgroup()
    ntors = int(torgroup.order())
    torstruct = [int(n) for n in list(torgroup.invariants())]
    torgens = [point_string(P.element()) for P in torgroup.gens()]

    # get label of elliptic curve over Q for base_change cases (a
    # subset of Q-curves)

    if True:  # q_curve: now we have not precomputed Q-curve status
              # but still want to test for base change!
        if verbose:
            print("testing {} for base-change...".format(label))
        E1list = E.descend_to(QQ)
        if len(E1list):
            base_change = [cremona_to_lmfdb(E1.label()) for E1 in E1list]
            if verbose:
                print "%s is base change of %s" % (label, base_change)
        else:
            base_change = []
            # print "%s is a Q-curve, but not base-change..." % label
    else:
        base_change = []

    # NB if this is not a global minimal model then local_data may
    # include a prime at which we have good reduction.  This causes no
    # problems except that the bad_reduction_type is then None which
    # cannot be converted to an integer.  The bad reduction types are
    # coded as (Sage) integers in {-1,0,1}.
    local_data = [{'p': ideal_to_string(ld.prime()),
                   'normp': str(ld.prime().norm()),
                   'ord_cond':int(ld.conductor_valuation()),
                   'ord_disc':int(ld.discriminant_valuation()),
                   'ord_den_j':int(max(0,-(E.j_invariant().valuation(ld.prime())))),
                   'red':None if ld.bad_reduction_type()==None else int(ld.bad_reduction_type()),
                   'kod':web_latex(ld.kodaira_symbol()).replace('$',''),
                   'cp':int(ld.tamagawa_number())}
                  for ld in E.local_data()]

    non_minimal_primes = [ideal_to_string(P) for P in E.non_minimal_primes()]
    minD = ideal_to_string(E.minimal_discriminant_ideal())

    edata = {
        'field_label': field_label,
        'degree': deg,
        'signature': sig,
        'abs_disc': abs_disc,
        'class_label': class_label,
        'short_class_label': short_class_label,
        'label': label,
        'short_label': short_label,
        'conductor_label': conductor_label,
        'conductor_ideal': conductor_ideal,
        'conductor_norm': conductor_norm,
        'iso_label': iso_label,
        'iso_nlabel': iso_nlabel,
        'number': number,
        'ainvs': ainvs,
        'jinv': jinv,
        'cm': cm,
        'q_curve': q_curve,
        'base_change': base_change,
        'torsion_order': ntors,
        'torsion_structure': torstruct,
        'torsion_gens': torgens,
        'equation': web_latex(E),
        'local_data': local_data,
        'minD': minD,
        'non_min_p': non_minimal_primes,
    }

    return label, edata
Ejemplo n.º 12
0
class WebEC(object):
    """
    Class for an elliptic curve over Q
    """
    def __init__(self, dbdata):
        """
        Arguments:

            - dbdata: the data from the database
        """
        logger.debug("Constructing an instance of ECisog_class")
        self.__dict__.update(dbdata)
        # Next lines because the hyphens make trouble
        self.xintcoords = parse_list(
            dbdata['x-coordinates_of_integral_points'])
        self.non_surjective_primes = dbdata['non-surjective_primes']
        # Next lines because the python identifiers cannot start with 2
        self.twoadic_index = dbdata['2adic_index']
        self.twoadic_log_level = dbdata['2adic_log_level']
        self.twoadic_gens = dbdata['2adic_gens']
        self.twoadic_label = dbdata['2adic_label']
        # All other fields are handled here
        self.make_curve()

    @staticmethod
    def by_label(label):
        """
        Searches for a specific elliptic curve in the curves
        collection by its label, which can be either in LMFDB or
        Cremona format.
        """
        try:
            N, iso, number = split_lmfdb_label(label)
            data = db_ec().find_one({"lmfdb_label": label})
        except AttributeError:
            try:
                N, iso, number = split_cremona_label(label)
                data = db_ec().find_one({"label": label})
            except AttributeError:
                return "Invalid label"  # caller must catch this and raise an error

        if data:
            return WebEC(data)
        return "Curve not found"  # caller must catch this and raise an error

    def make_curve(self):
        # To start with the data fields of self are just those from
        # the database.  We need to reformat these, construct the
        # actual elliptic curve E, and compute some further (easy)
        # data about it.
        #

        # Weierstrass equation

        data = self.data = {}
        data['ainvs'] = [int(ai) for ai in self.ainvs]
        self.E = EllipticCurve(data['ainvs'])
        data['equation'] = web_latex(self.E)

        # conductor, j-invariant and discriminant

        data['conductor'] = N = ZZ(self.conductor)
        bad_primes = N.prime_factors()
        try:
            data['j_invariant'] = QQ(str(self.jinv))
        except KeyError:
            data['j_invariant'] = self.E.j_invariant()
        data['j_inv_factor'] = latex(0)
        if data['j_invariant']:
            data['j_inv_factor'] = latex(data['j_invariant'].factor())
        data['j_inv_str'] = unicode(str(data['j_invariant']))
        data['j_inv_latex'] = web_latex(data['j_invariant'])
        data['disc'] = D = self.E.discriminant()
        data['disc_latex'] = web_latex(data['disc'])
        data['disc_factor'] = latex(data['disc'].factor())
        data['cond_factor'] = latex(N.factor())
        data['cond_latex'] = web_latex(N)

        # CM and endomorphism ring

        data['CMD'] = self.cm
        data['CM'] = "no"
        data['EndE'] = "\(\Z\)"
        if self.cm:
            data['CM'] = "yes (\(D=%s\))" % data['CMD']
            if data['CMD'] % 4 == 0:
                d4 = ZZ(data['CMD']) // 4
                data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4
            else:
                data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD']

        # modular degree

        try:
            data['degree'] = self.degree
        except AttributeError:
            try:
                data['degree'] = self.E.modular_degree()
            except RuntimeError:
                data['degree']  # invalid, but will be displayed nicely

        # Minimal quadratic twist

        E_pari = self.E.pari_curve()
        from sage.libs.pari.all import PariError
        try:
            minq, minqD = self.E.minimal_quadratic_twist()
        except PariError:  # this does occur with 164411a1
            ec.debug(
                "PariError computing minimal quadratic twist of elliptic curve %s"
                % lmfdb_label)
            minq = self.E
            minqD = 1
        data['minq_D'] = minqD
        if self.E == minq:
            data['minq_label'] = self.lmfdb_label
            data['minq_info'] = '(itself)'
        else:
            minq_ainvs = [str(c) for c in minq.ainvs()]
            data['minq_label'] = db_ec().find_one({'ainvs':
                                                   minq_ainvs})['lmfdb_label']
            data['minq_info'] = '(by %s)' % minqD

        minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label'])

        # rational and integral points

        mw = self.mw = {}

        xintpoints_projective = [self.E.lift_x(x) for x in self.xintcoords]
        xintpoints = [P.xy() for P in xintpoints_projective]
        mw['int_points'] = ', '.join(web_latex(P) for P in xintpoints)

        # Generators of infinite order

        mw['rank'] = self.rank
        try:
            self.generators = [self.E(g) for g in parse_points(self.gens)]
            mw['generators'] = [web_latex(P.xy()) for P in self.generators]
            mw['heights'] = [P.height() for P in self.generators]
        except AttributeError:
            mw['generators'] = ''
            mw['heights'] = []

        # Torsion subgroup: order, structure, generators

        mw['tor_order'] = self.torsion
        tor_struct = [int(c) for c in self.torsion_structure]
        if mw['tor_order'] == 1:
            mw['tor_struct'] = '\mathrm{Trivial}'
            mw['tor_gens'] = ''
        else:
            mw['tor_struct'] = ' \\times '.join(
                ['\Z/{%s}\Z' % n for n in tor_struct])
            mw['tor_gens'] = ', '.join(
                web_latex(self.E(g).xy())
                for g in parse_points(self.torsion_generators))

        # Images of Galois representations

        try:
            data['galois_images'] = [
                trim_galois_image_code(s) for s in self.galois_images
            ]
            data['non_surjective_primes'] = self.non_surjective_primes
        except AttributeError:
            #print "No Galois image data"
            data['galois_images'] = []
            data['non_surjective_primes'] = []

        data['galois_data'] = [{
            'p': p,
            'image': im
        } for p, im in zip(data['non_surjective_primes'],
                           data['galois_images'])]

        if self.twoadic_gens:
            from sage.matrix.all import Matrix
            data['twoadic_gen_matrices'] = ','.join(
                [latex(Matrix(2, 2, M)) for M in self.twoadic_gens])
            data[
                'twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html"
        # Leading term of L-function & BSD data

        bsd = self.bsd = {}

        r = self.rank
        if r >= 2:
            bsd['lder_name'] = "L^{(%s)}(E,1)/%s!" % (r, r)
        elif r:
            bsd['lder_name'] = "L'(E,1)"
        else:
            bsd['lder_name'] = "L(E,1)"

        bsd['reg'] = self.regulator
        bsd['omega'] = self.real_period
        bsd['sha'] = int(0.1 + self.sha_an)
        bsd['lder'] = self.special_value

        # 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)

        if self.iso == '990h':
            data['Gamma0optimal'] = bool(self.number == 3)
        else:
            data['Gamma0optimal'] = bool(self.number == 1)

        data['p_adic_data_exists'] = False
        if data['Gamma0optimal']:
            data['p_adic_data_exists'] = (padic_db().find({
                'lmfdb_iso':
                self.lmfdb_iso
            }).count()) > 0
        data['p_adic_primes'] = [
            p for p in sage.all.prime_range(5, 100)
            if self.E.is_ordinary(p) and not p.divides(N)
        ]

        # Local data

        local_data = self.local_data = []
        # if we use E.tamagawa_numbers() it calls E.local_data(p) which
        # used to crash on some curves e.g. 164411a1
        tamagawa_numbers = []
        for p in bad_primes:
            local_info = self.E.local_data(p, algorithm="generic")
            local_data_p = {}
            local_data_p['p'] = p
            local_data_p['tamagawa_number'] = local_info.tamagawa_number()
            tamagawa_numbers.append(ZZ(local_info.tamagawa_number()))
            local_data_p['kodaira_symbol'] = web_latex(
                local_info.kodaira_symbol()).replace('$', '')
            local_data_p['reduction_type'] = local_info.bad_reduction_type()
            local_data_p['ord_cond'] = local_info.conductor_valuation()
            local_data_p['ord_disc'] = local_info.discriminant_valuation()
            local_data_p['ord_den_j'] = max(0,
                                            -self.E.j_invariant().valuation(p))
            local_data.append(local_data_p)

        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
        ]
        bsd['tamagawa_factors'] = r'\cdot'.join(cp_fac)
        bsd['tamagawa_product'] = sage.misc.all.prod(tamagawa_numbers)

        cond, iso, num = split_lmfdb_label(self.lmfdb_label)
        data['newform'] = web_latex(self.E.q_eigenform(10))

        self.make_code_snippets()

        self.friends = [('Isogeny class ' + self.lmfdb_iso,
                         url_for(".by_double_iso_label",
                                 conductor=N,
                                 iso_label=iso)),
                        ('Minimal quadratic twist %s %s' %
                         (data['minq_info'], data['minq_label']),
                         url_for(".by_triple_label",
                                 conductor=minq_N,
                                 iso_label=minq_iso,
                                 number=minq_number)),
                        ('All twists ',
                         url_for(".rational_elliptic_curves", jinv=self.jinv)),
                        ('L-function',
                         url_for("l_functions.l_function_ec_page",
                                 label=self.lmfdb_label)),
                        ('Symmetric square L-function',
                         url_for("l_functions.l_function_ec_sym_page",
                                 power='2',
                                 label=self.lmfdb_iso)),
                        ('Symmetric 4th power L-function',
                         url_for("l_functions.l_function_ec_sym_page",
                                 power='4',
                                 label=self.lmfdb_iso)),
                        ('Modular form ' + self.lmfdb_iso.replace('.', '.2'),
                         url_for("emf.render_elliptic_modular_forms",
                                 level=int(N),
                                 weight=2,
                                 character=1,
                                 label=iso))]

        self.downloads = [('Download coefficients of q-expansion',
                           url_for(".download_EC_qexp",
                                   label=self.lmfdb_label,
                                   limit=100)),
                          ('Download all stored data',
                           url_for(".download_EC_all",
                                   label=self.lmfdb_label))]

        self.plot = encode_plot(self.E.plot())
        self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot
        self.properties = [('Label', self.lmfdb_label), (None, self.plot_link),
                           ('Conductor', '\(%s\)' % data['conductor']),
                           ('Discriminant', '\(%s\)' % data['disc']),
                           ('j-invariant', '%s' % data['j_inv_latex']),
                           ('CM', '%s' % data['CM']),
                           ('Rank', '\(%s\)' % mw['rank']),
                           ('Torsion Structure', '\(%s\)' % mw['tor_struct'])]

        self.title = "Elliptic Curve %s (Cremona label %s)" % (
            self.lmfdb_label, self.label)

        self.bread = [('Elliptic Curves', url_for("ecnf.index")),
                      ('$\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, ' ')]

    def make_code_snippets(self):
        # read in code.yaml from current directory:

        _curdir = os.path.dirname(os.path.abspath(__file__))
        self.code = yaml.load(open(os.path.join(_curdir, "code.yaml")))

        # Fill in placeholders for this specific curve:

        for lang in ['sage', 'pari', 'magma']:
            self.code['curve'][lang] = self.code['curve'][lang] % (
                self.data['ainvs'], self.label)

        for k in self.code:
            if k != 'prompt':
                for lang in self.code[k]:
                    self.code[k][lang] = self.code[k][lang].split("\n")
                    # remove final empty line
                    if len(self.code[k][lang][-1]) == 0:
                        self.code[k][lang] = self.code[k][lang][:-1]
Ejemplo n.º 13
0
class WebEC(object):
    """
    Class for an elliptic curve over Q
    """
    def __init__(self, dbdata):
        """
        Arguments:

            - dbdata: the data from the database
        """
        logger.debug("Constructing an instance of ECisog_class")
        self.__dict__.update(dbdata)
        # Next lines because the hyphens make trouble
        self.xintcoords = split_list(dbdata['x-coordinates_of_integral_points'])
        self.non_surjective_primes = dbdata['non-surjective_primes']
        # Next lines because the python identifiers cannot start with 2
        self.twoadic_index = dbdata['2adic_index']
        self.twoadic_log_level = dbdata['2adic_log_level']
        self.twoadic_gens = dbdata['2adic_gens']
        self.twoadic_label = dbdata['2adic_label']
        # All other fields are handled here
        self.make_curve()

    @staticmethod
    def by_label(label):
        """
        Searches for a specific elliptic curve in the curves
        collection by its label, which can be either in LMFDB or
        Cremona format.
        """
        try:
            N, iso, number = split_lmfdb_label(label)
            data = db_ec().find_one({"lmfdb_label" : label})
        except AttributeError:
            try:
                N, iso, number = split_cremona_label(label)
                data = db_ec().find_one({"label" : label})
            except AttributeError:
                return "Invalid label" # caller must catch this and raise an error

        if data:
            return WebEC(data)
        return "Curve not found" # caller must catch this and raise an error

    def make_curve(self):
        # To start with the data fields of self are just those from
        # the database.  We need to reformat these.

        # Old version: required constructing the actual elliptic curve
        # E, and computing some further data about it.

        # New version (May 2016): extra data fields now in the
        # database so we do not have to construct the curve or do any
        # computation with it on the fly.  As a failsafe the old way
        # is still included.

        data = self.data = {}
        try:
            data['ainvs'] = [int(c) for c in self.xainvs[1:-1].split(',')]
        except AttributeError:
            data['ainvs'] = [int(ai) for ai in self.ainvs]
        data['conductor'] = N = ZZ(self.conductor)
        data['j_invariant'] = QQ(str(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_str'] = unicode(str(data['j_invariant']))
        data['j_inv_latex'] = web_latex(data['j_invariant'])
        mw = self.mw = {}
        mw['rank'] = self.rank
        mw['int_points'] = ''
        if self.xintcoords:
            a1, a2, a3, a4, a6 = [ZZ(a) for a in data['ainvs']]
            def lift_x(x):
                f = ((x + a2) * x + a4) * x + a6
                b = (a1*x + a3)
                d = (b*b + 4*f).sqrt()
                return (x, (-b+d)/2)
            mw['int_points'] = ', '.join(web_latex(lift_x(x)) for x in self.xintcoords)

        mw['generators'] = ''
        mw['heights'] = []
        if self.gens:
            mw['generators'] = [web_latex(tuple(P)) for P in parse_points(self.gens)]

        mw['tor_order'] = self.torsion
        tor_struct = [int(c) for c in self.torsion_structure]
        if mw['tor_order'] == 1:
            mw['tor_struct'] = '\mathrm{Trivial}'
            mw['tor_gens'] = ''
        else:
            mw['tor_struct'] = ' \\times '.join(['\Z/{%s}\Z' % n for n in tor_struct])
            mw['tor_gens'] = ', '.join(web_latex(tuple(P)) for P in parse_points(self.torsion_generators))

        # try to get all the data we need from the database entry (now in self)
        try:
            data['equation'] = self.equation
            local_data = self.local_data
            badprimes = [ZZ(ld['p']) for ld in local_data]
            D = self.signD * prod([ld['p']**ld['ord_disc'] for ld in local_data])
            data['disc'] = D
            Nfac = Factorization([(ZZ(ld['p']),ld['ord_cond']) for ld in local_data])
            Dfac = Factorization([(ZZ(ld['p']),ld['ord_disc']) for ld in local_data], unit=ZZ(self.signD))

            data['minq_D'] = minqD = self.min_quad_twist['disc']
            minq_label = self.min_quad_twist['label']
            data['minq_label'] = db_ec().find_one({'label':minq_label}, ['lmfdb_label'])['lmfdb_label']
            data['minq_info'] = '(itself)' if minqD==1 else '(by %s)' % minqD
            try:
                data['degree'] = self.degree
            except AttributeError:
                data['degree']  =0 # invalid, but will be displayed nicely
            mw['heights'] = self.heights
            if self.number == 1:
                data['an'] = self.anlist
                data['ap'] = self.aplist
            else:
                r = db_ec().find_one({'lmfdb_iso':self.lmfdb_iso, 'number':1}, ['anlist','aplist'])
                data['an'] = r['anlist']
                data['ap'] = r['aplist']

        # otherwise fall back to computing it from the curve
        except AttributeError:
            print("Falling back to constructing E")
            self.E = EllipticCurve(data['ainvs'])
            data['equation'] = web_latex(self.E)
            data['disc'] = D = self.E.discriminant()
            Nfac = N.factor()
            Dfac = D.factor()
            bad_primes = [p for p,e in Nfac]
            try:
                data['degree'] = self.degree
            except AttributeError:
                try:
                    data['degree'] = self.E.modular_degree()
                except RuntimeError:
                    data['degree'] = 0  # invalid, but will be displayed nicely
            minq, minqD = self.E.minimal_quadratic_twist()
            data['minq_D'] = minqD
            if minqD == 1:
                data['minq_label'] = self.lmfdb_label
                data['minq_info'] = '(itself)'
            else:
                # This relies on the minimal twist being in the
                # database, which is true when the database only
                # contains the Cremona database.  It would be a good
                # idea if, when the database is extended, we ensured
                # that for any curve included, all twists of smaller
                # conductor are also included.
                minq_ainvs = [str(c) for c in minq.ainvs()]
                data['minq_label'] = db_ec().find_one({'jinv':str(self.E.j_invariant()),
                                                       'ainvs': minq_ainvs},['lmfdb_label'])['lmfdb_label']
                data['minq_info'] = '(by %s)' % minqD

            if self.gens:
                self.generators = [self.E(g) for g in parse_points(self.gens)]
                mw['heights'] = [P.height() for P in self.generators]

            data['an'] = self.E.anlist(20,python_ints=True)
            data['ap'] = self.E.aplist(100,python_ints=True)
            self.local_data = local_data = []
            for p in bad_primes:
                ld = self.E.local_data(p, algorithm="generic")
                local_data_p = {}
                local_data_p['p'] = p
                local_data_p['cp'] = ld.tamagawa_number()
                local_data_p['kod'] = web_latex(ld.kodaira_symbol()).replace('$', '')
                local_data_p['red'] = ld.bad_reduction_type()
                local_data_p['ord_cond'] = ld.conductor_valuation()
                local_data_p['ord_disc'] = ld.discriminant_valuation()
                local_data_p['ord_den_j'] = max(0,-self.E.j_invariant().valuation(p))
                local_data.append(local_data_p)

        jfac = Factorization([(ZZ(ld['p']),ld['ord_den_j']) for ld in local_data])

        minq_N, minq_iso, minq_number = split_lmfdb_label(data['minq_label'])

        data['disc_factor'] = latex(Dfac)
        data['cond_factor'] =latex(Nfac)
        data['disc_latex'] = web_latex(D)
        data['cond_latex'] = web_latex(N)

        data['CMD'] = self.cm
        data['CM'] = "no"
        data['EndE'] = "\(\Z\)"
        if self.cm:
            data['CM'] = "yes (\(D=%s\))" % data['CMD']
            if data['CMD']%4==0:
                d4 = ZZ(data['CMD'])//4
                data['EndE'] = "\(\Z[\sqrt{%s}]\)" % d4
            else:
                data['EndE'] = "\(\Z[(1+\sqrt{%s})/2]\)" % data['CMD']
            data['ST'] = '<a href="%s">$%s$</a>' % (url_for('st.by_label', label='1.2.N(U(1))'),'N(\\mathrm{U}(1))')
        else:
            data['ST'] = '<a href="%s">$%s$</a>' % (url_for('st.by_label', label='1.2.SU(2)'),'\\mathrm{SU}(2)')

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

        try:
            data['galois_images'] = [trim_galois_image_code(s) for s in self.galois_images]
            data['non_surjective_primes'] = self.non_surjective_primes
        except AttributeError:
            #print "No Galois image data"
            data['galois_images'] = []
            data['non_surjective_primes'] = []

        data['galois_data'] = [{'p': p,'image': im }
                               for p,im in zip(data['non_surjective_primes'],
                                               data['galois_images'])]

        if self.twoadic_gens:
            from sage.matrix.all import Matrix
            data['twoadic_gen_matrices'] = ','.join([latex(Matrix(2,2,M)) for M in self.twoadic_gens])
            data['twoadic_rouse_url'] = ROUSE_URL_PREFIX + self.twoadic_label + ".html"

        # Leading term of L-function & BSD data
        bsd = self.bsd = {}
        r = self.rank
        if r >= 2:
            bsd['lder_name'] = "L^{(%s)}(E,1)/%s!" % (r,r)
        elif r:
            bsd['lder_name'] = "L'(E,1)"
        else:
            bsd['lder_name'] = "L(E,1)"

        bsd['reg'] = self.regulator
        bsd['omega'] = self.real_period
        bsd['sha'] = int(0.1+self.sha_an)
        bsd['lder'] = self.special_value

        # 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)

        if self.iso == '990h':
            data['Gamma0optimal'] = bool(self.number == 3)
        else:
            data['Gamma0optimal'] = bool(self.number == 1)


        data['p_adic_data_exists'] = False
        if data['Gamma0optimal']:
            data['p_adic_data_exists'] = (padic_db().find({'lmfdb_iso': self.lmfdb_iso}).count()) > 0

        tamagawa_numbers = [ZZ(ld['cp']) for ld in 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]
        bsd['tamagawa_factors'] = r'\cdot'.join(cp_fac)
        bsd['tamagawa_product'] = sage.misc.all.prod(tamagawa_numbers)

        cond, iso, num = split_lmfdb_label(self.lmfdb_label)
        data['newform'] =  web_latex(PowerSeriesRing(QQ, 'q')(data['an'], 20, check=True))
        data['newform_label'] = self.newform_label = newform_label(cond,2,1,iso)
        self.newform_link = url_for("emf.render_elliptic_modular_forms", level=cond, weight=2, character=1, label=iso)
        self.newform_exists_in_db = is_newform_in_db(self.newform_label)
        self._code = None

        self.friends = [
            ('Isogeny class ' + self.lmfdb_iso, url_for(".by_double_iso_label", conductor=N, iso_label=iso)),
            ('Minimal quadratic twist %s %s' % (data['minq_info'], data['minq_label']), url_for(".by_triple_label", conductor=minq_N, iso_label=minq_iso, number=minq_number)),
            ('All twists ', url_for(".rational_elliptic_curves", jinv=self.jinv)),
            ('L-function', url_for("l_functions.l_function_ec_page", label=self.lmfdb_label))]
        if not self.cm:
            if N<=300:
                self.friends += [('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2', label=self.lmfdb_iso))]
            if N<=50:
                self.friends += [('Symmetric cube L-function', url_for("l_functions.l_function_ec_sym_page", power='3', label=self.lmfdb_iso))]
        if self.newform_exists_in_db:
            self.friends += [('Modular form ' + self.newform_label, self.newform_link)]

        self.downloads = [('Download coefficients of q-expansion', url_for(".download_EC_qexp", label=self.lmfdb_label, limit=1000)),
                          ('Download all stored data', url_for(".download_EC_all", label=self.lmfdb_label)),
                          ('Download Magma code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='magma')),
                          ('Download Sage code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='sage')),
                          ('Download GP code', url_for(".ec_code_download", conductor=cond, iso=iso, number=num, label=self.lmfdb_label, download_type='gp'))
        ]

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

        self.plot_link = '<img src="%s" width="200" height="150"/>' % self.plot
        self.properties = [('Label', self.lmfdb_label),
                           (None, self.plot_link),
                           ('Conductor', '\(%s\)' % data['conductor']),
                           ('Discriminant', '\(%s\)' % data['disc']),
                           ('j-invariant', '%s' % data['j_inv_latex']),
                           ('CM', '%s' % data['CM']),
                           ('Rank', '\(%s\)' % mw['rank']),
                           ('Torsion Structure', '\(%s\)' % mw['tor_struct'])
                           ]

        self.title = "Elliptic Curve %s (Cremona label %s)" % (self.lmfdb_label, self.label)

        self.bread = [('Elliptic Curves', url_for("ecnf.index")),
                           ('$\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,' ')]

    def code(self):
        if self._code == None:
            self.make_code_snippets()
        return self._code

    def make_code_snippets(self):
        # read in code.yaml from current directory:

        _curdir = os.path.dirname(os.path.abspath(__file__))
        self._code =  yaml.load(open(os.path.join(_curdir, "code.yaml")))

        # Fill in placeholders for this specific curve:

        for lang in ['sage', 'pari', 'magma']:
            self._code['curve'][lang] = self._code['curve'][lang] % (self.data['ainvs'],self.label)
        return
        for k in self._code:
            if k != 'prompt':
                for lang in self._code[k]:
                    self._code[k][lang] = self._code[k][lang].split("\n")
                    # remove final empty line
                    if len(self._code[k][lang][-1])==0:
                        self._code[k][lang] = self._code[k][lang][:-1]
Ejemplo n.º 14
0
def render_curve_webpage_by_label(label):
    C = lmfdb.base.getDBConnection()
    data = C.elliptic_curves.curves.find_one({'lmfdb_label': label})
    if data is None:
        return elliptic_curve_jump_error(label, {})
    info = {}
    ainvs = [int(a) for a in data['ainvs']]
    E = EllipticCurve(ainvs)
    cremona_label = data['label']
    lmfdb_label = data['lmfdb_label']
    N = ZZ(data['conductor'])
    cremona_iso_class = data['iso']  # eg '37a'
    lmfdb_iso_class = data['lmfdb_iso']  # eg '37.a'
    rank = data['rank']
    try:
        j_invariant = QQ(str(data['jinv']))
    except KeyError:
        j_invariant = E.j_invariant()
    if j_invariant == 0:
        j_inv_factored = latex(0)
    else:
        j_inv_factored = latex(j_invariant.factor())
    jinv = unicode(str(j_invariant))
    CMD = 0
    CM = "no"
    EndE = "\(\Z\)"
    if E.has_cm():
        CMD = E.cm_discriminant()
        CM = "yes (\(%s\))"%CMD
        if CMD%4==0:
            d4 = ZZ(CMD)//4
            # r = d4.squarefree_part()
            # f = (d4//r).isqrt()
            # f="" if f==1 else str(f)
            # EndE = "\(\Z[%s\sqrt{%s}]\)"%(f,r)
            EndE = "\(\Z[\sqrt{%s}]\)"%(d4)
        else:            
            EndE = "\(\Z[(1+\sqrt{%s})/2]\)"%CMD

    # plot=E.plot()
    discriminant = E.discriminant()
    xintpoints_projective = [E.lift_x(x) for x in xintegral_point(data['x-coordinates_of_integral_points'])]
    xintpoints = proj_to_aff(xintpoints_projective)
    if 'degree' in data:
        modular_degree = data['degree']
    else:
        try:
            modular_degree = E.modular_degree()
        except RuntimeError:
            modular_degree = 0  # invalid, will be displayed nicely

    G = E.torsion_subgroup().gens()
    minq = E.minimal_quadratic_twist()[0]
    if E == minq:
        minq_label = lmfdb_label
    else:
        minq_ainvs = [str(c) for c in minq.ainvs()]
        minq_label = C.elliptic_curves.curves.find_one({'ainvs': minq_ainvs})['lmfdb_label']
# We do not just do the following, as Sage's installed database
# might not have all the curves in the LMFDB database.
# minq_label = E.minimal_quadratic_twist()[0].label()

    if 'gens' in data:
        generator = parse_gens(data['gens'])
    if len(G) == 0:
        tor_struct = '\mathrm{Trivial}'
        tor_group = '\mathrm{Trivial}'
    else:
        tor_group = ' \\times '.join(['\Z/{%s}\Z' % a.order() for a in G])
    if 'torsion_structure' in data:
        info['tor_structure'] = ' \\times '.join(['\Z/{%s}\Z' % int(a) for a in data['torsion_structure']])
    else:
        info['tor_structure'] = tor_group

    info.update(data)
    if rank >= 2:
        lder_tex = "L%s(E,1)" % ("^{(" + str(rank) + ")}")
    elif rank == 1:
        lder_tex = "L%s(E,1)" % ("'" * rank)
    else:
        assert rank == 0
        lder_tex = "L(E,1)"
    info['Gamma0optimal'] = (
        cremona_label[-1] == '1' if cremona_iso_class != '990h' else cremona_label[-1] == '3')
    info['modular_degree'] = modular_degree
    p_adic_data_exists = (C.elliptic_curves.padic_db.find(
        {'lmfdb_iso': lmfdb_iso_class}).count()) > 0 and info['Gamma0optimal']

    # Local data
    local_data = []
    for p in N.prime_factors():
        local_info = E.local_data(p)
        local_data.append({'p': p,
                           'tamagawa_number': local_info.tamagawa_number(),
                           'kodaira_symbol': web_latex(local_info.kodaira_symbol()).replace('$', ''),
                           'reduction_type': local_info.bad_reduction_type()
                           })

    mod_form_iso = lmfdb_label_regex.match(lmfdb_iso_class).groups()[1]

    info.update({
        'conductor': N,
        'disc_factor': latex(discriminant.factor()),
        'j_invar_factor': j_inv_factored,
        'label': lmfdb_label,
        'cremona_label': cremona_label,
        'iso_class': lmfdb_iso_class,
        'cremona_iso_class': cremona_iso_class,
        'equation': web_latex(E),
        #'f': ajax_more(E.q_eigenform, 10, 20, 50, 100, 250),
        'f': web_latex(E.q_eigenform(10)),
        'generators': ', '.join(web_latex(g) for g in generator) if 'gens' in data else ' ',
        'lder': lder_tex,
        'p_adic_primes': [p for p in sage.all.prime_range(5, 100) if E.is_ordinary(p) and not p.divides(N)],
        'p_adic_data_exists': p_adic_data_exists,
        'ainvs': format_ainvs(data['ainvs']),
        'CM': CM,
        'CMD': CMD,
        'EndE': EndE,
        'tamagawa_numbers': r' \cdot '.join(str(sage.all.factor(c)) for c in E.tamagawa_numbers()),
        'local_data': local_data,
        'cond_factor': latex(N.factor()),
        'xintegral_points': ', '.join(web_latex(P) for P in xintpoints),
        'tor_gens': ', '.join(web_latex(eval(g)) for g in data['torsion_generators']) if False else ', '.join(web_latex(P.element().xy()) for P in list(G))
    })
    info['friends'] = [
        ('Isogeny class ' + lmfdb_iso_class, "/EllipticCurve/Q/%s" % lmfdb_iso_class),
        ('Minimal quadratic twist ' + minq_label, "/EllipticCurve/Q/%s" % minq_label),
        ('All twists ', url_for("rational_elliptic_curves", jinv=jinv)),
        ('L-function', url_for("l_functions.l_function_ec_page", label=lmfdb_label)),
        ('Symmetric square L-function', url_for("l_functions.l_function_ec_sym_page", power='2',
                                                label=lmfdb_iso_class)),
        ('Symmetric 4th power L-function', url_for("l_functions.l_function_ec_sym_page", power='4',
                                                   label=lmfdb_iso_class))]

    info['friends'].append(('Modular form ' + lmfdb_iso_class.replace('.', '.2'), url_for(
        "emf.render_elliptic_modular_forms", level=int(N), weight=2, character=0, label=mod_form_iso)))

    info['downloads'] = [('Download coeffients of q-expansion', url_for("download_EC_qexp", label=lmfdb_label, limit=100)),
                         ('Download all stored data', url_for("download_EC_all", label=lmfdb_label))]

    # info['learnmore'] = [('Elliptic Curves', url_for("not_yet_implemented"))]
    # info['plot'] = image_src(plot)
    info['plot'] = url_for('plot_ec', label=lmfdb_label)

    properties2 = [('Label', '%s' % lmfdb_label),
                   (None, '<img src="%s" width="200" height="150"/>' % url_for(
                       'plot_ec', label=lmfdb_label)),
                   ('Conductor', '\(%s\)' % N),
                   ('Discriminant', '\(%s\)' % discriminant),
                   ('j-invariant', '%s' % web_latex(j_invariant)),
                   ('CM', '%s' % CM),
                   ('Rank', '\(%s\)' % rank),
                   ('Torsion Structure', '\(%s\)' % tor_group)
                   ]
    # properties.extend([ "prop %s = %s<br/>" % (_,_*1923) for _ in range(12) ])
    credit = 'John Cremona'
    if info['label'] == info['cremona_label']:
        t = "Elliptic Curve %s" % info['label']
    else:
        t = "Elliptic Curve %s (Cremona label %s)" % (info['label'], info['cremona_label'])

    bread = [('Elliptic Curves ', url_for("rational_elliptic_curves")), ('Elliptic curves %s' %
             lmfdb_label, ' ')]

    return render_template("elliptic_curve/elliptic_curve.html",
                           properties2=properties2, credit=credit, bread=bread, title=t, info=info, friends=info['friends'], downloads=info['downloads'])
Ejemplo n.º 15
0
def allgens(line):
    r""" Parses one line from an allgens file.  Returns the label and
    a dict containing fields with keys 'conductor', 'iso', 'number',
    'ainvs', 'jinv', 'cm', 'rank', 'gens', 'torsion_order', 'torsion_structure',
    'torsion_generators', all values being strings or ints, and more.

    Input line fields:

    conductor iso number ainvs rank torsion_structure gens torsion_gens

    Sample input line:

    20202 i 2 [1,0,0,-298389,54947169] 1 [2,4] [-570:6603:1] [-622:311:1] [834:19239:1]
    """
    global lmfdb_label_to_label
    global label_to_lmfdb_label

    data = split(line)
    iso = data[0] + data[1]
    label = iso + data[2]
    try:
        lmfdb_label = label_to_lmfdb_label[label]
    except AttributeError:
        print("Label {} not found in label_to_lmfdb_label dict!".format(label))
        lmfdb_label = ""

    global nallgens
    nallgens += 1
    if nallgens % 100 == 0:
        print("processing allgens for {} (#{})".format(label, nallgens))
    rank = int(data[4])
    t = data[5]
    tor_struct = [] if t == '[]' else t[1:-1].split(",")
    torsion = int(prod([int(ti) for ti in tor_struct], 1))
    ainvs = parse_ainvs(data[3])
    E = EllipticCurve(ainvs)
    jinv = text_type(E.j_invariant())
    if E.has_cm():
        cm = int(E.cm_discriminant())
    else:
        cm = int(0)
    N = E.conductor()
    bad_p = N.prime_factors()  # will be sorted
    num_bad_p = len(bad_p)

    local_data = [{
        'p':
        int(ld.prime().gen()),
        'ord_cond':
        int(ld.conductor_valuation()),
        'ord_disc':
        int(ld.discriminant_valuation()),
        'ord_den_j':
        int(max(0, -(E.j_invariant().valuation(ld.prime().gen())))),
        'red':
        int(ld.bad_reduction_type()),
        'rootno':
        int(E.root_number(ld.prime().gen())),
        'kod':
        web_latex(ld.kodaira_symbol()).replace('$', ''),
        'cp':
        int(ld.tamagawa_number())
    } for ld in E.local_data()]
    semistable = all([ld['ord_cond'] == 1 for ld in local_data])

    gens = [
        gen.replace("[", "(").replace("]", ")") for gen in data[6:6 + rank]
    ]
    tor_gens = ["%s" % parse_tgens(tgens[1:-1]) for tgens in data[6 + rank:]]

    from lmfdb.elliptic_curves.web_ec import parse_points
    heights = [float(E(P).height()) for P in parse_points(gens)]

    Etw, Dtw = E.minimal_quadratic_twist()
    if Etw.conductor() == N:
        min_quad_twist = {
            'label': label,
            'lmfdb_label': lmfdb_label,
            'disc': int(1)
        }
    else:
        minq_ainvs = Etw.ainvs()
        r = curves.lucky({
            'jinv': str(E.j_invariant()),
            'ainvs': minq_ainvs
        },
                         projection=['label', 'lmfdb_label'])
        min_quad_twist = {
            'label': r['label'],
            'lmfdb_label': r['lmfdb_label'],
            'disc': int(Dtw)
        }

    trace_hash = TraceHashClass(iso, E)

    return label, {
        'conductor': int(data[0]),
        'iso': iso,
        'number': int(data[2]),
        'ainvs': ainvs,
        'jinv': jinv,
        'cm': cm,
        'rank': rank,
        'gens': gens,
        'torsion': torsion,
        'torsion_structure': tor_struct,
        'torsion_generators': tor_gens,
        'trace_hash': trace_hash,
        'equation': web_latex(E),
        'bad_primes': bad_p,
        'num_bad_primes': num_bad_p,
        'local_data': local_data,
        'semistable': semistable,
        'signD': int(E.discriminant().sign()),
        'heights': heights,
        'aplist': E.aplist(100, python_ints=True),
        'anlist': E.anlist(20, python_ints=True),
        'min_quad_twist': min_quad_twist,
    }
Ejemplo n.º 16
0
def render_curve_webpage_by_label(label):
    C = lmfdb.base.getDBConnection()
    data = C.elliptic_curves.curves.find_one({"lmfdb_label": label})
    if data is None:
        return elliptic_curve_jump_error(label, {})
    info = {}
    ainvs = [int(a) for a in data["ainvs"]]
    E = EllipticCurve(ainvs)
    cremona_label = data["label"]
    lmfdb_label = data["lmfdb_label"]
    N = ZZ(data["conductor"])
    cremona_iso_class = data["iso"]  # eg '37a'
    lmfdb_iso_class = data["lmfdb_iso"]  # eg '37.a'
    rank = data["rank"]
    try:
        j_invariant = QQ(str(data["jinv"]))
    except KeyError:
        j_invariant = E.j_invariant()
    if j_invariant == 0:
        j_inv_factored = latex(0)
    else:
        j_inv_factored = latex(j_invariant.factor())
    jinv = unicode(str(j_invariant))
    CMD = 0
    CM = "no"
    EndE = "\(\Z\)"
    if E.has_cm():
        CMD = E.cm_discriminant()
        CM = "yes (\(%s\))" % CMD
        if CMD % 4 == 0:
            d4 = ZZ(CMD) // 4
            # r = d4.squarefree_part()
            # f = (d4//r).isqrt()
            # f="" if f==1 else str(f)
            # EndE = "\(\Z[%s\sqrt{%s}]\)"%(f,r)
            EndE = "\(\Z[\sqrt{%s}]\)" % (d4)
        else:
            EndE = "\(\Z[(1+\sqrt{%s})/2]\)" % CMD

    # plot=E.plot()
    discriminant = E.discriminant()
    xintpoints_projective = [E.lift_x(x) for x in xintegral_point(data["x-coordinates_of_integral_points"])]
    xintpoints = proj_to_aff(xintpoints_projective)
    if "degree" in data:
        modular_degree = data["degree"]
    else:
        try:
            modular_degree = E.modular_degree()
        except RuntimeError:
            modular_degree = 0  # invalid, will be displayed nicely

    G = E.torsion_subgroup().gens()
    E_pari = E.pari_curve(prec=200)
    from sage.libs.pari.all import PariError

    try:
        minq = E.minimal_quadratic_twist()[0]
    except PariError:  # this does occur with 164411a1
        print "PariError computing minimal quadratic twist of elliptic curve %s" % lmfdb_label
        minq = E
    if E == minq:
        minq_label = lmfdb_label
    else:
        minq_ainvs = [str(c) for c in minq.ainvs()]
        minq_label = C.elliptic_curves.curves.find_one({"ainvs": minq_ainvs})["lmfdb_label"]
    # We do not just do the following, as Sage's installed database
    # might not have all the curves in the LMFDB database.
    # minq_label = E.minimal_quadratic_twist()[0].label()

    if "gens" in data:
        generator = parse_gens(data["gens"])
    if len(G) == 0:
        tor_struct = "\mathrm{Trivial}"
        tor_group = "\mathrm{Trivial}"
    else:
        tor_group = " \\times ".join(["\Z/{%s}\Z" % a.order() for a in G])
    if "torsion_structure" in data:
        info["tor_structure"] = " \\times ".join(["\Z/{%s}\Z" % int(a) for a in data["torsion_structure"]])
    else:
        info["tor_structure"] = tor_group

    info.update(data)
    if rank >= 2:
        lder_tex = "L%s(E,1)" % ("^{(" + str(rank) + ")}")
    elif rank == 1:
        lder_tex = "L%s(E,1)" % ("'" * rank)
    else:
        assert rank == 0
        lder_tex = "L(E,1)"
    info["Gamma0optimal"] = cremona_label[-1] == "1" if cremona_iso_class != "990h" else cremona_label[-1] == "3"
    info["modular_degree"] = modular_degree
    p_adic_data_exists = (C.elliptic_curves.padic_db.find({"lmfdb_iso": lmfdb_iso_class}).count()) > 0 and info[
        "Gamma0optimal"
    ]

    # Local data
    local_data = []
    for p in N.prime_factors():
        local_info = E.local_data(p, algorithm="generic")
        local_data.append(
            {
                "p": p,
                "tamagawa_number": local_info.tamagawa_number(),
                "kodaira_symbol": web_latex(local_info.kodaira_symbol()).replace("$", ""),
                "reduction_type": local_info.bad_reduction_type(),
            }
        )

    mod_form_iso = lmfdb_label_regex.match(lmfdb_iso_class).groups()[1]

    tamagawa_numbers = [E.local_data(p, algorithm="generic").tamagawa_number() for p in N.prime_factors()]
    # if we use E.tamagawa_numbers() it calls E.local_data(p) which
    # crashes on some curves e.g. 164411a1
    info.update(
        {
            "conductor": N,
            "disc_factor": latex(discriminant.factor()),
            "j_invar_factor": j_inv_factored,
            "label": lmfdb_label,
            "cremona_label": cremona_label,
            "iso_class": lmfdb_iso_class,
            "cremona_iso_class": cremona_iso_class,
            "equation": web_latex(E),
            #'f': ajax_more(E.q_eigenform, 10, 20, 50, 100, 250),
            "f": web_latex(E.q_eigenform(10)),
            "generators": ", ".join(web_latex(g) for g in generator) if "gens" in data else " ",
            "lder": lder_tex,
            "p_adic_primes": [p for p in sage.all.prime_range(5, 100) if E.is_ordinary(p) and not p.divides(N)],
            "p_adic_data_exists": p_adic_data_exists,
            "ainvs": format_ainvs(data["ainvs"]),
            "CM": CM,
            "CMD": CMD,
            "EndE": EndE,
            "tamagawa_numbers": r" \cdot ".join(str(sage.all.factor(c)) for c in tamagawa_numbers),
            "local_data": local_data,
            "cond_factor": latex(N.factor()),
            "xintegral_points": ", ".join(web_latex(P) for P in xintpoints),
            "tor_gens": ", ".join(web_latex(eval(g)) for g in data["torsion_generators"])
            if False
            else ", ".join(web_latex(P.element().xy()) for P in list(G)),
        }
    )
    info["friends"] = [
        ("Isogeny class " + lmfdb_iso_class, "/EllipticCurve/Q/%s" % lmfdb_iso_class),
        ("Minimal quadratic twist " + minq_label, "/EllipticCurve/Q/%s" % minq_label),
        ("All twists ", url_for("rational_elliptic_curves", jinv=jinv)),
        ("L-function", url_for("l_functions.l_function_ec_page", label=lmfdb_label)),
        (
            "Symmetric square L-function",
            url_for("l_functions.l_function_ec_sym_page", power="2", label=lmfdb_iso_class),
        ),
        (
            "Symmetric 4th power L-function",
            url_for("l_functions.l_function_ec_sym_page", power="4", label=lmfdb_iso_class),
        ),
    ]

    info["friends"].append(
        (
            "Modular form " + lmfdb_iso_class.replace(".", ".2"),
            url_for("emf.render_elliptic_modular_forms", level=int(N), weight=2, character=0, label=mod_form_iso),
        )
    )

    info["downloads"] = [
        ("Download coeffients of q-expansion", url_for("download_EC_qexp", label=lmfdb_label, limit=100)),
        ("Download all stored data", url_for("download_EC_all", label=lmfdb_label)),
    ]

    # info['learnmore'] = [('Elliptic Curves', url_for("not_yet_implemented"))]
    # info['plot'] = image_src(plot)
    info["plot"] = url_for("plot_ec", label=lmfdb_label)

    properties2 = [
        ("Label", "%s" % lmfdb_label),
        (None, '<img src="%s" width="200" height="150"/>' % url_for("plot_ec", label=lmfdb_label)),
        ("Conductor", "\(%s\)" % N),
        ("Discriminant", "\(%s\)" % discriminant),
        ("j-invariant", "%s" % web_latex(j_invariant)),
        ("CM", "%s" % CM),
        ("Rank", "\(%s\)" % rank),
        ("Torsion Structure", "\(%s\)" % tor_group),
    ]
    # properties.extend([ "prop %s = %s<br/>" % (_,_*1923) for _ in range(12) ])
    credit = "John Cremona"
    if info["label"] == info["cremona_label"]:
        t = "Elliptic Curve %s" % info["label"]
    else:
        t = "Elliptic Curve %s (Cremona label %s)" % (info["label"], info["cremona_label"])

    bread = [("Elliptic Curves ", url_for("rational_elliptic_curves")), ("Elliptic curves %s" % lmfdb_label, " ")]

    return render_template(
        "elliptic_curve/elliptic_curve.html",
        properties2=properties2,
        credit=credit,
        bread=bread,
        title=t,
        info=info,
        friends=info["friends"],
        downloads=info["downloads"],
    )