Vector and Tensor Algebra

by

Rolf Sulanke

Started June 26,  2012
Version 3,  May 14, 2015
Finished May 26, 2017
Created with Mathematica v. 8.0.1, ..., 11.1.1

Introduction

This notebook and the package tensalgv3.m contain besides of elementary vector algebra  a complete tensor algebra as a part of affine geometry. In Mathematica there doesn’t exist a built-in Tensor definition, but some tensor operations, like e.g. TensorProduct are built-in. In the notebook I define a symbolic  tensor   object which admits  to introduce covariant, contravariant and mixed type tensors. The concept can be used  for symbolic calculations as well as for calculations with tensors whose coordinates are numerically or functionally specified.  Tensor products, the wedge product as the base of exterior algebra, and contraction of tensors are introduced. Their properties are deduced and compared with the corresponding Mathematica built-in tensor functions. Since also in the commercial packages of  Harald H. Soleng, Tensors in Physics, and Steven M. Christensen, MathTensor,  an abstract tensor concept is missing I hope that the notebook and the package presented here deliver useful tools for applications of Mathematica to problems in algebra,  geometry and physics needing tensor calculus. The analytic part of tensor calculus will be treated in connection with differential geometric applications in future notebooks.

This notebook starts with  basic concepts of vector and matrix calculus as far as they are independent of scalar products, developed and used in  affine or projective geometry. These concepts are contained and treated more detailed in the Mathematica notebook vectoralgebra.nb.  The present  notebook is a revision and enlargement of  the notebook vectoralgebra.nb now completed with basic concepts of tensor algebra.  The  new defined  Mathematica functions are collected in the package tensalgv3.m which contains the package vectorcalc.m.   The theoretical background can be found in  textbooks of  linear algebra, e.g. in [Mal], [OSI] and [OSII].

For all comments, in particular for hints about possible failures or deficiencies, the author will be very grateful. Please send all comments to the address mentioned in the References.

Keywords

vector objects, null vector, basis vectors, covector, random vector,  linear forms, matrix, random matrix, rank, transposed matrix, tensor,  symmetric, antisymmetric, covariant, contravariant, mixed type, symmetrize, alternate, tensor product, p-forms, p-vectors,  contraction, epsilon tensor, basis tensors, tensor coordinates, wedge product,  strict coordinates, Array.

Hints

Initialization: Before starting interactive work with this notebook,  read the next section carefully and follow the instructions given there!

Hyperlinks to other places of this notebook are marked by colored text, e.g.  address. A mouse click on such text brings you to the linked place, and by the toolbar  button ◂ (“Go Back”) you return to the subsection containing the link.

Blue text marks places at which the marked concepts are defined or emphasized.

Warnings are contained in subsection 1.1; read it!

New versions  of the present notebook and the package tensalgv3.m may appear on my homepage, control it!

Copyright

This notebook, the linked notebooks, and the accompanying packages are  public. Authors who intend to publish a changed or completed version of them should do it under their own names, and under the condition that they cite the original notebook with the Internet address or other source where they got it. I am not responsible for errors or damages originated by the use of the procedures contained in my notebooks or packages; anybody who applies them should test carefully whether they are appropriate for his purposes.

Initialization

A Needed Package

In the notebook we apply many constructs of  linear algebra, defined in Mathematica, or in our new developed package tensalgv3.m.  Before initializing the notebook ensure that this package is in your working directory.

Preparation: Path of the Working Directory

Enter the path of your working directory before first using this notebook on your computer.  If this is done once, and if the package  "tensalgv3.m"  is in this directory,   you may start calling the initialization as described in the next subsection.

At the moment, DO NOT CONFIRM THE EVALUATION OF THE INITIALIZATION CELLS !

If the result of the following command

Directory[]

does not coincide with the working directory, e.g. "C:\Eigene Dateien\mymath", containing the needed packages, insert the Path of your working directory in the next command line (corresponding to your  operating system) and activate it first, without calling the initialization:

For Windows:

SetDirectory["E:\\mymath\\diffgeo\\riemann"]

$Path

For Linux:

SetDirectory["~/mymath/diffgeo/DiffGeoCourse"]

"/home/rolf/mymath/diffgeo/riemann"

Now give the cell corresponding to your  operating system the properties "Cell Evaluatable" and "Initialization cell" (Menu Cell/Cell Properties), and inactivate these properties for the corresponding cells of the other operating system. If this is done, save the notebook. Next time you may start the notebook directly with the evaluation of the initialization, as follows:

Initialization

IMPORTANT!

Before starting to work interactively with the notebook, first do the following three steps:

1. Activate Kernel/Evaluation/Evaluate Initialization from the menu.

Needs["tensalgv3`"]

2. Inactivate the Menu item “Evaluation/Dynamic Updating Enabled “

If this property is enabled, some functions, e.g. TensorRank, don’t work.

3. Set the dimension:

In most applications of  vectors and tensors an underlying space or a manifold is given. Its dimension dim is set in the next cell:

dim = 3

Remark 
The global constant dim appears in many functions as default argument of the dimension; without fixing it as a positive integer many error messages will be created, and they will not work. The order in which the two steps are carried out is deciding: In case you fixed the dimension dim already before evaluating the initialization cells, it will be fixed for all the concepts as the default value of this variable.  Fixing it after the Initialization makes it possible to change the value of dim any time, if wanted.

Test: dim

?dim

Context[dim]

vec[x]

dim = 6

?randomv

randomv[]

We reset the dimension;

dim = 3

1.  List of Symbols and their Usages

Remark 
For the notations  Mathematica commonly uses the convention that the names of built-in functions or objects start with a capital letter. In my notebooks and packages I name all the newly introduced functions starting with small letters.

1.1.  Algebraic Operations for Functions

1.1.1 Addition of Functions

The built-in function Plus acts on the values; it makes no sense to apply it to the functions:

h =Plus[f,Cos,Sin,Pi ]

h[x]

%/.x-> Pi

Of course, one may define the sum of the functions for any given family of summands using the values for the argument pattern x:

Clear[h];h[x_]:=Plus[f[x],Cos[x],Sin[x],Pi ];h[x]

In mathematics, in particular, in defining tensors, one needs operations with functions as objects, not only with their evaluations. Therefore we added in tensalgv3.m an operation plus  for the addition of  functions:

h[Pi]

?plus

plus[f___][x__]:= Module[{ff,n,i,hh={}},ff=Flatten[{f}];n=Length[ff];For[i=1,i<n+1,i++,If[NumberQ[ff[[i]]],AppendTo[hh,ff[[i]]],AppendTo[hh,ff[[i]][x]]]];Sum[hh[[i]],{i,n}]]

The Attributes of plus and of Plus:

Attributes[plus]

Attributes[Plus]

plus yields a function with arbitrary many variables; expressions defined by plus are functions only if each summand can be evaluated for the same entered variables and has values in the same Abelian domain, in which the commutative operation + (Plus) is defined.

For  details of this problem and analogous problems concerning the multiplication of functions see the notebook [OpF].

Tests of plus

plus[f,7,g,Cos][x]

plus[{f,7,g,Cos}][x]

g=Pi&;g[y]

h= plus[Pi];h[x]

h= plus[ ];h[x]

Clear[h];h=plus[3,Pi];h[x]

Clear[h,f,g];h=plus[f,g];h[x]

Clear[h,f,g];h=plus[f,Pi];h[x]

Clear[h,f,g];h=plus[Pi,g];h[x]

Clear[h];h=plus[];h[x,y]

Clear[h];h=plus[f];h[x,y]

Clear[h];h=plus[ Cos];h[x]

Properties and Applications

The addition of vectors corresponds to the addition of the coordinate functions defined by plus:

dim = 3;Plus[vec[x],vec[y]]==vec[plus[x,y]]

?vec

vec[plus[z,-x]]

vec[plus[z,-5]]

The same for the addition of matrices:

Plus[matrix[x],matrix[y]]==matrix[plus[x,y]]

matrix[plus[z,Pi]]//MatrixForm

The commutativity of plus is confirmed:

plus[f,g] == plus[g,f]

The associativity of plus is  confirmed too:

Clear[g,f,h];Simplify[ plus[ plus[f,g],h]== plus[g,plus[f,h]]]

The associativity for  explicit functions plus[...][x] is  valid:

Clear[g,f,h];Simplify[plus[ plus[f,g],h][x] == plus[g,plus[f,h]][x]]

1.1.2. Multiplication of Functions with Numeric Quantities

The package tensalgv3.m contains the cell

Unprotect[Times];Times[h_?NumericQ,f_][x___]:=Times[h,f[x]];Protect[Times];

which admits the multiplication of a function with a scalar. This useful property we need for the multiplication for tensors with scalars. More general, we defined to this purpose an auxiliary function times;

?times

times[a,f][x] is a function evaluating always to a *f[x]:

fc = times[a,Cos]

fc[x]

fc[Pi/5]

Times[a, Cos] isn't t a function :

fcc=Times[a,Cos]

fcc[Pi/5]

Clear[fc,fcc,f,g,H,h]

Example. Fields

In differential geometry and physics the name “field” means a quantity depending on coordinates; this concept has nothing to do with the fields used in algebra.  Here is an example of a general vector field defined on the Euclidean space (E^3)  with coordinates {x,y,z}:

vfield[x_, y_,z_] := vec[f[x,y,z]]

vfield[x,y,z]

ww[x_, y_,z_] := vec[f[x,y,z]]/.{f[x,y,z][1]->x^2+y^2,f[x,y,z][2]-> x*y*z,f[x,y,z][3]->0}

ww[x,y,z]

z*ww[x,y,z]

The next cell gives nonsense:

(z*ww)[x,y,z]

We want

wwz= times[z,ww]

wwz[x,y,z]

Also matrices or  Arrays can serve to define fields:

tff[u_, v_] := matrix[f[u, v]]

tff[u, v] // MatrixForm

Clear[vfield,ww,wwz,tff]

1.1.3. Introducing Null[ ]

?Null

Applying the tensor functions there  may appear  undefined expressions like Null[ ]. Thus we entered in tensalgv3.m  the following enhancement of Null:

Unprotect[Null]; Null[___] := Null;  Protect[Null];

Null[1,2][3]

%== Null

Null == Null[]

1.2.  Symbols in the Package tensalgv3.m

To see the usages click on the name!

?tensalgv3`*

Remark. Wedge is built - in as a symbol name only; we gave it a mathematical meaning identifying it with wedge

??Wedge

Attributes[Wedge]

Context[Wedge]

?wedge

See details in the next subsection; first read Section 4.3.

??Tensor

But mark Tensor and press F1: Read a lot about the properties and operations of objects not being defined in Mathematica!

1.2.1. Wedge Products

Attention! In the notebook appear several functions defining a wedge product:

?wedge

Context[wedge]

The symbol Wedge doesn’t have any mathematical meaning in Mathematica:

?Wedge

Context[Wedge]

Attributes[Wedge]

We give Wedge the meaning of the wedge product defined in tensalgv3.m:

Attributes[Wedge]={Flat, OneIdentity,Protected}

Wedge[x___] := wedge[x]

The context remains System`

Context[Wedge]

Attributes[Wedge]

Attributes[wedge]

Wedge[tstb[{1},{}],tstb[{2},{}],tstb[{3},{}]]

coords[%]

The only advantage is that we may use now the traditional notation

tstb[{1},{}]⋀tstb[{2},{}]⋀tstb[{3},{}]

%== wedge[tstb[{1},{}],tstb[{2},{}],tstb[{3},{}]]

Other more special wedge products are wedgeform[w1,w2,...] of linear forms and wedgevector[x1,x2,...] of vectors x1,x2,.. .returning the corresponding antisymmetric covariant or contravariant tensors. Built-in is the function

?TensorWedge

Attributes[TensorWedge]

We wanted to see how TensorWedge has been defined  as a built-in function. We try

ClearAttributes[TensorWedge, ReadProtected]

??TensorWedge

Unfortunately, this Output remains unclear for a user not being an insider

Attributes[TensorWedge]

TensorWedge[stb[1],stb[2],stb[3]]

Normal[%]

%[[1,2,3]]

1.2.2.  Further Examples

Test other examples of the table in 1.2 above!

vector[x]

%== Sum[x[i]*stb[i],{i,dim}]

stb[3]

?noprops

noprops acts on matrices (= List of Lists ) only:

noprops[x,y,2*x]

{{x},{y},{2*x}}//MatrixForm

noprops[{{x},{y},{2*x}}]

Indeed,

y==x*(y/x)

wl = {vec[x], vec[x]+ vec[y], null[],2* vec[z]}

noprops[wl]

wl = {vec[x], 3*vec[x], vec[y], null[], vec[z]}

noprops[wl]

Clear[wl]

1.3.  Symbols in the Global Context

Here is the List of all symbols currently valid in the Global context; one may renew it at any time:

