Hi alonli!
The notation you're using makes me think that you've read our article. If so, thank you very much! It is awesome to note that our work benefits other researchers.
Now, I understand that what you're providing in the question is (probably) a simple example of what you really want to do. However, there are many manipulations that are doing nothing (including the indiscriminate use of the algorithm canonicalise, which is slow because it tries many "simplification" algorithms).
I came out with a shorter list of algorithms that work better in your example (you might have to modify it to fit your necessities):
{a#,b#}::Indices.
\delta{#}::KroneckerDelta.
{a#,b#}::Integer(0..3)
\partial{#}::PartialDerivative.
\epsilon{#}::EpsilonTensor(delta=-\delta).
h_{a1 b1}::Symmetric.
toh := {h_{a1 a1} = h, h_{a1}^{a1} = h}.
def LLmanip(ex):
expand_delta(ex)
distribute(ex)
eliminate_kronecker(ex)
substitute(ex, toh)
sort_product(ex)
rename_dummies(ex)
lower_free_indices(ex)
meld(ex)
rename_dummies(ex)
return ex
LL := \delta^{a1 b1 a2 b2 a3 b3 a4 b4} \partial_{a1}{h_{a2 b2}} \partial_{b1}{h_{a3 b3}} h_{a4 b4};
LLmanip(LL);
I want to drive your attention to the single use of the algorithm meld intead of several canonicalise, single sort_product and rename_dummies, and the lack of the sort_sum (perhaps you might still want it there).
I know that it is useful to define functions for general purpose manipulations, but it is important to keep them "optimal", because once your expressions start to get a bit complex, the functions become very time consuming.
Cheers,
Dox.