Product
represents a product of terms. Like any other expression, it inherits all properties of Tensor. Product
adopts the following convention on its indices: the indices of product are sorted concatenated indices of its multipliers:
def t = 'f_ba*t^c_c*h^a'.t println t.indice
> ^{ac}_{abc}
Product
sorts its arguments and collapses equal scalar terms into powers.
Product
is one of the most sophisticated entities in Redberry: contractions of indices brings additional structure of mathematical graph which is reflected in Product
by introducing several methods to access graph structure of product.
The data structure used to represent product contains three main blocks: numerical factor, subproduct of scalar factors (with indices.size() == 0
), and tensorial content (with indices.size() != 0
). Each block can be obtained via special additional methods introduced in product.
There are several features in addition to those defined in Tensor that are inherent only in Product:
.select(positions) |
returns a product of elements at specified positions |
.remove(positions) |
returns a product with removed elements at specified positions |
.factor |
returns a numerical factor of product |
.indexlessSubProduct |
returns a subproduct of indexless terms, i.e. product of those terms which indices.size() == 0 |
.dataSubProduct |
returns a subproduct of indexed terms, i.e. product of those terms which indices.size() != 0 |
.content |
returns a data structure that allows to access graph structure of product |
Consider examples:
def p = '2*a*b*f_ba*t^c_c*h^a'.t println p.select(2, 3, 5)
> a*t^{c}_{c}*h^{a}
println p.remove(2, 3, 5)
> 2*b*f_{ba}
println p.factor
> 2
println p.indexlessSubProduct
> 2*b*a
println p.dataSubProduct
> t^{c}_{c}*f_{ba}*h^{a}
The presence of indices bring a graph structure of Product: each multiplier represents a graph vertex, while contractions between indices are edges. One can check whether two graph isomorphic, i.e. that two tensors are equal to within free and dummy indices relabelling using Mappings of indices.
There is a special property .content
, which allows to access graph structure of Product
; the object returned by .content
called ProductContent
. It holds tensorial part of product and additional low-level data structures used in Redberry to represent tensorial graphs. Besides, ProductContent
provides several useful methods like .scalars
and .nonScalar
which return array of scalar sub-products and non-scalar part of tensor respectively. Consider examples:
def p = '2*a*f_a*t^ba*g_b*t^i_i*t^mn*f_nmk'.t println p.size()
> 8
def content = p.content println content.size()
> 6
println content[0..content.size()]
> [f_a, t^ba, g_b, t^i_i, t^mn, f_nmk]
println content.scalars
> [t^{i}_{i}, t^{ba}*g_{b}*f_{a}]
println content.nonScalar
> t^{mn}*f_{nmk}
In order to access low-level structure that encodes Product
graph there is .structureOfContractions
property in ProductContent
. This structure allows to check connected components of graph and check for each particular tensor with which tensors it is contracted. Without going into low-level details, let's illustrate this features by the examples:
def p = '3*c*f_i*g_b*t^i_i*t^pq*f_qpk*t^bie'.t def content = p.content //get graph representation def graph = content.structureOfContractions //array of connected components println graph.components
> [0, 1, 0, 1, 2, 0]The array of components indicates whether two tensors belongs to the same connected component of graph: two tensors at i-th and j-th positions belongs to the same component if
components[i] == components[j]
, and components[i]
is the number of corresponding component.
//get position of tensor t^bie def i = content.findIndexOf { it.equals('t^bie'.t) } //contractions of tensor t^bie def contractions = graph.getContractedWith(i) for (def c in contractions) { if (c.tensor == -1)//free index println "Free indices:" + content[i].indices[c.indicesFrom] else { println "With tensor: " + content[c.tensor] println "This indices:" + content[i].indices[c.indicesFrom] println "With indices:" + content[c.tensor].indices[c.indicesTo] } }
> Free indices:^{e} > With tensor: g_{b} > This indices:^{b} > With indices:_{b} > With tensor: f_{i} > This indices:^{i} > With indices:_{i}