?Global`*

2. Vectors and Linear Forms

2.1. Vectors, Bases, Coordinates

We consider finite-dimensional vector spaces over an arbitrary field, mostly the field of real or complex numbers.  For the mathematical concepts used here see any textbook about linear algebra or analytic geometry, e.g.  [OSI].

2.1.1. The Definition

First we set the dimension of the vector space:

dim = 3;

Our first definition of a vector is very simple, namely  an Array with a predefined dimension

vec[x_,n_:dim]:=Array[x,n];

?vec

vec[x]

The coordinates x[i] are supposed to be elements of  the given field. We remark that this isn’t a mandatory property of  vec following from its definition in Mathematica;  it is an additional convention. Mathematica has an object Vector allowing to specify the basic field  as a domain, see below.

Head[vec[x]]

In Mathematica a tensor is usually defined by its Array of coordinates :

?Array

Head[Array[a,{3,4}]]

The evaluation of the next cell yields an unsatisfactory result since (a+b)[i] isn’t defined

vec[a] +vec[b] == vec[a+b]

Use plus to get the correct result

vec[a] +vec[b] == vec[plus[a, b]]

The reason is that plus, but not Plus, defines  the addition of functions, see Subsection 1.1.

The dimension must be a non-negative integer; it is the number of coordinates of the vector in dimension dim. The 0-dimensional vector space contains only one vector, the empty tuple:

vec[x,0]

Vectors of the same vector space may be added:

vec[a]+vec[b]

With respect to the addition the vector space is an Abelian group

vec[a] +vec[b] == vec[b] + vec[a]

Its neutral element is the null vector

?null

null[]

vec[a] +vec[-a] == null []

null[0]

Vectors may be multiplied by scalars, assumed to be elements of the basis field:

3*vec[x]== vec[3*x]

Warning! Multiplication of arguments of vec using Times may generate nonsense! 
Use times instead.

Example:

Replace 3 by the unspecified factor y:

y*vec[x] ==vec[y*x]

Simplify[y*vec[x]== vec[y*x]]

This is a senseless expression since (xy)[i] isn’t defined.

%/.y-> 0

0*vec[x]==vec[0*x]

is senseless too.

Applying times yields correct results:

?times

0*vec[x]==vec[times[0,x]]

y*vec[x]==vec[times[y,x]]

Compare the Attributes:

Attributes[times]

Attributes[Times]

2.1.2. Checking the Vector Space Axioms

Vectors may be multiplied by scalars. The result is a vector of the same dimension, of course:

Defining

b[i_] := a*x[i]

we  obtain

a*vec[x]

The result is a vector of the same dimension over the same field:

vec[b]==a*vec[x]

Tacitly Mathematica assumes the field to be commutative and not skew:

a*vec[x]== vec[x]*a

Vectors of the same dimension may be added adding their coordinates:

c[i_] := a[i] +b[i]

vec[c] ==  vec[a] + vec[b]

vec[c]

Clear[c]

vec[c]

The neutral element is the null vector

null[]

vec[a] -vec[a]

Solve[vec[a]+vec[x] == null[], vec[x]]

Thus the vectors of the same dimension form an Abelian group. Also the other axioms of  a vector space (see [OSI], § 4.2) are satisfied:

Simplify[(vec[x] +vec[y])*a == vec[x]*a +vec[y]*a]

Simplify[vec[x]*(a+b) == vec[x]*a+vec[x]*b]

Simplify[(vec[x]*a)*b == vec[x]*(a*b)]

vec[x]*1 == vec[x]

Lemma. vec[x] *a== null[] if and only if a==0 or vec[x]== null[]:

Solve[vec[x]*a== null[], Append[vec[x],a]]

In the next equation Mathematica silently assumes that a is not zero.

Solve[a*vec[x]== null[], vec[x]]

(vec[x]/.Flatten[%] )== null[]

Solve[z*vec[x] == vec[x], z]

2.1.3. Bases and Coordinates

The coordinates of vec[x] are assumed to be the coordinates  with respect to the standard basis, consisting of  unit vectors of the coordinate axes:

?stb

stb[2]

Each vector is a linear combination of the standard basis with its coordinates as uniquely defined  coordinates

vec[x] == Sum[stb[i]*x[i],{i,dim}]

The uniqueness follows from

Solve[vec[x] == Sum[stb[i]*y[i], {i,dim}], vec[y]]

?basis

basis[IdentityMatrix[dim]] == IdentityMatrix[dim]

basis[IdentityMatrix[dim]]

% == Table[stb[i],{i,dim}]

Warning: In this case the result of basis[] coincides with the argument; eventually it may happen  that the order of the rows is changed. 
We consider a sequence of 10 random vectors of dimension 3:

?randomv

dim = 3;btn = Table[randomv[3, {-5,5}],{10}]

rank[btn]

Here is a random basis of the 3-dimensional real vector space:
(Ignore the messages)

bt =basis[btn]

Clearly, with probability 1 any dim of dim - dimensional random vectors form a basis of the vector space. Orthogonality is not required; the Euclidean or metric properties of  Euclidean vector spaces can be considered in another notebook. Here is the matrix of the Dot products of the random basis vectors:

Table[bt[[i]].bt[[j]], {i,dim},{j,dim}]//MatrixForm

Their determinant is

Det[bt]

The rows of the matrix bt form a basis of the 3-dimensional vector space,  they are the coordinates of the new basis vectors bt[[i]] with respect to the standard basis {stb[j]}

Sum[bt[[1,j]]*stb[j],{j,dim}] == bt[[1]]

Sum[bt[[2,j]]*stb[j],{j,dim}] == bt[[2]]

Sum[bt[[3,j]]*stb[j],{j,dim}] == bt[[3]]

The standard  basis may be expressed with respect to the basis bt[[i]] by the inverse matrix

?Inverse

ct = Inverse[bt]

Simplify[ct.bt]//MatrixForm

The small numbers in this matrix are caused by the  numerical calculation; they can be smoothed to zero applying Chop:

Chop[%]//MatrixForm

Table[Chop[Sum[bt[[i,j]]*ct[[j,k]],{j,dim}]],{i,dim},{k,dim}]//MatrixForm

The coordinates of vec[x] with respect to the basis bt[[i]]  we denote by y[i]. We have two basis representations of vec[x], the standard one:

vec[x]==Sum[stb[i]*x[i],{i,dim}]

The coordinates y[j] of vec[x] with respect  to the basis {bt[[i]]} are uniquely defined as the solution of the following equation:

Solve[vec[x]==Sum[bt[[j]]*y[j],{j,dim}],vec[y]]

vy =vec[y]/.Flatten[%]

On the other hand, , we may insert the representations of bt[[j]] with respect to the standard basis

bt[[j]] ==  Sum[bt[[j,i]]*stb[i],{i,dim}]

into the equation

vec[x]==Sum[bt[[j]]*y[j],{j,dim}]==Sum[Sum[stb[i]*bt[[j,i]],{i,dim}]*y[j],{j,dim}]

Interchanging the order of summation in the double sum at the right side of this equation

vec[x] ==Sum[stb[i]*Sum[y[j]bt[[j,i]],{j,dim}],{i,dim}]

and comparing the coordinates of this vector on both sides, we obtain

x[i] == Sum[y[j]*bt[[j,i]],{j,dim}]

Now apply the inverse transformation:

Simplify[Chop[Table[Sum[x[i]*ct[[i,k]],{i,dim}]==Sum[y[j]*bt[[j,i]]*ct[[i,k]],{i,dim},{j,dim}],{k,dim}]]]

We obtain the vector vec[y] with respect to the basis {bt[[i]]} expressed by the coordinates of vec[x]  as linear functions of its standard coordinates:

Solve[%,vec[y]]

Clearly, it coincides up to numerical disturbances with the coordinate vector vy calculated above :

Chop[Simplify[vy- vec[y]/.Flatten[%]]]

Comparing the formulas for the transformation of the basis vectors and the corresponding coordinates:

bt[[j]] ==  Sum[stb[i]*bt[[j,i]],{i,dim}]

y[j]== Sum[x[i]*ct[[i,j]],{i,dim}]

one recognizes the following 

Rule. Under a linear basis transformation the corresponding vector coordinates are transformed transposed inverse (also called contragredient) to the basis vectors.

Using matrix multiplication as implemented in Mathematica this rule can also be verified as follows:

{bt[[1]],bt[[2]],bt[[3]]}== bt.{stb[1],stb[2],stb[3]}

We again apply Chop to smooth numerical disturbances:

Chop[Simplify[vy == Transpose[Inverse[bt]].vec[x]]]

Clear[vy,bt,btn,ct,y]

2.1.4. Appendix. Other Vector Definitions

The Obsolete Definition “vector”

In tensalgv3.m exists yet the object

?vector

vector is the same as vec; it is a long name for the same object only.

?vec

After evaluating vector[x] with the symbol x one may replace x with a function. e.g.

vector[x]/.x-> ff

Clear[a,b];vector[plus[a,-b]]== vector[a] -vector[b]

Also vector fields or vector functions) may be defined:

vf[u_,v_] :=vector[f[u,v]]

vf[u,v]

Derivatives can be calculated:

D[vf[u,v],{{u,v}}]

Grad[vf[u,v],{u,v}]

%==%%

Changing the Head of Arrows

The built-in Array function offers the possibility to change the Head  We define a new vector function

Clear[vvector];vvector[a_,d_:dim]:= Array[a, d,{1},vector]

vvector[a]

Head[vvector[a]]

Length[vvector[a]]

Unfortunately, with this change of the Head, many functions or operations valid for Arrays are not valid for vvectors anymore:

ArrayQ[vvector[a]]

TensorQ[vvector[a]]

TensorQ[ Array[a,dim,{1}]]

TensorDimensions[vvector[a]]

TensorDimensions[ Array[a,dim,{1}]]

Clear[a,b,c];c[i_] := a[i]+b[i]

vvector[c]

Simplify[vvector[c]== vvector[a] +vvector[b]]

Simplify[vvector[plus[a,b]]== vvector[a] +vvector[b]]

?Cross

Cross[vvector[a],vvector[b]]

With this definition Cross does not work, in difference to

Cross[vec[a],vec[b]]

he same takes place with the determinant function:

Clear[a,b,c];Det[{vvector[a],vvector[b],vvector[c]}]

Det[{vec[a],vec[b],vec[c]}]

Head[{vvector[a],vvector[b],vvector[c]}]

MatrixQ[{vvector[a],vvector[b],vvector[c]}]

To avoid such errors problems we try to use instead of vec the elements of

The domain Vectors

?Vectors

is built-in Mathematica. but the following  statement is not confirmed:

dim

vec[x] ∈ Vectors[3]

With an appropriate assumption the property will be confirmed:

Assuming[Element[x[i_],Reals],Simplify[vec[x] ∈ Vectors[3]]]

Simplify[vvector[x] ∈ Vectors[3]]

Assuming[{x∈ Vectors[3,Reals], y∈ Vectors[3,Reals]},x+y∈ Vectors[3,Reals]]

The triples must have complex numbers as members to be confirmed as vectors:

null[]∈ Vectors[3]

{1.,1+I,1-I}∈ Vectors[3]

Working with symbolic objects like vector functions etc. one has to set the corresponding assumptions:

vec[f[x]]

Assuming[f[x][i_] ∈ Reals,Simplify[vec[f[x]] ∈ Vectors[3]]]

Many functions for vectors in Mathematica work only if the Head of the vectors is List; therefore we will not change this Head and

Remove[vvector]

2.2. Covectors and Linear  Forms. The Dual Basis

2.2.1. Linear Maps. Linear Forms. Covectors

dim = 3;

The reason for introducing the covectors is that they are the abstract concept of the linear forms on the vector space.  A function w: V ↦ K on the vector space V with values in the field K (or, more general, a map into a vector space W over K) is said to be linear if it has the properties

w[x+y] == w[x]+w[y],w[x*α] == w[x]*α,
 x,y ∈ V,α ∈ K.

Linear functions with values in the field K of the vector space are named linear forms.  One easily proves
Proposition. The set L[V, W] of all linear maps of the vector space V into the vector space W over the same field K is itself a vector space over K with respect to the argument-wise addition of functions and multiplication with scalars α∈K. 
If dim[V] = n and dim[W] = m, then dim[L[V, W]] = n*m.
Proof. In the package tensalgv3.m is introduced

?linform

?lf

where the covectors are defined as

?covector

linform[a][vec[x]]

The relation between linear forms and covectors is

linform[a][vec[x]]== covec[a].vec[x]

covec[a] is the tuple of the coordinates of lf[a] with respect to the standard basis. 
The algebraic operations of linear forms correspond to the vector operations of the covectors.

Consider two linear forms lf[a], lf[b] and its sum

Clear[a,b,x];sab := plus[lf[a],lf[b]]

sab[vec[x]]

Simplify[sab[vec[x]+ vec[y]] == sab[vec[x]] + sab[vec[y]]]

Simplify[sab[α*vec[x]] == α*sab[vec[x]]]

Thus sab is linear too. Analogously for the product with a scalar:

g = lf[times[u,a]];g[vec[x]]

Simplify[g[vec[x]+ vec[y]] == g[vec[x]] + g[vec[y]]]

Simplify[g[α*vec[x]] == α*g[vec[x]]]

Thus on L[v,w] the structure of a vector space over the field K is well defined. We show

Lemma. A linear map on  w a finite dimensional vector space V is uniquely defined by its values on  a basis of V. 
Proof.  The linearity implies

w[∑ stb[i]*x[i]]  ==  ∑w[stb[i]]*x[i].                (1)■

We return to the case of the one-dimensional vector space W=K.

w=linform[a];w[Sum[stb[i]*x[i],{i,dim}] ] ==  Sum[w[stb[i]]*x[i],{i,dim}]

w[vec[x]] == Sum[w[stb[i]]*x[i],{i,dim}]

Remember, x[i] are the coordinates of vec[x]; the formula shows again that w[vex[x] is uniquely defined by the values w[stb[i]].

Clear[b];wa[i_] := w[stb[i]];covector[wa]

%== covec[a]

The formulas obtained above for sab[vec[x]] and g[vec[x]] show that the map 
lf[a] -> covec[a] is linear; its kernel is the null-form lf[0&]:

lf[0&][vec[x]]

Solve[Table[lf[a][stb[i]]==0,{i,dim}],vec[a]]

Thus the set L[V,K] becomes a dim-dimensional vector space as stated in the proposition; remember that K can be considered as a 1-dimensional vector space over K. If w in formula (1) maps V into a d-dimensional vector space W, then w[stb[i]] is a d-dimensional vector; for simplicity take dim[W] = dim too, Thus

Clear[w,a];wa =matrix[a]; w[v_List] := wa.v;w[vec[x]]

matrix[a]//MatrixForm

Formula (1) is confirmed again:

w[vec[x]]== Sum[w[stb[i]]*x[i],{i,dim}]

w[stb[1]]

These formulas show that L[V,W] with the operations defined above is  a vector space over K isomorphic to the vector space of matrices  with m= dim{W] rows and n = dim[v] columns; thus its dimension is n*m (== 9 in our case).■

2.2.2. Transformations of Bases and Coordinates

The essential difference between vectors and covectors is the transformation behavior of their coordinates which we will consider now. Since these objects are modelled in Mathematica by formally identical functions, we distinguish  vectors and covectors giving them distinct names.

As in section 2.1 we take a second basis in the vector space V choosing a random basis 
{b[i]}:

dim=3;

bt = randommatrix[3]

We define the new basis vectors b[i] as the vectors whose coordinates with respect to the standard basis are the rows of bt:

vb[i_Integer] := bt[[i]]

Table[vb[i],{i,dim}] == bt

With probability 1 the matrix bt has rank 3:

MatrixRank[Transpose[bt]]

Therefore the vectors {vb[i]} form a basis of the vector space V. We get the transformation matrix mb by

Clear[b];matrix[b]

Table[ b[i,j] = bt[[i,j]],{i,dim},{j,dim}];mb=matrix[b];

mb== bt

Thus the basis vectors are transformed according to 
Formula (1):

Table[vb[i] == Sum[b[i,j]*stb[j],{j,dim}],{i,dim}]

or

Table[vb[i],{i,dim}] ==mb.Table[stb[i],{i,dim}]

Now, any vector vx ∈ V has two basis presentations:

vx ==Sum[x[j]*stb[j], {j,dim}]== Sum[y[i]*vb[i], {i,dim}]

vx = vec[x];

To calculate the y[i] as functions of the x[j] we insert the decomposition of vb[i] with respect to{ stb[j]} into the last formula:

vec[x] == Sum[y[i]*Sum[b[i,j]*stb[j],{j,dim}], {i,dim}]

Interchanging the order of summation gives

vec[x]== Sum[Sum[b[i,j]*y[i], {i,dim}]*stb[j],{j,dim}]

Comparing the components of stb[j] in this equation with the basis representation of vec[x] in the standard basis we obtain

x[j] == Sum[b[i,j]*y[i], {i,dim}]

vec[x] == vec[y].mb

to calculate the coordinates {y[i]] = vec[y]  of the vector with respect to the basis {b[i]} we define

mc =Inverse[mb]

Table[c[i,j] = mc[[i,j]],{i,dim},{j,dim}];mc==matrix[c]

we obtain the corresponding  transformation of the coordinates:

y[j_] := Sum[x[k]*c[k,j],{k,dim}]

vec[y] - Table[ Sum[c[k,j]*x[k],{k,dim}],{j,dim}]

or  expressed with matrix multiplication

Formula (2):

vec[y]== Transpose[mc].vec[x]

Comparing this with the transformation (1) of the basis vectors

and remembering

Formula (3):

mc== Inverse[mb]

we see that the transformation matrix of the basis vectors is contragredient 
( transposed inverse) to the transformation matrix of the coordinates, or vice versa:

Transpose[mc]== Transpose[Inverse[mb]]

Therefore the vectors are considered as contravariant objects, later we shall identify them as contravariant tensors of order 1.

Often it is convenient to use the Kronecker delta in calculations with matrices or tensors:

? KroneckerDelta

Usually the KroneckerDelta of two elements is abbreviated as

δ[i_,j_] := KroneckerDelta[i,j]

Examples:

δ[1,1]

δ[x,x]

δ[1,1,3,2]

δ[1,2]

Two tests:

Simplify[vec[x] - Sum[vb[i]*y[i],{i,dim}]]

Chop[%]

Simplify[Sum[vb[i]*y[i],{i,dim}] - Sum[stb[i]*x[i],{i,dim}]]

Chop[%]

Thus, up to unavoidable small numerical disturbances, all is correct.

We remember that the evaluation of a linear form corresponds to the Dot product of its coordinate covector with the coordinate vector of the argument:

Clear[w]; linform[w][vec[x]] == covec[w].vec[x]

Proposition. Let {vb[i]}  be a basis of a finite dimensional vector space over the field K. Then there exists a uniquely defined basis {wb[i]} of the corresponding dual vector space, satisfying

wb[i].vb[j] == δ[i,j]

Proof. Clearly, by proposition 2.2.1 the wb[i] are uniquely defined covectors via the corresponding linear forms whose values for a basis are given. Expressing the wb[i] and vb[j] in their coordinates with respect to a fixed basis in V,e.g. the standard basis, we see that the last equation implies

Table[wb[i],{i,dim}] ==  Inverse[Table[vb[i],{i,dim}]]

Thus the covectors {wb[i]} are linearly independent and form a basis in the space of covectors. ■

This basis is named the dual basis to the basis {vb[i]}.  We consider the standard basis of the vector space.  The corresponding dual basis of the dual space is its standard basis, the covectors wb[i] of which are defined by stb[i]:

Table[stb[i].stb[j],{i,dim},{j,dim}]//MatrixForm

Thus, the dual basis of the standard basis is the standard basis of the dual space.

Let {wb[i]} denote the dual basis to {vb}[i].   We define the covectors wb[i], i = 1,2,3, represented in the standard basis stb[i] as

wb[i_Integer] :=Sum[ c[k,i]*stb[k],{k,dim}]

Table[wb[i].vb[j], {i,dim}, {j,dim}]//MatrixForm

Chop[%]

Thus {wb[i]} is the dual basis to {vb[i]} up to small numerical disturbances, and the defining equation show the transformation rule 

Formula (4):

wb[i] =Sum[ c[k,i]*stb[k],{k,dim}]

Table[wb[i],{i,dim}] == Transpose[mc].Table[stb[i],{i,dim}]

The cobases are transformed as the vector coordinates (see Formula (2)), that means contravariantly to the bases in V. It follows that the coordinates of covectors are transformed contravariantly to the cobases, and that means covariantly to the bases of the vector space V. This explains the name giving mentioned at the beginning of this subsection.

The coordinates of covectors with respect to the dual bases are transformed according to the bases of the vector space V (see Formula (1)).

This transformation behavior  is called covariant since it coincides with the transformation of the basis vectors, contrarily to the transformation of the vector coordinates, which we have seen to be contravariant. We emphasize once more that the transformation behavior of the coordinates of vectors and covectors is not inherent in or a consequence of the Mathematica definition of these objects; it is an additional information hinting to the context of their use, and this is the reason that we gave distinct names for objects having the same model in Mathematica.

Clear[ma,mb,bt,w,vb,a,b,u,v,c,wu,wv,x,y]

3. Tensors

In this chapter we introduce a tensor object into Mathematica as the basis for all applications of tensors in algebra, geometry and physics.  The tensor object allows to distinguish covariant, contravariant, and mixed type tensors. In Mathematica an explicit tensor object isn’t built-in, in spite of the fact that many useful tensor operations can be found, see below Chapter 5. They refer to Arrays of special type: the coordinate Arrays of the tensor. Tensors of mixed type aren’t considered. We assume that the user already knows the basic concepts of tensor algebra. A beginner should first read a textbook containing tensor concepts, e.g. [OSII], §§ 10,11, where one finds more hints to the literature.

3..1.  Definition. Examples. Properties

We are going to introduce a tensor as a Mathematica object with Head tensor. It is characterized by three non-negative integers, the covariant degree k, the contravariant degree h, and the dimension d:= dim of the underlying vector space V over the field K, usually the field of real or complex numbers; here degree means the number of covariant respectively contravariant indices. The main part of the definition is the function a yielding the coordinates of the tensor with respect to the standard basis {stb[i]} of the underlying vector space V. The function a depends on two Tuples of indices:

{ii}∈ Tuples[Range[d,k]], {jj}∈ Tuples[Range[d,h]],
{{ii},{jj}} ↦a[{ii},{jj}]∈ K.

In most applications the coordinates are functions with values in K depending on the points x of a manifold.  The traditional notation is (without the  parentheses { }) is

tradf[a[{ii}, {jj}]]

(Superscript[(Subscript[a, {ii}]), {jj}])== (Subscript[a, (Subscript[i, 1...(Subscript[i, k])])]^(                  (Subscript[j, 1...(Subscript[j, h])]) ))

Example.

dim= 3;ta = tensor[a,3,2]

ta[{1,3,3},{2,1}]

tradf[%]

Head[ta]

The Array of the coordinates has a specific form obtained by the function tensorToArray

tensorToArray[ta]//MatrixForm

Vectors, covectors and matrices may be considered as special cases of tensors. ◼

3.1.1. The Definition

?tensor

Here is the definition contained in tensalgv3.m  (with dim -> dimm):

tensor[a_, k_Integer, h_Integer, d_:dim][i_,j_] := Module[{ii,jj},
If[Not[MemberQ[Tuples[Range[d],k],i]&&MemberQ[Tuples[Range[d],h],j]], 
	Print["Incorrect indices!"];Return[]];
a[i,j]]; 

tensor[a_, k_Integer, h_Integer, d_:dim][i_] := 
Print["A tensor needs two arguments being Lists!"];Return[];

tensor[a_, k_Integer, h_Integer, d_:dim][] := 
Print["A tensor needs two arguments being Lists!"];Return[];

To operate with tensors the following rules and functions are defined in tensalgv2.m (for  products see subsections 3.2.3 and 3.3).

Unprotect[Plus]; tensor[a_,k_,l_]+tensor[b_,k_,l_]:= tensor[plus[a,b],k,l]; 
tensor[a_,k_,l_]+tensor[b_,k_,l_,d_:dim]:= tensor[plus[a,b],k,l,d]; 
tensor[a_,k_,l_,d_:dim]+tensor[b_,k_,l_,d_:dim]:= tensor[plus[a,b],k,l,d];Protect[Plus];

Unprotect[Times];-tensor[b_,k_,l_]:= tensor[-b,k,l]; 
-tensor[b_,k_,l_,d_:dim]:= tensor[-b,k,l,d]; Protect[Times];

The type of a tensor is defined as the list of its defining entities: the coordinate function a, the integers {k,h}, where k denotes the number of covariant and h the number of contravariant indices, and the dimension d of the underlying vector space. Often the type of a tensor is understood as the pair {k,h}. A tensor with k=0 is said to be contravariant, and with h=0 it is covariant. The order (or degree) of the tensor is defined to be k+h. The type as a Mathematica function of the tensor is defined in tensalgv2.m as

?type

type[tt_] := Module[{typ, a,k,h,d,ty},
typ[tensor[a_,k_,h_,d_:dim]] := {a,{k,h},d};
If[(Head[tt] === tensor), ty=typ[tt],  Print["Argument is not a tensor!"];ty=Null,
Print["Argument is not a tensor!"];ty=Null];ty];

The coordinates (or components) of a tensor are obtained by the function.:

?coords

coords[tt_] := Module[{ti,k,h,dd,rk,rh, i,j},
If[Not[Head[tt]=== tensor],Print["The argument must be a tensor!"];Return[]];
ti = type[tt]; k = ti[[2,1]]; h =ti[[2,2]]; dd =ti[[3]];
rk = Tuples[Range[dd],k];rh = Tuples[Range[dd],h];
Table[ tt[rk[[i]],rh[[j]]],{i,1,dd^k},{j,1,dd^h}]];

coords[ta]

The following function yields the coordinates of the tensor as an Array r better reflecting the structure of the tensor:

?tensorToArray

The definition in tensalgv2.m is

SetAttributes[tensorToArray,Listable];
tensorToArray[tt_]:= Module[{ti,aa,k,h,dd,bb,i},
If[Not[Head[tt]=== tensor],Print["The argument must be a tensor!"];Return[]];
ti = type[tt];aa = ti[[1]]; k = ti[[2,1]]; h =ti[[2,2]]; dd =ti[[3]];
If[k==0&&h==0, Return[{coords[tt][[1,1]]}]];
Off[Part::take];
 bb[i___]:= aa[{i}[[1;;k]],{i}[[k+1;;k+h]]];
Array[bb,Table[dd,{k+h}]]];

cta =tensorToArray[ta]

The built-in function TensorQ confirms that this Array is considered to be a (not explicitly defined!) tensor in the  sense of Mathematica

TensorQ[%]

TensorQ exists but isn’t explained:

?TensorQ

An object Tensor isn’t built-in:

?Tensor

Constructing a tensor with a given Array of coordinates  use the function

?ArrayTotensor

The type {k,h} can’t be recognized from the given Array; it must be required explicitly:

tb = ArrayTotensor[cta]

without naming k we get per default a contravariant tensor:

type[tb]

To get the tensor ta back we have to define

taa = ArrayTotensor[cta,3]

Of  course, the tensors ta and taa aren’t  confirmed to be equal, since they have formally different coordinate functions:

Simplify[ta ==taa]

But they are equivalent having the same coordinates. This can be controlled by the function

?equi

defined in tensalgv2.m.

Simplify[equi[ta, taa]]

Clearly we get

equi[ta, tb]

since

type[tb]

differs from

type[ta]

already in the covariance degree. But the corresponding Arrays coincide:

ctb = tensorToArray[tb];

Simplify[cta==ctb]

Clear[ta,tb,cta,ctb,taa]

3..2.  Tensors of the same Type Form a Vector Space

3.2.1. Addition of Tensors

Tensors of the same type {k,l} and the same dimension d may be added. Here is a tensor of the same type as

dim=3;

ta=tensor[a,3,2,3]

tb = tensor[b,3,2,3]

The addition of tensors corresponds to the addition of their coordinate functions using plus:

?plus

tc = ta+tb

FullSimplify[ta-tb == tensor[plus[a,-b],3,2,3]]

taa = -ta;type[taa]

ta+taa

%==nulltensor[3,2,3]

equi[ta+taa,nulltensor[3,2,3]]

-nulltensor[3,2,3]

FullSimplify[nulltensor[3,2,3]== -nulltensor[3,2,3]]

equi[nulltensor[3,2,3],-nulltensor[3,2,3]]

type[tc-ta]

Simplify[tc-ta==tb]

equi[tc-ta,tb]

Two tensors whose definitions differ formally may have the same coordinates in the same underlying coordinate system; then they can be considered as equal and can be identified. To test this use the function

equi[ta,tb]

OK, the program can’t decide whether a== b is valid. Now consider

equi[tc-ta,tb]

equi[tc-ta-tb, nulltensor[3,2]]

equi[ta,ta+ nulltensor[3,2]]

Summarizing we have shown
Lemma. Tensors of the same type {k,h} over the same d-dimensional vector space V with coordinates in the same Abelian domain form an Abelian group with the addition as operation; the neutral element is the nulltensor of the same type and dimension.  ▮

We denote this tensor space by (T^((k,h)))(V^d).

3.2.2. Multiplication with a Scalar

We remember that a scalar means  a function with values in the field underlying the vector space or an element of this field. The multiplication of a tensor with a scalar can be done using * or Times. This is possible only after loading the package tensalgv2.m, in which the following enhancement of the operation Times is contained:

Unprotect[Times];Times/: Times[x_, tensor[a_,k_,l_,d_:dimm]]:= tensor[times[x,a],k,l,d];
Times/: Times[x_, tensor[a_,k_,l_]]:=tensor[times[x,a],k,l];Protect[Times];

and a collection of rules regulate the behavior of the new function times.

tx= tensor[x,3,2,3];

Clear[tfx,f];tfx =f[u,v]tx

type[tfx]

tfx[{2,3,1},{1,1}]

Simplify[coords[tfx]== f[u,v]*coords[tx]]

0*tx == nulltensor[3,2,3]

equi[0*tx , nulltensor[3,2,3]]

Simplify[x*nulltensor[3,2,3] == nulltensor[3,2,3]]

equi[x*nulltensor[3,2,3], nulltensor[3,2,3]]

Solve[Flatten[coords[y*tx ]]==Flatten[coords[ nulltensor[3,2,3]]], Flatten[{y,coords[tx]}]]

It follows
Proposition. The set of all tensors of type {k,l} over a vector space of dimension d with coordinates from the base field is a vector space of dimension (d^(k+l)) over  this field with the above defined operations of addition and multiplication with scalars.

Proof. We have seen that the operations are accomplished coordinate wise as in the case of a standard vector space. The dimension  equals the number of coordinates, namely the stated (d^(k+l)).
In our case

Length[Flatten[coords[tx]]]== 3^(3+2)

3^(3+2)

▮

3.2.3. Tensor Basis

Now we will construct a tensor basis in the space of tensors of type (k, h) on a vector space of dimension dim over a field. To this aim we introduce an auxiliary function of four groups of variables, called double test and named cc:

?cc

cc[i_,j_][k_,l_]:= If[(i==k)&& (j==l),1,0,0]

cc[{1,1},{2,1}][{1,1},{2,3}]

cc[{1,1},{2,1}][{1,1},{2,1}]

Now the standard basis consists of the tensors

?tstb

tstb[i_,j_,d_:dim] := tensor[cc[i][j],Length[i],Length[j],d]

where i runs over the k - Tuples and j over the h - Tuples of indices.

dim = 3;

tb = tstb[{1,1},{2,3}]

type[tb]

tensorToArray[tb]

tb[{1,1},{2,3}]

tb[{1,1},{2,1}]

tb[{1,1},{2,5}]

ta = tensor[a,2,1]

ta[{1,2},{3}]

A coordinate decomposition of the tensor ta with respect to the standard tensor basis tstb is defined by

Clear[taa];taa :=   Sum[ta[{i,j},{k}]*tstb[{i,j},{k}],{i,dim},{j,dim},{k,dim}]

taa

The difference of taa and ta  is not evaluated to yield the nulltensor

tadiff=ta-taa

The logical, but not formal equality is not confirmed:

FullSimplify[taa== ta]

The equality of the difference to the nulltensor is not recognized:

type[taa-ta]

The identity of the tensors ta,taa is confirmed by the function equi:

equi[taa,ta]

?tensorbasis

tensorbasis[bb_,k_,h_,d_:dim] :=Module[{ rk = Tuples[Range[d],k],rh = Tuples[Range[d],h],i,j},Flatten[Table[ bb[rk[[i]],rh[[j]]],{i,1,d^k},{j,1,d^h}]]]

baa=tensorbasis[tstb,2,1]

%==tensorbasis[tstb,2,1,3]

type[ta]

Length[
Flatten[coords[ta]]]

Length[baa]

The next simplification is confirmed

Simplify[
Flatten[coords[ta]].baa==taa]

and equi confirms the identity of the coordinates:

equi[taa,Flatten[coords[ta]].baa]

taa[{3,2},{1}]

3.2.4. Traditional Notation

In tensor algebra a very simple, but effective notation for the coordinate transformation is used: covariant indices are written below as Subscripts, and contravariant indices as Superscripts of the coordinate functions.

One may enter expressions as  ((Subscript[a, i2 j3])^(j1 j2 j3)) using the method found under

?Subsuperscript

click on >> and see the Details.

Example.

dim

ta = tensor[a,2,1]

ta[{1,2},{2}]

The following direct setting leads to errors:

ta[{2,3},{2}]/.a[{i_,j_},{k_}]-> ((Subscript[a, i j])^k)

Subsuperscript[a,i j,k h]

The order of the indices has been changed!

Subsuperscript[a,i j,k h]/.{i->2,j->3,k->2,h->3}

The  indices 2, 3  are multiplied.

Unfortunately,  the ambiguity in the mathematical use of superscript as indices, and, at the same time, as powers, doesn’t admit the use  of superscript indices for symbolic calculations.  This problem encountered already  other authors, see the following quotation from the notebook cartan.nb from “Tensors in Physics” of Harald H. Soleng, in which many special applications of tensors in physics may be found. 

Quotation:

Also in this program the Subsuperscript form of the tensor coordinates may not be applied for symbolic calculations:

SWeylInvariant

Sum[(Superscript["C", "ijkl"])(Subscript["C", "ijkl"])" ", {i,4}]

End of the quotation.

The problem of introducing the traditional  sub- superscript notation of tensor coordinates is discussed in Section 5 of the notebook [OpF]. In the following we use this notation for a simple conceptual description of tensor operations, and for the adaption to the standard notation in differential geometry and relativity theory only.  To obtain expressions  calculable in Mathematica one has to use the notation introduced in the definition of tensor[....].
To get the  transition from the input form to the traditional form of the tensor coordinates and vice versa  two simple functions defined in tensalgv2.m are useful, tradf and mathff:

?tradf

tradf[a[{1,2},{2}]]

tradf[x___]:=Module[{xx={x},zz,i,j,a},
If[Length[xx] ==  0,Return[1]];
If[(Head[x]===Symbol)|| NumberQ[x], Return[x]];
zz=Cases[xx,a_[{i___},{j___}]];
If[Length[zz]== 1, Return[trad[zz[[1]]]],
Print["Invalid argument!"];Return[],
Print["Invalid argument!"];Return[]]]

tradf/: tradf[x_+y__] := tradf[x]+tradf[y]; 

tradf/: tradf[z_*x__]:= tradf[z]*tradf[x];

tradf/: tradf[z_/x__]:= tradf[z]/tradf[x];

The function trad is not exported; it does the transformation work:

?trad

trad[a_[{i___},{j___}]]:=Module[{ii = ToString[{i}],jj = ToString[{j}]},
Superscript[Subscript[a,ii],jj]]

?mathff

Clear[mathff];mathff[x___]:= Module[{n=Length[{x}],xx},
If[n==0, Return[1]];
If[Not[MatchQ[x,Superscript[Subscript[a_, i_],j_]]], Print[ "No"];Return[x], xx = mathf[x],xx=mathf[x]];
xx]

mathff/: mathff[x_+y_] := mathff[x]+mathff[y]; 
mathff/: mathff[x_*y_]:= mathff[x]*mathff[y];
mathff/: mathff[z_*x__]:= mathff[z]*mathff[x];
mathff/: mathff[z_/x__]:= mathff[z]/mathff[x];

The function mathf is not exported; it does the transformation work:

?mathf

SetAttributes[mathf, Listable]; 

mathf[Superscript[Subscript[a_,i_],j_]] :=a[ToExpression[i,InputForm],ToExpression[j,InputForm]];

Examples

bij =tradf[c[{i,j},{3,4}]]

FullForm[bij]

bij

mathff[bij ]

mathff[% ]

tradf[]

mathff[]

mathff[x^2]

tradf[x^2]

tradf[x^2]/.x->4

mathff[%/.x-> 4]

tradf[Cos[x]]

mathff[%]

tradf[2*Pi]

mathff[2*Pi]

N[%]

tradf[%]

tradf[c[{1,2},{3}]*a[{4},{5}]+b[{1},{}]]

mathff[%]

tradf[c[{1,2},{}]-c[{2,1},{}]]

mathff[%]

Warning. The following command  doesn’t work correctly, we obtain

c[{2,i,k},{10}]/.a_[{i___},{j___}]->tradf[a[{i},{j}]]

instead of

tradf[c[{2,i,k},{10}]]

ta = tensor[a,2,1,2];

?ta

The coordinates of ta in traditional form are

tradtaacoo=tradf[coords[ta]]

mathff[%]

Consider the formal tensor basis

bbb= tensorbasis[bb,2,1,2]

To arrive the traditional tensor form we have to transpose the upper and lower index sets of the basis elements. In tensalgv2.m we defined the function

?ptind

ptind[(Superscript[(Subscript[a_, i_]), j_])]:= (Superscript[(Subscript[a, j]), j])

ptind[(Superscript[(Subscript[a, "{3, 2}"]), "{2}"])]

tradf applied to the formal tensor basis yields

tradf[bbb]

Here is the formal tensor basis in traditional notation.

Map[ptind,%]

The formal basis decomposition of the tensor ta is

Flatten[tradtaacoo].%

The so-called Einstein sum convention says that, if in a tensor expression the same index say i, appears twice, once up and once down, then this means that the expression is the sum of the given one over the range of the index i, here Range{d], d:dim. Thus the formal basis representation of ta is in  traditional form with the sum convention

(Superscript[(Subscript[a, jk]), i]) (Superscript[(Subscript[bb, i]), jk]) = (Underscript[(Overscript[Σ, d]), i,j,k=1]) (Superscript[(Subscript[a, jk]), i]) (Superscript[(Subscript[bb, i]), jk])  ≐

Sum[a[{j,k},{i}]*bb[{i},{j,k}], {i,2},{j,2},{k,2}]

Sum[a[{j,k},{i}]*bb[{i},{j,k}], {i,2},{j,2},{k,2}]

tradf[%]

We remember the Input Form

taa =Sum[a[{i,j},{k}]*tstb[{i,j},{k},2], {i,2},{j,2},{k,2}]

tradf does not transform this expression :

tradf[Sum[a[{i,j},{k}]*tstb[{i,j},{k},2], {i,2},{j,2},{k,2}]]

The basis decomposition taa of ta is confirmed by equi:

equi[ta,taa]

3.3. The Tensor Product

The tensor product ⊗ is the most general bilinear map between tensor spaces:

⊗: (Ta,Tb)∈(T^((k,h)))(V^d)×(T^((m,n)))(V^d)  ↦ Ta⊗Tb∈ (T^((k+m,h+n)))(V^d).

It is the heart of tensor algebra. In this Section we explain  the definition given in tensalgv2.m and deduce its properties.

3.3.1. The Definition

In the package tensalgv2.m the function tensorproduct is defined:

?tensorproduct

Warning! Since in Mathematica the definition of tensors is missing the tensor product we have to define now differs from the built-in  function TensorProduct. See also  Section 5.2 below.

?TensorProduct

Here is the definition of the tensor product contained in tensalgv2.m:

First we defined the coordinate function tp[ta,tb] of the tensor product of two tensors ta, tb serving as an auxiliary function in the definition of the tensor product:

?tp

tp[ta_tensor,tb_tensor][i_List, j_List]:=Module[{ka,kb,ha,hb},
ka =type[ta][[2,1]];ha =type[ta][[2,2]];
kb =type[tb][[2,1]];hb =type[tb][[2,2]];
ta[Part[i,1;;ka],Part[j,1;;ha]]*tb[Part[i,ka+1;;ka+kb],Part[j,ha+1;;ha+hb]]];

This function and the not exported function tenpro are the kernel of the definition; tenpro is the tensor product of two tensors.

tenpro[ta_,tb_] := Module[
{ka,kb,ha,hb,dd},
If[Not[(Head[ta]===  tensor)&&(Head[tb]===  tensor)], Print["Incorrect arguments!"];Return[],
ka=type[ta][[2,1]];ha=type[ta][[2,2]];dd=type[ta][[3]];
If[Not[type[tb][[3]]== dd],Print["Different dimensions!"];Return[]] ;
kb=type[tb][[2,1]];hb=type[tb][[2,2]];
If[ta == nulltensor[ka,ha,dd], Return[nulltensor[ka+kb,ha+hb,dd]]];
If[tb == nulltensor[kb,hb,dd], Return[nulltensor[ka+kb,ha+hb,dd]]];
If[(ka+kb==0)&&(ha+hb==0), Return[tensor[type[ta][[1]]*type[tb][[1]],0,0,dd]]];
tensor[tp[ta,tb],ka+kb,ha+hb,dd]]
];

The definition of tp and thus also the tensorproduct assume that all the factors are tensors with the same underlying vector space of dimension d, with default value d : dim. The iteration of tenpro yields the tensor product:

tensorproduct[ta___]:=Module[{ttt, tt,k,i},
ttt=Flatten[{ta}];    k = Length[ttt];
If[k==0, Return[1]];
If[k== 1,If[Head[ta]=== tensor, Return[ta],Print["Incorrect arguments!"];Return[]],
tt = ttt[[1]]];
i=2;While[i<k+1,tt =tenpro[tt,ttt[[i]]];i++]; tt];
Attributes[tensorproduct] ={Flat,HoldAll};

Attributes[tensorproduct]

3.3.2. Examples. Standard and Traditional Notation

dim =3;

ta=tensor[a,1,1];tb=tensor[b,2,1];tc=tensor[c,2,1];

tab =tensorproduct[ta,tb]

tab[{1,2,2},{3,1}]

tradf[%]

For two factors instead of tensorproduct also the infix notation may be used:

tab == ta⊗tb

The next cell  isn’t evaluated:

tab⊗ta⊗tb

type[%]

One has to set parentheses:

(tab⊗ta)⊗tb

%==tensorproduct[tab, ta,tb]

tx is not yet  defined as a tensor, thus we get an error message:

Clear[tx];tensorproduct[ta,tx]

%==Null

Strange:  Output: invisible 0 instead of the programmed  Null!

tx = tensor[x,2,0,4]

The factors of a tensor product must have the same underlying vector space, at least the same underlying d:dim:

tensorproduct[ta,tx]

%

Strange again: invisible  Output: {x,{2,0},4} is obtained instead of the programmed Null!

tensorproduct[{}]

tpr =tensorproduct[{ta, tb,tc}]

type[tpr]

tpr[{1,1,1,3,3},{2,3,1}]

tradf[%]

mathff[%]

tpr[{1,1,1,3,3},{2,4,1}]

Sum[tpr[{i,1,1,2,2},{2,2,i}],{i,dim}]

tradf[%]

mathff[%]

TB =coords[tb]

TensorQ[%]

tradf[coords[tb]]

Now we apply the built-in TensorProduct to the coordinates of ta, tb:

TA = coords[ta];TAB = TensorProduct[TA,TB];

TTab = coords[tensorproduct[ta,tb]];

WARNING. The function coords of the tensorproduct doesn’t coincide with the TensorProduct of the coords of the factors!

TTab == TAB

The order of the flattened Arrays differ:

Simplify[Flatten[TTab]===  Flatten[TAB]]

but the corresponding coordinates coincide:

Sort[Flatten[TTab]]===Sort[ Flatten[TAB]]

We conclude
Lemma. The set of the coordinates of the TensorProduct of coords[ta],.... coincides with the set of the coordinates of tensorproduct[ta,...].

3.3.3. Properties of tensorproduct

Tensors of type (0,0) are treated as scalars:

Clear[a,c,tc];dim = 4;ta = tensor[a,0,0];

ta[{},{}]

tb=tensor[b,2,1]

tc=tensorproduct[ta, tb]

tc[{3,2},{1}]

% /a==  b[{3,2},{1}]

tradf[%%]

Clear[tb,tc,b,c];tb = tensor[b,0,0];

tc =tensorproduct[ta,tb]

tc[{},{}]

tradf[%]

type[tc]

tensorproduct with a nulltensor yields the nulltensor of the corresponding type:

?nulltensor

nulltensor[0,0]

%[{},{}]

tb = tensor[b,2,2]

tb0 = tensorproduct[nulltensor[0,0],tb]

tb0[{1,2},{3,3}]

%%==nulltensor[2,2]

tradf[%%]

ta = tensor[a,1,1]

ta0 = tensorproduct[ta, nulltensor[2,0]]

%==nulltensor[3,1]

ta0 = tensorproduct[nulltensor[0,2],ta]

type[ta0]

%%==nulltensor[1,3]

ta0[{2},{1,1,3}]

tensorproduct[ta0, nulltensor[2,2]]

Clearly, the tensor product is associative, example:

dim = 3;Clear[ta,tb,tc];ta=tensor[a,1,1];tb=tensor[b,2,1];tc = tensor[c,0,2];

tpr = tensorproduct[ta,tb,tc]

tab=tensorproduct[ta,tb]

tabc=tensorproduct[tab, tc]

%==tpr

tabc[{1,2,3},{1,2,3,1}]

tbc = tensorproduct[tb,tc]

tabc1= tensorproduct[ta,tbc]

Obviously, the tensors tabc and tabc1 are formally different:

type[tabc]

type[tabc1]

equi[tabc,tabc1]

The coordinate functions are calculated in another order, they differ:

td=tabc-tabc1

The coordinate Arrays coincide:

equi[tabc,tabc1]

equi[td,nulltensor[3,4]]

This has been tested without setting Attributes[tensorproduct] ={Flat}; thus we could set this Attribute in tensalgv2.m.

?Attributes

Attributes[tensorproduct]

tensorproduct[ta,tensorproduct[tb,tc]]==tensorproduct[tensorproduct[ta,tb],tc]

Simplify[tensorproduct[ta,tensorproduct[tb,tc]]==tensorproduct[ta,tb,tc]]

The tensor product is not commutative, example:

tbca=tensorproduct[tbc,ta]

td1 =tabc-tbca

td1[{1,1,1},{1,3,3,3}]

tradf[%]

Clear[ta,tb,w];ta =tensor[a,0,1];tb =tensor[b,0,1];tw=tensorproduct[ta,tb]- tensorproduct[tb,ta]

tw[{},{1,2}]

tradf[%]

ttw = Table[tw[{},{i,j}],{i,dim},{j,dim}]//MatrixForm

The result is the matrix of the wedge product(see  Section 4.3 below) of the tensors ta, tb, not the nullmatrix in general:

Table[wedge[ta,tb][{},{i,j}],{i,dim},{j,dim}]//MatrixForm

%==%%

Example.
As an exercise one  shows: The elements of the tensor basis are tensor products of vectors and covectors, more precisely:

?tstb

tstb[{i1,...,ik},{j1,...,jh}][{a1,...,ak},{b1,...,bh}] 
== (Subscript[δ, a1]^i1)*...*(Subscript[δ, ak]^ik)*(Subscript[δ, j1]^b1)*...*(Subscript[δ, jh]^bh)

where the Kronecker symbol  (Subscript[δ, a]^i)  =δ[i,a] stands for

?KroneckerDelta

dim = 3;

pv[i_,j_] := tensorproduct[tstb[{i},{}],tstb[{j},{}]]

Table[equi[ pv[i,j],tstb[{i,j},{}]],{i,dim},{j,dim}]

OK! Construct more examples! ◼

3.3.4. Functions as coordinates

To define a tensor with explicit coordinates, first define the symbolic tensor of the wanted type, and then specify the coordinates,  which can be arbitrary objects, e. g. functions.

Example. The metric tensor of the unit sphere has the shape

tg :=tensor[g,2,0,2]

The type of the tensor is

type[tg]

We specify the coordinates:

g[{1,2},{}] = 0;

g[{2,1},{}] = 0;

g[{1,1},{}] = 1;

g[{2,2},{}] =Cos[x]^2;

coords[tg]

To deform the metric a bit, we define a second tensor th of the same type (2,0)

th := tensor[h,2,0,2]

type[th]

with coordinates

h[{1,2},{}] = 0;

h[{2,1},{}] = 0;

h[{1,1},{}] =0;

h[{2,2},{}] =1;

The deformed tensor  is

tgdef[t_] := tg + f[t]*th

type[tgdef[t]]

Here times is a function defining the product of a tensor with a scalar.

coords[tgdef[t]]

The explicitly defined coordinates may have arbitrary, may be distinct Heads:

Head[tg[{2,2},{}]]

tg[{2,2},{}]

Head[th[{2,2},{}]]

Head[tgdef[t][{2,2},{}]]

coords[tgdef[.5]]

Clear[a,b,c,ta,tb,tc];ta =tensor[a,0,2];tb = tensor[Sin[x],0,0];tc=tensorproduct[ta,tb]

type[tc]

tc[{},{1,2}]

%/.x->Pi/3

tradf[%]

Remark.
Tensors with functions as coordinates are named tensor fields; they are studied in  the Tensor Analysis. We intend to implement this into Mathematica in a future notebook. For the algebraic operations of such tensor fields it must be assumed that  the tensor coordinates are functions defined in the same domain, mostly an open region in the space of variables, and have values in the field of real or complex numbers.

3.4. Basis Transformations. Tensor Invariants

3.4.1. Basis and Coordinate Transformations

In Subsection 2.2.2 we considered a basis transformation, see formula (1) there, with  the transformation matrix mb

dim = 3;

Clear[mb,b,i,j];mb =matrix[b]/.b[i_,j_]-> b[{i},{j}];

mb//MatrixForm

Now the coefficients b[i,j] remain symbolical, not explicitly defined elements assumed to belong to the field K.

Clear[vb]; For[i=1,i<dim+1,i++,vb[i] = mb[[i]]]

vb[1]

The new basis vectors have the rows of mb as coordinates with respect to the standard basis:

Table[vb[i] == Sum[b[{i},{j}]*stb[j],{j,dim}],{i,dim}]

Mathematica automatically, and we too, assume:

MatrixRank[mb]

Using the Einstein sum convention we can write the basis transformation in traditional form:

(Subscript[vb, {i}  ])  ≐  (Superscript[(Subscript[b, {i}]), {j}])(Subscript[va, {j}  ])                                                      (1)

with the notations

(Subscript[vb, {i}  ]) ≐ vb[i];    (Superscript[(Subscript[b, {i}]), {j}]) ≐ b[i,j]; (Subscript[va, {i}  ])≐stb[i] ;

From formula 2.2.2.(2) it follows for the corresponding transformation of the coordinates of the vector vx:

y[j] = Sum[x[k]*c[k,j],{k,dim}]

or in traditional form

(y^({j})) = (x^({k} ))(Superscript[(Subscript[c, {k}]), {j}])                                                                   (2)

where the c[k,j] = (Superscript[(Subscript[c, {k}]), {j}]) are the elements of the inverse matrix

mc= Inverse[mb];

c[i_,j_] := mc[[i,j]]

c[1,2]

tradf[%]

Simplify[mc.mb]//MatrixForm

In traditional form this equation is

(Superscript[(Subscript[b, {i}]), {j}])(Superscript[(Subscript[c, {j}]), {k}]) == ((Subscript[δ, {i}])^({k}))                                                               (3)

where δ stands for the Kronecker symbol. Clearly, the basis {(Subscript[va, {j}  ])} is decomposed with respect to the basis {(Subscript[vb, {i}])} by the inverse Matrix mc=matrix[c] = (mb^(-1)):

(Subscript[va, {i}  ])  =  (Superscript[(Subscript[c, {i}]), {j}]) (Subscript[vb, {j}  ])

Now we consider covectors, see Section 2.2.2  Let (wa^({i})) denote the dual basis  of the standard basis (Subscript[ba, [j}]) of the underlying vector space; the defining equations of this  uniquely defined dual basis is

(wa^({i})).(Subscript[ba, [j}]) == ((Subscript[δ, {j}])^({i}))

For the coordinate decomposition of the vector vx = (x^({i}))(Subscript[a, { i}]) it follows

(wa^({i}))(vx) == (x^({i}))

such that the dual basis is transformed contragrediently to the basis of the vector space

(wb^({j})) == (wa^({k}))(Superscript[(Subscript[c, {k}]), {j}])                                                                                                       (4)

where (wb^({j})) denotes the dual basis to (Subscript[vb, {i} ]). Furthermore  the coordinates (Subscript[u, {i}]) of the dual vector 
wu  = (wb^({j}))(Subscript[v, {j}]) = w(a^({i}))(Subscript[u, {i}])
are transformed covariant to the basis vectors  according to

(Subscript[v, {j}])== (Superscript[(Subscript[b, {j}]), {k}]) (Subscript[u, {k}])                                                                                                            (5)

Proposition. The transformation of the bases of a vector space and its dual and the coordinates of  vectors and covectors with respect to these bases obey the  rules (1) - (5).

Proof. This is  only a transcription of the results described  in Subsection 2.2.2, where mb = ((Superscript[(Subscript[b, {i}]), {j}])), mc = (mb^(-1)) = ((Superscript[(Subscript[c, {i}]), {j}])) denote the transformation matrix and its inverse such that (3) is satisfied.   ▮

Now we remember that the chosen bases tensors in the space of tensors of given type (k ,h) are the tensor products of the basis vectors of the underlying vector space and its dual, see the Example in Section 3.3. Thus, using the symbol ⊗ for the tensor product, we may write the basis representation  of a tensor of type (k,h) with respect to the standard basis  in the form

ta = ((Subscript[ta, {(Subscript[i, 1,...,(Subscript[i, k])])}])^({(Subscript[j, 1,...,(Subscript[j, h]) ])} ))(wa^({(Subscript[i, 1])})) ⊗...⊗(wa^({(Subscript[i, k])}))⊗(Subscript[va, {(Subscript[j, 1])}])...⊗(Subscript[va, {(Subscript[j, h])}])                  (6)

The representation with respect to the transformed basis and its dual can be written as

ta = ((Subscript[(OverHat[ta]), {(Subscript[m, 1,...,(Subscript[m, k])])}])^({(Subscript[l, 1,...,(Subscript[l, h]) ])} ))(wb^({(Subscript[m, 1])})) ⊗...⊗(wb^({(Subscript[m, k])}))⊗(Subscript[vb, {(Subscript[l, 1])}])...⊗(Subscript[vb, {(Subscript[l, h])}])

To calculate the transformed coordinates ((Subscript[(OverHat[ta]), {(Subscript[i, 1,...,(Subscript[i, k])])}])^({(Subscript[j, 1,...,(Subscript[j, h]) ])} )) we insert the inverse  transformations of the basis vectors (4) and (1):

(wa^({i})) ==(wb^({n}))(Superscript[(Subscript[b, {n}]), {i}]) ,                               (Subscript[va, {j}])== (Subscript[c, {j}]^(        {m}))(Subscript[vb, {m}])      (7)

into (6) and obtain, since the tensor product is multilinear, the transformation rule of the tensor coordinates comparing the components at the basis tensors

((Subscript[(OverHat[ta]), {(Subscript[m, 1,...,(Subscript[m, k])])}])^({(Subscript[n, 1,...,(Subscript[n, h]) ])} ))== 
((Subscript[ta, {(Subscript[i, 1,...,(Subscript[i, k])])}])^({(Subscript[j, 1,...,(Subscript[j, h]) ])} ))(Superscript[(Subscript[b, {(Subscript[n, 1])}]), {(Subscript[i, 1])}])... (Superscript[(Subscript[b, {(Subscript[n, k])}]), {(Subscript[i, k])}])(Superscript[(Subscript[c, {(Subscript[j, 1])}]), {(Subscript[m, 1])}])...(Superscript[(Subscript[c, {(Subscript[j, h])}]), {(Subscript[m, h])}])    (8)

This explains the notations covariant and contravariant used in tensor calculus:
Proposition. The coordinates of a tensor of type {k,h} are transformed according to the formula (8): k times covariantly to the basis transformation in the underlying vector space V, what means with the same matrix mb, and h times contravariantly, i. e. with the contravariant matrix Transpose{(mb^((-1)))].  ■

Therefore the first group of the indices at the bottom are named covariant, and the upmarket indices of the second group are named contravariant.

3.4.2. Example. Linear Endomorphisms

A linear map of the vector space into itself is called a linear endomorphism.  It can be defined using a square matrix:

dim = 3;

fa[x_List]:= matrix[a].x

fa[vec[x]]

This definition can be considered as a special case of constructing linear maps or functions by the help of tensors. Start with the tensor

ta := tensor[b,1,1]

type[ta]

ta[{1},{2}]

with the definition

b[{i_},{j_}]:= a[j,i]

we get

ta[{1},{2}]

coords[ta] ==Transpose[ matrix[a]]

Now we define the tensor vx corresponding to the vector vec[x]

c[{},{i_}] := x[i]

vx := tensor[c,0,1]

type[vx]

vx[{},{2}]

Flatten[coords[vx]]== vec[x]

We will construct the linear endomorphism fa using the  tensors ta, vx. We take the tensor  product

tax :=tensorproduct[ta,vx]

type[tax]

tax[{1},{2,3}]

Now we calculate the  so called contraction, this is  a summation over all values of a covariant and a contravariant index:

ty[i_] := Sum[tax[{j},{i,j}],{j,dim}]

vec[ty]

Simplify[% == fa[vec[x]]]

Thus, with ((Subscript[a, j])^i) :> a[j, i] we obtain a 1-1 correspondence between the space of linear endomorphisms and tensors of type (1,1). One easily calculates that the so defined tensor coordinates  have the necessary behavior under coordinate transformations.

The endomorphisms have two invariants (in the sense: functions invariant under  coordinate transformations), namely the trace

?trace

trace[a_?MatrixQ] := Sum[a[[i,i]],{i,dim}]

don’t confuse with the built-in function

?Trace

trace[matrix[a]]

Obviously, the corresponding tensor expression is the contraction of the tensor ta

Sum[ta[{i},{i}],{i,dim}]

Another invariant of the endomorphism a is

Det[matrix[a]]

Lemma. Determinant and trace of the matrix of an endomorphism of a vector space (or of  the coordinates of a tensor of type (1,1) ) are invariant under a coordinate transformation of the underlying vector space.

Proof. Equation (8) in 3.4.1 specializes to

((Subscript[(OverHat[ta]), {n}])^({m} )) = ((Subscript[ta, {i}])^({j} ))(Superscript[(Subscript[b, {n}]), {i}])(Superscript[(Subscript[c, {j}]), {m}])                                                       (1)

This equation can be written as a product of matrices

(((Subscript[(OverHat[ta]), {n}])^({m} ))) =((Superscript[(Subscript[b, {n}]), {i}])).( ((Subscript[ta, {i}])^({j} ))).((Superscript[(Subscript[c, {j}]), {m}]))                                   (2)

By (3) of 3.4.1 the matrices ((Superscript[(Subscript[b, {n}]), {i}])) and  ((Superscript[(Subscript[c, {j}]), {m}])) are inverse to each other;  the theorem about the determinant of product matrices yields

Det[(((Subscript[(OverHat[ta]), {n}])^({m} )))] == Det[( ((Subscript[ta, {i}])^({j} )))].

Setting in (2)  n=m and summarize over m, it follows from (3) of 3.4.1
trace[(OverTilde[ta])] =((Subscript[(OverHat[ta]), {m}])^({m} )) = ((Subscript[ta, {i}])^({i} )) = trace[ta]. ◼

3.4.3. Contractions

By the tensor definition, the transformation rule for the tensor coordinates of a mixed tensor with coordinates  tt  with respect to the tensor basis corresponding to {(Subscript[stb, i])} and bb  with respect to the tensor basis corresponding to {(Subscript[vb, i])} is

(Subscript[bb, i j...]^(k l...)) == (Subscript[tt, a b...]^(c d...))  (Subscript[b, i]^a) (Subscript[b, j]^b)...(Subscript[c, c]^k) (Subscript[c, d]^l)...

Definition. Let ta be a mixed tensor of type (h, k ), h>0, k >0. Fix a covariant position a, 1 ≤a≤h, and a contravariant position b,1 ≤b≤k. Then the contraction[ta, a,b] of the tensor ta is defined by the coordinates equation

(Subscript[contraction[ta_. a_,b_], ...]^(...)) := Sum[(Subscript[ta, ..j..]^(...j...)), {j,dim}] ≐ (Subscript[ta, ..j..]^(...j...))

here the summation goes over the index j at the places a and b; the other indices, denoted by the dots, remain unchanged.
One easily proves using 3.4.1, (8)

Proposition. The contraction of  a tensor of type (h, k) is a tensor of type (h-1, k-1); the resulting tensor doesn’t depending on the coordinates.

Proof. Let (Subscript[(OverHat[t]), i...]^(k...)) denote the tensor coordinates with respect to {(Subscript[bt, i ])} and (Subscript[t, i...]^(k...)) those with respect to  {(Subscript[stb, i ])}.  Then

((Subscript[(OverHat[ta]), {(Subscript[n, 1,.,j,.,(Subscript[n, k])])}])^({(Subscript[m, 1,.,j,.,(Subscript[m, h]) ])} )) =

((Subscript[ta, {(Subscript[i, 1,.,r,.,(Subscript[i, k])])}])^({(Subscript[j, 1,..,s,.,(Subscript[j, h]) ])} ))(Superscript[(Subscript[b, {(Subscript[n, 1])}]), {(Subscript[i, 1])}])..(Superscript[(Subscript[b, {j}]), {r}]).. (Superscript[(Subscript[b, {(Subscript[n, k])}]), {(Subscript[i, k])}])(Superscript[(Subscript[c, {(Subscript[j, 1])}]), {(Subscript[m, 1])}])..(Superscript[(Subscript[c, {s}]), {j}])..(Superscript[(Subscript[c, {(Subscript[j, h])}]), {(Subscript[m, h])}])    =

((Subscript[ta, {(Subscript[i, 1,.,r,.,(Subscript[i, k])])}])^({(Subscript[j, 1,..,s,.,(Subscript[j, h]) ])} ))(Superscript[(Subscript[b, {(Subscript[n, 1])}]), {(Subscript[i, 1])}])... (Superscript[(Subscript[b, {(Subscript[n, k])}]), {(Subscript[i, k])}])(Superscript[(Subscript[c, {(Subscript[j, 1])}]), {(Subscript[m, 1])}])...(Superscript[(Subscript[c, {(Subscript[j, h])}]), {(Subscript[m, h])}]) (Superscript[(Subscript[δ, {s}]), {r}])   =

((Subscript[ta, {(Subscript[i, 1,.,s,.,(Subscript[i, k])])}])^({(Subscript[j, 1,..,s,.,(Subscript[j, h]) ])} ))(Superscript[(Subscript[b, {(Subscript[n, 1])}]), {(Subscript[i, 1])}])... (Superscript[(Subscript[b, {(Subscript[n, k])}]), {(Subscript[i, k])}])(Superscript[(Subscript[c, {(Subscript[j, 1])}]), {(Subscript[m, 1])}])...(Superscript[(Subscript[c, {(Subscript[j, h])}]), {(Subscript[m, h])}]).

The first and the last term of this equation chain give the wanted transformation rule for the coordinates of the contracted tensor. Thus  the contraction is independent of the coordinate system.
Now we are going to work out a definition of an arbitrary contraction of a mixed tensor over a list of appropriate slot pairs. The final definition is contained in tensalgv2.m.

?contraction

In the definition are contained two non exported modules, tract and contractio.  tract defines the coordinate function of a tensor contracted over a single slot pair:

tract[tt_,i_,j_][x_,y_] := Module[{ty,h,k,d,aa, bb,cov,cont,x1,y1,jj,xx,yy},
(* Tensor properties *)
If[Head[tt] === tensor, ty = type[tt], Return[type[tt]],Return[type[tt]]];
aa = ty[[1]];h=ty[[2,1]]; k= ty[[2,2]]; d=ty[[3]];
(* The indices *)
cov =Tuples[Range[d],h-1];cont =Tuples[Range[d],k-1];
If[Not[MemberQ[cov,x]&&MemberQ[cont,y]],Print["Incorrect Arguments!"];Return[]];
(* The coordinate function *)
x1[jj_] :=Join[x[[1;;i-1]],{jj},x[[i;;h-1]]];
y1[jj_] :=Join[y[[1;;j-1]],{jj},y[[j;;k-1]]];
bb[xx_,yy_]:=Sum[aa[x1[jj],y1[jj]],{jj,1,d}];
bb[x,y]]

contractio is the corresponding contracted tensor:

contractio[tt_,i_,j_] := Module[{ty},If[Head[tt] === tensor, ty = type[tt], Return[type[tt]],Return[type[tt]]]; ca = tract[tt,i,j]; tensor[ca, type[tt][[2,1]]-1,type[tt][[2,2]]-1, type[tt][[3]]]]

Here is the exported definition:

contraction[tt_, slots_]:=Module[{ty, k,h,aa,d,
nn, eps = 0,err,vv,  vi,vj,i,w,m,j,ww,ta,sa},(*Test of the tensor property *)
If[Head[tt]=== tensor, ty =type[tt],Print["First argument must be a tensor"];Return[]];
aa=ty[[1]];h =ty[[2,1]];k =ty[[2,2]];d =ty[[3]];
(*Suitability Test of the slots *)
If[MatrixQ[slots], nn= Length[slots],eps =1;Goto[err]];
If[Not[Length[slots[[1]]] == 2],eps =1;Goto[err]];
vv =Reverse[ Sort[slots]];
vi =Table[vv[[i,1]],{i,nn}];vj= Table[vv[[i,2]],{i,nn}];
If[Not[andtest[Map[IntegerQ,Join[vi,vj]]]],eps =1;Goto[err]];
If[(Min[vi]<1)||(Min[vj]<1),eps =1;Goto[err]];
If[(Max[vi]>h)||(Max[vj]>k),eps =1;Goto[err]];
    If[(Length[Union[vi]]< nn)||(Length[Union[vj]]< nn),eps =1;Goto[err]];
Label[err];
If[eps== 1, Print["slots:Arguments incorrect!"];Return[]];
(* Transformation of the slots sequence *)
w[i_,1] := vv[[i,1]];w[1,2] = vv[[1,2]];
For[ i =2,i≤ nn,i++,
For[m=0;j=1,j<i,j++,If[vv[[j,2]]<vv[[i,2]],m =m+1]];
w[i,2]=vv[[i,2]]-m];ww=Array[w,{nn,2}];
(* Iteration of contractio *)
ta = tt;i = 1;While[i<nn+1,sa = contractio[ta,w[i,1],w[i,2]]; ta =sa;i++];
sa]

Finally, the contraction with a single slot pair:

contraction[tt_,i_Integer,j_Integer] := contraction[tt,{{i,j}}]

3.5. Examples of Contractions

3.5.1. Example  tensor[a, 3, 2, 3]

Clear[ta];ta = tensor[a, 3,2,3]

Clear[tb];tb =contraction[ta,1,2]

type[tb]

tb[{1,2},{3}]

coords[tb]

tb[{1,1},{1}]

tradf[%]

tb[{1,1},{4}]

tb2=contraction[ta,{{1,1},{2,2}}]

type[tb2]

tb2[{3},{}]

contraction[ta,{{1,2},{3,1}}]

%[{3},{}]

contraction[ta,{{-1,1},{1,2}}]

contraction[ta,{{1,1},{2,3}}]

contraction[ta,1,1]

%[{1,1},{3}]

3.5.2. Example tensor[a, 5, 8, 3]

Clear[ta];ta = tensor [a,5,8,3]

tc =contraction[ta, {{2,4},{3,1},{4,7}}]

type[tc]

tc3 = tc[{1,2},{1,2,1,2,1}]

tradf[%]

Here is the same  coordinate of the contraction calculated by direct summation:

ta3= Sum[a[{ 1,i,j,k,2},{j,1,2,i ,1, 2,k,1}],{i,3},{j,3},{k,3}];

Simplify[ta3-tc3]

tc =contraction[ta, {{2,4},{3,1},{3,8}}]

tc =contraction[ta, {{5,7},{2,2},{3,8}}]

type[%]

3.5.3. Example tensor[a,1,1]

Clear[ta];dim= 3;ta = tensor[a,1,1]

contraction[ta,1,1]

%[{},{}]

% == trace[coords[ta]]

3.5.4. Multilinear Functions and Forms

We show that the covariant tensors of type(k,0) are a vector space isomorphic to the space of multilinear functions in k variables with values in the basic field. Indeed, let f be such a function, and {(Subscript[b, i])} a basis of the underlying vector space. Set

(Subscript[w, i1...ik]):=f[(Subscript[b, i1]),(Subscript[b, i2]),...,(Subscript[b, ik])]

Since f is linear in each argument one sees that the (Subscript[w, i1...ik])  are the coordinates of a covariant tensor  w of type (k,0). Furthermore, the map f ↦ w is linear and bijective: injective, since only the zero-function yields the nulltensor, and surjective, since every tensor of type (k,0) defines the multilinear map of k variables:

(Subscript[f, w])[(Subscript[x, 1,])(Subscript[x, 2]),...,(Subscript[x, k])]:=(Subscript[w, i1...ik])(Subscript[x, 1]^i1)(Subscript[x, 2]^i2)...(Subscript[x, k]^ik)

Here  (Subscript[x, j]^ij) are the coordinates of the vector  (Subscript[x, j]):

(Subscript[x, j])==(Subscript[b, i]) (Subscript[x, j]^i)

Example. We define a multilinear function corresponding to a covariant tensor of type (4,0):

a[x_, y_] := w[x]

tw =tensor[a,4,0,3]

tw[{1,2,2,1},{}]

(Subscript[tx, i_]) := tensor[(Subscript[x, i]),0,1,3]

(Subscript[tx, 1])

(Subscript[x, i_])[{},{j_}]:= (Subscript[x, i])[j]

(Subscript[tx, 1])[{},{3}]

Table[{i,i},{i,4}]

In tensor terms, the corresponding multilinear form is given by

fw[y1_,y2_,y3_,y4_] :=contraction[tensorproduct[tw,y1,y2,y3,y4],Table[{i,i},{i,4}]][{},{}]

fw[(Subscript[tx, 1]),(Subscript[tx, 2]),(Subscript[tx, 3]),(Subscript[tx, 4])]

Of course, this is the same as

Sum[a[{i,j,k,l},{}]* (Subscript[x, 1])[{},{i}] (Subscript[x, 2])[{},{j}] (Subscript[x, 3])[{},{k}] (Subscript[x, 4])[{},{l}],{i,3},{j,3},{k,3},{l,3}]

Simplify[%-%%]

The corresponding 4-form is

ffw[y_] := fw[y,y,y,y]

Simplify[ffw[(Subscript[tx, 1])]]

The following specialization of w:

a[{i_,j_,k_,l_},{}]:= δ[i,j]*δ[i,k]*δ[i,l]

yields the polynomial

ffw[(Subscript[tx, 1])]

Clear[w,fw,ffw,x, tx]

4. Symmetry Properties of Tensors

4.1. CovariantTensors and their Coordinate Arrays

In this Subsection we consider covariant tensors, these are those of type {p, 0}. With the function

?tensorToArray

we obtain an Array whose elements are the coordinates of the tensor and whose structure reflects the tensor type.

Example.

dim = 3;

Clear[ta]; ta[p_,d_:dim] := tensor[a,p,0,d]

array[a_,p_,d_:dim] :=tensorToArray[ta[p,d]]

array[a,0]

array[a,2]

ar42=array[a,4,2]

TensorRank[ar42]

TensorDimensions[ar42]

■

?Array

vec[x] == Array[x,dim]

The Arrays generated by tensorToArray[ta[p,d]] are all of the type

Array[a,Table[d,{p}]]

In the Example we have

ar42 ==Array[a,Table[2,{4}]]/.a[i_,j_,k_,l_]-> a[{i,j,k,l},{}]

Given an Array of the described type we may reproduce the tensor using the function

?ArrayTotensor

Example.

taa = ArrayTotensor[ar42,4]

The tensors are formally defined  in different ways, thus they don’t coincide as Mathematica objects:

TrueQ[ta[4,2] ==taa]

As tensors they coincide since their types and coordinate functions coincide:

equi[ta[4,2] ,taa]

We treat the covariant tensors as their Arrays of coordinates.  This has the advantage that we may apply the built-in tensor functions of Mathematica, where covariant and contravariant tensors aren’t distinguished; tensors of mixed type (k,h) with k>0 and h>0 aren’t considered in Mathematica. Because of the duality all what is done for covariant tensors is valid  for contrariant tensors too.

4.2. Symmetric and Antisymmetric Tensors

Definition. A covariant (or contravariant) tensor ta  of degree p is said to be symmetric iff for each permutation (j1,..., jp) of the indices (i1,..., ip) its coordinates satisfy

a[{j1,..., jp}] ==  a[{i1,..., ip}];

ta is antisymmetric if for all these permutations one has

a[{j1,..., jp}] == a[{i1,..., ip}]*Signature[{i1,..., ip}]*Signature[{j1,..., jp}];

?Signature

The signature of the permutation is (((-1))^n), where n is the number of transpositions of pairs of elements that must be composed to get the natural order of the elements of list  If any two elements of list are the same, Signature[list] == 0. A tensor ta is antisymmetric if for any transposition of indices i, j  one has

a[{..,i,..,j,..}] == - a[{..,j,..,i,..}].

4.2.1. Symmetric Tensors

In this Subsection we construct functions creating symmetric covariant or contravariant tensors. We begin with the definition of symmetric Arrays appearing as coordinate Arrays of such tensors or of symmetric multilinear forms.

symform[a_,p_,d_:dim]:=Module[{rr,i},
If[Not[IntegerQ[p]]||p<0,Print["Wrong p = ",p];Return[]];
If[p==0, Return[a]]; rr=SymmetrizedArray[{i__}->a[i],Table[d,{p}],{Symmetric[Range[p]]}];
Normal[rr]];

?symform

dim = 3;

symform[a,0,4]

symform[a,1,4]

symform[a,2,4]//MatrixForm

ta=ArrayTotensor[%,2]

ta[{1,3},{}]

ta[{3,1},{}]

symform[a,3]

ta=ArrayTotensor[%,3]

ta[{1,3,2},{}]

ta[{2,3,1},{}]

?cosymtensor

cosymtensor[a_,p_,d_:dim] :=If[p===0,Return[a],ArrayTotensor[symform[a,p,d],p]];

dim = 3;

cosymtensor[a,0]

ta1=cosymtensor[a,1]

%[{2},{}]

coords[ta1]

tensorToArray[ta1]

ArrayTotensor[%,1]

equi[%,ta1]

ta2 = cosymtensor[a,2]

ta2[{1,3},{}]

ta2[{1,3},{}]+ta2[{3,1},{}]

tensorToArray[ta2]

The contravariant tensor with the same coordinates is obtained analogously:

contrasymtensor[a_,p_,d_:dim] := If[p===0,Return[a],ArrayTotensor[symform[a,p,d]]];

?contrasymtensor

contrasymtensor[a,1,4]

coords[%]

cta = contrasymtensor[a,5,4]

cta[{},{1,2,2,3,4}]

cta[{},{3,2,1,4,2}]

contrasymtensor[a,1,3]

%[{},{2}]

4.2.2. Antisymmetric Tensors

In this Subsection we construct functions creating antisymmetric covariant or contravariant tensors. We begin with the definition of antisymmetric Arrays appearing as coordinate Arrays of such tensors or of antisymmetric multilinear forms.

altform[a_,p_,d_:dim]:=Module[{rr,i},
If[Not[IntegerQ[p]]||p<0,Print["Wrong p = ",p];Return[]];
If[p== 0, Return[a]];
rr=SymmetrizedArray[{i__}->a[i],Table[d,{p}],{Antisymmetric[Range[p]]}];
Normal[rr]];

?altform

dim = 3;

altform[a,0,4]

altform[a,1,4]

Clearly. antisymmetric forms with p>d are null forms:

altform[a,4]

altform[a,2,4]//MatrixForm

ta=ArrayTotensor[%,2]

ta[{1,3},{}]

ta[{3,1},{}]

coalttensor[a_,p_,d_:dim] :=If[p== 0,Return[tensor[a,0,0,d]], ArrayTotensor[altform[a,p,d],p]];

?coalttensor

dim = 3;

ta = coalttensor[a,2]

ta[{1,3},{}]

ta[{1,3},{}]+ta[{3,1},{}]

tensorToArray[ta]

coalttensor[a,1]

tensorToArray[%]

coalttensor[a,0]

%[{},{}]

A contravariant antisymmetric tensor is generated analogously :

contraalttensor[a_,p_,d_:dim] := If[p== 0,Return[tensor[a,0,0,d]],ArrayTotensor[altform[a,p,d]]]

?contraalttensor

Test the next cells for p = 0,1,2,3,4:

dim = 4;cta =contraalttensor[a,3]

cta[{},{3,2,1}]

For an antisymmetric covariant or contravariant tensor of degree p all its coordinates are known if the coordinates a[I] for the subsets I ∈ Subsets[ Range[d:dim, {p}]  of  p elements are known:

?Subsets

Lemma. A covariant (or contravariant) tensor ta of degree p is antisymmetric if  its coordinates a[j1,...,jp] satisfy
1. In case j1,...,jp contains two equal numbers, a[j1,...,jp] = 0;
2.  For any permutation {j1, ..., jp} of I = {i1,...,ip} with i1<i2<...<ip is

a[j1,...,jp] = Signature[j1,...,jp]*a[i1,...,ip]

◼ 
The coordinates corresponding to the mentioned subsets are named the strict coordinates of the antisymmetric tensors. They are given by the function

?strictcoords

In our example:

strictcoords[cta]

cta[{},{2,2,3}]

cta[{},{2,4,3}] == - cta[{},{2,3,4}]

For p>d we obtain a nulltensor:

cta = contraalttensor[a,5]

equi[cta,nulltensor[0,5,4]]

The function strictcoords will be evaluated for antisymmetric tensors only:

dim = 5; ta = tensor[a,3,0]; strictcoords[ta]

cta = coalttensor[a,3]

strictcoords[cta]

tensorToArray[cta]//MatrixForm

strictcoords evaluates for antisymmetric Arrays too:

strictcoords[%]

strictcoords[vec[a]]

strictcoords[covecTotensor[vec[a]]]

Now we will construct a standard basis in the space of covariant antisymmetric tensors ta of degree p using the properties of the coordinates, see the Lemma above.  We start with the basis representation in the tensor space, see 3.2.3, in general traditional notation

ta == (Subscript[a, (Subscript[i, 1...(Subscript[i, p])])])(tstb^(Subscript[i, 1...(Subscript[i, p])]))  with (tstb^(Subscript[i, 1...(Subscript[i, p])])) ≐ tstb[{(Subscript[i, 1]),..,(Subscript[i, p])},{}]

the standard tensor basis; remember the sum convention. Applying the Lemma we may write this sum as follows:

ta == (Subscript[Σ, (Subscript[i, 1<..<(Subscript[i, p])])])(Subscript[a, (Subscript[i, 1...(Subscript[i, p])])])(Subscript[Σ, {(Subscript[j, 1]),..,(Subscript[j, p])}∈ Perm[{(Subscript[i, 1,..,(Subscript[i, p])])}]])Signature[{(Subscript[j, 1]),..,(Subscript[j, p])}]*tstb[{(Subscript[j, 1]),..,(Subscript[j, p])},{}],

where the first sum runs over all elements of Subsets[Range[dim],{p}] and the second sum over all permutations of the subset {(Subscript[i, 1]),..,(Subscript[i, p])}. The coefficients

(Subscript[a, (Subscript[i, 1...(Subscript[i, p])])]) ≐ ta[ {(Subscript[i, 1]),..,(Subscript[i, p])}, {}]

are the strict coordinates of the antisymmetric tensor. The second sum doesn’t depend on the tensor, it defines  the basis element of the standard basis in the space of p-forms corresponding to the subset {(Subscript[i, 1]),..,(Subscript[i, p])}. We gave a Mathematica construct for this element in tensalgv2.m:

?wtstb

wtstb[i_,d_:dim]:= Module[{p,pp,ppi,j},
If[Head[i]===List,p = Length[i],Print["Invalid argument!"];Return[]];
If[p==1, Return[tstb[i,{},d]]]; 
If[(p<=d),pp = Subsets[Range[d],{p}],Print["Invalid argument!"];Return[]];
If[MemberQ[pp,i],ppi =Permutations[Range[p]],Print["Invalid argument!"];Return[]];
Sum[Signature[ppi[[j]]]*tstb[Permute[i,ppi[[j]]],{}],{j,p!}]]

dim= 5;wtstb[{1,3,5}]

%[{1,3,5},{}]

strictcoords[wtstb[{1,3,5}]]

wtstb[{1,2,3,4,5}]

strictcoords[%]

wtstb[{}]

strictcoords[%]

wtstb[{3}]

strictcoords[%]

wtstb[{1,3}]

strictcoords[%]

type[cta]

scta=strictcoords[cta]

scta[[3]]

ss53=Subsets[Range[5],{3}]

ss53[[3]]

prcta = Sum[scta[[i]]*wtstb[ss53[[i]]],{i,10}]

equi[prcta,cta]

Timing[strictcoords[prcta]]

We summarize:
Proposition. The subset of antisymmetric covariant tensors  of degree p (shortly p-forms) is a vector  subspace of dimension Binomial[d,p], 0≤p≤d,  d the dimension of the underlying vector space. The tensors wtstb[{(Subscript[i, 1]),..,(Subscript[i, p])}, d], {(Subscript[i, 1]),..,(Subscript[i, p])}∈Subsets[Range[{d}],p] are the standard basis of this subspace; the corresponding coordinates of a p-form ta are its strict coordinates.  An analogous statement is true for the subspace of antisymmetric contravariant tensors of degree p (shortly p-vectors). ■

The number of these basis elements is (({{d}, {p}})), with

?Binomial

The standard basis vtstb in the space of the p-vectors over the underlying vector space of dimension d is quite analogous to the basis wtstb; it is given in tensalgv2.m as

vtstb[i_,d_:dim]:= Module[{p,pp,ppi,j},
If[Head[i]===List,p = Length[i],Print["Invalid argument!"];Return[]];
If[p==1, Return[tstb[{},i,d]]]; 
If[(p<=d),pp = Subsets[Range[d],{p}],Print["Invalid argument!"];Return[]];
If[MemberQ[pp,i],ppi =Permutations[Range[p]],Print["Invalid argument!"];Return[]];
Sum[Signature[ppi[[j]]]*tstb[{},Permute[i,ppi[[j]]],d],{j,p!}]]

?vtstb

ta =contraalttensor[a,3]

scta=strictcoords[ta]

ss53=Subsets[Range[5],{3}]

prcta = Sum[scta[[i]]*vtstb[ss53[[i]]],{i,10}]

Timing[equi[ta,prcta]]

Clear[ta,cta,prcta,scta,ss53]

4.3. Symmetrization  and Alternation of Tensors

Symmetrization and alternation of tensors are operations for tensors making them symmetric resp. antisymmetric in their covariant or Contravariant parts. In this Subsection we create such operations and show their properties,

4.3.1. Symmetrization  of Coordinate Functions

In tensalgv2.m we defined  two operators, the symmetrizations covs, contras which act over the covariant resp contravariant indices and transform the coordinate function  into a symmetric coordinate function of the same type.

dim = 3;

?covs

?contras

covs[aa_][ii_,jj_]:=If[(Length[ii]==0), aa[ii,jj],
Sum[covpermute[aa,Permutations[Range[Length[ii]]][[l]]][ii,jj],{l,Length[ii]!}]];

contras[aa_][ii_,jj_]:=If[(Length[jj]==0), aa[ii,jj],
Sum[contrapermute[aa,Permutations[Range[Length[jj]]][[l]]][ii,jj],{l,Length[jj]!}]];

Examples

covs[a][{1,2,3},{i}]

Now the new coordinate function covs[a] is symmetric in the covariant indices:

%-covs[a][{3,2,1},{i}]

contras[a][{i},{j1,j2,j3}]

Of course, permutations of order 0 or 1 don’t change the expression;

covs[a][{},{i}]

contras[a][{i,j},{k}]

If the index lists are empty or contain one index only, the symmetrized coordinate functions coincide with the original coordinate functions:

contras[a][{i1,i2},{}]

(covs[a]-a)[{},{i1,i2,i3}]

4.3.2.  Alternation of Coordinate Functions

Similarly to the symmetrizations the alternating operators of the coordinate functions are defined:

?cova

?contra

cova[aa_,d_:dim][ii_,jj_]:=If[(Length[ii]==0), aa[ii,jj],
If[MemberQ[Subsets[Range[d],{Length[ii]}],Sort[ii]],
Return[Sum[Signature[Permutations[Range[Length[ii]]][[l]]]*covpermute[aa,Permutations[Range[Length[ii]]][[l]]][ii,jj],{l,Length[ii]!}]],0]];
cova/: cova[a_ + b_,d_:dim] := cova[a,d] + cova[b,d];
cova/: cova[times[c_,a_],d_:dim] := times[c,cova[a,d]];

contra[aa_,d_:dim][ii_,jj_]:=If[(Length[jj]==0), aa[ii,jj],
If[MemberQ[Subsets[Range[d],{Length[jj]}],Sort[jj]],
Return[Sum[Signature[Permutations[Range[Length[jj]]][[l]]]*contrapermute[aa,Permutations[Range[Length[jj]]][[l]]][ii,jj],{l,Length[jj]!}]],0]];
contra/: contra[a_ +b_,d_:dim] := contra[a,d] + contra[b,d];
contra/: contra[times[c_,a_],d_:dim] := times[c,contra[a,d]];

Examples

dim=5;

cova[a][{1,2,3,4},{1}]

cova[a][{1,3,2,4},{1}]

%==-%%

If the Lists contain less than two elements, the original coordinate functions are reproduced:

cova[b][{},{2,3}]

cova[b][{1},{}]

(cova[b]-contra[b])[{},{}]

plus[cova[b],-contra[b]][{},{}]

cova[b][{},{}]

(cova[b]-contra[b])[{i},{j}]

plus[cova[b],-contra[b]][{i},{j}]

dim=5;contra[b][{},{j}]

Composing the operators yields

i1=1;i2=4;a=3;b=2;c=5;

cova[contra[x]][{i1,i2},{a,b,c}]==  contra[cova[x]][{i1,i2},{a,b,c}]

cova[cova[x]][{i1,i2},{a,b,c}]

contra[contra[x]][{i1,i2},{a,b,c}]

covs[contra[contra[x]]][{i1,i2},{a,b,c}]

plus[cova[contra[x]],-  contra[cova[x]]][{i1,i2},{a,b,c}]

Clear[i1,i2,a,b,c]

4.3.3. The Definitions covsym, contrasym, covalt, contralt

We remember some simple definitions. Let a, b with a<b be slots of  two covariant or two contravariant indices of a tensor ta. The tensor is said to be symmetric with respect to the indices ia, ib, if

(Subscript[ta, ..ia..ib...]^(......)) == (Subscript[ta, ..ib..ia...]^(......))

and it is said to be alternating (or antisymmetric) with respect to the indices ia, ib, if

(Subscript[ta, ..ia..ib...]^(......)) == - (Subscript[ta, ..ib..ia...]^(......))

for all indices 1 ≤ ia , ib ≤ dim, analogously for contravariant indices. This terminology can also be applied for subsets of index slots in the sense that they are valid for all pairs within the subset.  In particular, a covariant (resp. contravariant)  tensor is said to be symmetric resp. alternating (or antisymmetric)  if it has this property for all pairs of its index slots.

In tensalgv2.m are defined two operators, the symmetrization covsym and the alternation covalt, which act over the covariant indices and transform the tensor into a symmetric resp. antisymmetric tensor of the same type; the contravariant indices remain untouched. Furthermore we defined analogous operations for the contravariant indices, leaving the covariant indices untouched.

dim

?covsym

covsym[tt_]:= Module[{ttp,k,h,dd,aa},
If[Not[Head[tt]=== tensor], Print["Argument must be a tensor!"];Return[]];
ttp = type[tt];aa=ttp[[1]];k=ttp[[2,1]];h=ttp[[2,2]];dd=ttp[[3]];
If[k==0,tt,tensor[covs[aa],k,h,dd]]]

?covs

?contrasym

contrasym[tt_]:= Module[{ttp, k,h,dd,aa},
If[Not[Head[tt] ===  tensor], Print["Argument must be a tensor!"];Return[]];
ttp = type[tt]; k =ttp[[2,1]];h =ttp[[2,2]];dd =ttp[[3]];
aa = ttp[[1]];
If[(h==0), tt,tensor[contras[aa],k,h,dd]]];

?contras

Here are the definitions of the alternations:

?covalt

covalt[tt_]:= Module[{ttp, k,h,dd,aa},
If[Not[Head[tt] ===  tensor], Print["Argument must be a tensor!"];Return[]];
ttp = type[tt]; k =ttp[[2,1]];h =ttp[[2,2]]; dd =ttp[[3]];
 aa = ttp[[1]];
If[k>dd, Return[nulltensor[k,h,dd]]];
If[(k<=1), tt,tensor[cova[aa,dd],k,h,dd]]];
covalt/: covalt[times[c_,a_]] := times[c,covalt[a]];

?cova

?contralt

contralt[tt_]:= Module[{ttp, k,h,dd,aa},
If[Not[Head[tt] ===  tensor], Print["Argument must be a tensor!"];Return[]];
ttp = type[tt]; k =ttp[[2,1]];h =ttp[[2,2]];dd =ttp[[3]];
 aa = ttp[[1]];
If[h>dd,Return[nulltensor[k,h,dd]]];
If[(h≤ 1), tt,tensor[contra[aa],k,h,dd]]];

?contra

4.3.4. Linearity of the Symmetry Operations

dim= 4;ta = tensor[a,3,2]

tb = tensor[b,3,2]

type[ta+tb]

Simplify[covsym[ta + tb] - (covsym[ta]+covsym[tb])]

equi[%, nulltensor[3,2]]

contrasym[ta]+contrasym[tb]-contrasym[ta + tb]

equi[%, nulltensor[3,2]]

covalt[ta + tb] - (covalt[ta]+covalt[tb])

equi[%, nulltensor[3,2]]

contralt[ta]+contralt[tb]-contralt[ta + tb]

equi[%, nulltensor[3,2]]

covsym[c*ta] -c*covsym[ta]

equi[%, nulltensor[3,2]]

contrasym[c*ta] -c*contrasym[ta]

equi[%, nulltensor[3,2]]

covalt[c*ta] - c*covalt[ta]

equi[%, nulltensor[3,2]]

contralt[c*ta] - c*contralt[ta]

equi[%, nulltensor[3,2]]

We conclude
Proposition. The maps covsym, contrasym, covalt, contralt are linear maps of the tensor spaces of type (k,h) into themselves.

4.3.5. Iteration of the Symmetry Operations

dim=4;

ta=tensor[a,3,2,4]

taa =covalt[covalt[ta]]

taa[{1,2,3},{2,4}]

equi[taa,6*covalt[ta]]

tb = contralt[ta]

tbb = contralt[tb]

Simplify[ tbb[{1,2,3},{1,2}]-2tb[{1,2,3},{1,2}]]

equi[tbb,2*tb]

Immediately from the definitions it follows
Lemma. For the symmetrization and alternation the following formulas are valid

covsym[covsym[ta]] ==  k!*covsym[ta]

covalt[covalt[ta]] ==  k!*covalt[ta]

contrasym[contrasym[ta]] ==  h!*contrasym[ta]

contralt[contralt[ta]] ==  h!*contralt[ta]

covsym[covalt[ta]] == nulltensor[k,h]

covalt[covsym[ta]]== nulltensor[k,h]

contrasym[contralt[ta]] == nulltensor[k,h]

contralt[contrasym[ta]]== nulltensor[k,h]

where {k,h} =type[ta][[2]].  The sign == in these equations means that the tensors of both sides have the same coordinates. ◼

We control yet the last four  identities. In the covariant case:

taa=covsym[covalt[ta]]

taa[{1,2,3},{1,1}]

equi[taa,nulltensor[3,2]]

taa=covalt[covsym[ta]]

taa[{1,2,3},{1,1}]

equi[taa,nulltensor[3,2]]

The contravariant case gives the stated results analogously.

Clear[ta,taa]

4.4. Exterior Algebra

4.4.1. Introduction

The antisymmetric (or the symmetric) tensors of type (p,0) and of type (0,p) over the same d-dimensional vector space form subspaces of the spaces of tensors of the same type.  In the following we discuss the antisymmetric covariant tensors. The concepts can be applied to the antisymmetric contravariant tensors as well. The components of the tensors are assumed to belong to a field  K of characteristic zero, mostly the field of complex, real or rational numbers.  Let V denote a given vector space of dimension dim over K, V´ its dual. Then denote by (Overscript[∧, p]) V´    the subspace of alternating tensors of type (p,0) over V; its elements are called p-forms. Analogously   (Overscript[∧, p]) V denotes the space of alternating tensors of type (0,p) over V; its elements are named p-vectors. Clearly we have

(Overscript[∧, 0]) V == (Overscript[∧, 0]) V´ == K; (Overscript[∧, 1]) V == V;  (Overscript[∧, 1]) V´ == V´; 
  (Overscript[∧, p]) V == 0  and   (Overscript[∧, p]) V´ == 0 for p > dimension[V].                                            (1)

Here 0 stands for the corresponding nulltensor.  Obviously, the tensor product of two symmetric or antisymmetric tensors needs not to have the same property.  Let us consider the direct sum of vector spaces

∧ V´:= K⊕V´⊕ (Overscript[∧, 2])V´⊕..⊕ (Overscript[∧, p])V´⊕...⊕ (Overscript[∧, d])V´,d= dimension[V].                                                                                             (2)

Termwise addition  and multiplication with elements of K gives ∧ V ´ the structure of a finite dimensional vector space over K. Using strict coordinates we proved in Subsection 4.2.2

dimension[(Overscript[∧, p])V´]== Binomial[d,p] =(({{d}, {p}}))                                          (3)

Summarizing we obtain for the

dimension[∧V´] == (Underoverscript[ ∑, (p=0), d])Binomial[d,p] == (2^d).                                 (4)

Our aim now is introducing the so-called wedge product for the elements of  ∧ VV to create the structure of an associative, non-commutative algebra, named the exterior algebra over VV, or the Grassmann algebra of alternating p-forms. Analogously the Grassmann algebra  ∧ V of p-vectors  is defined.

For the general theory and its application in this notebook see e.g. [OSII], section 8.3.

4.4.2. extprod

In this Subsection we treat the “exterior” or “wedge” product extprod  of p-forms  and p-vectors represented by their Arrays of coordinates. We begin with the product extprod  of p-forms defined as an operation over their coordinate Arrays using the built-in function

?TensorWedge

(Click on »  !)

Context[TensorWedge]

Consider the covectors

dim=3;w1=covec[f1];w2=covec[f2];w3=covec[f3]

TensorWedge[w1,w2]

The result is an alternating bilinear form in dimension 3 with  the coordinates

Normal[%]

?Normal

In tensalgv2.m we defined

?extprod

extprod[w___] := Normal[TensorWedge[w]]

extprod[]

A general symbol isn’t recognized as a 0-form, extprod is not evaluated:

extprod[g]

A numeric argument  is treated as a 0-form, i.e. a scalar:

extprod[Pi]

extprod[Cos[x]]

Again

extprod[g,w1]

extprod[Pi,w1]

%== Pi*w1

extprod[%%,w2]

Simplify[%== Pi*extprod[w1,w2]]

extprod is linear:

Simplify[extprod[w1, w2+Pi*w3] == extprod[w1, w2] +Pi* extprod[w1,w3]]

The exterior product is Flat (associative).

extprod[w1,extprod[w2,w3]]

Simplify[% == extprod[w1,w2,w3]]

Clearly, the exterior product of four1-forms of dimension 3 is zero:

extprod[w1,w2,w3, covec[x]]

Here is an antisymmetric biform:

dim= 3;a2 = altform[a,2]

The exterior product of a2 with a scalar and a 1-form is the antisymmetric 3-form

extprod[3,a2,w1]

with the only strict coordinate

strictcoords[%]

factors of an exterior product not being antisymmetric will be symmetrized during the operation:

b2 = Array[b,{3,3}]

%//MatrixForm

extprod[b2]

extprod[w1,b2]

The only strict coordinate is

strictcoords[%]

b24 = Array[b,{3,4}]

As expected, we get an error message, and the wed extprod isn’t evaluated.

extprod[b24,w1]

We summarize:
Proposition.  The operation extprod restricted to the alternating p-forms realizes the multiplication of the Graßmann algebra. It is a multilinear, anticommutative and associative operation. ◼

anticommutative means: For a p-form (Subscript[a, p]) and a q-form (Subscript[b, q]) the following rule is fulfilled:

extprod[(Subscript[a, p]),(Subscript[b, q])] ==  (((-1))^(p*q))extprod[(Subscript[b, q]),(Subscript[a, p])]

It is also an alternating algebra, see Wikipedia.

For our examples:

extprod[w1,w2] == -extprod[w2,w1]

TensorSymmetry[extprod[w1,w2]]

extprod[w1,a2] == extprod[a2,w1]

The built-in function TensorSymmetry shows that the results of extprod are antisymmetric  Arrays, in our case (p+q)-forms:

?TensorSymmetry

TensorSymmetry[w1  \[TensorWedge] w2]

The  Infix symbol \[TensorWedge] may be used for TensorWedge, not for extprod:

w1 \[TensorWedge] w2

Normal[%]

The same operations may be done for p-vectors. Consider the example

v1 =tensor[a,0,1];v2 =tensor[b,0,2];

vv1 =tensorToArray[v1];vv2=tensorToArray[v2];

extprod[vv1,vv2]

To get the corresponding tensor apply

v3 = ArrayTotensor[%];

In the definition of v3 the covariant degree of the tensor has the default value p=0, thus it doesn’t appear in the formula. 
v3 is a 3-vector: an antisymmetric contravariant tensor of degree 3:

TensorSymmetry[%%]

type[v3]

v3[{},{1,2,3}]

Simplify[v3[{},{1,2,3}]+v3[{},{1,3,2}]]

strictcoords[v3]

4.4.3. The General  wedge Product

In this subsection we consider the exterior algebra of p-forms and analogously the exterior algebra of p-vectors as a part of tensor algebra.

We define the alternating product using the notation wedge corresponding to our convention for the notation of symbols for non built-in functions: these symbols should start with a small letter. For the definition we follow formula (8.3.7), p. 100 of the German edition of [OSII]:

?wedge

The definition of wedge is given in tensalgv2.m as

Attributes[wedge]= {Flat, OneIdentity};
wedge[x___] := Module[{ii ,k,kk,j,ko,kc,CON,num,tlist},
ii=Flatten[{x}];k = Length[ii];
If[k== 0,Return[1]];
For[tlist={};num=1;j=1,j<k+1,j++,
If[Not[NumericQ[ii[[j]]]||(Head[ii[[j]]]=== tensor)],
Print["Invalid Argument ", ii[[j]]," !"];Return[],
If[NumericQ[ii[[j]]],num =num*ii[[j]],
AppendTo[tlist,ii[[j]]]]]];
 k = Length[tlist];
ko = Sum[type[tlist[[j]]][[2,1]],{j,k}];
dd=type[tlist[[1]]][[3]];
kc = Sum[type[tlist[[j]]][[2,2]],{j,k}];
If[ko>0&&kc>0,Print["Invalid: Mixed type arguments!"];Return[]];
For[j=1,j<k+1,j++,If[Not[type[tlist[[j]]][[3]]===dd],
Print["Arguments have varying dimensions!"];Return[]]];
If[ko==0, Goto[CON]];
kk =Product[1/((type[tlist[[j]]][[2,1]])!),{j,k}];
If[ko>dd, Return[nulltensor[ko,0,dd]],
Return[num*kk*covalt[tensorproduct[tlist]]]];
Label[CON];
kk =Product[1/((type[tlist[[j]]][[2,2]])!),{j,k}];
If[kc>dd, Return[nulltensor[0,kc,dd]],
num*kk*contralt[tensorproduct[tlist]]]];

Wedge[x___] := wedge[x];a_⋀b_ := wedge[a,b]

Context[wedge]

?Wedge

All tensor arguments of wedge must be either covariant (of type {p,0}) or Contravariant (of type {0,p}); tensors of different kind or  tensors mixed type {p.q} with p>0 and q>0 aren’t admitted.

Examples.

dim = 5;

tv[i_]:=covecTotensor[vec[f[i]]]

tv[2]

coords[%]

wtv3=wedge[tv[1],tv[2],tv[3]]

wtv3[{1,3,5},{}]

tv[1]⋀tv[2]⋀tv[3]

equi[%,wtv3]

wtv5=wedge[tv[1],tv[2],tv[3],tv[4],tv[5]]

%[{1,2,3,4,5},{}]

Simplify[% == Det[Table[vec[f[i]],{i,5}]]]

wedge[]

tm = tensor[mx,2,2]

wedge[tv[1],tm]

wedge[tv[1]]

Simplify[equi[%,tv[1]]]

Lemma 1. wedge reproduces antisymmetric tensors tva:

strictcoords[tva] == strictcoords[wedge[tva]]

In particular it follows

strictcoords[wedge[ta]] == strictcoords[wedge[wedge[ta]]]

Proof. Check the formula for several antisymmetric tensors tva:

tva = coalttensor[a,5]

strictcoords[tva] == strictcoords[wedge[tva]]

ta=tensor[a,4,0];Timing[equi[wedge[wedge[ta]],wedge[ta]]]

Timing[Simplify[strictcoords[wedge[wedge[ta]]]== strictcoords[wedge[ta]]]]

■
Applying equi confirms the statement too, but needs much more time. Now we consider the function covalt:

?covalt

covalt  applied to covariant, non mixed tensor gives almost the same result as wedge, namely

Lemma 2. For all purely covariant tensors ta of degree p one has the relation

strictcoords[covalt[ta]]== strictcoords[(p!)*wedge[ta]]

For purely contravariant tensors one has

strictcoords[contralt[ta]]== strictcoords[(p!)*wedge[ta]]

Proof. Check the formula for several  tensors ta:

ta=tensor[a,4,0];Timing[equi[covalt[ta],(4!)*wedge[ta]]]

Timing[Simplify[strictcoords[covalt[ta]]- strictcoords[(4!)*wedge[ta]]]]

ta=tensor[a,0,5];Timing[equi[contralt[ta],(5!)*wedge[ta]]]

Timing[Simplify[strictcoords[contralt[ta]]==  strictcoords[(5!)*wedge[ta]]]]

◼
Now we will show

Theorem. Restricting the definition wedge to the subspace of all antisymmetric forms this subspace  becomes an associative algebra over the field K, called the exterior algebra of antisymmetric forms.  The map covalt is a homomorphism  of the covariant tensor algebra onto this exterior algebra, what means, it is a linear map satisfying

covalt[ta ⊗tb] ==covalt[ta]⋀covalt[tb]

Proof.  The linearity of covalt has been proved already in Subsection 4.3.4. Now check the last formula for various covariant tensors:

dim=6;ta = tensor[a,2,0];tb=tensor[b,3,0];

Timing[equi[covalt[ta ⊗tb],covalt[ta]⋀covalt[tb]]]

%[[1]]/60

297.3223059`/60

