====== Tensor ======
----
=====Basics=====
''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.
=====Common properties =====
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.
====Indices====
Presence of indices it a key feature of all tensorial expressions. [[documentation:ref: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 [[documentation:ref:Indices]] with zero size).
[[documentation:ref: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 [[documentation:guide:tensors_and_indices]].
====Container====
Any tensor has a content (summands in case of [[documentation:ref:Sum]], multipliers in case of [[documentation:ref: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 |
(one can also find more special methods like ''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'.t
and 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)
====Iterable====
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 [[documentation:guide:tree_traversal]].
=====Creating tensors programmatically=====
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}
=====Advanced features: builders and factories=====
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)
=====See also=====
* Related guides: [[documentation:guide:tensors_and_indices]], [[documentation:guide:tree_traversal]]
* Related reference material: [[documentation:ref:sum]], [[documentation:ref:product]], [[documentation:ref:indices]], [[documentation:ref:simpletensor]], [[documentation:ref:tensorfield]]
* JavaDocs: [[http://api.redberry.cc/redberry/1.1.9/java-api/cc/redberry/core/tensor/Tensor.html| Tensor]]
* Source code: [[https://bitbucket.org/redberry/redberry/src/tip/core/src/main/java/cc/redberry/core/tensor/Tensor.java|Tensor.java]]