Welcome to Cadabra Q&A, where you can ask questions and receive answers from other members of the community.
0 votes

At the end of the page Programming in Cadabra, there is a function expand_nabla, which expands the covariant derivative in terms of partial derivative and contractions with the connection.

In that implementation the dummy index contracting the connection with the tensor is called p, i.e. is fixed. Since the dummy index is fixed, consecutive application of the covariant derivative cannot be expanded. For example, if we consider the code below,

foo := \nabla_{a}{ \nabla_{b}{ h^{c}_{d} } };
expand_nabla(_);

an error is raised since the expansion of the inner covariant derivative introduces the index p, and then the expansion of the second covariant derivative attempts to introduce another p index (already in use).

Question

How could the definition of the expand_nabla function be generalised to modify automatically the name of the dummy index?

Moreover, assuming that we have a set of symbols with the property Indices assigned, Is it possible to preserve the type of index? i.e. to introduce a new dummy index using a symbol of the list.

asked in General questions by (7.2k points)

1 Answer

+1 vote

I faced the same problem in my time, so I ended up writing a function like this.

def select_index(used_indices):
    indeces = r'w v u s r q o n m l k j i h g f e d c b a'.split() 
    for uind in indeces:
        found = False
        for qind in used_indices:
            if qind == uind:
                found = True
                break   
        if not found:
            used_indices.add(uind)
            index = uind
            break   
    return Ex(index), used_indices

def one_nabla(ex, used_indices): 
    t3, used_indices = select_index(used_indices)
    free = dict()
    free['sub'] = set()
    free['up'] = set()
    for nabla in ex[r'\nabla']:
        nabla.name=r'\partial' 
        dindex = nabla.indices().__next__() 
        for arg in nabla.args():             
            ret:=0;
            for index in arg.free_indices(): 
                if index.parent_rel==sub:
                    free['sub'].add(str(index.ex()))
                else:
                    free['up'].add(str(index.ex()))
            for key in free.keys(): 
                for index in free[key]:
                    ind = Ex(index)
                    if key == 'sub': 
                        t1:= -\Gamma^{@[t3]}_{@(dindex) @[ind]};
                    else:
                        t1:=  \Gamma^{@[ind]}_{@(dindex) @[t3]};
                    t2:= @[arg];
                    for term_index in arg.free_indices(): 
                        if str(term_index.ex()) == index:
                            if term_index.parent_rel==sub:
                                t2[term_index]:= _{@[t3]};
                            else:
                                t2[term_index]:= ^{@[t3]};
                    ret += Ex(str(nabla.multiplier)) * t1 * t2 
            nabla += ret #
            break
        break
    return ex, used_indices

def nabla_calculation(ex, used_indices, count): 
    ex = ex.ex()
    for element in ex.top().terms(): 
        new:=1;
        local_count = 0
        for nabla in element[r'\nabla']: 
            local_count += 1
        if local_count == 0: 
            continue
        elif local_count == 1: 
            new, used_indices = (one_nabla(element, used_indices))
            element.replace(new.ex())
            count -= 1
        else: 
            if element.ex().top().name == r'\prod': 
                i = 0
                while i < 2:
                    for mult in element.ex().top().children():
                        local_count2 = 0
                        for nabla in mult[r'\nabla']:
                            local_count2 += 1
                        if local_count2 == 0:
                            new *= mult.ex()
                        elif local_count2 == 1: 
                            for nabla in mult[r'\nabla']:
                                nabla1, used_indices = one_nabla(nabla, used_indices)
                                nabla.replace(nabla1.ex())
                                new *= nabla.ex()
                        else:  
                            mult1, used_indices = nabla_calculation(mult, used_indices, local_count)
                            new *= mult1
                        i+=1
                new *= Ex(str(element.multiplier))
                element.replace(new)                
            else:
                for nabla in element[r'\nabla']:
                    for arg1 in nabla.args():
                        arg2, used_indices = nabla_calculation(arg1, used_indices, count - 1)
                        index = nabla.indices().__next__() 
                        t := \nabla_{@(index)}{@[arg2]};
                    new = t
                    nabla1, used_indices = one_nabla(new, used_indices)
                    new = Ex(str(nabla.multiplier)) * nabla1
                    nabla.replace(new)
    return ex, used_indices

def expand_nabla(ex): 
    if ex.top().name == '\equals':
        for child in ex.top().children():
            ret:=0;
            for element in child.ex().top().terms():
                count = 0
                used_indices = set()
                for nabla in element.ex()[r'\nabla']:
                    count += 1
                if count == 0:
                    ret += element.ex()
        #new_ex += element.ex()
                else:
                    for n in element.ex():
                        for index in n.indices():
                            used_indices.add(str(index))
        #for nabla in element[r'\nabla']:
                    element1, used_indices = nabla_calculation(element, used_indices, count)
                    ret +=element1
            child.replace(ret)
    else:
        for element in ex.top().terms():
                count = 0
                used_indices = set()
                for nabla in element.ex()[r'\nabla']:
                    count += 1
                if count == 0:
                    pass
                else:
                    for n in element.ex():
                        for index in n.indices():
                            used_indices.add(str(index))
                element1, used_indices = nabla_calculation(element, used_indices, count)
                element.replace(element1)
    return ex

To use it, you need to call the main function expand_nabla(ex). It is suitable for large expressions, does not duplicate already occupied indexes, and also knows how to work with equalities.

answered ago by (660 points)
...