The result: {297.322,True}; the calculation needed 49.5067 min.

Timing[Simplify[strictcoords[covalt[ta ⊗tb]] == strictcoords[covalt[ta]⋀covalt[tb]]]]

114.7075353`/60

The result: {119.107,True}; the calculation needed 1.91179 min.

◼
Analogously one proves

Theorem. Restricting the definition wedge to the subspace of all antisymmetric contravariant tensors this subspace  becomes an associative algebra over the field K, called the contravariant exterior algebra. The map contralt is a homomorphism  of the contravariant tensor algebra onto this exterior algebra, what means, it is a linear map satisfying

contralt[ta ⊗tb] == contralt[ta]⋀contralt[tb]    ◼

Clear[ta,tb]; dim= 3;

4.4.4. The Epsilon Tensor

The epsilon tensor of order k is a tensor of type (k,k), antisymmetric in its covariant and its contravariant indices. 
Its coordinate function is the epsilon function eps:

?eps

eps[a_List,b_List,d_:dim]:= Module[{k=Length[a],rg,ee=0},
If[(k>d), Print["False arguments!"];Return[]];
rg = Tuples[Range[d],k];
If[Not[MemberQ[rg,Sort[a]]]||Not[MemberQ[rg,Sort[b]]], Print["False arguments!"];Return[]];
If[Sort[a] == Sort[b],ee =Signature[a]*Signature[b]];ee]

Context[eps]

Attributes[eps]

dim=4;

eps[{3,4,1},{3,1,2}]

eps[{3,1,2},{1,3,2}]

eps[{3,1,2},{3,1,2}]

eps[{3,1,2},{1,1,2}]

eps[{3,1,2},{1,5,2}]

?epst

epst[k_,d_:dim]:=Module[{tt},
If[(k<0)||Not[IntegerQ[k]],Print["incorrect arguments!"];Return[]];
If[k≤d,tt= tensor[eps,k,k,d],tt=nulltensor[k,k,d]];tt]

epst[4]

epst[4][{1,2,4,3},{1,2,3,4}]

type[epst[4]]

epst[3][{1,2,3},{1,3,2}]

coords[epst[3]];

Length[%]

outzero[%%];

Length[%]

type[epst[3]]

(Subscript[tx, i_ ]):= tensor[(Subscript[x, i]),0,1]

(Subscript[tx, 1 ])

?contraction

txalt = contraction[tensorproduct[epst[3],(Subscript[tx, 1 ]),(Subscript[tx, 2 ]),(Subscript[tx, 3 ])],{{1,4},{2,5},{3,6}}]

Timing[strictcoords[txalt]]

Timing[strictcoords[wedge[(Subscript[tx, 1 ]),(Subscript[tx, 2 ]),(Subscript[tx, 3 ])]]]

Timing[Simplify[%[[2]]-%%[[2]]]]

Timing[equi[txalt,wedge[(Subscript[tx, 1 ]),(Subscript[tx, 2 ]),(Subscript[tx, 3 ])]]]

The epsilon function (or the tensor epst) can be used to define the wedge product. For the determinant one has

dim=4;ssd =Permutations[Range[dim]];Sum[eps[Range[dim],ssd[[j]]]*x[ssd[[j,1]]]*y[ssd[[j,2]]]*z[ssd[[j,3]]]*w[ssd[[j,4]]],{j,dim!}]

%==Det[{vec[x],vec[y],vec[z],vec[w]}]

For dimensions dim > 4 the tensor operations are very time consuming:

dim=5;ta = tensor[a,2,0];tb= tensor[b,2,0];

tab =tensorproduct[epst[4],ta,tb]

alt4 =contraction[tab,{{5,1},{6,2},{7,3},{8,4}}]

Timing[strictcoords[alt4]]

4039.1934921`/60

