a field-theory motivated approach to computer algebra

This notebook explains various aspects of Cadabra 2 for users of earlier versions. But even if you have never used Cadabra before, this is probably a useful notebook to read through. Under the hood, many things have changed, most notably the fact that you can now freely mix mathematical expressions with proper Python code to manipulate them. But let's get started at the beginning. Just like in the old Cadabra, you enter expressions by using the := assignment operator.
ex := A_{m n} B_{m n};
$$\displaystyle{}A_{m n} B_{m n}$$
Here 'ex' is, as before, the label that will be assigned to this expression. Cadabra's default has always been to display every line that ends in a semi-colon, ';', and that rule has not changed. The difference is how things are implemented now. While 'ex' used to be simply a label, in Cadabra 2 it is a full-fledged Python object. You can see this by e.g. asking for its type:
type(ex);
<class 'cadabra2.Ex'>
Note how we again used the ';' line ending to instruct Cadabra to display the result. If you had left it off, you would get no output (try it!). Note how you have used a bit of Python (the type function call) without telling Cadabra that this was actually Python, not a Cadabra expression. How that works will become clear in a minute. Properties of mathematical symbols can, as before, be entered by using the '::' operator, as the following shows.
A_{m n}::Symmetric; B_{m n}::AntiSymmetric.
$$\displaystyle{}\text{Attached property Symmetric to }A_{m n}.$$
Notice how we suppressed printing of the property assignment on the second line, by using a period instead of a semi-colon. The main notational difference with respect to the old Cadabra comes when running algorithms on expressions. Algorithms have become Python functions, and the somewhat esoteric algorithm calling convention of the old Cadabra (with an ampersand, like in '@canonicalise!(%)' is now replaced by a cleaner looking Pythonesque form:
canonicalise(ex);
$$\displaystyle{}0$$
As you can see, simply call the function and pass in the Python object corresponding to the mathematical expression you want to act on. With Python at your disposal, you can now do things which were impossible or at the least very clumsy with the old @procedure construction. For instance, you can now use loop constructions like the following:
ex := A_{m n} B_{m n}; rule:= A_{m n} -> A_{m n} + C_{m n}; for i in range(3): substitute(ex, rule);
$$\displaystyle{}A_{m n} B_{m n}$$
$$\displaystyle{}A_{m n} \rightarrow A_{m n}+C_{m n}$$
$$\displaystyle{}\left(A_{m n}+C_{m n}\right) B_{m n}$$
$$\displaystyle{}\left(A_{m n}+2C_{m n}\right) B_{m n}$$
$$\displaystyle{}\left(A_{m n}+3C_{m n}\right) B_{m n}$$