示例#1
0
    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
示例#2
0
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))
示例#3
0
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)
示例#4
0
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
示例#5
0
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
示例#6
0
    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
示例#7
0
    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