The program needed 67 min for this calculation.

Length[%%[[2]]]

%%%[[2,3]]

alt4[{2,1,3,4},{}]+alt4[{1,2,3,4},{}]

5. Tensors in Mathematica

5.1. The Built-in Tensor Functions

In Mathematica the word Tensor is used for Arrays in contexts of specific applications, e.g. operations and concepts of tensor algebra, Tensor itself remains undefined:

?Tensor

On the other hand, there are some built-in functions using the word Tensor. e.g.

?*Tensor*

LeviCivitaTensor - is a special case of the epsilon tensor 
TensorContract corresponds to contraction, acts on Arrays.
TensorExpand acts symbolically on all kind of distributive products.
TensorDimensions acts on Arrays.
TensorProduct acts on Arrays of tensor coordinates.
TensorQ is not explained.
TensorRank acts on Arrays.
TensorReduce acts on Arrays, symmetry properties may be added as Options or Assumptions.
TensorSymmetry yields the symmetry properties of explicit or symbolic Arrays.
TensorTranspose acts on Arrays of tensor coordinates.
TensorWedge acts on Arrays of tensor coordinates

The concepts of Tensors in Mathematica are considered in the tutorial Symbolic Tensors.

5.2. Tensors and Arrays

5.2.1 TensorQ and ArrayQ

