(mul, verbose=False)
return result
Returns all dummies in the mul sorted in canonical order

    The purpose of the canonical ordering is that dummies can be substituted
    consistently across terms with the result that equivalent terms can be
    simplified.

    It is not possible to determine if two terms are equivalent based solely on
    the dummy order.  However, a consistent substitution guided by the ordered
    dummies should lead to trivially (non-)equivalent terms, thereby revealing
    the equivalence.  This also means that if two terms have identical sequences of
    dummies, the (non-)equivalence should already be apparent.

    Strategy
    --------

    The canoncial order is given by an arbitrary sorting rule.  A sort key
    is determined for each dummy as a tuple that depends on all factors where
    the index is present.  The dummies are thereby sorted according to the
    contraction structure of the term, instead of sorting based solely on the
    dummy symbol itself.

    After all dummies in the term has been assigned a key, we check for identical
    keys, i.e. unorderable dummies.  If any are found, we call a specialized
    method, _determine_ambiguous(), that will determine a unique order based
    on recursive calls to _get_ordered_dummies().

    Key description
    ---------------

    A high level description of the sort key:

        1. Range of the dummy index
        2. Relation to external (non-dummy) indices
        3. Position of the index in the first factor
        4. Position of the index in the second factor

    The sort key is a tuple with the following components:

        1. A single character indicating the range of the dummy (above, below
           or general.)
        2. A list of strings with fully masked string representations of all
           factors where the dummy is present.  By masked, we mean that dummies
           are represented by a symbol to indicate either below fermi, above or
           general.  No other information is displayed about the dummies at
           this point.  The list is sorted stringwise.
        3. An integer number indicating the position of the index, in the first
           factor as sorted in 2.
        4. An integer number indicating the position of the index, in the second
           factor as sorted in 2.

    If a factor is either of type AntiSymmetricTensor or SqOperator, the index
    position in items 3 and 4 is indicated as 'upper' or 'lower' only.
    (Creation operators are considered upper and annihilation operators lower.)

    If the masked factors are identical, the two factors cannot be ordered
    unambiguously in item 2.  In this case, items 3, 4 are left out.  If several
    indices are contracted between the unorderable factors, it will be handled by
    _determine_ambiguous()
Returns all dummies in the mul sorted in canonical order
Returns all dummies in the mul sorted in canonical order
def _get_ordered_dummies(mul, verbose=False):
    """Returns all dummies in the mul sorted in canonical order

    The purpose of the canonical ordering is that dummies can be substituted
    consistently across terms with the result that equivalent terms can be
    simplified.

    It is not possible to determine if two terms are equivalent based solely on
    the dummy order.  However, a consistent substitution guided by the ordered
    dummies should lead to trivially (non-)equivalent terms, thereby revealing
    the equivalence.  This also means that if two terms have identical sequences of
    dummies, the (non-)equivalence should already be apparent.

    Strategy
    --------

    The canoncial order is given by an arbitrary sorting rule.  A sort key
    is determined for each dummy as a tuple that depends on all factors where
    the index is present.  The dummies are thereby sorted according to the
    contraction structure of the term, instead of sorting based solely on the
    dummy symbol itself.

    After all dummies in the term has been assigned a key, we check for identical
    keys, i.e. unorderable dummies.  If any are found, we call a specialized
    method, _determine_ambiguous(), that will determine a unique order based
    on recursive calls to _get_ordered_dummies().

    Key description
    ---------------

    A high level description of the sort key:

        1. Range of the dummy index
        2. Relation to external (non-dummy) indices
        3. Position of the index in the first factor
        4. Position of the index in the second factor

    The sort key is a tuple with the following components:

        1. A single character indicating the range of the dummy (above, below
           or general.)
        2. A list of strings with fully masked string representations of all
           factors where the dummy is present.  By masked, we mean that dummies
           are represented by a symbol to indicate either below fermi, above or
           general.  No other information is displayed about the dummies at
           this point.  The list is sorted stringwise.
        3. An integer number indicating the position of the index, in the first
           factor as sorted in 2.
        4. An integer number indicating the position of the index, in the second
           factor as sorted in 2.

    If a factor is either of type AntiSymmetricTensor or SqOperator, the index
    position in items 3 and 4 is indicated as 'upper' or 'lower' only.
    (Creation operators are considered upper and annihilation operators lower.)

    If the masked factors are identical, the two factors cannot be ordered
    unambiguously in item 2.  In this case, items 3, 4 are left out.  If several
    indices are contracted between the unorderable factors, it will be handled by
    _determine_ambiguous()


    """
    # setup dicts to avoid repeated calculations in key()
    args = Mul.make_args(mul)
    fac_dum = dict([ (fac, fac.atoms(Dummy)) for fac in args] )
    fac_repr = dict([ (fac, __kprint(fac)) for fac in args] )
    all_dums = reduce(set.union, list(fac_dum.values()), set())
    mask = {}
    for d in all_dums:
        if d.assumptions0.get('below_fermi'):
            mask[d] = '0'
        elif d.assumptions0.get('above_fermi'):
            mask[d] = '1'
        else:
            mask[d] = '2'
    dum_repr = dict([ (d, __kprint(d)) for d in all_dums ])

    def _key(d):
        dumstruct = [ fac for fac in fac_dum if d in fac_dum[fac] ]
        other_dums = reduce(
            set.union, [ fac_dum[fac] for fac in dumstruct ], set())
        fac = dumstruct[-1]
        if other_dums is fac_dum[fac]:
            other_dums = fac_dum[fac].copy()
        other_dums.remove(d)
        masked_facs = [ fac_repr[fac] for fac in dumstruct ]
        for d2 in other_dums:
            masked_facs = [ fac.replace(dum_repr[d2], mask[d2])
                    for fac in masked_facs ]
        all_masked = [ fac.replace(dum_repr[d], mask[d])
                       for fac in masked_facs ]
        masked_facs = dict(list(zip(dumstruct, masked_facs)))

        # dummies for which the ordering cannot be determined
        if has_dups(all_masked):
            all_masked.sort()
            return mask[d], tuple(all_masked)  # positions are ambiguous

        # sort factors according to fully masked strings
        keydict = dict(list(zip(dumstruct, all_masked)))
        dumstruct.sort(key=lambda x: keydict[x])
        all_masked.sort()

        pos_val = []
        for fac in dumstruct:
            if isinstance(fac, AntiSymmetricTensor):
                if d in fac.upper:
                    pos_val.append('u')
                if d in fac.lower:
                    pos_val.append('l')
            elif isinstance(fac, Creator):
                pos_val.append('u')
            elif isinstance(fac, Annihilator):
                pos_val.append('l')
            elif isinstance(fac, NO):
                ops = [ op for op in fac if op.has(d) ]
                for op in ops:
                    if isinstance(op, Creator):
                        pos_val.append('u')
                    else:
                        pos_val.append('l')
            else:
                # fallback to position in string representation
                facpos = -1
                while 1:
                    facpos = masked_facs[fac].find(dum_repr[d], facpos + 1)
                    if facpos == -1:
                        break
                    pos_val.append(facpos)
        return (mask[d], tuple(all_masked), pos_val[0], pos_val[-1])
    dumkey = dict(list(zip(all_dums, list(map(_key, all_dums)))))
    result = sorted(all_dums, key=lambda x: dumkey[x])
    if has_dups(iter(dumkey.values())):
        # We have ambiguities
        unordered = defaultdict(set)
        for d, k in dumkey.items():
            unordered[k].add(d)
        for k in [ k for k in unordered if len(unordered[k]) < 2 ]:
            del unordered[k]

        unordered = [ unordered[k] for k in sorted(unordered) ]
        result = _determine_ambiguous(mul, result, unordered)
    return result
