Cadabra
a field-theory motivated approach to computer algebra

Object properties and declaration

Generic properties

Symbols in Cadabra have no a-priori "meaning". If you write \Gamma, the program will not know that it is supposed to be, for instance, a Clifford algebra generator. You will have to declare the properties of symbols, i.e. you have to tell Cadabra explicitly that if you write \Gamma, you actually mean a Clifford algebra generator. This indirect way of attaching a meaning to a symbol has the advantage that you can use whatever notation you like; if you prefer to write \gamma, or perhaps even \rho if your paper uses that, then this is perfectly possible (object properties are a bit like "attributes" in Mathematica or "domains" in Axiom and MuPAD). Properties are all written using capitals to separate words, as in AntiSymmetric. This makes it easier to distinguish them from commands. Properties of objects are declared by using the "::" characters. This can be done "at first use", i.e. by just adding the property to the object when it first appears in the input. As an example, one can write
F_{m n p}::AntiSymmetric;
\(\displaystyle{}\text{Attached property AntiSymmetric to }F_{m n p}.\)
This declares the object to be anti-symmetric in its indices. The property information is stored separately, so that further appearances of the "F_{m n p}" object will automatically share this property. A list of all properties is available from the manual pages on the web site. Note that properties are attached to patterns, Therefore, you can have
R_{m n}::Symmetric; R_{m n p q}::RiemannTensor;
\(\displaystyle{}\text{Attached property Symmetric to }R_{m n}.\)
\(\displaystyle{}\text{Attached property TableauSymmetry to }R_{m n p q}.\)
at the same time. The program will not warn you if you use incompatible properties, so if you make a declaration like above and then later on do
R_{m n}::AntiSymmetric;
\(\displaystyle{}\text{Attached property AntiSymmetric to }R_{m n}.\)
this may lead to undefined results.
The fact that objects are attached to patterns also means that you can use something like wildcards. In the following declaration,
{m#, n#}::Indices(vector);
\(\displaystyle{}\text{Attached property Indices(position=free) to }\left[m\#, n\#\right].\)
the entire infinite set of objects $m1, m2, m3, ...$ and $n1, n2, n3, ...$ are declared to be in the dummy index set "vector" (this way of declaring ranges of objects is similar to the "autodeclare" declaration method of FORM).
Properties can be assigned to an entire list of symbols with one command, namely by attaching the property to the list. For example,
{n, m, p, q}::Integer(1..d);
\(\displaystyle{}\text{Attached property Integer to }\left[n, m, p, q\right].\)
This associates the property "Integer" to each and every symbol in the list. However, there is also a concept of "list properties", which are properties which are associated to the list as a whole. Examples of list properties are "AntiCommuting" or "Indices".
Objects can have more than one property attached to them, and one should therefore not confuse properties with the "type" of the object. Consider for instance
x::Coordinate; W_{m n p q}::WeylTensor; W_{m n p q}::Depends(x);
\(\displaystyle{}\text{Attached property Coordinate to }x.\)
\(\displaystyle{}\text{Attached property TableauSymmetryWeylTensor to }W_{m n p q}.\)
\(\displaystyle{}\text{Attached property Depends to }W_{m n p q}.\)
This attaches two completely independent properties to the pattern $W_{m n p q}$.
In the examples above, several properties had arguments (e.g. "vector" or "1..d"). The general form of these arguments is a set of key-value pairs, as in
T_{m n p q}::TableauSymmetry(shape={2,1}, indices={0,2,1});
\(\displaystyle{}\text{Attached property TableauSymmetry to }T_{m n p q}.\)
In the simple cases discussed so far, the key and the equal sign was suppressed. This is allowed because one of the keys plays the role of the default key. Therefore, the following two are equivalent,
{ m, n }::Integer(range=0..d); { m, n }::Integer(0..d);
\(\displaystyle{}\text{Attached property Integer to }\left[m, n\right].\)
\(\displaystyle{}\text{Attached property Integer to }\left[m, n\right].\)
See the detailed documentation of the individual properties for allowed keys and the one which is taken as the default.
Finally, there is a concept of "inherited properties". Consider e.g. a sum of spinors, declared as
{\psi_1, \psi_2, \psi_3}::Spinor; ex:= \psi_1 + \psi_2 + \psi_3;
\(\displaystyle{}\text{Attached property Spinor to }\left[\psi_{1}, \psi_{2}, \psi_{3}\right].\)
\(\displaystyle{}\psi_{1}+\psi_{2}+\psi_{3}\)
\psi_{1} + \psi_{2} + \psi_{3}
Here the sum has inherited the property "Spinor", even though it does not have normal or intrinsic property of this type. Properties can also inherit from each other, e.g.
\Gamma_{#}::GammaMatrix. ex:=\Gamma_{p o i u y};
\(\displaystyle{}\Gamma_{p o i u y}\)
\Gamma_{p o i u y}
canonicalise(_);
\(\displaystyle{}-\Gamma_{i o p u y}\)
-\Gamma_{i o p u y}
The GammaMatrix property inherits from AntiSymmetric property, and therefore the \Gamma object is automatically anti-symmetric in its indices. indices.

List properties and symbol groups

Some properties are not naturally associated to a single symbol or object, but have to do with collections of them. A simple example of such a property is AntiCommuting. Although it sometimes makes sense to say that "$\psi_m$ is anticommuting" (meaning that $\psi_m \psi_n = - \psi_n \psi_m$), it happens just as often that you want to say "$\psi$ and $\chi$ anticommute" (meaning that $\psi\chi = - \chi\psi$). The latter property is clearly relating two different objects. Another example is dummy indices. While it may make sense to say that "$m$ is a dummy index", this does not allow the program to substitute $m$ with another index when a clash of dummy index names occurs (e.g. upon substitution of one expression into another). More useful is to say that "$m$, $n$, and $p$ are dummy indices of the same type", so that the program can relabel a pair of $m$'s into a pair of $p$'s when necessary. In Cadabra such properties are called "list properties". You can associate a list property to a list of symbols by simply writing, e.g. for the first example above,
{ \psi, \chi }::AntiCommuting;
\(\displaystyle{}\text{Attached property AntiCommuting to }\left[\psi, \chi\right].\)
Note that you can also attach normal properties to multiple symbols in one go using this notation. The program will figure out automatically whether you want to associate a normal property or a list property to the symbols in the list. Lists are ordered, although the ordering does not necessarily mean anything for all list properties (it is relevant for e.g. SortOrder but irrelevant for e.g. AntiCommuting).

Querying and transferring properties

For many built-in algorithms, assigning properties to the objects which appear in your expressions will be enough to make them work. However, sometimes you may want to explicitly query whether a particular symbol has a particular property. The following example shows how this works.
A_{m n}::Symmetric; if Symmetric.get($A_{m n}$): print("A_{m n} is symmetric.") if AntiSymmetric.get($A_{m n}$): print("A_{m n} is anti-symmetric.")
\(\displaystyle{}\text{Property Symmetric attached to }A_{m n}.\)
A_{m n} is symmetric.
The object returned by Property.get(...) is either None or the property which you asked about. It is possible to do something with that property, e.g. attach it to another symbol. In the example below, we start off with one tensor with a symmetry, and then attach it to another symbol.
A_{m n p}::TableauSymmetry(shape={1,1}, indices={1,2}); p = TableauSymmetry.get($A_{m n p}$) p.attach($B_{m n p}$) ex:= B_{m n p} - B_{m p n}; canonicalise(_);
\(\displaystyle{}\text{Property TableauSymmetry attached to }A_{m n p}.\)
\(\displaystyle{}B_{m n p}-B_{m p n}\)
B_{m n p}-B_{m p n}
\(\displaystyle{}2B_{m n p}\)
2B_{m n p}
Copyright © 2001-2024 Kasper Peeters
Questions? info@cadabra.science