Reading the first passage in the tutorial Symbolic Tensors one supposes that Tensor and Array are synonyms in Mathematica. On the other hand, there exists a function

?TensorQ

without any explanation.  Thus, the situation is a bit strange: this function is expected to test whether a Mathematica object is a tensor, but Tensor is not defined in Mathematica. Further we have the function

?ArrayQ

very detailed explained. We could not find objects for which the values of both functions differ. Thus our hypothesis that Array and Tensor mean the same in Mathematica is confirmed.

Array[a,{2,1,4,3}]

TensorQ[%]

Dimensions[%%]

ArrayQ[%%%]

TensorQ[vec[x]]

ArrayQ[vec[x]]

gr1=Graphics[Line[{{0,0},{0,1}}]];

a[i_,j_] :=Graphics[Line[{{0,0},{i,j}}]];

agr = Array[a,{3,3}];

a[1,1]

TensorQ[agr]

agr[[2,2]]

Clear[b];b[i_,{}] := a[i[[1]],i[[2]]]

tbb = tensor[b,3,0,3]

coords[tbb]

ArrayQ[%]

TensorQ[%%]

We see that both objects, Array and our tensor, may have unusual coordinates.

TensorQ[Range[3]]

ArrayQ[Range[3]]

Assuming[Head[aa] ==  List, TensorQ[aa]]

