test_clebsch_gordan.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. from sympy.core.numbers import (I, pi, Rational)
  2. from sympy.core.singleton import S
  3. from sympy.core.symbol import symbols
  4. from sympy.functions.elementary.exponential import exp
  5. from sympy.functions.elementary.miscellaneous import sqrt
  6. from sympy.functions.elementary.trigonometric import (cos, sin)
  7. from sympy.functions.special.spherical_harmonics import Ynm
  8. from sympy.matrices.dense import Matrix
  9. from sympy.physics.wigner import (clebsch_gordan, wigner_9j, wigner_6j, gaunt,
  10. real_gaunt, racah, dot_rot_grad_Ynm, wigner_3j, wigner_d_small, wigner_d)
  11. from sympy.testing.pytest import raises, skip
  12. # for test cases, refer : https://en.wikipedia.org/wiki/Table_of_Clebsch%E2%80%93Gordan_coefficients
  13. def test_clebsch_gordan_docs():
  14. assert clebsch_gordan(Rational(3, 2), S.Half, 2, Rational(3, 2), S.Half, 2) == 1
  15. assert clebsch_gordan(Rational(3, 2), S.Half, 1, Rational(3, 2), Rational(-1, 2), 1) == sqrt(3)/2
  16. assert clebsch_gordan(Rational(3, 2), S.Half, 1, Rational(-1, 2), S.Half, 0) == -sqrt(2)/2
  17. def test_clebsch_gordan():
  18. # Argument order: (j_1, j_2, j, m_1, m_2, m)
  19. h = S.One
  20. k = S.Half
  21. l = Rational(3, 2)
  22. i = Rational(-1, 2)
  23. n = Rational(7, 2)
  24. p = Rational(5, 2)
  25. assert clebsch_gordan(k, k, 1, k, k, 1) == 1
  26. assert clebsch_gordan(k, k, 1, k, k, 0) == 0
  27. assert clebsch_gordan(k, k, 1, i, i, -1) == 1
  28. assert clebsch_gordan(k, k, 1, k, i, 0) == sqrt(2)/2
  29. assert clebsch_gordan(k, k, 0, k, i, 0) == sqrt(2)/2
  30. assert clebsch_gordan(k, k, 1, i, k, 0) == sqrt(2)/2
  31. assert clebsch_gordan(k, k, 0, i, k, 0) == -sqrt(2)/2
  32. assert clebsch_gordan(h, k, l, 1, k, l) == 1
  33. assert clebsch_gordan(h, k, l, 1, i, k) == 1/sqrt(3)
  34. assert clebsch_gordan(h, k, k, 1, i, k) == sqrt(2)/sqrt(3)
  35. assert clebsch_gordan(h, k, k, 0, k, k) == -1/sqrt(3)
  36. assert clebsch_gordan(h, k, l, 0, k, k) == sqrt(2)/sqrt(3)
  37. assert clebsch_gordan(h, h, S(2), 1, 1, S(2)) == 1
  38. assert clebsch_gordan(h, h, S(2), 1, 0, 1) == 1/sqrt(2)
  39. assert clebsch_gordan(h, h, S(2), 0, 1, 1) == 1/sqrt(2)
  40. assert clebsch_gordan(h, h, 1, 1, 0, 1) == 1/sqrt(2)
  41. assert clebsch_gordan(h, h, 1, 0, 1, 1) == -1/sqrt(2)
  42. assert clebsch_gordan(l, l, S(3), l, l, S(3)) == 1
  43. assert clebsch_gordan(l, l, S(2), l, k, S(2)) == 1/sqrt(2)
  44. assert clebsch_gordan(l, l, S(3), l, k, S(2)) == 1/sqrt(2)
  45. assert clebsch_gordan(S(2), S(2), S(4), S(2), S(2), S(4)) == 1
  46. assert clebsch_gordan(S(2), S(2), S(3), S(2), 1, S(3)) == 1/sqrt(2)
  47. assert clebsch_gordan(S(2), S(2), S(3), 1, 1, S(2)) == 0
  48. assert clebsch_gordan(p, h, n, p, 1, n) == 1
  49. assert clebsch_gordan(p, h, p, p, 0, p) == sqrt(5)/sqrt(7)
  50. assert clebsch_gordan(p, h, l, k, 1, l) == 1/sqrt(15)
  51. def test_clebsch_gordan_numpy():
  52. try:
  53. import numpy as np
  54. except ImportError:
  55. skip("numpy not installed")
  56. assert clebsch_gordan(*np.zeros(6).astype(np.int64)) == 1
  57. assert wigner_3j(2, np.float64(6.0), 4.0, 0, 0, 0) == sqrt(715)/143
  58. assert wigner_3j(0, 0.5, 0.5, 0, 0.5, -0.5) == sqrt(2)/2
  59. raises(ValueError, lambda: wigner_3j(2.1, 6, 4, 0, 0, 0))
  60. def test_wigner():
  61. try:
  62. import numpy as np
  63. except ImportError:
  64. skip("numpy not installed")
  65. def tn(a, b):
  66. return (a - b).n(64) < S('1e-64')
  67. assert tn(wigner_9j(1, 1, 1, 1, 1, 1, 1, 1, 0, prec=64), Rational(1, 18))
  68. assert wigner_9j(3, 3, 2, 3, 3, 2, 3, 3, 2) == 3221*sqrt(
  69. 70)/(246960*sqrt(105)) - 365/(3528*sqrt(70)*sqrt(105))
  70. assert wigner_6j(5, 5, 5, 5, 5, 5) == Rational(1, 52)
  71. assert tn(wigner_6j(8, 8, 8, 8, 8, 8, prec=64), Rational(-12219, 965770))
  72. assert wigner_6j(1, 1, 1, 1.0, np.float64(1.0), 1) == Rational(1, 6)
  73. assert wigner_6j(3.0, np.float32(3), 3.0, 3, 3, 3) == Rational(-1, 14)
  74. # regression test for #8747
  75. half = S.Half
  76. assert wigner_9j(0, 0, 0, 0, half, half, 0, half, half) == half
  77. assert (wigner_9j(3, 5, 4,
  78. 7 * half, 5 * half, 4,
  79. 9 * half, 9 * half, 0)
  80. == -sqrt(Rational(361, 205821000)))
  81. assert (wigner_9j(1, 4, 3,
  82. 5 * half, 4, 5 * half,
  83. 5 * half, 2, 7 * half)
  84. == -sqrt(Rational(3971, 373403520)))
  85. assert (wigner_9j(4, 9 * half, 5 * half,
  86. 2, 4, 4,
  87. 5, 7 * half, 7 * half)
  88. == -sqrt(Rational(3481, 5042614500)))
  89. assert (wigner_9j(5, 5, 5.0,
  90. np.float64(5.0), 5, 5,
  91. 5, 5, 5)
  92. == 0)
  93. assert (wigner_9j(1.0, 2.0, 3.0,
  94. 3, 2, 1,
  95. 2, 1, 3)
  96. == -4*sqrt(70)/11025)
  97. def test_gaunt():
  98. def tn(a, b):
  99. return (a - b).n(64) < S('1e-64')
  100. assert gaunt(1, 0, 1, 1, 0, -1) == -1/(2*sqrt(pi))
  101. assert isinstance(gaunt(1, 1, 0, -1, 1, 0).args[0], Rational)
  102. assert isinstance(gaunt(0, 1, 1, 0, -1, 1).args[0], Rational)
  103. assert tn(gaunt(
  104. 10, 10, 12, 9, 3, -12, prec=64), (Rational(-98, 62031)) * sqrt(6279)/sqrt(pi))
  105. def gaunt_ref(l1, l2, l3, m1, m2, m3):
  106. return (
  107. sqrt((2 * l1 + 1) * (2 * l2 + 1) * (2 * l3 + 1) / (4 * pi)) *
  108. wigner_3j(l1, l2, l3, 0, 0, 0) *
  109. wigner_3j(l1, l2, l3, m1, m2, m3)
  110. )
  111. threshold = 1e-10
  112. l_max = 3
  113. l3_max = 24
  114. for l1 in range(l_max + 1):
  115. for l2 in range(l_max + 1):
  116. for l3 in range(l3_max + 1):
  117. for m1 in range(-l1, l1 + 1):
  118. for m2 in range(-l2, l2 + 1):
  119. for m3 in range(-l3, l3 + 1):
  120. args = l1, l2, l3, m1, m2, m3
  121. g = gaunt(*args)
  122. g0 = gaunt_ref(*args)
  123. assert abs(g - g0) < threshold
  124. if m1 + m2 + m3 != 0:
  125. assert abs(g) < threshold
  126. if (l1 + l2 + l3) % 2:
  127. assert abs(g) < threshold
  128. assert gaunt(1, 1, 0, 0, 2, -2) is S.Zero
  129. def test_realgaunt():
  130. # All non-zero values corresponding to l values from 0 to 2
  131. for l in range(3):
  132. for m in range(-l, l+1):
  133. assert real_gaunt(0, l, l, 0, m, m) == 1/(2*sqrt(pi))
  134. assert real_gaunt(1, 1, 2, 0, 0, 0) == sqrt(5)/(5*sqrt(pi))
  135. assert real_gaunt(1, 1, 2, 1, 1, 0) == -sqrt(5)/(10*sqrt(pi))
  136. assert real_gaunt(2, 2, 2, 0, 0, 0) == sqrt(5)/(7*sqrt(pi))
  137. assert real_gaunt(2, 2, 2, 0, 2, 2) == -sqrt(5)/(7*sqrt(pi))
  138. assert real_gaunt(2, 2, 2, -2, -2, 0) == -sqrt(5)/(7*sqrt(pi))
  139. assert real_gaunt(1, 1, 2, -1, 0, -1) == sqrt(15)/(10*sqrt(pi))
  140. assert real_gaunt(1, 1, 2, 0, 1, 1) == sqrt(15)/(10*sqrt(pi))
  141. assert real_gaunt(1, 1, 2, 1, 1, 2) == sqrt(15)/(10*sqrt(pi))
  142. assert real_gaunt(1, 1, 2, -1, 1, -2) == sqrt(15)/(10*sqrt(pi))
  143. assert real_gaunt(1, 1, 2, -1, -1, 2) == -sqrt(15)/(10*sqrt(pi))
  144. assert real_gaunt(2, 2, 2, 0, 1, 1) == sqrt(5)/(14*sqrt(pi))
  145. assert real_gaunt(2, 2, 2, 1, 1, 2) == sqrt(15)/(14*sqrt(pi))
  146. assert real_gaunt(2, 2, 2, -1, -1, 2) == -sqrt(15)/(14*sqrt(pi))
  147. assert real_gaunt(-2, -2, -2, -2, -2, 0) is S.Zero # m test
  148. assert real_gaunt(-2, 1, 0, 1, 1, 1) is S.Zero # l test
  149. assert real_gaunt(-2, -1, -2, -1, -1, 0) is S.Zero # m and l test
  150. assert real_gaunt(-2, -2, -2, -2, -2, -2) is S.Zero # m and k test
  151. assert real_gaunt(-2, -1, -2, -1, -1, -1) is S.Zero # m, l and k test
  152. x = symbols('x', integer=True)
  153. v = [0]*6
  154. for i in range(len(v)):
  155. v[i] = x # non literal ints fail
  156. raises(ValueError, lambda: real_gaunt(*v))
  157. v[i] = 0
  158. def test_racah():
  159. assert racah(3,3,3,3,3,3) == Rational(-1,14)
  160. assert racah(2,2,2,2,2,2) == Rational(-3,70)
  161. assert racah(7,8,7,1,7,7, prec=4).is_Float
  162. assert racah(5.5,7.5,9.5,6.5,8,9) == -719*sqrt(598)/1158924
  163. assert abs(racah(5.5,7.5,9.5,6.5,8,9, prec=4) - (-0.01517)) < S('1e-4')
  164. def test_dot_rota_grad_SH():
  165. theta, phi = symbols("theta phi")
  166. assert dot_rot_grad_Ynm(1, 1, 1, 1, 1, 0) != \
  167. sqrt(30)*Ynm(2, 2, 1, 0)/(10*sqrt(pi))
  168. assert dot_rot_grad_Ynm(1, 1, 1, 1, 1, 0).doit() == \
  169. sqrt(30)*Ynm(2, 2, 1, 0)/(10*sqrt(pi))
  170. assert dot_rot_grad_Ynm(1, 5, 1, 1, 1, 2) != \
  171. 0
  172. assert dot_rot_grad_Ynm(1, 5, 1, 1, 1, 2).doit() == \
  173. 0
  174. assert dot_rot_grad_Ynm(3, 3, 3, 3, theta, phi).doit() == \
  175. 15*sqrt(3003)*Ynm(6, 6, theta, phi)/(143*sqrt(pi))
  176. assert dot_rot_grad_Ynm(3, 3, 1, 1, theta, phi).doit() == \
  177. sqrt(3)*Ynm(4, 4, theta, phi)/sqrt(pi)
  178. assert dot_rot_grad_Ynm(3, 2, 2, 0, theta, phi).doit() == \
  179. 3*sqrt(55)*Ynm(5, 2, theta, phi)/(11*sqrt(pi))
  180. assert dot_rot_grad_Ynm(3, 2, 3, 2, theta, phi).doit().expand() == \
  181. -sqrt(70)*Ynm(4, 4, theta, phi)/(11*sqrt(pi)) + \
  182. 45*sqrt(182)*Ynm(6, 4, theta, phi)/(143*sqrt(pi))
  183. def test_wigner_d():
  184. half = S(1)/2
  185. assert wigner_d_small(half, 0) == Matrix([[1, 0], [0, 1]])
  186. assert wigner_d_small(half, pi/2) == Matrix([[1, 1], [-1, 1]])/sqrt(2)
  187. assert wigner_d_small(half, pi) == Matrix([[0, 1], [-1, 0]])
  188. alpha, beta, gamma = symbols("alpha, beta, gamma", real=True)
  189. D = wigner_d(half, alpha, beta, gamma)
  190. assert D[0, 0] == exp(I*alpha/2)*exp(I*gamma/2)*cos(beta/2)
  191. assert D[0, 1] == exp(I*alpha/2)*exp(-I*gamma/2)*sin(beta/2)
  192. assert D[1, 0] == -exp(-I*alpha/2)*exp(I*gamma/2)*sin(beta/2)
  193. assert D[1, 1] == exp(-I*alpha/2)*exp(-I*gamma/2)*cos(beta/2)
  194. # Test Y_{n mi}(g*x)=\sum_{mj}D^n_{mi mj}*Y_{n mj}(x)
  195. theta, phi = symbols("theta phi", real=True)
  196. v = Matrix([Ynm(1, mj, theta, phi) for mj in range(1, -2, -1)])
  197. w = wigner_d(1, -pi/2, pi/2, -pi/2)@v.subs({theta: pi/4, phi: pi})
  198. w_ = v.subs({theta: pi/2, phi: pi/4})
  199. assert w.expand(func=True).as_real_imag() == w_.expand(func=True).as_real_imag()