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 writeF_{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 haveR_{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 isAntiCommuting
. 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}