Assuming[Head[aa] ==  List, ArrayQ[aa]]

FullSimplify[Assuming[Head[aa] ==  List, ArrayQ[aa]]]

Here is a List not being an Array nor a Tensor:

anti = {aa,{aa,bb},{{cc}},dd}

Head[anti]

TensorQ[{anti}]

ArrayQ[anti]

Array[f,5,{10,20}]

TensorQ[Array[f,5,{10,20}]]

Array[f, {2,3},{{-1/2,1/2},{0,1}}]

TensorQ[%]

Clear[a];Array[a,{2,3},1,Plus]

ArrayQ[%]

TensorQ[%%]

5.2.2. tensorToArray

In the following we consider Arrays as Tensors in Mathematica. To compare these  Tensors with the tensor concept in tensalgv2.m we define a function tensorToArray[ta] returning an Array with the coordinates of ta as elements:

?tensorToArray

The definition contained in tensalgv2.m is

SetAttributes[tensorToArray,Listable];
tensorToArray[tt_]:= Module[{ti,aa,k,h,dd,bb,i},
If[Not[Head[tt]=== tensor],Print["The argument must be a tensor!"];Return[]];
ti = type[tt];aa = ti[[1]]; k = ti[[2,1]]; h =ti[[2,2]]; dd =ti[[3]];
Off[Part::take];
 bb[i___]:= aa[{i}[[1;;k]],{i}[[k+1;;k+h]]];
Array[bb,Table[dd,{k+h}]]];

