a field-theory motivated approach to computer algebra

Using Cadabra and Sympy together

This notebook illustrates the interaction of Cadabra with Sympy. In particular, it shows how Cadabra expressions can be manipulated by Sympy if they are index-free (i.e. non-tensors).

Scalar expression handling

While Cadabra's main strength is its handling of tensorial expressions, you can of course write down scalar expressions as well. Here is a simple example:
ex:=(\sin(x)**2 + \cos(x)**2)/x;
\(\displaystyle{}\left({\left(\sin{x}\right)}^{2}+{\left(\cos{x}\right)}^{2}\right) {x}^{-1}\)
((\sin(x))**2 + (\cos(x))**2) (x)**(-1)
Note the use of LaTeX notation for things like trigonometric functions. You can then directly call Sympy algorithms on these Cadabra expressions, for instance to integrate them:
map_sympy(_, "integrate");
Note how, in the spirit of Cadabra's logic that algorithms act in-place, this changes the actual expression: ex now contains the integral of the original expression:
You can do more complicated things as well, like integrals and sums. Here is an example which is first entered in Cadabra form (which is inert), and then fed through Sympy, which then immediately evaluates the expression. Again note the use of standard LaTeX-like constructions to write down integrals.
ex2:= \int{\sin(x) \cos(x)}{x};
\(\displaystyle{}\int \sin{x} \cos{x}\,\,{\rm d}x\)
\int{\sin(x) \cos(x)}{x}
1/2 (\sin(x))**2
Note how map_sympy was used without giving it any Sympy function. This simply makes Sympy evaluate the expression, which in this case means evaluating the integral.
Note that map_sympy is almost always the way in which you want to invoke Sympy. You can, alternatively, feed expressions directly into Sympy functions (by virtue of the fact that Ex objects have a _sympy_() method), but that has the disadvantage that you will not change the original expression (it will not act in-place):
ex3:= \sin(x)\cos(x);
\(\displaystyle{}\sin{x} \cos{x}\)
\sin(x) \cos(x)
ex3._sympy_(); print(type(ex3._sympy_()))
\(\displaystyle{}\sin{\left(x \right)} \cos{\left(x \right)}\)
<class 'sympy.core.mul.Mul'>
sympy.integrate(ex3); print(type(sympy.integrate(ex3)))
\(\displaystyle{}\frac{\sin^{2}{\left(x \right)}}{2}\)
<class 'sympy.core.mul.Mul'>
Observe how the result of these evaluations are non-Cadabra objects (they have not been converted back, the way in which map_sympy does).
The fact that algorithms act in-place may sometimes be a bit unexpected. Here's an example of solving a cubic equation to make this clear:
ex4:= x**3 - x**2 - 4;
map_sympy(_, "solve");
\(\displaystyle{}\left[2, - \frac{1}{2} - \frac{1}{2}\sqrt{7} I, - \frac{1}{2}+\frac{1}{2}\sqrt{7} I\right]\)
{2, - 1/2 - 1/2 \sqrt(7) I, - 1/2 + 1/2 \sqrt(7) I}
\(\displaystyle{}\left[2, - \frac{1}{2} - \frac{1}{2}\sqrt{7} I, - \frac{1}{2}+\frac{1}{2}\sqrt{7} I\right]\)
{2, - 1/2 - 1/2 \sqrt(7) I, - 1/2 + 1/2 \sqrt(7) I}

Tensor expression handling

The real power of mixing Cadabra with Sympy lies in the fact that you can act with Sympy on any Cadabra subexpression which is a pure scalar (i.e. which has no indices). Here is a contrived example which has a sum of tensors, with pre-factors which are scalar expressions which Sympy can simplify.
{r,t}::Coordinate; \partial{#}::PartialDerivative; ex:= (\sin(r)**2 + \cos(r)**2) A_{m} \partial_{r}{r} - A_{m} + \int{r**2}{r} B_{m};
\(\displaystyle{}\text{Attached property Coordinate to }\left[r, t\right].\)
\(\displaystyle{}\text{Attached property PartialDerivative to }\partial{\#}.\)
\(\displaystyle{}\left({\left(\sin{r}\right)}^{2}+{\left(\cos{r}\right)}^{2}\right) A_{m} \partial_{r}{r}-A_{m}+\int {r}^{2}\,\,{\rm d}r B_{m}\)
((\sin(r))**2 + (\cos(r))**2) A_{m} \partial_{r}(r)-A_{m} + \int{(r)**2}{r} B_{m}
map_sympy(_, "simplify");
\(\displaystyle{}\frac{1}{3}{r}^{3} B_{m}\)
1/3 (r)**3 B_{m}
{r,t}::Coordinate; {\mu,\nu}::Indices(values={r,t}); ex:= \partial_{\mu}{ A^{\mu \nu} }; rl:= A^{t t} -> t \sin(r)**2, A^{r r} -> \int{\cos{r}**2}{r};
\(\displaystyle{}\text{Attached property Coordinate to }\left[r, t\right].\)
\(\displaystyle{}\text{Attached property Indices(position=free) to }\left[\mu, \nu\right].\)
\(\displaystyle{}\partial_{\mu}{A^{\mu \nu}}\)
\partial_{\mu}(A^{\mu \nu})
\(\displaystyle{}\left[A^{t t} \rightarrow t {\left(\sin{r}\right)}^{2}, A^{r r} \rightarrow \int {\left(\cos{r}\right)}^{2}\,\,{\rm d}r\right]\)
{A^{t t} -> t (\sin(r))**2, A^{r r} -> \int{(\cos(r))**2}{r}}
evaluate(ex, rl);
\(\displaystyle{}\square{}^{\nu}\left\{\begin{aligned}\square{}^{r}& = {\left(\cos{r}\right)}^{2}\\[-.5ex] \square{}^{t}& = {\left(\sin{r}\right)}^{2}\\[-.5ex] \end{aligned}\right. \)
\components^{\nu}({{r} = (\cos(r))**2, {t} = (\sin(r))**2})

Sympy only with sympy notation throughout

Of course, if you do not like or need Cadabra's input format, then you can happily use Sympy in the way you have always used it, but taking advantage of the display logic of the Cadabra notebook:
from sympy import * x,y=var('x,y') i = Integral(sin(x),(x,0,2));
\(\displaystyle{}\int\limits_{0}^{2} \sin{\left(x \right)}\, dx\)
\(\displaystyle{}1 - \cos{\left(2 \right)}\)
Hope this has triggered your interest; as usual, any feedback to is greatly appreciated.
Copyright © 2001-2023 Kasper Peeters