cg.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. #TODO:
  2. # -Implement Clebsch-Gordan symmetries
  3. # -Improve simplification method
  4. # -Implement new simpifications
  5. """Clebsch-Gordon Coefficients."""
  6. from sympy.concrete.summations import Sum
  7. from sympy.core.add import Add
  8. from sympy.core.expr import Expr
  9. from sympy.core.function import expand
  10. from sympy.core.mul import Mul
  11. from sympy.core.power import Pow
  12. from sympy.core.relational import Eq
  13. from sympy.core.singleton import S
  14. from sympy.core.symbol import (Wild, symbols)
  15. from sympy.core.sympify import sympify
  16. from sympy.functions.elementary.miscellaneous import sqrt
  17. from sympy.functions.elementary.piecewise import Piecewise
  18. from sympy.printing.pretty.stringpict import prettyForm, stringPict
  19. from sympy.functions.special.tensor_functions import KroneckerDelta
  20. from sympy.physics.wigner import clebsch_gordan, wigner_3j, wigner_6j, wigner_9j
  21. from sympy.printing.precedence import PRECEDENCE
  22. __all__ = [
  23. 'CG',
  24. 'Wigner3j',
  25. 'Wigner6j',
  26. 'Wigner9j',
  27. 'cg_simp'
  28. ]
  29. #-----------------------------------------------------------------------------
  30. # CG Coefficients
  31. #-----------------------------------------------------------------------------
  32. class Wigner3j(Expr):
  33. """Class for the Wigner-3j symbols.
  34. Explanation
  35. ===========
  36. Wigner 3j-symbols are coefficients determined by the coupling of
  37. two angular momenta. When created, they are expressed as symbolic
  38. quantities that, for numerical parameters, can be evaluated using the
  39. ``.doit()`` method [1]_.
  40. Parameters
  41. ==========
  42. j1, m1, j2, m2, j3, m3 : Number, Symbol
  43. Terms determining the angular momentum of coupled angular momentum
  44. systems.
  45. Examples
  46. ========
  47. Declare a Wigner-3j coefficient and calculate its value
  48. >>> from sympy.physics.quantum.cg import Wigner3j
  49. >>> w3j = Wigner3j(6,0,4,0,2,0)
  50. >>> w3j
  51. Wigner3j(6, 0, 4, 0, 2, 0)
  52. >>> w3j.doit()
  53. sqrt(715)/143
  54. See Also
  55. ========
  56. CG: Clebsch-Gordan coefficients
  57. References
  58. ==========
  59. .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988.
  60. """
  61. is_commutative = True
  62. def __new__(cls, j1, m1, j2, m2, j3, m3):
  63. args = map(sympify, (j1, m1, j2, m2, j3, m3))
  64. return Expr.__new__(cls, *args)
  65. @property
  66. def j1(self):
  67. return self.args[0]
  68. @property
  69. def m1(self):
  70. return self.args[1]
  71. @property
  72. def j2(self):
  73. return self.args[2]
  74. @property
  75. def m2(self):
  76. return self.args[3]
  77. @property
  78. def j3(self):
  79. return self.args[4]
  80. @property
  81. def m3(self):
  82. return self.args[5]
  83. @property
  84. def is_symbolic(self):
  85. return not all(arg.is_number for arg in self.args)
  86. # This is modified from the _print_Matrix method
  87. def _pretty(self, printer, *args):
  88. m = ((printer._print(self.j1), printer._print(self.m1)),
  89. (printer._print(self.j2), printer._print(self.m2)),
  90. (printer._print(self.j3), printer._print(self.m3)))
  91. hsep = 2
  92. vsep = 1
  93. maxw = [-1]*3
  94. for j in range(3):
  95. maxw[j] = max([ m[j][i].width() for i in range(2) ])
  96. D = None
  97. for i in range(2):
  98. D_row = None
  99. for j in range(3):
  100. s = m[j][i]
  101. wdelta = maxw[j] - s.width()
  102. wleft = wdelta //2
  103. wright = wdelta - wleft
  104. s = prettyForm(*s.right(' '*wright))
  105. s = prettyForm(*s.left(' '*wleft))
  106. if D_row is None:
  107. D_row = s
  108. continue
  109. D_row = prettyForm(*D_row.right(' '*hsep))
  110. D_row = prettyForm(*D_row.right(s))
  111. if D is None:
  112. D = D_row
  113. continue
  114. for _ in range(vsep):
  115. D = prettyForm(*D.below(' '))
  116. D = prettyForm(*D.below(D_row))
  117. D = prettyForm(*D.parens())
  118. return D
  119. def _latex(self, printer, *args):
  120. label = map(printer._print, (self.j1, self.j2, self.j3,
  121. self.m1, self.m2, self.m3))
  122. return r'\left(\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \end{array}\right)' % \
  123. tuple(label)
  124. def doit(self, **hints):
  125. if self.is_symbolic:
  126. raise ValueError("Coefficients must be numerical")
  127. return wigner_3j(self.j1, self.j2, self.j3, self.m1, self.m2, self.m3)
  128. class CG(Wigner3j):
  129. r"""Class for Clebsch-Gordan coefficient.
  130. Explanation
  131. ===========
  132. Clebsch-Gordan coefficients describe the angular momentum coupling between
  133. two systems. The coefficients give the expansion of a coupled total angular
  134. momentum state and an uncoupled tensor product state. The Clebsch-Gordan
  135. coefficients are defined as [1]_:
  136. .. math ::
  137. 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
  138. Parameters
  139. ==========
  140. j1, m1, j2, m2 : Number, Symbol
  141. Angular momenta of states 1 and 2.
  142. j3, m3: Number, Symbol
  143. Total angular momentum of the coupled system.
  144. Examples
  145. ========
  146. Define a Clebsch-Gordan coefficient and evaluate its value
  147. >>> from sympy.physics.quantum.cg import CG
  148. >>> from sympy import S
  149. >>> cg = CG(S(3)/2, S(3)/2, S(1)/2, -S(1)/2, 1, 1)
  150. >>> cg
  151. CG(3/2, 3/2, 1/2, -1/2, 1, 1)
  152. >>> cg.doit()
  153. sqrt(3)/2
  154. >>> CG(j1=S(1)/2, m1=-S(1)/2, j2=S(1)/2, m2=+S(1)/2, j3=1, m3=0).doit()
  155. sqrt(2)/2
  156. Compare [2]_.
  157. See Also
  158. ========
  159. Wigner3j: Wigner-3j symbols
  160. References
  161. ==========
  162. .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988.
  163. .. [2] `Clebsch-Gordan Coefficients, Spherical Harmonics, and d Functions
  164. <https://pdg.lbl.gov/2020/reviews/rpp2020-rev-clebsch-gordan-coefs.pdf>`_
  165. in P.A. Zyla *et al.* (Particle Data Group), Prog. Theor. Exp. Phys.
  166. 2020, 083C01 (2020).
  167. """
  168. precedence = PRECEDENCE["Pow"] - 1
  169. def doit(self, **hints):
  170. if self.is_symbolic:
  171. raise ValueError("Coefficients must be numerical")
  172. return clebsch_gordan(self.j1, self.j2, self.j3, self.m1, self.m2, self.m3)
  173. def _pretty(self, printer, *args):
  174. bot = printer._print_seq(
  175. (self.j1, self.m1, self.j2, self.m2), delimiter=',')
  176. top = printer._print_seq((self.j3, self.m3), delimiter=',')
  177. pad = max(top.width(), bot.width())
  178. bot = prettyForm(*bot.left(' '))
  179. top = prettyForm(*top.left(' '))
  180. if not pad == bot.width():
  181. bot = prettyForm(*bot.right(' '*(pad - bot.width())))
  182. if not pad == top.width():
  183. top = prettyForm(*top.right(' '*(pad - top.width())))
  184. s = stringPict('C' + ' '*pad)
  185. s = prettyForm(*s.below(bot))
  186. s = prettyForm(*s.above(top))
  187. return s
  188. def _latex(self, printer, *args):
  189. label = map(printer._print, (self.j3, self.m3, self.j1,
  190. self.m1, self.j2, self.m2))
  191. return r'C^{%s,%s}_{%s,%s,%s,%s}' % tuple(label)
  192. class Wigner6j(Expr):
  193. """Class for the Wigner-6j symbols
  194. See Also
  195. ========
  196. Wigner3j: Wigner-3j symbols
  197. """
  198. def __new__(cls, j1, j2, j12, j3, j, j23):
  199. args = map(sympify, (j1, j2, j12, j3, j, j23))
  200. return Expr.__new__(cls, *args)
  201. @property
  202. def j1(self):
  203. return self.args[0]
  204. @property
  205. def j2(self):
  206. return self.args[1]
  207. @property
  208. def j12(self):
  209. return self.args[2]
  210. @property
  211. def j3(self):
  212. return self.args[3]
  213. @property
  214. def j(self):
  215. return self.args[4]
  216. @property
  217. def j23(self):
  218. return self.args[5]
  219. @property
  220. def is_symbolic(self):
  221. return not all(arg.is_number for arg in self.args)
  222. # This is modified from the _print_Matrix method
  223. def _pretty(self, printer, *args):
  224. m = ((printer._print(self.j1), printer._print(self.j3)),
  225. (printer._print(self.j2), printer._print(self.j)),
  226. (printer._print(self.j12), printer._print(self.j23)))
  227. hsep = 2
  228. vsep = 1
  229. maxw = [-1]*3
  230. for j in range(3):
  231. maxw[j] = max([ m[j][i].width() for i in range(2) ])
  232. D = None
  233. for i in range(2):
  234. D_row = None
  235. for j in range(3):
  236. s = m[j][i]
  237. wdelta = maxw[j] - s.width()
  238. wleft = wdelta //2
  239. wright = wdelta - wleft
  240. s = prettyForm(*s.right(' '*wright))
  241. s = prettyForm(*s.left(' '*wleft))
  242. if D_row is None:
  243. D_row = s
  244. continue
  245. D_row = prettyForm(*D_row.right(' '*hsep))
  246. D_row = prettyForm(*D_row.right(s))
  247. if D is None:
  248. D = D_row
  249. continue
  250. for _ in range(vsep):
  251. D = prettyForm(*D.below(' '))
  252. D = prettyForm(*D.below(D_row))
  253. D = prettyForm(*D.parens(left='{', right='}'))
  254. return D
  255. def _latex(self, printer, *args):
  256. label = map(printer._print, (self.j1, self.j2, self.j12,
  257. self.j3, self.j, self.j23))
  258. return r'\left\{\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \end{array}\right\}' % \
  259. tuple(label)
  260. def doit(self, **hints):
  261. if self.is_symbolic:
  262. raise ValueError("Coefficients must be numerical")
  263. return wigner_6j(self.j1, self.j2, self.j12, self.j3, self.j, self.j23)
  264. class Wigner9j(Expr):
  265. """Class for the Wigner-9j symbols
  266. See Also
  267. ========
  268. Wigner3j: Wigner-3j symbols
  269. """
  270. def __new__(cls, j1, j2, j12, j3, j4, j34, j13, j24, j):
  271. args = map(sympify, (j1, j2, j12, j3, j4, j34, j13, j24, j))
  272. return Expr.__new__(cls, *args)
  273. @property
  274. def j1(self):
  275. return self.args[0]
  276. @property
  277. def j2(self):
  278. return self.args[1]
  279. @property
  280. def j12(self):
  281. return self.args[2]
  282. @property
  283. def j3(self):
  284. return self.args[3]
  285. @property
  286. def j4(self):
  287. return self.args[4]
  288. @property
  289. def j34(self):
  290. return self.args[5]
  291. @property
  292. def j13(self):
  293. return self.args[6]
  294. @property
  295. def j24(self):
  296. return self.args[7]
  297. @property
  298. def j(self):
  299. return self.args[8]
  300. @property
  301. def is_symbolic(self):
  302. return not all(arg.is_number for arg in self.args)
  303. # This is modified from the _print_Matrix method
  304. def _pretty(self, printer, *args):
  305. m = (
  306. (printer._print(
  307. self.j1), printer._print(self.j3), printer._print(self.j13)),
  308. (printer._print(
  309. self.j2), printer._print(self.j4), printer._print(self.j24)),
  310. (printer._print(self.j12), printer._print(self.j34), printer._print(self.j)))
  311. hsep = 2
  312. vsep = 1
  313. maxw = [-1]*3
  314. for j in range(3):
  315. maxw[j] = max([ m[j][i].width() for i in range(3) ])
  316. D = None
  317. for i in range(3):
  318. D_row = None
  319. for j in range(3):
  320. s = m[j][i]
  321. wdelta = maxw[j] - s.width()
  322. wleft = wdelta //2
  323. wright = wdelta - wleft
  324. s = prettyForm(*s.right(' '*wright))
  325. s = prettyForm(*s.left(' '*wleft))
  326. if D_row is None:
  327. D_row = s
  328. continue
  329. D_row = prettyForm(*D_row.right(' '*hsep))
  330. D_row = prettyForm(*D_row.right(s))
  331. if D is None:
  332. D = D_row
  333. continue
  334. for _ in range(vsep):
  335. D = prettyForm(*D.below(' '))
  336. D = prettyForm(*D.below(D_row))
  337. D = prettyForm(*D.parens(left='{', right='}'))
  338. return D
  339. def _latex(self, printer, *args):
  340. label = map(printer._print, (self.j1, self.j2, self.j12, self.j3,
  341. self.j4, self.j34, self.j13, self.j24, self.j))
  342. return r'\left\{\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \\ %s & %s & %s \end{array}\right\}' % \
  343. tuple(label)
  344. def doit(self, **hints):
  345. if self.is_symbolic:
  346. raise ValueError("Coefficients must be numerical")
  347. return wigner_9j(self.j1, self.j2, self.j12, self.j3, self.j4, self.j34, self.j13, self.j24, self.j)
  348. def cg_simp(e):
  349. """Simplify and combine CG coefficients.
  350. Explanation
  351. ===========
  352. This function uses various symmetry and properties of sums and
  353. products of Clebsch-Gordan coefficients to simplify statements
  354. involving these terms [1]_.
  355. Examples
  356. ========
  357. Simplify the sum over CG(a,alpha,0,0,a,alpha) for all alpha to
  358. 2*a+1
  359. >>> from sympy.physics.quantum.cg import CG, cg_simp
  360. >>> a = CG(1,1,0,0,1,1)
  361. >>> b = CG(1,0,0,0,1,0)
  362. >>> c = CG(1,-1,0,0,1,-1)
  363. >>> cg_simp(a+b+c)
  364. 3
  365. See Also
  366. ========
  367. CG: Clebsh-Gordan coefficients
  368. References
  369. ==========
  370. .. [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988.
  371. """
  372. if isinstance(e, Add):
  373. return _cg_simp_add(e)
  374. elif isinstance(e, Sum):
  375. return _cg_simp_sum(e)
  376. elif isinstance(e, Mul):
  377. return Mul(*[cg_simp(arg) for arg in e.args])
  378. elif isinstance(e, Pow):
  379. return Pow(cg_simp(e.base), e.exp)
  380. else:
  381. return e
  382. def _cg_simp_add(e):
  383. #TODO: Improve simplification method
  384. """Takes a sum of terms involving Clebsch-Gordan coefficients and
  385. simplifies the terms.
  386. Explanation
  387. ===========
  388. First, we create two lists, cg_part, which is all the terms involving CG
  389. coefficients, and other_part, which is all other terms. The cg_part list
  390. is then passed to the simplification methods, which return the new cg_part
  391. and any additional terms that are added to other_part
  392. """
  393. cg_part = []
  394. other_part = []
  395. e = expand(e)
  396. for arg in e.args:
  397. if arg.has(CG):
  398. if isinstance(arg, Sum):
  399. other_part.append(_cg_simp_sum(arg))
  400. elif isinstance(arg, Mul):
  401. terms = 1
  402. for term in arg.args:
  403. if isinstance(term, Sum):
  404. terms *= _cg_simp_sum(term)
  405. else:
  406. terms *= term
  407. if terms.has(CG):
  408. cg_part.append(terms)
  409. else:
  410. other_part.append(terms)
  411. else:
  412. cg_part.append(arg)
  413. else:
  414. other_part.append(arg)
  415. cg_part, other = _check_varsh_871_1(cg_part)
  416. other_part.append(other)
  417. cg_part, other = _check_varsh_871_2(cg_part)
  418. other_part.append(other)
  419. cg_part, other = _check_varsh_872_9(cg_part)
  420. other_part.append(other)
  421. return Add(*cg_part) + Add(*other_part)
  422. def _check_varsh_871_1(term_list):
  423. # Sum( CG(a,alpha,b,0,a,alpha), (alpha, -a, a)) == KroneckerDelta(b,0)
  424. a, alpha, b, lt = map(Wild, ('a', 'alpha', 'b', 'lt'))
  425. expr = lt*CG(a, alpha, b, 0, a, alpha)
  426. simp = (2*a + 1)*KroneckerDelta(b, 0)
  427. sign = lt/abs(lt)
  428. build_expr = 2*a + 1
  429. index_expr = a + alpha
  430. return _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, b, lt), (a, b), build_expr, index_expr)
  431. def _check_varsh_871_2(term_list):
  432. # Sum((-1)**(a-alpha)*CG(a,alpha,a,-alpha,c,0),(alpha,-a,a))
  433. a, alpha, c, lt = map(Wild, ('a', 'alpha', 'c', 'lt'))
  434. expr = lt*CG(a, alpha, a, -alpha, c, 0)
  435. simp = sqrt(2*a + 1)*KroneckerDelta(c, 0)
  436. sign = (-1)**(a - alpha)*lt/abs(lt)
  437. build_expr = 2*a + 1
  438. index_expr = a + alpha
  439. return _check_cg_simp(expr, simp, sign, lt, term_list, (a, alpha, c, lt), (a, c), build_expr, index_expr)
  440. def _check_varsh_872_9(term_list):
  441. # Sum( CG(a,alpha,b,beta,c,gamma)*CG(a,alpha',b,beta',c,gamma), (gamma, -c, c), (c, abs(a-b), a+b))
  442. a, alpha, alphap, b, beta, betap, c, gamma, lt = map(Wild, (
  443. 'a', 'alpha', 'alphap', 'b', 'beta', 'betap', 'c', 'gamma', 'lt'))
  444. # Case alpha==alphap, beta==betap
  445. # For numerical alpha,beta
  446. expr = lt*CG(a, alpha, b, beta, c, gamma)**2
  447. simp = 1
  448. sign = lt/abs(lt)
  449. x = abs(a - b)
  450. y = abs(alpha + beta)
  451. build_expr = a + b + 1 - Piecewise((x, x > y), (0, Eq(x, y)), (y, y > x))
  452. index_expr = a + b - c
  453. 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)
  454. # For symbolic alpha,beta
  455. x = abs(a - b)
  456. y = a + b
  457. build_expr = (y + 1 - x)*(x + y + 1)
  458. index_expr = (c - x)*(x + c) + c + gamma
  459. 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)
  460. # Case alpha!=alphap or beta!=betap
  461. # Note: this only works with leading term of 1, pattern matching is unable to match when there is a Wild leading term
  462. # For numerical alpha,alphap,beta,betap
  463. expr = CG(a, alpha, b, beta, c, gamma)*CG(a, alphap, b, betap, c, gamma)
  464. simp = KroneckerDelta(alpha, alphap)*KroneckerDelta(beta, betap)
  465. sign = sympify(1)
  466. x = abs(a - b)
  467. y = abs(alpha + beta)
  468. build_expr = a + b + 1 - Piecewise((x, x > y), (0, Eq(x, y)), (y, y > x))
  469. index_expr = a + b - c
  470. 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)
  471. # For symbolic alpha,alphap,beta,betap
  472. x = abs(a - b)
  473. y = a + b
  474. build_expr = (y + 1 - x)*(x + y + 1)
  475. index_expr = (c - x)*(x + c) + c + gamma
  476. 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)
  477. return term_list, other1 + other2 + other4
  478. def _check_cg_simp(expr, simp, sign, lt, term_list, variables, dep_variables, build_index_expr, index_expr):
  479. """ Checks for simplifications that can be made, returning a tuple of the
  480. simplified list of terms and any terms generated by simplification.
  481. Parameters
  482. ==========
  483. expr: expression
  484. The expression with Wild terms that will be matched to the terms in
  485. the sum
  486. simp: expression
  487. The expression with Wild terms that is substituted in place of the CG
  488. terms in the case of simplification
  489. sign: expression
  490. The expression with Wild terms denoting the sign that is on expr that
  491. must match
  492. lt: expression
  493. The expression with Wild terms that gives the leading term of the
  494. matched expr
  495. term_list: list
  496. A list of all of the terms is the sum to be simplified
  497. variables: list
  498. A list of all the variables that appears in expr
  499. dep_variables: list
  500. A list of the variables that must match for all the terms in the sum,
  501. i.e. the dependent variables
  502. build_index_expr: expression
  503. Expression with Wild terms giving the number of elements in cg_index
  504. index_expr: expression
  505. Expression with Wild terms giving the index terms have when storing
  506. them to cg_index
  507. """
  508. other_part = 0
  509. i = 0
  510. while i < len(term_list):
  511. sub_1 = _check_cg(term_list[i], expr, len(variables))
  512. if sub_1 is None:
  513. i += 1
  514. continue
  515. if not sympify(build_index_expr.subs(sub_1)).is_number:
  516. i += 1
  517. continue
  518. sub_dep = [(x, sub_1[x]) for x in dep_variables]
  519. cg_index = [None]*build_index_expr.subs(sub_1)
  520. for j in range(i, len(term_list)):
  521. 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)))
  522. if sub_2 is None:
  523. continue
  524. if not sympify(index_expr.subs(sub_dep).subs(sub_2)).is_number:
  525. continue
  526. 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)
  527. if not any(i is None for i in cg_index):
  528. min_lt = min(*[ abs(term[2]) for term in cg_index ])
  529. indices = [ term[0] for term in cg_index]
  530. indices.sort()
  531. indices.reverse()
  532. [ term_list.pop(j) for j in indices ]
  533. for term in cg_index:
  534. if abs(term[2]) > min_lt:
  535. term_list.append( (term[2] - min_lt*term[3])*term[1] )
  536. other_part += min_lt*(sign*simp).subs(sub_1)
  537. else:
  538. i += 1
  539. return term_list, other_part
  540. def _check_cg(cg_term, expr, length, sign=None):
  541. """Checks whether a term matches the given expression"""
  542. # TODO: Check for symmetries
  543. matches = cg_term.match(expr)
  544. if matches is None:
  545. return
  546. if sign is not None:
  547. if not isinstance(sign, tuple):
  548. raise TypeError('sign must be a tuple')
  549. if not sign[0] == (sign[1]).subs(matches):
  550. return
  551. if len(matches) == length:
  552. return matches
  553. def _cg_simp_sum(e):
  554. e = _check_varsh_sum_871_1(e)
  555. e = _check_varsh_sum_871_2(e)
  556. e = _check_varsh_sum_872_4(e)
  557. return e
  558. def _check_varsh_sum_871_1(e):
  559. a = Wild('a')
  560. alpha = symbols('alpha')
  561. b = Wild('b')
  562. match = e.match(Sum(CG(a, alpha, b, 0, a, alpha), (alpha, -a, a)))
  563. if match is not None and len(match) == 2:
  564. return ((2*a + 1)*KroneckerDelta(b, 0)).subs(match)
  565. return e
  566. def _check_varsh_sum_871_2(e):
  567. a = Wild('a')
  568. alpha = symbols('alpha')
  569. c = Wild('c')
  570. match = e.match(
  571. Sum((-1)**(a - alpha)*CG(a, alpha, a, -alpha, c, 0), (alpha, -a, a)))
  572. if match is not None and len(match) == 2:
  573. return (sqrt(2*a + 1)*KroneckerDelta(c, 0)).subs(match)
  574. return e
  575. def _check_varsh_sum_872_4(e):
  576. alpha = symbols('alpha')
  577. beta = symbols('beta')
  578. a = Wild('a')
  579. b = Wild('b')
  580. c = Wild('c')
  581. cp = Wild('cp')
  582. gamma = Wild('gamma')
  583. gammap = Wild('gammap')
  584. cg1 = CG(a, alpha, b, beta, c, gamma)
  585. cg2 = CG(a, alpha, b, beta, cp, gammap)
  586. match1 = e.match(Sum(cg1*cg2, (alpha, -a, a), (beta, -b, b)))
  587. if match1 is not None and len(match1) == 6:
  588. return (KroneckerDelta(c, cp)*KroneckerDelta(gamma, gammap)).subs(match1)
  589. match2 = e.match(Sum(cg1**2, (alpha, -a, a), (beta, -b, b)))
  590. if match2 is not None and len(match2) == 4:
  591. return S.One
  592. return e
  593. def _cg_list(term):
  594. if isinstance(term, CG):
  595. return (term,), 1, 1
  596. cg = []
  597. coeff = 1
  598. if not isinstance(term, (Mul, Pow)):
  599. raise NotImplementedError('term must be CG, Add, Mul or Pow')
  600. if isinstance(term, Pow) and sympify(term.exp).is_number:
  601. if sympify(term.exp).is_number:
  602. [ cg.append(term.base) for _ in range(term.exp) ]
  603. else:
  604. return (term,), 1, 1
  605. if isinstance(term, Mul):
  606. for arg in term.args:
  607. if isinstance(arg, CG):
  608. cg.append(arg)
  609. else:
  610. coeff *= arg
  611. return cg, coeff, coeff/abs(coeff)