def isoclass(line): r""" Parses one line from an isoclass file. Returns the label and a dict containing fields with keys . Input line fields (5); the first 4 are the standard labels and the 5th the isogeny matrix as a list of lists of ints. field_label conductor_label iso_label number isogeny_matrix Sample input line: 2.0.4.1 65.18.1 a 1 [[1,6,3,18,9,2],[6,1,2,3,6,3],[3,2,1,6,3,6],[18,3,6,1,2,9],[9,6,3,2,1,18],[2,3,6,9,18,1]] """ # Parse the line and form the full label: data = split(line) if len(data) < 5: print "isoclass line %s does not have 5 fields (excluding gens), skipping" % line field_label = data[0] # string 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 number = int(data[3]) # int short_label = "%s-%s%s" % (conductor_label, iso_label, str(number)) label = "%s-%s" % (field_label, short_label) mat = data[4] mat = [[int(a) for a in r.split(",")] for r in mat[2:-2].split("],[")] isogeny_degrees = dict([[n+1,sorted(list(set(row)))] for n,row in enumerate(mat)]) edata = {'label': [field_label,conductor_label,iso_label], 'isogeny_matrix': mat, 'isogeny_degrees': isogeny_degrees} return label, edata
def galrep(line): r""" Parses one line from a galrep file. Returns the label and a dict containing two fields: 'non-surjective_primes', a list of primes p for which the Galois representation modulo p is not surjective (cut off at p=37 for CM curves for which this would otherwise contain all primes), 'galois_images', a list of strings encoding the image when not surjective, following Sutherland's coding scheme for subgroups of GL(2,p). Note that these codes start with a 1 or 2 digit prime followed a letter in ['B','C','N','S']. Input line fields (4+); the first is a standard label of the form field-conductor-an where 'a' is the isogeny class (one or more letters), 'n' is the number ofe the curve in the class (from 1) and any remaining ones are galrep codes. label codes Sample input line (field='2.0.3.1', conductor='10000.0.100', class='a', number=1) 2.0.3.1-10000.0.100-a1 2B 3B[2] """ data = split(line) label = data[0] # single string field_label, conductor_label, c_label = data[0].split("-") iso_label = ''.join([c for c in c_label if c.isalpha()]) number = ''.join([c for c in c_label if c.isdigit()]) assert iso_label + number == c_label conductor_label = convert_conductor_label(field_label, conductor_label) print("Converting conductor label from {} to {}".format( data[0].split("-")[1], conductor_label)) short_label = "%s-%s%s" % (conductor_label, iso_label, str(number)) label = "%s-%s" % (field_label, short_label) image_codes = data[1:] # pr = [ int(s[:2]) if s[1].isdigit() else int(s[:1]) for s in image_codes] pr = [int(split_galois_image_code(s)[0]) for s in image_codes] return label, { 'non-surjective_primes': pr, 'galois_images': image_codes, }
def galrep(line): r""" Parses one line from a galrep file. Returns the label and a dict containing two fields: 'non-surjective_primes', a list of primes p for which the Galois representation modulo p is not surjective (cut off at p=37 for CM curves for which this would otherwise contain all primes), 'galois_images', a list of strings encoding the image when not surjective, following Sutherland's coding scheme for subgroups of GL(2,p). Note that these codes start with a 1 or 2 digit prime followed a letter in ['B','C','N','S']. Input line fields (4+); the first is a standard label of the form field-conductor-an where 'a' is the isogeny class (one or more letters), 'n' is the number ofe the curve in the class (from 1) and any remaining ones are galrep codes. label codes Sample input line (field='2.0.3.1', conductor='10000.0.100', class='a', number=1) 2.0.3.1-10000.0.100-a1 2B 3B[2] """ data = split(line) label = data[0] # single string field_label, conductor_label, c_label = data[0].split("-") iso_label = ''.join([c for c in c_label if c.isalpha()]) number = ''.join([c for c in c_label if c.isdigit()]) assert iso_label+number==c_label conductor_label = convert_conductor_label(field_label, conductor_label) print("Converting conductor label from {} to {}".format(data[0].split("-")[1], conductor_label)) short_label = "%s-%s%s" % (conductor_label, iso_label, str(number)) label = "%s-%s" % (field_label, short_label) image_codes = data[1:] # pr = [ int(s[:2]) if s[1].isdigit() else int(s[:1]) for s in image_codes] pr = [ int(split_galois_image_code(s)[0]) for s in image_codes] return label, { 'non-surjective_primes': pr, 'galois_images': image_codes, }
def isoclass(line): r""" Parses one line from an isoclass file. Returns the label and a dict containing fields with keys . Input line fields (5); the first 4 are the standard labels and the 5th the isogeny matrix as a list of lists of ints. field_label conductor_label iso_label number isogeny_matrix Sample input line: 2.0.4.1 65.18.1 a 1 [[1,6,3,18,9,2],[6,1,2,3,6,3],[3,2,1,6,3,6],[18,3,6,1,2,9],[9,6,3,2,1,18],[2,3,6,9,18,1]] """ # Parse the line and form the full label: data = split(line) if len(data) < 5: print "isoclass line %s does not have 5 fields (excluding gens), skipping" % line field_label = data[0] # string 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 number = int(data[3]) # int short_label = "%s-%s%s" % (conductor_label, iso_label, str(number)) label = "%s-%s" % (field_label, short_label) mat = data[4] mat = [[int(a) for a in r.split(",")] for r in mat[2:-2].split("],[")] isogeny_degrees = dict([[n + 1, sorted(list(set(row)))] for n, row in enumerate(mat)]) edata = { 'label': [field_label, conductor_label, iso_label], 'isogeny_matrix': mat, 'isogeny_degrees': isogeny_degrees } return label, edata
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
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