def usecase__get_classes_dp1(rank): ''' Computes classes in the Neron-Severi lattice with predefined self-intersection and intersection with the canonical class. Parameters ---------- rank : int ''' # canonical class d = get_ak(rank) # basis change a_lst = ['e0-e1', 'e0-e2'] a_lst = [Div.new(a, rank) for a in a_lst] m1_lst = get_divs(d, 1, -1, True) print(d) M = sage_identity_matrix(rank) d_lst = [] d_tup_lst = get_bases_lst(a_lst, M, d_lst, m1_lst, False) B = sage_matrix(sage_ZZ, [dt.e_lst for dt in d_tup_lst[0]]) # list the classes for (dc, cc) in [(2, 0), (1, -1), (0, -2), (2, 2), (2, 4), (3, 1)]: NSTools.p('(dc, cc) =', (dc, cc)) c_lst = get_divs(d, dc, cc, False) for c in c_lst: NSTools.p('\t\t', c, '\t\t', c.get_basis_change(B))
def triples(dpl, mval): ''' Parameters ---------- dpl : DPLattice mval : integer Returns ------- list<(Div,Div,Div)> List of triples in "dpl.fam_lst": [ (a,b,c),... ] so that (1) There does not exists e in "dpl.m1_lst" with the property that a*e==b*e==c*e==0. (2) 1 <= max( a*b, a*c, b*c ) <= mval. ''' key = 'triples__' + str(dpl).replace('\n', '---') + '---' + str(mval) if key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] f_lst = dpl.fam_lst e_lst = dpl.m1_lst # obtain list of triples (a,b,c) in f_lst # that are not orthogonal to any element in e_lst t_lst = [] idx_lst_lst = sage_Subsets(range(len(f_lst)), 3) eta = ETA(len(idx_lst_lst), 500000) for idx_lst in idx_lst_lst: eta.update('t_lst') t = [f_lst[idx] for idx in idx_lst] if t[0] * t[1] > mval: continue if t[0] * t[2] > mval: continue if t[1] * t[2] > mval: continue # elements in f_lst correspond to divisor classes of curves on a # surface and thus t[i]*t[j]>=1 for all i,j \in {0,1,2} so that i!=j. cont = False for e in e_lst: if [f * e for f in t] == [0, 0, 0]: cont = True break if cont: continue if not contains_perm(t_lst, t): t_lst += [t] NSTools.p('t_lst =', t_lst) # cache output NSTools.get_tool_dct()[key] = t_lst NSTools.save_tool_dct() return t_lst
def get_webs(dpl): ''' Returns lists of families of conics for each possible complex basis change. The n-th family in each list correspond to a fixed family wrt. different bases for each n. Parameters ---------- dpl : DPLattice Represents the Neron-Severi lattice of a weak del Pezzo surface. Returns ------- list<list<Div>> A list of lists of Div objects. Each Div object f has the property that f*(3e0-e1-...-er)=2, f*f==0 and f*d>=0 for all d in dpl.d_lst. Such a Div object corresponds geometrically to a family of conics. For each index i, the i-th entry of each list of Div object corresponds to the same family of conics. ''' key = 'get_webs__' + str(dpl).replace('\n', '---') if key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] ak = get_ak(dpl.get_rank()) all_m1_lst = get_divs(ak, 1, -1, True) akc, cc = (3, 1) M = sage_identity_matrix(dpl.get_rank()) fam_lst_lst = [] for e0 in get_divs(ak, akc, cc, True): NSTools.p('e0 =', e0) for B_lst in get_bases_lst([e0], M, dpl.d_lst, all_m1_lst, True): B = sage_matrix(sage_ZZ, [d.e_lst for d in B_lst]) dplB = dpl.get_basis_change(B) fam_lst_lst += [dplB.real_fam_lst] # reduce fam_lst pat_lst_lst = [] rfam_lst_lst = [] for fam_lst in fam_lst_lst: pat_lst = [0 if fam[0] != 1 else 1 for fam in fam_lst] if pat_lst not in pat_lst_lst: pat_lst_lst += [pat_lst] rfam_lst_lst += [fam_lst] # cache output NSTools.get_tool_dct()[key] = rfam_lst_lst NSTools.save_tool_dct() return rfam_lst_lst
def import_cls(cls_lst, inv): ''' This method is used by get_cls(). Parameters ---------- cls_lst : list<DPLattice> A list of DPLattice objects of rank "inv.get_rank()-1". These lattices correspond to Neron-Severi lattices of weak Del Pezzo surfaces. inv : DPLattice A DPLattice object representing an involution. We expect inv.Md_lst to be set. Returns ------- list<DPLattice> A list of compatible DPLattice objects in cls_lst that are converted so as to have the same rank and involution matrix as inv.get_rank() and inv.M, respectively. The returned list always contains inv itself. ''' out_lst = [] for cls in cls_lst: # convert divisors to new rank Md_lst = [Div.new(str(d), inv.get_rank()) for d in cls.Md_lst] d_lst = [Div.new(str(d), inv.get_rank()) for d in cls.d_lst] # import if the involution is compatible if set(Md_lst) == set(inv.Md_lst): NSTools.p('importing: ', (inv.get_rank(), cls.get_marked_Mtype(), cls.get_real_type()), Md_lst, '==', inv.Md_lst) out = DPLattice(d_lst, inv.Md_lst, inv.M) out.set_attributes() out_lst += [out] # always ensure that at least inv object is contained if out_lst == []: return [inv] # we expect that inv is contained in the out_lst # for correctness of the get_cls() algorithm. assert inv in out_lst return out_lst
def usecase__get_cls(max_rank): ''' Classification of root bases in root system of rank at most "max_rank". See "DPLattice.get_cls_root_bases()". Parameters ---------- max_rank : int Maximal rank. ''' row_format = '{:>6}{:>5}{:>8}{:>16}{:>5}{:>5}{:>5}{:>5}{:>6}{:>7}{:>70}{:>135}{:>340}' rownr = 0 for rank in range(3, max_rank + 1): dpl_lst = DPLattice.get_cls(rank) row_lst = [[ 'rownr', 'rank', 'Mtype', 'type', '#-2', '#-1', '#fam', '#-2R', '#-1R', '#famR', 'Md_lst', 'd_lst', 'M' ]] for dpl in sorted(dpl_lst): row_lst += [ [rownr, rank, dpl.get_marked_Mtype(), dpl.get_real_type()] + list(dpl.get_numbers()) + [str(dpl.Md_lst)] + [str(dpl.d_lst)] + [str(list(dpl.M))] ] rownr += 1 s = '' for row in row_lst: s += row_format.format(*row) + '\n' NSTools.p('Classification of root bases:\n' + s) NSTools.p('rank =', rank, ', len =', len(dpl_lst)) NSTools.p(80 * '#') for rank in range(3, max_rank + 1): NSTools.p('rank =', rank, ', len =', len(DPLattice.get_cls(rank))) NSTools.p(80 * '#')
def get_ext_graph(d_lst, M): ''' Parameters ---------- d_lst : list<Div> A list of "Div" objects of equal rank. M : sage_matrix<sage_ZZ> A square matrix with integral coefficients of rank "d_lst[0].rank()" Returns ------- A labeled "sage_Graph()" where the elements of "d_lst" are the vertices. A pair of non-orthogonal vertices are connected by and edge labeled with their non-zero intersection product. Two vertices which are related via M are connected with an edge labeled 1000. Labeled self-loops are also included. ''' NSTools.p('d_lst =', len(d_lst), d_lst, ', M =', list(M)) G = sage_Graph() G.add_vertices(range(len(d_lst))) for i in range(len(d_lst)): for j in range(len(d_lst)): if d_lst[i] * d_lst[j] != 0: G.add_edge(i, j, d_lst[i] * d_lst[j]) for i in range(len(d_lst)): j = d_lst.index(d_lst[i].mat_mul(M)) G.add_edge(i, j, 1000) return G
def update(self, *info_lst): ''' Should be called inside a loop. Prints an estimation for the time it takes for a program to terminate (ETA for short). We refer to the program termination as arrival. Parameters ---------- *info_lst : string Variable length argument list consisting of additional information that is printed together with ETA. ''' if self.counter % self.ival == 0: cur_time = self.time() ival_time = (cur_time - self.prv_time) / (60 * self.ival) passed_time = sage_n((cur_time - self.ini_time) / 60, digits=5) self.eta_time = sage_n(ival_time * (self.total - self.counter), digits=5) s = '' for info in info_lst: s += str(info) + ' ' NSTools.p('ETA =', self.eta_time, 'm,', 'counter =', self.counter, '/', self.total, ',', 'time =', passed_time, 'm,', 'info =', s) # update previous time self.prv_time = cur_time # increase counter self.counter += 1
def usecase__construct_surfaces(): ''' We construct a surface parametrization and its Neron-Severi lattice. Requires the linear_series package. ''' # Blowup of projective plane in 3 colinear points # and 2 infinitely near points. The image of the # map associated to the linear series is a quartic # del Pezzo surface with 5 families of conics. # Moreover the surface contains 8 straight lines. # ring = PolyRing('x,y,z', True) p1 = (-1, 0) p2 = (0, 0) p3 = (1, 0) p4 = (0, 1) p5 = (2, 0) bp_tree = BasePointTree() bp_tree.add('z', p1, 1) bp_tree.add('z', p2, 1) bp_tree.add('z', p3, 1) bp = bp_tree.add('z', p4, 1) bp.add('t', p5, 1) ls = LinearSeries.get([3], bp_tree) NSTools.p(ls.get_bp_tree()) NSTools.p('implicit equation =\n\t', ls.get_implicit_image()) # construct NS-lattice where p1=e1,...,p5=e5 rank = 6 d_lst = ['e0-e1-e2-e3', 'e4-e5'] # basepoint p5 is infinitely near to p4 Md_lst = [] M = sage_identity_matrix(6) d_lst = [Div.new(d, rank) for d in d_lst] Md_lst = [Div.new(Md, rank) for Md in Md_lst] M = sage_matrix(M) dpl = DPLattice(d_lst, Md_lst, M) NSTools.p('Neron-Severi lattice =', dpl) # search representative for the equivalence class in classification assert dpl in DPLattice.get_cls(rank)
def get_cls(rank=9): ''' Parameters ---------- rank : int An integer in [1,...,9]. Returns ------- list<DPLattice> A list of DPLattice objects corresponding to Neron-Severi lattices of weak Del Pezzo surfaces of degree (10-rank). The list contains exactly one representative for each equivalence class. All the Div objects referenced in the DPLattice objects of the output have the default intersection matrix: diagonal matrix with diagonal: (1,-1,...,-1). If rank<3 then the empty list is returned. ''' if rank < 3: return [] # check cache key = 'get_cls_' + str(rank) if key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] NSTools.p('rank =', rank) # collect all lattices with either d_lst==[] of Md_lst==[] bas_lst = DPLattice.get_bas_lst(rank) inv_lst = DPLattice.get_inv_lst(rank) # we loop through all involutions NSTools.p('start looping through inv_lst: ', len(inv_lst), [inv.get_marked_Mtype() for inv in inv_lst]) dpl_lst = [] for inv in inv_lst: NSTools.p('looping through inv_lst:', (rank, inv.get_marked_Mtype(), inv.Md_lst)) # recover the known classification if inv.Mtype == 'A0': NSTools.p( 'Since Mtype equals A0 we recover the classification from bas_lst.' ) dpl_lst += [bas for bas in bas_lst] continue # partition the roots into two sets s_lst, q_lst = DPLattice.get_part_roots(inv) # import classification for rank-1 bas1_lst = DPLattice.import_cls(DPLattice.get_cls(rank - 1), inv) NSTools.p( 'looping through inv_lst continued after recursive call:', (rank, inv.get_marked_Mtype(), inv.Md_lst)) # correct partition of roots (bas1_lst always contains inv) if len(bas1_lst) > 1: e = Div.new('e' + str(rank - 1), inv.get_rank()) s_lst = [s for s in s_lst if s * e != 0] q_lst = [q for q in q_lst if q * e != 0] NSTools.p('bas1_lst =', len(bas1_lst), [(bas1.Mtype, bas1.type) for bas1 in bas1_lst]) NSTools.p('s_lst =', len(s_lst), s_lst) NSTools.p('q_lst =', len(q_lst), q_lst) # collect all possible root bases in s_lst and q_lst bas2_lst = [] bas3_lst = [] visited_type_lst = [] eta = ETA(len(bas_lst), 1) for bas in bas_lst: # display progress info eta.update('get_cls seeking bases in s_lst and q_lst: ', (rank, inv.get_marked_Mtype(), bas.get_real_type())) # each type in bas_lst is treated only once if bas.type in visited_type_lst: continue visited_type_lst += [bas.type] # collect bases of type bas.type in s_lst if DPLattice.get_num_types(inv, bas, bas_lst) != 0: bas2_lst += DPLattice.seek_bases(inv, bas.d_lst, s_lst) # collect bases of type bas.type in q_lst if 2 * len(bas.d_lst) > rank - 1: continue # the rank of a root subsystem is bounded by rank-1 tmp_lst = DPLattice.seek_bases(inv, bas.d_lst, q_lst) for tmp in tmp_lst: tmp.d_lst += [d.mat_mul(inv.M) for d in tmp.d_lst] if is_root_basis( tmp.d_lst ): # the roots and their involutions might have intersection product 1 tmp.d_lst.sort() bas3_lst += [tmp] # debug info NSTools.p('Setting Dynkin types of', len(bas2_lst + bas3_lst), 'items...please wait...') eta = ETA(len(bas2_lst + bas3_lst), len(bas2_lst + bas3_lst) / 10) for bas in bas2_lst + bas3_lst: bas.type = get_dynkin_type(bas.d_lst) bas.Mtype = get_dynkin_type(bas.Md_lst) eta.update(bas.get_rank(), bas.get_marked_Mtype(), bas.type) bas1_lst.sort() bas2_lst.sort() bas3_lst.sort() t_lst1 = [bas.type for bas in bas1_lst] t_lst2 = [bas.type for bas in bas2_lst] t_lst3 = [bas.type for bas in bas3_lst] lst1 = sorted(list(set([(t, t_lst1.count(t)) for t in t_lst1]))) lst2 = sorted(list(set([(t, t_lst2.count(t)) for t in t_lst2]))) lst3 = sorted(list(set([(t, t_lst3.count(t)) for t in t_lst3]))) NSTools.p('inv =', inv.get_marked_Mtype(), ', rank =', rank) NSTools.p('bas1_lst =', len(bas1_lst), lst1) NSTools.p('bas2_lst =', len(bas2_lst), lst2) NSTools.p('bas3_lst =', len(bas3_lst), lst3) # construct a list of combinations of DPLattice objects in bas1_lst bas2_lst and comb_lst = [] total = len(bas1_lst) * len(bas2_lst) * len(bas3_lst) step = total / 10 if total > 10 else total eta = ETA(total, step) for bas1 in bas1_lst: for bas2 in bas2_lst: for bas3 in bas3_lst: eta.update( 'last loop in get_cls: ( bas1.type, bas2.type, bas3.type )=', (bas1.type, bas2.type, bas3.type)) d_lst = bas1.d_lst + bas2.d_lst + bas3.d_lst # notice that d_lst can be equal to [] if len(d_lst) > rank - 1: continue # the rank of a root subsystem is bounded by rank-1 if is_root_basis(d_lst): dpl = DPLattice(d_lst, inv.Md_lst, inv.M) if dpl not in dpl_lst: dpl.set_attributes() dpl_lst += [dpl] NSTools.p( '\t appended: ', (rank, dpl.get_marked_Mtype(), dpl.get_real_type()), ', ( bas1.type, bas2.type, bas3.type ) =', (bas1.type, bas2.type, bas3.type)) # store in cache # dpl_lst.sort() NSTools.get_tool_dct()[key] = dpl_lst NSTools.save_tool_dct() return dpl_lst
def usecase__roman_circles(): ''' We compute circles on a Roman surface. ''' # parametrization of the Roman surface # p_lst = '[ z^2+x^2+y^2, -z*x, -x*y, z*y ]' # we consider the stereographic projection from # S^3 = { x in P^4 | -x0^2+x1^2+x2^2+x3^2+x4^2 = 0 } # where the center of projection is (1:0:0:0:1): # (x0:x1:x2:x3:x4) |---> (x0-x4:x1:x2:x3) # inverse stereographic projection into 3-sphere # s_lst = '[ y0^2+y1^2+y2^2+y3^2, 2*y0*y1, 2*y0*y2, 2*y0*y3, -y0^2+y1^2+y2^2+y3^2 ]' # compose p_lst with s_lst # ring = PolyRing('x,y,z,y0,y1,y2,y3') x, y, z, y0, y1, y2, y3 = ring.gens() p_lst = ring.coerce(p_lst) s_lst = ring.coerce(s_lst) dct = {y0: p_lst[0], y1: p_lst[1], y2: p_lst[2], y3: p_lst[3]} sp_lst = [s.subs(dct) for s in s_lst] NSTools.p('sp_lst =') for sp in sp_lst: NSTools.p('\t\t', sage_factor(sp)) NSTools.p('gcd(sp_lst) =', sage_gcd(sp_lst)) # determine base points # ring = PolyRing('x,y,z', True) sp_lst = ring.coerce(sp_lst) ls = LinearSeries(sp_lst, ring) NSTools.p(ls.get_bp_tree()) # We expect that the basepoints come from the intersection # of the Roman surface with the absolute conic: # A = { (y0:y1:y2:y3) in P^3 | y0=y1^2+y2^2+y3^2 = 0 } # # Circles are the image via p_lst of lines that pass through # complex conjugate points. # ring = PolyRing('x,y,z', False) # reinitialize ring with updated numberfield a0, a1, a2, a3 = ring.root_gens() # a0=(1-I*sqrt(3)) with conjugate a0-1 and minimal polynomial t^2-t+1 # we compute candidate classes of circles # h = Div.new('4e0-e1-e2-e3-e4-e5-e6-e7-e8') div_lst = get_divs(h, 2, -2, False) + get_divs(h, 2, -1, False) NSTools.p('Classes of circles up to permutation:') for c in div_lst: NSTools.p('\t\t', c) # We recover the preimages of circles in the Roman surface # under the map p_lst, by constructing for each candidate # class the corresponding linear series. # 2e0-e1-e2-e3-e4-e5-e6-e7-e8 b = [(a0 - 1, -a0), (-a0, a0 - 1)] b += [(-a0 + 1, a0), (a0, -a0 + 1)] b += [(a0 - 1, a0), (-a0, -a0 + 1)] b += [(-a0 + 1, -a0), (a0, a0 - 1)] bp_tree = BasePointTree() for i in range(6): bp_tree.add('z', b[i], 1) NSTools.p('basepoints =', b) NSTools.p(LinearSeries.get([2], bp_tree)) # e0-e1-e2 b = [(a0 - 1, -a0), (-a0, a0 - 1)] bp_tree = BasePointTree() bp = bp_tree.add('z', b[0], 1) bp = bp_tree.add('z', b[1], 1) NSTools.p('basepoints =', b) NSTools.p(LinearSeries.get([1], bp_tree)) # e0-e3-e4 b = [(-a0 + 1, a0), (a0, -a0 + 1)] bp_tree = BasePointTree() bp = bp_tree.add('z', b[0], 1) bp = bp_tree.add('z', b[1], 1) NSTools.p('basepoints =', b) NSTools.p(LinearSeries.get([1], bp_tree)) # e0-e5-e6 b = [(a0 - 1, a0), (-a0, -a0 + 1)] bp_tree = BasePointTree() bp = bp_tree.add('z', b[0], 1) bp = bp_tree.add('z', b[1], 1) NSTools.p('basepoints =', b) NSTools.p(LinearSeries.get([1], bp_tree)) # e0-e7-e8 b = [(-a0 + 1, -a0), (a0, a0 - 1)] bp_tree = BasePointTree() bp = bp_tree.add('z', b[0], 1) bp = bp_tree.add('z', b[1], 1) NSTools.p('basepoints =', b) NSTools.p(LinearSeries.get([1], bp_tree)) return
def get_bas_lst(rank=9): ''' See [Algorithm 5, http://arxiv.org/abs/1302.6678] for more info. Parameters ---------- rank : int An integer in [3,...,9]. Returns ------- list<DPLattice> A list of "DPLattice" objects dpl such that dpl.d_lst is the bases of a root subsystem and dpl.Mtype == A0. The list contains exactly one representative for all root subsystems up to equivalence. The list represents a classification of root subsystems of the root system with Dynkin type either: A1, A1+A2, A4, D5, E6, E7 or E8, corresponding to ranks 3, 4, 5, 6, 7, 8 and 9 respectively (eg. A1+A2 if rank equals 4, and E8 if rank equals 9). Note that the root systems live in a subspace of the vector space associated to the Neron-Severi lattice of a weak Del Pezzo surface. ''' # check whether classification of root bases is in cache key = 'get_bas_lst__' + str(rank) if key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] NSTools.p('start') A = [12, 23, 34, 45, 56, 67, 78] B = [1123, 1145, 1456, 1567, 1678, 278] C = [1127, 1347, 1567, 234, 278, 308] D = [1123, 1345, 1156, 1258, 1367, 1247, 1468, 1178] dpl_lst = [] for (lst1, lst2) in [(A, []), (A, B), (A, C), ([], D)]: # restrict to divisors in list, that are of rank at most "max_rank" lst1 = [ Div.new(str(e), rank) for e in lst1 if rank >= Div.get_min_rank(str(e)) ] lst2 = [ Div.new(str(e), rank) for e in lst2 if rank >= Div.get_min_rank(str(e)) ] # the involution is trivial Md_lst = [] M = sage_identity_matrix(sage_QQ, rank) # loop through the lists sub1 = sage_Subsets(range(len(lst1))) sub2 = sage_Subsets(range(len(lst2))) eta = ETA(len(sub1) * len(sub2), 20) for idx2_lst in sub2: for idx1_lst in sub1: eta.update('get_bas_lst rank =', rank) d_lst = [lst1[idx1] for idx1 in idx1_lst] d_lst += [lst2[idx2] for idx2 in idx2_lst] if not is_root_basis(d_lst): continue dpl = DPLattice(d_lst, Md_lst, M) if dpl not in dpl_lst: dpl.set_attributes() dpl_lst += [dpl] # cache output dpl_lst.sort() NSTools.get_tool_dct()[key] = dpl_lst NSTools.save_tool_dct() return dpl_lst
def usecase__graphs(max_rank): ''' Lists attributes of simple family graphs. Parameters ---------- max_rank : int Maximal rank of DPLattice objects that are considered. ''' row_format = '{:<6}{:<5}{:<8}{:<16}{:<7}{:<10}{:<95}{:<30}{:<15}{:<15}{:<15}{:<15}' already_in_cache = True dpl_lst = [] rownr = 0 row_lst = [[ 'rownr', 'deg', 'Mtype', 'type', '#vert', '#edges', 'degrees', 'labels', 'complete', 'connected', 'vert-xfer', 'edge-xfer' ]] for rank in range(3, max_rank + 1): NSTools.p('rank =', rank) for dpl in DPLattice.get_cls(rank): already_in_cache = already_in_cache and (dpl.SG != None) dpl_lst += [dpl] SG, SG_data = dpl.get_SG() row_lst += [[ rownr, 10 - rank, dpl.get_marked_Mtype(), dpl.get_real_type() ] + SG_data] rownr += 1 if rank == 9 and (rownr <= 390 or rownr % 100 == 0): NSTools.p('\t\trownr =', rownr) s = '' for row in row_lst: s += row_format.format(*row) + '\n' NSTools.p('Classification of simple family graphs:\n' + s) if not already_in_cache: NSTools.p('Saving data for simple family graphs...') NSTools.save_tool_dct() # example for how to plot a simple family graph # NSTools.p('Plotting a simple family graph...') SG, SG_data = DPLattice.get_cls(6)[0].get_SG() P = SG.graphplot(vertex_size=1, vertex_labels=True, edge_labels=True, color_by_label=False, layout='circular').plot() P.save(os.environ['OUTPUT_PATH'] + 'graph.png') NSTools.p('#components =', SG.connected_components_number())
def usecase__analyze_graphs(max_rank): ''' We analyze the graphs of DPLattice objects in the output of DPLattice.get_cls(). Parameters ---------- max_rank : int Maximal rank of DPLattice objects that are considered. ''' # Examine which of the graphs associated to DPLattices # are isomorphic to one of the constructed graphs. # NSTools.p('\t Compare contructed graphs with classified graphs...') rownr = -1 max_verts = 0 for rank in range(3, max_rank + 1): NSTools.p('\t ---') for dpl in DPLattice.get_cls(rank): rownr += 1 # retrieve the graph SG for analysis SG, SG_data = dpl.get_SG() if SG.num_verts() <= 3: continue # check if each edge label is in [2,4] if [e for e in SG_data[3] if e not in [2, 4]] != []: continue # initialize string s = '' s += str(rownr) + ' ' + 'rank=' + str(rank) + ' ' # Initialize G_lst which is a list of tuples (G,G_str) # where G is a constructed graph and G_str is its string identifier. # The identifiers are according Theorem 1 in arXiv:1807.05881v2. # G_lst = [] for nv1 in range(1, SG.num_verts() + 1): for nv2 in range(1, SG.num_verts() + 1): # determine list c_lst of 2-element subsets of [1,...,m] # so that m is minimal under the condition that len(c_lst)>=nv1 c_lst = [] for i in range(2 * nv1): c_lst = list(sage_Combinations(i, 2)) if len(c_lst) >= nv1: break # construct graphs # Gd = sage_Graph() Gd.add_vertices(range(nv1)) G_lst += [(Gd, 'Gd:' + str(nv1))] Ge = sage_Graph() Ge.add_vertices(range(nv1)) for i in Ge.vertices(): for j in Ge.vertices(): Ge.add_edge(i, j, 2) G_lst += [(Ge, 'Ge:' + str(nv1))] Gf = sage_Graph() Gf.add_vertices(range(len(c_lst))) for i in Gf.vertices(): for j in Gf.vertices(): q = len([c for c in c_lst[i] if c in c_lst[j]]) Gf.add_edge(i, j, 4 - 2 * q) G_lst += [(Gf, 'Gf:' + str(Gf.num_verts()))] Gg = sage_Graph() Gg.add_vertices(range(len(c_lst))) for i in Gg.vertices(): for j in Gg.vertices(): q = len([c for c in c_lst[i] if c in c_lst[j]]) if q > 0: Gg.add_edge(i, j, 2) G_lst += [(Gg, 'Gg:' + str(Gg.num_verts()))] # construct combined graphs # if nv1 + nv2 > SG.num_verts(): continue Gd2 = sage_Graph() Gd2.add_vertices(range(nv2)) Ge2 = sage_Graph() Ge2.add_vertices(range(nv2)) for i in Ge2.vertices(): for j in Ge2.vertices(): Ge2.add_edge(i, j, 2) if nv1 + nv2 == SG.num_verts(): if (Gd.num_verts(), Ge2.num_verts()) != (1, 1): Gde = sage_Graph() Gde.add_vertices(Ge2.vertices()) Gde.add_edges(Ge2.edges()) for i in range(Ge2.num_verts() - 1, -1, -1): Gde.relabel({i: i + Gd.num_verts()}) Gde.add_vertices(Gd.vertices()) Gde.add_edges(Gd.edges()) for i in range(Gd.num_verts()): for j in range(Gd.num_verts(), Gde.num_verts()): Gde.add_edge(i, j, 2) G_lst += [(Gde, 'Gde:' + str(Gd.num_verts()) + '+' + str(Ge2.num_verts()))] if len(c_lst) + nv2 == SG.num_verts(): Gfd = sage_Graph() Gfd.add_vertices(Gd2.vertices()) Gfd.add_edges(Gd2.edges()) for i in range(Gd2.num_verts() - 1, -1, -1): Gfd.relabel({i: i + Gf.num_verts()}) Gfd.add_vertices(Gf.vertices()) Gfd.add_edges(Gf.edges()) for i in range(Gf.num_verts()): for j in range(Gf.num_verts(), Gfd.num_verts()): Gfd.add_edge(i, j, 2) G_lst += [(Gfd, 'Gfd:' + str(Gf.num_verts()) + '+' + str(Gd2.num_verts()))] Gge = sage_Graph() Gge.add_vertices(Ge2.vertices()) Gge.add_edges(Ge2.edges()) for i in range(Ge2.num_verts() - 1, -1, -1): Gge.relabel({i: i + Gg.num_verts()}) Gge.add_vertices(Gg.vertices()) Gge.add_edges(Gg.edges()) for i in range(Gg.num_verts()): for j in range(Gg.num_verts(), Gge.num_verts()): Gge.add_edge(i, j, 2) G_lst += [(Gge, 'Gge:' + str(Gg.num_verts()) + '+' + str(Ge2.num_verts()))] # check for each of the constructed graphs whether # it is isomorphic to dpl.get_SG() # for (G, G_str) in G_lst: if SG.is_isomorphic(G, edge_labels=True): max_verts = max(max_verts, G.num_verts()) if G_str not in s: s += G_str + ' ' if ':' in s: NSTools.p('\t', s) NSTools.p('max_verts =', max_verts)
def get_bases_lst(a_lst, M, d_lst, m1_lst, perm=False): ''' Returns a list of basis with specified generators. Parameters ---------- a_lst : list<Div> A list of linear independent Div objects of the same rank with 3<=rank<=9. It is required that "set(a_lst)==set([ a.mat_mul(M) for a in a_lst ])". M : sage_matrix<sage_ZZ> A unimodular matrix representing an involution. d_lst : list<Div> A list of Div objects d of the same rank as any element in "a_lst", so that "d*k==0" and "d*d==-2". These represent a root basis for the indecomposable (-2)-classes in the Neron-Severi lattice of a weak del Pezzo surface. m1_lst : list<Div> A list of Div objects d of the same rank as any element in "a_lst", so that "d*k==d*d==-1". These represent (-1)-classes in the Neron-Severi lattice of a weak del Pezzo surface. perm : bool If False, then we consider two bases the same if the generators of the first basis can be obtained from the second basis via a permutation matrix. Returns ------- list<tuple<Div>> A list of tuples of Div objects. Each tuple of Div objects represents a basis for the Neron-Severi lattice determined by d_lst and m1_lst. The bases are of the form < a1,...,as, b1,...,bt > with the following property * a1,...,as are defined by the input "a_lst" * bi is an element in m1_lst such that bi*bj=am*bi=0 for all 1<=i<j<=t and 1<=m<=s If "a_lst==[]" then "[[]]" is returned. ''' key = 'get_bases_lst__' + str( (a_lst, M, d_lst, m1_lst, perm)) + '__' + str(M.rank()) if key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] if a_lst == []: return [[]] if len(a_lst) == a_lst[0].rank(): return [tuple(a_lst)] e_lst = [] for m1 in get_indecomp_divs(m1_lst, d_lst): if set([m1 * a for a in a_lst]) != {0}: continue if m1 * m1.mat_mul(M) > 0: continue e_lst += [m1] bas_lst = [] for e in e_lst: Me = e.mat_mul(M) new_d_lst = [d for d in d_lst if d * e == d * Me == 0] new_m1_lst = [m1 for m1 in m1_lst if m1 * e == m1 * Me == 0] add_lst = [e] if e != Me: add_lst += [Me] bas2_lst = get_bases_lst(a_lst + add_lst, M, new_d_lst, new_m1_lst, perm) if perm: bas_lst += bas2_lst else: for bas2 in bas2_lst: found = False for bas in bas_lst: # check whether the two bases are the same up to # permutation of generators if set(bas) == set(bas2): found = True break # break out of nearest for loop if not found: NSTools.p('found new basis: ', bas2, ', bas2_lst =', bas2_lst) bas_lst += [bas2] # cache output NSTools.get_tool_dct()[key] = bas_lst NSTools.save_tool_dct() return bas_lst
NSTools.start_timer() # # Should be between 3 and 9. # computes classifications up to rank "max_rank". # max_rank = 9 ######################################### # # # (Un)comment one or more use cases # # # ######################################### usecase__get_cls(max_rank) usecase__get_classes_dp1(max_rank) usecase__graphs(max_rank) usecase__analyze_graphs(max_rank) usecase__construct_surfaces() # usecase__roman_circles() # takes about 3 minutes ######################################### # # # End of list of use case methods. # # # ######################################### NSTools.end_timer() NSTools.p('The End')
def get_part_roots(inv): ''' Return two subsets of roots using the input involution. This method is used by get_cls(). Parameters ---------- inv : DPLattice We expect inv.type=='A0'. We will use inv.Mtype and inv.M. Returns ------- list<Div>, list<Div> Let R be defined by the list get_divs( get_ak( inv.get_rank() ), 0, -2, True ) whose elements are Div objects. If r is a Div object, then M(r) is shorthand notation for r.mat_mul(inv.M). The two returned lists correspond respectively to S := { r in R | M(r)=r } and Q union Q' := { r in R | M(r) not in {r,-r} and r*M(r)>0 } where Q = M(Q'). ''' r_lst = get_divs(get_ak(inv.get_rank()), 0, -2, True) s_lst = [r for r in r_lst if r.mat_mul(inv.M) == r] tq1_lst = [ r for r in r_lst if r.mat_mul(inv.M) not in [r, r.int_mul(-1)] ] tq_lst = [q for q in tq1_lst if q * q.mat_mul(inv.M) >= 0] q_lst = [] for q in sorted(tq_lst): if q not in q_lst and q.mat_mul(inv.M) not in q_lst: q_lst += [q] # q_lst += [ q.int_mul( -1 ) for q in q_lst ] NSTools.p('r_lst =', len(r_lst), r_lst) NSTools.p('s_lst =', len(s_lst), s_lst) NSTools.p('tq1_lst =', len(tq1_lst), tq1_lst) NSTools.p('tq_lst =', len(tq_lst), tq_lst) NSTools.p('q_lst =', len(q_lst), q_lst) NSTools.p(' M -->', len(q_lst), [q.mat_mul(inv.M) for q in q_lst]) NSTools.p('inv.Md_lst =', inv.Mtype, inv.Md_lst, ', rank =', inv.get_rank()) return s_lst, q_lst
def get_inv_lst(rank=9): ''' Outputs a list representing a classification of root subsystems that define unimodular involutions on the Neron-Severi lattice of a weak del Pezzo surface. We consider root subsystems of the root system with Dynkin type either: A1, A1+A2, A4, D5, E6, E7 or E8, corresponding to ranks 3, 4, 5, 6, 7, 8 and 9 respectively (eg. A1+A2 if rank equals 4, and E8 if rank equals 9). Note that root systems live in a subspace of the vector space associated to the Neron-Severi lattice of a weak Del Pezzo surface. Parameters ---------- max_rank : int An integer in [3,...,9]. Returns ------- list<DPLattice> A list of "DPLattice" objects dpl such that dpl.Md_lst is the bases of a root subsystem and dpl.type == A0. The list contains exactly one representative for root subsystems up to equivalence, so that the root subsystem defines a unimodular involution. ''' # check cache key = 'get_inv_lst__' + str(rank) if False and key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] bas_lst = DPLattice.get_bas_lst(rank) NSTools.p('rank =', rank) amb_lst = [] inv_lst = [] eta = ETA(len(bas_lst), 1) for bas in bas_lst: eta.update(bas.type) M = basis_to_involution(bas.d_lst, rank) if not is_integral_involution(M): continue inv = DPLattice([], bas.d_lst, M) inv.set_attributes() NSTools.p('Found type of involution: ', bas.type) # real structures with different Dynkin types may be equivalent if inv not in inv_lst: inv_lst += [inv] else: inv_prv = [inv2 for inv2 in inv_lst if inv == inv2][0] inv_lst = [inv2 for inv2 in inv_lst if not inv2 == inv] amb_lst += [inv, inv_prv] if inv > inv_prv: inv_lst += [inv] else: inv_lst += [inv_prv] NSTools.p('\tAmbitious type:', inv.Mtype, '==', inv_prv.Mtype, ' inv>inv_prv: ', inv > inv_prv, ' ambitious types =', [amb.Mtype for amb in amb_lst if amb == inv]) # store in cache inv_lst.sort() NSTools.get_tool_dct()[key] = inv_lst NSTools.save_tool_dct() return inv_lst
def get_SG(self): ''' The simple family graph associated to the Neron-Severi lattice of a weak del Pezzo surface is defined as the incidence diagram of self.real_fam_lst, with the edges labeled <=1 removed. All vertices are labeled with the index of the element in self.real_fam_lst. In the mathematical version (see arxiv paper) the vertices are labeled with the dimension of the linear series, which is always 1 with one exception: If len(self.real_fam_lst)==0 and rank==3, then the simple family graph consists of a single vertex labeled 2. Example ------- # The following graph is related to the E8 root system: # dpl = DPLattice.get_cls( 9 )[0] assert set(dpl.get_SG().num_verts()) == {2160} assert set(dpl.get_SG().get_degree()) == {2095} assert set(dpl.get_SG().edge_labels()) == {2,3,4,5,6,7,8} Returns ------- sage_GRAPH, [int, int, list<int>, list<int>, bool, bool, bool, bool ] The simple family graph self.SG and a list self.SG_data associated to the current DPLattice object. Here self.SG_data consists of data that describes self.SG. This method also initializes self.SG and self.SG_data. ''' if self.SG != None: return self.SG, self.SG_data if self.get_rank() == 9 and self.get_numbers()[-1] > 800: NSTools.p( 'Initializing simple family graph of current DPLattice object...', self.get_rank(), self.get_marked_Mtype(), self.get_real_type()) f = self.real_fam_lst f_range = range(len(f)) self.SG = sage_Graph() self.SG.add_vertices(f_range) for i in f_range: for j in f_range: if f[i] * f[j] > 1: self.SG.add_edge(i, j, f[i] * f[j]) self.SG_data = [ self.SG.num_verts(), # number of vertices self.SG.num_edges(), # number of edges sorted(list(set( self.SG.degree()))), # possible numbers of outgoing edges sorted(list(set(self.SG.edge_labels()))), # possible edge labels self.SG.is_clique(), # True iff the graph is complete. self.SG.is_connected(), self.SG.is_vertex_transitive(), self.SG.is_edge_transitive() ] return self.SG, self.SG_data
def get_divs(d, dc, cc, perm=False): ''' Computes divisors in unimodular lattice with prescribed intersection product. Parameters ---------- d : Div object d0*e0 + d1*e1 +...+ dr*er such that * product signature equals (1,d.rank()-1) * d0>0 * d1,...,dr<=0 dc : int A positive integer. cc : int Self intersection. perm : boolean If True, then generators are permuted. Returns ------- list<Div> Returns a sorted list of "Div" objects * c = c0*e0 + c1*e1 +...+ cr*er such that * d.rank() == r+1 * dc == d*c (signature = (1,rank-1)) * cc == c*c (signature = (1,rank-1)) and each Div object satisfies exactly one of the following conditions: * c == ei - ej for 0>i>j>=r, * c == ei for i>0, or * c0 > 0, c1,...,cr <= 0 If "perm" is False, then then only one representative for each c is returned up to permutation of ei for i>0. For example, e0-e1-e2 and e0-e1-e3 are considered equivalent, and only e0-e1-e2 is returned, since e0-e1-e2>e0-e1-e3 (see "get_div_set()" for the ordering). In particular, c1 >= c2 >= ... >= cr. Note ---- If d=[3]+8*[-1], (dc,cc)==(0,-2) and perm=False then the Div classes are '12', '1123', '212' and '308'. See "Div.get_label()" for the notation. These classes correspond to the (-2)-classes in the Neron-Severi lattice associated to a weak del Pezzo surface. If perm==False then only one representative for each q is returned up to permutation of ei for i>0. For example, e0-e1-e2 and e0-e1-e3 are considered equivalent, and only e0-e1-e2 is returned, since e0-e1-e2>e0-e1-e3 (see "Div.__lt__()" for the ordering). ''' # check if input was already computed # key = 'get_divs_' + str((d, dc, cc, perm)) if key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] # construct div set # NSTools.p('Constructing div set classes for ', (d, dc, cc, perm)) out_lst = [] # compute classes of the form ei or ei-ej for i,j>0 # if (dc, cc) == (1, -1) or (dc, cc) == (0, -2): m2_lst = [] # list of divisors of the form ei-ej for i,j>0 m1_lst = [] # list of divisors of the form ei for i>0 if perm: # Example: # >>> list(Combinations( [1,2,3,4], 2 )) # [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] # Notice that r=d.rank()-1 if c = c0*e0 + c1*e1 +...+ cr*er. # for comb in sage_Combinations(range(1, d.rank()), 2): m2_lst += [Div.new(str(comb[0]) + str(comb[1]), d.rank())] m1_lst += [ Div.new('e' + str(i), d.rank()) for i in range(1, d.rank()) ] else: # up to permutation of the generators # we may assume that i==1 and j==2. # m2_lst += [Div.new('12', d.rank())] m1_lst += [Div.new('e1', d.rank())] # add the classes that satisfy return # specification to the output list # for c in m1_lst + m2_lst: if (dc, cc) == (d * c, c * c): out_lst += [c] # # Note: cc = c0^2 - c1^2 -...- cr^2 # c0 = 0 cur_eq_diff = -1 while True: c0 = c0 + 1 dc_tail = d[0] * c0 - dc # = d1*c1 +...+ dr*cr dd_tail = d[0]**2 - d * d # = d1^2 +...+ dr^2 cc_tail = c0**2 - cc # = c1^2 +...+ cr^2 # not possible according to io-specs. # if dc_tail < 0 or dd_tail < 0 or cc_tail < 0: NSTools.p('continue... (c0, dc_tail, dd_tail, cc_tail) =', (c0, dc_tail, dd_tail, cc_tail)) if dd_tail < 0: raise Exception('dd_tail =', dd_tail) continue # Cauchy-Schwarz inequality <x,y>^2 <= <x,x>*<y,y> holds? # prv_eq_diff = cur_eq_diff cur_eq_diff = abs(dc_tail * dc_tail - dd_tail * cc_tail) if prv_eq_diff == -1: prv_eq_diff = cur_eq_diff NSTools.p('prv_eq_diff =', prv_eq_diff, ', cur_eq_diff =', cur_eq_diff, ', dc_tail^2 =', dc_tail * dc_tail, ', dd_tail*cc_tail =', dd_tail * cc_tail, ', (c0, dc_tail, dd_tail, cc_tail) =', (c0, dc_tail, dd_tail, cc_tail)) if prv_eq_diff < cur_eq_diff and dc_tail * dc_tail > dd_tail * cc_tail: NSTools.p('stop by Cauchy-Schwarz inequality...') break # out of while loop # obtain all possible [d1*c1+1,...,dr*cr+1] # r = d.rank() - 1 if perm and len(set(d[1:])) != 1: p_lst_lst = sage_Compositions(dc_tail + r, length=r) else: p_lst_lst = sage_Partitions(dc_tail + r, length=r) # data for ETA computation total = len(p_lst_lst) counter = 0 ival = 5000 # obtain [c1,...,cr] from [d1*c1+1,...,dr*cr+1] # for p_lst in p_lst_lst: # ETA if counter % ival == 0: start = time.time() counter += 1 if counter % ival == 0: passed_time = time.time() - start NSTools.p('ETA in minutes =', passed_time * (total - counter) / (ival * 60), ' (', counter, '/', total, '), c0 =', c0, ', prv_eq_diff =', prv_eq_diff, ', cur_eq_diff =', cur_eq_diff) # dc_tail=d1*c1 +...+ dr*cr = p1 +...+ pr with pi>=0 p_lst = [p - 1 for p in p_lst] # obtain c_tail=[c1,...,cr] from [p1,...,pr] valid_part = True c_tail = [] # =[c1,...,cr] for i in range(0, len(p_lst)): if p_lst[i] == 0 or d[i + 1] == 0: c_tail += [p_lst[i]] else: quo, rem = sage_ZZ(p_lst[i]).quo_rem(d[i + 1]) if rem != 0: valid_part = False break # out of i-for-loop else: c_tail += [quo] if not valid_part: continue # add to out list if valid # c = Div([c0] + c_tail) if c.rank() == d.rank() and (dc, cc) == (d * c, c * c): if perm and len(set(d[1:])) == 1: # since d1==...==dr we do not have to # check each permutation. for pc_tail in sage_Permutations(c_tail): out_lst += [Div([c0] + list(pc_tail))] else: out_lst += [c] # sort list of "Div" objects out_lst.sort() # cache output NSTools.get_tool_dct()[key] = out_lst NSTools.save_tool_dct() return out_lst
def cls_to_tex(): ''' Create tex code for the output of DPLattice.get_cls() Returns ------- string A string representing a table of tables in Tex format. The table represent the classification of Neron-Severi lattice of weak del Pezzo surfaces. ''' # create a list of occuring divisors # div_lst = [] for rank in range(3, 9 + 1): for dpl in DPLattice.get_cls(rank): # construct list for involution (e0,...,er)|-->(i0,...,ir) i_lst = [ Div(row).mat_mul(dpl.M) for row in sage_identity_matrix(rank) ] # add each divisor that occurs to div_lst for elt in i_lst + dpl.d_lst: div_lst += [Div(elt.e_lst + (9 - len(elt.e_lst)) * [0])] div_lst = list(set(div_lst)) div_lst.sort() e0 = Div([1, 0, 0, 0, 0, 0, 0, 0, 0]) div_lst.remove(e0) div_lst = [e0] + div_lst # create dictionary characters for elements in div_lst # abc = 'abcdefghijklmnopqrstuvwxyz' ch_lst = [] ch_lst += [ '\\frac{' + ch1 + '}{' + ch2 + '}\\!' for ch1 in '0123456789' for ch2 in '0123456789' ] ch_lst += [ '\\frac{' + ch1 + '}{' + ch2 + '}\\!' for ch1 in '0123456789' for ch2 in 'abcdef' ] NSTools.p('(len(ch_lst), len(div_lst)) =', (len(ch_lst), len(div_lst))) assert len(ch_lst) >= len(div_lst) # create legend and dictionary # lgd_lst = [] sym_dct = {} for i in range(len(div_lst)): sym_dct.update({str(div_lst[i]): ch_lst[i]}) lgd_lst += [[ '$' + ch_lst[i] + '$ :', ('$' + str(div_lst[i]) + '$').replace('e', 'e_') ]] while len(lgd_lst) % 3 != 0: lgd_lst += [['', '']] nnrows = len(lgd_lst) / 3 # create tex for legend # tex_lgd = '' tex_lgd += '\\begin{table}\n' tex_lgd += '\\setstretch{1.4}\n' tex_lgd += '\\tiny\n' tex_lgd += '\\caption{Classification of Neron-Severi lattices of weak del Pezzo surfaces (see THM{nsl})}\n' tex_lgd += '\\label{tab:nsl}\n' tex_lgd += 'A dictionary for symbols in the columns $\\sigma_A$ and $B$:\n\\\\\n' tex_lgd += '\\begin{tabular}{@{}l@{}l@{~~~~}l@{}l@{~~~~}l@{}l@{}}\n' for idx in range(nnrows): c1, c2, c3, c4, c5, c6 = lgd_lst[idx] + lgd_lst[ idx + nnrows] + lgd_lst[idx + 2 * nnrows] tex_lgd += c1 + ' & ' + c2 + ' & ' + c3 + ' & ' + c4 + ' & ' + c5 + ' & ' + c6 tex_lgd += '\\\\\n' tex_lgd += '\\end{tabular}\n' tex_lgd += '\\end{table}\n\n' # number of rows of the big table # nrows = 57 # dictionary for replacing string symbols # rep_dct = { 'A': 'A_', 'D': 'D_', 'E': 'E_', '{': '\\underline{', '[': '\\udot{', ']': '}' } # create classification table # tab_lst = [] # rank 1 and 2 tab9 = [['i ', '$9$', "$A_0 $", '$A_0$', '$0$', '$1$', '']] tab8 = [['ii ', '$8$', "$A_0 $", '$A_0$', '$0$', '$2$', ''], ['iii', '$8$', "$A_0 $", '$A_0$', '$0$', '$1$', ''], ['iv ', '$8$', "$A_0 $", '$A_0$', '$1$', '$1$', ''], ['v ', '$8$', "$A_0 $", '$A_1$', '$0$', '$1$', '']] tab_lst += [tab9, tab8] # rank 3,4,5,6,7,8 and 9 idx = 0 Mtype_lst = ['A1', '4A1'] # for breaking up table for degree 2 case for rank in range(3, 9 + 1): tab = [] for dpl in DPLattice.get_cls(rank): col1 = '$' + str(idx) + '$' col2 = '$' + str(dpl.get_degree()) + '$' col3 = '$' + str(dpl.get_marked_Mtype()) + '$' for key in rep_dct: col3 = str(col3).replace(key, rep_dct[key]) col4 = '$' + str(dpl.get_real_type()) + '$' for key in rep_dct: col4 = str(col4).replace(key, rep_dct[key]) col5 = '$' + str(dpl.get_numbers()[4]) + '$' col6 = '$' + str(dpl.get_numbers()[5]) + '$' i_lst = [ str(Div(rw).mat_mul(dpl.M)) for rw in sage_identity_matrix(rank) ] col7 = '' for i in i_lst: col7 += sym_dct[i] if col7 in [ '012', '0123', '01234', '012345', '0123456', '01234567', '012345678' ]: col7 = '' col8 = '' for d in dpl.d_lst: col8 += sym_dct[str(d)] # these subroot systems cannot be realized as weak del Pezzo surfaces if col4 in [ '$7\underline{A_1}$', '$8\underline{A_1}$', '$4\underline{A_1}+\underline{D_4}$' ]: col1 = '$\\times$' # break (sometimes) the table for degree 2 according to Mtype if dpl.get_degree() == 2 and dpl.Mtype in Mtype_lst: nheaders = len( tab) / nrows # each header shifts the row number while len( tab ) % nrows != nrows - 1 - nheaders: # add rows until end of table tab += [7 * ['']] Mtype_lst.remove(dpl.Mtype) # add row tab += [[ col1, col2, col3, col4, col5, col6, '$' + col7 + '||\!' + col8 + '$' ]] idx += 1 tab_lst += [tab] # reformat table # # i d A B E G Ac%Bc hl = '@{~}l@{~~~}l@{~~~}l@{~~}l@{~~}l@{~~}l@{~~}l@{}' hrow = ['', 'd', '$D(A)$', '$D(B)$', '$\#E$', '$\#G$', '$\sigma_A||B$'] etab_lst = [] etab = [hrow] tab_idx = 0 for tab in tab_lst: for row in tab: if len(etab) >= nrows: etab_lst += [etab] etab = [hrow] etab += [row] if len(etab) < nrows and tab_idx <= 3: etab += [ 7 * [''], 7 * [''] ] # add two empty rows to separate tables with different rank else: while len(etab) < nrows: etab += [7 * ['']] # add empty rows to fill up table etab_lst += [etab] etab = [hrow] tab_idx += 1 NSTools.p('etab_lst: ', [len(etab) for etab in etab_lst]) # create tex for main classification table # tex_tab = '' tab_idx = 0 for etab in etab_lst: if tab_idx % 2 == 0: tex_tab += '\\begin{table}\n' tex_tab += '\\setstretch{1.6}\n' tex_tab += '\\centering\\tiny\n' tex_tab += '\\begin{tabular}{@{}l@{\\hspace{1cm}}l@{}}\n' elif tab_idx % 2 == 1: tex_tab += '&\n' tex_tab += '\\begin{tabular}{' + hl + '}\n' for row in etab: col1, col2, col3, col4, col5, col6, col78 = row tex_tab += col1 + ' & ' + col2 + ' & ' + col3 + ' & ' + col4 + ' & ' tex_tab += col5 + ' & ' + col6 + ' & ' + col78 tex_tab += ' \\\\\n' if row == hrow: tex_tab += '\\hline\n' tex_tab += '\\end{tabular}\n' if tab_idx % 2 == 1: tex_tab += '\\end{tabular}\n' tex_tab += '\\end{table}\n\n' tab_idx += 1 if tab_idx % 2 == 1: tex_tab += '&\n' tex_tab += '\\end{tabular}\n\n' # creating tex for commands tex_cmd = '' tex_cmd += '\\newcommand{\\udot}[1]{\\tikz[baseline=(todotted.base)]{\\node[inner sep=1pt,outer sep=0pt] (todotted) {$#1$};\\draw[densely dotted] (todotted.south west) -- (todotted.south east);}}' tex_cmd += '\n' tex_cmd += '\\newcommand{\\udash}[1]{\\tikz[baseline=(todotted.base)]{\\node[inner sep=1pt,outer sep=0pt] (todotted) {$#1$};\\draw[densely dashed] (todotted.south west) -- (todotted.south east);}}' tex_cmd += '\n\n' out = tex_cmd + tex_lgd + tex_tab return out
def get_root_bases_orbit(d_lst, positive=True): ''' Computes the orbit of a root base under the Weyl group. Parameters ---------- d_lst : list<Div> A list of lists of "Div" objects "d" of the same rank or the empty list. positive : bool Returns ------- list<list<Div>> A list of distinct lists of "Div" objects "d" of the same rank. such that d*d=-2 and d*(-3h+e1+...+er)=0 where r=rank-1. If "d_lst" is the empty list, then "[]" is returned. Otherwise we return a list of root bases such that each root basis is obtained as follows from a root "s" such that s*s=-2 and s*(-3h+e1+...+er)=0: [ d + (d*s)d for d in d_lst ] We do this for all possible roots in [s1,s2,s3,...]: [ [ d + (d*s1)d for d in d_lst ], [ d + (d*s2)d for d in d_lst ], ... ] Mathematically, this means that we consider the Weyl group of the root system with Dynkin type determined by the rank of elements in "d_lst". The Dynkin type is either A1, A1+A2, A4, D5, E6, E7 or E8. We return the orbit of the elements in "d_lst" under the action of the Weyl group. If "positive==True" then the roots in the basis are all positive and thus of the form <ij>, <1ijk>, <2ij>, <30i> with i<j<k. For example '15' and '1124' but not '-15' or '-1124'. See "Div.get_label()" for the notation. ''' if d_lst == []: return [[]] rank = d_lst[0].rank() # in cache? key = 'get_root_bases_orbit_' + str(d_lst) + '_' + str(rank) if key in NSTools.get_tool_dct(): return NSTools.get_tool_dct()[key] # obtain list of all positive (-2)-classes m2_lst = get_divs(get_ak(rank), 0, -2, True) # m2_lst += [ m2.int_mul( -1 ) for m2 in m2_lst] NSTools.p('d_lst =', len(d_lst), d_lst, ', m2_lst =', len(m2_lst), m2_lst) # data for ETA computation counter = 0 total = len(m2_lst) ival = 5000 d_lst.sort() d_lst_lst = [d_lst] for cd_lst in d_lst_lst: total = len(m2_lst) * len(d_lst_lst) for m2 in m2_lst: # ETA if counter % ival == 0: start = time.time() counter += 1 if counter % ival == 0: passed_time = time.time() - start NSTools.p('ETA in minutes =', passed_time * (total - counter) / (ival * 60), ', len(d_lst_lst) =', len(d_lst_lst), ', total =', total) # # The action of roots on a root base is by reflection: # cd - 2(cd*m2/m2*m2)m2 # Notice that m2*m2==-2. # od_lst = [cd + m2.int_mul(cd * m2) for cd in cd_lst] # print( 'm2 =', m2, ', od_lst =', od_lst, ', cd_lst =', cd_lst, ', d_lst_lst =', d_lst_lst, ' positive =', positive ) od_lst.sort() if od_lst not in d_lst_lst: d_lst_lst += [od_lst] # select positive roots if positive==True pd_lst_lst = [] for d_lst in d_lst_lst: if positive and '-' in [d.get_label(True)[0] for d in d_lst]: continue # continue with for loop since a negative root in basis pd_lst_lst += [d_lst] # cache output NSTools.get_tool_dct()[key] = pd_lst_lst NSTools.save_tool_dct() NSTools.p('#orbit(' + str(d_lst) + ') =', len(pd_lst_lst)) return pd_lst_lst
def get_dynkin_type(d_lst): ''' Parameters ---------- d_lst : list<Div> A list of lists of "Div" objects "d" of the same rank, such that d*d=-2 and d*(-3h+e1+...+er)=0 where r=rank-1 and rank in [3,...,9]. We assume that "is_root_basis(d_lst)==True": linear independent, self intersection number -2 and pairwise product either 0 or 1. Returns ------- string Returns a string denoting the Dynkin type of a root system with basis "d_lst". Returns 'A0' if "d_lst==[]". Note ---- For example: [<1145>, <1123>, <23>, <45>, <56>, <78>] --> '3A1+A3' where <1145> is shorthand for "Div.new('1145')". Raises ------ ValueError If the Dynkin type of d_lst cannot be recognized. ''' if d_lst == []: return 'A0' # check whether values are cached # construct_dynkin_types = True max_r = d_lst[0].rank() - 1 key = 'get_dynkin_type_' + str(max_r) for r in range(max_r, 8 + 1): if 'get_dynkin_type_' + str(r) in NSTools.get_tool_dct(): key = 'get_dynkin_type_' + str(r) construct_dynkin_types = False # construct list of dynkin types if values are not cached # if construct_dynkin_types: NSTools.p('Constructing list of Dynkin types... max_r =', max_r) ade_lst = [] for comb_lst in sage_Combinations(max_r * ['A', 'D', 'E'], max_r): for perm_lst in sage_Permutations(comb_lst): ade_lst += [perm_lst] # # "ade_lst" contains all combinations of 'A', 'D', 'E' # and looks as follows: # # ade_lst[0] = ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'] # ade_lst[1] = ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'D'] # ade_lst[2] = ['A', 'A', 'A', 'A', 'A', 'A', 'D', 'A'] # ... # ade_lst[?] = ['A', 'D', 'A', 'D', 'A', 'D', 'E', 'A'] # ... # ade_lst[-1]= ['E', 'E', 'E', 'E', 'E', 'E', 'E', 'E'] # type_lst = [] ts_lst = [] for ade in ade_lst: for r in range(1, max_r + 1): for p_lst in sage_Partitions(r + max_r, length=max_r): # obtain type list t_lst = [(ade[i], p_lst[i] - 1) for i in range(max_r) if p_lst[i] != 1] t_lst.sort() # obtain Root system # or continue if invalid Cartan/Dynkin type if ('D', 2) in t_lst or ('D', 3) in t_lst: continue try: rs = sage_RootSystem(t_lst) except ValueError as err: continue # not a valid Cartan type # obtain graph G mat = list(-1 * rs.cartan_matrix()) G = sage_Graph() G.add_vertices(range(len(mat))) for i in range(len(mat)): for j in range(len(mat[0])): if mat[i][j] == 1: G.add_edge(i, j) # obtain string for type # Example: [(A,1),(A,1),(A,1),(A,3)] ---> '3A1+A3' tmp_lst = [t for t in t_lst] ts = '' while len(tmp_lst) > 0: t = tmp_lst[0] c = tmp_lst.count(t) while t in tmp_lst: tmp_lst.remove(t) if ts != '': ts += '+' if c > 1: ts += str(c) ts += t[0] + str(t[1]) # add to type_lst if new if ts not in ts_lst: type_lst += [(G, ts, t_lst)] ts_lst += [ts] NSTools.p('added to list: ', ts, '\t\t...please wait...') NSTools.p('Finished constructing list of Dynkin types.') # cache the constructed "type_lst" NSTools.get_tool_dct()[key] = type_lst NSTools.save_tool_dct() # end if else: type_lst = NSTools.get_tool_dct()[key] G1 = get_graph(d_lst) # loop through all types and check equivalence for (G2, ts, t_lst) in type_lst: if G1.is_isomorphic(G2): return ts raise ValueError('Could not recognize Dynkin type: ', d_lst)