123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754 |
- #TODO:
- # -Implement Clebsch-Gordan symmetries
- # -Improve simplification method
- # -Implement new simpifications
- """Clebsch-Gordon Coefficients."""
- from sympy.concrete.summations import Sum
- from sympy.core.add import Add
- from sympy.core.expr import Expr
- from sympy.core.function import expand
- from sympy.core.mul import Mul
- from sympy.core.power import Pow
- from sympy.core.relational import Eq
- from sympy.core.singleton import S
- from sympy.core.symbol import (Wild, symbols)
- from sympy.core.sympify import sympify
- from sympy.functions.elementary.miscellaneous import sqrt
- from sympy.functions.elementary.piecewise import Piecewise
- from sympy.printing.pretty.stringpict import prettyForm, stringPict
- from sympy.functions.special.tensor_functions import KroneckerDelta
- from sympy.physics.wigner import clebsch_gordan, wigner_3j, wigner_6j, wigner_9j
- from sympy.printing.precedence import PRECEDENCE
- __all__ = [
- 'CG',
- 'Wigner3j',
- 'Wigner6j',
- 'Wigner9j',
- 'cg_simp'
- ]
- #-----------------------------------------------------------------------------
- # CG Coefficients
- #-----------------------------------------------------------------------------
- class Wigner3j(Expr):
- """Class for the Wigner-3j symbols.
- Explanation
- ===========
- Wigner 3j-symbols are coefficients determined by the coupling of
- two angular momenta. When created, they are expressed as symbolic
- quantities that, for numerical parameters, can be evaluated using the
- ``.doit()`` method [1]_.
- Parameters
- ==========
- j1, m1, j2, m2, j3, m3 : Number, Symbol
- Terms determining the angular momentum of coupled angular momentum
- systems.
- Examples
- ========
- Declare a Wigner-3j coefficient and calculate its value
- >>> from sympy.physics.quantum.cg import Wigner3j
- >>> w3j = Wigner3j(6,0,4,0,2,0)
- >>> w3j
- Wigner3j(6, 0, 4, 0, 2, 0)
- >>> w3j.doit()
- sqrt(715)/143
- See Also
- ========
- CG: Clebsch-Gordan coefficients
- References
- ==========
- .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988.
- """
- is_commutative = True
- def __new__(cls, j1, m1, j2, m2, j3, m3):
- args = map(sympify, (j1, m1, j2, m2, j3, m3))
- return Expr.__new__(cls, *args)
- @property
- def j1(self):
- return self.args[0]
- @property
- def m1(self):
- return self.args[1]
- @property
- def j2(self):
- return self.args[2]
- @property
- def m2(self):
- return self.args[3]
- @property
- def j3(self):
- return self.args[4]
- @property
- def m3(self):
- return self.args[5]
- @property
- def is_symbolic(self):
- return not all(arg.is_number for arg in self.args)
- # This is modified from the _print_Matrix method
- def _pretty(self, printer, *args):
- m = ((printer._print(self.j1), printer._print(self.m1)),
- (printer._print(self.j2), printer._print(self.m2)),
- (printer._print(self.j3), printer._print(self.m3)))
- hsep = 2
- vsep = 1
- maxw = [-1]*3
- for j in range(3):
- maxw[j] = max([ m[j][i].width() for i in range(2) ])
- D = None
- for i in range(2):
- D_row = None
- for j in range(3):
- s = m[j][i]
- wdelta = maxw[j] - s.width()
- wleft = wdelta //2
- wright = wdelta - wleft
- s = prettyForm(*s.right(' '*wright))
- s = prettyForm(*s.left(' '*wleft))
- if D_row is None:
- D_row = s
- continue
- D_row = prettyForm(*D_row.right(' '*hsep))
- D_row = prettyForm(*D_row.right(s))
- if D is None:
- D = D_row
- continue
- for _ in range(vsep):
- D = prettyForm(*D.below(' '))
- D = prettyForm(*D.below(D_row))
- D = prettyForm(*D.parens())
- return D
- def _latex(self, printer, *args):
- label = map(printer._print, (self.j1, self.j2, self.j3,
- self.m1, self.m2, self.m3))
- return r'\left(\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \end{array}\right)' % \
- tuple(label)
- def doit(self, **hints):
- if self.is_symbolic:
- raise ValueError("Coefficients must be numerical")
- return wigner_3j(self.j1, self.j2, self.j3, self.m1, self.m2, self.m3)
- class CG(Wigner3j):
- r"""Class for Clebsch-Gordan coefficient.
- Explanation
- ===========
- Clebsch-Gordan coefficients describe the angular momentum coupling between
- two systems. The coefficients give the expansion of a coupled total angular
- momentum state and an uncoupled tensor product state. The Clebsch-Gordan
- coefficients are defined as [1]_:
- .. math ::
- C^{j_3,m_3}_{j_1,m_1,j_2,m_2} = \left\langle j_1,m_1;j_2,m_2 | j_3,m_3\right\rangle
- Parameters
- ==========
- j1, m1, j2, m2 : Number, Symbol
- Angular momenta of states 1 and 2.
- j3, m3: Number, Symbol
- Total angular momentum of the coupled system.
- Examples
- ========
- Define a Clebsch-Gordan coefficient and evaluate its value
- >>> from sympy.physics.quantum.cg import CG
- >>> from sympy import S
- >>> cg = CG(S(3)/2, S(3)/2, S(1)/2, -S(1)/2, 1, 1)
- >>> cg
- CG(3/2, 3/2, 1/2, -1/2, 1, 1)
- >>> cg.doit()
- sqrt(3)/2
- >>> CG(j1=S(1)/2, m1=-S(1)/2, j2=S(1)/2, m2=+S(1)/2, j3=1, m3=0).doit()
- sqrt(2)/2
- Compare [2]_.
- See Also
- ========
- Wigner3j: Wigner-3j symbols
- References
- ==========
- .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988.
- .. [2] `Clebsch-Gordan Coefficients, Spherical Harmonics, and d Functions
- <https://pdg.lbl.gov/2020/reviews/rpp2020-rev-clebsch-gordan-coefs.pdf>`_
- in P.A. Zyla *et al.* (Particle Data Group), Prog. Theor. Exp. Phys.
- 2020, 083C01 (2020).
- """
- precedence = PRECEDENCE["Pow"] - 1
- def doit(self, **hints):
- if self.is_symbolic:
- raise ValueError("Coefficients must be numerical")
- return clebsch_gordan(self.j1, self.j2, self.j3, self.m1, self.m2, self.m3)
- def _pretty(self, printer, *args):
- bot = printer._print_seq(
- (self.j1, self.m1, self.j2, self.m2), delimiter=',')
- top = printer._print_seq((self.j3, self.m3), delimiter=',')
- pad = max(top.width(), bot.width())
- bot = prettyForm(*bot.left(' '))
- top = prettyForm(*top.left(' '))
- if not pad == bot.width():
- bot = prettyForm(*bot.right(' '*(pad - bot.width())))
- if not pad == top.width():
- top = prettyForm(*top.right(' '*(pad - top.width())))
- s = stringPict('C' + ' '*pad)
- s = prettyForm(*s.below(bot))
- s = prettyForm(*s.above(top))
- return s
- def _latex(self, printer, *args):
- label = map(printer._print, (self.j3, self.m3, self.j1,
- self.m1, self.j2, self.m2))
- return r'C^{%s,%s}_{%s,%s,%s,%s}' % tuple(label)
- class Wigner6j(Expr):
- """Class for the Wigner-6j symbols
- See Also
- ========
- Wigner3j: Wigner-3j symbols
- """
- def __new__(cls, j1, j2, j12, j3, j, j23):
- args = map(sympify, (j1, j2, j12, j3, j, j23))
- return Expr.__new__(cls, *args)
- @property
- def j1(self):
- return self.args[0]
- @property
- def j2(self):
- return self.args[1]
- @property
- def j12(self):
- return self.args[2]
- @property
- def j3(self):
- return self.args[3]
- @property
- def j(self):
- return self.args[4]
- @property
- def j23(self):
- return self.args[5]
- @property
- def is_symbolic(self):
- return not all(arg.is_number for arg in self.args)
- # This is modified from the _print_Matrix method
- def _pretty(self, printer, *args):
- m = ((printer._print(self.j1), printer._print(self.j3)),
- (printer._print(self.j2), printer._print(self.j)),
- (printer._print(self.j12), printer._print(self.j23)))
- hsep = 2
- vsep = 1
- maxw = [-1]*3
- for j in range(3):
- maxw[j] = max([ m[j][i].width() for i in range(2) ])
- D = None
- for i in range(2):
- D_row = None
- for j in range(3):
- s = m[j][i]
- wdelta = maxw[j] - s.width()
- wleft = wdelta //2
- wright = wdelta - wleft
- s = prettyForm(*s.right(' '*wright))
- s = prettyForm(*s.left(' '*wleft))
- if D_row is None:
- D_row = s
- continue
- D_row = prettyForm(*D_row.right(' '*hsep))
- D_row = prettyForm(*D_row.right(s))
- if D is None:
- D = D_row
- continue
- for _ in range(vsep):
- D = prettyForm(*D.below(' '))
- D = prettyForm(*D.below(D_row))
- D = prettyForm(*D.parens(left='{', right='}'))
- return D
- def _latex(self, printer, *args):
- label = map(printer._print, (self.j1, self.j2, self.j12,
- self.j3, self.j, self.j23))
- return r'\left\{\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \end{array}\right\}' % \
- tuple(label)
- def doit(self, **hints):
- if self.is_symbolic:
- raise ValueError("Coefficients must be numerical")
- return wigner_6j(self.j1, self.j2, self.j12, self.j3, self.j, self.j23)
- class Wigner9j(Expr):
- """Class for the Wigner-9j symbols
- See Also
- ========
- Wigner3j: Wigner-3j symbols
- """
- def __new__(cls, j1, j2, j12, j3, j4, j34, j13, j24, j):
- args = map(sympify, (j1, j2, j12, j3, j4, j34, j13, j24, j))
- return Expr.__new__(cls, *args)
- @property
- def j1(self):
- return self.args[0]
- @property
- def j2(self):
- return self.args[1]
- @property
- def j12(self):
- return self.args[2]
- @property
- def j3(self):
- return self.args[3]
- @property
- def j4(self):
- return self.args[4]
- @property
- def j34(self):
- return self.args[5]
- @property
- def j13(self):
- return self.args[6]
- @property
- def j24(self):
- return self.args[7]
- @property
- def j(self):
- return self.args[8]
- @property
- def is_symbolic(self):
- return not all(arg.is_number for arg in self.args)
- # This is modified from the _print_Matrix method
- def _pretty(self, printer, *args):
- m = (
- (printer._print(
- self.j1), printer._print(self.j3), printer._print(self.j13)),
- (printer._print(
- self.j2), printer._print(self.j4), printer._print(self.j24)),
- (printer._print(self.j12), printer._print(self.j34), printer._print(self.j)))
- hsep = 2
- vsep = 1
- maxw = [-1]*3
- for j in range(3):
- maxw[j] = max([ m[j][i].width() for i in range(3) ])
- D = None
- for i in range(3):
- D_row = None
- for j in range(3):
- s = m[j][i]
- wdelta = maxw[j] - s.width()
- wleft = wdelta //2
- wright = wdelta - wleft
- s = prettyForm(*s.right(' '*wright))
- s = prettyForm(*s.left(' '*wleft))
- if D_row is None:
- D_row = s
- continue
- D_row = prettyForm(*D_row.right(' '*hsep))
- D_row = prettyForm(*D_row.right(s))
- if D is None:
- D = D_row
- continue
- for _ in range(vsep):
- D = prettyForm(*D.below(' '))
- D = prettyForm(*D.below(D_row))
- D = prettyForm(*D.parens(left='{', right='}'))
- return D
- def _latex(self, printer, *args):
- label = map(printer._print, (self.j1, self.j2, self.j12, self.j3,
- self.j4, self.j34, self.j13, self.j24, self.j))
- return r'\left\{\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \\ %s & %s & %s \end{array}\right\}' % \
- tuple(label)
- def doit(self, **hints):
- if self.is_symbolic:
- raise ValueError("Coefficients must be numerical")
- return wigner_9j(self.j1, self.j2, self.j12, self.j3, self.j4, self.j34, self.j13, self.j24, self.j)
- def cg_simp(e):
- """Simplify and combine CG coefficients.
- Explanation
- ===========
- This function uses various symmetry and properties of sums and
- products of Clebsch-Gordan coefficients to simplify statements
- involving these terms [1]_.
- Examples
- ========
- Simplify the sum over CG(a,alpha,0,0,a,alpha) for all alpha to
- 2*a+1
- >>> from sympy.physics.quantum.cg import CG, cg_simp
- >>> a = CG(1,1,0,0,1,1)
- >>> b = CG(1,0,0,0,1,0)
- >>> c = CG(1,-1,0,0,1,-1)
- >>> cg_simp(a+b+c)
- 3
- See Also
- ========
- CG: Clebsh-Gordan coefficients
- References
- ==========
- .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988.
- """
- if isinstance(e, Add):
- return _cg_simp_add(e)
- elif isinstance(e, Sum):
- return _cg_simp_sum(e)
- elif isinstance(e, Mul):
- return Mul(*[cg_simp(arg) for arg in e.args])
- elif isinstance(e, Pow):
- return Pow(cg_simp(e.base), e.exp)
- else:
- return e
- def _cg_simp_add(e):
- #TODO: Improve simplification method
- """Takes a sum of terms involving Clebsch-Gordan coefficients and
- simplifies the terms.
- Explanation
- ===========
- First, we create two lists, cg_part, which is all the terms involving CG
- coefficients, and other_part, which is all other terms. The cg_part list
- is then passed to the simplification methods, which return the new cg_part
- and any additional terms that are added to other_part
- """
- cg_part = []
- other_part = []
- e = expand(e)
- for arg in e.args:
- if arg.has(CG):
- if isinstance(arg, Sum):
- other_part.append(_cg_simp_sum(arg))
- elif isinstance(arg, Mul):
- terms = 1
- for term in arg.args:
- if isinstance(term, Sum):
- terms *= _cg_simp_sum(term)
- else:
- terms *= term
- if terms.has(CG):
- cg_part.append(terms)
- else:
- other_part.append(terms)
- else:
- cg_part.append(arg)
- else:
- other_part.append(arg)
- cg_part, other = _check_varsh_871_1(cg_part)
- other_part.append(other)
- cg_part, other = _check_varsh_871_2(cg_part)
- other_part.append(other)
- cg_part, other = _check_varsh_872_9(cg_part)
- other_part.append(other)
- return Add(*cg_part) + Add(*other_part)
- def _check_varsh_871_1(term_list):
- # Sum( CG(a,alpha,b,0,a,alpha), (alpha, -a, a)) == KroneckerDelta(b,0)
- a, alpha, b, lt = map(Wild, ('a', 'alpha', 'b', 'lt'))
- expr = lt*CG(a, alpha, b, 0, a, alpha)
- simp = (2*a + 1)*KroneckerDelta(b, 0)
- sign = lt/abs(lt)
- build_expr = 2*a + 1
- index_expr = a + alpha
- return _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, b, lt), (a, b), build_expr, index_expr)
- def _check_varsh_871_2(term_list):
- # Sum((-1)**(a-alpha)*CG(a,alpha,a,-alpha,c,0),(alpha,-a,a))
- a, alpha, c, lt = map(Wild, ('a', 'alpha', 'c', 'lt'))
- expr = lt*CG(a, alpha, a, -alpha, c, 0)
- simp = sqrt(2*a + 1)*KroneckerDelta(c, 0)
- sign = (-1)**(a - alpha)*lt/abs(lt)
- build_expr = 2*a + 1
- index_expr = a + alpha
- return _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, c, lt), (a, c), build_expr, index_expr)
- def _check_varsh_872_9(term_list):
- # Sum( CG(a,alpha,b,beta,c,gamma)*CG(a,alpha',b,beta',c,gamma), (gamma, -c, c), (c, abs(a-b), a+b))
- a, alpha, alphap, b, beta, betap, c, gamma, lt = map(Wild, (
- 'a', 'alpha', 'alphap', 'b', 'beta', 'betap', 'c', 'gamma', 'lt'))
- # Case alpha==alphap, beta==betap
- # For numerical alpha,beta
- expr = lt*CG(a, alpha, b, beta, c, gamma)**2
- simp = 1
- sign = lt/abs(lt)
- x = abs(a - b)
- y = abs(alpha + beta)
- build_expr = a + b + 1 - Piecewise((x, x > y), (0, Eq(x, y)), (y, y > x))
- index_expr = a + b - c
- term_list, other1 = _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, b, beta, c, gamma, lt), (a, alpha, b, beta), build_expr, index_expr)
- # For symbolic alpha,beta
- x = abs(a - b)
- y = a + b
- build_expr = (y + 1 - x)*(x + y + 1)
- index_expr = (c - x)*(x + c) + c + gamma
- term_list, other2 = _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, b, beta, c, gamma, lt), (a, alpha, b, beta), build_expr, index_expr)
- # Case alpha!=alphap or beta!=betap
- # Note: this only works with leading term of 1, pattern matching is unable to match when there is a Wild leading term
- # For numerical alpha,alphap,beta,betap
- expr = CG(a, alpha, b, beta, c, gamma)*CG(a, alphap, b, betap, c, gamma)
- simp = KroneckerDelta(alpha, alphap)*KroneckerDelta(beta, betap)
- sign = sympify(1)
- x = abs(a - b)
- y = abs(alpha + beta)
- build_expr = a + b + 1 - Piecewise((x, x > y), (0, Eq(x, y)), (y, y > x))
- index_expr = a + b - c
- term_list, other3 = _check_cg_simp(expr, simp, sign, sympify(1), term_list, (a, alpha, alphap, b, beta, betap, c, gamma), (a, alpha, alphap, b, beta, betap), build_expr, index_expr)
- # For symbolic alpha,alphap,beta,betap
- x = abs(a - b)
- y = a + b
- build_expr = (y + 1 - x)*(x + y + 1)
- index_expr = (c - x)*(x + c) + c + gamma
- term_list, other4 = _check_cg_simp(expr, simp, sign, sympify(1), term_list, (a, alpha, alphap, b, beta, betap, c, gamma), (a, alpha, alphap, b, beta, betap), build_expr, index_expr)
- return term_list, other1 + other2 + other4
- def _check_cg_simp(expr, simp, sign, lt, term_list, variables, dep_variables, build_index_expr, index_expr):
- """ Checks for simplifications that can be made, returning a tuple of the
- simplified list of terms and any terms generated by simplification.
- Parameters
- ==========
- expr: expression
- The expression with Wild terms that will be matched to the terms in
- the sum
- simp: expression
- The expression with Wild terms that is substituted in place of the CG
- terms in the case of simplification
- sign: expression
- The expression with Wild terms denoting the sign that is on expr that
- must match
- lt: expression
- The expression with Wild terms that gives the leading term of the
- matched expr
- term_list: list
- A list of all of the terms is the sum to be simplified
- variables: list
- A list of all the variables that appears in expr
- dep_variables: list
- A list of the variables that must match for all the terms in the sum,
- i.e. the dependent variables
- build_index_expr: expression
- Expression with Wild terms giving the number of elements in cg_index
- index_expr: expression
- Expression with Wild terms giving the index terms have when storing
- them to cg_index
- """
- other_part = 0
- i = 0
- while i < len(term_list):
- sub_1 = _check_cg(term_list[i], expr, len(variables))
- if sub_1 is None:
- i += 1
- continue
- if not sympify(build_index_expr.subs(sub_1)).is_number:
- i += 1
- continue
- sub_dep = [(x, sub_1[x]) for x in dep_variables]
- cg_index = [None]*build_index_expr.subs(sub_1)
- for j in range(i, len(term_list)):
- sub_2 = _check_cg(term_list[j], expr.subs(sub_dep), len(variables) - len(dep_variables), sign=(sign.subs(sub_1), sign.subs(sub_dep)))
- if sub_2 is None:
- continue
- if not sympify(index_expr.subs(sub_dep).subs(sub_2)).is_number:
- continue
- cg_index[index_expr.subs(sub_dep).subs(sub_2)] = j, expr.subs(lt, 1).subs(sub_dep).subs(sub_2), lt.subs(sub_2), sign.subs(sub_dep).subs(sub_2)
- if not any(i is None for i in cg_index):
- min_lt = min(*[ abs(term[2]) for term in cg_index ])
- indices = [ term[0] for term in cg_index]
- indices.sort()
- indices.reverse()
- [ term_list.pop(j) for j in indices ]
- for term in cg_index:
- if abs(term[2]) > min_lt:
- term_list.append( (term[2] - min_lt*term[3])*term[1] )
- other_part += min_lt*(sign*simp).subs(sub_1)
- else:
- i += 1
- return term_list, other_part
- def _check_cg(cg_term, expr, length, sign=None):
- """Checks whether a term matches the given expression"""
- # TODO: Check for symmetries
- matches = cg_term.match(expr)
- if matches is None:
- return
- if sign is not None:
- if not isinstance(sign, tuple):
- raise TypeError('sign must be a tuple')
- if not sign[0] == (sign[1]).subs(matches):
- return
- if len(matches) == length:
- return matches
- def _cg_simp_sum(e):
- e = _check_varsh_sum_871_1(e)
- e = _check_varsh_sum_871_2(e)
- e = _check_varsh_sum_872_4(e)
- return e
- def _check_varsh_sum_871_1(e):
- a = Wild('a')
- alpha = symbols('alpha')
- b = Wild('b')
- match = e.match(Sum(CG(a, alpha, b, 0, a, alpha), (alpha, -a, a)))
- if match is not None and len(match) == 2:
- return ((2*a + 1)*KroneckerDelta(b, 0)).subs(match)
- return e
- def _check_varsh_sum_871_2(e):
- a = Wild('a')
- alpha = symbols('alpha')
- c = Wild('c')
- match = e.match(
- Sum((-1)**(a - alpha)*CG(a, alpha, a, -alpha, c, 0), (alpha, -a, a)))
- if match is not None and len(match) == 2:
- return (sqrt(2*a + 1)*KroneckerDelta(c, 0)).subs(match)
- return e
- def _check_varsh_sum_872_4(e):
- alpha = symbols('alpha')
- beta = symbols('beta')
- a = Wild('a')
- b = Wild('b')
- c = Wild('c')
- cp = Wild('cp')
- gamma = Wild('gamma')
- gammap = Wild('gammap')
- cg1 = CG(a, alpha, b, beta, c, gamma)
- cg2 = CG(a, alpha, b, beta, cp, gammap)
- match1 = e.match(Sum(cg1*cg2, (alpha, -a, a), (beta, -b, b)))
- if match1 is not None and len(match1) == 6:
- return (KroneckerDelta(c, cp)*KroneckerDelta(gamma, gammap)).subs(match1)
- match2 = e.match(Sum(cg1**2, (alpha, -a, a), (beta, -b, b)))
- if match2 is not None and len(match2) == 4:
- return S.One
- return e
- def _cg_list(term):
- if isinstance(term, CG):
- return (term,), 1, 1
- cg = []
- coeff = 1
- if not isinstance(term, (Mul, Pow)):
- raise NotImplementedError('term must be CG, Add, Mul or Pow')
- if isinstance(term, Pow) and sympify(term.exp).is_number:
- if sympify(term.exp).is_number:
- [ cg.append(term.base) for _ in range(term.exp) ]
- else:
- return (term,), 1, 1
- if isinstance(term, Mul):
- for arg in term.args:
- if isinstance(arg, CG):
- cg.append(arg)
- else:
- coeff *= arg
- return cg, coeff, coeff/abs(coeff)
|