/Numerics-v2/Test/SpinTest.cs

# · C# · 1125 lines · 683 code · 328 blank · 114 comment · 80 complexity · 799a55ac1dfe2138ec0a7cfbe057b89f MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using Microsoft.VisualStudio.TestTools.UnitTesting;
  5. using Meta.Numerics;
  6. using Meta.Numerics.Functions;
  7. using Meta.Numerics.Spin;
  8. namespace Test {
  9. public struct SpinRange {
  10. public Spin Minimum;
  11. public Spin Maximum;
  12. }
  13. public struct ThreeJSymbol {
  14. public SpinState Column1;
  15. public SpinState Column2;
  16. public SpinState Column3;
  17. }
  18. public struct SixJSymbol {
  19. public Spin J1;
  20. public Spin J2;
  21. public Spin J3;
  22. public Spin J4;
  23. public Spin J5;
  24. public Spin J6;
  25. }
  26. /// <summary>
  27. /// Summary description for SpinTest
  28. /// </summary>
  29. [TestClass]
  30. public class SpinTest {
  31. public SpinTest () {
  32. //
  33. // TODO: Add constructor logic here
  34. //
  35. }
  36. private TestContext testContextInstance;
  37. /// <summary>
  38. ///Gets or sets the test context which provides
  39. ///information about and functionality for the current test run.
  40. ///</summary>
  41. public TestContext TestContext {
  42. get {
  43. return testContextInstance;
  44. }
  45. set {
  46. testContextInstance = value;
  47. }
  48. }
  49. #region Additional test attributes
  50. //
  51. // You can use the following additional attributes as you write your tests:
  52. //
  53. // Use ClassInitialize to run code before running the first test in the class
  54. // [ClassInitialize()]
  55. // public static void MyClassInitialize(TestContext testContext) { }
  56. //
  57. // Use ClassCleanup to run code after all tests in a class have run
  58. // [ClassCleanup()]
  59. // public static void MyClassCleanup() { }
  60. //
  61. // Use TestInitialize to run code before running each test
  62. // [TestInitialize()]
  63. // public void MyTestInitialize() { }
  64. //
  65. // Use TestCleanup to run code after each test has run
  66. // [TestCleanup()]
  67. // public void MyTestCleanup() { }
  68. //
  69. #endregion
  70. private static ThreeJSymbol[] GenerateRandomThreeJSymbols (double j_max, int n) {
  71. int tj_max = (int) Math.Truncate(2.0 * j_max);
  72. ThreeJSymbol[] symbols = new ThreeJSymbol[n];
  73. Random rng = new Random(1);
  74. for (int i = 0; i < n; i++) {
  75. int tj1 = rng.Next(tj_max + 1);
  76. int tj2 = rng.Next(tj_max + 1);
  77. int tm1 = -tj1 + 2 * rng.Next(tj1);
  78. int tm2 = -tj2 + 2 * rng.Next(tj2);
  79. int tm3 = -(tm1 + tm2);
  80. int tj3_min = Math.Abs(tj1 - tj2);
  81. int tj3_max = tj1 + tj2;
  82. if (Math.Abs(tm3) > tj3_min) tj3_min = Math.Abs(tm3);
  83. int tj3 = tj3_min + 2 * rng.Next((tj3_max - tj3_min) / 2);
  84. ThreeJSymbol symbol = new ThreeJSymbol();
  85. symbol.Column1 = new SpinState(tj1 / 2.0, tm1 / 2.0);
  86. symbol.Column2 = new SpinState(tj2 / 2.0, tm2 / 2.0);
  87. symbol.Column3 = new SpinState(tj3 / 2.0, tm3 / 2.0);
  88. symbols[i] = symbol;
  89. }
  90. return (symbols);
  91. }
  92. private static SixJSymbol[] GenerateRandomSixJSymbols (double j_max, int n) {
  93. int tj_max = (int) Math.Truncate(2.0 * j_max);
  94. SixJSymbol[] symbols = new SixJSymbol[n];
  95. Random rng = new Random(1);
  96. int i = 0;
  97. while (i < n) {
  98. //for (int i = 0; i < n; i++) {
  99. // pick 1, 4, and 2 randomly
  100. int tj1 = rng.Next(tj_max + 1);
  101. int tj4 = rng.Next(tj_max + 1);
  102. int tj2 = rng.Next(tj_max + 1);
  103. // make sure 5 has appropriate wholess so that 1+2 has same wholeness as 4+5
  104. int tj5 = rng.Next(tj_max + 1);
  105. if (((tj1 + tj2) % 2) != ((tj4 + tj5) % 2)) tj5++;
  106. // make sure 3 can be formed from 1+2 or 4+5
  107. int tj12_min = Math.Abs(tj1 - tj2);
  108. int tj45_min = Math.Abs(tj4 - tj5);
  109. int tj3_min;
  110. if (tj12_min > tj45_min) {
  111. tj3_min = tj12_min;
  112. } else {
  113. tj3_min = tj45_min;
  114. }
  115. int tj12_max = tj1 + tj2;
  116. int tj45_max = tj4 + tj5;
  117. int tj3_max;
  118. if (tj12_max < tj45_max) {
  119. tj3_max = tj12_max;
  120. } else {
  121. tj3_max = tj45_max;
  122. }
  123. if (tj3_min > tj3_max) continue;
  124. int tj3 = tj3_min + 2 * rng.Next((tj3_max - tj3_min) / 2 + 1);
  125. // make sure 6 can be formed from 1+5 or 4+2
  126. int tj15_min = Math.Abs(tj1 - tj5);
  127. int tj42_min = Math.Abs(tj4 - tj2);
  128. int tj6_min;
  129. if (tj15_min > tj42_min) {
  130. tj6_min = tj15_min;
  131. } else {
  132. tj6_min = tj42_min;
  133. }
  134. int tj15_max = tj1 + tj5;
  135. int tj42_max = tj4 + tj2;
  136. int tj6_max;
  137. if (tj15_max < tj42_max) {
  138. tj6_max = tj15_max;
  139. } else {
  140. tj6_max = tj42_max;
  141. }
  142. if (tj6_min > tj6_max) continue;
  143. int tj6 = tj6_min + 2 * rng.Next((tj6_max - tj6_min) / 2 + 1);
  144. // make the symbol
  145. SixJSymbol symbol = new SixJSymbol();
  146. symbol.J1 = new Spin(tj1 / 2.0);
  147. symbol.J2 = new Spin(tj2 / 2.0);
  148. symbol.J3 = new Spin(tj3 / 2.0);
  149. symbol.J4 = new Spin(tj4 / 2.0);
  150. symbol.J5 = new Spin(tj5 / 2.0);
  151. symbol.J6 = new Spin(tj6 / 2.0);
  152. symbols[i] = symbol;
  153. i++;
  154. }
  155. return (symbols);
  156. }
  157. private static SpinRange CombinedSpinRange (Spin j1, Spin j2) {
  158. int tj1 = (int) Math.Round(2 * j1.J);
  159. int tj2 = (int) Math.Round(2 * j2.J);
  160. int tj_min = Math.Abs(tj1 - tj2);
  161. int tj_max = tj1 + tj2;
  162. SpinRange range = new SpinRange();
  163. range.Minimum = new Spin(tj_min / 2.0);
  164. range.Maximum = new Spin(tj_max / 2.0);
  165. return (range);
  166. }
  167. private static SpinRange CombinedSpinRange (SpinRange r1, SpinRange r2) {
  168. SpinRange range = new SpinRange();
  169. if (r1.Minimum.J > r2.Minimum.J) {
  170. range.Minimum = r1.Minimum;
  171. } else {
  172. range.Minimum = r2.Minimum;
  173. }
  174. if (r1.Maximum.J < r2.Maximum.J) {
  175. range.Maximum = r1.Maximum;
  176. } else {
  177. range.Maximum = r2.Maximum;
  178. }
  179. return (range);
  180. }
  181. private static Spin[] AllSpinsInRange (SpinRange range) {
  182. int tj_min = (int) Math.Round(2 * range.Minimum.J);
  183. int tj_max = (int) Math.Round(2 * range.Maximum.J);
  184. List<Spin> spins = new List<Spin>();
  185. for (int tj = tj_min; tj <= tj_max; tj+=2) {
  186. spins.Add(new Spin(tj / 2.0));
  187. }
  188. return (spins.ToArray());
  189. }
  190. private static Spin[] RandomSpinsInRange (SpinRange range, int n) {
  191. int tj_min = (int) Math.Truncate(2.0 * range.Minimum.J);
  192. int tj_max = (int) Math.Truncate(2.0 * range.Maximum.J);
  193. Spin[] spins = new Spin[n];
  194. Random rng = new Random(1);
  195. for (int i = 0; i < n; i++) {
  196. spins[i] = new Spin((tj_min + 2 * rng.Next((tj_max - tj_min) / 2 + 1))/2.0);
  197. }
  198. return (spins);
  199. }
  200. private static Spin[] GenerateRandomSpins (double j_min, double j_max, int n) {
  201. int tj_min = (int) Math.Truncate(2.0 * j_min);
  202. int tj_max = (int) Math.Truncate(2.0 * j_max);
  203. Spin[] spins = new Spin[n];
  204. Random rng = new Random(1);
  205. for (int i = 0; i < n; i++) {
  206. spins[i] = new Spin(rng.Next(tj_min, tj_max) / 2.0);
  207. }
  208. return (spins);
  209. }
  210. private static Spin[] GenerateRandomCombinedSpins (Spin j1, Spin j2, int n) {
  211. int tj_min = (int) (2.0 * Math.Abs(j1.J - j2.J));
  212. int tj_max = (int) (2.0 * (j1.J + j2.J));
  213. Spin[] spins = new Spin[n];
  214. Random rng = new Random(1);
  215. for (int i = 0; i < n; i++) {
  216. int tj = tj_min + 2 * rng.Next((tj_max - tj_min) / 2);
  217. spins[i] = new Spin(tj / 2.0);
  218. }
  219. return (spins);
  220. }
  221. private static Spin[] GenerateCombinedSpins (Spin j1, Spin j2) {
  222. int tj_min = (int) (2.0 * Math.Abs(j1.J - j2.J));
  223. int tj_max = (int) (2.0 * (j1.J + j2.J));
  224. List<Spin> spins = new List<Spin>();
  225. for (int tj = tj_min; tj <= tj_max; tj += 2) {
  226. spins.Add(new Spin(tj / 2.0));
  227. }
  228. return (spins.ToArray());
  229. }
  230. private static SpinState[] GenerateRandomSpinStates (Spin j, int n) {
  231. int tj = (int) (2.0 * j.J);
  232. SpinState[] result = new SpinState[n];
  233. Random rng = new Random(1);
  234. for (int i = 0; i < n; i++) {
  235. int tm = -tj + 2 * rng.Next(tj + 1);
  236. result[i] = new SpinState(j, tm / 2.0);
  237. }
  238. return (result);
  239. }
  240. // generates only spinors of the same parity as j_min
  241. private static SpinState[] GenerateRandomSpinStates (double j_min, double j_max, int n) {
  242. int tj_min = (int) Math.Truncate(2.0 * j_min);
  243. int tj_max = (int) Math.Truncate(2.0 * j_max);
  244. SpinState[] states = new SpinState[n];
  245. Random rng = new Random(1);
  246. for (int i = 0; i < n; i++) {
  247. int tj = tj_min + 2 * rng.Next((tj_max - tj_min) / 2);
  248. int tm = -tj + 2 * rng.Next(tj);
  249. states[i] = new SpinState(tj / 2.0, tm / 2.0);
  250. }
  251. return (states);
  252. }
  253. [TestMethod]
  254. public void SpinEquality () {
  255. Spin s0 = Spin.SpinZero;
  256. Spin s1a = Spin.SpinOne;
  257. Spin s1b = new Spin(1.0);
  258. Assert.IsTrue(s1a == s1b);
  259. Assert.IsTrue(s0 != s1a);
  260. SpinState[] ss = s1a.States();
  261. Assert.IsTrue(ss.Length == s1a.Dimension);
  262. for (int i = 0; i < ss.Length; i++) {
  263. for (int j = 0; j < ss.Length; j++) {
  264. if (i == j) {
  265. Assert.IsTrue(ss[i] == ss[j]);
  266. } else {
  267. Assert.IsTrue(ss[i] != ss[j]);
  268. }
  269. }
  270. }
  271. }
  272. [TestMethod]
  273. [ExpectedException(typeof(ArgumentOutOfRangeException))]
  274. public void SpinInvalid1 () {
  275. // spin indices are non-negative
  276. Spin s = new Spin(-1.0);
  277. }
  278. [TestMethod]
  279. [ExpectedException(typeof(ArgumentOutOfRangeException))]
  280. public void SpinInvalid2 () {
  281. // spin indices are integer or half-integer
  282. Spin s = new Spin(1.25);
  283. }
  284. [TestMethod]
  285. [ExpectedException(typeof(ArgumentOutOfRangeException))]
  286. public void SpinStateInvalid1 () {
  287. // spin state indices are smaller than spin indices
  288. SpinState s = new SpinState(1.0, -2.0);
  289. }
  290. [TestMethod]
  291. [ExpectedException(typeof(ArgumentOutOfRangeException))]
  292. public void SpinStateInvalid2 () {
  293. // spin states indices match the parity of spin indices
  294. SpinState s = new SpinState(1.0, 0.5);
  295. }
  296. [TestMethod]
  297. [ExpectedException(typeof(ArgumentOutOfRangeException))]
  298. public void SpinStateInvalid3 () {
  299. // spin state values are integer or half-integer
  300. SpinState s = new SpinState(3.0, 0.75);
  301. }
  302. [TestMethod]
  303. public void ClebschGordonSepcialCase () {
  304. // 0 x 0 => 0
  305. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(0, 0), new SpinState(0, 0), new SpinState(0, 0)), 1.0));
  306. // 1/2 x 1/2 => 1
  307. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(0.5, 0.5), new SpinState(0.5, 0.5), new SpinState(1.0, 1.0)), 1.0));
  308. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(0.5, 0.5), new SpinState(0.5, -0.5), new SpinState(1.0, 0.0)), Math.Sqrt(1.0 / 2.0)));
  309. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(0.5, -0.5), new SpinState(0.5, 0.5), new SpinState(1.0, 0.0)), Math.Sqrt(1.0 / 2.0)));
  310. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(0.5, -0.5), new SpinState(0.5, -0.5), new SpinState(1.0, -1.0)), 1.0));
  311. // 1/2 x 1/2 => 0
  312. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(0.5, 0.5), new SpinState(0.5, -0.5), new SpinState(0.0, 0.0)), Math.Sqrt(1.0 / 2.0)));
  313. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(0.5, -0.5), new SpinState(0.5, 0.5), new SpinState(0.0, 0.0)), -Math.Sqrt(1.0 / 2.0)));
  314. // 1 x 1/2 => 3/2
  315. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(0.5, 0.5), new SpinState(1.5, 1.5)), 1.0));
  316. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(0.5, -0.5), new SpinState(1.5, 0.5)), Math.Sqrt(1.0 / 3.0)));
  317. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(0.5, 0.5), new SpinState(1.5, 0.5)), Math.Sqrt(2.0 / 3.0)));
  318. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(0.5, -0.5), new SpinState(1.5, -0.5)), Math.Sqrt(2.0 / 3.0)));
  319. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(0.5, 0.5), new SpinState(1.5, -0.5)), Math.Sqrt(1.0 / 3.0)));
  320. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(0.5, -0.5), new SpinState(1.5, -1.5)), 1.0));
  321. // 1 x 1/2 => 1/2
  322. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(0.5, -0.5), new SpinState(0.5, 0.5)), Math.Sqrt(2.0 / 3.0)));
  323. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(0.5, 0.5), new SpinState(0.5, 0.5)), -Math.Sqrt(1.0 / 3.0)));
  324. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(0.5, -0.5), new SpinState(0.5, -0.5)), Math.Sqrt(1.0 / 3.0)));
  325. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(0.5, 0.5), new SpinState(0.5, -0.5)), -Math.Sqrt(2.0 / 3.0)));
  326. // 1 x 1 => 2
  327. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(1.0, 1.0), new SpinState(2.0, 2.0)), 1.0));
  328. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(1.0, 0.0), new SpinState(2.0, 1.0)), Math.Sqrt(1.0 / 2.0)));
  329. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(1.0, 1.0), new SpinState(2.0, 1.0)), Math.Sqrt(1.0 / 2.0)));
  330. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(1.0, -1.0), new SpinState(2.0, 0.0)), Math.Sqrt(1.0 / 6.0)));
  331. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(1.0, 0.0), new SpinState(2.0, 0.0)), Math.Sqrt(2.0 / 3.0)));
  332. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(1.0, 1.0), new SpinState(2.0, 0.0)), Math.Sqrt(1.0 / 6.0)));
  333. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(1.0, -1.0), new SpinState(2.0, -1.0)), Math.Sqrt(1.0 / 2.0)));
  334. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(1.0, 0.0), new SpinState(2.0, -1.0)), Math.Sqrt(1.0 / 2.0)));
  335. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0,-1.0), new SpinState(1.0,-1.0), new SpinState(2.0, -2.0)), 1.0));
  336. // 1 x 1=> 1
  337. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(1.0, 0.0), new SpinState(1.0, 1.0)), Math.Sqrt(1.0 / 2.0)));
  338. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(1.0, 1.0), new SpinState(1.0, 1.0)), -Math.Sqrt(1.0 / 2.0)));
  339. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(1.0, -1.0), new SpinState(1.0, 0.0)), Math.Sqrt(1.0 / 2.0)));
  340. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(1.0, 0.0), new SpinState(1.0, 0.0)), 0.0));
  341. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(1.0, 1.0), new SpinState(1.0, 0.0)), -Math.Sqrt(1.0 / 2.0)));
  342. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(1.0, -1.0), new SpinState(1.0, -1.0)), Math.Sqrt(1.0 / 2.0)));
  343. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(1.0, 0.0), new SpinState(1.0, -1.0)), -Math.Sqrt(1.0 / 2.0)));
  344. // 1 x 1=> 0
  345. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 1.0), new SpinState(1.0, -1.0), new SpinState(0.0, 0.0)), Math.Sqrt(1.0 / 3.0)));
  346. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, 0.0), new SpinState(1.0, 0.0), new SpinState(0.0, 0.0)), -Math.Sqrt(1.0 / 3.0)));
  347. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.ClebschGodron(new SpinState(1.0, -1.0), new SpinState(1.0, 1.0), new SpinState(0.0, 0.0)), Math.Sqrt(1.0 / 3.0)));
  348. }
  349. [TestMethod]
  350. public void ClebschGordonOrthonormalityJM () {
  351. int t00 = 0;
  352. int t01 = 0;
  353. int t1 = 0;
  354. foreach (Spin j1 in GenerateRandomSpins(0.0, 50.0, 4)) {
  355. foreach (Spin j2 in GenerateRandomSpins(0.0, 50.0, 4)) {
  356. foreach (Spin j3a in GenerateRandomCombinedSpins(j1, j2, 4)) {
  357. foreach (Spin j3b in GenerateRandomCombinedSpins(j1, j2, 4)) {
  358. foreach (SpinState s3a in GenerateRandomSpinStates(j3a, 4)) {
  359. foreach (SpinState s3b in GenerateRandomSpinStates(j3b, 4)) {
  360. // skip the trivial zeros; if the sums of the Ms for each coefficient
  361. // are not the same, no term can be non-zero
  362. if (s3a.M != s3b.M) continue;
  363. // sum over m1 and m2
  364. double s = 0.0;
  365. bool nonZero = false;
  366. foreach (SpinState s1 in j1.States()) {
  367. foreach (SpinState s2 in j2.States()) {
  368. double ds = SpinMath.ClebschGodron(s1, s2, s3a) * SpinMath.ClebschGodron(s1, s2, s3b);
  369. if (ds != 0.0) nonZero = true;
  370. s += ds;
  371. }
  372. }
  373. // check orthonormality
  374. if ((s3a.J == s3b.J) && (s3a.M == s3b.M)) {
  375. Assert.IsTrue(TestUtilities.IsNearlyEqual(s, 1.0));
  376. t1++;
  377. } else {
  378. Assert.IsTrue(Math.Abs(s) <= TestUtilities.TargetPrecision);
  379. if (nonZero) {
  380. t01++;
  381. } else {
  382. t00++;
  383. }
  384. }
  385. }
  386. }
  387. }
  388. }
  389. }
  390. }
  391. Console.WriteLine("Trivial zeros: {0}", t00);
  392. Console.WriteLine("Non-trivial zerios: {0}", t01);
  393. Console.WriteLine("Ones: {0}", t1);
  394. }
  395. [TestMethod]
  396. public void ClebschGordonOrthonormalityMM () {
  397. int t00 = 0;
  398. int t01 = 0;
  399. int t1 = 1;
  400. foreach (Spin j1 in GenerateRandomSpins(0.0, 50.0, 4)) {
  401. foreach (Spin j2 in GenerateRandomSpins(0.0, 50.0, 4)) {
  402. foreach (SpinState s1a in GenerateRandomSpinStates(j1, 4)) {
  403. foreach (SpinState s1b in GenerateRandomSpinStates(j1, 4)) {
  404. foreach (SpinState s2a in GenerateRandomSpinStates(j2, 4)) {
  405. foreach (SpinState s2b in GenerateRandomSpinStates(j2, 4)) {
  406. // if the sum of M's are equal, all terms will be trivially zero
  407. if ((s1a.M + s2a.M) != (s1b.M + s2b.M)) continue;
  408. // sum over j and m
  409. double s = 0.0;
  410. bool nonZero = false;
  411. double j_min = Math.Abs(j1.J - j2.J);
  412. double j_max = j1.J + j2.J;
  413. for (double j = j_min; j <= j_max; j = j + 1.0) {
  414. Spin j3 = new Spin(j);
  415. foreach (SpinState s3 in j3.States()) {
  416. double ds = SpinMath.ClebschGodron(s1a, s2a, s3) * SpinMath.ClebschGodron(s1b, s2b, s3);
  417. if (ds != 0.0) nonZero = true;
  418. s += ds;
  419. }
  420. }
  421. if ((s1a.M == s1b.M) && (s2a.M == s2b.M)) {
  422. Assert.IsTrue(TestUtilities.IsNearlyEqual(s, 1.0));
  423. t1++;
  424. } else {
  425. Assert.IsTrue(Math.Abs(s) < TestUtilities.TargetPrecision);
  426. if (nonZero) {
  427. t01++;
  428. } else {
  429. t00++;
  430. }
  431. }
  432. }
  433. }
  434. }
  435. }
  436. }
  437. }
  438. Console.WriteLine("Trivial zeros: {0}", t00);
  439. Console.WriteLine("Non-trivial zerios: {0}", t01);
  440. Console.WriteLine("Ones: {0}", t1);
  441. }
  442. [TestMethod]
  443. public void ThreeJExchangeSymmetry () {
  444. foreach (ThreeJSymbol symbol in GenerateRandomThreeJSymbols(50.0, 10)) {
  445. SpinState s1 = symbol.Column1;
  446. SpinState s2 = symbol.Column2;
  447. SpinState s3 = symbol.Column3;
  448. Console.WriteLine("( {0} {1} {2} )", s1.J, s2.J, s3.J);
  449. Console.WriteLine("( {0} {1} {2} )", s1.M, s2.M, s3.M);
  450. double s123 = SpinMath.ThreeJ(s1, s2, s3);
  451. double s231 = SpinMath.ThreeJ(s2, s3, s1);
  452. double s312 = SpinMath.ThreeJ(s3, s1, s2);
  453. Console.WriteLine("{0},{1},{2}", s123, s231, s312);
  454. Assert.IsTrue(TestUtilities.IsNearlyEqual(s123, s231));
  455. Assert.IsTrue(TestUtilities.IsNearlyEqual(s123, s312));
  456. int P;
  457. if (((int) (s1.J + s2.J + s3.J)) % 2 == 0) {
  458. P = 1;
  459. } else {
  460. P = -1;
  461. }
  462. double s132 = SpinMath.ThreeJ(s1, s3, s2);
  463. double s213 = SpinMath.ThreeJ(s2, s1, s3);
  464. double s321 = SpinMath.ThreeJ(s3, s2, s1);
  465. Console.WriteLine("{0},{1},{2}", s132, s213, s321);
  466. Assert.IsTrue(TestUtilities.IsNearlyEqual(s123, P * s132));
  467. Assert.IsTrue(TestUtilities.IsNearlyEqual(s123, P * s213));
  468. Assert.IsTrue(TestUtilities.IsNearlyEqual(s123, P * s321));
  469. }
  470. }
  471. [TestMethod]
  472. public void ThreeJRacahSymmetry () {
  473. foreach (ThreeJSymbol symbol in GenerateRandomThreeJSymbols(50.0, 10)) {
  474. // compute the 3j symbol
  475. SpinState s1 = symbol.Column1;
  476. SpinState s2 = symbol.Column2;
  477. SpinState s3 = symbol.Column3;
  478. double C = SpinMath.ThreeJ(s1, s2, s3);
  479. // form other 3j symbols related by Racah symmetry
  480. double k1, k2, k3, n1, n2, n3;
  481. SpinState t1, t2, t3;
  482. // rows 1->2->3
  483. k1 = (s2.J + s2.M + s3.J + s3.M) / 2.0;
  484. k2 = (s1.J + s1.M + s3.J + s3.M) / 2.0;
  485. k3 = (s1.J + s1.M + s2.J + s2.M) / 2.0;
  486. n1 = s1.J - s1.M - k1;
  487. n2 = s2.J - s2.M - k2;
  488. n3 = s3.J - s3.M - k3;
  489. t1 = new SpinState(k1, n1);
  490. t2 = new SpinState(k2, n2);
  491. t3 = new SpinState(k3, n3);
  492. double CC = SpinMath.ThreeJ(t1, t2, t3);
  493. Assert.IsTrue(TestUtilities.IsNearlyEqual(C, CC));
  494. // transpose rows and columns
  495. /*
  496. k1 = s1.J;
  497. k2 = (s2.J + s3.J + s1.M) / 2.0;
  498. k3 = (s2.J + s3.J - s1.M) / 2.0;
  499. n1 = s2.J - s3.J;
  500. n2 = s3.J - s3.M - k2;
  501. n3 = s3.J + s3.M - k2;
  502. t1 = new SpinState(k1, n1);
  503. t2 = new SpinState(k2, n2);
  504. t3 = new SpinState(k3, n3);
  505. double CT = SpinMath.ThreeJ(t1, t2, t3);
  506. Assert.IsTrue(TestUtilities.IsNearlyEqual(C, CT));
  507. */
  508. }
  509. }
  510. [TestMethod]
  511. public void ThreeJRecursion () {
  512. foreach (ThreeJSymbol symbol in GenerateRandomThreeJSymbols(50.0, 10)) {
  513. double j1 = symbol.Column1.J;
  514. double m1 = symbol.Column1.M;
  515. double j2 = symbol.Column2.J;
  516. double m2 = symbol.Column2.M;
  517. double j3 = symbol.Column3.J;
  518. double m3 = symbol.Column3.M;
  519. double s1 = SpinMath.ThreeJ(new SpinState(j1, m1), new SpinState(j2, m2), new SpinState(j3, m3));
  520. double s2 = 0.0;
  521. if ((Math.Abs(m2-1.0) <= j2) && (Math.Abs(m3+1.0) <= j3))
  522. s2 = SpinMath.ThreeJ(new SpinState(j1, m1), new SpinState(j2, m2 - 1.0), new SpinState(j3, m3 + 1.0));
  523. double s3 = 0.0;
  524. if ((Math.Abs(m1-1.0) <= j1) && (Math.Abs(m3+1.0) <= j3))
  525. s3 = SpinMath.ThreeJ(new SpinState(j1, m1 - 1.0), new SpinState(j2, m2), new SpinState(j3, m3 + 1.0));
  526. double c1 = Math.Sqrt((j3 + m3 + 1.0) * (j3 - m3));
  527. double c2 = Math.Sqrt((j2 - m2 + 1.0) * (j2 + m2));
  528. double c3 = Math.Sqrt((j1 - m1 + 1.0) * (j1 + m1));
  529. Assert.IsTrue(TestUtilities.IsSumNearlyEqual(c1 * s1, c2 * s2, -c3 * s3));
  530. }
  531. }
  532. [TestMethod]
  533. public void ThreeJLegendreIntegral () {
  534. // pick three legendre polynomials
  535. foreach (int l1 in TestUtilities.GenerateUniformIntegerValues(0, 16, 8)) {
  536. foreach (int l2 in TestUtilities.GenerateUniformIntegerValues(0, 16, 4)) {
  537. foreach (int l3 in TestUtilities.GenerateUniformIntegerValues(0, 16, 2)) {
  538. Console.WriteLine("{0} {1} {2}", l1, l2, l3);
  539. // do the integral over their product
  540. Func<double, double> f = delegate(double x) {
  541. return (
  542. OrthogonalPolynomials.LegendreP(l1, x) *
  543. OrthogonalPolynomials.LegendreP(l2, x) *
  544. OrthogonalPolynomials.LegendreP(l3, x)
  545. );
  546. };
  547. double I = FunctionMath.Integrate(f, Interval.FromEndpoints(-1.0, 1.0));
  548. // it should be the same as 2 ( l1 l2 l3 )^2
  549. // ( 0 0 0 )
  550. double S = SpinMath.ThreeJ(new SpinState(l1, 0), new SpinState(l2, 0), new SpinState(l3, 0));
  551. Console.WriteLine(" {0} {1}", I, 2.0 * S * S);
  552. if (Math.Abs(S) < TestUtilities.TargetPrecision) {
  553. Assert.IsTrue(I < TestUtilities.TargetPrecision);
  554. } else {
  555. Assert.IsTrue(TestUtilities.IsNearlyEqual(I, 2.0 * S * S));
  556. }
  557. }
  558. }
  559. }
  560. }
  561. [TestMethod]
  562. public void SixJSpecialCase () {
  563. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.SixJ(Spin.SpinZero, Spin.SpinZero, Spin.SpinZero, Spin.SpinZero, Spin.SpinZero, Spin.SpinZero), 1.0));
  564. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.SixJ(Spin.SpinZero, Spin.SpinZero, Spin.SpinZero, Spin.SpinOneHalf, Spin.SpinOneHalf, Spin.SpinOneHalf), - 1.0 / Math.Sqrt(2.0) ));
  565. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.SixJ(Spin.SpinOneHalf, Spin.SpinOneHalf, Spin.SpinZero, Spin.SpinOneHalf, Spin.SpinOneHalf, Spin.SpinZero), - 1.0 / 2.0));
  566. }
  567. [TestMethod]
  568. public void SixJReggeSymmetry () {
  569. SixJSymbol[] symbols = GenerateRandomSixJSymbols(15.0, 15);
  570. foreach (SixJSymbol symbol in symbols) {
  571. Spin sa1 = symbol.J1;
  572. Spin sa2 = symbol.J2;
  573. Spin sa3 = symbol.J3;
  574. Spin sa4 = symbol.J4;
  575. Spin sa5 = symbol.J5;
  576. Spin sa6 = symbol.J6;
  577. Console.WriteLine("{0} {1} {2}", sa1.J, sa2.J, sa3.J);
  578. Console.WriteLine("{0} {1} {2}", sa4.J, sa5.J, sa6.J);
  579. double sa = SpinMath.SixJ(sa1, sa2, sa3, sa4, sa5, sa6);
  580. Spin sb1 = new Spin(sa1.J);
  581. Spin sb2 = new Spin((sa3.J + sa5.J + sa6.J - sa2.J) / 2.0);
  582. Spin sb3 = new Spin((sa2.J + sa5.J + sa6.J - sa3.J) / 2.0);
  583. Spin sb4 = new Spin(sa4.J);
  584. Spin sb5 = new Spin((sa2.J + sa3.J + sa6.J - sa5.J) / 2.0);
  585. Spin sb6 = new Spin((sa2.J + sa3.J + sa5.J - sa6.J) / 2.0);
  586. Console.WriteLine("{0} {1} {2}", sb1.J, sb2.J, sb3.J);
  587. Console.WriteLine("{0} {1} {2}", sb4.J, sb5.J, sb6.J);
  588. double sb = SpinMath.SixJ(sb1, sb2, sb3, sb4, sb5, sb6);
  589. Console.WriteLine("{0} vs. {1}", sa, sb);
  590. Console.WriteLine("---");
  591. Assert.IsTrue(TestUtilities.IsNearlyEqual(sa, sb));
  592. }
  593. }
  594. [TestMethod]
  595. public void SixJOrthonormality () {
  596. SixJSymbol[] sas = GenerateRandomSixJSymbols(50.0, 5);
  597. foreach (SixJSymbol sa in sas) {
  598. Spin j1 = sa.J1;
  599. Spin j2 = sa.J2;
  600. Spin j4 = sa.J4;
  601. Spin j5 = sa.J5;
  602. Spin j6a = sa.J6;
  603. SpinRange r15 = CombinedSpinRange(j1, j5);
  604. SpinRange r42 = CombinedSpinRange(j4, j2);
  605. SpinRange r6b = CombinedSpinRange(r15, r42);
  606. Spin[] j6bs = RandomSpinsInRange(r6b, 5);
  607. j6bs[0] = j6a;
  608. SpinRange r12 = CombinedSpinRange(j1, j2);
  609. SpinRange r45 = CombinedSpinRange(j4, j5);
  610. SpinRange r3 = CombinedSpinRange(r12, r45);
  611. Spin[] j3s = AllSpinsInRange(r3);
  612. foreach (Spin j6b in j6bs) {
  613. double s = 0.0;
  614. foreach (Spin j3 in j3s) {
  615. double t = (2.0 * j3.J + 1.0) *
  616. SpinMath.SixJ(j1, j2, j3, j4, j5, j6a) *
  617. SpinMath.SixJ(j1, j2, j3, j4, j5, j6b);
  618. s += t;
  619. }
  620. Console.WriteLine("j1={0} j2={1} j4={2} j5={3} j6a={4} j6b={5} s={6}", j1.J, j2.J, j4.J, j5.J, j6a.J, j6b.J, s);
  621. if (j6a == j6b) {
  622. Assert.IsTrue(TestUtilities.IsNearlyEqual(s, 1.0 / (2.0 * j6a.J + 1.0)));
  623. } else {
  624. Assert.IsTrue(Math.Abs(s) < TestUtilities.TargetPrecision);
  625. }
  626. }
  627. }
  628. }
  629. // { a b x }
  630. // \sum_x (-1)^{2x} (2x+1) { a b f } = 1
  631. [TestMethod]
  632. public void SixJSum () {
  633. Spin[] spins = GenerateRandomSpins(0.0, 50.0, 5);
  634. foreach (Spin a in spins) {
  635. foreach (Spin b in spins) {
  636. Spin[] combined_spins = GenerateRandomCombinedSpins(a, b, 5);
  637. foreach (Spin f in combined_spins) {
  638. double s = 0.0;
  639. Spin[] xs = GenerateCombinedSpins(a, b);
  640. foreach (Spin x in xs) {
  641. double t = (2 * x.J + 1) * SpinMath.SixJ(a, b, x, a, b, f);
  642. if ((((int) Math.Round(2 * x.J)) % 2) != 0) t = -t;
  643. s += t;
  644. }
  645. Assert.IsTrue(TestUtilities.IsNearlyEqual(s, 1.0));
  646. }
  647. }
  648. }
  649. }
  650. // \sum_{x} (-1)^{f+g+x} (2x+1) { a b x } { c d x } = { a d f }
  651. // { c d f } { b a g } { b c g }
  652. [TestMethod]
  653. public void SixJProductSum () {
  654. SixJSymbol[] symbols = GenerateRandomSixJSymbols(50.0, 5);
  655. foreach (SixJSymbol symbol in symbols) {
  656. Spin a = symbol.J1;
  657. Spin b = symbol.J2;
  658. Spin c = symbol.J4;
  659. Spin d = symbol.J5;
  660. Spin f = symbol.J6;
  661. SpinRange ac = CombinedSpinRange(a, c);
  662. Console.WriteLine("ac: [{0},{1}]", ac.Minimum.J, ac.Maximum.J);
  663. SpinRange bd = CombinedSpinRange(b, d);
  664. Console.WriteLine("bd: [{0},{1}]", bd.Minimum.J, bd.Maximum.J);
  665. SpinRange ac_bd = CombinedSpinRange(ac, bd);
  666. Console.WriteLine("g: [{0},{1}]", ac_bd.Minimum.J, ac_bd.Maximum.J);
  667. Spin[] gs = RandomSpinsInRange(ac_bd, 3);
  668. SpinRange ab = CombinedSpinRange(a, b);
  669. SpinRange cd = CombinedSpinRange(c, d);
  670. SpinRange ab_cd = CombinedSpinRange(ab, cd);
  671. Spin[] xs = AllSpinsInRange(ab_cd);
  672. foreach (Spin g in gs) {
  673. //double s = 0.0;
  674. List<double> s = new List<double>();
  675. foreach (Spin x in xs) {
  676. double t = (2.0 * x.J + 1.0) *
  677. SpinMath.SixJ(a, b, x, c, d, f) *
  678. SpinMath.SixJ(c, d, x, b, a, g);
  679. int p = (int) Math.Round(f.J + g.J + x.J);
  680. if (p % 2 != 0) t = -t;
  681. Console.WriteLine(" {0} {1}", x.J, t);
  682. //s += t;
  683. s.Add(t);
  684. }
  685. double r = SpinMath.SixJ(a, d, f, b, c, g);
  686. Console.WriteLine("a={0} b={1} c={2} d={3} f={4} g={5}: {6} {7}", a.J, b.J, c.J, d.J, f.J, g.J, s, r);
  687. Assert.IsTrue(TestUtilities.IsSumNearlyEqual(s, r));
  688. //Assert.IsTrue(TestUtilities.IsNearlyEqual(s, r));
  689. }
  690. }
  691. }
  692. // ( a b c ) { a b c }
  693. // ( A B C ) { d e f } = \sum_{D, E, F} (-1)^{d + e + f + D + E + F}
  694. //
  695. // ( d e c ) ( e f a ) ( f d b )
  696. // ( D -E C ) ( E -F A ) ( F -D B )
  697. [TestMethod]
  698. public void SixJThreeJRelation () {
  699. SixJSymbol[] symbols = GenerateRandomSixJSymbols(50.0, 5);
  700. foreach (SixJSymbol symbol in symbols) {
  701. Spin a = symbol.J1;
  702. Spin b = symbol.J2;
  703. Spin c = symbol.J3;
  704. Spin d = symbol.J4;
  705. Spin e = symbol.J5;
  706. Spin f = symbol.J6;
  707. SpinState[] ams = GenerateRandomSpinStates(a, 2);
  708. SpinState[] bms = GenerateRandomSpinStates(b, 2);
  709. foreach (SpinState am in ams) {
  710. foreach (SpinState bm in bms) {
  711. if (Math.Abs(am.M + bm.M) > c.J) continue;
  712. SpinState cm = new SpinState(c, -(am.M + bm.M));
  713. double g1 = SpinMath.ThreeJ(am, bm, cm);
  714. double g2 = SpinMath.SixJ(a, b, c, d, e, f);
  715. double p = g1 * g2;
  716. double q = 0.0;
  717. List<double> ts = new List<double>();
  718. SpinState[] dms = d.States();
  719. foreach (SpinState dm in dms) {
  720. if (Math.Abs(dm.M + cm.M) > e.J) continue;
  721. SpinState em = new SpinState(e, dm.M + cm.M);
  722. SpinState mem = new SpinState(e, -em.M);
  723. double f1 = SpinMath.ThreeJ(dm, mem, cm);
  724. if (Math.Abs(em.M + am.M) > f.J) continue;
  725. SpinState fm = new SpinState(f, em.M + am.M);
  726. SpinState mfm = new SpinState(f, -fm.M);
  727. double f2 = SpinMath.ThreeJ(em, mfm, am);
  728. SpinState mdm = new SpinState(d, -dm.M);
  729. double f3 = SpinMath.ThreeJ(fm, mdm, bm);
  730. double t = f1 * f2 * f3;
  731. int s = (int) Math.Round(dm.J + dm.M + em.J + em.M + fm.J + fm.M);
  732. if (s % 2 != 0) t = -t;
  733. q += t;
  734. ts.Add(t);
  735. }
  736. Console.WriteLine("{0} v. {1}", p, q);
  737. //Assert.IsTrue(TestUtilities.IsNearlyEqual(p, q));
  738. Assert.IsTrue(TestUtilities.IsSumNearlyEqual(ts, p));
  739. }
  740. }
  741. }
  742. }
  743. [TestMethod]
  744. public void SixJExchangeSymmetry () {
  745. SixJSymbol[] symbols = GenerateRandomSixJSymbols(50.0, 5);
  746. foreach (SixJSymbol symbol in symbols) {
  747. Spin j1 = symbol.J1;
  748. Spin j2 = symbol.J2;
  749. Spin j3 = symbol.J3;
  750. Spin j4 = symbol.J4;
  751. Spin j5 = symbol.J5;
  752. Spin j6 = symbol.J6;
  753. double s1 = SpinMath.SixJ(j1, j2, j3, j4, j5, j6);
  754. // odd column permutation
  755. double s2 = SpinMath.SixJ(j2, j1, j3, j5, j4, j6);
  756. Assert.IsTrue(TestUtilities.IsNearlyEqual(s1, s2));
  757. // even column permutation
  758. double s3 = SpinMath.SixJ(j2, j3, j1, j5, j6, j4);
  759. Assert.IsTrue(TestUtilities.IsNearlyEqual(s1, s3));
  760. // flip
  761. double s4 = SpinMath.SixJ(j1, j5, j6, j4, j2, j3);
  762. Assert.IsTrue(TestUtilities.IsNearlyEqual(s1, s4));
  763. }
  764. }
  765. [TestMethod]
  766. public void SixJZeroMinimum () {
  767. // we include these special cases in order to exercise code in the recursion routine that is active
  768. // only when the minimum spin of the recursion is zero
  769. Spin s1 = Spin.SpinOne;
  770. Spin s2 = new Spin(2.0);
  771. Spin s3 = new Spin(3.0);
  772. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.SixJ(s2, s2, s2, s1, s2, s2), -1.0 / 10.0));
  773. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.SixJ(s2, s2, s2, s2, s2, s2), -3.0 / 70.0));
  774. Assert.IsTrue(TestUtilities.IsNearlyEqual(SpinMath.SixJ(s2, s2, s2, s3, s2, s2), 4.0 / 35.0));
  775. }
  776. /*
  777. [TestMethod]
  778. public void SixJZeroMinimumTest () {
  779. Spin sa1 = new Spin(1.5);
  780. Spin sa2 = new Spin(1.5);
  781. Spin sa3 = new Spin(3.0);
  782. Spin sa4 = new Spin(3.0);
  783. Spin sa5 = new Spin(3.0);
  784. Spin sa6 = new Spin(1.5);
  785. double c = SpinMath.SixJ_ShultenGorton_Recurse(4, 6, 6, 6, 4, 4);
  786. //double c = SpinMath.SixJ(sa1, sa2, sa3, sa4, sa5, sa6);
  787. Console.WriteLine(c);
  788. }
  789. */
  790. }
  791. }