Tensor
is a parent object type for all mathematical expressions used in Redberry. Thus, all expressions in Redberry are tensors. From the programming point of view that means, that Tensor
is a parent class for all types of mathematical expressions used in Redberry (sums, products, etc.). Tensor
defines common properties and functional shared in all its subtypes.
There are several unifying properties of all mathematical expressions like size, indices, container properties etc. All these properties can be obtained from any expression using methods defined in Tensor
. Let us consider each property in detail.
Presence of indices it a key feature of all tensorial expressions. Indices can be obtained for any expression using .indices
property (or equivalently getIndices()
method):
println 'g_ab * T^cd'.t.indices
> ^cd_ab
println 'T_ba^dc'.t.indices
> _ba^dc(in case of pure scalars
.indices
will return Indices with zero size).
Indices is a feature which is inherent in any type of expression (sum, product, function, simple tensor etc.). Meanwhile, indices of different types of expressions can have slightly different nature. For example, indices of SimpleTensor
will appear exactly in that order as they were specified by the user, while indices of e.g. Product
will be always sorted in some way; indices of Sum
are sorted free indices of its summands etc. Thus, Tensor
defines only abstract method .indices
while each particular subtype of Tensor
is responsible for implementation. For more details see Tensors and Indices.
Any tensor has a content (summands in case of Sum, multipliers in case of Product etc.). The following table summarises, how one can access container properties of expressions:
T.size() |
returns a size of tensor, i.e. number of summands in case of sum, number of arguments in case of function etc. |
T[i] |
returns element at i-th position |
T[i..j] |
returns a list of elements from i-th (inclusive) to j-th (exclusive) |
T[i, j, k] |
returns a list of elements at specified positions |
T.set(i, expr) |
returns a new tensor with expr at i-th position |
remove
in Sum and Product)
One can also construct Tensor
from a list in the following way
def list = ['a', 'b', 'c'] println list as Sum // same as list.sum()
> a + c + b
def t = 'a + b + c + d + e + f + g'.t println t[1..4] as Product // same as t[1..4].multiply()
> b*d*e
Note, that the ordering of expressions (summands in sum, or multipliers in product) can be different from that one in which the expression was inputted by the user. So, if one inputted e.g.
def t = 'a + b + c'.tand then take
t[0]
, the result will not necessary be a
but also can be b
or c
. This ordering will be always same during a single run of Redberry, by can be changed from run to run. In order to make this ordering same in all runs one can put the following line in the beginning of the code:
CC.resetTensorNames(1)
This simply means that one can iterate through expression content using for
loop:
def expr = 'a*(b+e)*Sin[c]*d*(e+f)'.t def sums = [] for (def t in expr) //iterating through expr if (t instanceof Sum) //taking only sums sums << t println sums
> [b+e, e+f]
println sums as Sum
> b + 2*e + f
In such a way one can iterate only through direct "children" of expression. In order to iterate through all expression parts, one should use Tree traversal.
In most cases one will input expression by hand, but often it becomes necessary to construct tensors programmatically from a given content. There are a variety ways to do that in Redberry.
First of all, Redberry allows to use simple algebraic operators on tensors:
def x = 'x'.t, y = 'y'.t println x/y + sin(x*y) + y**3
> x/y + Sin[x] + y**3(however such syntax may be inconvenient for indexed expressions in some cases, since it does not reflect presence of indices)
Sometimes we need to construct expression from a given content. Here is a some useful methods that allow to do that:
sum(exprs) |
returns a sum of specified expressions |
multiply(exprs) |
returns a product of specified expressions |
simpleTensor(head, indices) |
returns a simple tensor with specified name and simple indices |
field(head, indices, args) |
returns a tensor field with specified name, simple indices and arguments |
fieldDerivative(field, dIndices, arg, order) |
returns a derivative of field with respect to specified argument, argument indices and order |
Consider examples:
def list = ['a', 'b', 'c'].t println sum(list)
> a+b+c
println multiply(list)
> a*b*c
println simpleTensor('f', '_abc'.si)
> f_abc
println field('f', '_abc'.si, list)
> f_abc[a,b,c]
println fieldDerivative('f_a[x_a, y_b]'.t, '_ij'.si, 1, 2)
> f~(0,2)_{aij}[x_a, y_b]
One should be careful when using multiply()
to multiply tensors with dummy indices. By default, multiply()
will not rename conflicting dummy indices:
println multiply('A^a_a'.t, 'B^a_a'.t)
> InconsistentIndicesException: Inconsistent index ^a.If one need to rename such dummies, one can use
multiplyAndRenameConflictingDummies()
:
println multiplyAndRenameConflictingDummies('A^a_a'.t, 'B^a_a'.t)
> A^{a}_{a}*B^{b}_{b}
In some cases (especially when writing custom transformations) it becomes necessary to rebuild expression without knowing it particular type in advance. For this purpose, each expression provide a “builder” (via .builder
property), which allows to construct tensor of same type.
For example, suppose we need to implement a “toy” transformation which simply removes all simple tensors from product or sum. This can be done in the following way:
def tr = { expr -> def builder = expr.builder for (def t in expr) if (t.class != SimpleTensor) builder << t builder.build() } as Transformation def product = 'a*(b+e)*Sin[c]*d*(e+f)'.t println tr >> product
> Sin[c]*(e+b)*(f+e)
def sum = 'a+b*e+Sin[c]+d+e*f'.t println tr >> sum
> Sin[c]+b*e+e*f
When the length of result is known in advance, one can use .factory
. Consider a “toy” transformation which appends tensor a
to expression:
def tr = { Tensor expr -> def data = expr as List data << 'a'.t expr.factory.create(data) } as Transformation def product = 'f[x]*Sin[a]'.t println tr >> product
> a*Sin[a]*f[x]
println tr >> (product as List).sum()
> a+Sin[a]+f[x]
The general convention on builders and factories is that if t
is any expression, then the result of rebuild is same:
assert t == (t.builder << (t as List)).build() assert t == t.factory.create(t as List)