Standard form of mathematical expressions

Next topic: Symmetries of tensors

A core feature of any CAS is its ability to reduce arbitrary expression to some standard form (SF) in which it is then used everywhere in manipulations. This approach facilitates comparison and matching of expressions and gives a way for more robust and fast algorithms for almost all CAS operations. Redberry uses the same paradigm: any intermediate and resulting expression is guaranteed to be in the SF.

Standard form of indexless expressions

The standard form of indexless expressions is same as in the majority of other CASs. Consider the following examples, which give an idea of SF in Redberry:

println '(a-b)+c+(b-a)'.t

   > c

println 'c*(a-b)*(b-a)/c'.t

   > -(a-b)**2

println '2/3-27**(1/3)/9'.t

   > 1/3

println 'Sin[2 + 2.*I]**(1/4)'.t

   > 1.38307 - 0.144188*I

The first example demonstrates that Redberry performs the reduction of similar terms. The second — that same (to within a sign) multipliers are collected into powers or reduced. Numbers are always collected and reduced if possible. If expression contains floating-point numbers, then it will be completely reduced (calculated). This behaviour is similar to the majority of “scalar” CASs and needs no more detailed explanation.

Standard form of indexed expressions

The additional conventions on standard form arise in expressions that contain tensors. The most remarkable convention is on the SF of Sum. It is best to demonstrate it by example:

println 'a*F_mn+(a+b)*F_mn'.t

   > (2*a+b)*F_{mn}

As one can see, Redberry tries to factor out tensorial and leave “scalar” (with zero sized Indices) parts of products; this is a general rule used by Redberry for bringing sums into standard form. Consider another example:
println '(x_a^a + y_b^b)*X_m*X_n + (z_a^a - y_d^d)*X_m*X_n'.t

   > (x_{a}^{a}+z_{a}^{a})*X_{m}*X_{n}

Here Redberry factored tensorial part X_m*X_n since both sums (x_a^a+y_b^b) and (z_n^n-y_d^d) have indices with zero size (because Indices of Sum are free indices of its summands):
println 'x_a^a + y_b^b'.t.indices.size()

   > 0

On the other hand, the following tensor will not be further reduced:
println 'x_a^a * X_m*X_n + y_a^a * X_m*X_n'.t

   > x_a^a * X_m*X_n + y_a^a * X_m*X_n

This is because each multiplier of e.g. x_a^a * X_m*X_n (and in particular x_a^a) has indices of nonzero size:
def ind = 'x_a^a'.t.indices
//only free indices has zero size
println [ind.size(), ind.free.size()]

   > [2, 0]


Consider the last example:

println '(x_a^a + y_b^b)*X_m*X^m + (z_m^m - y_d^d)*X_a*X^a'.t

   > (x_{a}^{a}+z_{a}^{a})*X^{m}*X_{m}

Here Redberry factored the same tensorial part (X_m*X^m equals to X_a*X^a) and collected similar terms in the formed Sum. One can also see that Redberry automatically relabelled conflicting dummy indices.

The ordering of terms

The important detail is ordering of summands and multipliers within sums and products. Elements of sums and products are sorted by their 32 bit hash codes. Hashes for simple tensors (e.g. x or k_p) are generated randomly at each Redberry run, while hashes of complicated tensors (e.g. Sin[x]*k_i) are calculated according to certain complex rules. Actually, hash functions are organised in such a way that they are “insensitive” for particular names of indices but “sensitive” for their contractions. So, renaming of dummy or free indices does not affect hash code, but any modification of structure of contractions (e.g. contraction of two free indices) does.

Usage of the pseudorandom generator allows to obtain nearly uniform distribution of hashes of tensors, which significantly improves the performance. However, the ordering of expressions changes from run to run, and e.g. product a*b*c will be sorted differently at different runs (b*c*a or b*a*c etc.). Of course, all similar expressions will have similar ordering in current session. One can fix the seed of pseudorandom generator inside, so that expressions will have same ordering from run to run, by putting the following line in the beginning of the code:

import cc.redberry.core.context.CC
import cc.redberry.groovy.Redberry

use(Redberry) {
CC.resetTensorNames(1)