Examples
Enter the  parameters in tensor arbitrarily and control the results in the following cells:

ta= tensor[a,2,4,3]

type[ta]

taa = tensorToArray[ta]

TensorRank[taa]

TensorDimensions[taa]

taa[[3,1,2,3,1,2]]

tac=coords[ta]

TensorRank[tac]

TensorDimensions[tac]

tac and taa differ as Arrays, but the sorted Lists of all their elements coincide:

Sort[Flatten[tac] ]==Sort[ Flatten[taa]]

TensorRank[coords[nulltensor[1,2,3]]]

tensorToArray[tensor[a,3,0,3]]

TensorRank[tensorToArray[nulltensor[1,2,3]]]

tensorToArray[tensor[a,0,0]]

tensorToArray[tensor[a,1,0]]

tensorToArray[tensor[a,0,1]]

5.2.3. ArrayTotensor

?ArrayTotensor

ArrayTotensor[a_,k_:0] := Module[{rg,dd ,tt,ii,jj,b},
If[ArrayQ[a],rg = TensorRank[a],
Print["Argument must be an Array!"]; Return[],
Print["Argument must be an Array!"]; Return[]];
If[(k<0)||(k>rg)||Not[IntegerQ[k]], Print[k ," bad argument!"]; Return[]];
If[VectorQ[a]&&(k== 0),Return[covecTotensor[a]]];
If[VectorQ[a]&&(k== 1),Return[vecTotensor[a]]];
dd=Length[a[[1]]];
If[Not[TensorDimensions[a] ==Table[dd,{rg}]], 
Print["Array does't fit!"];Return[]];
If[k>rg, Print[k ,">", rg, "; k too large!"]; Return[]];
tt = tensor[b,k,rg-k,dd];
b[ii_,jj_] := a[[Join[ii,jj]/.List-> Sequence]];
tt];

