# Tensors and Indices

Next topic: Einstein notation

There are three central object types in Redberry: Tensor, Indices, and Transformation. Objects of the same type share common properties and can be manipulated in a common way. Tensors and their indices are considered in this section, while transformations are discussed in Applying and manipulating transformations.

## Tensors

Each mathematical expression in Redberry is a Tensor. Using object-oriented programming terminology, this means that all mathematical types are inherited from a base super type Tensor, and thereby can be manipulated in a uniform way and share a common properties like presence of indices. Here we briefly overview types of mathematical expressions in Redberry and how one can access and modify their structure.

### Types of tensors

There are following subtypes of tensors in Redberry:

• SimpleTensor — a simple variable like x or f_mn
• TensorField — a function like f[x] or T_ab[x_m, y]
• Product — product of tensors
• Sum — sum of tensors
• Power — a power like a**2
• Complex — any number like 2 or 2+I or 1.232
• Expression — a substitution rule like x = y etc.

One can find the type of expression in the following way:

def t = 'a * T_mn + g_mn'.t
//print name of Tensor subtype
println t.class.simpleName


   > Sum


//check that t is of type Tensor
println(t instanceof Tensor)

   > true

//check that t is of type Sum
println(t instanceof Sum)

   > true

//check that t is not Product
println(t instanceof Product)

   > false


### Structure of tensors

Each mathematical expression in Redberry is a Tensor. Any Tensor has Indices and content (summands in case of sum, arguments in case of functions, etc.; thus, tensors in Redberry are containers of other tensors). The Indices of tensor can be obtained by getting the value of .indices property:

def p = 'a * F^a_{mn} + b^a * h_m * h_n'.t
//accessing indices of tensor
println p.indices

   > ^{a}_{mn}

Indices of tensors will be discussed below.

The following lines highlight container features of tensors:

def p = 'a * F^a_{mn} + b^a * h_m * h_n + t_mn^a'.t
//size of tensor as container
println p.size()

   > 3

//getting elements by index
println( [p[0], p[2] ] )

   > [a*F^a_{mn},  t_mn^a]

//get several elements by index as a list
println( p[0, 2] as List )

   > [a*F^a_{mn},  t_mn^a]

//enumerating elements
p.each{ i -> println i }

   > a*F^a_{mn}+
> b^a*h_m*h_n
> t_mn^a

//convert tensor to a list of tensors
println p as List

   > [a*F^a_{mn}, b^a*h_m*h_n, t_mn^a]

//get range as list
println p[0..1] as List

   > [a*F^a_{mn}, b^a*h_m*h_n]


For more specialised methods of tensors see Tensor page.

### Modifying and instantiating tensors

In most cases expressions are modified using transformations (expand or collect terms, eliminate metrics etc.). However, sometimes it is useful to modify expressions “in place” (set or remove parts of expressions etc.). Tensors are immutable in Redberry and all modification operations return new instances. So, in order to modify expressions one can do:

def t = 'A_i + C_i + D_i'.t
def x = t.set(1, 'B_i'.t)
println x

   > A_i + B_i + D_i

def y = t.remove(0)
println y

   > C_i + D_i

y = t.remove(0, 2)
println y

   > C_i

println t

   > A_i + C_i + D_i


As one can see that original tensor t was not modified, but new tensors x and y were created.

In order to create tensors programmatically, Redberry defines all arithmetic operations for Tensor objects:

def x = 'x'.t, y = 'y'.t
def t = (-x+sin(y))**2/(x+sin(-y))+x-sin(y)
println t

   > 2*Sin[-y]+2*x

Such syntax, however, may be inconvenient when dealing with indexed objects, since names of variables do not reflect structure of indices of expressions. For further reading see Tensor page.

See Mappings of indices on how to modify indices of tensor.

## Indices

The presence of indices is a main distinguishing property of tensorial CASs. As mentioned above, there is an .indices property defined for each expression in Redberry. The returned object is an object of type Indices. It has a number of methods and properties to work with it. Consider some examples:

def t = '2*x_am*f^m*(a^n+b^n)'.t
def ind = t.indices
println([ind, ind.size()])

   > [^{mn}_{am}, 4]

//get only free indices
println ind.free

   > ^{n}_{a}

//get inverted indices
println ind.inverted

   > ^{am}_{mn}

//get upper indices
println ind.upper

   > ^{mn}


Methods used in the above example, are inherent in any indices object. Their names clearly implies their meaning. For further information about properties of Indices see Indices.

### Ordering of indices

Although the presence of indices is inherent in all tensors, different types of expressions have different subtypes of Indices. This difference arises from availability or unavailability of information about indices ordering. Consider indices of SimpleTensor:

def simple = 'F_{mn}^{\\beta\\alpha}_{ba\\alpha}'.t
println simple.indices

   > _{mnba}^{\beta\alpha}_{\alpha}


The indices of SimpleTensor are SimpleIndices. The ordering of indices inside SimpleIndices is same as specified by the user — this is the main distinguishing property of SimpleIndices. In other words, reordering of indices will result in changing of mathematical sense of corresponding simple tensor (unless this tensor is symmetric with respect to this permutation, see Symmetries of tensors). However, since each index belongs to some index type (e.g. Greek upper/lower case or Latin upper/lower case, etc.) and indices of different types have different mathematical nature (e.g. Greek indices are Lorentz, Latin are SU(N) etc.), the relative positions of indices with different types is not important. Thus, Redberry sorts indices of simple tensors according to their types, preserving the relative order of indices belonging to the same type (as in the above example). Indices of TensorField are also SimpleIndices and follows the same ordering rules.

Another type of indices is inherent in all other types of tensors. Consider the following Product:

def pr = 'F_{mn}*F^{\\beta\\alpha}*F_{ba\\alpha}'.t
println pr.indices

   > ^{\alpha\beta}_{abmn\alpha}


From the mathematical point of view, the ordering of Product indices is undefined. This allows to set up some certain ordering rules (mainly for technical reasons, related to performance). As one can see from the example, all indices are sorted according to the following rules: first upper then lower, first Latin then Greek, in each group of indices with the same type indices are sorted in lexical order. The similar rules are adopted for Sum:

def sum = 'R^a_amn^\alpha + K^i_inm^\alpha'.t
println sum.indices

   > ^{\alpha}_{mn}

The only difference, is that according to the sense of Sum, its Indices are actually free indices of its summands.

All methods from above listings (.inverted, .free etc.), return objects with the same rules of ordering as in the initial Indices.

### Single index

At the low-level, Redberry stores each single index as 32-bit integer that encodes all information about index: state, type and name (serial number in the alphabet). Within a particular index type, there are $2^{16}$ possible names, which can be inputted using the subscript like

def t = 'X_{a_1 a_{122} b_9 \\mu_3}'.t


One can access different properties of single index in the following way:

def indices = '^abc'.si
//take second index
def index = indices[1]
//print index
println index.toStringIndex()

   > ^b

//get index type
println index.type

   > LatinLower

//check whether index is upper
println index.isUpper()

   > true

//convert index to covariant and print
println index.toLower().toStringIndex()

   > _b

//invert index and print
println index.invert().toStringIndex()

   > _b


### Parsing indices

In order to parse Indices from its string representation one can use .i property:

def indices = '_{\\beta n m}^{\\alpha}'.i
println indices

   > ^{\alpha}_{mn\beta}

In the case of SimpleIndices one can use .si property:
def indices = '_{\\beta n m}^{\\alpha}'.si
println indices

   > _{nm\beta}^{\alpha}