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

I like the idea of Cadabra, but I really have trouble getting it to do even simple things, like contracting the indices of a tensor. Here's what I'm trying to do:

{\tau,\theta,\varphi,\kappa,\rho,\lambda,\psi,\omega}::Indices(position=fixed).
\delta{#}::KroneckerDelta.
Z:= u^{\tau} u_{\theta} \delta_{\tau}^{\theta}_{\lambda}^{\rho}_{\psi}^{\varphi}_{\omega}^{\kappa} k^{\omega} k_{\kappa} \varepsilon_{\varphi}^{\psi} \varepsilon_{\eta}^{\lambda};
@breakgendelta!(%);
@distribute!(%);
@eliminate_kr!(%);
@substitute!(%)(u^{\alpha}\varepsilon_{\alpha}^{\beta}->0, \varepsilon_{\alpha}^{\beta}u_{\beta}->0);
@sumsort!(%);
@collect_terms!(%);

This gives me a nice expression with two free indices. Now suppose I want to contract those indices.

@(Z_{\alpha}^{\alpha})

doesn't work. I've tried various substitution rules that don't work, and I've tried @all_contractions which it tells me is not applicable, even though there's only one contraction to do! I've read through the reference guide to no avail. I'm sure it's something simple but not obvious and I've spent the whole day trying to figure out how to contract indices with no success.

Thanks in advance!

in General questions by

1 Answer

+1 vote

Note: this question uses v1.x notation, see below for a v2.x version.

Make a new expression which consists of the old one multiplied with the appropriate Kronecker delta, e.g.

Zc:= @(Z) \delta^{\eta}_{\rho}:
@distribute!(%);
@eliminate_kr!(%);

In general, the thing on the left hand side of the := symbol is a name, not a mathematical expression.

The above is in v1.x notation. I'd strongly urge you to upgrade to the v2.x series. In that case, your notebook becomes

{\tau,\theta,\varphi,\kappa,\rho,\lambda,\psi,\omega}::Indices(position=fixed).
\delta{#}::KroneckerDelta.
Z:= u^{\tau} u_{\theta}\delta_{\tau}^{\theta}_{\lambda}^{\rho}_{\psi}^{\varphi}_{\omega}^{\kappa} 
k^{\omega} k_{\kappa} \varepsilon_{\varphi}^{\psi} \varepsilon_{\eta}^{\lambda};

(which is unchanged) and then the somewhat different

expand_delta(_)
distribute(_)
eliminate_kronecker(_)
substitute(_, $u^{\alpha}\varepsilon_{\alpha}^{\beta}->0, \varepsilon_{\alpha}^{\beta}u_{\beta}->0$);

The contraction you asked about is then

Zc:= @(Z) \delta^{\eta}_{\rho}:
distribute(_)
eliminate_kronecker(_);

Hope this helps.

by (76.4k points)

Thanks Kasper, that works for finding $tr(Z)$, but it doesn't seem to extend to finding, for example, $tr(Z.Z)$. For example if I try

@(Z) \delta_{\eta}^{i} \delta_{j}^{\rho} @(Z) \delta_{i}^{\tau} \delta_{\omega}^{\rho}

it somehow knows that I want to contract the first two $\delta$ with the first @(Z), but the last two $\delta$ will contract with the first two $\delta$ but not the second @(Z), so in the end I end up with the 4-index object $Z \otimes Z$. I guess a more general question is: how does it know that

@(Z) \delta_{\eta}^{\rho}

should contract on both indices rather than contracting on just one? Or that it isn't a tensor product $Z\otimes\delta$?

I also tried

Z2:=@(Z) @(Z):
@(Z2) \delta_{\mu}^{\nu} \delta_{\sigma}^{\tau}

and this also doesn't seem to work.

Actually, I seem to get erroneous index multiplicities in Z2.

PS: I'm using version 1 because I can't get version 2 working. I had some installation trouble back in April (I posted a question about it back then) and now it crashes on everything I do. It seems to crash on every line terminated by a ;

To resolve the v2 issue please follow up on that original thread and let me know what you did after my reply there.

For your current issue, you are thinking about things the wrong way. I'll show you how I would do these kind of problems, then maybe it clicks.

Your problem is about a tensor $Z$ with components $Z^{\rho}{}_{\eta}$ which is given by some particular expression in terms of other tensors. You then want to build another expression, which involves products of your $Z$ tensor with itself (and possibly others). You then want to write out the $Z$s in that new expression in terms of the other, more basic tensors.

So the starting point should be a Cadabra expression, let's call it Zrule, which says how the components of $Z$ are related to the other tensor components. Something like

{\tau,\eta,\rho,\lambda}::Indices(position=fixed).
Zrule:= Z^{\rho}_{\eta} = u^{\rho} u_{\eta};

(I have used a vastly simpler expression than in your problem just to make this answer easier to read; the logic remains the same though). You now have an object Zrule, which tells Cadabra how components of $Z$ can be converted to components of $u$. Important: the Zrule itself is not a tensor, it is the name of an expression which involves tensor components. Call it banana if you like, to make this more clear.

Now you make an expression which is built from $Z$, e.g. the ${\rm tr}(Z.Z)$ you mentioned:

ex:= Z^{\rho}_{\eta} Z^{\eta}_{\rho};

Cadabra will display this as $Z^{\rho}{}_{\eta} Z^{\eta}{}_{\rho}$. Not in terms of $u$, because you did not ask for that yet. You can get the expression in terms of $u$ by substituting the $Z$ components using the Zrule,

substitute(_, Zrule);          # in cadabra v2
@substitute!(%){ @(Zrule) }    # in cadabra v1

This will produce $u^\rho u_\eta u^\eta u_\rho$. If you ask to display ex again (by typing ex;) you will see that the ex object has now been replaced by the expression in terms of $u$ above.

Does that make it more clear? Main lesson: do not think in terms of the left-hand sides of := lines as being tensors themselves. The left-hand sides of := lines are names of expressions. Names do not carry indices. Call them after your favourite fruits or pets animals if you get confused.

Hope this helps.

One more thing. I understand where you are coming from: in almost all other computer algebra systems, things work more or less like what you wanted to do. However, that method is a lot less flexible, because (if Cadabra would follow it) it would always automatically convert $Z^{\rho}{}_{\eta}$ to its expression in terms of $u$ components. Cadabra's logic is that the user should remain in control about when such substitutions are made.

If you always want to automatically substitute, you can add the substitution rules that you want to be automatic to the post_process function (but only in v2).

...