Demo for Tensors.m
Andrew D. Lewis
Department of Mathematics and Statistics
Queen's University
Kingston ON K7L 3N6
Canada

andrew@mast.queensu.ca

This is a set of Mathematica commands for performing basic tensor analysis.  This is an initial implementation, and will hopefully be improved upon as time goes by.

Reference: Geometric Control of Mechanical Systems.  Modeling, Analysis, and Design for Simple Mechanical Control Systems
                      Francesco Bullo and Andrew D. Lewis
                       http://penelope.mast.queensu.ca/smcs/
       January 2003

<< Tensors.m

To get help, type ?command

?ChangeCoordinates

Tensor basics

Tensors of type $(r,s)$ are stored as lists of depth $r+s$, with the basic list element being a component of the tensor.  One can initialize a tensor to have all zero entries.  The following command initializes a $(0,2)$-tensor in a $2$-dimensional vector space.

g = InitializeTensor[{0, 2}, 2]

Note that a tensor can be thought of as being on a vector space, or on the tangent space to a manifold.  As far as how it is stored, they are the same thing.

Let us work with a specific tensor, namely the standard Riemannian metric on $\real^2$, first using Cartesian coordinates.

g[[1, 1]] = g[[2, 2]] = 1; g

A tensor can be evaluated on various of its arguments.  For example, the Riemannian metric above can be evaluated on two vectors.

u  =  {u1, u2}

v  =  {v1, v2}

EvaluateTensor[g, {u, v}, {0, 2}, {{}, {}}]

The syntax here bears explanation.  The first argument is the tensor itself.  The second argument is a list containing the vectors and covectors on which the tensor will be evaluated.  The third argument is the type of the tensor.  The fourth argument consists of two lists.  The first list is the contravariant (i.e., up) indices that will be left free, and the second is the covariant (i.e., down) indices that will be left free.

To see how this works, let us use the same tensor, but now evaluate it on only one argument.  This corresponds in this case to the ``flat map.''

EvaluateTensor[g, {u}, {0, 2}, {{}, {1}}]

Since the tensor is symmetric, the answer will be the same if the second covariant index is left free.

EvaluateTensor[g, {u}, {0, 2}, {{}, {2}}]

It may be helpful to ``flatten'' a tensor, by which an $(r,s)$-tensor on a vector space of dimension $n$ is converted to a list of length $n^{r+s}$.  There are a few commands associated with this and related operations.  First let us convert a list to a tensor.

Aten  =  Vec2Ten[{a11, a12, a21, a22}, {0, 2}, 2]

Now let us convert this back to a list.

Avec  =  Ten2Vec[Aten, {0, 2}, 2]

Specific entries can be grabbed as well.  For example, one may want to grab from a long list the element corresponding to a certain tensor index.  The following manipulations use the Mathematica Sequence command.

IVec2Ten[3, {0, 2}, 2]

Aten[[Sequence @@ %]]

One can also go the other way.

ITen2Vec[{1, 2}, 2]

Avec[[%]]

In the above commands, the ``I'' stands for ``index,'' reflecting the fact that these commands have to do with manipulation of indices.

Lie differentiation

One can Lie differentiate tensors of arbitrary type.  The command takes as arguments, the tensor being Lie differentiated, the vector field with respect to which differentiation is being done, a list containing the coordinates, and the type of the tensor.

X  =  {-y, x}

q  =  {x, y}

LXg  =  LieDerivative[g, X, q, {0, 2}]

Note that the vector field is Killing.

Changes of coordinate

Coordinate changes can be done symbolically.  First let us do a linear change of coordinates (i.e., a change of basis).  We will work with the existing tensor g.  One requires a change of basis matrix, and this is defined as follows.  Suppose the existing basis is $\{e_1,\dots,e_n\}$ and the new basis is $\{f_1,\dots,f_n\}$.  One may then write $f_i=P^j_ie_j$ (using the summation convention) for some invertible $n\times n$ matrix $\mat{P}$.  The change of basis matrix in Mathematica is defined so that P[[i,j]] is $P^i_j$.  Thus, for us, if the new basis is

f1  =  {1, 1}; f2  =  {0, 1};

then we should define

P  =  Transpose[{f1, f2}]

We then have

ChangeBasis[g, P, {0, 2}]

Now let us change coordinates.  We already have the coordinates q defined above.  Let us introduce new coordinates which are polar coordinates.

qp  =  {r, theta}

What is needed for the change of basis function is the original coordinates expressed in terms of the new coordinates.

qofqp  =  {r Cos[theta], r Sin[theta]}

Now we may make the change of coordinates by providing all of the above data, along with the type of the tensor.  For example, for the vector field we have

Xp  =  
      Simplify[ChangeCoordinates[X, q, qp, qofqp, {1, 0}]]

Also the metric.

gp  =  
      Simplify[ChangeCoordinates[g, q, qp, qofqp, {0, 2}]]

The concept of a Killing vector field is coordinate invariant.

LieDerivative[gp, Xp, qp, {0, 2}]

One can also change coordinates for the Christoffel symbols of an affine connection.  We shall do this for the Levi-Civita connection for the Riemannian metric g used above, noting that its Christoffel symbols are zero in Cartesian coordinates.

conn  =  Table[0, {i, 2}, {j, 2}, {k, 2}]

Simplify[
      ChangeCoordinates[conn, q, qp, 
        qofqp, "Affine Connection"]]

One may recognize these as the Christoffel symbols for the standard metric in polar coordinates.

There is also a Jacobian function included.  The Mathematica Jacobian manipulations require too much setup to use conveniently.

TheJacobian[qofqp, qp]