a field-theory motivated approach to computer algebra

Cadabra quick start

This is a quick start introduction to Cadabra. For more in-depth discussion of its features, consult the various tutorials. After installation, Cadabra can be started with cadabra2-gtk from the command line, or by picking it from the 'Education' menu.

Bosonic basics

Before discussing actual calculations, let us start with a few words concerning notation. This discussion can be short because mathematical expressions are entered essentially as one would enter them in TeX (with a few restrictions to avoid ambiguities, which we will discuss as we go along). In order to manipulate expressions, Cadabra often needs to know a bit more about properties of tensors or other symbols. Such properties are entered using the '::' symbol. A simple example is the declaration of index sets, useful for automatic dummy index relabelling. An example will clarify this,
{ a, b, c, d }::Indices; ex:= A_{a b} B_{b c};
\(\displaystyle{}\text{Attached property Indices(position=free) to }\left[a, b, c, d\right].\)
\(\displaystyle{}A_{a b} B_{b c}\)
A_{a b} B_{b c}
substitute(_, $B_{a b} -> C_{a b c} D_{c}$ );
\(\displaystyle{}A_{a b} C_{b c d} D_{d}\)
A_{a b} C_{b c d} D_{d}
The automatic index relabelling which has taken place in this substitute command was clearly only possible because of the property declaration in the first line. Note how the substitute command has also figured out that B_{a b} on the left-hand side is equivalent to B_{b c}, without any explicit wildcards or patterns. We will see more of this type of field-theory motivated logic throughout the paper.
Indices can be simple letters, as in the example above, but it is also perfectly possible to put accents on them. This can be useful for e.g. notations involving chiral spinors. The following example illustrates the use of accents on indices.
\dot{#}::Accent; A_{\dot{a} \dot{b}}::AntiSymmetric; ex:= A_{\dot{b} \dot{a}};
\(\displaystyle{}-A_{\dot{a} \dot{b}}\)
Here we also see a second usage of property declarations: the construction in the first line declares that the $A_{\dot{a} \dot{b}}$ tensor is antisymmetric in its indices. The canonicalise command subsequently writes the input in a canonical form, which in this trivial example simply means that the indices gets sorted in alphabetical order. Continuing the example above, one can also use subscripts or superscripts on indices, as in the example below.
{ a_{1}, a_{2}, a_{3}, a_{4} }::Indices(vector); ex:= V_{a_{1}} W_{a_{1}}; substitute(_, $V_{a_{2}} -> M_{a_{2} a_{1}} N_{a_{1}}$ );
\(\displaystyle{}\text{Attached property Indices(position=free) to }\left\{a_{1}, \mmlToken{mo}[linebreak="goodbreak"]{} a_{2}, \mmlToken{mo}[linebreak="goodbreak"]{} a_{3}, \mmlToken{mo}[linebreak="goodbreak"]{} a_{4}\right\}.\)
\(\displaystyle{}V_{a_{1}} W_{a_{1}}\)
\(\displaystyle{}M_{a_{1} a} N_{a} W_{a_{1}}\)
The input format is a mixture of Cadabra's own LaTeX-like language for the description of mathematical expressions, and Python. The underscore symbol '_' always refers to the last-used expression.
A guiding principle in Cadabra is that nothing ever has to be declared unless this is absolutely needed. This is in contrast to many other systems, where for instance one has to declare manifolds and index sets and so on before one can even enter a tensor expression. This makes code hard to read, but more importantly, such additional declarations are hard to remember. As an example of how Cadabra works, one can e.g. input tensor expressions and perform substitution on them, without ever declaring the symbols used for indices. Only when the program needs to generate new dummy indices does one need to declare index sets, so that dummy indices can be taken out of the right set. The general guideline is that "one only needs to tell the program about the meaning of symbols when this is actually required to do the manipulation correctly".
In order to declare symmetries of tensors, it is possible to use simple shorthands like the AntiSymmetric in the example above. More generally, symmetries can be declared using a generic Young tableau notation. An object with the symmetries of a Riemann tensor, for example, can be declared as in the following example.
R_{a b c d}::TableauSymmetry(shape={2,2}, indices={0,2,1,3}). ex:=R_{a b c d} R_{d c a b}; canonicalise(_);
\(\displaystyle{}R_{a b c d} R_{d c a b}\)
\(\displaystyle{}-R_{a b c d} R_{a b c d}\)
The first line indicates that the tensor has the symmetries of the $2\times 2$ Young tableau. The canonicalise command writes the input in canonical form with respect to mono-term symmetries (anti-symmetry in the two index pairs and symmetry under pair exchange). The Ricci symmetry is also encoded in the Young tableau and will be discussed later. Many tensor symmetries have shorthand notations, so one often does not have spell out the tableau (e.g. RiemannTensor or SatisfiesBianchi).

Derivatives and dependencies

There are relatively few surprises when it comes to derivatives. It is possible to write derivatives with respect to coordinates, i.e. write $\partial_x$ where $x$ is a coordinate, but it is also possible to use indices, as in $\partial_i$ with $i$ being a vector index. It is also possible to make objects implicitly dependent on a derivative operator, so that one can write $\partial A$ without an explicit specification of the coordinate which is involved here.
In order to make this possible, however, derivative objects have to be declared just like indices, otherwise the system does not know which symbol ($\partial$, $D$, $d$, $\nabla$, ...) one wants to use for them. Implicit dependencies of objects on coordinates associated to derivatives is indicated through a Depends property. Here is an example to illustrate all this:
{m,n,p,q,r}::Indices(position=free); \nabla{#}::Derivative; \partial{#}::PartialDerivative; A_{m n}::AntiSymmetric; V_{m}::Depends(\nabla{#}); ex:= \partial_{m p}( A_{q r} V_{n} ) A^{p m};
\(\displaystyle{}\text{Attached property Indices(position=free) to }\left\{m, \mmlToken{mo}[linebreak="goodbreak"]{} n, \mmlToken{mo}[linebreak="goodbreak"]{} p, \mmlToken{mo}[linebreak="goodbreak"]{} q, \mmlToken{mo}[linebreak="goodbreak"]{} r\right\}.\)
\(\displaystyle{}\text{Attached property Derivative to }\nabla{\#}.\)
\(\displaystyle{}\text{Attached property PartialDerivative to }\partial{\#}.\)
\(\displaystyle{}\text{Attached property AntiSymmetric to }A_{m n}.\)
\(\displaystyle{}\text{Attached property Depends to }V_{m}.\)
\(\displaystyle{}\partial_{m p}\left(A_{q r} V_{n}\right) A^{p m}\)
ex:=\nabla_{m p}( A_{q r} V_{n} ) A^{p m}; canonicalise(_);
\(\displaystyle{}\nabla_{m p}\left(A_{q r} V_{n}\right) A^{p m}\)
\(\displaystyle{}-\nabla^{m p}\left(A_{q r} V_{n}\right) A_{m p}\)
\(\displaystyle{}-A_{q r} \nabla^{m p}{V_{n}} A_{m p}\)
Note how the symmetry of a double partial derivative has automatically been taken into account (it is part of the PartialDerivative property). This is called "property inheritance".

More to follow ...

Copyright © 2001-2023 Kasper Peeters