123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- from sympy.concrete.products import Product
- from sympy.concrete.summations import Sum
- from sympy.core.basic import Basic
- from sympy.core.function import Lambda
- from sympy.core.numbers import (I, pi)
- from sympy.core.singleton import S
- from sympy.core.symbol import Dummy
- from sympy.functions.elementary.complexes import Abs
- from sympy.functions.elementary.exponential import exp
- from sympy.functions.special.gamma_functions import gamma
- from sympy.integrals.integrals import Integral
- from sympy.matrices.expressions.matexpr import MatrixSymbol
- from sympy.matrices.expressions.trace import Trace
- from sympy.tensor.indexed import IndexedBase
- from sympy.core.sympify import _sympify
- from sympy.stats.rv import _symbol_converter, Density, RandomMatrixSymbol, is_random
- from sympy.stats.joint_rv_types import JointDistributionHandmade
- from sympy.stats.random_matrix import RandomMatrixPSpace
- from sympy.tensor.array import ArrayComprehension
- __all__ = [
- 'CircularEnsemble',
- 'CircularUnitaryEnsemble',
- 'CircularOrthogonalEnsemble',
- 'CircularSymplecticEnsemble',
- 'GaussianEnsemble',
- 'GaussianUnitaryEnsemble',
- 'GaussianOrthogonalEnsemble',
- 'GaussianSymplecticEnsemble',
- 'joint_eigen_distribution',
- 'JointEigenDistribution',
- 'level_spacing_distribution'
- ]
- @is_random.register(RandomMatrixSymbol)
- def _(x):
- return True
- class RandomMatrixEnsembleModel(Basic):
- """
- Base class for random matrix ensembles.
- It acts as an umbrella and contains
- the methods common to all the ensembles
- defined in sympy.stats.random_matrix_models.
- """
- def __new__(cls, sym, dim=None):
- sym, dim = _symbol_converter(sym), _sympify(dim)
- if dim.is_integer == False:
- raise ValueError("Dimension of the random matrices must be "
- "integers, received %s instead."%(dim))
- return Basic.__new__(cls, sym, dim)
- symbol = property(lambda self: self.args[0])
- dimension = property(lambda self: self.args[1])
- def density(self, expr):
- return Density(expr)
- def __call__(self, expr):
- return self.density(expr)
- class GaussianEnsembleModel(RandomMatrixEnsembleModel):
- """
- Abstract class for Gaussian ensembles.
- Contains the properties common to all the
- gaussian ensembles.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Random_matrix#Gaussian_ensembles
- .. [2] https://arxiv.org/pdf/1712.07903.pdf
- """
- def _compute_normalization_constant(self, beta, n):
- """
- Helper function for computing normalization
- constant for joint probability density of eigen
- values of Gaussian ensembles.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Selberg_integral#Mehta's_integral
- """
- n = S(n)
- prod_term = lambda j: gamma(1 + beta*S(j)/2)/gamma(S.One + beta/S(2))
- j = Dummy('j', integer=True, positive=True)
- term1 = Product(prod_term(j), (j, 1, n)).doit()
- term2 = (2/(beta*n))**(beta*n*(n - 1)/4 + n/2)
- term3 = (2*pi)**(n/2)
- return term1 * term2 * term3
- def _compute_joint_eigen_distribution(self, beta):
- """
- Helper function for computing the joint
- probability distribution of eigen values
- of the random matrix.
- """
- n = self.dimension
- Zbn = self._compute_normalization_constant(beta, n)
- l = IndexedBase('l')
- i = Dummy('i', integer=True, positive=True)
- j = Dummy('j', integer=True, positive=True)
- k = Dummy('k', integer=True, positive=True)
- term1 = exp((-S(n)/2) * Sum(l[k]**2, (k, 1, n)).doit())
- sub_term = Lambda(i, Product(Abs(l[j] - l[i])**beta, (j, i + 1, n)))
- term2 = Product(sub_term(i).doit(), (i, 1, n - 1)).doit()
- syms = ArrayComprehension(l[k], (k, 1, n)).doit()
- return Lambda(tuple(syms), (term1 * term2)/Zbn)
- class GaussianUnitaryEnsembleModel(GaussianEnsembleModel):
- @property
- def normalization_constant(self):
- n = self.dimension
- return 2**(S(n)/2) * pi**(S(n**2)/2)
- def density(self, expr):
- n, ZGUE = self.dimension, self.normalization_constant
- h_pspace = RandomMatrixPSpace('P', model=self)
- H = RandomMatrixSymbol('H', n, n, pspace=h_pspace)
- return Lambda(H, exp(-S(n)/2 * Trace(H**2))/ZGUE)(expr)
- def joint_eigen_distribution(self):
- return self._compute_joint_eigen_distribution(S(2))
- def level_spacing_distribution(self):
- s = Dummy('s')
- f = (32/pi**2)*(s**2)*exp((-4/pi)*s**2)
- return Lambda(s, f)
- class GaussianOrthogonalEnsembleModel(GaussianEnsembleModel):
- @property
- def normalization_constant(self):
- n = self.dimension
- _H = MatrixSymbol('_H', n, n)
- return Integral(exp(-S(n)/4 * Trace(_H**2)))
- def density(self, expr):
- n, ZGOE = self.dimension, self.normalization_constant
- h_pspace = RandomMatrixPSpace('P', model=self)
- H = RandomMatrixSymbol('H', n, n, pspace=h_pspace)
- return Lambda(H, exp(-S(n)/4 * Trace(H**2))/ZGOE)(expr)
- def joint_eigen_distribution(self):
- return self._compute_joint_eigen_distribution(S.One)
- def level_spacing_distribution(self):
- s = Dummy('s')
- f = (pi/2)*s*exp((-pi/4)*s**2)
- return Lambda(s, f)
- class GaussianSymplecticEnsembleModel(GaussianEnsembleModel):
- @property
- def normalization_constant(self):
- n = self.dimension
- _H = MatrixSymbol('_H', n, n)
- return Integral(exp(-S(n) * Trace(_H**2)))
- def density(self, expr):
- n, ZGSE = self.dimension, self.normalization_constant
- h_pspace = RandomMatrixPSpace('P', model=self)
- H = RandomMatrixSymbol('H', n, n, pspace=h_pspace)
- return Lambda(H, exp(-S(n) * Trace(H**2))/ZGSE)(expr)
- def joint_eigen_distribution(self):
- return self._compute_joint_eigen_distribution(S(4))
- def level_spacing_distribution(self):
- s = Dummy('s')
- f = ((S(2)**18)/((S(3)**6)*(pi**3)))*(s**4)*exp((-64/(9*pi))*s**2)
- return Lambda(s, f)
- def GaussianEnsemble(sym, dim):
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = GaussianEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- def GaussianUnitaryEnsemble(sym, dim):
- """
- Represents Gaussian Unitary Ensembles.
- Examples
- ========
- >>> from sympy.stats import GaussianUnitaryEnsemble as GUE, density
- >>> from sympy import MatrixSymbol
- >>> G = GUE('U', 2)
- >>> X = MatrixSymbol('X', 2, 2)
- >>> density(G)(X)
- exp(-Trace(X**2))/(2*pi**2)
- """
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = GaussianUnitaryEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- def GaussianOrthogonalEnsemble(sym, dim):
- """
- Represents Gaussian Orthogonal Ensembles.
- Examples
- ========
- >>> from sympy.stats import GaussianOrthogonalEnsemble as GOE, density
- >>> from sympy import MatrixSymbol
- >>> G = GOE('U', 2)
- >>> X = MatrixSymbol('X', 2, 2)
- >>> density(G)(X)
- exp(-Trace(X**2)/2)/Integral(exp(-Trace(_H**2)/2), _H)
- """
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = GaussianOrthogonalEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- def GaussianSymplecticEnsemble(sym, dim):
- """
- Represents Gaussian Symplectic Ensembles.
- Examples
- ========
- >>> from sympy.stats import GaussianSymplecticEnsemble as GSE, density
- >>> from sympy import MatrixSymbol
- >>> G = GSE('U', 2)
- >>> X = MatrixSymbol('X', 2, 2)
- >>> density(G)(X)
- exp(-2*Trace(X**2))/Integral(exp(-2*Trace(_H**2)), _H)
- """
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = GaussianSymplecticEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- class CircularEnsembleModel(RandomMatrixEnsembleModel):
- """
- Abstract class for Circular ensembles.
- Contains the properties and methods
- common to all the circular ensembles.
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Circular_ensemble
- """
- def density(self, expr):
- # TODO : Add support for Lie groups(as extensions of sympy.diffgeom)
- # and define measures on them
- raise NotImplementedError("Support for Haar measure hasn't been "
- "implemented yet, therefore the density of "
- "%s cannot be computed."%(self))
- def _compute_joint_eigen_distribution(self, beta):
- """
- Helper function to compute the joint distribution of phases
- of the complex eigen values of matrices belonging to any
- circular ensembles.
- """
- n = self.dimension
- Zbn = ((2*pi)**n)*(gamma(beta*n/2 + 1)/S(gamma(beta/2 + 1))**n)
- t = IndexedBase('t')
- i, j, k = (Dummy('i', integer=True), Dummy('j', integer=True),
- Dummy('k', integer=True))
- syms = ArrayComprehension(t[i], (i, 1, n)).doit()
- f = Product(Product(Abs(exp(I*t[k]) - exp(I*t[j]))**beta, (j, k + 1, n)).doit(),
- (k, 1, n - 1)).doit()
- return Lambda(tuple(syms), f/Zbn)
- class CircularUnitaryEnsembleModel(CircularEnsembleModel):
- def joint_eigen_distribution(self):
- return self._compute_joint_eigen_distribution(S(2))
- class CircularOrthogonalEnsembleModel(CircularEnsembleModel):
- def joint_eigen_distribution(self):
- return self._compute_joint_eigen_distribution(S.One)
- class CircularSymplecticEnsembleModel(CircularEnsembleModel):
- def joint_eigen_distribution(self):
- return self._compute_joint_eigen_distribution(S(4))
- def CircularEnsemble(sym, dim):
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = CircularEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- def CircularUnitaryEnsemble(sym, dim):
- """
- Represents Circular Unitary Ensembles.
- Examples
- ========
- >>> from sympy.stats import CircularUnitaryEnsemble as CUE
- >>> from sympy.stats import joint_eigen_distribution
- >>> C = CUE('U', 1)
- >>> joint_eigen_distribution(C)
- Lambda(t[1], Product(Abs(exp(I*t[_j]) - exp(I*t[_k]))**2, (_j, _k + 1, 1), (_k, 1, 0))/(2*pi))
- Note
- ====
- As can be seen above in the example, density of CiruclarUnitaryEnsemble
- is not evaluated because the exact definition is based on haar measure of
- unitary group which is not unique.
- """
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = CircularUnitaryEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- def CircularOrthogonalEnsemble(sym, dim):
- """
- Represents Circular Orthogonal Ensembles.
- Examples
- ========
- >>> from sympy.stats import CircularOrthogonalEnsemble as COE
- >>> from sympy.stats import joint_eigen_distribution
- >>> C = COE('O', 1)
- >>> joint_eigen_distribution(C)
- Lambda(t[1], Product(Abs(exp(I*t[_j]) - exp(I*t[_k])), (_j, _k + 1, 1), (_k, 1, 0))/(2*pi))
- Note
- ====
- As can be seen above in the example, density of CiruclarOrthogonalEnsemble
- is not evaluated because the exact definition is based on haar measure of
- unitary group which is not unique.
- """
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = CircularOrthogonalEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- def CircularSymplecticEnsemble(sym, dim):
- """
- Represents Circular Symplectic Ensembles.
- Examples
- ========
- >>> from sympy.stats import CircularSymplecticEnsemble as CSE
- >>> from sympy.stats import joint_eigen_distribution
- >>> C = CSE('S', 1)
- >>> joint_eigen_distribution(C)
- Lambda(t[1], Product(Abs(exp(I*t[_j]) - exp(I*t[_k]))**4, (_j, _k + 1, 1), (_k, 1, 0))/(2*pi))
- Note
- ====
- As can be seen above in the example, density of CiruclarSymplecticEnsemble
- is not evaluated because the exact definition is based on haar measure of
- unitary group which is not unique.
- """
- sym, dim = _symbol_converter(sym), _sympify(dim)
- model = CircularSymplecticEnsembleModel(sym, dim)
- rmp = RandomMatrixPSpace(sym, model=model)
- return RandomMatrixSymbol(sym, dim, dim, pspace=rmp)
- def joint_eigen_distribution(mat):
- """
- For obtaining joint probability distribution
- of eigen values of random matrix.
- Parameters
- ==========
- mat: RandomMatrixSymbol
- The matrix symbol whose eigen values are to be considered.
- Returns
- =======
- Lambda
- Examples
- ========
- >>> from sympy.stats import GaussianUnitaryEnsemble as GUE
- >>> from sympy.stats import joint_eigen_distribution
- >>> U = GUE('U', 2)
- >>> joint_eigen_distribution(U)
- 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)
- """
- if not isinstance(mat, RandomMatrixSymbol):
- raise ValueError("%s is not of type, RandomMatrixSymbol."%(mat))
- return mat.pspace.model.joint_eigen_distribution()
- def JointEigenDistribution(mat):
- """
- Creates joint distribution of eigen values of matrices with random
- expressions.
- Parameters
- ==========
- mat: Matrix
- The matrix under consideration.
- Returns
- =======
- JointDistributionHandmade
- Examples
- ========
- >>> from sympy.stats import Normal, JointEigenDistribution
- >>> from sympy import Matrix
- >>> A = [[Normal('A00', 0, 1), Normal('A01', 0, 1)],
- ... [Normal('A10', 0, 1), Normal('A11', 0, 1)]]
- >>> JointEigenDistribution(Matrix(A))
- JointDistributionHandmade(-sqrt(A00**2 - 2*A00*A11 + 4*A01*A10 + A11**2)/2
- + A00/2 + A11/2, sqrt(A00**2 - 2*A00*A11 + 4*A01*A10 + A11**2)/2 + A00/2 + A11/2)
- """
- eigenvals = mat.eigenvals(multiple=True)
- if not all(is_random(eigenval) for eigenval in set(eigenvals)):
- raise ValueError("Eigen values do not have any random expression, "
- "joint distribution cannot be generated.")
- return JointDistributionHandmade(*eigenvals)
- def level_spacing_distribution(mat):
- """
- For obtaining distribution of level spacings.
- Parameters
- ==========
- mat: RandomMatrixSymbol
- The random matrix symbol whose eigen values are
- to be considered for finding the level spacings.
- Returns
- =======
- Lambda
- Examples
- ========
- >>> from sympy.stats import GaussianUnitaryEnsemble as GUE
- >>> from sympy.stats import level_spacing_distribution
- >>> U = GUE('U', 2)
- >>> level_spacing_distribution(U)
- Lambda(_s, 32*_s**2*exp(-4*_s**2/pi)/pi**2)
- References
- ==========
- .. [1] https://en.wikipedia.org/wiki/Random_matrix#Distribution_of_level_spacings
- """
- return mat.pspace.model.level_spacing_distribution()
|