random_matrix_models.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. from sympy.concrete.products import Product
  2. from sympy.concrete.summations import Sum
  3. from sympy.core.basic import Basic
  4. from sympy.core.function import Lambda
  5. from sympy.core.numbers import (I, pi)
  6. from sympy.core.singleton import S
  7. from sympy.core.symbol import Dummy
  8. from sympy.functions.elementary.complexes import Abs
  9. from sympy.functions.elementary.exponential import exp
  10. from sympy.functions.special.gamma_functions import gamma
  11. from sympy.integrals.integrals import Integral
  12. from sympy.matrices.expressions.matexpr import MatrixSymbol
  13. from sympy.matrices.expressions.trace import Trace
  14. from sympy.tensor.indexed import IndexedBase
  15. from sympy.core.sympify import _sympify
  16. from sympy.stats.rv import _symbol_converter, Density, RandomMatrixSymbol, is_random
  17. from sympy.stats.joint_rv_types import JointDistributionHandmade
  18. from sympy.stats.random_matrix import RandomMatrixPSpace
  19. from sympy.tensor.array import ArrayComprehension
  20. __all__ = [
  21. 'CircularEnsemble',
  22. 'CircularUnitaryEnsemble',
  23. 'CircularOrthogonalEnsemble',
  24. 'CircularSymplecticEnsemble',
  25. 'GaussianEnsemble',
  26. 'GaussianUnitaryEnsemble',
  27. 'GaussianOrthogonalEnsemble',
  28. 'GaussianSymplecticEnsemble',
  29. 'joint_eigen_distribution',
  30. 'JointEigenDistribution',
  31. 'level_spacing_distribution'
  32. ]
  33. @is_random.register(RandomMatrixSymbol)
  34. def _(x):
  35. return True
  36. class RandomMatrixEnsembleModel(Basic):
  37. """
  38. Base class for random matrix ensembles.
  39. It acts as an umbrella and contains
  40. the methods common to all the ensembles
  41. defined in sympy.stats.random_matrix_models.
  42. """
  43. def __new__(cls, sym, dim=None):
  44. sym, dim = _symbol_converter(sym), _sympify(dim)
  45. if dim.is_integer == False:
  46. raise ValueError("Dimension of the random matrices must be "
  47. "integers, received %s instead."%(dim))
  48. return Basic.__new__(cls, sym, dim)
  49. symbol = property(lambda self: self.args[0])
  50. dimension = property(lambda self: self.args[1])
  51. def density(self, expr):
  52. return Density(expr)
  53. def __call__(self, expr):
  54. return self.density(expr)
  55. class GaussianEnsembleModel(RandomMatrixEnsembleModel):
  56. """
  57. Abstract class for Gaussian ensembles.
  58. Contains the properties common to all the
  59. gaussian ensembles.
  60. References
  61. ==========
  62. .. [1] https://en.wikipedia.org/wiki/Random_matrix#Gaussian_ensembles
  63. .. [2] https://arxiv.org/pdf/1712.07903.pdf
  64. """
  65. def _compute_normalization_constant(self, beta, n):
  66. """
  67. Helper function for computing normalization
  68. constant for joint probability density of eigen
  69. values of Gaussian ensembles.
  70. References
  71. ==========
  72. .. [1] https://en.wikipedia.org/wiki/Selberg_integral#Mehta's_integral
  73. """
  74. n = S(n)
  75. prod_term = lambda j: gamma(1 + beta*S(j)/2)/gamma(S.One + beta/S(2))
  76. j = Dummy('j', integer=True, positive=True)
  77. term1 = Product(prod_term(j), (j, 1, n)).doit()
  78. term2 = (2/(beta*n))**(beta*n*(n - 1)/4 + n/2)
  79. term3 = (2*pi)**(n/2)
  80. return term1 * term2 * term3
  81. def _compute_joint_eigen_distribution(self, beta):
  82. """
  83. Helper function for computing the joint
  84. probability distribution of eigen values
  85. of the random matrix.
  86. """
  87. n = self.dimension
  88. Zbn = self._compute_normalization_constant(beta, n)
  89. l = IndexedBase('l')
  90. i = Dummy('i', integer=True, positive=True)
  91. j = Dummy('j', integer=True, positive=True)
  92. k = Dummy('k', integer=True, positive=True)
  93. term1 = exp((-S(n)/2) * Sum(l[k]**2, (k, 1, n)).doit())
  94. sub_term = Lambda(i, Product(Abs(l[j] - l[i])**beta, (j, i + 1, n)))
  95. term2 = Product(sub_term(i).doit(), (i, 1, n - 1)).doit()
  96. syms = ArrayComprehension(l[k], (k, 1, n)).doit()
  97. return Lambda(tuple(syms), (term1 * term2)/Zbn)
  98. class GaussianUnitaryEnsembleModel(GaussianEnsembleModel):
  99. @property
  100. def normalization_constant(self):
  101. n = self.dimension
  102. return 2**(S(n)/2) * pi**(S(n**2)/2)
  103. def density(self, expr):
  104. n, ZGUE = self.dimension, self.normalization_constant
  105. h_pspace = RandomMatrixPSpace('P', model=self)
  106. H = RandomMatrixSymbol('H', n, n, pspace=h_pspace)
  107. return Lambda(H, exp(-S(n)/2 * Trace(H**2))/ZGUE)(expr)
  108. def joint_eigen_distribution(self):
  109. return self._compute_joint_eigen_distribution(S(2))
  110. def level_spacing_distribution(self):
  111. s = Dummy('s')
  112. f = (32/pi**2)*(s**2)*exp((-4/pi)*s**2)
  113. return Lambda(s, f)
  114. class GaussianOrthogonalEnsembleModel(GaussianEnsembleModel):
  115. @property
  116. def normalization_constant(self):
  117. n = self.dimension
  118. _H = MatrixSymbol('_H', n, n)
  119. return Integral(exp(-S(n)/4 * Trace(_H**2)))
  120. def density(self, expr):
  121. n, ZGOE = self.dimension, self.normalization_constant
  122. h_pspace = RandomMatrixPSpace('P', model=self)
  123. H = RandomMatrixSymbol('H', n, n, pspace=h_pspace)
  124. return Lambda(H, exp(-S(n)/4 * Trace(H**2))/ZGOE)(expr)
  125. def joint_eigen_distribution(self):
  126. return self._compute_joint_eigen_distribution(S.One)
  127. def level_spacing_distribution(self):
  128. s = Dummy('s')
  129. f = (pi/2)*s*exp((-pi/4)*s**2)
  130. return Lambda(s, f)
  131. class GaussianSymplecticEnsembleModel(GaussianEnsembleModel):
  132. @property
  133. def normalization_constant(self):
  134. n = self.dimension
  135. _H = MatrixSymbol('_H', n, n)
  136. return Integral(exp(-S(n) * Trace(_H**2)))
  137. def density(self, expr):
  138. n, ZGSE = self.dimension, self.normalization_constant
  139. h_pspace = RandomMatrixPSpace('P', model=self)
  140. H = RandomMatrixSymbol('H', n, n, pspace=h_pspace)
  141. return Lambda(H, exp(-S(n) * Trace(H**2))/ZGSE)(expr)
  142. def joint_eigen_distribution(self):
  143. return self._compute_joint_eigen_distribution(S(4))
  144. def level_spacing_distribution(self):
  145. s = Dummy('s')
  146. f = ((S(2)**18)/((S(3)**6)*(pi**3)))*(s**4)*exp((-64/(9*pi))*s**2)
  147. return Lambda(s, f)
  148. def GaussianEnsemble(sym, dim):
  149. sym, dim = _symbol_converter(sym), _sympify(dim)
  150. model = GaussianEnsembleModel(sym, dim)
  151. rmp = RandomMatrixPSpace(sym, model=model)
  152. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  153. def GaussianUnitaryEnsemble(sym, dim):
  154. """
  155. Represents Gaussian Unitary Ensembles.
  156. Examples
  157. ========
  158. >>> from sympy.stats import GaussianUnitaryEnsemble as GUE, density
  159. >>> from sympy import MatrixSymbol
  160. >>> G = GUE('U', 2)
  161. >>> X = MatrixSymbol('X', 2, 2)
  162. >>> density(G)(X)
  163. exp(-Trace(X**2))/(2*pi**2)
  164. """
  165. sym, dim = _symbol_converter(sym), _sympify(dim)
  166. model = GaussianUnitaryEnsembleModel(sym, dim)
  167. rmp = RandomMatrixPSpace(sym, model=model)
  168. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  169. def GaussianOrthogonalEnsemble(sym, dim):
  170. """
  171. Represents Gaussian Orthogonal Ensembles.
  172. Examples
  173. ========
  174. >>> from sympy.stats import GaussianOrthogonalEnsemble as GOE, density
  175. >>> from sympy import MatrixSymbol
  176. >>> G = GOE('U', 2)
  177. >>> X = MatrixSymbol('X', 2, 2)
  178. >>> density(G)(X)
  179. exp(-Trace(X**2)/2)/Integral(exp(-Trace(_H**2)/2), _H)
  180. """
  181. sym, dim = _symbol_converter(sym), _sympify(dim)
  182. model = GaussianOrthogonalEnsembleModel(sym, dim)
  183. rmp = RandomMatrixPSpace(sym, model=model)
  184. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  185. def GaussianSymplecticEnsemble(sym, dim):
  186. """
  187. Represents Gaussian Symplectic Ensembles.
  188. Examples
  189. ========
  190. >>> from sympy.stats import GaussianSymplecticEnsemble as GSE, density
  191. >>> from sympy import MatrixSymbol
  192. >>> G = GSE('U', 2)
  193. >>> X = MatrixSymbol('X', 2, 2)
  194. >>> density(G)(X)
  195. exp(-2*Trace(X**2))/Integral(exp(-2*Trace(_H**2)), _H)
  196. """
  197. sym, dim = _symbol_converter(sym), _sympify(dim)
  198. model = GaussianSymplecticEnsembleModel(sym, dim)
  199. rmp = RandomMatrixPSpace(sym, model=model)
  200. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  201. class CircularEnsembleModel(RandomMatrixEnsembleModel):
  202. """
  203. Abstract class for Circular ensembles.
  204. Contains the properties and methods
  205. common to all the circular ensembles.
  206. References
  207. ==========
  208. .. [1] https://en.wikipedia.org/wiki/Circular_ensemble
  209. """
  210. def density(self, expr):
  211. # TODO : Add support for Lie groups(as extensions of sympy.diffgeom)
  212. # and define measures on them
  213. raise NotImplementedError("Support for Haar measure hasn't been "
  214. "implemented yet, therefore the density of "
  215. "%s cannot be computed."%(self))
  216. def _compute_joint_eigen_distribution(self, beta):
  217. """
  218. Helper function to compute the joint distribution of phases
  219. of the complex eigen values of matrices belonging to any
  220. circular ensembles.
  221. """
  222. n = self.dimension
  223. Zbn = ((2*pi)**n)*(gamma(beta*n/2 + 1)/S(gamma(beta/2 + 1))**n)
  224. t = IndexedBase('t')
  225. i, j, k = (Dummy('i', integer=True), Dummy('j', integer=True),
  226. Dummy('k', integer=True))
  227. syms = ArrayComprehension(t[i], (i, 1, n)).doit()
  228. f = Product(Product(Abs(exp(I*t[k]) - exp(I*t[j]))**beta, (j, k + 1, n)).doit(),
  229. (k, 1, n - 1)).doit()
  230. return Lambda(tuple(syms), f/Zbn)
  231. class CircularUnitaryEnsembleModel(CircularEnsembleModel):
  232. def joint_eigen_distribution(self):
  233. return self._compute_joint_eigen_distribution(S(2))
  234. class CircularOrthogonalEnsembleModel(CircularEnsembleModel):
  235. def joint_eigen_distribution(self):
  236. return self._compute_joint_eigen_distribution(S.One)
  237. class CircularSymplecticEnsembleModel(CircularEnsembleModel):
  238. def joint_eigen_distribution(self):
  239. return self._compute_joint_eigen_distribution(S(4))
  240. def CircularEnsemble(sym, dim):
  241. sym, dim = _symbol_converter(sym), _sympify(dim)
  242. model = CircularEnsembleModel(sym, dim)
  243. rmp = RandomMatrixPSpace(sym, model=model)
  244. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  245. def CircularUnitaryEnsemble(sym, dim):
  246. """
  247. Represents Circular Unitary Ensembles.
  248. Examples
  249. ========
  250. >>> from sympy.stats import CircularUnitaryEnsemble as CUE
  251. >>> from sympy.stats import joint_eigen_distribution
  252. >>> C = CUE('U', 1)
  253. >>> joint_eigen_distribution(C)
  254. Lambda(t[1], Product(Abs(exp(I*t[_j]) - exp(I*t[_k]))**2, (_j, _k + 1, 1), (_k, 1, 0))/(2*pi))
  255. Note
  256. ====
  257. As can be seen above in the example, density of CiruclarUnitaryEnsemble
  258. is not evaluated because the exact definition is based on haar measure of
  259. unitary group which is not unique.
  260. """
  261. sym, dim = _symbol_converter(sym), _sympify(dim)
  262. model = CircularUnitaryEnsembleModel(sym, dim)
  263. rmp = RandomMatrixPSpace(sym, model=model)
  264. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  265. def CircularOrthogonalEnsemble(sym, dim):
  266. """
  267. Represents Circular Orthogonal Ensembles.
  268. Examples
  269. ========
  270. >>> from sympy.stats import CircularOrthogonalEnsemble as COE
  271. >>> from sympy.stats import joint_eigen_distribution
  272. >>> C = COE('O', 1)
  273. >>> joint_eigen_distribution(C)
  274. Lambda(t[1], Product(Abs(exp(I*t[_j]) - exp(I*t[_k])), (_j, _k + 1, 1), (_k, 1, 0))/(2*pi))
  275. Note
  276. ====
  277. As can be seen above in the example, density of CiruclarOrthogonalEnsemble
  278. is not evaluated because the exact definition is based on haar measure of
  279. unitary group which is not unique.
  280. """
  281. sym, dim = _symbol_converter(sym), _sympify(dim)
  282. model = CircularOrthogonalEnsembleModel(sym, dim)
  283. rmp = RandomMatrixPSpace(sym, model=model)
  284. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  285. def CircularSymplecticEnsemble(sym, dim):
  286. """
  287. Represents Circular Symplectic Ensembles.
  288. Examples
  289. ========
  290. >>> from sympy.stats import CircularSymplecticEnsemble as CSE
  291. >>> from sympy.stats import joint_eigen_distribution
  292. >>> C = CSE('S', 1)
  293. >>> joint_eigen_distribution(C)
  294. Lambda(t[1], Product(Abs(exp(I*t[_j]) - exp(I*t[_k]))**4, (_j, _k + 1, 1), (_k, 1, 0))/(2*pi))
  295. Note
  296. ====
  297. As can be seen above in the example, density of CiruclarSymplecticEnsemble
  298. is not evaluated because the exact definition is based on haar measure of
  299. unitary group which is not unique.
  300. """
  301. sym, dim = _symbol_converter(sym), _sympify(dim)
  302. model = CircularSymplecticEnsembleModel(sym, dim)
  303. rmp = RandomMatrixPSpace(sym, model=model)
  304. return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
  305. def joint_eigen_distribution(mat):
  306. """
  307. For obtaining joint probability distribution
  308. of eigen values of random matrix.
  309. Parameters
  310. ==========
  311. mat: RandomMatrixSymbol
  312. The matrix symbol whose eigen values are to be considered.
  313. Returns
  314. =======
  315. Lambda
  316. Examples
  317. ========
  318. >>> from sympy.stats import GaussianUnitaryEnsemble as GUE
  319. >>> from sympy.stats import joint_eigen_distribution
  320. >>> U = GUE('U', 2)
  321. >>> joint_eigen_distribution(U)
  322. Lambda((l[1], l[2]), exp(-l[1]**2 - l[2]**2)*Product(Abs(l[_i] - l[_j])**2, (_j, _i + 1, 2), (_i, 1, 1))/pi)
  323. """
  324. if not isinstance(mat, RandomMatrixSymbol):
  325. raise ValueError("%s is not of type, RandomMatrixSymbol."%(mat))
  326. return mat.pspace.model.joint_eigen_distribution()
  327. def JointEigenDistribution(mat):
  328. """
  329. Creates joint distribution of eigen values of matrices with random
  330. expressions.
  331. Parameters
  332. ==========
  333. mat: Matrix
  334. The matrix under consideration.
  335. Returns
  336. =======
  337. JointDistributionHandmade
  338. Examples
  339. ========
  340. >>> from sympy.stats import Normal, JointEigenDistribution
  341. >>> from sympy import Matrix
  342. >>> A = [[Normal('A00', 0, 1), Normal('A01', 0, 1)],
  343. ... [Normal('A10', 0, 1), Normal('A11', 0, 1)]]
  344. >>> JointEigenDistribution(Matrix(A))
  345. JointDistributionHandmade(-sqrt(A00**2 - 2*A00*A11 + 4*A01*A10 + A11**2)/2
  346. + A00/2 + A11/2, sqrt(A00**2 - 2*A00*A11 + 4*A01*A10 + A11**2)/2 + A00/2 + A11/2)
  347. """
  348. eigenvals = mat.eigenvals(multiple=True)
  349. if not all(is_random(eigenval) for eigenval in set(eigenvals)):
  350. raise ValueError("Eigen values do not have any random expression, "
  351. "joint distribution cannot be generated.")
  352. return JointDistributionHandmade(*eigenvals)
  353. def level_spacing_distribution(mat):
  354. """
  355. For obtaining distribution of level spacings.
  356. Parameters
  357. ==========
  358. mat: RandomMatrixSymbol
  359. The random matrix symbol whose eigen values are
  360. to be considered for finding the level spacings.
  361. Returns
  362. =======
  363. Lambda
  364. Examples
  365. ========
  366. >>> from sympy.stats import GaussianUnitaryEnsemble as GUE
  367. >>> from sympy.stats import level_spacing_distribution
  368. >>> U = GUE('U', 2)
  369. >>> level_spacing_distribution(U)
  370. Lambda(_s, 32*_s**2*exp(-4*_s**2/pi)/pi**2)
  371. References
  372. ==========
  373. .. [1] https://en.wikipedia.org/wiki/Random_matrix#Distribution_of_level_spacings
  374. """
  375. return mat.pspace.model.level_spacing_distribution()