====== Tensors and Indices ======
Next topic: [[documentation:guide:einstein_notation]]
---- There are three central object types in Redberry: [[documentation:ref:Tensor]], [[documentation:ref:Indices]], and [[documentation:ref: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 [[documentation:guide:applying_and_manipulating_transformations]]. =====Tensors===== Each mathematical expression in Redberry is a [[documentation:ref:Tensor]]. Using object-oriented programming terminology, this means that all mathematical types are inherited from a base super type [[documentation:ref: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: * [[documentation:ref:SimpleTensor]] --- a simple variable like ''x'' or ''f_mn'' * [[documentation:ref:TensorField]] --- a function like ''f[x]'' or ''T_ab[x_m, y]'' * [[documentation:ref:Product]] --- product of tensors * [[documentation:ref:Sum]] --- sum of tensors * [[documentation:ref: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 [[documentation:ref:Tensor]]. Any [[documentation:ref:Tensor]] has [[documentation:ref:Indices]] and content (summands in case of sum, arguments in case of functions, etc.; thus, tensors in Redberry are containers of other tensors). The [[documentation:ref: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} [[documentation:ref:Indices]] of tensors will be discussed [[#Indices|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 [[documentation:ref: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 [[documentation:ref: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 [[documentation:ref:Tensor]] page. See [[documentation:guide: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 [[documentation:ref: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 [[documentation:ref:Indices|Indices]]. ====Ordering of indices==== Although the presence of indices is inherent in all tensors, different types of expressions have different subtypes of [[documentation:ref:Indices]]. This difference arises from availability or unavailability of information about indices ordering. Consider indices of [[documentation:ref:SimpleTensor]]: def simple = 'F_{mn}^{\\beta\\alpha}_{ba\\alpha}'.t println simple.indices > _{mnba}^{\beta\alpha}_{\alpha} The indices of [[documentation:ref:SimpleTensor]] are [[documentation:ref:SimpleIndices]]. The ordering of indices inside [[documentation:ref:SimpleIndices]] is same as specified by the user --- this is the main distinguishing property of [[documentation:ref: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 [[documentation:guide:Symmetries of Tensors]]). However, since each index belongs to some [[documentation:ref:IndexType|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 [[documentation:ref:TensorField]] are also [[documentation:ref:SimpleIndices]] and follows the same ordering rules. Another type of indices is inherent in all other types of tensors. Consider the following [[documentation:ref: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 [[documentation:ref: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 [[documentation:ref: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 [[documentation:ref:Sum]], its [[documentation:ref: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 [[documentation:ref: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 [[documentation:ref: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 [[documentation:ref:SimpleIndices]] one can use ''.si'' property: def indices = '_{\\beta n m}^{\\alpha}'.si println indices > _{nm\beta}^{\alpha} =====See also===== * Related guides: [[documentation:guide:applying_and_manipulating_transformations]]