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.
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.
There are following subtypes of tensors in Redberry:
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
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.
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*xSuch 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.
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.
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.
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
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}