# Expansion of covariant derivative

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.

+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
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:
else:
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():
#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():
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.