This shows you the differences between two versions of the page.
| — | 
                    documentation:guide:programming_with_redberry [2015/11/21 12:33] (current) | 
            ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Programming with Redberry ====== | ||
| + | <html> | ||
| + | <div class="text-right" style="font-size: 15px; "> | ||
| + | </html> | ||
| + | Next topic: [[documentation:guide:notes_on_internal_architecture]] | ||
| + | <html> | ||
| + | <span class="glyphicon glyphicon-arrow-right"></span> | ||
| + | </div> | ||
| + | </html> | ||
| + | |||
| + | ---- | ||
| + | =====Basics===== | ||
| + | Redberry interface is written in [[http://groovy.codehaus.org|Groovy]] and is intended to be used within the Groovy environment. Groovy is a general-purpose programming language and one can use all features and programming language constructs that are available in Groovy: looping, branching, functions, lambda-expressions, lists, classes etc. Besides, Redberry provides a specialized domain-specific programming constructs which are useful for computer algebra purposes.  | ||
| + | |||
| + | The documentation on general programming constructs can be found on Groovy website. The most useful things are: | ||
| + | * Looping: [[http://groovy.codehaus.org/Looping|http://groovy.codehaus.org/Looping]] | ||
| + | * Branching: [[http://groovy.codehaus.org/Logical+Branching|http://groovy.codehaus.org/Logical+Branching]] | ||
| + | * Collections:  | ||
| + | * Overview: [[http://groovy.codehaus.org/Collections|http://groovy.codehaus.org/Collections]] | ||
| + | * Detailed examples: [[http://groovy.codehaus.org/JN1015-Collections|http://groovy.codehaus.org/JN1015-Collections]] | ||
| + | * Functions: [[http://groovy.codehaus.org/Closures|http://groovy.codehaus.org/Closures]] | ||
| + | * Strings: [[http://groovy.codehaus.org/JN1525-Strings|http://groovy.codehaus.org/JN1525-Strings]] | ||
| + | |||
| + | One can find a comprehensive documentation on other Groovy features on [[http://groovy.codehaus.org|Groovy website]].  | ||
| + | |||
| + | |||
| + | |||
| + | =====Examples===== | ||
| + | Let us consider some applications of Redberry that involve programming. | ||
| + | |||
| + | ====Basic constructs==== | ||
| + | Consider some basic programming constructs: | ||
| + | |||
| + | === • Looping=== | ||
| + | Loop over expression (or list/set/array etc.): | ||
| + | <sxh groovy; gutter: false> | ||
| + | def t = 'a + b + c'.t | ||
| + | for(def a in t) | ||
| + | println a | ||
| + | //or equivalently | ||
| + | for(int i=0; i < t.size(); ++i) | ||
| + | println t[i] | ||
| + | //one more way | ||
| + | t.each { a-> | ||
| + | println a | ||
| + | } | ||
| + | //using while | ||
| + | def i =0 | ||
| + | while(i < t.size()){ | ||
| + | println t[i++] | ||
| + | } | ||
| + | </sxh> | ||
| + | For further details see Groovy documentation: | ||
| + | * Looping: [[http://groovy.codehaus.org/Looping|http://groovy.codehaus.org/Looping]] | ||
| + | * Collections:  | ||
| + | * Overview: [[http://groovy.codehaus.org/Collections|http://groovy.codehaus.org/Collections]] | ||
| + | * Detailed examples: [[http://groovy.codehaus.org/JN1015-Collections|http://groovy.codehaus.org/JN1015-Collections]] | ||
| + | |||
| + | === • Logical branching=== | ||
| + | Typical if-else statement: | ||
| + | <sxh groovy; gutter: false> | ||
| + | def t = 'a + b + c'.t | ||
| + | if( t.size() > 3){ | ||
| + | //do something | ||
| + | } else if (t.size() == 3){ | ||
| + | //do something else | ||
| + | } else{ | ||
| + | //do something  else else | ||
| + | } | ||
| + | </sxh> | ||
| + | It is important to note, that in order to compare tensors, one should use ''.equals()'' method instead of ''=='' operator (this issue will be fixed in Groovy 3.0 and one can use ''=='' for comparison everywhere): | ||
| + | <sxh groovy; gutter: false> | ||
| + | def expr1 = 'a_i + b_i + c_i'.t | ||
| + | def expr2 = 'a_j + b_j + c_j'.t | ||
| + | |||
| + | if ( expr1.equals(expr2) ){ | ||
| + | //somth | ||
| + | } | ||
| + | </sxh> | ||
| + | |||
| + | For further details see Groovy documentation: | ||
| + | * Branching: [[http://groovy.codehaus.org/Logical+Branching|http://groovy.codehaus.org/Logical+Branching]] | ||
| + | |||
| + | === • Functions === | ||
| + | Inside a Groovy script one can define function using closure: | ||
| + | <sxh groovy; gutter: false> | ||
| + | def pow3 = { x -> x**3 } | ||
| + | println pow3(3) //gives 27 | ||
| + | |||
| + | def max = { x, y -> x > y ? x : y } | ||
| + | println max(2, 3) //gives 3 | ||
| + | </sxh> | ||
| + | The last statement inside a closure is automatically considered as return statement. | ||
| + | |||
| + | In Redberry a function that transform expression to another expression is a [[documentation:ref:Transformation]]. In order to convert a closure to a transformation one can do: | ||
| + | <sxh groovy; gutter: false> | ||
| + | def tr = { expr -> | ||
| + | //invert indices | ||
| + | (expr.indices % expr.indices.inverted) >> expr | ||
| + | } as Transformation | ||
| + | |||
| + | println tr >> 't_ab'.t // gives t^ab | ||
| + | //use & to join transformation  | ||
| + | println((EliminateMetrics & tr) >> 'g^ab*t_ac'.t) //gives t_b^c | ||
| + | </sxh> | ||
| + | |||
| + | For further details see Groovy documentation: | ||
| + | * Functions: [[http://groovy.codehaus.org/Closures|http://groovy.codehaus.org/Closures]] | ||
| + | |||
| + | === • Iterables=== | ||
| + | Lists and expressions are //iterable//, which means that one can use for example '' .each { } '' in order to iterate over expression elements. Additionally, there are some other useful methods that allow to iterate over a list (or expression) and select elements: | ||
| + | <sxh groovy; gutter: false> | ||
| + | def t = 'a_i + b_i+ c_i'.t | ||
| + | t.eachWithIndex { e, i -> println "$i: $e" } // gives 0: a_i 1: b_i 2: c_i | ||
| + | |||
| + | println t.collect { 2 * it } // gives [2*a_i, 2*b_i, 2*c_i] | ||
| + | |||
| + | println t.find { (it % 'a_k'.t).first != null } // gives a_i | ||
| + | |||
| + | println t.findAll { it.indices.lower.size() < 2 } // gives [a_i, b_i, c_i] | ||
| + | </sxh> | ||
| + | To convert a list to [[documentation:ref:Sum]] or [[documentation:ref:Product]] one can use ''.sum()'' or ''.multiply()'' methods: | ||
| + | <sxh groovy; gutter: false> | ||
| + | // gives a_i + b_i + c_i | ||
| + | println t.findAll( { it.indices.lower.size() < 2 } ).sum() | ||
| + | </sxh> | ||
| + | For further details see Groovy documentation: | ||
| + | * Collections: [[http://groovy.codehaus.org/JN1015-Collections|http://groovy.codehaus.org/JN1015-Collections]] | ||
| + | |||
| + | ====Toy example==== | ||
| + | Let us consider function that selects all elements with size less than ''n'' from [[documentation:ref:Sum]] and write them to list: | ||
| + | <sxh groovy; gutter: true> | ||
| + | def trunc = { expr, n -> | ||
| + | //if expr is not Sum --- return empty List | ||
| + | if (expr.class != Sum) | ||
| + | return [] | ||
| + | //resulting list | ||
| + | def r = [] | ||
| + | //loop over summands | ||
| + | for (def s in expr) { | ||
| + | //check summand size | ||
| + | if (s.class == Product && s.size() >= n) | ||
| + | continue; | ||
| + | r << s | ||
| + | } | ||
| + | return r | ||
| + | } | ||
| + | |||
| + | println trunc('a + a*b + a*b*c + a*b*c*d'.t, 3) | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > [a, a*b] | ||
| + | </sxh> | ||
| + | <sxh groovy; gutter: true; first-line: 18 > | ||
| + | println trunc('a + a*b + a*b*c + a*b*c*d'.t, 4) | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > [a, a*b, a*b*c] | ||
| + | </sxh> | ||
| + | |||
| + | The for-loop at lines 8-13 can be rewritten equivalently using ''each'' [[http://groovy.codehaus.org/Closures|closure]]: | ||
| + | <sxh groovy; gutter: false> | ||
| + | expr.each { s -> | ||
| + | //check summand size | ||
| + | if (s.class != Product || s.size() < n) | ||
| + | r << s | ||
| + | } | ||
| + | </sxh> | ||
| + | Convert resulting list to a new [[documentation:ref:Sum]]: | ||
| + | <sxh groovy; gutter: true; first-line: 19> | ||
| + | def selected = trunc('a + a*b + a*b*c + a*b*c*d'.t, 4) | ||
| + | def newSum = selected.sum() | ||
| + | println newSum | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > a + a*b + a*b*c | ||
| + | </sxh> | ||
| + | Implement Redberry [[documentation:ref:Transformation]] that removes all elements with size greater or equals than ''4'' from [[documentation:ref:Sum]]: | ||
| + | <sxh groovy; gutter: true; first-line: 22> | ||
| + | //transformation that removes elements from sum with size >=4 | ||
| + | def truncTr4 = { expr -> | ||
| + | expr.class == Sum ? trunc(expr, 4).sum() : expr | ||
| + | } as Transformation | ||
| + | //apply transformation using >> | ||
| + | println truncTr4 >> 'a + a*b + a*b*c + a*b*c*d'.t | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > a + a*b + a*b*c | ||
| + | </sxh> | ||
| + | Here we omitted the ''return'' keyword at line 24. Let us generalise [[documentation:ref:Transformation]] ''truncTr4'' to work with arbitrary ''n'': | ||
| + | <sxh groovy; gutter: true; first-line: 28> | ||
| + | //transformation that removes elements from sum with size >=n | ||
| + | def truncTr = { n -> | ||
| + | { expr -> | ||
| + | expr.class == Sum ? trunc(expr, n).sum() : expr | ||
| + | } as Transformation | ||
| + | } | ||
| + | //apply transformation using >> | ||
| + | println truncTr(4) >> 'a + a*b + a*b*c + a*b*c*d'.t | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > a + a*b + a*b*c | ||
| + | </sxh> | ||
| + | [[documentation:ref:Transformation]] ''truncTr'' will apply only to the top algebraic level. In order to implement [[documentation:ref:Transformation]] that will change all sums in expression, one can use [[Tree traversal]]: | ||
| + | <sxh groovy; gutter: true; first-line: 36> | ||
| + | //transformation that applies to each part of expression | ||
| + | def truncAll = { n -> | ||
| + | { expr -> | ||
| + | expr.transformParentAfterChild { e -> | ||
| + | e.class == Sum ? truncTr(n) >> e : e | ||
| + | } | ||
| + | } as Transformation | ||
| + | } | ||
| + | //this will do nothing | ||
| + | println truncTr(4) >> 'x*(a + a*b + a*b*c + a*b*c*d)'.t | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > x*(a + a*b + a*b*c + a*b*c*d) | ||
| + | </sxh> | ||
| + | <sxh groovy; gutter: true; first-line: 46> | ||
| + | //this will be applied | ||
| + | println truncAll(4) >> 'x*(a + a*b + a*b*c + a*b*c*d)'.t | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > x*(a + a*b + a*b*c) | ||
| + | </sxh> | ||
| + | |||
| + | ====Advanced example: Fourier transform of Lagrangian==== | ||
| + | Let us write a function that performs Fourier transform of Langrangian. So, for example: | ||
| + | |||
| + | \[ | ||
| + | \int d^4 x\left( -\frac{1}{4}\left(\partial_\mu A_\nu (x) - \partial_\nu A_\mu (x)\right)^2 \right)= | ||
| + | \int d^4 p\left( \frac{1}{2}A_\mu(p) \left(g^{\mu\nu} p^2 - p^\mu p^\nu \right) A_\nu(-p) \right) | ||
| + | \] | ||
| + | |||
| + | Let's implement Redberry [[documentation:ref:Transformation]] that transforms l.h.s. integrand to r.h.s integrand.  | ||
| + | |||
| + | Obviously this transformation will separately transform each term in Lagrangian. So, let's first implement  Fourier transform of a single [[documentation:ref:product]]. For simplicity we assume that we have only one field. The algorithm sequentially goes through [[documentation:ref:product]] multipliers; for each field (e.g. $\partial_{a}\partial_{b} A_c(x_a)$) it generates a new momentum (e.g. $p2_a$) and replaces field argument with momentum and partial derivatives with products of momentum (e.g. $\partial_{a}\partial_{b} A_c(x_a) \to i \times  p2_a \times i \times p2_b \times A_c(p_a)$). The last generated momentum should be replaced with negated sum of all other momentums (e.g. $p3_i \to -p0_i - p1_i -p2_i$). Here's the implementation: | ||
| + | <sxh groovy; gutter: true> | ||
| + | //transform product of tensors | ||
| + | def fourierProduct = { product -> | ||
| + | //list of generated momentums | ||
| + | def momentums = [] | ||
| + | //the result | ||
| + | def result = product.builder | ||
| + | //counter of momentums | ||
| + | def i = 0 | ||
| + | //let's transform each term in product | ||
| + | for (def term in product) { | ||
| + | //transform those terms that are functions | ||
| + | if (term.class == TensorField) { | ||
| + | //generate next momentum | ||
| + | def momentum = "p${i++}".t | ||
| + | momentums << momentum | ||
| + | //replace function argument with momentum | ||
| + | // (e.g. f~(2)_{a bc}[x_a] -> f~(2)_{a bc}[p_a]) | ||
| + | term = "${term[0]} = $momentum${term[0].indices}".t >> term | ||
| + | //in case of derivative we need | ||
| + | // to replace partials with momentums | ||
| + | if (term.isDerivative()) { | ||
| + | //indices of differentiating variables | ||
| + | def dIndices = term.partitionOfIndices[1] | ||
| + | //extract just parent field from derivative | ||
| + | // (e.g. f~(2)_{a bc}[p_a] -> f_a[p_a]) | ||
| + | term = term.parentField | ||
| + | //multiply by momentums | ||
| + | // (e.g. f~(2)_{a bc}[p_a] -> I*p_b*I*p_c*f_a[p_a]) | ||
| + | dIndices.each { indices -> | ||
| + | term *= "I * $momentum $indices".t | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | //put transformed term to new product | ||
| + | result << term | ||
| + | } | ||
| + | //result | ||
| + | def r = result.build() | ||
| + | //we must replace the last momentum with -(sum of other momentums) | ||
| + | def rhs = '0'.t | ||
| + | //sum generated momentums except last one | ||
| + | momentums.eachWithIndex { momentum, c -> | ||
| + | if (c != momentums.size() - 1) | ||
| + | rhs -= "${momentum}_a".t | ||
| + | } | ||
| + | //replace last momentum with sum of others and return | ||
| + | "${momentums[momentums.size() - 1]}_a = $rhs".t >> r | ||
| + | } as Transformation | ||
| + | </sxh> | ||
| + | |||
| + | The final transformation for a sum of terms is: | ||
| + | <sxh groovy; gutter: true; first-line: 49> | ||
| + | //transform sum of products | ||
| + | def fourierSum = { expr -> | ||
| + | //expand all brackets and unfold powers of scalar tensors | ||
| + | expr = (ExpandAndEliminate & PowerUnfold) >> expr | ||
| + | //apply fourierProduct to each summand: | ||
| + | expr = expr.collect { s -> fourierProduct >> s }.sum() | ||
| + | //simplify and return: | ||
| + | ExpandAndEliminate >> expr | ||
| + | } as Transformation | ||
| + | </sxh> | ||
| + | |||
| + | Consider, for example, Lagrangian of electromagnetic field : | ||
| + | |||
| + | <sxh groovy; gutter: true; first-line: 58> | ||
| + | def emTensor = 'F_ab := A~(1)_ab[x_a] - A~(1)_ba[x_a]'.t | ||
| + | def lagrangian = '-(1/4)*F_ab*F^ab - 1/(2*x)*(A~(1)_a^a[x_a])**2'.t | ||
| + | def fourier = (fourierSum & Collect['A_a[p_a]'.t, Factor]) >> lagrangian | ||
| + | println fourier | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > A_{a}[p0_{a}]*A^{c}[-p0_{a}]* | ||
| + | ((1/2)*x**(-1)*(-1+x)*p0^{a}*p0_{c}-(1/2)*p0_{b}*p0^{b}*d^{a}_{c}) | ||
| + | </sxh> | ||
| + | One can use this result in order to inverse the quadratic form in the Lagrangian (using [[documentation:ref:Reduce]] function) and obtain propagator: | ||
| + | <sxh groovy; gutter: true; first-line: 62> | ||
| + | def q2 = 'K^a_c := (1/2)*x**(-1)*(-1+x)*p0^a*p0_c-(1/2)*p0_b*p0^b*d^a_c'.t | ||
| + | def options = [ExternalSolver : [ | ||
| + | Solver: 'Mathematica', | ||
| + | Path  : '/usr/local/bin']] | ||
| + | def emProp = Reduce(['2*K^a_i*P^i_b = d^a_b'.t], ['P_ab'], options) | ||
| + | println emProp | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > [[P_{ab} = | ||
| + | (p0^{c}*p0_{c})**(-2)*(1-x)*p0_{a}*p0_{b}-(p0^{c}*p0_{c})**(-1)*g_{ab}]] | ||
| + | </sxh> | ||
| + | which is a well known result: | ||
| + | |||
| + | \[ | ||
| + | \frac{1}{p^2} \left( -g_{ab} + (1-x)\frac{p_a p_b}{p^2}\right) | ||
| + | \] | ||
| + | |||
| + | Another example: let's find a propagator for a Fierz-Pauli Lagrangian in $D$ dimensions: | ||
| + | |||
| + | \[ | ||
| + | L = -\frac{1}{2} \partial_l h_{mn} \partial^l h^{mn} | ||
| + | + \partial_m h_{nl} \partial^n h^{ml} | ||
| + | - \partial^m h_{ml} \partial^l h^n_n | ||
| + | + \frac{1}{2} \partial_l h^m_m \partial^l h^n_n | ||
| + | -\frac{1}{2} m^2 \left (h_{mn} h^{mn} - h^a_a h^b_b \right) | ||
| + | \] | ||
| + | |||
| + | <sxh groovy; gutter: true; first-line: 68> | ||
| + | def FierzPauli = ''' | ||
| + | -(1/2)*h~(1)_mnl[x_a]*h~(1)^mnl[x_a] | ||
| + | + h~(1)_nlm[x_a]*h~(1)^mln[x_a] | ||
| + | - h~(1)_ml^m[x_a]*h~(1)_n^nl[x_a] | ||
| + | + (1/2)*h~(1)^m_ml[x_a]*h~(1)^n_n^l[x_a] | ||
| + | -(1/2)*m**2*(h_mn[x_a]*h^mn[x_a] - h^a_a[x_a]*h^b_b[x_a]) | ||
| + | '''.t | ||
| + | fourier = (fourierSum & Collect['h_ab[p_a]'.t, Factor]) >> FierzPauli | ||
| + | println fourier | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > h_{ml}[-p0_{a}]*h_{n}^{a}[p0_{a}]* | ||
| + | (-p0^{l}*p0^{m}*d^{n}_{a}+p0^{m}*p0^{n}*d_{a}^{l} | ||
| + | -(1/2)*(m**2+p0_{b}*p0^{b})*d_{a}^{l}*g^{nm} | ||
| + | +(1/2)*(m**2+p0_{b}*p0^{b})*d_{a}^{n}*g^{lm}) | ||
| + | </sxh> | ||
| + | Given this quadratic form, one can find a propagator in the following way: | ||
| + | <sxh groovy; gutter: true; first-line: 77> | ||
| + | //quadratic form | ||
| + | q2 = '''(-p0^{l}*p0^{m}*d^{n}_{a}+p0^{m}*p0^{n}*d_{a}^{l} | ||
| + | -(1/2)*(m**2+p0_{b}*p0^{b})*d_{a}^{l}*g^{nm} | ||
| + | +(1/2)*(m**2+p0_{b}*p0^{b})*d_{a}^{n}*g^{lm})'''.t | ||
| + | //making symmetric with respect to field indices | ||
| + | def p1 = '^ml'.si | ||
| + | p1.symmetries.setSymmetric() | ||
| + | def p2 = '^n_a'.si | ||
| + | p2.symmetries.setSymmetric() | ||
| + | q2 = (Symmetrize[p1] & Symmetrize[p2]) >> q2 | ||
| + | //make a substitution | ||
| + | "K^mln_a := $q2".t | ||
| + | |||
| + | //the propagator symmetries | ||
| + | addSymmetry 'P^ab_mn', [[0, 1]].p | ||
| + | addSymmetry 'P^ab_mn', [[0, 2], [1, 3]].p | ||
| + | options = [Transformations: 'd_n^n = D'.t, | ||
| + | ExternalSolver : [ | ||
| + | Solver: 'Mathematica', | ||
| + | Path  : '/usr/local/bin']] | ||
| + | //equation | ||
| + | def eq = '(K_abcd + K_cdab)*P^abmn = (d_c^m*d_d^n+d_c^n*d_d^m)/2'.t | ||
| + | def grProp = Reduce([eq], ['P_abmn'], options) | ||
| + | println grProp | ||
| + | </sxh> | ||
| + | <sxh plain; gutter: false> | ||
| + | > [[P_{abmn} = -(1/2)*(m**2+p0^{e}*p0_{e})**(-1)*g_{an}*g_{bm} | ||
| + | -(1/2)*(m**2+p0^{e}*p0_{e})**(-1)*g_{am}*g_{bn} | ||
| + | -(1/2)*m**(-2)*(m**2+p0^{e}*p0_{e})**(-1)*p0_{a}*p0_{m}*g_{bn} | ||
| + | -(1/2)*m**(-2)*(m**2+p0^{e}*p0_{e})**(-1)*p0_{b}*p0_{n}*g_{am} | ||
| + | -(1/2)*m**(-2)*(m**2+p0^{e}*p0_{e})**(-1)*p0_{b}*p0_{m}*g_{an} | ||
| + | -(1/2)*m**(-2)*(m**2+p0^{e}*p0_{e})**(-1)*p0_{a}*p0_{n}*g_{bm} | ||
| + | +(m**2+p0^{e}*p0_{e})**(-1)*(-1+D)**(-1)*g_{ab}*g_{mn} | ||
| + | -m**(-4)*(m**2+p0^{e}*p0_{e})**(-1)*(-1+D)**(-1)*(-2+D) | ||
| + | *p0_{a}*p0_{b}*p0_{m}*p0_{n}+m**(-2)*(m**2+p0^{e}*p0_{e})**(-1) | ||
| + | *(-1+D)**(-1)*p0_{m}*p0_{n}*g_{ab}+m**(-2)*(m**2+p0^{e}*p0_{e})**(-1) | ||
| + | *(-1+D)**(-1)*p0_{a}*p0_{b}*g_{mn}]] | ||
| + | </sxh> | ||
| + | Which is also a well known result that can be equivalently written as | ||
| + | \[ | ||
| + | P_{absl} = -\frac{1}{m^2 + p^2} \left( \frac{1}{2}(P_{as} P_{bl} + P_{al} P_{bs}) - \frac{1}{D-1} P_{ab} P_{sl} \right), | ||
| + | \] | ||
| + | where $P_{ab} = g_{ab} + p_a p_b / m^2$. | ||