xAct Tutorial

José M. Martín-García, Wolfram

ICERM 2020, September 15

Plan of the talk

1. Introduction to xAct

2. Abstract computations with xTensor

3. Component computations with xCoba

4. Perturbation theory with xPert

5. Riemann identities witn Invar

6. Other xAct packages

7. Conclusions and future plans

1. Introduction to xAct

1.1. History

The project started in early 2002, during my postdoc at the University of Southampton.

Perturbation theory and the search for formulations of the Einstein equations were the drivers to keep improving the system, by then called exTensor.

R. Prix taught me about GPL, and suggested to change to xTensor.

P. Matteucci suggested to use some recent articles (July 2001, by R. Portugal) using permutation group theory to handle indices.

The amount of group theory I had to code was so large that the project split into two packages: xPerm for permutation group theory, xTensor for tensors. The name xAct was introduced to refer to the whole project.

First public version of xAct was released on March 4, 2004, with two packages.

A. García-Parrado and G. Faye were two early users, and helped much shaping the system.

Many additions during 2004 - 2010. Slower pace of evolution since then.

Now there are 20 packages in xAct.

1.2. Resources

The xAct webpage: [(HyperlinkTemplate[www.xact.es, http://www.xact.esNone, http://www.xact.es, HyperlinkActionRecycled, HyperlinkActive, , ])]

The xAct-contrib github: [(HyperlinkTemplate[github.com/xAct-contrib, https://github.com/xAct-contribNone, https://github.com/xAct-contrib, HyperlinkActionRecycled, HyperlinkActive, , ])], [(HyperlinkTemplate[contrib.xact.es, http://contrib.xact.es/None, http://contrib.xact.es/, HyperlinkActionRecycled, HyperlinkActive, , ])]

The xAct forum: [(HyperlinkTemplate[groups.google.com/g/xact, https://groups.google.com/g/xactNone, https://groups.google.com/g/xact, HyperlinkActionRecycled, HyperlinkActive, , ])]

There are also questions in [(HyperlinkTemplate[mathematica.stackexchange.com, https://mathematica.stackexchange.com/search?q=xActNone, https://mathematica.stackexchange.com/search?q=xAct, HyperlinkActionRecycled, HyperlinkActive, , ])]

1.3. The structure of xAct

xAct is a collection of packages, organized hierarchically.

When you load a package, all dependencies are loaded too.

Core packages

xCore: tools for the rest of the system. JMMG. 2007.

xPerm: permutation group theory. JMMG. 2003.

xTensor: abstract tensor computations. JMMG. 2002.

xCoba: tensor computations by components. D. Yllanes, JMMG. 2005.

Application packages

xPert : metric perturbation theory. D. Brizuela, JMMG, G. Mena-Marugán. 2006.

Harmonics: tensor spherical harmonics. D. Brizuela, JMMG, G. Mena-Marugán. 2006.

Invar: invariants of the Riemann tensor. JMMG, R. Portugal, D. Yllanes. 2006.

Spinors: spinor calculus. A. García-Parrado, JMMG. 2007.

SymManipulator: symmetrized tensor expressions. T. Bäckdahl. 2011.

xPand: cosmological perturbations. C. Pitrou, X. Roy, O. Umeh. 2012.

xTras: field theory. T. Nutma. 2012.

xTerior: exterior calculus. A. García-Parrado, L. C. Stein. 2013.

SpinFrames: NP and GHP form of spinor equations. T. Bäckdahl, S. Aksteiner. 2015.

xIST/COPPER: general scalar-tensor theories and perturbations. J. Noller. 2016.

EFTofPNG: effective field theory of post-Newtonian gravity. M. Levi, J. Steinhoff. 2017.

bimEX: 3+1 bimetric relativity. F. Torsello. 2019.

FieldsX: fermions, gauge fields and BRST cohomology. M. B. Fröb. 2020.

Input/Output packages

TexAct: Tex formatting of tensor expressions. T. Bäckdahl, JMMG, B. Wardell. 2008.

xPrint: graphical interface to xAct. Alessandro Stecchina. 2009.

Exceptional packages

AVF: algebra-valued forms. H. D. Wahlquist . 2003. In 2011 F. B. Estabrook sent it to me.

1.4. Abstract vs. component computations

Abstract computations

Deal with tensor expressions and identities that are independent of choices of frames and/or coordinates.

Performed using Penrose’s abstract indices, nicely described in Wald’s General Relativity book.

xAct follows Wald’s conventions for signs, Riemann indices order, etc. For Spinors we followed Spinors and space-time, by Penrose & Rindler.

There are sign configuration variables, with Wald’s defaults. They can be changed at the beginning of a session.

Component computations

Deal with tensors expressed as arrays of components in a given frame (aka basis), each component being a scalar function of some coordinate fields.

Performed with constructions that always know in which frame we are working.

Different frames will be represented with different colors. If an expression has colored parts it is still possibly basis-dependent. If the expression has all its parts in black then it is frame-independent (abstract).

Both together

These two types of computation are so different that traditionally there usually were two separate packages, or a switch to move from one type to the other.

xAct combines both types in a coherent framework, essentially performing the component computations with the abstract tools, and forcing everything to be geometric. The key idea is always to keep track of basis/chart dependencies.

Somebody said that “Differential Geometry is the study of objects that stay invariant under changes of notation”. Every dependency must be explicit in a computer implementation. xAct only breaks that rule once: the raising and lowering of indices with metrics.

1.5. Crash course in Wolfram Language

Some historical data

Currently in version 12.1.1, since June 2020.

Versions:

versions={
{{1988,6,23},"1.0.0"},
{{1988,10,31},"1.1.0"},
{{1989,8,1},"1.2.0"},
{{1991,1,15},"2.0.0"},
{{1992,6,15},"2.1.0"},
{{1993,6,1},"2.2.0"},
{{1996,9,3},"3.0.0"},
{{1999,5,19},"4.0.0"},
{{2000,11,2},"4.1.0"},
{{2002,11,1},"4.2.0"},
{{2003,6,12},"5.0.0"},
{{2004,10,25},"5.1.0"},
{{2005,6,20},"5.2.0"},
{{2007,5,1},"6.0.0"},
{{2007,7,5},"6.0.1"},
{{2008,2,25},"6.0.2"},
{{2008,6},"6.0.3"},
{{2008,11,18},"7.0.0"},
{{2009,3,5},"7.0.1"},
{{2010,11,15},"8.0.0"},
{{2011,3,7},"8.0.1"},
{{2011,10,24},"8.0.4"},
{{2012,11,28},"9.0.0"},
{{2013,1,30},"9.0.1"},
{{2014,7,9},"10.0.0"},
{{2014,9,17},"10.0.1"},
{{2014,12,10},"10.0.2"},
{{2015,3,30},"10.1.0"},
{{2015,7,14},"10.2.0"},
{{2015,10,15},"10.3.0"},
{{2015,12,16},"10.3.1"},
{{2016,3,2},"10.4.0"},
{{2016,4,18},"10.4.1"},
{{2016,8,8},"11.0.0"},
{{2016,9,28},"11.0.1"},
{{2017,3,16},"11.1.0"},
{{2017,4,25},"11.1.1"},
{{2017,9,14},"11.2.0"},
{{2018,3,8},"11.3.0"},
{{2019,4,16},"12.0.0"},
{{2020,3,18},"12.1.0"},
{{2020,6,17},"12.1.1"}};
values={#1,{1,1/10,1/100}.(FromDigits/@StringSplit[#2,"."])}&@@@versions;
DateListPlot[values,Mesh->Full,Joined->False,GridLines->{{{1990,1,1},{2000,1,1},{2010,1,1}},None}]

Number of symbols:

symbols=Table[EntityClass["WolframLanguageSymbol","VersionIntroduced"->version]//EntityList,{version,{1.0,2.0,2.2,3.0,4.0,4.1,4.2,5.0,5.1,5.2,6.0,7.0,8.0,9.0,10.0,10.1,10.2,10.3,10.4,11.0,11.1,11.2,11.3,12.0,12.1}}];
accum=Accumulate[Length/@symbols];
symbolsN={
{{1988,6,23},554}, (* 1.0 *)
{{1991,1,15},815}, (* 2.0 *)
{{1993,6,1},816}, (* 2.2 *)
{{1996,9,3},1345}, (* 3.0 *)
{{1999,5,19},1446}, (* 4.0 *)
{{2000,11,2},1450}, (* 4.1 *)
{{2002,11,1},1453}, (* 4.2 *)
{{2003,6,12},1502}, (* 5.0 *)
{{2004,10,25},1553}, (* 5.1 *)
{{2005,6,20},1554}, (* 5.2 *)
{{2007,5,1},2160}, (* 6.0 *)
{{2008,11,18},2560}, (* 7.0 *)
{{2010,11,15},3198}, (* 8.0 *)
{{2012,11,28},3598}, (* 9.0 *)
{{2014,7,9},4251}, (* 10.0 *)
{{2015,3,30},4368}, (* 10.1 *)
{{2015,7,14},4485}, (* 10.2 *)
{{2015,10,15},4560}, (* 10.3 *)
{{2016,3,2},4622}, (* 10.4 *)
{{2016,8,8},4800}, (* 11.0 *)
{{2017,3,16},4928}, (* 11.1 *)
{{2017,9,14},5034}, (* 11.2 *)
{{2018,3,8},5148}, (* 11.3 *)
{{2019,4,16},5424},  (* 12.0 *)
{{2020,3,18},5605} (* 12.1 *)
};
DateListPlot[symbolsN,Mesh->Full,Joined->False,GridLines->{{{1990,1,1},{2000,1,1},{2010,1,1}},None}]

Word cloud of individual words in the symbol names (like Function, ImageSize o RegionMemberFunction):

breakAtCapital[string_String]:=StringSplit[StringReplace[string,{"$"->"",RegularExpression["[0-9]"]->""}],RegularExpression["(?<=[a-z])(?=[A-Z])"]]
words=Flatten[breakAtCapital/@Names["System`*"]];
WordCloud[words]

Everything is an expression

Algebraic computation:

expr=(3/2-Sqrt[5/11])^7

Expand[expr]

N[expr,20]

FullForm[expr]

Integral/differential computation:

Integrate[1/(1+x^3),x]

D[%,x]

Simplify[%]

%//FullForm

Equivalent notations for (unary) function application:

{f[x],f@x,x//f}

Polynomial computations

Take a polynomial, expressed in any form:

(x+1)^10(-128+448 x+ (x^2)(-672+560 x-280 (x^2)+84 (x^3)-14 (x^4)+(x^5)))

Expand will convert it into a linear combination of monomials:

Expand[%]

Simplify will try to write it in the simplest possible form:

Simplify[%]

The power of pattern matching

• Replacement rules with patterns are a very powerful technique to handle indices:

rule=T[a_,b_]T[-b_,c_]:>T[a,c]

{T[-i,j]T[k,i],T[-i,j]T[i,k]}

The first case matches, because the Times product is commutative:

%/.rule

• There are many other powerful WL constructions that are very useful in the handling of tensor expressions: upvalues, delayed assignments, flexible typesetting, and much more. I’ll mention some of them. There are, of course, many other commands to process and visualization information.

2. Abstract computations with xTensor

2.1. Load the package

If xAct is correctly installed, then this should load the xTensor package:

<<xAct`xTensor`

2.2. Define basic objects

xTensor has its own type system and registration mechanism, that keeps track of all that has been declared during a session, and the mutual relations. The same symbol cannot be used to define two different objects. However two different symbols can be typeset in the same way (say both the Riemann and Ricci tensors can be typeset as R).

• Define a 4d manifold. Note how its associated vector bundle is defined too:

DefManifold[M,4,{a,b,c,d,e,f,g,i,j,k,l}]

• Define a contravariant vector field:

DefTensor[v[a],M]

v[a]

FullForm[%]

• The delta tensor is already defined:

delta[-a,b]

delta[-a,b]v[a]

delta[-a,a]

• Define an antisymmetric covariant vector field:

DefTensor[F[-a,-b],M,Antisymmetric[{-a,-b}]]

F[-a,-b]

In the following we will use InputForm intstead of FullForm, because the former shows covariant indices in a more readable form:

InputForm[%]

FullForm[%%]

2.3. Canonicalization

ToCanonical is the most important function in xTensor. It handles everything about index canonicalization, internally calling the group-theoretical algorithms. It first calls Expand on the tensorial expression and then acts on each term of the expanded expression individually.

• There is no automatic canonicalization:

F[-b,-a]

ToCanonical[ F[-b,-a] ]

• Commutative products:

v[a]F[-a,-b]v[b]

ToCanonical[%]

• Define two more objects:

DefTensor[w[-a],M]

DefTensor[T[-a,b],M]

• Expression with dummy indices:

expr=F[-a,-d]v[d]v[b]w[-b]+3v[b]v[c]w[-c]T[-b,e]F[-e,-a]

ToCanonical[expr]

WL’s Simplify can reduce the expression (there is no tensorial knowledge here):

Simplify[%]

• The combination of ToCanonical followed by Simplify is called Simplification in xTensor:

Simplification[expr]

With large expressions Simplify may take longer to evaluate than ToCanonical.

2.4a. Covariant derivatives

• Several other objects are defined together with a covariant derivative (we’ll look at them in the next slide):

DefCovD[CD[-a],SymbolOfCovD->{"|","D"}]

This is the covariant derivative of a vector field:

CD[-a][ v[b] ]

• You can select whether to show covariant derivatives in prefix (the default) or postfix notation:

$CovDFormat="Postfix";

CD[-a][ F[b,-c] ]

$CovDFormat="Prefix";

CD[-a][ F[b,-c] ]

• The Leibnitz rule is automatic:

CD[-a][3 F[-b,-c]v[c] ]

%v[b]//ToCanonical

2.4b. Covariant derivatives: associated tensors

• Let us have a look at the tensors defined with the covariant derivative CD. They all have CD appended in the tensor name:

{TorsionCD[a,-b,-c],ChristoffelCD[a,-b,-c],RiemannCD[-a,-b,-c,d],RicciCD[-a,-b]}

By default, covariant derivatives are defined without torsion.

• Because CD is not associated to a metric, its Riemann tensor is only antisymmetric in one pair:

RiemannCD[-a,-b,-c,d]+RiemannCD[-b,-a,-c,d]//ToCanonical

• Deduce that result from derivatives of the Christoffel tensor:

RiemannCD[-a,-b,-c,d]//RiemannToChristoffel

%+RiemannCD[-b,-a,-c,d]//RiemannToChristoffel

%//ToCanonical

• When internal dummy indices are introduced, they have unique names with a number that changes each time. This avoids “index collisions”. These indices are called “dollar indices” and there is a function to hide them:

RiemannCD[-a,-b,-c,d]//RiemannToChristoffel//ScreenDollarIndices

Write this and you will not see them again:

$PrePrint=ScreenDollarIndices;

RiemannCD[-a,-b,-c,d]//RiemannToChristoffel

but they are still there:

%//InputForm

2.4c. Christoffel tensors and the fiducial ordinary derivative PD

In xAct (and in particular in xTensor) everything must have a well defined tensorial character. The standard statement “Christoffels are not tensors” is unacceptable in xAct. We must find a way to reinterpret them as tensors! This is possible by interpreting the Christoffels as relating two covariant derivatives. Where is the other one?

Following Wald (GR, page 32), we introduce the concept of “ordinary derivative”, defined in terms of an underlying (but fully general) coordinate system. In practical terms it is a covariant derivative without torsion and without curvature. It is called PD in xTensor.

• The Christoffel of CD is the Christoffel tensor relating CD to PD:

Christoffel[CD,PD][a,-b,-d]

%//InputForm

Christoffel[PD,CD][a,-b,-d]

%//InputForm

• Define another covariant derivative, this time with torsion:

DefCovD[Cdt[-a],Torsion->True,SymbolOfCovD->{":","𝒟"}]

• The associated tensors are now:

{TorsionCdt[a,-b,-c],ChristoffelCdt[a,-b,-c],RiemannCdt[-a,-b,-c,d],RicciCdt[-a,-b]}

The fact that there is torsion means that commutation of derivatives now gives an additional term:

CD[-a]@CD[-b]@v[c]//SortCovDs

Cdt[-a]@Cdt[-b]@v[c]//SortCovDs

• We can relate the different derivatives using ChangeCovD, also called CovDToChristoffel:

expr=Cdt[-a][v[a]]

ChangeCovD[expr,Cdt,PD]

ChangeCovD[expr,Cdt,CD]

ChangeCovD[%,CD,PD]

The function BreakChristoffel expresses Christoffel[cd1, cd2][a, -b, -c] as Christoffel[cd1][a, -b, -c] - Christoffel[cd2][a, -b, -c]:

%//BreakChristoffel

%//Simplify

2.4d. Second Bianchi identity

• We can change the typesetting of any tensor at any moment:

PrintAs[ChristoffelCD]

(The ^= notation denotes a WL “upvalue”.)

PrintAs[ChristoffelCD]^="Γ"

PrintAs[RiemannCD]^="R"

Now we will see:

ChristoffelCD[a,-b,-c]

RiemannCD[-a,-b,-c,d]

• Antisymmetrize three indices of the covariant derivative of Riemann:

Antisymmetrize[  CD[-e][ RiemannCD[-c,-d,-b,a] ],{-c,-d,-e}  ]

3%//ToCanonical

Now expand the covariant derivatives into partial derivatives and Christoffel terms:

%//CovDToChristoffel

Convert Riemann into derivatives of Christoffel:

%//RiemannToChristoffel

Check that this is zero:

ToCanonical[%]

• Exercise: prove the second Bianchi identity for a connection with torsion, starting from the following expression. You will need to transform torsion tensors into differences of Christoffels using TorsionToChristoffel.

Cdt[-a][ RiemannCdt[-b,-c,-d,e] ]-TorsionCdt[f,-a,-b]RiemannCdt[-c,-f,-d,e]

6Antisymmetrize[%,{-a,-b,-c}]

Solution. Click on the cell bracket on the right to open, but first try to solve it on your own!

%//CovDToChristoffel
%//RiemannToChristoffel
%//TorsionToChristoffel
%//ToCanonical

• Exercise: prove the first Bianchi identity for a connection with torsion, starting from this expression:

RiemannCdt[-a,-b,-c,d]+Cdt[-a][ TorsionCdt[d,-b,-c] ]-TorsionCdt[e,-a,-b]TorsionCdt[d,-c,-e]

6Antisymmetrize[%,{-a,-b,-c}]

Solution. Click on the cell bracket on the right to open, but first try to solve it on your own!

%//CovDToChristoffel
%//RiemannToChristoffel
%//TorsionToChristoffel
%//ToCanonical

• Undefine the covariant derivative CD. The associated tensors are removed too.

UndefCovD[CD]

2.5a. Metric tensor fields

A metric is a symmetric two-covariant tensor field with which we do three special things:

	1) We define its Levi-Civita connection, which gives zero on the metric. Then the curvature tensors of that connection obey further properties.
	
	2) We will use the metric to raise and lower indices of all other tensors on the same vector bundle.
	
	3) We define an associated volume form.

• Define a metric field. We could call it g, but g is already used as an index, so instead we call it met and format it as g. The -1 indicates that the metric has negative signature:

DefMetric[-1,met[-a,-b],cd,PrintAs->"g"]

This is the metric:

met[-a,-b]

%//InputForm

and this is the inverse metric:

met[a,b]

• Contractions of the metric and its inverse are automatic:

met[-a,-b]met[b,c]

• The Levi-Civita connection of the metric gives zero:

cd[-a][met[-b,-c]]

cd[-a][met[b,c]]

Other derivatives do not give zero on the metric:

PD[-a][met[-b,-c]]

Cdt[-a][met[-b,-c]]

2.5b. Metric contractions

Contractions with delta are always automatic, but contractions of metrics are not, because there are situations in which we want full control on whether indices are covariant or contravariant. In other words, sometimes we want to have all metric factors explicit, and not absorved in “moved indices”. We’ll use the command ContractMetric to force metric contractions.

• This contraction is not automatic:

met[-a,-b]v[b]

This forces metric contraction:

ContractMetric[%]

• Contraction over the Levi-Civita connection works in the same way:

met[-a,-b]cd[-c][v[b]]

ContractMetric[%]

For other derivatives we need to activate an option:

met[-a,-b]PD[-c][v[b]]

ContractMetric[%]

ContractMetric[%%,OverDerivatives->True]

2.5c. Full Riemann symmetry

Contractions with delta are always automatic, but contractions of metrics are not, because there are situations in which we want full control on whether indices are covariant or contravariant. In other words, sometimes we want to have all metric factors explicit, and not absorved in “moved indices”. We’ll use the command ContractMetric to force metric contractions.

• We had shown antisymmetry of the first pair of indices of Riemann. Now we can show antisymmetry in the second pair:

Riemanncd[-a,-b,-c,-d]

Riemanncd[-a,-b,-c,-d]+Riemanncd[-a,-b,-d,-c]//RiemannToChristoffel

%//ChristoffelToGradMetric

ToCanonical[%]

• Exercise: show that Riemann is symmetric under exchange of the first and second pair of indices.

Solution. Click on the cell bracket on the right to open, but first try to solve it on your own!

Riemanncd[-a,-b,-c,-d]-Riemanncd[-c,-d,-a,-b]
%//RiemannToChristoffel
%//ChristoffelToGradMetric
%//ToCanonical

2.5d. Other metric-related tensors

• Contractions of Riemann give Ricci tensors:

Riemanncd[-a,-b,a,-d]

%//InputForm

• Contractions of Ricci give Ricci scalars:

Riccicd[-a,-b]met[a,b]

%//InputForm

• Expand the Riemann tensor in terms of Weyl:

Riemanncd[-a,-b,-c,-d]//RiemannToWeyl

%//WeylToRiemann

%//ToCanonical

• Expand the Ricci tensor in terms of the Einstein tensor:

Riccicd[-a,-b]//RicciToEinstein

• Expand the Ricci tensor in terms of a trace-free part:

Riccicd[-a,-b]//RicciToTFRicci

%//InputForm

• There is also the volume form antisiymmetric tensor associated to the metric:

epsilonmet[-a,-b,-c,-d]

which is also associated to the Levi-Civita connection:

%//cd[-e]

Products of epsilon tensors give “determinants” of metric tensors:

epsilonmet[-a,-b,-c,-d]epsilonmet[-e,-f,-i,-j]

epsilonmet[-a,-b,-c,-d]epsilonmet[-e,-f,-i,d]

epsilonmet[-a,-b,-c,-d]epsilonmet[-e,-f,c,d]

epsilonmet[-a,-b,-c,-d]epsilonmet[-e,b,c,d]

epsilonmet[-a,-b,-c,-d]epsilonmet[a,b,c,d]

• Or the determinant of the metric, which is a density of weight +2, indicated with the two overtildes:

Detmet[]

We will talk about tensor densities when performing component computations with xCoba.

2.6. Working with two metrics

When we have two (or more) metrics on the same vector bundle, we can only use one to raise and lower indices, which is the “first metric”, i.e. the first that was defined. The other(s) is(are) referred to as “frozen metric(s)” in xAct.

• Define another metric:

DefMetric[-1,MET[-a,-b],CD,SymbolOfCovD->{"#","𝔇"}]

• These are inverses of each other:

MET[-a,-b]InvMET[b,c]

but not these:

MET[-a,-b]MET[b,c]

%//SeparateMetric[met]

• Same thing happens with the Riemann tensors

RiemannCD[-a,-b,-c,-e]==RiemannCD[-a,-b,-c,d]met[-d,-e]

RiemannDownCD[-a,-b,-c,-e]==RiemannCD[-a,-b,-c,d]MET[-d,-e]

• Suppose we want to construct rules relating the two metrics as in a conformal transformation:

DefTensor[ϕ[],M]

Note that the pattern a_ accepts both covariant and contravariant indices. The typesetting shows them as contravariant:

confRules={MET[a_,b_]:>ϕ[]^2met[a,b],InvMET[a_,b_]:>met[a,b]/ϕ[]^2}

• Let us compute the relation between Christoffel tensors:

ChristoffelCD[a,-b,-c]-Christoffelcd[a,-b,-c]

%//ChristoffelToGradMetric

%/.confRules

ToCanonical[%,UseMetricOnVBundle->None]

• Show that the Weyl tensors (with all indices down!) are proportional to each other, and find the proportionality coefficient (call it X):

DefTensor[X[],M]

WeylCD[-a,-b,-c,-d]-X[]Weylcd[-a,-b,-c,-d]

%//WeylToRiemann

%//RiemannDownToRiemann

%//RiemannToChristoffel

%//NoScalar

%//ChristoffelToGradMetric

Expand[%/.confRules]

ToCanonical can bring that down to 176, though this is not yet very useful:

expr=ToCanonical[%,UseMetricOnVBundle->None]

Length[expr]

In this case Simplify is fast enough:

Simplify[expr]

If the expression was too big, we could just look at some leading terms, say the second derivatives of the metric:

Total@Cases[expr,_. PD[_]@PD[_]@met[_,_]]//Simplify

2.7. Lie derivatives

• Lie derivative of a tensor field along a vector field:

LieD[v[a]][met[-b,-c]]

LieDToCovD[%,cd]

%//ContractMetric

2.8. Variational derivatives

• Working with variational derivatives with respect to the metric, we need to instruct ToCanonical not to move indices up and down:

SetOptions[ToCanonical,UseMetricOnVBundle->None]

• Compute the Maxwell equations from the Lagrangian:

DefTensor[MaxwellA[-a],M,PrintAs->"A"]

MaxwellF[a_,b_]:=PD[a][MaxwellA[b]]-PD[b][MaxwellA[a]]

lagrangian=met[a,b]met[c,d]MaxwellF[-a,-c]MaxwellF[-b,-d]/4

sdg=Sqrt[-Detmet[]]

result1=VarD[MaxwellA[a]][sdg lagrangian]/sdg//ToCanonical

result2=met[b,c]cd[-b][MaxwellF[-a,-c]]//ChangeCovD//ChristoffelToGradMetric//ToCanonical

result1-result2//ToCanonical

• Expand the Ricci scalar in terms of derivatives of the metric:

rs=RicciScalarcd[]//RiemannToChristoffel//NoScalar//ChristoffelToGradMetric//Expand

Now we perform a direct variational derivative of the Einstein-Hilbert Lagrangian with respect to the inverse metric. It is a long computation, of about 10 seconds:

result1=VarD[met[a,b]][sdg rs]/sdg//ToCanonical

The expected result is the Einstein tensor:

result2=Einsteincd[-a,-b]//EinsteinToRicci//RiemannToChristoffel//NoScalar//ChristoffelToGradMetric//ToCanonical

result1-result2//ToCanonical

• We’ll see later a much better form of computing this result later with xPert or xTras.

• Set the defaults of ToCanonical back:

SetOptions[ToCanonical,UseMetricOnVBundle->All]

2.9. Other things xTensor can do

Tensors on “inner” (and possibly complex) vector bundles

So far we only worked with tensors on the tangent bundle. xTensor can handle other bundles, including complex bundles and hermitian tensors on them, though it is not yet ready to support metric fields on those inner bundles.

• Define a complex vector bundle. Declaring it as complex means that the complex conjugated bundle will be constructed:

DefVBundle[InnerC,M,4,{𝔄,𝔅,ℭ,𝔇,𝔈,𝔉,𝔊,ℌ},Dagger->Complex]

Define a connection on InnerC:

DefCovD[ICD[-a],InnerC,SymbolOfCovD->{"*","D"}]

The derivative ICD has curvature in both the tangent bundle TangentM3 and in the inner bundle InnerC. That becomes most apparent when changing to a different derivative:

DefTensor[Y[a,𝔅],M,Dagger->Complex]

ICD[-c][Y[a,𝔅]]

ChangeCovD[%,ICD]

ChangeCovD[%,PD,ICD]

Induced metrics

• Start from our metric met, and define an induced metric, orthogonal to a given timelike vector field:

DefTensor[n[a],M]

This is another form of upvalue for the symbol n:

n/:n[a_]n[-a_]:=-1

n[b]n[-b]

DefMetric[1,metrich[-a,-b],cdh,SymbolOfCovD->{"|","D"},InducedFrom->{met,n},PrintAs->"h"]

We now can work with the extrinsic curvature tensor, and the acceleration vector of the normal n:

ExtrinsicKmetrich[-a,-b]

%//ExtrinsicKToGradNormal//ContractMetric

Project curvature tensors along this structure.

GaussCodazzi[Riemanncd[-a,-b,-c,-d],metrich]

n[a]n[c]%//Expand

Cartesian product manifolds (global metric is diagonal by blocks)

• See documentation file xTensorDoc.nb.

And many other things

EchoFunction[Length]@Names["xAct`xTensor`*"]

2.10. Clean up

• Check the list of tensors we have defined:

$Tensors

Some of them are metrics:

$Metrics

Covariant derivatives:

$CovDs

• Undefine the metrics, which undefines all associated objects:

Map[UndefMetric,$Metrics]

• We still have tensors associated to the connections Cdt and ICD:

$Tensors

$CovDs

UndefCovD/@{Cdt,ICD}

• We still have some tensors:

$Tensors

UndefTensor/@%

UndefVBundle[InnerC]

• We still keep the manifold M to work with xCoba.

$Manifolds

2.11. The group theory algorithms

• The main ideas follow R. Portugal and collaborators [(HyperlinkTemplate[arXiv:math-ph/0107031, https://arxiv.org/abs/math-ph/0107031None, https://arxiv.org/abs/math-ph/0107031, HyperlinkActionRecycled, HyperlinkActive, , ])], [(HyperlinkTemplate[arXiv:math-ph/0107032, https://arxiv.org/abs/math-ph/0107032None, https://arxiv.org/abs/math-ph/0107032, HyperlinkActionRecycled, HyperlinkActive, , ])].

• Generalized for xAct by JMMG. [(HyperlinkTemplate[arXiv:0803.0862, https://arxiv.org/abs/0803.0862None, https://arxiv.org/abs/0803.0862, HyperlinkActionRecycled, HyperlinkActive, , ])].

• B. Niehoff made these methods even faster. [(HyperlinkTemplate[arXiv:1702.08114, https://arxiv.org/abs/1702.08114None, https://arxiv.org/abs/1702.08114, HyperlinkActionRecycled, HyperlinkActive, , ])]. Not yet implemented in xAct.

• Key idea: a tensor configuration T[-b, a, c, b, -c] is a mapping from indices (a subset of {a, -a, b, -b, c, -c, ...}) to tensor slots {1, 2, 3, 4, 5}.

• There are two groups of symmetry:

	1) The symmetries S of the slots: whether the tensor is antisymmetric in the first three indices, say.
	
	2) The symmetries D of the dummy indices: exchange a and b due to renaming, or exchange of b and -b due to the metric being symmetric.

The mapping g (the tensor configuration) from indices to slots is equivalent to all configurations D.g.S. This is called a “double coset” in group theory.

• Hence what is needed is an algorithm to find the “canonical representative” configuration in a double coset, meaning the smallest configuration according to some predefined order. This problem is known to be NP, though methods exist that are fast for the majority of practical cases. Sometimes, however, some computations will be very slow...

3. Component computations with xCoba

3.1. Load the package

If xAct is correctly installed, then this should load the xCoba package:

<<xAct`xCoba`

3.2. Define a basis or frame

The main new ingredient to define components is a basis (or frame) of vector fields. We will give the basis a name, and then refer to the vectors via numbers.

• Define two different bases red and blue on the TangentM vector bundle, both with numbers 0, 1, 2, 3. We will use Red and Blue colors, respectively:

DefBasis[red,TangentM,{0,1,2,3},BasisColor->Red]

DefBasis[blue,TangentM,{0,1,2,3},BasisColor->Blue]

3.3. CTensor

CTensor[...] expressions play in xCoba the role of tensor symbols in xAct. The main syntax is

	CTensor[array, bases, weight]

We will talk later about density weights.

• This is a vector field in the red basis:

w=CTensor[{10,-23,1,6},{red}]

w[a]

• Tensor product with itself:

w[a]w[b]

%//InputForm

Once more:

w[a]w[b]w[c]//ToCanonical

HeadOfTensor[%,{a,b,c}]

• Introduce a vector in the blue basis:

u=CTensor[{2,-2,2,-2},{blue}]

u[a]w[b]

%//InputForm

3.4. Basis vectors

• Take our vector w:

w[a]

• Expand into a linear combination of abstract basis vectors:

BasisExpand[%,{red}]

%//InputForm

%%//FromBasisExpand

• Basis is an abstract tensor of two indices. The basis is stored in the index, using the notation {number, basis} or {number, -basis}. This is the notation of “marked indices”. Basis indices represent a contraction with a basis vectors, and hence the objects above are actually vectors, not rank-2 objects.

{Basis[{0,-red},a],Basis[-a,{1,red}]}

• Basis vectors can be combined as usual. Take these two objects and multiply them:

Basis[{0,-red},a]Basis[-a,{1,red}]

%//ContractBasis

Basis[{1,-red},a]Basis[-a,{1,red}]//ContractBasis

• Basis is actually just another name for delta, and they interconvert into each other as appropriate:

Basis[-a,b]

delta[{0,-red},b]

3.5. Components of abstract tensors

• Define a Tensor T on M:

DefTensor[T[a,b],M]

• A component of an abstract tensor is the contraction of the tensor with basis vectors:

T[a,b]Basis[-a,{1,red}]Basis[-b,{2,red}]

%//ContractBasis

%//InputForm

• This is the array of scalar components (no abstract index remains!) of the tensor T:

ComponentArray[T,{red,red}]

• If we have a CTensor object then this will extract individual components:

w[a]

w[{1,red}]

That is equivalent to

w[a]Basis[-a,{1,red}]

%//ContractBasis

• If we have a different basis:

w[a]Basis[-a,{1,blue}]

we get this linear combination of the scalars of the basis change between the red and blue bases:

%//ContractBasis

3.6. Changes of basis

• We need to provide values of all scalars Basis[{n, -basis1}, {m, basis2}].

change=RandomInteger[{-10,10},{4,4}]

Det[change]

SetBasisChange[CTensor[change,{-red,blue}]]

• Now, let us go back to our previous computation:

w[a]

w[{1,red}]

w[{1,blue}]

• In fact we can change the whole vector:

wb=ToCTensor[w,{blue}]

ToCTensor[wb,{red}]

• Exercise: compute the tensor product of w with itself and express it in the four possible basis configurations, namely {red, red}, {red, blue}, {blue, red}, and {blue, blue}.

w[a]w[b]
ww=HeadOfTensor[%,{a,b}]
{ww[a,b],
ToCTensor[ww,{red,blue}][a,b],
ToCTensor[ww,{blue,red}][a,b],
ToCTensor[ww,{blue,blue}][a,b]}

Note how the tensor does not look symmetric when using mixed bases.

3.7. Densities

• The determinant of a 2-covariant tensor, like a metric, must be computed by first choosing a basis to form a matrix of components, and then taking the determinant of that metric. Hence the scalar determinant actually depends on the choice of basis. It is a density.

ctred=CTensor[With[{m=RandomInteger[{-10,10},{4,4}]},m+Transpose[m]],{-red,-red}]

ctblue=ToCTensor[ctred,{-blue,-blue}]

Determinant[ctred]

detr=%[]

Determinant[ctblue]

detb=%[]

jac=Determinant[CTensor[change,{-red,blue}]][]

%//InputForm

{detr,detb jac^2}//Simplify

3.8. Charts

• Define a chart called cart, with scalar fields named t, x, y, z, whose respective vectors are numbered 0, 1, 2, 3. This defines a basis with the same name:

DefChart[cart,M,{0,1,2,3},{t[],x[],y[],z[]},ChartColor->Green]

• The coordinate chart has an associated (“parallel”) ordinary derivative (zero torsion and zero curvature), which gives zero on all vector and covector fields of the chart:

PDcart[-a][Basis[{0,-cart},b]]

• Recall the PD ordinary derivative of xTensor. It was precisely the parallel derivative of some unspecified (but general) coordinate chart.

• Now we can construct functions of the coordinate scalar fields. Note the important distinction between the words “function” and “field”.

DefScalarFunction[F]

Note the fact that we need to provide the empty pairs of brackes, because t, x, y, z are scalar fields! Those brackets are as important as those of v[a].

F[t[],x[],y[],z[]]

All covariant derivatives (ordinary or not) give the same result acting on scalar fields:

PD[-a][%]

3.9. Metric field: Schwarzschild

• Define a coordinate system:

DefChart[schw,M,{0,1,2,3},{t[],r[],θ[],ϕ[]},ChartColor->Brown,FormatBasis->{"Partials","Differentials"}]

• Now define a metric field, the Schwarzschild metric:

DefConstantSymbol[mass,PrintAs->"M"]

met=CTensor[DiagonalMatrix[{-1+2mass/r[],1/(1-2mass/r[]),r[]^2,r[]^2Sin[θ[]]^2}],{-schw,-schw}];

met[-a,-b]

BasisExpand[%,{schw}]

• That tensor does not have any information about being a metric. In particular we cannot yet use it to raise and lower indices:

met[a,b]//Catch

We can compute the inverse metric manually:

Inv[met]

• This is a contravariant vector, because we are using basis schw, rather than basis -schw for a covariant vector:

v=CTensor[{0,r[],0,0},{schw}]

v[a]

v[-a]//Catch

Of course, you can manually use the metric to lower indices:

met[-a,-b]v[b]

• We now declare the met is the metric on the vbundle of the coordinated frame of schw (this is roughly the equivalent of DefMetric in xTensor). In some cases SetCMetric can unambiguously compute the signature from the matrix of components, but in general it cannot, so we need to specify the signature as a triple {number of +1s, number of -1s, number of 0s}:

SetCMetric[met,schw,SignatureOfMetric->{3,1,0}]

Now that we have a metric, raising and lowering indices automatically produces the expected result:

{v[a],v[-a]}

This is a contraction:

v[a]v[-a]

• If we raise one index of the metric we get the identity:

met[-a,b]

This is the inverse metric:

met[a,b]

• The determinant is a scalar density:

Determinant[met][]

%//InputForm

The volume form is a tensor, not a density, and its components are scalars:

epsilon[met]

epsilon[met][{0,-schw},{1,-schw},{2,-schw},{3,-schw}]

3.10. Levi-Civita connection and curvature

In the same way that we have “component tensors” CTensor[array, bases, weight], we also have “component covariant derivatives”  using CCovD.

• This is the Levi-Civita connection associated to the metric. It has the structure CCovD[pd, christoffel, metric], meaning that it is the covariant derivative that can be obtained from the derivative pd using the given christoffel tensor. The metric tensor is stored to denote where the object came from:

cd=CovDOfMetric[met]

• Now we can use it to compute covariant derivatives of tensor fields. The notation is identical to that used in xTensor. Note that the differentiation index is added last (following the behavior of D):

cd[-a][v[b]]

• The covariant derivative of the metric field is zero:

cd[-a][met[-b,-c]]

• This is the Christoffel tensor of the Levi-Civita connection with respect to the partial derivative of the chart:

Christoffel[cd,PDschw]

%[a,-b,-c]

ToBasisExpand[%]

• Commuting covariant derivatives:

comm=cd[-b]@cd[-a]@v[c]-cd[-a]@cd[-b]@v[c]//Simplify

• We can check that the result coincides with the expected contraction with the Riemann tensor of the connection:

comm==Riemann[cd][-a,-b,-d,c]v[d]

%//Simplify

• Being a Levi-Civita connection, it has zero torsion:

Torsion[cd][a,-b,-c]

• The Schwarzschild metric is Ricci-flat:

Ricci[cd][-a,-b]

Riemann[cd][-a,-b,-c,d]

• Unset the metric, so that we can use other metrics on the same manifold.

UnsetCMetric[met]

3.11. Kerr-Newman in Boyer-Lindquist coordinates

• Deactivate info messages:

$DefInfoQ=$UndefInfoQ=False;
$CVVerbose=False;

• Define the Boyer-Lindquist coordinates:

DefChart[BL,M,{0,1,2,3},{t[],r[],θ[],ϕ[]}]

and the parameters of the solution:

DefConstantSymbol[Mass,PrintAs->"M"]
DefConstantSymbol[Rotation,PrintAs->"a"]
DefConstantSymbol[Charge,PrintAs->"Q"]

• The metric is:

ρ2=r[]^2+Rotation^2Cos[θ[]]^2;
Δ=r[]^2-2Mass r[]+Rotation^2+Charge^2;

ρ2(((PD[-a][r[]]PD[-b][r[]])/Δ)+PD[-a][θ[]]PD[-b][θ[]])-(PD[-a][t[]]-Rotation Sin[θ[]]^2PD[-a][ϕ[]])(PD[-b][t[]]-Rotation Sin[θ[]]^2PD[-b][ϕ[]])(Δ/(ρ2))+((r[]^2+Rotation^2)PD[-a][ϕ[]]-Rotation PD[-a][t[]])((r[]^2+Rotation^2)PD[-b][ϕ[]]-Rotation PD[-b][t[]])((Sin[θ[]]^2)/(ρ2))

KNmetricBL=HeadOfTensor[FromBasisExpand[%],{-a,-b}]//Simplify

• Compute curvature tensors with MetricCompute:

MetricCompute[KNmetricBL,BL,All,Parallelize->True]//AbsoluteTiming

• Extract information:

cd=CovDOfMetric[KNmetricBL];

RicciScalar[cd][]

• Ricci tensor:

KNricciBL=Ricci[cd]

Kretschmann[cd][]

Riemann[cd][-a,-b,-c,d]

• Check the Bianchi identity:

cdriemdown=TensorDerivative[RiemannDown[cd],cd];

cdriemdown[-a,-b,-c,-d,-e]+cdriemdown[-b,-c,-a,-d,-e]+cdriemdown[-c,-a,-b,-d,-e]//Simplify

3.12. Kerr-Newman in the rotating frame

• Now define the rotating frame in terms of the BL coordinates. Note that we need to provide the frame in a very specific way:

change={{((r[]^2+Rotation^2)/(ρ2)),0,0,(Rotation/(ρ2))},{0,1,0,0},{0,0,1,0},{Rotation Sin[θ[]]^2,0,0,1}}

DefBasis[frame,TangentM,{0,1,2,3},BasisColor->Blue,BasisChange->CTensor[change,{-frame,BL}]]

This means:

ToCTensor[Basis,{-frame,BL}]

ToCTensor[Basis,{-BL,frame}]//Simplify

• For example:

ToCTensor[CTensor[{vt,vr,vθ,vϕ},{BL}],{frame}]

Remove[vt,vr,vθ,vϕ]

• The metric tensor is diagonal in this frame:

factor=((r[]^2)/(ρ2))(1-((2Mass)/(r[]))+((Rotation^2+Charge^2)/(r[]^2)));

KNmetricRF=CTensor[DiagonalMatrix[{-factor,1/factor,ρ2,ρ2 Sin[θ[]]^2}],{-frame,-frame},0]

KNmetricRF[-a,-b]

• Check correspondence with the metric previously computed:

ToCTensor[KNmetricRF,{-BL,-BL}][-a,-b]==KNmetricBL[-a,-b]//Simplify

• Compute curvature tensors:

MetricCompute[KNmetricRF,BL,All,Parallelize->True]//AbsoluteTiming

• Extract information:

cd=CovDOfMetric[KNmetricRF];

RicciScalar[cd][]

• The Ricci tensor is again diagonal in this frame:

Ricci[cd]

%[-a,-b]

Kretschmann[cd][]

Riemann[cd]

Check the symmetries of the Riemann tensor with all indices down:

TensorSymmetry[RiemannDown[cd][[1]]]

• Check the Bianchi identity:

cdriemdown=TensorDerivative[RiemannDown[cd],cd];

cdriemdown[-a,-b,-c,-d,-e]+cdriemdown[-b,-c,-a,-d,-e]+cdriemdown[-c,-a,-b,-d,-e]//Simplify

3.13. Cleanup

$Tensors

$Bases

$Charts

UndefTensor/@{Jacobianbluered,JacobianBLframe};

UndefBasis/@{red,blue,frame}

UndefChart/@{cart,schw,BL}

Exit

4. Perturbation theory with xPert

4.1. Load the package

If xAct is correctly installed, then this should load the xPert package:

<<xAct`xPert`

Note that some configuration defaults of xTensor were changed.

4.2. Define a perturbative structure around a background metric

• Define a set of perturbations (of arbitrary order) around the background metric g, with perturbative parameter ϵ:

DefManifold[M,4,{a,b,c,d,e,f,i,j,k,l}]

DefMetric[-1,g[-a,-b],cd]

DefMetricPerturbation[g,h,ϵ]

• Perturbations of the metric to any other:

Perturbed[g[-a,-b],3]

Perturbation[g[-a,-b],3]

It helps to color the “perturbation label indices”:

Unprotect[IndexForm];
IndexForm[LI[x_]]:=ColorString[ToString[x],Red]

Perturbation[g[-a,-b],3]

%//InputForm

4.3. Perturb the inverse metric

• Perturb the inverse metric:

Perturbed[g[a,b],3]

%//ExpandPerturbation

• Perturb at a specific order:

Perturbation[g[a,b],2]

%//ExpandPerturbation

• Check inversion relations:

Perturbed[g[-a,-b],3]

Perturbed[g[b,c],3]//ExpandPerturbation

% %%//Expand

%//ContractMetric//ToCanonical

Collect[%,ϵ]

4.4. Perturb the curvature tensors

• Change the typesetting of Riemann, given that here we work with only one metric:

PrintAs[Riemanncd]^="R";

• First perturbation of Riemann:

Perturbation[Riemanncd[-a,-b,-c,d],1]

ExpandPerturbation[%]

SortCovDs[%]//ToCanonical

• Around flat spacetime:

%/._Riemanncd->0

• Fourth-order perturbation of Riemann:

Perturbation[Riemanncd[-a,-b,-c,d],4]

ExpandPerturbation[%]//Expand

UndefTensor[h]

UndefParameter[ϵ]

UndefMetric[g]

4.5. Use xPert to perform variational derivatives

• This is implemented in xTras, by Teake Nutma:

<<xAct`xTras`

• Define the metric again. Internally a perturbation structure is defined for it:

DefMetric[-1,g[-a,-b],cd,CurvatureRelations->True]

PrintAs[Riemanncd]^="R";
PrintAs[Riccicd]^="R";
PrintAs[RicciScalarcd]^="R";
PrintAs[Einsteincd]^="G";

• Now we can use the extended VarD command of xTras. Integrations by parts performed with the given connection:

VarD[g[-a,-b],cd][RicciScalarcd[]]

• There is also a version that takes care of the determinants of the metric:

?VarL

VarL[g[-a,-b]][RicciScalarcd[]]

%//RicciToEinstein//ToCanonical

• Variation of more complicated scalars, like Ricci square:

s1=Riccicd[-a,-b]Riccicd[a,b]

VarL[g[-a,-b]][s1]

%//ContractMetric//ToCanonical

Or Riemann square:

s2=Riemanncd[-a,-b,-c,-d]Riemanncd[a,b,c,d]

VarL[g[-a,-b]][s2]

%//ContractMetric//ToCanonical

• xTras does many other things!

?xAct`xTras`*

• Terminate the kernel, to avoid undefining the various things xTras created

Exit

5. Riemann invariants with Invar

5.1. Load the package

If xAct is correctly installed, then this should load the xPert package:

<<xAct`Invar`

• For normal operation, Invar requires downloading large collections of identities of Riemann polynomials.

5.2. Example of pure brute force

• In dimension 4 the Riemann tensor has 20 - 6 = 14 independent scalar freedoms. Hence the scalar polynomials of the Riemann tensor must obey very many identities. How can we handle this in a practical way?

• Simple idea: precompute them all up to certain order. We did with this xAct for all identities containing up to products of 7 Riemann tensors, and then we generalized to handle up to 12 derivatives of the metric, meaning things like 10 derivatives of Riemann, or the product of the fourth-order derivatives of two Riemanns, etc. We got more than half a million identities, and they are all tabulated.

• Define a manifold and its metric:

DefManifold[M4,4,IndexRange[a,z]]

DefMetric[-1,metric[-a,-b],CD,CurvatureRelations->False,PrintAs->"g"]

PrintAs[RiemannCD]^="R";
PrintAs[epsilonmetric]^="ϵ";

• Random Riemann monomials:

RandomRiemannMonomial[{0,2,2},metric]

expr=RiemannToInv[%]

InvSimplify[expr]

%//InvToRiemann

5.3. A higher-order case

• This goes to the limit of 12 metric derivatives:

RandomRiemannMonomial[{0,2,4},metric]

expr=RiemannToInv[%]

InvSimplify[expr]

riemanns=%//InvToRiemann

5.4. How do you copy that to LATEX ?

• We use TexAct, specialized in producing LATEX code for tensors:

<<xAct`TexAct`

• We have this number of terms:

Length[riemanns]

• Produce TEX result:

tex=TexPrint[riemanns]

• That’s a single line, a bit too long. We need to break it:

Options[TexBreak]

btex=TexBreak[tex]

• Add the initial and final marks:

ftex="\\begin{eqnarray} && " <> btex <> "\\end{eqnarray}"

• And so it is ready to view:

TexView[ftex]

or to export to a file:

TexPort[ftex,"~/riemanns"]

6. Conclusions

Summary

xAct is a collection of (currently) 20 packages of Wolfram Language code.

xAct implements extensive and efficient functionality for tensor computations.

xAct handles both abstract and component computations in a coherent way, performing component computations with the abstract tools.

xAct uses a strict point of view in which all objects have definite geometric meaning. This is done by keeping track of all manifold, vbundle, basis/chart, metric dependencies. Expressions are never ambiguous.

The strictness of the language allows blind attack of very large expressions, effectively using a combination of fine-tuned algorithmics and brute force.

Over the last 15 years, more than 500 references (and about 30 theses) across multiple areas of Physics and Maths acknowledge the use of xAct.

In the recent review on Tensor Computer Algebra by Malcolm MacCallum, xAct features as one of the main recommended general systems.

If you try it, don’t hesitate to ask questions in the xAct forum, or directly to me, jose@xact.es.

Future plans

• Collect metrics.

• Implement more component utilities: metric classification, find Killing vectors, etc.

• Make xPert and xCoba talk to each other.

• Add code to support numeric work.