
PROJECT
libraries
header.cnb
timing.cnb
sec3_lanczos_lovelock.cnb
sec4_bianchi_identities.cnb
sec5_variational_principle.cnb
sec6_solution1_schw.cnb
sec6_solution2_frw.cnb
Figure 1: Organisation of notebooks discussed in the present paper.
1import sympy
2import cdb.core.manip as manip
3import cdb.core.component as comp
4import cdb.sympy.solvers as solv
The last three imports are from the Cadabra standard library [11], and provide common operations. The cdb.core.comp
library is useful for component calculations and cdb.sympy.solvers is a simple Cadabra wrapper for the equation
solvers provided in the sympy library.
Although these imports appear to be regular Python packages, they are in fact Cadabra notebooks and can be
found at <cadabra-root-dir>/lib/python3.x/site-packages/cdb. When Cadabra finds an import state-
ment, it will not only do a standard search in sys.path for Python packages, but it will also search for notebook
files, which are automatically converted into Python scripts and imported using the native functionality. Not only
does this make writing packages for Cadabra very natural, but it also makes these packages very easy to read, as
the documentation is written next to the code in L
A
T
E
X cells using the \algorithm command, and test code can be
written under the exported functions in ghost cells which are ignored when imported, similarly to how if __name__
== "__main__" statements are used in Python.
After this, the header.cnb file defines one main function init_properties, which accepts a coordinates pa-
rameter containing the range of coordinates used through the notebooks and a metrics parameter with the names of
the metric tensors required, and uses this to inject appropriate property declarations into the Cadabra kernel. It begins
by declaring the coordinates and indices used in the notebooks:
5def init_properties(*, coordinates, metrics=[$g_{\mu\nu}$], signature=-1):
6"""
7Initialise common property declarations which are shared between the
8two papers
9"""
10 @(coordinates)::Coordinate.
11 index_list := {\mu,\nu,\rho,\sigma,\alpha,\beta,\gamma,\tau,\chi,\psi,\lambda,\lambda#}.
12 @(index_list)::Indices(position=independent, values=@(coordinates)).
13 Integer(index_list, Ex(rf"1..{len(coordinates)}"))
The use of the pull-in syntax @(...) allows us to use Cadabra expressions inside other expressions, similarly to
how curly brackets are used in Python strings to include other objects (e.g. 'This is some {}'.format('text')).
As the ::Property syntax expects a Cadabra expression on the left hand side, not a variable name, we use it here to
declare properties on these expressions which are not hard-coded. As well as assigning the Indices property to our
index list, we also assign the Integer property which makes the number of coordinates countable, allowing functions
like eliminate_kronecker which makes the substitution δµ
µ→Dto work. One final thing to note is the #, which
declares an infinite set of labelled indices (i.e. \lambda1,\lambda2) which is useful to ensure that spare indices are
always available (useful when running code in loops and when doing higher perturbative orders of a computation, for
which it is not always easy to estimate how many dummy indices will be required).
The function then associates the relevant properties to the metrics defined in the metrics parameter:
15 sig = Ex(signature)
4