def _get_ordered_dummies ( mul , verbose = False ) : # setup dicts to avoid repeated calculations in key() args = Mul . make_args ( mul ) fac_dum = dict ( [ ( fac , fac . atoms ( Dummy ) ) for fac in args ] ) fac_repr = dict ( [ ( fac , __kprint ( fac ) ) for fac in args ] ) all_dums = reduce ( set . union , list ( fac_dum . values ( ) ) , set ( ) ) mask = { } for d in all_dums : if d . assumptions0 . get ( 'below_fermi' ) : mask [ d ] = '0' elif d . assumptions0 . get ( 'above_fermi' ) : mask [ d ] = '1' else : mask [ d ] = '2' dum_repr = dict ( [ ( d , __kprint ( d ) ) for d in all_dums ] ) def _key ( d ) : dumstruct = [ fac for fac in fac_dum if d in fac_dum [ fac ] ] other_dums = reduce ( set . union , [ fac_dum [ fac ] for fac in dumstruct ] , set ( ) ) fac = dumstruct [ - 1 ] if other_dums is fac_dum [ fac ] : other_dums = fac_dum [ fac ] . copy ( ) other_dums . remove ( d ) masked_facs = [ fac_repr [ fac ] for fac in dumstruct ] for d2 in other_dums : masked_facs = [ fac . replace ( dum_repr [ d2 ] , mask [ d2 ] ) for fac in masked_facs ] all_masked = [ fac . replace ( dum_repr [ d ] , mask [ d ] ) for fac in masked_facs ] masked_facs = dict ( list ( zip ( dumstruct , masked_facs ) ) ) # dummies for which the ordering cannot be determined if has_dups ( all_masked ) : all_masked . sort ( ) return mask [ d ] , tuple ( all_masked ) # positions are ambiguous # sort factors according to fully masked strings keydict = dict ( list ( zip ( dumstruct , all_masked ) ) ) dumstruct . sort ( key = lambda x : keydict [ x ] ) all_masked . sort ( ) pos_val = [ ] for fac in dumstruct : if isinstance ( fac , AntiSymmetricTensor ) : if d in fac . upper : pos_val . append ( 'u' ) if d in fac . lower : pos_val . append ( 'l' ) elif isinstance ( fac , Creator ) : pos_val . append ( 'u' ) elif isinstance ( fac , Annihilator ) : pos_val . append ( 'l' ) elif isinstance ( fac , NO ) : ops = [ op for op in fac if op . has ( d ) ] for op in ops : if isinstance ( op , Creator ) : pos_val . append ( 'u' ) else : pos_val . append ( 'l' ) else : # fallback to position in string representation facpos = - 1 while 1 : facpos = masked_facs [ fac ] . find ( dum_repr [ d ] , facpos + 1 ) if facpos == - 1 : break pos_val . append ( facpos ) return ( mask [ d ] , tuple ( all_masked ) , pos_val [ 0 ] , pos_val [ - 1 ] ) dumkey = dict ( list ( zip ( all_dums , list ( map ( _key , all_dums ) ) ) ) ) result = sorted ( all_dums , key = lambda x : dumkey [ x ] ) if has_dups ( iter ( dumkey . values ( ) ) ) : # We have ambiguities unordered = defaultdict ( set ) for d , k in dumkey . items ( ) : unordered [ k ] . add ( d ) for k in [ k for k in unordered if len ( unordered [ k ] ) < 2 ] : del unordered [ k ] unordered = [ unordered [ k ] for k in sorted ( unordered ) ] result = _determine_ambiguous ( mul , result , unordered ) return result