Example vec[x]

vec[x]

ArrayQ[vec[x]]

TensorRank[vec[x]]

TensorDimensions[vec[x]]

ArrayTotensor[vec[x]]

tensorToArray[%]

Try the next cells with various parameters of tensor:

Clear[a];tensorToArray[tensor[a,1,2]]

ArrayTotensor[%,1]

equi[%,tensor[a,1,2]]

%%[{1},{2,3}]

t3=ArrayTotensor[{{a,b,c},{d,e,f},{g,h,i}}]

type[t3]

t3[{},{3,2}]

coords[t3]

tensorToArray[t3]

5.2.4. Tensor as Generalization of Array

As a Mathematica function, tensor is a kind of   “superarray”.

A tensor of type (0,0) is  a scalar represented by its symbol

t0 :=tensor[a,0,0]

t0[]

t0[{}, {}]

Formally, tensors of type (0, 0)  may be arbitrary objects, e.g. numbers:

tb =tensor[1,0,0]

type[tb]

tb[{}, {}]

Head[%]

tb[{},{i}]

or functions

tb =tensor[Sin[x],0,0]

type[tb]

tb[{}, {}]

%/.x-> 1.

Head[%%]

but not  Arrays or Lists:

b = Array[a,{2,2}]

tb=tensor[b,0,0]

tb[{},{}]

tensorToArray[tb]

This function may not work here, since

ArrayQ[%%]

Clear[b,tb]

tensor is a general structure generalizing the built-in function Array :

Clear[a,b3];b3=Array[a,{dim,dim,dim}]

t3 := tensor[a,3,0]

Table[t3[{i,j,k},{}],{i,1,dim},{j,1,dim},{k,1,dim}]

%/.a[{i_,j_,k_},{}]-> a[i,j,k]

%== b3

The coordinates of a mixed tensor form an indexed table of Arrays,  for example

Clear[a,tt];tt:=tensor[a,2,2]

type[tt]

tt[{1,2},{3,3}]

Here is the List of all its coordinates:

ttt=Flatten[Table[tt[{i,h},{j,k}],{i,1,dim},{h,1,dim},{j,1,dim},{k,1,dim}]]

Now we flatten the table of Arrays

Flatten[Table[Array[a[i,j],{dim,dim}],{i,dim},{j,1,dim}]]

and got the same, up to a minor change of notation

%==ttt/. a[{i_,j_},{r_,s_}]-> a[i,j][r,s]

5.3. The LeviCivitaTensor

LeviCivitaTensor  as special case of an epsilon tensor:

?LeviCivitaTensor

LCT3=Normal[LeviCivitaTensor[3]]

sig[{i_,j_,k_},{}]:= LCT3[[i,j,k]]

tensLCT3 = tensor[sig,3,0,3]

lct3 =ArrayTotensor[LCT3,3]

equi[tensLCT3,lct3]

Flatten[Table[tensLCT3[{i,j,k},{}]-LCT3[[i,j,k]],{i,3},{j,3},{k,3}]] == null[27]

Here is a definition of the epsilon tensors containing the LeviCivitaTensor as a special case

?eps

signature[k_][a_,{}] := eps[Range[k],a]

Remark: The definition makes sense for k ≤ dim only.

eps[Range[3],{2,1,3}]

Clear[epsilon];epsilon[k_,d_:dim] := If[k>d,Print["Argument k too large!"];Return[],tensor[signature[k],k,0,d]]

tensorToArray[epsilon[3,3]]

%==LCT3

LCT4=Normal[LeviCivitaTensor[4]];

TensorQ[LCT4]

dim= 5;epsilon[4,4][{1,2,3,4},{}] == LCT4[[1,2,3,4]]

tensorToArray[epsilon[4,4]]

%==LCT4

Also we have with

lct4 = ArrayTotensor[LCT4,4]

equi[epsilon[4,4],lct4]

dim= 3;

Table[eps[Range[3],{i,j,k}],{i,3},{j,3},{k,3}]

%==Normal[LeviCivitaTensor[3]]

5.4. TensorProduct

TensorProduct is useful for the description of tensors on two vector spaces V, W over the same field. It does not reflect the covariant and contravariant tensor structures for tensors on a fixed vector space V forming the tensor algebra. This is done by the tensor functions defined in tensalgv2.m.

dim = 3; ta = tensor[a,2,1,3];tb = tensor[b,1,1,3];

The corresponding Tensors in Mathematica are

compta = tensorToArray[ta]

comptb = tensorToArray[tb]

If the tensors ta, tb are represented by their Arrays of components compta, comptb, the TensorProduct of them is

TP=TensorProduct[compta,comptb];

TensorDimensions[TP]

TP[[1,2,3,2,1]]

The corresponding tensor of type (3,2) is

tTP= ArrayTotensor[TP,3]

The tensorproduct[ta,tb] is

tc = tensorproduct[ta,tb]

tc[{1,2,3},{2,1}]

This differs from

tTP[{1,2,3},{2,1}]

tc and  tTP differ.

equi[tc,tTP]

tc is represented by the Array comptc:

comptc = tensorToArray[tc];

TensorDimensions[comptc]

diffc := TP -comptc

diffc[[1,3,2,1,3]]

The difference  TP - comptc is not the null Array:

Flatten[diffc]== null[315]

The sorted flatten lists of the coordinates coincide:

Sort[Flatten[TP]]-Sort[Flatten[coords[tc]]]==null[3^5]

Sort[Flatten[TensorProduct[coords[ta],coords[tb]]]]== Sort[Flatten[coords[tc]]]

The Tensorproduct does not take into account the subdivision of the indices into covariant and contravariant types. 
For contravariant tensors both product gives the same result:

dim = 3;ta =tensor[a,0,3]; tb = tensor[b,0,2]; tc =tensorproduct[ta,tb]

TA = tensorToArray[ta];TB = tensorToArray[tb];TC = tensorToArray[tc];

TC== TensorProduct[TA,TB]

tc[{},{1,2,2,3,2}]

TC[[1,2,2,3,2]]

tcc = ArrayTotensor[TC]

equi[tc,tcc]

5.5. TensorContract

Unfortunately, the concepts of covariant, contravariant, and mixed tensors are missing also in the Tutorial  “Symbolic Tensors”, where detailed information about the built-in tensor functions can be found.

TensorContract doesn’t act on the list coords[ta] of the components

Clear[ta,a];ta = tensor[a,3,2,3]

TensorContract[coords[ta],{1,2}]

It acts on the corresponding Array:

compta = tensorToArray[ta];

TensorContract[compta,{1,4}]

The result of the next double contraction is a 3-covector:

tac2=TensorContract[compta,{{1,4},{2,5}}]

The corresponding contraction with tensor Output is (consider the change in the numbering of the slots!):

taa=contraction[ta,{{1,1},{2,2}}]

taac2 = tensorToArray[taa]

taac2 == tac2

For contraction only slot pairs of type{covariant,contravariant) is possible; the command

contraction[ta,{4,5}]

is refused. For Tensors this command is possible, since all slots are not distinguished:

TensorContract[compta,{4,5}]

The result is not invariant under basis transformations, it would depend on the coordinate system.

5.6. TensorWedge

?TensorWedge

TensorWedge yields an  structured Array object (calculation below)

?StructuredArray

The tensor coordinates are obtained applying Normal to this object. As an example consider

dim = 4;twa = tensor[a,2,0]; twb = tensor[b,2,0]

The wedge product as defined in tensalgv2. m is

twc =wedge[twa,twb]

We consider the corresponding Arrays

compwa = tensorToArray[twa];

compwb =  tensorToArray[twb];

compwc = tensorToArray[twc];

Applying TensorWedge to the Arrays compwa, compwb yields

twab=TensorWedge[compwa,compwb]

The tensor coordinates fill a large Array already for the considered low dimensions :

Normal[twab]

This Array coincides with the coordinate Array compwc of the  wedge product twc

Simplify[compwc ==Normal[twab]]

If the  sum of the ranks of the factors is greater than the dimension, we obtain the nulltensor

dim = 2;twa = tensor[a,2,0]; twb = tensor[b,1,0]

twc = wedge[twa,twb]

For TensorWedge applied to the coordinates arrays we get the nulltensor too, of course:

compwa = tensorToArray[twa];

compwb = tensorToArray[twb];

twab=TensorWedge[compwa,compwb]

ntwab = Normal[twab]

ntwab == tensorToArray[twc]

TensorDimensions[Normal[twab]]

We recommend the user to test arbitrary examples, inserting  types and dimension in the definitions of twa, twb above.

References

Tutorial Symbolic Tensors

[Mal] A. I. Maltzev. Grundlagen der linearen Algebra (Russ.) , Moskau 1956.

[OSI] A.L.Onishchik, Sulanke, R.  Algebra und Geometrie, Einführung. VEB Deutscher Verlag der Wissenschaften,Berlin 1977. (2. Aufl.Berlin 1986, Russ. Moscow 2004)

[OSII] A.L.Onishchik, Sulanke,R. Algebra und Geometrie, 2. Moduln und Algebren. VEB Deutscher Verlag der Wissenschaften, Berlin 1988, (Russ. Moscow 2008).

[OSIII] A.L. Onishchik,  Sulanke, R. Projektive and Cayley-Klein Geometries, ( Springer Monographs in Mathematics, Berlin Heidelberg 2006, see also my homepage.

[Soleng]  Harald H. Soleng. Tensors in Physics: CARTAN 1.8. December 28, 2011
    Copyright © 2002-2011 by Ad Infinitum
    http://www.adinfinitum.no/cartan

Steven. M. Christensen & Associates. MathTensor.

[OpF] R. Sulanke. Operations with Functions. Mathematica notebook. 2013. On my homepage.  Download.

Address

sulanke@mathematik.hu-berlin.de

Homepage:  http://www2.mathematik.hu-berlin.de/~sulanke