xAct: Efficient Tensor Computer Algebra

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

Wolfram R&D Livestream, November 23, 2022

Plan of the talk

1. Introduction to xAct

2. Abstract computations with xTensor

3. Component computations with xCoba

4. Perturbation examples with xPert and xTras

5. Frequent questions and future plans

1. Introduction to xAct

1.1. Main

Project developed since 2002, all in Wolfram Language, with a bit of C code.

xAct is a hierarchical collection of packages, currently 23. Several more soon.

Performs both abstract computations and component computations, possibly mixed.

Successfully used in 800+ publications (47% gr-qc,  40% hep-th,  7% astro-ph,  6% other).

950+ different authors. Highest numbers of publications using xAct packages:

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, currently 23, organized hierarchically.

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

There are about 1500 xAct symbols in total, 450 of them just in xTensor.

Packages

Core packages

Application packages

Input/Output packages

Exceptional packages

Dependency graph

• Loops are not allowed. All dependencies must point downwards:

Graph[,VertexLabels->Automatic,GraphLayout->"LayeredDigraphEmbedding",VertexLabelStyle->Bold]

1.4. Example session. Abstract vs. component computations

Abstract computations

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

• Load xTensor:

<<xAct`xTensor`

• Define a manifold, two tensor fields and a metric field:

DefManifold[M,4,{a,b,c,d,e,f}]

DefTensor[v[a],M]

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

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

• Automatic contractions with the delta tensor:

v[a]delta[-a,b]

• Automatic use of the Leibnitz rule:

F[-b,-c]v[c]

CD[-a][%]

• Canonicalize a product:

%v[b]

%//ToCanonical

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.

{$AccelerationSign,$epsilonSign,$ExtrinsicKSign,$RicciSign,$RiemannSign,$TorsionSign}

Component computations

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

• Load xCoba

<<xAct`xCoba`

• Define a basis field on the tangent bundle of M:

DefBasis[B,TangentM,{0,1,2,3}]

• Construct tensors defined by numeric components in the basis B:

A=CTensor[{{0,1,2,3},{-1,0,4,5},{-2,-4,0,6},{-3,-5,-6,0}},{-B,-B}];
w=CTensor[{7,8,9,10},{B}];

w[b]

A[-a,-b]

A[-a,-b]w[b]

%w[a]

Component computations always keep track of which bases we are using.

Different bases 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 basis-independent (abstract).

Both together

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

xAct combines both types in a coherent framework:

• Contract an abstract and a component tensor:

F[a,b]A[-a,-b]

%//ToCanonical

F[-a,-b]w[a]w[b]

%//ToCanonical

• Automatic use of metric components (w was defined contravariant):

w[-a]w[a]//Expand

• Replace an abstract tensor by a component tensor to particularize an abstract expression:

%/.g->CTensor[DiagonalMatrix[{-1,1,1,1}],{-B,-B}]

Cleanup

• Undefine abstract tensor fields:

UndefTensor/@{g,F,v}

• Undefine bases:

UndefBasis[B]

• Undefine manifolds:

UndefManifold[M]

• Remove other variables:

Remove[A,w]

• There are no symbols left in the user workspace:

?Global`*

2. Abstract computations with xTensor

2.1. Setup

• Define again a 4D manifold with a metric field:

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

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

• Note the result is typeset as g, even though internally we still have the symbol met:

met[-a,-b]

%//InputForm

met[a,b]

met[-a,b]

• CD is the Levi-Civita connection of the metric:

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

• The curvature tensors of CD:

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

RicciCD[-a,-b]

RicciScalarCD[]

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

RicciCD[-a,a]

• Symmetries under index permutations:

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

%//ToCanonical

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

%//ToCanonical

• By default covariant derivatives have zero torsion:

TorsionCD[-a,-b,c]

2.2. Partial derivatives and Christoffels

• Expand the Riemann tensor as partial derivatives of Christoffel tensors:

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

New dummies are always unique:

%//RiemannToChristoffel

• Use this magical incantation to hide the inner dummies:

$PrePrint=ScreenDollarIndices;

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

though internally they are still there!

%//InputForm

• Expand Christoffels into derivatives of the metric:

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

• This type of brute-force expansion is very useful to prove identities. First Bianchi identity:

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

%//RiemannToChristoffel//ChristoffelToGradMetric

%//ToCanonical

• Second Bianchi identity:

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

%//CovDToChristoffel

%//RiemannToChristoffel

%//ChristoffelToGradMetric

%//ToCanonical

2.3. Now with torsion

• 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:

DefTensor[v[a],M]

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

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

• This is now the second Bianchi identity:

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

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

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

• First Bianchi identity for a connection with torsion:

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

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

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

• Undefine the metric field and the covariant derivative with torsion:

UndefMetric[met]

UndefCovD[Cdt]

UndefTensor[v]

2.4. Other things to do with xTensor

• Work with different metrics at the same time. Note: it’s only possible to “raise and lower indices” with the first one!

• In particular, it is possible to work with conformally related metrics.

• Also with “induced metrics”, orthogonal to a given vector. Gauss-Codazzi equations.

• Work with “product metrics”, defined by diagonal blocks. Not yet general metrics by blocks!

• Work with the Einstein and Weyl tensors. Work with epsilon tensors.

• Take Lie derivatives.

• Take variational derivatives.

• There are products other than Times, say antisymmetric products and products assigning degrees to objects.

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

• ...

• See the documentation file xTensorDoc.nb in the xAct/Documentation/English folder.

3. Component computations with xCoba

3.1. Load the package

<<xAct`xCoba`

3.2. Charts and metric fields: Schwarzschild

• Define a coordinate system called schw, with scalar fields t, r, θ, ϕ corresponding to components 0, 1, 2, 3:

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][a,b]

• 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}]//Simplify

3.3. 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:

met

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.4. 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 (and their derivatives) with MetricCompute:

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

• Extract information:

cd=CovDOfMetric[KNmetricBL];

RicciScalar[cd][]

• Curvature tensors:

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

Ricci[cd][-a,-b]

Kretschmann[cd][]

Extract a component of the Ricci tensor:

Ricci[cd][{0,-BL},{3,-BL}]

3.5. Kerr-Newman in the rotating frame

• Now define the rotating frame in terms of the BL coordinates, using a specific change of frame matrix:

changeMatrix={{((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[changeMatrix,{-frame,BL}]]

(change=CTensor[changeMatrix,{-frame,BL}])[-a,b]

• The metric tensor is diagonal in this frame:

Simplify[change[-a,c]change[-b,d]KNmetricBL[-c,-d]]

KNmetricRF=HeadOfTensor[%,{-a,-b}]

• 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][]//FullSimplify

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

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

TensorSymmetry[RiemannDown[cd]//First]

3.6. Cleanup

UndefTensor[JacobianBLframe]

UndefChart/@$Charts;

UndefBasis/@$Bases;

UndefConstantSymbol/@{mass,Mass,Rotation,Charge};

UndefManifold[M]

Remove[cd,change,changeMatrix,comm,KNmetricBL,KNmetricRF,met,v,Δ,ρ2]

?Global`*

4. Perturbation examples with xPert and xTras

4.1. Load the xPert package

Exit

<<xAct`xPert`

• 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-order perturbation of Riemann:

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

ExpandPerturbation[%]

SortCovDs[%]//ToCanonical

• Around flat spacetime:

%/._Riemanncd->0

• We can do the same thing for higher perturbation orders:

order=2;

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

(ExpandPerturbation[%]//Expand//SortCovDs//ToCanonical)/._Riemanncd->0

We know that the first order terms must be here somewhere, but with perturbation order 3 instead of 1:

%//ScreenDollarIndices//ColorPositions[Position[%,LI[order]][[All,{1}]]]

• Clean up:

UndefTensor[h]

UndefParameter[ϵ]

UndefMetric[g]

Remove[order]

4.5. Use xPert and xTras to perform variational derivatives

<<xAct`xTras`

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

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

This is very useful to study the equations of motion of theories based on Langrangians other than the Einstein-Hilbert Lagrangian. For example in f(R) theories.

5. Questions

5.1. Missing bits of functionality

5.1.1. Perturbation theory of tensors defined by xCoba

• Currently there is no special support for the combination xPert + xCoba.

• In principle, it’s possible to decompose perturbation equations in a given basis or chart, but there is no way to specify conditions on how to perturb basis objects.

5.1.2. Handling of general metrics specified by blocks

• xTensor can work with “product metrics” specified by diagonal blocks corresponding to two independent manifolds.

• The general case would be an n+m decomposition, and it would be very nice to support this.

• The particular case of 1+m is specially important. xTensor can handle induced metrics, but in full (1+m)-dimensional notation.

5.1.3. General handling of multi-term symmetries

• xTras has some support for Young tableaux, but we need something more powerful and complete.

5.1.4. Collections of standard metric fields

5.1.5. More functionality to explore metrics

• Metric classification, find Killing vectors, etc.

5.1.6. Extension to support global (not just local) differential geometry

5.1.7. Tensors defined along submanifolds, say curves

5.1.8. Continue extending the index-free notations introduced in xTerior

5.1.9. Support numeric work

• For example, a way to specify how to discretize a formulation of the Einstein equations.

5.2. Things that could be done better

5.2.1. Further simplify the process of replacing an abstract configuration by a component configuration

5.2.2. Implement Ben Niehoff’s fast methods for permutation group theory

5.2.3. Simplify the restriction to flat metrics

5.3. Future of the development of xAct

• Move xAct to the new Wolfram Paclet Repository, to make releases easier.

• Move code development to github.

• Collect known issues and suggestions.

• Modernize documentation.

6. Conclusions

xAct is a collection of (currently) 23 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 18 years, more than 850 references (including 75 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.