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_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 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 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_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 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 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