Initialisation du repository de Beta
This commit is contained in:
commit
14985f6dbb
9469 changed files with 1903273 additions and 0 deletions
202
venv/lib/python3.12/site-packages/sympy/stats/__init__.py
Normal file
202
venv/lib/python3.12/site-packages/sympy/stats/__init__.py
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
"""
|
||||
SymPy statistics module
|
||||
|
||||
Introduces a random variable type into the SymPy language.
|
||||
|
||||
Random variables may be declared using prebuilt functions such as
|
||||
Normal, Exponential, Coin, Die, etc... or built with functions like FiniteRV.
|
||||
|
||||
Queries on random expressions can be made using the functions
|
||||
|
||||
========================= =============================
|
||||
Expression Meaning
|
||||
------------------------- -----------------------------
|
||||
``P(condition)`` Probability
|
||||
``E(expression)`` Expected value
|
||||
``H(expression)`` Entropy
|
||||
``variance(expression)`` Variance
|
||||
``density(expression)`` Probability Density Function
|
||||
``sample(expression)`` Produce a realization
|
||||
``where(condition)`` Where the condition is true
|
||||
========================= =============================
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import P, E, variance, Die, Normal
|
||||
>>> from sympy import simplify
|
||||
>>> X, Y = Die('X', 6), Die('Y', 6) # Define two six sided dice
|
||||
>>> Z = Normal('Z', 0, 1) # Declare a Normal random variable with mean 0, std 1
|
||||
>>> P(X>3) # Probability X is greater than 3
|
||||
1/2
|
||||
>>> E(X+Y) # Expectation of the sum of two dice
|
||||
7
|
||||
>>> variance(X+Y) # Variance of the sum of two dice
|
||||
35/6
|
||||
>>> simplify(P(Z>1)) # Probability of Z being greater than 1
|
||||
1/2 - erf(sqrt(2)/2)/2
|
||||
|
||||
|
||||
One could also create custom distribution and define custom random variables
|
||||
as follows:
|
||||
|
||||
1. If you want to create a Continuous Random Variable:
|
||||
|
||||
>>> from sympy.stats import ContinuousRV, P, E
|
||||
>>> from sympy import exp, Symbol, Interval, oo
|
||||
>>> x = Symbol('x')
|
||||
>>> pdf = exp(-x) # pdf of the Continuous Distribution
|
||||
>>> Z = ContinuousRV(x, pdf, set=Interval(0, oo))
|
||||
>>> E(Z)
|
||||
1
|
||||
>>> P(Z > 5)
|
||||
exp(-5)
|
||||
|
||||
1.1 To create an instance of Continuous Distribution:
|
||||
|
||||
>>> from sympy.stats import ContinuousDistributionHandmade
|
||||
>>> from sympy import Lambda
|
||||
>>> dist = ContinuousDistributionHandmade(Lambda(x, pdf), set=Interval(0, oo))
|
||||
>>> dist.pdf(x)
|
||||
exp(-x)
|
||||
|
||||
2. If you want to create a Discrete Random Variable:
|
||||
|
||||
>>> from sympy.stats import DiscreteRV, P, E
|
||||
>>> from sympy import Symbol, S
|
||||
>>> p = S(1)/2
|
||||
>>> x = Symbol('x', integer=True, positive=True)
|
||||
>>> pdf = p*(1 - p)**(x - 1)
|
||||
>>> D = DiscreteRV(x, pdf, set=S.Naturals)
|
||||
>>> E(D)
|
||||
2
|
||||
>>> P(D > 3)
|
||||
1/8
|
||||
|
||||
2.1 To create an instance of Discrete Distribution:
|
||||
|
||||
>>> from sympy.stats import DiscreteDistributionHandmade
|
||||
>>> from sympy import Lambda
|
||||
>>> dist = DiscreteDistributionHandmade(Lambda(x, pdf), set=S.Naturals)
|
||||
>>> dist.pdf(x)
|
||||
2**(1 - x)/2
|
||||
|
||||
3. If you want to create a Finite Random Variable:
|
||||
|
||||
>>> from sympy.stats import FiniteRV, P, E
|
||||
>>> from sympy import Rational, Eq
|
||||
>>> pmf = {1: Rational(1, 3), 2: Rational(1, 6), 3: Rational(1, 4), 4: Rational(1, 4)}
|
||||
>>> X = FiniteRV('X', pmf)
|
||||
>>> E(X)
|
||||
29/12
|
||||
>>> P(X > 3)
|
||||
1/4
|
||||
|
||||
3.1 To create an instance of Finite Distribution:
|
||||
|
||||
>>> from sympy.stats import FiniteDistributionHandmade
|
||||
>>> dist = FiniteDistributionHandmade(pmf)
|
||||
>>> dist.pmf(x)
|
||||
Lambda(x, Piecewise((1/3, Eq(x, 1)), (1/6, Eq(x, 2)), (1/4, Eq(x, 3) | Eq(x, 4)), (0, True)))
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
'P', 'E', 'H', 'density', 'where', 'given', 'sample', 'cdf','median',
|
||||
'characteristic_function', 'pspace', 'sample_iter', 'variance', 'std',
|
||||
'skewness', 'kurtosis', 'covariance', 'dependent', 'entropy', 'independent',
|
||||
'random_symbols', 'correlation', 'factorial_moment', 'moment', 'cmoment',
|
||||
'sampling_density', 'moment_generating_function', 'smoment', 'quantile',
|
||||
'coskewness', 'sample_stochastic_process',
|
||||
|
||||
'FiniteRV', 'DiscreteUniform', 'Die', 'Bernoulli', 'Coin', 'Binomial',
|
||||
'BetaBinomial', 'Hypergeometric', 'Rademacher', 'IdealSoliton', 'RobustSoliton',
|
||||
'FiniteDistributionHandmade',
|
||||
|
||||
'ContinuousRV', 'Arcsin', 'Benini', 'Beta', 'BetaNoncentral', 'BetaPrime',
|
||||
'BoundedPareto', 'Cauchy', 'Chi', 'ChiNoncentral', 'ChiSquared', 'Dagum', 'Davis', 'Erlang',
|
||||
'ExGaussian', 'Exponential', 'ExponentialPower', 'FDistribution',
|
||||
'FisherZ', 'Frechet', 'Gamma', 'GammaInverse', 'Gompertz', 'Gumbel',
|
||||
'Kumaraswamy', 'Laplace', 'Levy', 'Logistic','LogCauchy', 'LogLogistic', 'LogitNormal', 'LogNormal', 'Lomax',
|
||||
'Moyal', 'Maxwell', 'Nakagami', 'Normal', 'GaussianInverse', 'Pareto', 'PowerFunction',
|
||||
'QuadraticU', 'RaisedCosine', 'Rayleigh','Reciprocal', 'StudentT', 'ShiftedGompertz',
|
||||
'Trapezoidal', 'Triangular', 'Uniform', 'UniformSum', 'VonMises', 'Wald',
|
||||
'Weibull', 'WignerSemicircle', 'ContinuousDistributionHandmade',
|
||||
|
||||
'FlorySchulz', 'Geometric','Hermite', 'Logarithmic', 'NegativeBinomial', 'Poisson', 'Skellam',
|
||||
'YuleSimon', 'Zeta', 'DiscreteRV', 'DiscreteDistributionHandmade',
|
||||
|
||||
'JointRV', 'Dirichlet', 'GeneralizedMultivariateLogGamma',
|
||||
'GeneralizedMultivariateLogGammaOmega', 'Multinomial', 'MultivariateBeta',
|
||||
'MultivariateEwens', 'MultivariateT', 'NegativeMultinomial',
|
||||
'NormalGamma', 'MultivariateNormal', 'MultivariateLaplace', 'marginal_distribution',
|
||||
|
||||
'StochasticProcess', 'DiscreteTimeStochasticProcess',
|
||||
'DiscreteMarkovChain', 'TransitionMatrixOf', 'StochasticStateSpaceOf',
|
||||
'GeneratorMatrixOf', 'ContinuousMarkovChain', 'BernoulliProcess',
|
||||
'PoissonProcess', 'WienerProcess', 'GammaProcess',
|
||||
|
||||
'CircularEnsemble', 'CircularUnitaryEnsemble',
|
||||
'CircularOrthogonalEnsemble', 'CircularSymplecticEnsemble',
|
||||
'GaussianEnsemble', 'GaussianUnitaryEnsemble',
|
||||
'GaussianOrthogonalEnsemble', 'GaussianSymplecticEnsemble',
|
||||
'joint_eigen_distribution', 'JointEigenDistribution',
|
||||
'level_spacing_distribution',
|
||||
|
||||
'MatrixGamma', 'Wishart', 'MatrixNormal', 'MatrixStudentT',
|
||||
|
||||
'Probability', 'Expectation', 'Variance', 'Covariance', 'Moment',
|
||||
'CentralMoment',
|
||||
|
||||
'ExpectationMatrix', 'VarianceMatrix', 'CrossCovarianceMatrix'
|
||||
|
||||
]
|
||||
from .rv_interface import (P, E, H, density, where, given, sample, cdf, median,
|
||||
characteristic_function, pspace, sample_iter, variance, std, skewness,
|
||||
kurtosis, covariance, dependent, entropy, independent, random_symbols,
|
||||
correlation, factorial_moment, moment, cmoment, sampling_density,
|
||||
moment_generating_function, smoment, quantile, coskewness,
|
||||
sample_stochastic_process)
|
||||
|
||||
from .frv_types import (FiniteRV, DiscreteUniform, Die, Bernoulli, Coin,
|
||||
Binomial, BetaBinomial, Hypergeometric, Rademacher,
|
||||
FiniteDistributionHandmade, IdealSoliton, RobustSoliton)
|
||||
|
||||
from .crv_types import (ContinuousRV, Arcsin, Benini, Beta, BetaNoncentral,
|
||||
BetaPrime, BoundedPareto, Cauchy, Chi, ChiNoncentral, ChiSquared,
|
||||
Dagum, Davis, Erlang, ExGaussian, Exponential, ExponentialPower,
|
||||
FDistribution, FisherZ, Frechet, Gamma, GammaInverse, GaussianInverse,
|
||||
Gompertz, Gumbel, Kumaraswamy, Laplace, Levy, Logistic, LogCauchy,
|
||||
LogLogistic, LogitNormal, LogNormal, Lomax, Maxwell, Moyal, Nakagami,
|
||||
Normal, Pareto, QuadraticU, RaisedCosine, Rayleigh, Reciprocal,
|
||||
StudentT, PowerFunction, ShiftedGompertz, Trapezoidal, Triangular,
|
||||
Uniform, UniformSum, VonMises, Wald, Weibull, WignerSemicircle,
|
||||
ContinuousDistributionHandmade)
|
||||
|
||||
from .drv_types import (FlorySchulz, Geometric, Hermite, Logarithmic, NegativeBinomial, Poisson,
|
||||
Skellam, YuleSimon, Zeta, DiscreteRV, DiscreteDistributionHandmade)
|
||||
|
||||
from .joint_rv_types import (JointRV, Dirichlet,
|
||||
GeneralizedMultivariateLogGamma, GeneralizedMultivariateLogGammaOmega,
|
||||
Multinomial, MultivariateBeta, MultivariateEwens, MultivariateT,
|
||||
NegativeMultinomial, NormalGamma, MultivariateNormal, MultivariateLaplace,
|
||||
marginal_distribution)
|
||||
|
||||
from .stochastic_process_types import (StochasticProcess,
|
||||
DiscreteTimeStochasticProcess, DiscreteMarkovChain,
|
||||
TransitionMatrixOf, StochasticStateSpaceOf, GeneratorMatrixOf,
|
||||
ContinuousMarkovChain, BernoulliProcess, PoissonProcess, WienerProcess,
|
||||
GammaProcess)
|
||||
|
||||
from .random_matrix_models import (CircularEnsemble, CircularUnitaryEnsemble,
|
||||
CircularOrthogonalEnsemble, CircularSymplecticEnsemble,
|
||||
GaussianEnsemble, GaussianUnitaryEnsemble, GaussianOrthogonalEnsemble,
|
||||
GaussianSymplecticEnsemble, joint_eigen_distribution,
|
||||
JointEigenDistribution, level_spacing_distribution)
|
||||
|
||||
from .matrix_distributions import MatrixGamma, Wishart, MatrixNormal, MatrixStudentT
|
||||
|
||||
from .symbolic_probability import (Probability, Expectation, Variance,
|
||||
Covariance, Moment, CentralMoment)
|
||||
|
||||
from .symbolic_multivariate_probability import (ExpectationMatrix, VarianceMatrix,
|
||||
CrossCovarianceMatrix)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
223
venv/lib/python3.12/site-packages/sympy/stats/compound_rv.py
Normal file
223
venv/lib/python3.12/site-packages/sympy/stats/compound_rv.py
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.stats.rv import (NamedArgsMixin, random_symbols, _symbol_converter,
|
||||
PSpace, RandomSymbol, is_random, Distribution)
|
||||
from sympy.stats.crv import ContinuousDistribution, SingleContinuousPSpace
|
||||
from sympy.stats.drv import DiscreteDistribution, SingleDiscretePSpace
|
||||
from sympy.stats.frv import SingleFiniteDistribution, SingleFinitePSpace
|
||||
from sympy.stats.crv_types import ContinuousDistributionHandmade
|
||||
from sympy.stats.drv_types import DiscreteDistributionHandmade
|
||||
from sympy.stats.frv_types import FiniteDistributionHandmade
|
||||
|
||||
|
||||
class CompoundPSpace(PSpace):
|
||||
"""
|
||||
A temporary Probability Space for the Compound Distribution. After
|
||||
Marginalization, this returns the corresponding Probability Space of the
|
||||
parent distribution.
|
||||
"""
|
||||
|
||||
def __new__(cls, s, distribution):
|
||||
s = _symbol_converter(s)
|
||||
if isinstance(distribution, ContinuousDistribution):
|
||||
return SingleContinuousPSpace(s, distribution)
|
||||
if isinstance(distribution, DiscreteDistribution):
|
||||
return SingleDiscretePSpace(s, distribution)
|
||||
if isinstance(distribution, SingleFiniteDistribution):
|
||||
return SingleFinitePSpace(s, distribution)
|
||||
if not isinstance(distribution, CompoundDistribution):
|
||||
raise ValueError("%s should be an isinstance of "
|
||||
"CompoundDistribution"%(distribution))
|
||||
return Basic.__new__(cls, s, distribution)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return RandomSymbol(self.symbol, self)
|
||||
|
||||
@property
|
||||
def symbol(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def is_Continuous(self):
|
||||
return self.distribution.is_Continuous
|
||||
|
||||
@property
|
||||
def is_Finite(self):
|
||||
return self.distribution.is_Finite
|
||||
|
||||
@property
|
||||
def is_Discrete(self):
|
||||
return self.distribution.is_Discrete
|
||||
|
||||
@property
|
||||
def distribution(self):
|
||||
return self.args[1]
|
||||
|
||||
@property
|
||||
def pdf(self):
|
||||
return self.distribution.pdf(self.symbol)
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.distribution.set
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return self._get_newpspace().domain
|
||||
|
||||
def _get_newpspace(self, evaluate=False):
|
||||
x = Dummy('x')
|
||||
parent_dist = self.distribution.args[0]
|
||||
func = Lambda(x, self.distribution.pdf(x, evaluate))
|
||||
new_pspace = self._transform_pspace(self.symbol, parent_dist, func)
|
||||
if new_pspace is not None:
|
||||
return new_pspace
|
||||
message = ("Compound Distribution for %s is not implemented yet" % str(parent_dist))
|
||||
raise NotImplementedError(message)
|
||||
|
||||
def _transform_pspace(self, sym, dist, pdf):
|
||||
"""
|
||||
This function returns the new pspace of the distribution using handmade
|
||||
Distributions and their corresponding pspace.
|
||||
"""
|
||||
pdf = Lambda(sym, pdf(sym))
|
||||
_set = dist.set
|
||||
if isinstance(dist, ContinuousDistribution):
|
||||
return SingleContinuousPSpace(sym, ContinuousDistributionHandmade(pdf, _set))
|
||||
elif isinstance(dist, DiscreteDistribution):
|
||||
return SingleDiscretePSpace(sym, DiscreteDistributionHandmade(pdf, _set))
|
||||
elif isinstance(dist, SingleFiniteDistribution):
|
||||
dens = {k: pdf(k) for k in _set}
|
||||
return SingleFinitePSpace(sym, FiniteDistributionHandmade(dens))
|
||||
|
||||
def compute_density(self, expr, *, compound_evaluate=True, **kwargs):
|
||||
new_pspace = self._get_newpspace(compound_evaluate)
|
||||
expr = expr.subs({self.value: new_pspace.value})
|
||||
return new_pspace.compute_density(expr, **kwargs)
|
||||
|
||||
def compute_cdf(self, expr, *, compound_evaluate=True, **kwargs):
|
||||
new_pspace = self._get_newpspace(compound_evaluate)
|
||||
expr = expr.subs({self.value: new_pspace.value})
|
||||
return new_pspace.compute_cdf(expr, **kwargs)
|
||||
|
||||
def compute_expectation(self, expr, rvs=None, evaluate=False, **kwargs):
|
||||
new_pspace = self._get_newpspace(evaluate)
|
||||
expr = expr.subs({self.value: new_pspace.value})
|
||||
if rvs:
|
||||
rvs = rvs.subs({self.value: new_pspace.value})
|
||||
if isinstance(new_pspace, SingleFinitePSpace):
|
||||
return new_pspace.compute_expectation(expr, rvs, **kwargs)
|
||||
return new_pspace.compute_expectation(expr, rvs, evaluate, **kwargs)
|
||||
|
||||
def probability(self, condition, *, compound_evaluate=True, **kwargs):
|
||||
new_pspace = self._get_newpspace(compound_evaluate)
|
||||
condition = condition.subs({self.value: new_pspace.value})
|
||||
return new_pspace.probability(condition)
|
||||
|
||||
def conditional_space(self, condition, *, compound_evaluate=True, **kwargs):
|
||||
new_pspace = self._get_newpspace(compound_evaluate)
|
||||
condition = condition.subs({self.value: new_pspace.value})
|
||||
return new_pspace.conditional_space(condition)
|
||||
|
||||
|
||||
class CompoundDistribution(Distribution, NamedArgsMixin):
|
||||
"""
|
||||
Class for Compound Distributions.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
dist : Distribution
|
||||
Distribution must contain a random parameter
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats.compound_rv import CompoundDistribution
|
||||
>>> from sympy.stats.crv_types import NormalDistribution
|
||||
>>> from sympy.stats import Normal
|
||||
>>> from sympy.abc import x
|
||||
>>> X = Normal('X', 2, 4)
|
||||
>>> N = NormalDistribution(X, 4)
|
||||
>>> C = CompoundDistribution(N)
|
||||
>>> C.set
|
||||
Interval(-oo, oo)
|
||||
>>> C.pdf(x, evaluate=True).simplify()
|
||||
exp(-x**2/64 + x/16 - 1/16)/(8*sqrt(pi))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Compound_probability_distribution
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, dist):
|
||||
if not isinstance(dist, (ContinuousDistribution,
|
||||
SingleFiniteDistribution, DiscreteDistribution)):
|
||||
message = "Compound Distribution for %s is not implemented yet" % str(dist)
|
||||
raise NotImplementedError(message)
|
||||
if not cls._compound_check(dist):
|
||||
return dist
|
||||
return Basic.__new__(cls, dist)
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.args[0].set
|
||||
|
||||
@property
|
||||
def is_Continuous(self):
|
||||
return isinstance(self.args[0], ContinuousDistribution)
|
||||
|
||||
@property
|
||||
def is_Finite(self):
|
||||
return isinstance(self.args[0], SingleFiniteDistribution)
|
||||
|
||||
@property
|
||||
def is_Discrete(self):
|
||||
return isinstance(self.args[0], DiscreteDistribution)
|
||||
|
||||
def pdf(self, x, evaluate=False):
|
||||
dist = self.args[0]
|
||||
randoms = [rv for rv in dist.args if is_random(rv)]
|
||||
if isinstance(dist, SingleFiniteDistribution):
|
||||
y = Dummy('y', integer=True, negative=False)
|
||||
expr = dist.pmf(y)
|
||||
else:
|
||||
y = Dummy('y')
|
||||
expr = dist.pdf(y)
|
||||
for rv in randoms:
|
||||
expr = self._marginalise(expr, rv, evaluate)
|
||||
return Lambda(y, expr)(x)
|
||||
|
||||
def _marginalise(self, expr, rv, evaluate):
|
||||
if isinstance(rv.pspace.distribution, SingleFiniteDistribution):
|
||||
rv_dens = rv.pspace.distribution.pmf(rv)
|
||||
else:
|
||||
rv_dens = rv.pspace.distribution.pdf(rv)
|
||||
rv_dom = rv.pspace.domain.set
|
||||
if rv.pspace.is_Discrete or rv.pspace.is_Finite:
|
||||
expr = Sum(expr*rv_dens, (rv, rv_dom._inf,
|
||||
rv_dom._sup))
|
||||
else:
|
||||
expr = Integral(expr*rv_dens, (rv, rv_dom._inf,
|
||||
rv_dom._sup))
|
||||
if evaluate:
|
||||
return expr.doit()
|
||||
return expr
|
||||
|
||||
@classmethod
|
||||
def _compound_check(self, dist):
|
||||
"""
|
||||
Checks if the given distribution contains random parameters.
|
||||
"""
|
||||
randoms = []
|
||||
for arg in dist.args:
|
||||
randoms.extend(random_symbols(arg))
|
||||
if len(randoms) == 0:
|
||||
return False
|
||||
return True
|
||||
570
venv/lib/python3.12/site-packages/sympy/stats/crv.py
Normal file
570
venv/lib/python3.12/site-packages/sympy/stats/crv.py
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
"""
|
||||
Continuous Random Variables Module
|
||||
|
||||
See Also
|
||||
========
|
||||
sympy.stats.crv_types
|
||||
sympy.stats.rv
|
||||
sympy.stats.frv
|
||||
"""
|
||||
|
||||
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.core.function import Lambda, PoleError
|
||||
from sympy.core.numbers import (I, nan, oo)
|
||||
from sympy.core.relational import (Eq, Ne)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, symbols)
|
||||
from sympy.core.sympify import _sympify, sympify
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.delta_functions import DiracDelta
|
||||
from sympy.integrals.integrals import (Integral, integrate)
|
||||
from sympy.logic.boolalg import (And, Or)
|
||||
from sympy.polys.polyerrors import PolynomialError
|
||||
from sympy.polys.polytools import poly
|
||||
from sympy.series.series import series
|
||||
from sympy.sets.sets import (FiniteSet, Intersection, Interval, Union)
|
||||
from sympy.solvers.solveset import solveset
|
||||
from sympy.solvers.inequalities import reduce_rational_inequalities
|
||||
from sympy.stats.rv import (RandomDomain, SingleDomain, ConditionalDomain, is_random,
|
||||
ProductDomain, PSpace, SinglePSpace, random_symbols, NamedArgsMixin, Distribution)
|
||||
|
||||
|
||||
class ContinuousDomain(RandomDomain):
|
||||
"""
|
||||
A domain with continuous support
|
||||
|
||||
Represented using symbols and Intervals.
|
||||
"""
|
||||
is_Continuous = True
|
||||
|
||||
def as_boolean(self):
|
||||
raise NotImplementedError("Not Implemented for generic Domains")
|
||||
|
||||
|
||||
class SingleContinuousDomain(ContinuousDomain, SingleDomain):
|
||||
"""
|
||||
A univariate domain with continuous support
|
||||
|
||||
Represented using a single symbol and interval.
|
||||
"""
|
||||
def compute_expectation(self, expr, variables=None, **kwargs):
|
||||
if variables is None:
|
||||
variables = self.symbols
|
||||
if not variables:
|
||||
return expr
|
||||
if frozenset(variables) != frozenset(self.symbols):
|
||||
raise ValueError("Values should be equal")
|
||||
# assumes only intervals
|
||||
return Integral(expr, (self.symbol, self.set), **kwargs)
|
||||
|
||||
def as_boolean(self):
|
||||
return self.set.as_relational(self.symbol)
|
||||
|
||||
|
||||
class ProductContinuousDomain(ProductDomain, ContinuousDomain):
|
||||
"""
|
||||
A collection of independent domains with continuous support
|
||||
"""
|
||||
|
||||
def compute_expectation(self, expr, variables=None, **kwargs):
|
||||
if variables is None:
|
||||
variables = self.symbols
|
||||
for domain in self.domains:
|
||||
domain_vars = frozenset(variables) & frozenset(domain.symbols)
|
||||
if domain_vars:
|
||||
expr = domain.compute_expectation(expr, domain_vars, **kwargs)
|
||||
return expr
|
||||
|
||||
def as_boolean(self):
|
||||
return And(*[domain.as_boolean() for domain in self.domains])
|
||||
|
||||
|
||||
class ConditionalContinuousDomain(ContinuousDomain, ConditionalDomain):
|
||||
"""
|
||||
A domain with continuous support that has been further restricted by a
|
||||
condition such as $x > 3$.
|
||||
"""
|
||||
|
||||
def compute_expectation(self, expr, variables=None, **kwargs):
|
||||
if variables is None:
|
||||
variables = self.symbols
|
||||
if not variables:
|
||||
return expr
|
||||
# Extract the full integral
|
||||
fullintgrl = self.fulldomain.compute_expectation(expr, variables)
|
||||
# separate into integrand and limits
|
||||
integrand, limits = fullintgrl.function, list(fullintgrl.limits)
|
||||
|
||||
conditions = [self.condition]
|
||||
while conditions:
|
||||
cond = conditions.pop()
|
||||
if cond.is_Boolean:
|
||||
if isinstance(cond, And):
|
||||
conditions.extend(cond.args)
|
||||
elif isinstance(cond, Or):
|
||||
raise NotImplementedError("Or not implemented here")
|
||||
elif cond.is_Relational:
|
||||
if cond.is_Equality:
|
||||
# Add the appropriate Delta to the integrand
|
||||
integrand *= DiracDelta(cond.lhs - cond.rhs)
|
||||
else:
|
||||
symbols = cond.free_symbols & set(self.symbols)
|
||||
if len(symbols) != 1: # Can't handle x > y
|
||||
raise NotImplementedError(
|
||||
"Multivariate Inequalities not yet implemented")
|
||||
# Can handle x > 0
|
||||
symbol = symbols.pop()
|
||||
# Find the limit with x, such as (x, -oo, oo)
|
||||
for i, limit in enumerate(limits):
|
||||
if limit[0] == symbol:
|
||||
# Make condition into an Interval like [0, oo]
|
||||
cintvl = reduce_rational_inequalities_wrap(
|
||||
cond, symbol)
|
||||
# Make limit into an Interval like [-oo, oo]
|
||||
lintvl = Interval(limit[1], limit[2])
|
||||
# Intersect them to get [0, oo]
|
||||
intvl = cintvl.intersect(lintvl)
|
||||
# Put back into limits list
|
||||
limits[i] = (symbol, intvl.left, intvl.right)
|
||||
else:
|
||||
raise TypeError(
|
||||
"Condition %s is not a relational or Boolean" % cond)
|
||||
|
||||
return Integral(integrand, *limits, **kwargs)
|
||||
|
||||
def as_boolean(self):
|
||||
return And(self.fulldomain.as_boolean(), self.condition)
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
if len(self.symbols) == 1:
|
||||
return (self.fulldomain.set & reduce_rational_inequalities_wrap(
|
||||
self.condition, tuple(self.symbols)[0]))
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Set of Conditional Domain not Implemented")
|
||||
|
||||
|
||||
class ContinuousDistribution(Distribution):
|
||||
def __call__(self, *args):
|
||||
return self.pdf(*args)
|
||||
|
||||
|
||||
class SingleContinuousDistribution(ContinuousDistribution, NamedArgsMixin):
|
||||
""" Continuous distribution of a single variable.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Serves as superclass for Normal/Exponential/UniformDistribution etc....
|
||||
|
||||
Represented by parameters for each of the specific classes. E.g
|
||||
NormalDistribution is represented by a mean and standard deviation.
|
||||
|
||||
Provides methods for pdf, cdf, and sampling.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.stats.crv_types.*
|
||||
"""
|
||||
|
||||
set = Interval(-oo, oo)
|
||||
|
||||
def __new__(cls, *args):
|
||||
args = list(map(sympify, args))
|
||||
return Basic.__new__(cls, *args)
|
||||
|
||||
@staticmethod
|
||||
def check(*args):
|
||||
pass
|
||||
|
||||
@cacheit
|
||||
def compute_cdf(self, **kwargs):
|
||||
""" Compute the CDF from the PDF.
|
||||
|
||||
Returns a Lambda.
|
||||
"""
|
||||
x, z = symbols('x, z', real=True, cls=Dummy)
|
||||
left_bound = self.set.start
|
||||
|
||||
# CDF is integral of PDF from left bound to z
|
||||
pdf = self.pdf(x)
|
||||
cdf = integrate(pdf.doit(), (x, left_bound, z), **kwargs)
|
||||
# CDF Ensure that CDF left of left_bound is zero
|
||||
cdf = Piecewise((cdf, z >= left_bound), (0, True))
|
||||
return Lambda(z, cdf)
|
||||
|
||||
def _cdf(self, x):
|
||||
return None
|
||||
|
||||
def cdf(self, x, **kwargs):
|
||||
""" Cumulative density function """
|
||||
if len(kwargs) == 0:
|
||||
cdf = self._cdf(x)
|
||||
if cdf is not None:
|
||||
return cdf
|
||||
return self.compute_cdf(**kwargs)(x)
|
||||
|
||||
@cacheit
|
||||
def compute_characteristic_function(self, **kwargs):
|
||||
""" Compute the characteristic function from the PDF.
|
||||
|
||||
Returns a Lambda.
|
||||
"""
|
||||
x, t = symbols('x, t', real=True, cls=Dummy)
|
||||
pdf = self.pdf(x)
|
||||
cf = integrate(exp(I*t*x)*pdf, (x, self.set))
|
||||
return Lambda(t, cf)
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
return None
|
||||
|
||||
def characteristic_function(self, t, **kwargs):
|
||||
""" Characteristic function """
|
||||
if len(kwargs) == 0:
|
||||
cf = self._characteristic_function(t)
|
||||
if cf is not None:
|
||||
return cf
|
||||
return self.compute_characteristic_function(**kwargs)(t)
|
||||
|
||||
@cacheit
|
||||
def compute_moment_generating_function(self, **kwargs):
|
||||
""" Compute the moment generating function from the PDF.
|
||||
|
||||
Returns a Lambda.
|
||||
"""
|
||||
x, t = symbols('x, t', real=True, cls=Dummy)
|
||||
pdf = self.pdf(x)
|
||||
mgf = integrate(exp(t * x) * pdf, (x, self.set))
|
||||
return Lambda(t, mgf)
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
return None
|
||||
|
||||
def moment_generating_function(self, t, **kwargs):
|
||||
""" Moment generating function """
|
||||
if not kwargs:
|
||||
mgf = self._moment_generating_function(t)
|
||||
if mgf is not None:
|
||||
return mgf
|
||||
return self.compute_moment_generating_function(**kwargs)(t)
|
||||
|
||||
def expectation(self, expr, var, evaluate=True, **kwargs):
|
||||
""" Expectation of expression over distribution """
|
||||
if evaluate:
|
||||
try:
|
||||
p = poly(expr, var)
|
||||
if p.is_zero:
|
||||
return S.Zero
|
||||
t = Dummy('t', real=True)
|
||||
mgf = self._moment_generating_function(t)
|
||||
if mgf is None:
|
||||
return integrate(expr * self.pdf(var), (var, self.set), **kwargs)
|
||||
deg = p.degree()
|
||||
taylor = poly(series(mgf, t, 0, deg + 1).removeO(), t)
|
||||
result = 0
|
||||
for k in range(deg+1):
|
||||
result += p.coeff_monomial(var ** k) * taylor.coeff_monomial(t ** k) * factorial(k)
|
||||
return result
|
||||
except PolynomialError:
|
||||
return integrate(expr * self.pdf(var), (var, self.set), **kwargs)
|
||||
else:
|
||||
return Integral(expr * self.pdf(var), (var, self.set), **kwargs)
|
||||
|
||||
@cacheit
|
||||
def compute_quantile(self, **kwargs):
|
||||
""" Compute the Quantile from the PDF.
|
||||
|
||||
Returns a Lambda.
|
||||
"""
|
||||
x, p = symbols('x, p', real=True, cls=Dummy)
|
||||
left_bound = self.set.start
|
||||
|
||||
pdf = self.pdf(x)
|
||||
cdf = integrate(pdf, (x, left_bound, x), **kwargs)
|
||||
quantile = solveset(cdf - p, x, self.set)
|
||||
return Lambda(p, Piecewise((quantile, (p >= 0) & (p <= 1) ), (nan, True)))
|
||||
|
||||
def _quantile(self, x):
|
||||
return None
|
||||
|
||||
def quantile(self, x, **kwargs):
|
||||
""" Cumulative density function """
|
||||
if len(kwargs) == 0:
|
||||
quantile = self._quantile(x)
|
||||
if quantile is not None:
|
||||
return quantile
|
||||
return self.compute_quantile(**kwargs)(x)
|
||||
|
||||
|
||||
class ContinuousPSpace(PSpace):
|
||||
""" Continuous Probability Space
|
||||
|
||||
Represents the likelihood of an event space defined over a continuum.
|
||||
|
||||
Represented with a ContinuousDomain and a PDF (Lambda-Like)
|
||||
"""
|
||||
|
||||
is_Continuous = True
|
||||
is_real = True
|
||||
|
||||
@property
|
||||
def pdf(self):
|
||||
return self.density(*self.domain.symbols)
|
||||
|
||||
def compute_expectation(self, expr, rvs=None, evaluate=False, **kwargs):
|
||||
if rvs is None:
|
||||
rvs = self.values
|
||||
else:
|
||||
rvs = frozenset(rvs)
|
||||
|
||||
expr = expr.xreplace({rv: rv.symbol for rv in rvs})
|
||||
|
||||
domain_symbols = frozenset(rv.symbol for rv in rvs)
|
||||
|
||||
return self.domain.compute_expectation(self.pdf * expr,
|
||||
domain_symbols, **kwargs)
|
||||
|
||||
def compute_density(self, expr, **kwargs):
|
||||
# Common case Density(X) where X in self.values
|
||||
if expr in self.values:
|
||||
# Marginalize all other random symbols out of the density
|
||||
randomsymbols = tuple(set(self.values) - frozenset([expr]))
|
||||
symbols = tuple(rs.symbol for rs in randomsymbols)
|
||||
pdf = self.domain.compute_expectation(self.pdf, symbols, **kwargs)
|
||||
return Lambda(expr.symbol, pdf)
|
||||
|
||||
z = Dummy('z', real=True)
|
||||
return Lambda(z, self.compute_expectation(DiracDelta(expr - z), **kwargs))
|
||||
|
||||
@cacheit
|
||||
def compute_cdf(self, expr, **kwargs):
|
||||
if not self.domain.set.is_Interval:
|
||||
raise ValueError(
|
||||
"CDF not well defined on multivariate expressions")
|
||||
|
||||
d = self.compute_density(expr, **kwargs)
|
||||
x, z = symbols('x, z', real=True, cls=Dummy)
|
||||
left_bound = self.domain.set.start
|
||||
|
||||
# CDF is integral of PDF from left bound to z
|
||||
cdf = integrate(d(x), (x, left_bound, z), **kwargs)
|
||||
# CDF Ensure that CDF left of left_bound is zero
|
||||
cdf = Piecewise((cdf, z >= left_bound), (0, True))
|
||||
return Lambda(z, cdf)
|
||||
|
||||
@cacheit
|
||||
def compute_characteristic_function(self, expr, **kwargs):
|
||||
if not self.domain.set.is_Interval:
|
||||
raise NotImplementedError("Characteristic function of multivariate expressions not implemented")
|
||||
|
||||
d = self.compute_density(expr, **kwargs)
|
||||
x, t = symbols('x, t', real=True, cls=Dummy)
|
||||
cf = integrate(exp(I*t*x)*d(x), (x, -oo, oo), **kwargs)
|
||||
return Lambda(t, cf)
|
||||
|
||||
@cacheit
|
||||
def compute_moment_generating_function(self, expr, **kwargs):
|
||||
if not self.domain.set.is_Interval:
|
||||
raise NotImplementedError("Moment generating function of multivariate expressions not implemented")
|
||||
|
||||
d = self.compute_density(expr, **kwargs)
|
||||
x, t = symbols('x, t', real=True, cls=Dummy)
|
||||
mgf = integrate(exp(t * x) * d(x), (x, -oo, oo), **kwargs)
|
||||
return Lambda(t, mgf)
|
||||
|
||||
@cacheit
|
||||
def compute_quantile(self, expr, **kwargs):
|
||||
if not self.domain.set.is_Interval:
|
||||
raise ValueError(
|
||||
"Quantile not well defined on multivariate expressions")
|
||||
|
||||
d = self.compute_cdf(expr, **kwargs)
|
||||
x = Dummy('x', real=True)
|
||||
p = Dummy('p', positive=True)
|
||||
|
||||
quantile = solveset(d(x) - p, x, self.set)
|
||||
|
||||
return Lambda(p, quantile)
|
||||
|
||||
def probability(self, condition, **kwargs):
|
||||
z = Dummy('z', real=True)
|
||||
cond_inv = False
|
||||
if isinstance(condition, Ne):
|
||||
condition = Eq(condition.args[0], condition.args[1])
|
||||
cond_inv = True
|
||||
# Univariate case can be handled by where
|
||||
try:
|
||||
domain = self.where(condition)
|
||||
rv = [rv for rv in self.values if rv.symbol == domain.symbol][0]
|
||||
# Integrate out all other random variables
|
||||
pdf = self.compute_density(rv, **kwargs)
|
||||
# return S.Zero if `domain` is empty set
|
||||
if domain.set is S.EmptySet or isinstance(domain.set, FiniteSet):
|
||||
return S.Zero if not cond_inv else S.One
|
||||
if isinstance(domain.set, Union):
|
||||
return sum(
|
||||
Integral(pdf(z), (z, subset), **kwargs) for subset in
|
||||
domain.set.args if isinstance(subset, Interval))
|
||||
# Integrate out the last variable over the special domain
|
||||
return Integral(pdf(z), (z, domain.set), **kwargs)
|
||||
|
||||
# Other cases can be turned into univariate case
|
||||
# by computing a density handled by density computation
|
||||
except NotImplementedError:
|
||||
from sympy.stats.rv import density
|
||||
expr = condition.lhs - condition.rhs
|
||||
if not is_random(expr):
|
||||
dens = self.density
|
||||
comp = condition.rhs
|
||||
else:
|
||||
dens = density(expr, **kwargs)
|
||||
comp = 0
|
||||
if not isinstance(dens, ContinuousDistribution):
|
||||
from sympy.stats.crv_types import ContinuousDistributionHandmade
|
||||
dens = ContinuousDistributionHandmade(dens, set=self.domain.set)
|
||||
# Turn problem into univariate case
|
||||
space = SingleContinuousPSpace(z, dens)
|
||||
result = space.probability(condition.__class__(space.value, comp))
|
||||
return result if not cond_inv else S.One - result
|
||||
|
||||
def where(self, condition):
|
||||
rvs = frozenset(random_symbols(condition))
|
||||
if not (len(rvs) == 1 and rvs.issubset(self.values)):
|
||||
raise NotImplementedError(
|
||||
"Multiple continuous random variables not supported")
|
||||
rv = tuple(rvs)[0]
|
||||
interval = reduce_rational_inequalities_wrap(condition, rv)
|
||||
interval = interval.intersect(self.domain.set)
|
||||
return SingleContinuousDomain(rv.symbol, interval)
|
||||
|
||||
def conditional_space(self, condition, normalize=True, **kwargs):
|
||||
condition = condition.xreplace({rv: rv.symbol for rv in self.values})
|
||||
domain = ConditionalContinuousDomain(self.domain, condition)
|
||||
if normalize:
|
||||
# create a clone of the variable to
|
||||
# make sure that variables in nested integrals are different
|
||||
# from the variables outside the integral
|
||||
# this makes sure that they are evaluated separately
|
||||
# and in the correct order
|
||||
replacement = {rv: Dummy(str(rv)) for rv in self.symbols}
|
||||
norm = domain.compute_expectation(self.pdf, **kwargs)
|
||||
pdf = self.pdf / norm.xreplace(replacement)
|
||||
# XXX: Converting set to tuple. The order matters to Lambda though
|
||||
# so we shouldn't be starting with a set here...
|
||||
density = Lambda(tuple(domain.symbols), pdf)
|
||||
|
||||
return ContinuousPSpace(domain, density)
|
||||
|
||||
|
||||
class SingleContinuousPSpace(ContinuousPSpace, SinglePSpace):
|
||||
"""
|
||||
A continuous probability space over a single univariate variable.
|
||||
|
||||
These consist of a Symbol and a SingleContinuousDistribution
|
||||
|
||||
This class is normally accessed through the various random variable
|
||||
functions, Normal, Exponential, Uniform, etc....
|
||||
"""
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.distribution.set
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return SingleContinuousDomain(sympify(self.symbol), self.set)
|
||||
|
||||
def sample(self, size=(), library='scipy', seed=None):
|
||||
"""
|
||||
Internal sample method.
|
||||
|
||||
Returns dictionary mapping RandomSymbol to realization value.
|
||||
"""
|
||||
return {self.value: self.distribution.sample(size, library=library, seed=seed)}
|
||||
|
||||
def compute_expectation(self, expr, rvs=None, evaluate=False, **kwargs):
|
||||
rvs = rvs or (self.value,)
|
||||
if self.value not in rvs:
|
||||
return expr
|
||||
|
||||
expr = _sympify(expr)
|
||||
expr = expr.xreplace({rv: rv.symbol for rv in rvs})
|
||||
|
||||
x = self.value.symbol
|
||||
try:
|
||||
return self.distribution.expectation(expr, x, evaluate=evaluate, **kwargs)
|
||||
except PoleError:
|
||||
return Integral(expr * self.pdf, (x, self.set), **kwargs)
|
||||
|
||||
def compute_cdf(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
z = Dummy("z", real=True)
|
||||
return Lambda(z, self.distribution.cdf(z, **kwargs))
|
||||
else:
|
||||
return ContinuousPSpace.compute_cdf(self, expr, **kwargs)
|
||||
|
||||
def compute_characteristic_function(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
t = Dummy("t", real=True)
|
||||
return Lambda(t, self.distribution.characteristic_function(t, **kwargs))
|
||||
else:
|
||||
return ContinuousPSpace.compute_characteristic_function(self, expr, **kwargs)
|
||||
|
||||
def compute_moment_generating_function(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
t = Dummy("t", real=True)
|
||||
return Lambda(t, self.distribution.moment_generating_function(t, **kwargs))
|
||||
else:
|
||||
return ContinuousPSpace.compute_moment_generating_function(self, expr, **kwargs)
|
||||
|
||||
def compute_density(self, expr, **kwargs):
|
||||
# https://en.wikipedia.org/wiki/Random_variable#Functions_of_random_variables
|
||||
if expr == self.value:
|
||||
return self.density
|
||||
y = Dummy('y', real=True)
|
||||
|
||||
gs = solveset(expr - y, self.value, S.Reals)
|
||||
|
||||
if isinstance(gs, Intersection):
|
||||
if len(gs.args) == 2 and gs.args[0] is S.Reals:
|
||||
gs = gs.args[1]
|
||||
if not gs.is_FiniteSet:
|
||||
raise ValueError("Can not solve %s for %s" % (expr, self.value))
|
||||
fx = self.compute_density(self.value)
|
||||
fy = sum(fx(g) * abs(g.diff(y)) for g in gs)
|
||||
return Lambda(y, fy)
|
||||
|
||||
def compute_quantile(self, expr, **kwargs):
|
||||
|
||||
if expr == self.value:
|
||||
p = Dummy("p", real=True)
|
||||
return Lambda(p, self.distribution.quantile(p, **kwargs))
|
||||
else:
|
||||
return ContinuousPSpace.compute_quantile(self, expr, **kwargs)
|
||||
|
||||
def _reduce_inequalities(conditions, var, **kwargs):
|
||||
try:
|
||||
return reduce_rational_inequalities(conditions, var, **kwargs)
|
||||
except PolynomialError:
|
||||
raise ValueError("Reduction of condition failed %s\n" % conditions[0])
|
||||
|
||||
|
||||
def reduce_rational_inequalities_wrap(condition, var):
|
||||
if condition.is_Relational:
|
||||
return _reduce_inequalities([[condition]], var, relational=False)
|
||||
if isinstance(condition, Or):
|
||||
return Union(*[_reduce_inequalities([[arg]], var, relational=False)
|
||||
for arg in condition.args])
|
||||
if isinstance(condition, And):
|
||||
intervals = [_reduce_inequalities([[arg]], var, relational=False)
|
||||
for arg in condition.args]
|
||||
I = intervals[0]
|
||||
for i in intervals:
|
||||
I = I.intersect(i)
|
||||
return I
|
||||
4732
venv/lib/python3.12/site-packages/sympy/stats/crv_types.py
Normal file
4732
venv/lib/python3.12/site-packages/sympy/stats/crv_types.py
Normal file
File diff suppressed because it is too large
Load diff
350
venv/lib/python3.12/site-packages/sympy/stats/drv.py
Normal file
350
venv/lib/python3.12/site-packages/sympy/stats/drv.py
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
from sympy.concrete.summations import (Sum, summation)
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.numbers import I
|
||||
from sympy.core.relational import (Eq, Ne)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, symbols)
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.integers import floor
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.logic.boolalg import And
|
||||
from sympy.polys.polytools import poly
|
||||
from sympy.series.series import series
|
||||
|
||||
from sympy.polys.polyerrors import PolynomialError
|
||||
from sympy.stats.crv import reduce_rational_inequalities_wrap
|
||||
from sympy.stats.rv import (NamedArgsMixin, SinglePSpace, SingleDomain,
|
||||
random_symbols, PSpace, ConditionalDomain, RandomDomain,
|
||||
ProductDomain, Distribution)
|
||||
from sympy.stats.symbolic_probability import Probability
|
||||
from sympy.sets.fancysets import Range, FiniteSet
|
||||
from sympy.sets.sets import Union
|
||||
from sympy.sets.contains import Contains
|
||||
from sympy.utilities import filldedent
|
||||
from sympy.core.sympify import _sympify
|
||||
|
||||
|
||||
class DiscreteDistribution(Distribution):
|
||||
def __call__(self, *args):
|
||||
return self.pdf(*args)
|
||||
|
||||
|
||||
class SingleDiscreteDistribution(DiscreteDistribution, NamedArgsMixin):
|
||||
""" Discrete distribution of a single variable.
|
||||
|
||||
Serves as superclass for PoissonDistribution etc....
|
||||
|
||||
Provides methods for pdf, cdf, and sampling
|
||||
|
||||
See Also:
|
||||
sympy.stats.crv_types.*
|
||||
"""
|
||||
|
||||
set = S.Integers
|
||||
|
||||
def __new__(cls, *args):
|
||||
args = list(map(sympify, args))
|
||||
return Basic.__new__(cls, *args)
|
||||
|
||||
@staticmethod
|
||||
def check(*args):
|
||||
pass
|
||||
|
||||
@cacheit
|
||||
def compute_cdf(self, **kwargs):
|
||||
""" Compute the CDF from the PDF.
|
||||
|
||||
Returns a Lambda.
|
||||
"""
|
||||
x = symbols('x', integer=True, cls=Dummy)
|
||||
z = symbols('z', real=True, cls=Dummy)
|
||||
left_bound = self.set.inf
|
||||
|
||||
# CDF is integral of PDF from left bound to z
|
||||
pdf = self.pdf(x)
|
||||
cdf = summation(pdf, (x, left_bound, floor(z)), **kwargs)
|
||||
# CDF Ensure that CDF left of left_bound is zero
|
||||
cdf = Piecewise((cdf, z >= left_bound), (0, True))
|
||||
return Lambda(z, cdf)
|
||||
|
||||
def _cdf(self, x):
|
||||
return None
|
||||
|
||||
def cdf(self, x, **kwargs):
|
||||
""" Cumulative density function """
|
||||
if not kwargs:
|
||||
cdf = self._cdf(x)
|
||||
if cdf is not None:
|
||||
return cdf
|
||||
return self.compute_cdf(**kwargs)(x)
|
||||
|
||||
@cacheit
|
||||
def compute_characteristic_function(self, **kwargs):
|
||||
""" Compute the characteristic function from the PDF.
|
||||
|
||||
Returns a Lambda.
|
||||
"""
|
||||
x, t = symbols('x, t', real=True, cls=Dummy)
|
||||
pdf = self.pdf(x)
|
||||
cf = summation(exp(I*t*x)*pdf, (x, self.set.inf, self.set.sup))
|
||||
return Lambda(t, cf)
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
return None
|
||||
|
||||
def characteristic_function(self, t, **kwargs):
|
||||
""" Characteristic function """
|
||||
if not kwargs:
|
||||
cf = self._characteristic_function(t)
|
||||
if cf is not None:
|
||||
return cf
|
||||
return self.compute_characteristic_function(**kwargs)(t)
|
||||
|
||||
@cacheit
|
||||
def compute_moment_generating_function(self, **kwargs):
|
||||
t = Dummy('t', real=True)
|
||||
x = Dummy('x', integer=True)
|
||||
pdf = self.pdf(x)
|
||||
mgf = summation(exp(t*x)*pdf, (x, self.set.inf, self.set.sup))
|
||||
return Lambda(t, mgf)
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
return None
|
||||
|
||||
def moment_generating_function(self, t, **kwargs):
|
||||
if not kwargs:
|
||||
mgf = self._moment_generating_function(t)
|
||||
if mgf is not None:
|
||||
return mgf
|
||||
return self.compute_moment_generating_function(**kwargs)(t)
|
||||
|
||||
@cacheit
|
||||
def compute_quantile(self, **kwargs):
|
||||
""" Compute the Quantile from the PDF.
|
||||
|
||||
Returns a Lambda.
|
||||
"""
|
||||
x = Dummy('x', integer=True)
|
||||
p = Dummy('p', real=True)
|
||||
left_bound = self.set.inf
|
||||
pdf = self.pdf(x)
|
||||
cdf = summation(pdf, (x, left_bound, x), **kwargs)
|
||||
set = ((x, p <= cdf), )
|
||||
return Lambda(p, Piecewise(*set))
|
||||
|
||||
def _quantile(self, x):
|
||||
return None
|
||||
|
||||
def quantile(self, x, **kwargs):
|
||||
""" Cumulative density function """
|
||||
if not kwargs:
|
||||
quantile = self._quantile(x)
|
||||
if quantile is not None:
|
||||
return quantile
|
||||
return self.compute_quantile(**kwargs)(x)
|
||||
|
||||
def expectation(self, expr, var, evaluate=True, **kwargs):
|
||||
""" Expectation of expression over distribution """
|
||||
# TODO: support discrete sets with non integer stepsizes
|
||||
|
||||
if evaluate:
|
||||
try:
|
||||
p = poly(expr, var)
|
||||
|
||||
t = Dummy('t', real=True)
|
||||
|
||||
mgf = self.moment_generating_function(t)
|
||||
deg = p.degree()
|
||||
taylor = poly(series(mgf, t, 0, deg + 1).removeO(), t)
|
||||
result = 0
|
||||
for k in range(deg+1):
|
||||
result += p.coeff_monomial(var ** k) * taylor.coeff_monomial(t ** k) * factorial(k)
|
||||
|
||||
return result
|
||||
|
||||
except PolynomialError:
|
||||
return summation(expr * self.pdf(var),
|
||||
(var, self.set.inf, self.set.sup), **kwargs)
|
||||
|
||||
else:
|
||||
return Sum(expr * self.pdf(var),
|
||||
(var, self.set.inf, self.set.sup), **kwargs)
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.pdf(*args)
|
||||
|
||||
|
||||
class DiscreteDomain(RandomDomain):
|
||||
"""
|
||||
A domain with discrete support with step size one.
|
||||
Represented using symbols and Range.
|
||||
"""
|
||||
is_Discrete = True
|
||||
|
||||
class SingleDiscreteDomain(DiscreteDomain, SingleDomain):
|
||||
def as_boolean(self):
|
||||
return Contains(self.symbol, self.set)
|
||||
|
||||
|
||||
class ConditionalDiscreteDomain(DiscreteDomain, ConditionalDomain):
|
||||
"""
|
||||
Domain with discrete support of step size one, that is restricted by
|
||||
some condition.
|
||||
"""
|
||||
@property
|
||||
def set(self):
|
||||
rv = self.symbols
|
||||
if len(self.symbols) > 1:
|
||||
raise NotImplementedError(filldedent('''
|
||||
Multivariate conditional domains are not yet implemented.'''))
|
||||
rv = list(rv)[0]
|
||||
return reduce_rational_inequalities_wrap(self.condition,
|
||||
rv).intersect(self.fulldomain.set)
|
||||
|
||||
|
||||
class DiscretePSpace(PSpace):
|
||||
is_real = True
|
||||
is_Discrete = True
|
||||
|
||||
@property
|
||||
def pdf(self):
|
||||
return self.density(*self.symbols)
|
||||
|
||||
def where(self, condition):
|
||||
rvs = random_symbols(condition)
|
||||
assert all(r.symbol in self.symbols for r in rvs)
|
||||
if len(rvs) > 1:
|
||||
raise NotImplementedError(filldedent('''Multivariate discrete
|
||||
random variables are not yet supported.'''))
|
||||
conditional_domain = reduce_rational_inequalities_wrap(condition,
|
||||
rvs[0])
|
||||
conditional_domain = conditional_domain.intersect(self.domain.set)
|
||||
return SingleDiscreteDomain(rvs[0].symbol, conditional_domain)
|
||||
|
||||
def probability(self, condition):
|
||||
complement = isinstance(condition, Ne)
|
||||
if complement:
|
||||
condition = Eq(condition.args[0], condition.args[1])
|
||||
try:
|
||||
_domain = self.where(condition).set
|
||||
if condition == False or _domain is S.EmptySet:
|
||||
return S.Zero
|
||||
if condition == True or _domain == self.domain.set:
|
||||
return S.One
|
||||
prob = self.eval_prob(_domain)
|
||||
except NotImplementedError:
|
||||
from sympy.stats.rv import density
|
||||
expr = condition.lhs - condition.rhs
|
||||
dens = density(expr)
|
||||
if not isinstance(dens, DiscreteDistribution):
|
||||
from sympy.stats.drv_types import DiscreteDistributionHandmade
|
||||
dens = DiscreteDistributionHandmade(dens)
|
||||
z = Dummy('z', real=True)
|
||||
space = SingleDiscretePSpace(z, dens)
|
||||
prob = space.probability(condition.__class__(space.value, 0))
|
||||
if prob is None:
|
||||
prob = Probability(condition)
|
||||
return prob if not complement else S.One - prob
|
||||
|
||||
def eval_prob(self, _domain):
|
||||
sym = list(self.symbols)[0]
|
||||
if isinstance(_domain, Range):
|
||||
n = symbols('n', integer=True)
|
||||
inf, sup, step = (r for r in _domain.args)
|
||||
summand = ((self.pdf).replace(
|
||||
sym, n*step))
|
||||
rv = summation(summand,
|
||||
(n, inf/step, (sup)/step - 1)).doit()
|
||||
return rv
|
||||
elif isinstance(_domain, FiniteSet):
|
||||
pdf = Lambda(sym, self.pdf)
|
||||
rv = sum(pdf(x) for x in _domain)
|
||||
return rv
|
||||
elif isinstance(_domain, Union):
|
||||
rv = sum(self.eval_prob(x) for x in _domain.args)
|
||||
return rv
|
||||
|
||||
def conditional_space(self, condition):
|
||||
# XXX: Converting from set to tuple. The order matters to Lambda
|
||||
# though so we should be starting with a set...
|
||||
density = Lambda(tuple(self.symbols), self.pdf/self.probability(condition))
|
||||
condition = condition.xreplace({rv: rv.symbol for rv in self.values})
|
||||
domain = ConditionalDiscreteDomain(self.domain, condition)
|
||||
return DiscretePSpace(domain, density)
|
||||
|
||||
class ProductDiscreteDomain(ProductDomain, DiscreteDomain):
|
||||
def as_boolean(self):
|
||||
return And(*[domain.as_boolean for domain in self.domains])
|
||||
|
||||
class SingleDiscretePSpace(DiscretePSpace, SinglePSpace):
|
||||
""" Discrete probability space over a single univariate variable """
|
||||
is_real = True
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.distribution.set
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return SingleDiscreteDomain(self.symbol, self.set)
|
||||
|
||||
def sample(self, size=(), library='scipy', seed=None):
|
||||
"""
|
||||
Internal sample method.
|
||||
|
||||
Returns dictionary mapping RandomSymbol to realization value.
|
||||
"""
|
||||
return {self.value: self.distribution.sample(size, library=library, seed=seed)}
|
||||
|
||||
def compute_expectation(self, expr, rvs=None, evaluate=True, **kwargs):
|
||||
rvs = rvs or (self.value,)
|
||||
if self.value not in rvs:
|
||||
return expr
|
||||
|
||||
expr = _sympify(expr)
|
||||
expr = expr.xreplace({rv: rv.symbol for rv in rvs})
|
||||
|
||||
x = self.value.symbol
|
||||
try:
|
||||
return self.distribution.expectation(expr, x, evaluate=evaluate,
|
||||
**kwargs)
|
||||
except NotImplementedError:
|
||||
return Sum(expr * self.pdf, (x, self.set.inf, self.set.sup),
|
||||
**kwargs)
|
||||
|
||||
def compute_cdf(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
x = Dummy("x", real=True)
|
||||
return Lambda(x, self.distribution.cdf(x, **kwargs))
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def compute_density(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
return self.distribution
|
||||
raise NotImplementedError()
|
||||
|
||||
def compute_characteristic_function(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
t = Dummy("t", real=True)
|
||||
return Lambda(t, self.distribution.characteristic_function(t, **kwargs))
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def compute_moment_generating_function(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
t = Dummy("t", real=True)
|
||||
return Lambda(t, self.distribution.moment_generating_function(t, **kwargs))
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def compute_quantile(self, expr, **kwargs):
|
||||
if expr == self.value:
|
||||
p = Dummy("p", real=True)
|
||||
return Lambda(p, self.distribution.quantile(p, **kwargs))
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
849
venv/lib/python3.12/site-packages/sympy/stats/drv_types.py
Normal file
849
venv/lib/python3.12/site-packages/sympy/stats/drv_types.py
Normal file
|
|
@ -0,0 +1,849 @@
|
|||
"""
|
||||
|
||||
Contains
|
||||
========
|
||||
FlorySchulz
|
||||
Geometric
|
||||
Hermite
|
||||
Logarithmic
|
||||
NegativeBinomial
|
||||
Poisson
|
||||
Skellam
|
||||
YuleSimon
|
||||
Zeta
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.numbers import I
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.combinatorial.factorials import (binomial, factorial, FallingFactorial)
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.integers import floor
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.bessel import besseli
|
||||
from sympy.functions.special.beta_functions import beta
|
||||
from sympy.functions.special.hyper import hyper
|
||||
from sympy.functions.special.zeta_functions import (polylog, zeta)
|
||||
from sympy.stats.drv import SingleDiscreteDistribution, SingleDiscretePSpace
|
||||
from sympy.stats.rv import _value_check, is_random
|
||||
|
||||
|
||||
__all__ = ['FlorySchulz',
|
||||
'Geometric',
|
||||
'Hermite',
|
||||
'Logarithmic',
|
||||
'NegativeBinomial',
|
||||
'Poisson',
|
||||
'Skellam',
|
||||
'YuleSimon',
|
||||
'Zeta'
|
||||
]
|
||||
|
||||
|
||||
def rv(symbol, cls, *args, **kwargs):
|
||||
args = list(map(sympify, args))
|
||||
dist = cls(*args)
|
||||
if kwargs.pop('check', True):
|
||||
dist.check(*args)
|
||||
pspace = SingleDiscretePSpace(symbol, dist)
|
||||
if any(is_random(arg) for arg in args):
|
||||
from sympy.stats.compound_rv import CompoundPSpace, CompoundDistribution
|
||||
pspace = CompoundPSpace(symbol, CompoundDistribution(dist))
|
||||
return pspace.value
|
||||
|
||||
|
||||
class DiscreteDistributionHandmade(SingleDiscreteDistribution):
|
||||
_argnames = ('pdf',)
|
||||
|
||||
def __new__(cls, pdf, set=S.Integers):
|
||||
return Basic.__new__(cls, pdf, set)
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.args[1]
|
||||
|
||||
@staticmethod
|
||||
def check(pdf, set):
|
||||
x = Dummy('x')
|
||||
val = Sum(pdf(x), (x, set._inf, set._sup)).doit()
|
||||
_value_check(Eq(val, 1) != S.false, "The pdf is incorrect on the given set.")
|
||||
|
||||
|
||||
|
||||
def DiscreteRV(symbol, density, set=S.Integers, **kwargs):
|
||||
"""
|
||||
Create a Discrete Random Variable given the following:
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
symbol : Symbol
|
||||
Represents name of the random variable.
|
||||
density : Expression containing symbol
|
||||
Represents probability density function.
|
||||
set : set
|
||||
Represents the region where the pdf is valid, by default is real line.
|
||||
check : bool
|
||||
If True, it will check whether the given density
|
||||
integrates to 1 over the given set. If False, it
|
||||
will not perform this check. Default is False.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import DiscreteRV, P, E
|
||||
>>> from sympy import Rational, Symbol
|
||||
>>> x = Symbol('x')
|
||||
>>> n = 10
|
||||
>>> density = Rational(1, 10)
|
||||
>>> X = DiscreteRV(x, density, set=set(range(n)))
|
||||
>>> E(X)
|
||||
9/2
|
||||
>>> P(X>3)
|
||||
3/5
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
"""
|
||||
set = sympify(set)
|
||||
pdf = Piecewise((density, set.as_relational(symbol)), (0, True))
|
||||
pdf = Lambda(symbol, pdf)
|
||||
# have a default of False while `rv` should have a default of True
|
||||
kwargs['check'] = kwargs.pop('check', False)
|
||||
return rv(symbol.name, DiscreteDistributionHandmade, pdf, set, **kwargs)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Flory-Schulz distribution ------------------------------------------------------------
|
||||
|
||||
class FlorySchulzDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('a',)
|
||||
set = S.Naturals
|
||||
|
||||
@staticmethod
|
||||
def check(a):
|
||||
_value_check((0 < a, a < 1), "a must be between 0 and 1")
|
||||
|
||||
def pdf(self, k):
|
||||
a = self.a
|
||||
return (a**2 * k * (1 - a)**(k - 1))
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
a = self.a
|
||||
return a**2*exp(I*t)/((1 + (a - 1)*exp(I*t))**2)
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
a = self.a
|
||||
return a**2*exp(t)/((1 + (a - 1)*exp(t))**2)
|
||||
|
||||
|
||||
def FlorySchulz(name, a):
|
||||
r"""
|
||||
Create a discrete random variable with a FlorySchulz distribution.
|
||||
|
||||
The density of the FlorySchulz distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := (a^2) k (1 - a)^{k-1}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a : A real number between 0 and 1
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, E, variance, FlorySchulz
|
||||
>>> from sympy import Symbol, S
|
||||
|
||||
>>> a = S.One / 5
|
||||
>>> z = Symbol("z")
|
||||
|
||||
>>> X = FlorySchulz("x", a)
|
||||
|
||||
>>> density(X)(z)
|
||||
(4/5)**(z - 1)*z/25
|
||||
|
||||
>>> E(X)
|
||||
9
|
||||
|
||||
>>> variance(X)
|
||||
40
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
https://en.wikipedia.org/wiki/Flory%E2%80%93Schulz_distribution
|
||||
"""
|
||||
return rv(name, FlorySchulzDistribution, a)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Geometric distribution ------------------------------------------------------------
|
||||
|
||||
class GeometricDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('p',)
|
||||
set = S.Naturals
|
||||
|
||||
@staticmethod
|
||||
def check(p):
|
||||
_value_check((0 < p, p <= 1), "p must be between 0 and 1")
|
||||
|
||||
def pdf(self, k):
|
||||
return (1 - self.p)**(k - 1) * self.p
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
p = self.p
|
||||
return p * exp(I*t) / (1 - (1 - p)*exp(I*t))
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
p = self.p
|
||||
return p * exp(t) / (1 - (1 - p) * exp(t))
|
||||
|
||||
|
||||
def Geometric(name, p):
|
||||
r"""
|
||||
Create a discrete random variable with a Geometric distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The density of the Geometric distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := p (1 - p)^{k - 1}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
p : A probability between 0 and 1
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Geometric, density, E, variance
|
||||
>>> from sympy import Symbol, S
|
||||
|
||||
>>> p = S.One / 5
|
||||
>>> z = Symbol("z")
|
||||
|
||||
>>> X = Geometric("x", p)
|
||||
|
||||
>>> density(X)(z)
|
||||
(4/5)**(z - 1)/5
|
||||
|
||||
>>> E(X)
|
||||
5
|
||||
|
||||
>>> variance(X)
|
||||
20
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Geometric_distribution
|
||||
.. [2] https://mathworld.wolfram.com/GeometricDistribution.html
|
||||
|
||||
"""
|
||||
return rv(name, GeometricDistribution, p)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Hermite distribution ---------------------------------------------------------
|
||||
|
||||
|
||||
class HermiteDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('a1', 'a2')
|
||||
set = S.Naturals0
|
||||
|
||||
@staticmethod
|
||||
def check(a1, a2):
|
||||
_value_check(a1.is_nonnegative, 'Parameter a1 must be >= 0.')
|
||||
_value_check(a2.is_nonnegative, 'Parameter a2 must be >= 0.')
|
||||
|
||||
def pdf(self, k):
|
||||
a1, a2 = self.a1, self.a2
|
||||
term1 = exp(-(a1 + a2))
|
||||
j = Dummy("j", integer=True)
|
||||
num = a1**(k - 2*j) * a2**j
|
||||
den = factorial(k - 2*j) * factorial(j)
|
||||
return term1 * Sum(num/den, (j, 0, k//2)).doit()
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
a1, a2 = self.a1, self.a2
|
||||
term1 = a1 * (exp(t) - 1)
|
||||
term2 = a2 * (exp(2*t) - 1)
|
||||
return exp(term1 + term2)
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
a1, a2 = self.a1, self.a2
|
||||
term1 = a1 * (exp(I*t) - 1)
|
||||
term2 = a2 * (exp(2*I*t) - 1)
|
||||
return exp(term1 + term2)
|
||||
|
||||
def Hermite(name, a1, a2):
|
||||
r"""
|
||||
Create a discrete random variable with a Hermite distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The density of the Hermite distribution is given by
|
||||
|
||||
.. math::
|
||||
f(x):= e^{-a_1 -a_2}\sum_{j=0}^{\left \lfloor x/2 \right \rfloor}
|
||||
\frac{a_{1}^{x-2j}a_{2}^{j}}{(x-2j)!j!}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
a1 : A Positive number greater than equal to 0.
|
||||
a2 : A Positive number greater than equal to 0.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Hermite, density, E, variance
|
||||
>>> from sympy import Symbol
|
||||
|
||||
>>> a1 = Symbol("a1", positive=True)
|
||||
>>> a2 = Symbol("a2", positive=True)
|
||||
>>> x = Symbol("x")
|
||||
|
||||
>>> H = Hermite("H", a1=5, a2=4)
|
||||
|
||||
>>> density(H)(2)
|
||||
33*exp(-9)/2
|
||||
|
||||
>>> E(H)
|
||||
13
|
||||
|
||||
>>> variance(H)
|
||||
21
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Hermite_distribution
|
||||
|
||||
"""
|
||||
|
||||
return rv(name, HermiteDistribution, a1, a2)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Logarithmic distribution ------------------------------------------------------------
|
||||
|
||||
class LogarithmicDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('p',)
|
||||
|
||||
set = S.Naturals
|
||||
|
||||
@staticmethod
|
||||
def check(p):
|
||||
_value_check((p > 0, p < 1), "p should be between 0 and 1")
|
||||
|
||||
def pdf(self, k):
|
||||
p = self.p
|
||||
return (-1) * p**k / (k * log(1 - p))
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
p = self.p
|
||||
return log(1 - p * exp(I*t)) / log(1 - p)
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
p = self.p
|
||||
return log(1 - p * exp(t)) / log(1 - p)
|
||||
|
||||
|
||||
def Logarithmic(name, p):
|
||||
r"""
|
||||
Create a discrete random variable with a Logarithmic distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The density of the Logarithmic distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := \frac{-p^k}{k \ln{(1 - p)}}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
p : A value between 0 and 1
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Logarithmic, density, E, variance
|
||||
>>> from sympy import Symbol, S
|
||||
|
||||
>>> p = S.One / 5
|
||||
>>> z = Symbol("z")
|
||||
|
||||
>>> X = Logarithmic("x", p)
|
||||
|
||||
>>> density(X)(z)
|
||||
-1/(5**z*z*log(4/5))
|
||||
|
||||
>>> E(X)
|
||||
-1/(-4*log(5) + 8*log(2))
|
||||
|
||||
>>> variance(X)
|
||||
-1/((-4*log(5) + 8*log(2))*(-2*log(5) + 4*log(2))) + 1/(-64*log(2)*log(5) + 64*log(2)**2 + 16*log(5)**2) - 10/(-32*log(5) + 64*log(2))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Logarithmic_distribution
|
||||
.. [2] https://mathworld.wolfram.com/LogarithmicDistribution.html
|
||||
|
||||
"""
|
||||
return rv(name, LogarithmicDistribution, p)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Negative binomial distribution ------------------------------------------------------------
|
||||
|
||||
class NegativeBinomialDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('r', 'p')
|
||||
set = S.Naturals0
|
||||
|
||||
@staticmethod
|
||||
def check(r, p):
|
||||
_value_check(r > 0, 'r should be positive')
|
||||
_value_check((p > 0, p < 1), 'p should be between 0 and 1')
|
||||
|
||||
def pdf(self, k):
|
||||
r = self.r
|
||||
p = self.p
|
||||
|
||||
return binomial(k + r - 1, k) * (1 - p)**k * p**r
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
r = self.r
|
||||
p = self.p
|
||||
|
||||
return (p / (1 - (1 - p) * exp(I*t)))**r
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
r = self.r
|
||||
p = self.p
|
||||
|
||||
return (p / (1 - (1 - p) * exp(t)))**r
|
||||
|
||||
def NegativeBinomial(name, r, p):
|
||||
r"""
|
||||
Create a discrete random variable with a Negative Binomial distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The density of the Negative Binomial distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := \binom{k + r - 1}{k} (1 - p)^k p^r
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
r : A positive value
|
||||
Number of successes until the experiment is stopped.
|
||||
p : A value between 0 and 1
|
||||
Probability of success.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import NegativeBinomial, density, E, variance
|
||||
>>> from sympy import Symbol, S
|
||||
|
||||
>>> r = 5
|
||||
>>> p = S.One / 3
|
||||
>>> z = Symbol("z")
|
||||
|
||||
>>> X = NegativeBinomial("x", r, p)
|
||||
|
||||
>>> density(X)(z)
|
||||
(2/3)**z*binomial(z + 4, z)/243
|
||||
|
||||
>>> E(X)
|
||||
10
|
||||
|
||||
>>> variance(X)
|
||||
30
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Negative_binomial_distribution
|
||||
.. [2] https://mathworld.wolfram.com/NegativeBinomialDistribution.html
|
||||
|
||||
"""
|
||||
return rv(name, NegativeBinomialDistribution, r, p)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Poisson distribution ------------------------------------------------------------
|
||||
|
||||
class PoissonDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('lamda',)
|
||||
|
||||
set = S.Naturals0
|
||||
|
||||
@staticmethod
|
||||
def check(lamda):
|
||||
_value_check(lamda > 0, "Lambda must be positive")
|
||||
|
||||
def pdf(self, k):
|
||||
return self.lamda**k / factorial(k) * exp(-self.lamda)
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
return exp(self.lamda * (exp(I*t) - 1))
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
return exp(self.lamda * (exp(t) - 1))
|
||||
|
||||
def expectation(self, expr, var, evaluate=True, **kwargs):
|
||||
if evaluate:
|
||||
if expr == var:
|
||||
return self.lamda
|
||||
if (
|
||||
isinstance(expr, FallingFactorial)
|
||||
and expr.args[1].is_integer
|
||||
and expr.args[1].is_positive
|
||||
and expr.args[0] == var
|
||||
):
|
||||
return self.lamda ** expr.args[1]
|
||||
return super().expectation(expr, var, evaluate, **kwargs)
|
||||
|
||||
def Poisson(name, lamda):
|
||||
r"""
|
||||
Create a discrete random variable with a Poisson distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The density of the Poisson distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := \frac{\lambda^{k} e^{- \lambda}}{k!}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
lamda : Positive number, a rate
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Poisson, density, E, variance
|
||||
>>> from sympy import Symbol, simplify
|
||||
|
||||
>>> rate = Symbol("lambda", positive=True)
|
||||
>>> z = Symbol("z")
|
||||
|
||||
>>> X = Poisson("x", rate)
|
||||
|
||||
>>> density(X)(z)
|
||||
lambda**z*exp(-lambda)/factorial(z)
|
||||
|
||||
>>> E(X)
|
||||
lambda
|
||||
|
||||
>>> simplify(variance(X))
|
||||
lambda
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Poisson_distribution
|
||||
.. [2] https://mathworld.wolfram.com/PoissonDistribution.html
|
||||
|
||||
"""
|
||||
return rv(name, PoissonDistribution, lamda)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Skellam distribution --------------------------------------------------------
|
||||
|
||||
|
||||
class SkellamDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('mu1', 'mu2')
|
||||
set = S.Integers
|
||||
|
||||
@staticmethod
|
||||
def check(mu1, mu2):
|
||||
_value_check(mu1 >= 0, 'Parameter mu1 must be >= 0')
|
||||
_value_check(mu2 >= 0, 'Parameter mu2 must be >= 0')
|
||||
|
||||
def pdf(self, k):
|
||||
(mu1, mu2) = (self.mu1, self.mu2)
|
||||
term1 = exp(-(mu1 + mu2)) * (mu1 / mu2) ** (k / 2)
|
||||
term2 = besseli(k, 2 * sqrt(mu1 * mu2))
|
||||
return term1 * term2
|
||||
|
||||
def _cdf(self, x):
|
||||
raise NotImplementedError(
|
||||
"Skellam doesn't have closed form for the CDF.")
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
(mu1, mu2) = (self.mu1, self.mu2)
|
||||
return exp(-(mu1 + mu2) + mu1 * exp(I * t) + mu2 * exp(-I * t))
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
(mu1, mu2) = (self.mu1, self.mu2)
|
||||
return exp(-(mu1 + mu2) + mu1 * exp(t) + mu2 * exp(-t))
|
||||
|
||||
|
||||
def Skellam(name, mu1, mu2):
|
||||
r"""
|
||||
Create a discrete random variable with a Skellam distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The Skellam is the distribution of the difference N1 - N2
|
||||
of two statistically independent random variables N1 and N2
|
||||
each Poisson-distributed with respective expected values mu1 and mu2.
|
||||
|
||||
The density of the Skellam distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := e^{-(\mu_1+\mu_2)}(\frac{\mu_1}{\mu_2})^{k/2}I_k(2\sqrt{\mu_1\mu_2})
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
mu1 : A non-negative value
|
||||
mu2 : A non-negative value
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Skellam, density, E, variance
|
||||
>>> from sympy import Symbol, pprint
|
||||
|
||||
>>> z = Symbol("z", integer=True)
|
||||
>>> mu1 = Symbol("mu1", positive=True)
|
||||
>>> mu2 = Symbol("mu2", positive=True)
|
||||
>>> X = Skellam("x", mu1, mu2)
|
||||
|
||||
>>> pprint(density(X)(z), use_unicode=False)
|
||||
z
|
||||
-
|
||||
2
|
||||
/mu1\ -mu1 - mu2 / _____ _____\
|
||||
|---| *e *besseli\z, 2*\/ mu1 *\/ mu2 /
|
||||
\mu2/
|
||||
>>> E(X)
|
||||
mu1 - mu2
|
||||
>>> variance(X).expand()
|
||||
mu1 + mu2
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Skellam_distribution
|
||||
|
||||
"""
|
||||
return rv(name, SkellamDistribution, mu1, mu2)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Yule-Simon distribution ------------------------------------------------------------
|
||||
|
||||
class YuleSimonDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('rho',)
|
||||
set = S.Naturals
|
||||
|
||||
@staticmethod
|
||||
def check(rho):
|
||||
_value_check(rho > 0, 'rho should be positive')
|
||||
|
||||
def pdf(self, k):
|
||||
rho = self.rho
|
||||
return rho * beta(k, rho + 1)
|
||||
|
||||
def _cdf(self, x):
|
||||
return Piecewise((1 - floor(x) * beta(floor(x), self.rho + 1), x >= 1), (0, True))
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
rho = self.rho
|
||||
return rho * hyper((1, 1), (rho + 2,), exp(I*t)) * exp(I*t) / (rho + 1)
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
rho = self.rho
|
||||
return rho * hyper((1, 1), (rho + 2,), exp(t)) * exp(t) / (rho + 1)
|
||||
|
||||
|
||||
def YuleSimon(name, rho):
|
||||
r"""
|
||||
Create a discrete random variable with a Yule-Simon distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The density of the Yule-Simon distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := \rho B(k, \rho + 1)
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
rho : A positive value
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import YuleSimon, density, E, variance
|
||||
>>> from sympy import Symbol, simplify
|
||||
|
||||
>>> p = 5
|
||||
>>> z = Symbol("z")
|
||||
|
||||
>>> X = YuleSimon("x", p)
|
||||
|
||||
>>> density(X)(z)
|
||||
5*beta(z, 6)
|
||||
|
||||
>>> simplify(E(X))
|
||||
5/4
|
||||
|
||||
>>> simplify(variance(X))
|
||||
25/48
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Yule%E2%80%93Simon_distribution
|
||||
|
||||
"""
|
||||
return rv(name, YuleSimonDistribution, rho)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Zeta distribution ------------------------------------------------------------
|
||||
|
||||
class ZetaDistribution(SingleDiscreteDistribution):
|
||||
_argnames = ('s',)
|
||||
set = S.Naturals
|
||||
|
||||
@staticmethod
|
||||
def check(s):
|
||||
_value_check(s > 1, 's should be greater than 1')
|
||||
|
||||
def pdf(self, k):
|
||||
s = self.s
|
||||
return 1 / (k**s * zeta(s))
|
||||
|
||||
def _characteristic_function(self, t):
|
||||
return polylog(self.s, exp(I*t)) / zeta(self.s)
|
||||
|
||||
def _moment_generating_function(self, t):
|
||||
return polylog(self.s, exp(t)) / zeta(self.s)
|
||||
|
||||
|
||||
def Zeta(name, s):
|
||||
r"""
|
||||
Create a discrete random variable with a Zeta distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The density of the Zeta distribution is given by
|
||||
|
||||
.. math::
|
||||
f(k) := \frac{1}{k^s \zeta{(s)}}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
s : A value greater than 1
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Zeta, density, E, variance
|
||||
>>> from sympy import Symbol
|
||||
|
||||
>>> s = 5
|
||||
>>> z = Symbol("z")
|
||||
|
||||
>>> X = Zeta("x", s)
|
||||
|
||||
>>> density(X)(z)
|
||||
1/(z**5*zeta(5))
|
||||
|
||||
>>> E(X)
|
||||
pi**4/(90*zeta(5))
|
||||
|
||||
>>> variance(X)
|
||||
-pi**8/(8100*zeta(5)**2) + zeta(3)/zeta(5)
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Zeta_distribution
|
||||
|
||||
"""
|
||||
return rv(name, ZetaDistribution, s)
|
||||
100
venv/lib/python3.12/site-packages/sympy/stats/error_prop.py
Normal file
100
venv/lib/python3.12/site-packages/sympy/stats/error_prop.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
"""Tools for arithmetic error propagation."""
|
||||
|
||||
from itertools import repeat, combinations
|
||||
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.stats.symbolic_probability import RandomSymbol, Variance, Covariance
|
||||
from sympy.stats.rv import is_random
|
||||
|
||||
_arg0_or_var = lambda var: var.args[0] if len(var.args) > 0 else var
|
||||
|
||||
|
||||
def variance_prop(expr, consts=(), include_covar=False):
|
||||
r"""Symbolically propagates variance (`\sigma^2`) for expressions.
|
||||
This is computed as as seen in [1]_.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expr : Expr
|
||||
A SymPy expression to compute the variance for.
|
||||
consts : sequence of Symbols, optional
|
||||
Represents symbols that are known constants in the expr,
|
||||
and thus have zero variance. All symbols not in consts are
|
||||
assumed to be variant.
|
||||
include_covar : bool, optional
|
||||
Flag for whether or not to include covariances, default=False.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
var_expr : Expr
|
||||
An expression for the total variance of the expr.
|
||||
The variance for the original symbols (e.g. x) are represented
|
||||
via instance of the Variance symbol (e.g. Variance(x)).
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, exp
|
||||
>>> from sympy.stats.error_prop import variance_prop
|
||||
>>> x, y = symbols('x y')
|
||||
|
||||
>>> variance_prop(x + y)
|
||||
Variance(x) + Variance(y)
|
||||
|
||||
>>> variance_prop(x * y)
|
||||
x**2*Variance(y) + y**2*Variance(x)
|
||||
|
||||
>>> variance_prop(exp(2*x))
|
||||
4*exp(4*x)*Variance(x)
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Propagation_of_uncertainty
|
||||
|
||||
"""
|
||||
args = expr.args
|
||||
if len(args) == 0:
|
||||
if expr in consts:
|
||||
return S.Zero
|
||||
elif is_random(expr):
|
||||
return Variance(expr).doit()
|
||||
elif isinstance(expr, Symbol):
|
||||
return Variance(RandomSymbol(expr)).doit()
|
||||
else:
|
||||
return S.Zero
|
||||
nargs = len(args)
|
||||
var_args = list(map(variance_prop, args, repeat(consts, nargs),
|
||||
repeat(include_covar, nargs)))
|
||||
if isinstance(expr, Add):
|
||||
var_expr = Add(*var_args)
|
||||
if include_covar:
|
||||
terms = [2 * Covariance(_arg0_or_var(x), _arg0_or_var(y)).expand() \
|
||||
for x, y in combinations(var_args, 2)]
|
||||
var_expr += Add(*terms)
|
||||
elif isinstance(expr, Mul):
|
||||
terms = [v/a**2 for a, v in zip(args, var_args)]
|
||||
var_expr = simplify(expr**2 * Add(*terms))
|
||||
if include_covar:
|
||||
terms = [2*Covariance(_arg0_or_var(x), _arg0_or_var(y)).expand()/(a*b) \
|
||||
for (a, b), (x, y) in zip(combinations(args, 2),
|
||||
combinations(var_args, 2))]
|
||||
var_expr += Add(*terms)
|
||||
elif isinstance(expr, Pow):
|
||||
b = args[1]
|
||||
v = var_args[0] * (expr * b / args[0])**2
|
||||
var_expr = simplify(v)
|
||||
elif isinstance(expr, exp):
|
||||
var_expr = simplify(var_args[0] * expr**2)
|
||||
else:
|
||||
# unknown how to proceed, return variance of whole expr.
|
||||
var_expr = Variance(expr)
|
||||
return var_expr
|
||||
512
venv/lib/python3.12/site-packages/sympy/stats/frv.py
Normal file
512
venv/lib/python3.12/site-packages/sympy/stats/frv.py
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
"""
|
||||
Finite Discrete Random Variables Module
|
||||
|
||||
See Also
|
||||
========
|
||||
sympy.stats.frv_types
|
||||
sympy.stats.rv
|
||||
sympy.stats.crv
|
||||
"""
|
||||
from itertools import product
|
||||
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import (I, nan)
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, Symbol)
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.logic.boolalg import (And, Or)
|
||||
from sympy.sets.sets import Intersection
|
||||
from sympy.core.containers import Dict
|
||||
from sympy.core.logic import Logic
|
||||
from sympy.core.relational import Relational
|
||||
from sympy.core.sympify import _sympify
|
||||
from sympy.sets.sets import FiniteSet
|
||||
from sympy.stats.rv import (RandomDomain, ProductDomain, ConditionalDomain,
|
||||
PSpace, IndependentProductPSpace, SinglePSpace, random_symbols,
|
||||
sumsets, rv_subs, NamedArgsMixin, Density, Distribution)
|
||||
|
||||
|
||||
class FiniteDensity(dict):
|
||||
"""
|
||||
A domain with Finite Density.
|
||||
"""
|
||||
def __call__(self, item):
|
||||
"""
|
||||
Make instance of a class callable.
|
||||
|
||||
If item belongs to current instance of a class, return it.
|
||||
|
||||
Otherwise, return 0.
|
||||
"""
|
||||
item = sympify(item)
|
||||
if item in self:
|
||||
return self[item]
|
||||
else:
|
||||
return 0
|
||||
|
||||
@property
|
||||
def dict(self):
|
||||
"""
|
||||
Return item as dictionary.
|
||||
"""
|
||||
return dict(self)
|
||||
|
||||
class FiniteDomain(RandomDomain):
|
||||
"""
|
||||
A domain with discrete finite support
|
||||
|
||||
Represented using a FiniteSet.
|
||||
"""
|
||||
is_Finite = True
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
return FiniteSet(sym for sym, val in self.elements)
|
||||
|
||||
@property
|
||||
def elements(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def dict(self):
|
||||
return FiniteSet(*[Dict(dict(el)) for el in self.elements])
|
||||
|
||||
def __contains__(self, other):
|
||||
return other in self.elements
|
||||
|
||||
def __iter__(self):
|
||||
return self.elements.__iter__()
|
||||
|
||||
def as_boolean(self):
|
||||
return Or(*[And(*[Eq(sym, val) for sym, val in item]) for item in self])
|
||||
|
||||
|
||||
class SingleFiniteDomain(FiniteDomain):
|
||||
"""
|
||||
A FiniteDomain over a single symbol/set
|
||||
|
||||
Example: The possibilities of a *single* die roll.
|
||||
"""
|
||||
|
||||
def __new__(cls, symbol, set):
|
||||
if not isinstance(set, FiniteSet) and \
|
||||
not isinstance(set, Intersection):
|
||||
set = FiniteSet(*set)
|
||||
return Basic.__new__(cls, symbol, set)
|
||||
|
||||
@property
|
||||
def symbol(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
return FiniteSet(self.symbol)
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.args[1]
|
||||
|
||||
@property
|
||||
def elements(self):
|
||||
return FiniteSet(*[frozenset(((self.symbol, elem), )) for elem in self.set])
|
||||
|
||||
def __iter__(self):
|
||||
return (frozenset(((self.symbol, elem),)) for elem in self.set)
|
||||
|
||||
def __contains__(self, other):
|
||||
sym, val = tuple(other)[0]
|
||||
return sym == self.symbol and val in self.set
|
||||
|
||||
|
||||
class ProductFiniteDomain(ProductDomain, FiniteDomain):
|
||||
"""
|
||||
A Finite domain consisting of several other FiniteDomains
|
||||
|
||||
Example: The possibilities of the rolls of three independent dice
|
||||
"""
|
||||
|
||||
def __iter__(self):
|
||||
proditer = product(*self.domains)
|
||||
return (sumsets(items) for items in proditer)
|
||||
|
||||
@property
|
||||
def elements(self):
|
||||
return FiniteSet(*self)
|
||||
|
||||
|
||||
class ConditionalFiniteDomain(ConditionalDomain, ProductFiniteDomain):
|
||||
"""
|
||||
A FiniteDomain that has been restricted by a condition
|
||||
|
||||
Example: The possibilities of a die roll under the condition that the
|
||||
roll is even.
|
||||
"""
|
||||
|
||||
def __new__(cls, domain, condition):
|
||||
"""
|
||||
Create a new instance of ConditionalFiniteDomain class
|
||||
"""
|
||||
if condition is True:
|
||||
return domain
|
||||
cond = rv_subs(condition)
|
||||
return Basic.__new__(cls, domain, cond)
|
||||
|
||||
def _test(self, elem):
|
||||
"""
|
||||
Test the value. If value is boolean, return it. If value is equality
|
||||
relational (two objects are equal), return it with left-hand side
|
||||
being equal to right-hand side. Otherwise, raise ValueError exception.
|
||||
"""
|
||||
val = self.condition.xreplace(dict(elem))
|
||||
if val in [True, False]:
|
||||
return val
|
||||
elif val.is_Equality:
|
||||
return val.lhs == val.rhs
|
||||
raise ValueError("Undecidable if %s" % str(val))
|
||||
|
||||
def __contains__(self, other):
|
||||
return other in self.fulldomain and self._test(other)
|
||||
|
||||
def __iter__(self):
|
||||
return (elem for elem in self.fulldomain if self._test(elem))
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
if isinstance(self.fulldomain, SingleFiniteDomain):
|
||||
return FiniteSet(*[elem for elem in self.fulldomain.set
|
||||
if frozenset(((self.fulldomain.symbol, elem),)) in self])
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Not implemented on multi-dimensional conditional domain")
|
||||
|
||||
def as_boolean(self):
|
||||
return FiniteDomain.as_boolean(self)
|
||||
|
||||
|
||||
class SingleFiniteDistribution(Distribution, NamedArgsMixin):
|
||||
def __new__(cls, *args):
|
||||
args = list(map(sympify, args))
|
||||
return Basic.__new__(cls, *args)
|
||||
|
||||
@staticmethod
|
||||
def check(*args):
|
||||
pass
|
||||
|
||||
@property # type: ignore
|
||||
@cacheit
|
||||
def dict(self):
|
||||
if self.is_symbolic:
|
||||
return Density(self)
|
||||
return {k: self.pmf(k) for k in self.set}
|
||||
|
||||
def pmf(self, *args): # to be overridden by specific distribution
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def set(self): # to be overridden by specific distribution
|
||||
raise NotImplementedError()
|
||||
|
||||
values = property(lambda self: self.dict.values)
|
||||
items = property(lambda self: self.dict.items)
|
||||
is_symbolic = property(lambda self: False)
|
||||
__iter__ = property(lambda self: self.dict.__iter__)
|
||||
__getitem__ = property(lambda self: self.dict.__getitem__)
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.pmf(*args)
|
||||
|
||||
def __contains__(self, other):
|
||||
return other in self.set
|
||||
|
||||
|
||||
#=============================================
|
||||
#========= Probability Space ===============
|
||||
#=============================================
|
||||
|
||||
|
||||
class FinitePSpace(PSpace):
|
||||
"""
|
||||
A Finite Probability Space
|
||||
|
||||
Represents the probabilities of a finite number of events.
|
||||
"""
|
||||
is_Finite = True
|
||||
|
||||
def __new__(cls, domain, density):
|
||||
density = {sympify(key): sympify(val)
|
||||
for key, val in density.items()}
|
||||
public_density = Dict(density)
|
||||
|
||||
obj = PSpace.__new__(cls, domain, public_density)
|
||||
obj._density = density
|
||||
return obj
|
||||
|
||||
def prob_of(self, elem):
|
||||
elem = sympify(elem)
|
||||
density = self._density
|
||||
if isinstance(list(density.keys())[0], FiniteSet):
|
||||
return density.get(elem, S.Zero)
|
||||
return density.get(tuple(elem)[0][1], S.Zero)
|
||||
|
||||
def where(self, condition):
|
||||
assert all(r.symbol in self.symbols for r in random_symbols(condition))
|
||||
return ConditionalFiniteDomain(self.domain, condition)
|
||||
|
||||
def compute_density(self, expr):
|
||||
expr = rv_subs(expr, self.values)
|
||||
d = FiniteDensity()
|
||||
for elem in self.domain:
|
||||
val = expr.xreplace(dict(elem))
|
||||
prob = self.prob_of(elem)
|
||||
d[val] = d.get(val, S.Zero) + prob
|
||||
return d
|
||||
|
||||
@cacheit
|
||||
def compute_cdf(self, expr):
|
||||
d = self.compute_density(expr)
|
||||
cum_prob = S.Zero
|
||||
cdf = []
|
||||
for key in sorted(d):
|
||||
prob = d[key]
|
||||
cum_prob += prob
|
||||
cdf.append((key, cum_prob))
|
||||
|
||||
return dict(cdf)
|
||||
|
||||
@cacheit
|
||||
def sorted_cdf(self, expr, python_float=False):
|
||||
cdf = self.compute_cdf(expr)
|
||||
items = list(cdf.items())
|
||||
sorted_items = sorted(items, key=lambda val_cumprob: val_cumprob[1])
|
||||
if python_float:
|
||||
sorted_items = [(v, float(cum_prob))
|
||||
for v, cum_prob in sorted_items]
|
||||
return sorted_items
|
||||
|
||||
@cacheit
|
||||
def compute_characteristic_function(self, expr):
|
||||
d = self.compute_density(expr)
|
||||
t = Dummy('t', real=True)
|
||||
|
||||
return Lambda(t, sum(exp(I*k*t)*v for k,v in d.items()))
|
||||
|
||||
@cacheit
|
||||
def compute_moment_generating_function(self, expr):
|
||||
d = self.compute_density(expr)
|
||||
t = Dummy('t', real=True)
|
||||
|
||||
return Lambda(t, sum(exp(k*t)*v for k,v in d.items()))
|
||||
|
||||
def compute_expectation(self, expr, rvs=None, **kwargs):
|
||||
rvs = rvs or self.values
|
||||
expr = rv_subs(expr, rvs)
|
||||
probs = [self.prob_of(elem) for elem in self.domain]
|
||||
if isinstance(expr, (Logic, Relational)):
|
||||
parse_domain = [tuple(elem)[0][1] for elem in self.domain]
|
||||
bools = [expr.xreplace(dict(elem)) for elem in self.domain]
|
||||
else:
|
||||
parse_domain = [expr.xreplace(dict(elem)) for elem in self.domain]
|
||||
bools = [True for elem in self.domain]
|
||||
return sum(Piecewise((prob * elem, blv), (S.Zero, True))
|
||||
for prob, elem, blv in zip(probs, parse_domain, bools))
|
||||
|
||||
def compute_quantile(self, expr):
|
||||
cdf = self.compute_cdf(expr)
|
||||
p = Dummy('p', real=True)
|
||||
set = ((nan, (p < 0) | (p > 1)),)
|
||||
for key, value in cdf.items():
|
||||
set = set + ((key, p <= value), )
|
||||
return Lambda(p, Piecewise(*set))
|
||||
|
||||
def probability(self, condition):
|
||||
cond_symbols = frozenset(rs.symbol for rs in random_symbols(condition))
|
||||
cond = rv_subs(condition)
|
||||
if not cond_symbols.issubset(self.symbols):
|
||||
raise ValueError("Cannot compare foreign random symbols, %s"
|
||||
%(str(cond_symbols - self.symbols)))
|
||||
if isinstance(condition, Relational) and \
|
||||
(not cond.free_symbols.issubset(self.domain.free_symbols)):
|
||||
rv = condition.lhs if isinstance(condition.rhs, Symbol) else condition.rhs
|
||||
return sum(Piecewise(
|
||||
(self.prob_of(elem), condition.subs(rv, list(elem)[0][1])),
|
||||
(S.Zero, True)) for elem in self.domain)
|
||||
return sympify(sum(self.prob_of(elem) for elem in self.where(condition)))
|
||||
|
||||
def conditional_space(self, condition):
|
||||
domain = self.where(condition)
|
||||
prob = self.probability(condition)
|
||||
density = {key: val / prob
|
||||
for key, val in self._density.items() if domain._test(key)}
|
||||
return FinitePSpace(domain, density)
|
||||
|
||||
def sample(self, size=(), library='scipy', seed=None):
|
||||
"""
|
||||
Internal sample method
|
||||
|
||||
Returns dictionary mapping RandomSymbol to realization value.
|
||||
"""
|
||||
return {self.value: self.distribution.sample(size, library, seed)}
|
||||
|
||||
|
||||
class SingleFinitePSpace(SinglePSpace, FinitePSpace):
|
||||
"""
|
||||
A single finite probability space
|
||||
|
||||
Represents the probabilities of a set of random events that can be
|
||||
attributed to a single variable/symbol.
|
||||
|
||||
This class is implemented by many of the standard FiniteRV types such as
|
||||
Die, Bernoulli, Coin, etc....
|
||||
"""
|
||||
@property
|
||||
def domain(self):
|
||||
return SingleFiniteDomain(self.symbol, self.distribution.set)
|
||||
|
||||
@property
|
||||
def _is_symbolic(self):
|
||||
"""
|
||||
Helper property to check if the distribution
|
||||
of the random variable is having symbolic
|
||||
dimension.
|
||||
"""
|
||||
return self.distribution.is_symbolic
|
||||
|
||||
@property
|
||||
def distribution(self):
|
||||
return self.args[1]
|
||||
|
||||
def pmf(self, expr):
|
||||
return self.distribution.pmf(expr)
|
||||
|
||||
@property # type: ignore
|
||||
@cacheit
|
||||
def _density(self):
|
||||
return {FiniteSet((self.symbol, val)): prob
|
||||
for val, prob in self.distribution.dict.items()}
|
||||
|
||||
@cacheit
|
||||
def compute_characteristic_function(self, expr):
|
||||
if self._is_symbolic:
|
||||
d = self.compute_density(expr)
|
||||
t = Dummy('t', real=True)
|
||||
ki = Dummy('ki')
|
||||
return Lambda(t, Sum(d(ki)*exp(I*ki*t), (ki, self.args[1].low, self.args[1].high)))
|
||||
expr = rv_subs(expr, self.values)
|
||||
return FinitePSpace(self.domain, self.distribution).compute_characteristic_function(expr)
|
||||
|
||||
@cacheit
|
||||
def compute_moment_generating_function(self, expr):
|
||||
if self._is_symbolic:
|
||||
d = self.compute_density(expr)
|
||||
t = Dummy('t', real=True)
|
||||
ki = Dummy('ki')
|
||||
return Lambda(t, Sum(d(ki)*exp(ki*t), (ki, self.args[1].low, self.args[1].high)))
|
||||
expr = rv_subs(expr, self.values)
|
||||
return FinitePSpace(self.domain, self.distribution).compute_moment_generating_function(expr)
|
||||
|
||||
def compute_quantile(self, expr):
|
||||
if self._is_symbolic:
|
||||
raise NotImplementedError("Computing quantile for random variables "
|
||||
"with symbolic dimension because the bounds of searching the required "
|
||||
"value is undetermined.")
|
||||
expr = rv_subs(expr, self.values)
|
||||
return FinitePSpace(self.domain, self.distribution).compute_quantile(expr)
|
||||
|
||||
def compute_density(self, expr):
|
||||
if self._is_symbolic:
|
||||
rv = list(random_symbols(expr))[0]
|
||||
k = Dummy('k', integer=True)
|
||||
cond = True if not isinstance(expr, (Relational, Logic)) \
|
||||
else expr.subs(rv, k)
|
||||
return Lambda(k,
|
||||
Piecewise((self.pmf(k), And(k >= self.args[1].low,
|
||||
k <= self.args[1].high, cond)), (S.Zero, True)))
|
||||
expr = rv_subs(expr, self.values)
|
||||
return FinitePSpace(self.domain, self.distribution).compute_density(expr)
|
||||
|
||||
def compute_cdf(self, expr):
|
||||
if self._is_symbolic:
|
||||
d = self.compute_density(expr)
|
||||
k = Dummy('k')
|
||||
ki = Dummy('ki')
|
||||
return Lambda(k, Sum(d(ki), (ki, self.args[1].low, k)))
|
||||
expr = rv_subs(expr, self.values)
|
||||
return FinitePSpace(self.domain, self.distribution).compute_cdf(expr)
|
||||
|
||||
def compute_expectation(self, expr, rvs=None, **kwargs):
|
||||
if self._is_symbolic:
|
||||
rv = random_symbols(expr)[0]
|
||||
k = Dummy('k', integer=True)
|
||||
expr = expr.subs(rv, k)
|
||||
cond = True if not isinstance(expr, (Relational, Logic)) \
|
||||
else expr
|
||||
func = self.pmf(k) * k if cond != True else self.pmf(k) * expr
|
||||
return Sum(Piecewise((func, cond), (S.Zero, True)),
|
||||
(k, self.distribution.low, self.distribution.high)).doit()
|
||||
|
||||
expr = _sympify(expr)
|
||||
expr = rv_subs(expr, rvs)
|
||||
return FinitePSpace(self.domain, self.distribution).compute_expectation(expr, rvs, **kwargs)
|
||||
|
||||
def probability(self, condition):
|
||||
if self._is_symbolic:
|
||||
#TODO: Implement the mechanism for handling queries for symbolic sized distributions.
|
||||
raise NotImplementedError("Currently, probability queries are not "
|
||||
"supported for random variables with symbolic sized distributions.")
|
||||
condition = rv_subs(condition)
|
||||
return FinitePSpace(self.domain, self.distribution).probability(condition)
|
||||
|
||||
def conditional_space(self, condition):
|
||||
"""
|
||||
This method is used for transferring the
|
||||
computation to probability method because
|
||||
conditional space of random variables with
|
||||
symbolic dimensions is currently not possible.
|
||||
"""
|
||||
if self._is_symbolic:
|
||||
self
|
||||
domain = self.where(condition)
|
||||
prob = self.probability(condition)
|
||||
density = {key: val / prob
|
||||
for key, val in self._density.items() if domain._test(key)}
|
||||
return FinitePSpace(domain, density)
|
||||
|
||||
|
||||
class ProductFinitePSpace(IndependentProductPSpace, FinitePSpace):
|
||||
"""
|
||||
A collection of several independent finite probability spaces
|
||||
"""
|
||||
@property
|
||||
def domain(self):
|
||||
return ProductFiniteDomain(*[space.domain for space in self.spaces])
|
||||
|
||||
@property # type: ignore
|
||||
@cacheit
|
||||
def _density(self):
|
||||
proditer = product(*[iter(space._density.items())
|
||||
for space in self.spaces])
|
||||
d = {}
|
||||
for items in proditer:
|
||||
elems, probs = list(zip(*items))
|
||||
elem = sumsets(elems)
|
||||
prob = Mul(*probs)
|
||||
d[elem] = d.get(elem, S.Zero) + prob
|
||||
return Dict(d)
|
||||
|
||||
@property # type: ignore
|
||||
@cacheit
|
||||
def density(self):
|
||||
return Dict(self._density)
|
||||
|
||||
def probability(self, condition):
|
||||
return FinitePSpace.probability(self, condition)
|
||||
|
||||
def compute_density(self, expr):
|
||||
return FinitePSpace.compute_density(self, expr)
|
||||
873
venv/lib/python3.12/site-packages/sympy/stats/frv_types.py
Normal file
873
venv/lib/python3.12/site-packages/sympy/stats/frv_types.py
Normal file
|
|
@ -0,0 +1,873 @@
|
|||
"""
|
||||
Finite Discrete Random Variables - Prebuilt variable types
|
||||
|
||||
Contains
|
||||
========
|
||||
FiniteRV
|
||||
DiscreteUniform
|
||||
Die
|
||||
Bernoulli
|
||||
Coin
|
||||
Binomial
|
||||
BetaBinomial
|
||||
Hypergeometric
|
||||
Rademacher
|
||||
IdealSoliton
|
||||
RobustSoliton
|
||||
"""
|
||||
|
||||
|
||||
from sympy.core.cache import cacheit
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.numbers import (Integer, Rational)
|
||||
from sympy.core.relational import (Eq, Ge, Gt, Le, Lt)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, Symbol)
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.combinatorial.factorials import binomial
|
||||
from sympy.functions.elementary.exponential import log
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.logic.boolalg import Or
|
||||
from sympy.sets.contains import Contains
|
||||
from sympy.sets.fancysets import Range
|
||||
from sympy.sets.sets import (Intersection, Interval)
|
||||
from sympy.functions.special.beta_functions import beta as beta_fn
|
||||
from sympy.stats.frv import (SingleFiniteDistribution,
|
||||
SingleFinitePSpace)
|
||||
from sympy.stats.rv import _value_check, Density, is_random
|
||||
from sympy.utilities.iterables import multiset
|
||||
from sympy.utilities.misc import filldedent
|
||||
|
||||
|
||||
__all__ = ['FiniteRV',
|
||||
'DiscreteUniform',
|
||||
'Die',
|
||||
'Bernoulli',
|
||||
'Coin',
|
||||
'Binomial',
|
||||
'BetaBinomial',
|
||||
'Hypergeometric',
|
||||
'Rademacher',
|
||||
'IdealSoliton',
|
||||
'RobustSoliton',
|
||||
]
|
||||
|
||||
def rv(name, cls, *args, **kwargs):
|
||||
args = list(map(sympify, args))
|
||||
dist = cls(*args)
|
||||
if kwargs.pop('check', True):
|
||||
dist.check(*args)
|
||||
pspace = SingleFinitePSpace(name, dist)
|
||||
if any(is_random(arg) for arg in args):
|
||||
from sympy.stats.compound_rv import CompoundPSpace, CompoundDistribution
|
||||
pspace = CompoundPSpace(name, CompoundDistribution(dist))
|
||||
return pspace.value
|
||||
|
||||
class FiniteDistributionHandmade(SingleFiniteDistribution):
|
||||
|
||||
@property
|
||||
def dict(self):
|
||||
return self.args[0]
|
||||
|
||||
def pmf(self, x):
|
||||
x = Symbol('x')
|
||||
return Lambda(x, Piecewise(*(
|
||||
[(v, Eq(k, x)) for k, v in self.dict.items()] + [(S.Zero, True)])))
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return set(self.dict.keys())
|
||||
|
||||
@staticmethod
|
||||
def check(density):
|
||||
for p in density.values():
|
||||
_value_check((p >= 0, p <= 1),
|
||||
"Probability at a point must be between 0 and 1.")
|
||||
val = sum(density.values())
|
||||
_value_check(Eq(val, 1) != S.false, "Total Probability must be 1.")
|
||||
|
||||
def FiniteRV(name, density, **kwargs):
|
||||
r"""
|
||||
Create a Finite Random Variable given a dict representing the density.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
name : Symbol
|
||||
Represents name of the random variable.
|
||||
density : dict
|
||||
Dictionary containing the pdf of finite distribution
|
||||
check : bool
|
||||
If True, it will check whether the given density
|
||||
integrates to 1 over the given set. If False, it
|
||||
will not perform this check. Default is False.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import FiniteRV, P, E
|
||||
|
||||
>>> density = {0: .1, 1: .2, 2: .3, 3: .4}
|
||||
>>> X = FiniteRV('X', density)
|
||||
|
||||
>>> E(X)
|
||||
2.00000000000000
|
||||
>>> P(X >= 2)
|
||||
0.700000000000000
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
"""
|
||||
# have a default of False while `rv` should have a default of True
|
||||
kwargs['check'] = kwargs.pop('check', False)
|
||||
return rv(name, FiniteDistributionHandmade, density, **kwargs)
|
||||
|
||||
class DiscreteUniformDistribution(SingleFiniteDistribution):
|
||||
|
||||
@staticmethod
|
||||
def check(*args):
|
||||
# not using _value_check since there is a
|
||||
# suggestion for the user
|
||||
if len(set(args)) != len(args):
|
||||
weights = multiset(args)
|
||||
n = Integer(len(args))
|
||||
for k in weights:
|
||||
weights[k] /= n
|
||||
raise ValueError(filldedent("""
|
||||
Repeated args detected but set expected. For a
|
||||
distribution having different weights for each
|
||||
item use the following:""") + (
|
||||
'\nS("FiniteRV(%s, %s)")' % ("'X'", weights)))
|
||||
|
||||
@property
|
||||
def p(self):
|
||||
return Rational(1, len(self.args))
|
||||
|
||||
@property # type: ignore
|
||||
@cacheit
|
||||
def dict(self):
|
||||
return dict.fromkeys(self.set, self.p)
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return set(self.args)
|
||||
|
||||
def pmf(self, x):
|
||||
if x in self.args:
|
||||
return self.p
|
||||
else:
|
||||
return S.Zero
|
||||
|
||||
|
||||
def DiscreteUniform(name, items):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a uniform distribution over
|
||||
the input set.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
items : list/tuple
|
||||
Items over which Uniform distribution is to be made
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import DiscreteUniform, density
|
||||
>>> from sympy import symbols
|
||||
|
||||
>>> X = DiscreteUniform('X', symbols('a b c')) # equally likely over a, b, c
|
||||
>>> density(X).dict
|
||||
{a: 1/3, b: 1/3, c: 1/3}
|
||||
|
||||
>>> Y = DiscreteUniform('Y', list(range(5))) # distribution over a range
|
||||
>>> density(Y).dict
|
||||
{0: 1/5, 1: 1/5, 2: 1/5, 3: 1/5, 4: 1/5}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Discrete_uniform_distribution
|
||||
.. [2] https://mathworld.wolfram.com/DiscreteUniformDistribution.html
|
||||
|
||||
"""
|
||||
return rv(name, DiscreteUniformDistribution, *items)
|
||||
|
||||
|
||||
class DieDistribution(SingleFiniteDistribution):
|
||||
_argnames = ('sides',)
|
||||
|
||||
@staticmethod
|
||||
def check(sides):
|
||||
_value_check((sides.is_positive, sides.is_integer),
|
||||
"number of sides must be a positive integer.")
|
||||
|
||||
@property
|
||||
def is_symbolic(self):
|
||||
return not self.sides.is_number
|
||||
|
||||
@property
|
||||
def high(self):
|
||||
return self.sides
|
||||
|
||||
@property
|
||||
def low(self):
|
||||
return S.One
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
if self.is_symbolic:
|
||||
return Intersection(S.Naturals0, Interval(0, self.sides))
|
||||
return set(map(Integer, range(1, self.sides + 1)))
|
||||
|
||||
def pmf(self, x):
|
||||
x = sympify(x)
|
||||
if not (x.is_number or x.is_Symbol or is_random(x)):
|
||||
raise ValueError("'x' expected as an argument of type 'number', 'Symbol', or "
|
||||
"'RandomSymbol' not %s" % (type(x)))
|
||||
cond = Ge(x, 1) & Le(x, self.sides) & Contains(x, S.Integers)
|
||||
return Piecewise((S.One/self.sides, cond), (S.Zero, True))
|
||||
|
||||
def Die(name, sides=6):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a fair die.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
sides : Integer
|
||||
Represents the number of sides of the Die, by default is 6
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Die, density
|
||||
>>> from sympy import Symbol
|
||||
|
||||
>>> D6 = Die('D6', 6) # Six sided Die
|
||||
>>> density(D6).dict
|
||||
{1: 1/6, 2: 1/6, 3: 1/6, 4: 1/6, 5: 1/6, 6: 1/6}
|
||||
|
||||
>>> D4 = Die('D4', 4) # Four sided Die
|
||||
>>> density(D4).dict
|
||||
{1: 1/4, 2: 1/4, 3: 1/4, 4: 1/4}
|
||||
|
||||
>>> n = Symbol('n', positive=True, integer=True)
|
||||
>>> Dn = Die('Dn', n) # n sided Die
|
||||
>>> density(Dn).dict
|
||||
Density(DieDistribution(n))
|
||||
>>> density(Dn).dict.subs(n, 4).doit()
|
||||
{1: 1/4, 2: 1/4, 3: 1/4, 4: 1/4}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
"""
|
||||
|
||||
return rv(name, DieDistribution, sides)
|
||||
|
||||
|
||||
class BernoulliDistribution(SingleFiniteDistribution):
|
||||
_argnames = ('p', 'succ', 'fail')
|
||||
|
||||
@staticmethod
|
||||
def check(p, succ, fail):
|
||||
_value_check((p >= 0, p <= 1),
|
||||
"p should be in range [0, 1].")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return {self.succ, self.fail}
|
||||
|
||||
def pmf(self, x):
|
||||
if isinstance(self.succ, Symbol) and isinstance(self.fail, Symbol):
|
||||
return Piecewise((self.p, x == self.succ),
|
||||
(1 - self.p, x == self.fail),
|
||||
(S.Zero, True))
|
||||
return Piecewise((self.p, Eq(x, self.succ)),
|
||||
(1 - self.p, Eq(x, self.fail)),
|
||||
(S.Zero, True))
|
||||
|
||||
|
||||
def Bernoulli(name, p, succ=1, fail=0):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a Bernoulli process.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
p : Rational number between 0 and 1
|
||||
Represents probability of success
|
||||
succ : Integer/symbol/string
|
||||
Represents event of success
|
||||
fail : Integer/symbol/string
|
||||
Represents event of failure
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Bernoulli, density
|
||||
>>> from sympy import S
|
||||
|
||||
>>> X = Bernoulli('X', S(3)/4) # 1-0 Bernoulli variable, probability = 3/4
|
||||
>>> density(X).dict
|
||||
{0: 1/4, 1: 3/4}
|
||||
|
||||
>>> X = Bernoulli('X', S.Half, 'Heads', 'Tails') # A fair coin toss
|
||||
>>> density(X).dict
|
||||
{Heads: 1/2, Tails: 1/2}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Bernoulli_distribution
|
||||
.. [2] https://mathworld.wolfram.com/BernoulliDistribution.html
|
||||
|
||||
"""
|
||||
|
||||
return rv(name, BernoulliDistribution, p, succ, fail)
|
||||
|
||||
|
||||
def Coin(name, p=S.Half):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a Coin toss.
|
||||
|
||||
This is an equivalent of a Bernoulli random variable with
|
||||
"H" and "T" as success and failure events respectively.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
p : Rational Number between 0 and 1
|
||||
Represents probability of getting "Heads", by default is Half
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Coin, density
|
||||
>>> from sympy import Rational
|
||||
|
||||
>>> C = Coin('C') # A fair coin toss
|
||||
>>> density(C).dict
|
||||
{H: 1/2, T: 1/2}
|
||||
|
||||
>>> C2 = Coin('C2', Rational(3, 5)) # An unfair coin
|
||||
>>> density(C2).dict
|
||||
{H: 3/5, T: 2/5}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.stats.Binomial
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Coin_flipping
|
||||
|
||||
"""
|
||||
return rv(name, BernoulliDistribution, p, 'H', 'T')
|
||||
|
||||
|
||||
class BinomialDistribution(SingleFiniteDistribution):
|
||||
_argnames = ('n', 'p', 'succ', 'fail')
|
||||
|
||||
@staticmethod
|
||||
def check(n, p, succ, fail):
|
||||
_value_check((n.is_integer, n.is_nonnegative),
|
||||
"'n' must be nonnegative integer.")
|
||||
_value_check((p <= 1, p >= 0),
|
||||
"p should be in range [0, 1].")
|
||||
|
||||
@property
|
||||
def high(self):
|
||||
return self.n
|
||||
|
||||
@property
|
||||
def low(self):
|
||||
return S.Zero
|
||||
|
||||
@property
|
||||
def is_symbolic(self):
|
||||
return not self.n.is_number
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
if self.is_symbolic:
|
||||
return Intersection(S.Naturals0, Interval(0, self.n))
|
||||
return set(self.dict.keys())
|
||||
|
||||
def pmf(self, x):
|
||||
n, p = self.n, self.p
|
||||
x = sympify(x)
|
||||
if not (x.is_number or x.is_Symbol or is_random(x)):
|
||||
raise ValueError("'x' expected as an argument of type 'number', 'Symbol', or "
|
||||
"'RandomSymbol' not %s" % (type(x)))
|
||||
cond = Ge(x, 0) & Le(x, n) & Contains(x, S.Integers)
|
||||
return Piecewise((binomial(n, x) * p**x * (1 - p)**(n - x), cond), (S.Zero, True))
|
||||
|
||||
@property # type: ignore
|
||||
@cacheit
|
||||
def dict(self):
|
||||
if self.is_symbolic:
|
||||
return Density(self)
|
||||
return {k*self.succ + (self.n-k)*self.fail: self.pmf(k)
|
||||
for k in range(0, self.n + 1)}
|
||||
|
||||
|
||||
def Binomial(name, n, p, succ=1, fail=0):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a binomial distribution.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n : Positive Integer
|
||||
Represents number of trials
|
||||
p : Rational Number between 0 and 1
|
||||
Represents probability of success
|
||||
succ : Integer/symbol/string
|
||||
Represents event of success, by default is 1
|
||||
fail : Integer/symbol/string
|
||||
Represents event of failure, by default is 0
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Binomial, density
|
||||
>>> from sympy import S, Symbol
|
||||
|
||||
>>> X = Binomial('X', 4, S.Half) # Four "coin flips"
|
||||
>>> density(X).dict
|
||||
{0: 1/16, 1: 1/4, 2: 3/8, 3: 1/4, 4: 1/16}
|
||||
|
||||
>>> n = Symbol('n', positive=True, integer=True)
|
||||
>>> p = Symbol('p', positive=True)
|
||||
>>> X = Binomial('X', n, S.Half) # n "coin flips"
|
||||
>>> density(X).dict
|
||||
Density(BinomialDistribution(n, 1/2, 1, 0))
|
||||
>>> density(X).dict.subs(n, 4).doit()
|
||||
{0: 1/16, 1: 1/4, 2: 3/8, 3: 1/4, 4: 1/16}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Binomial_distribution
|
||||
.. [2] https://mathworld.wolfram.com/BinomialDistribution.html
|
||||
|
||||
"""
|
||||
|
||||
return rv(name, BinomialDistribution, n, p, succ, fail)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Beta-binomial distribution ----------------------------------------------------------
|
||||
|
||||
class BetaBinomialDistribution(SingleFiniteDistribution):
|
||||
_argnames = ('n', 'alpha', 'beta')
|
||||
|
||||
@staticmethod
|
||||
def check(n, alpha, beta):
|
||||
_value_check((n.is_integer, n.is_nonnegative),
|
||||
"'n' must be nonnegative integer. n = %s." % str(n))
|
||||
_value_check((alpha > 0),
|
||||
"'alpha' must be: alpha > 0 . alpha = %s" % str(alpha))
|
||||
_value_check((beta > 0),
|
||||
"'beta' must be: beta > 0 . beta = %s" % str(beta))
|
||||
|
||||
@property
|
||||
def high(self):
|
||||
return self.n
|
||||
|
||||
@property
|
||||
def low(self):
|
||||
return S.Zero
|
||||
|
||||
@property
|
||||
def is_symbolic(self):
|
||||
return not self.n.is_number
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
if self.is_symbolic:
|
||||
return Intersection(S.Naturals0, Interval(0, self.n))
|
||||
return set(map(Integer, range(self.n + 1)))
|
||||
|
||||
def pmf(self, k):
|
||||
n, a, b = self.n, self.alpha, self.beta
|
||||
return binomial(n, k) * beta_fn(k + a, n - k + b) / beta_fn(a, b)
|
||||
|
||||
|
||||
def BetaBinomial(name, n, alpha, beta):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a Beta-binomial distribution.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n : Positive Integer
|
||||
Represents number of trials
|
||||
alpha : Real positive number
|
||||
beta : Real positive number
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import BetaBinomial, density
|
||||
|
||||
>>> X = BetaBinomial('X', 2, 1, 1)
|
||||
>>> density(X).dict
|
||||
{0: 1/3, 1: 2*beta(2, 2), 2: 1/3}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Beta-binomial_distribution
|
||||
.. [2] https://mathworld.wolfram.com/BetaBinomialDistribution.html
|
||||
|
||||
"""
|
||||
|
||||
return rv(name, BetaBinomialDistribution, n, alpha, beta)
|
||||
|
||||
|
||||
class HypergeometricDistribution(SingleFiniteDistribution):
|
||||
_argnames = ('N', 'm', 'n')
|
||||
|
||||
@staticmethod
|
||||
def check(n, N, m):
|
||||
_value_check((N.is_integer, N.is_nonnegative),
|
||||
"'N' must be nonnegative integer. N = %s." % str(N))
|
||||
_value_check((n.is_integer, n.is_nonnegative),
|
||||
"'n' must be nonnegative integer. n = %s." % str(n))
|
||||
_value_check((m.is_integer, m.is_nonnegative),
|
||||
"'m' must be nonnegative integer. m = %s." % str(m))
|
||||
|
||||
@property
|
||||
def is_symbolic(self):
|
||||
return not all(x.is_number for x in (self.N, self.m, self.n))
|
||||
|
||||
@property
|
||||
def high(self):
|
||||
return Piecewise((self.n, Lt(self.n, self.m) != False), (self.m, True))
|
||||
|
||||
@property
|
||||
def low(self):
|
||||
return Piecewise((0, Gt(0, self.n + self.m - self.N) != False), (self.n + self.m - self.N, True))
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
N, m, n = self.N, self.m, self.n
|
||||
if self.is_symbolic:
|
||||
return Intersection(S.Naturals0, Interval(self.low, self.high))
|
||||
return set(range(max(0, n + m - N), min(n, m) + 1))
|
||||
|
||||
def pmf(self, k):
|
||||
N, m, n = self.N, self.m, self.n
|
||||
return S(binomial(m, k) * binomial(N - m, n - k))/binomial(N, n)
|
||||
|
||||
|
||||
def Hypergeometric(name, N, m, n):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a hypergeometric distribution.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
N : Positive Integer
|
||||
Represents finite population of size N.
|
||||
m : Positive Integer
|
||||
Represents number of trials with required feature.
|
||||
n : Positive Integer
|
||||
Represents numbers of draws.
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Hypergeometric, density
|
||||
|
||||
>>> X = Hypergeometric('X', 10, 5, 3) # 10 marbles, 5 white (success), 3 draws
|
||||
>>> density(X).dict
|
||||
{0: 1/12, 1: 5/12, 2: 5/12, 3: 1/12}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Hypergeometric_distribution
|
||||
.. [2] https://mathworld.wolfram.com/HypergeometricDistribution.html
|
||||
|
||||
"""
|
||||
return rv(name, HypergeometricDistribution, N, m, n)
|
||||
|
||||
|
||||
class RademacherDistribution(SingleFiniteDistribution):
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return {-1, 1}
|
||||
|
||||
@property
|
||||
def pmf(self):
|
||||
k = Dummy('k')
|
||||
return Lambda(k, Piecewise((S.Half, Or(Eq(k, -1), Eq(k, 1))), (S.Zero, True)))
|
||||
|
||||
def Rademacher(name):
|
||||
r"""
|
||||
Create a Finite Random Variable representing a Rademacher distribution.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Rademacher, density
|
||||
|
||||
>>> X = Rademacher('X')
|
||||
>>> density(X).dict
|
||||
{-1: 1/2, 1: 1/2}
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
See Also
|
||||
========
|
||||
|
||||
sympy.stats.Bernoulli
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Rademacher_distribution
|
||||
|
||||
"""
|
||||
return rv(name, RademacherDistribution)
|
||||
|
||||
class IdealSolitonDistribution(SingleFiniteDistribution):
|
||||
_argnames = ('k',)
|
||||
|
||||
@staticmethod
|
||||
def check(k):
|
||||
_value_check(k.is_integer and k.is_positive,
|
||||
"'k' must be a positive integer.")
|
||||
|
||||
@property
|
||||
def low(self):
|
||||
return S.One
|
||||
|
||||
@property
|
||||
def high(self):
|
||||
return self.k
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return set(map(Integer, range(1, self.k + 1)))
|
||||
|
||||
@property # type: ignore
|
||||
@cacheit
|
||||
def dict(self):
|
||||
if self.k.is_Symbol:
|
||||
return Density(self)
|
||||
d = {1: Rational(1, self.k)}
|
||||
d.update({i: Rational(1, i*(i - 1)) for i in range(2, self.k + 1)})
|
||||
return d
|
||||
|
||||
def pmf(self, x):
|
||||
x = sympify(x)
|
||||
if not (x.is_number or x.is_Symbol or is_random(x)):
|
||||
raise ValueError("'x' expected as an argument of type 'number', 'Symbol', or "
|
||||
"'RandomSymbol' not %s" % (type(x)))
|
||||
cond1 = Eq(x, 1) & x.is_integer
|
||||
cond2 = Ge(x, 1) & Le(x, self.k) & x.is_integer
|
||||
return Piecewise((1/self.k, cond1), (1/(x*(x - 1)), cond2), (S.Zero, True))
|
||||
|
||||
def IdealSoliton(name, k):
|
||||
r"""
|
||||
Create a Finite Random Variable of Ideal Soliton Distribution
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
k : Positive Integer
|
||||
Represents the number of input symbols in an LT (Luby Transform) code.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import IdealSoliton, density, P, E
|
||||
>>> sol = IdealSoliton('sol', 5)
|
||||
>>> density(sol).dict
|
||||
{1: 1/5, 2: 1/2, 3: 1/6, 4: 1/12, 5: 1/20}
|
||||
>>> density(sol).set
|
||||
{1, 2, 3, 4, 5}
|
||||
|
||||
>>> from sympy import Symbol
|
||||
>>> k = Symbol('k', positive=True, integer=True)
|
||||
>>> sol = IdealSoliton('sol', k)
|
||||
>>> density(sol).dict
|
||||
Density(IdealSolitonDistribution(k))
|
||||
>>> density(sol).dict.subs(k, 10).doit()
|
||||
{1: 1/10, 2: 1/2, 3: 1/6, 4: 1/12, 5: 1/20, 6: 1/30, 7: 1/42, 8: 1/56, 9: 1/72, 10: 1/90}
|
||||
|
||||
>>> E(sol.subs(k, 10))
|
||||
7381/2520
|
||||
|
||||
>>> P(sol.subs(k, 4) > 2)
|
||||
1/4
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Soliton_distribution#Ideal_distribution
|
||||
.. [2] https://pages.cs.wisc.edu/~suman/courses/740/papers/luby02lt.pdf
|
||||
|
||||
"""
|
||||
return rv(name, IdealSolitonDistribution, k)
|
||||
|
||||
class RobustSolitonDistribution(SingleFiniteDistribution):
|
||||
_argnames= ('k', 'delta', 'c')
|
||||
|
||||
@staticmethod
|
||||
def check(k, delta, c):
|
||||
_value_check(k.is_integer and k.is_positive,
|
||||
"'k' must be a positive integer")
|
||||
_value_check(Gt(delta, 0) and Le(delta, 1),
|
||||
"'delta' must be a real number in the interval (0,1)")
|
||||
_value_check(c.is_positive,
|
||||
"'c' must be a positive real number.")
|
||||
|
||||
@property
|
||||
def R(self):
|
||||
return self.c * log(self.k/self.delta) * self.k**0.5
|
||||
|
||||
@property
|
||||
def Z(self):
|
||||
z = 0
|
||||
for i in Range(1, round(self.k/self.R)):
|
||||
z += (1/i)
|
||||
z += log(self.R/self.delta)
|
||||
return 1 + z * self.R/self.k
|
||||
|
||||
@property
|
||||
def low(self):
|
||||
return S.One
|
||||
|
||||
@property
|
||||
def high(self):
|
||||
return self.k
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return set(map(Integer, range(1, self.k + 1)))
|
||||
|
||||
@property
|
||||
def is_symbolic(self):
|
||||
return not (self.k.is_number and self.c.is_number and self.delta.is_number)
|
||||
|
||||
def pmf(self, x):
|
||||
x = sympify(x)
|
||||
if not (x.is_number or x.is_Symbol or is_random(x)):
|
||||
raise ValueError("'x' expected as an argument of type 'number', 'Symbol', or "
|
||||
"'RandomSymbol' not %s" % (type(x)))
|
||||
|
||||
cond1 = Eq(x, 1) & x.is_integer
|
||||
cond2 = Ge(x, 1) & Le(x, self.k) & x.is_integer
|
||||
rho = Piecewise((Rational(1, self.k), cond1), (Rational(1, x*(x-1)), cond2), (S.Zero, True))
|
||||
|
||||
cond1 = Ge(x, 1) & Le(x, round(self.k/self.R)-1)
|
||||
cond2 = Eq(x, round(self.k/self.R))
|
||||
tau = Piecewise((self.R/(self.k * x), cond1), (self.R * log(self.R/self.delta)/self.k, cond2), (S.Zero, True))
|
||||
|
||||
return (rho + tau)/self.Z
|
||||
|
||||
def RobustSoliton(name, k, delta, c):
|
||||
r'''
|
||||
Create a Finite Random Variable of Robust Soliton Distribution
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
k : Positive Integer
|
||||
Represents the number of input symbols in an LT (Luby Transform) code.
|
||||
delta : Positive Rational Number
|
||||
Represents the failure probability. Must be in the interval (0,1).
|
||||
c : Positive Rational Number
|
||||
Constant of proportionality. Values close to 1 are recommended
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import RobustSoliton, density, P, E
|
||||
>>> robSol = RobustSoliton('robSol', 5, 0.5, 0.01)
|
||||
>>> density(robSol).dict
|
||||
{1: 0.204253668152708, 2: 0.490631107897393, 3: 0.165210624506162, 4: 0.0834387731899302, 5: 0.0505633404760675}
|
||||
>>> density(robSol).set
|
||||
{1, 2, 3, 4, 5}
|
||||
|
||||
>>> from sympy import Symbol
|
||||
>>> k = Symbol('k', positive=True, integer=True)
|
||||
>>> c = Symbol('c', positive=True)
|
||||
>>> robSol = RobustSoliton('robSol', k, 0.5, c)
|
||||
>>> density(robSol).dict
|
||||
Density(RobustSolitonDistribution(k, 0.5, c))
|
||||
>>> density(robSol).dict.subs(k, 10).subs(c, 0.03).doit()
|
||||
{1: 0.116641095387194, 2: 0.467045731687165, 3: 0.159984123349381, 4: 0.0821431680681869, 5: 0.0505765646770100,
|
||||
6: 0.0345781523420719, 7: 0.0253132820710503, 8: 0.0194459129233227, 9: 0.0154831166726115, 10: 0.0126733075238887}
|
||||
|
||||
>>> E(robSol.subs(k, 10).subs(c, 0.05))
|
||||
2.91358846104106
|
||||
|
||||
>>> P(robSol.subs(k, 4).subs(c, 0.1) > 2)
|
||||
0.243650614389834
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Soliton_distribution#Robust_distribution
|
||||
.. [2] https://www.inference.org.uk/mackay/itprnn/ps/588.596.pdf
|
||||
.. [3] https://pages.cs.wisc.edu/~suman/courses/740/papers/luby02lt.pdf
|
||||
|
||||
'''
|
||||
return rv(name, RobustSolitonDistribution, k, delta, c)
|
||||
426
venv/lib/python3.12/site-packages/sympy/stats/joint_rv.py
Normal file
426
venv/lib/python3.12/site-packages/sympy/stats/joint_rv.py
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
"""
|
||||
Joint Random Variables Module
|
||||
|
||||
See Also
|
||||
========
|
||||
sympy.stats.rv
|
||||
sympy.stats.frv
|
||||
sympy.stats.crv
|
||||
sympy.stats.drv
|
||||
"""
|
||||
from math import prod
|
||||
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, Symbol)
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.sets.sets import ProductSet
|
||||
from sympy.tensor.indexed import Indexed
|
||||
from sympy.concrete.products import Product
|
||||
from sympy.concrete.summations import Sum, summation
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.integrals.integrals import Integral, integrate
|
||||
from sympy.matrices import ImmutableMatrix, matrix2numpy, list2numpy
|
||||
from sympy.stats.crv import SingleContinuousDistribution, SingleContinuousPSpace
|
||||
from sympy.stats.drv import SingleDiscreteDistribution, SingleDiscretePSpace
|
||||
from sympy.stats.rv import (ProductPSpace, NamedArgsMixin, Distribution,
|
||||
ProductDomain, RandomSymbol, random_symbols,
|
||||
SingleDomain, _symbol_converter)
|
||||
from sympy.utilities.iterables import iterable
|
||||
from sympy.utilities.misc import filldedent
|
||||
from sympy.external import import_module
|
||||
|
||||
# __all__ = ['marginal_distribution']
|
||||
|
||||
class JointPSpace(ProductPSpace):
|
||||
"""
|
||||
Represents a joint probability space. Represented using symbols for
|
||||
each component and a distribution.
|
||||
"""
|
||||
def __new__(cls, sym, dist):
|
||||
if isinstance(dist, SingleContinuousDistribution):
|
||||
return SingleContinuousPSpace(sym, dist)
|
||||
if isinstance(dist, SingleDiscreteDistribution):
|
||||
return SingleDiscretePSpace(sym, dist)
|
||||
sym = _symbol_converter(sym)
|
||||
return Basic.__new__(cls, sym, dist)
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.domain.set
|
||||
|
||||
@property
|
||||
def symbol(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def distribution(self):
|
||||
return self.args[1]
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return JointRandomSymbol(self.symbol, self)
|
||||
|
||||
@property
|
||||
def component_count(self):
|
||||
_set = self.distribution.set
|
||||
if isinstance(_set, ProductSet):
|
||||
return S(len(_set.args))
|
||||
elif isinstance(_set, Product):
|
||||
return _set.limits[0][-1]
|
||||
return S.One
|
||||
|
||||
@property
|
||||
def pdf(self):
|
||||
sym = [Indexed(self.symbol, i) for i in range(self.component_count)]
|
||||
return self.distribution(*sym)
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
rvs = random_symbols(self.distribution)
|
||||
if not rvs:
|
||||
return SingleDomain(self.symbol, self.distribution.set)
|
||||
return ProductDomain(*[rv.pspace.domain for rv in rvs])
|
||||
|
||||
def component_domain(self, index):
|
||||
return self.set.args[index]
|
||||
|
||||
def marginal_distribution(self, *indices):
|
||||
count = self.component_count
|
||||
if count.atoms(Symbol):
|
||||
raise ValueError("Marginal distributions cannot be computed "
|
||||
"for symbolic dimensions. It is a work under progress.")
|
||||
orig = [Indexed(self.symbol, i) for i in range(count)]
|
||||
all_syms = [Symbol(str(i)) for i in orig]
|
||||
replace_dict = dict(zip(all_syms, orig))
|
||||
sym = tuple(Symbol(str(Indexed(self.symbol, i))) for i in indices)
|
||||
limits = [[i,] for i in all_syms if i not in sym]
|
||||
index = 0
|
||||
for i in range(count):
|
||||
if i not in indices:
|
||||
limits[index].append(self.distribution.set.args[i])
|
||||
limits[index] = tuple(limits[index])
|
||||
index += 1
|
||||
if self.distribution.is_Continuous:
|
||||
f = Lambda(sym, integrate(self.distribution(*all_syms), *limits))
|
||||
elif self.distribution.is_Discrete:
|
||||
f = Lambda(sym, summation(self.distribution(*all_syms), *limits))
|
||||
return f.xreplace(replace_dict)
|
||||
|
||||
def compute_expectation(self, expr, rvs=None, evaluate=False, **kwargs):
|
||||
syms = tuple(self.value[i] for i in range(self.component_count))
|
||||
rvs = rvs or syms
|
||||
if not any(i in rvs for i in syms):
|
||||
return expr
|
||||
expr = expr*self.pdf
|
||||
for rv in rvs:
|
||||
if isinstance(rv, Indexed):
|
||||
expr = expr.xreplace({rv: Indexed(str(rv.base), rv.args[1])})
|
||||
elif isinstance(rv, RandomSymbol):
|
||||
expr = expr.xreplace({rv: rv.symbol})
|
||||
if self.value in random_symbols(expr):
|
||||
raise NotImplementedError(filldedent('''
|
||||
Expectations of expression with unindexed joint random symbols
|
||||
cannot be calculated yet.'''))
|
||||
limits = tuple((Indexed(str(rv.base),rv.args[1]),
|
||||
self.distribution.set.args[rv.args[1]]) for rv in syms)
|
||||
return Integral(expr, *limits)
|
||||
|
||||
def where(self, condition):
|
||||
raise NotImplementedError()
|
||||
|
||||
def compute_density(self, expr):
|
||||
raise NotImplementedError()
|
||||
|
||||
def sample(self, size=(), library='scipy', seed=None):
|
||||
"""
|
||||
Internal sample method
|
||||
|
||||
Returns dictionary mapping RandomSymbol to realization value.
|
||||
"""
|
||||
return {RandomSymbol(self.symbol, self): self.distribution.sample(size,
|
||||
library=library, seed=seed)}
|
||||
|
||||
def probability(self, condition):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class SampleJointScipy:
|
||||
"""Returns the sample from scipy of the given distribution"""
|
||||
def __new__(cls, dist, size, seed=None):
|
||||
return cls._sample_scipy(dist, size, seed)
|
||||
|
||||
@classmethod
|
||||
def _sample_scipy(cls, dist, size, seed):
|
||||
"""Sample from SciPy."""
|
||||
|
||||
import numpy
|
||||
if seed is None or isinstance(seed, int):
|
||||
rand_state = numpy.random.default_rng(seed=seed)
|
||||
else:
|
||||
rand_state = seed
|
||||
from scipy import stats as scipy_stats
|
||||
scipy_rv_map = {
|
||||
'MultivariateNormalDistribution': lambda dist, size: scipy_stats.multivariate_normal.rvs(
|
||||
mean=matrix2numpy(dist.mu).flatten(),
|
||||
cov=matrix2numpy(dist.sigma), size=size, random_state=rand_state),
|
||||
'MultivariateBetaDistribution': lambda dist, size: scipy_stats.dirichlet.rvs(
|
||||
alpha=list2numpy(dist.alpha, float).flatten(), size=size, random_state=rand_state),
|
||||
'MultinomialDistribution': lambda dist, size: scipy_stats.multinomial.rvs(
|
||||
n=int(dist.n), p=list2numpy(dist.p, float).flatten(), size=size, random_state=rand_state)
|
||||
}
|
||||
|
||||
sample_shape = {
|
||||
'MultivariateNormalDistribution': lambda dist: matrix2numpy(dist.mu).flatten().shape,
|
||||
'MultivariateBetaDistribution': lambda dist: list2numpy(dist.alpha).flatten().shape,
|
||||
'MultinomialDistribution': lambda dist: list2numpy(dist.p).flatten().shape
|
||||
}
|
||||
|
||||
dist_list = scipy_rv_map.keys()
|
||||
|
||||
if dist.__class__.__name__ not in dist_list:
|
||||
return None
|
||||
|
||||
samples = scipy_rv_map[dist.__class__.__name__](dist, size)
|
||||
return samples.reshape(size + sample_shape[dist.__class__.__name__](dist))
|
||||
|
||||
class SampleJointNumpy:
|
||||
"""Returns the sample from numpy of the given distribution"""
|
||||
|
||||
def __new__(cls, dist, size, seed=None):
|
||||
return cls._sample_numpy(dist, size, seed)
|
||||
|
||||
@classmethod
|
||||
def _sample_numpy(cls, dist, size, seed):
|
||||
"""Sample from NumPy."""
|
||||
|
||||
import numpy
|
||||
if seed is None or isinstance(seed, int):
|
||||
rand_state = numpy.random.default_rng(seed=seed)
|
||||
else:
|
||||
rand_state = seed
|
||||
numpy_rv_map = {
|
||||
'MultivariateNormalDistribution': lambda dist, size: rand_state.multivariate_normal(
|
||||
mean=matrix2numpy(dist.mu, float).flatten(),
|
||||
cov=matrix2numpy(dist.sigma, float), size=size),
|
||||
'MultivariateBetaDistribution': lambda dist, size: rand_state.dirichlet(
|
||||
alpha=list2numpy(dist.alpha, float).flatten(), size=size),
|
||||
'MultinomialDistribution': lambda dist, size: rand_state.multinomial(
|
||||
n=int(dist.n), pvals=list2numpy(dist.p, float).flatten(), size=size)
|
||||
}
|
||||
|
||||
sample_shape = {
|
||||
'MultivariateNormalDistribution': lambda dist: matrix2numpy(dist.mu).flatten().shape,
|
||||
'MultivariateBetaDistribution': lambda dist: list2numpy(dist.alpha).flatten().shape,
|
||||
'MultinomialDistribution': lambda dist: list2numpy(dist.p).flatten().shape
|
||||
}
|
||||
|
||||
dist_list = numpy_rv_map.keys()
|
||||
|
||||
if dist.__class__.__name__ not in dist_list:
|
||||
return None
|
||||
|
||||
samples = numpy_rv_map[dist.__class__.__name__](dist, prod(size))
|
||||
return samples.reshape(size + sample_shape[dist.__class__.__name__](dist))
|
||||
|
||||
class SampleJointPymc:
|
||||
"""Returns the sample from pymc of the given distribution"""
|
||||
|
||||
def __new__(cls, dist, size, seed=None):
|
||||
return cls._sample_pymc(dist, size, seed)
|
||||
|
||||
@classmethod
|
||||
def _sample_pymc(cls, dist, size, seed):
|
||||
"""Sample from PyMC."""
|
||||
|
||||
try:
|
||||
import pymc
|
||||
except ImportError:
|
||||
import pymc3 as pymc
|
||||
pymc_rv_map = {
|
||||
'MultivariateNormalDistribution': lambda dist:
|
||||
pymc.MvNormal('X', mu=matrix2numpy(dist.mu, float).flatten(),
|
||||
cov=matrix2numpy(dist.sigma, float), shape=(1, dist.mu.shape[0])),
|
||||
'MultivariateBetaDistribution': lambda dist:
|
||||
pymc.Dirichlet('X', a=list2numpy(dist.alpha, float).flatten()),
|
||||
'MultinomialDistribution': lambda dist:
|
||||
pymc.Multinomial('X', n=int(dist.n),
|
||||
p=list2numpy(dist.p, float).flatten(), shape=(1, len(dist.p)))
|
||||
}
|
||||
|
||||
sample_shape = {
|
||||
'MultivariateNormalDistribution': lambda dist: matrix2numpy(dist.mu).flatten().shape,
|
||||
'MultivariateBetaDistribution': lambda dist: list2numpy(dist.alpha).flatten().shape,
|
||||
'MultinomialDistribution': lambda dist: list2numpy(dist.p).flatten().shape
|
||||
}
|
||||
|
||||
dist_list = pymc_rv_map.keys()
|
||||
|
||||
if dist.__class__.__name__ not in dist_list:
|
||||
return None
|
||||
|
||||
import logging
|
||||
logging.getLogger("pymc3").setLevel(logging.ERROR)
|
||||
with pymc.Model():
|
||||
pymc_rv_map[dist.__class__.__name__](dist)
|
||||
samples = pymc.sample(draws=prod(size), chains=1, progressbar=False, random_seed=seed, return_inferencedata=False, compute_convergence_checks=False)[:]['X']
|
||||
return samples.reshape(size + sample_shape[dist.__class__.__name__](dist))
|
||||
|
||||
|
||||
_get_sample_class_jrv = {
|
||||
'scipy': SampleJointScipy,
|
||||
'pymc3': SampleJointPymc,
|
||||
'pymc': SampleJointPymc,
|
||||
'numpy': SampleJointNumpy
|
||||
}
|
||||
|
||||
class JointDistribution(Distribution, NamedArgsMixin):
|
||||
"""
|
||||
Represented by the random variables part of the joint distribution.
|
||||
Contains methods for PDF, CDF, sampling, marginal densities, etc.
|
||||
"""
|
||||
|
||||
_argnames = ('pdf', )
|
||||
|
||||
def __new__(cls, *args):
|
||||
args = list(map(sympify, args))
|
||||
for i in range(len(args)):
|
||||
if isinstance(args[i], list):
|
||||
args[i] = ImmutableMatrix(args[i])
|
||||
return Basic.__new__(cls, *args)
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return ProductDomain(self.symbols)
|
||||
|
||||
@property
|
||||
def pdf(self):
|
||||
return self.density.args[1]
|
||||
|
||||
def cdf(self, other):
|
||||
if not isinstance(other, dict):
|
||||
raise ValueError("%s should be of type dict, got %s"%(other, type(other)))
|
||||
rvs = other.keys()
|
||||
_set = self.domain.set.sets
|
||||
expr = self.pdf(tuple(i.args[0] for i in self.symbols))
|
||||
for i in range(len(other)):
|
||||
if rvs[i].is_Continuous:
|
||||
density = Integral(expr, (rvs[i], _set[i].inf,
|
||||
other[rvs[i]]))
|
||||
elif rvs[i].is_Discrete:
|
||||
density = Sum(expr, (rvs[i], _set[i].inf,
|
||||
other[rvs[i]]))
|
||||
return density
|
||||
|
||||
def sample(self, size=(), library='scipy', seed=None):
|
||||
""" A random realization from the distribution """
|
||||
|
||||
libraries = ('scipy', 'numpy', 'pymc3', 'pymc')
|
||||
if library not in libraries:
|
||||
raise NotImplementedError("Sampling from %s is not supported yet."
|
||||
% str(library))
|
||||
if not import_module(library):
|
||||
raise ValueError("Failed to import %s" % library)
|
||||
|
||||
samps = _get_sample_class_jrv[library](self, size, seed=seed)
|
||||
|
||||
if samps is not None:
|
||||
return samps
|
||||
raise NotImplementedError(
|
||||
"Sampling for %s is not currently implemented from %s"
|
||||
% (self.__class__.__name__, library)
|
||||
)
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.pdf(*args)
|
||||
|
||||
class JointRandomSymbol(RandomSymbol):
|
||||
"""
|
||||
Representation of random symbols with joint probability distributions
|
||||
to allow indexing."
|
||||
"""
|
||||
def __getitem__(self, key):
|
||||
if isinstance(self.pspace, JointPSpace):
|
||||
if (self.pspace.component_count <= key) == True:
|
||||
raise ValueError("Index keys for %s can only up to %s." %
|
||||
(self.name, self.pspace.component_count - 1))
|
||||
return Indexed(self, key)
|
||||
|
||||
|
||||
|
||||
class MarginalDistribution(Distribution):
|
||||
"""
|
||||
Represents the marginal distribution of a joint probability space.
|
||||
|
||||
Initialised using a probability distribution and random variables(or
|
||||
their indexed components) which should be a part of the resultant
|
||||
distribution.
|
||||
"""
|
||||
|
||||
def __new__(cls, dist, *rvs):
|
||||
if len(rvs) == 1 and iterable(rvs[0]):
|
||||
rvs = tuple(rvs[0])
|
||||
if not all(isinstance(rv, (Indexed, RandomSymbol)) for rv in rvs):
|
||||
raise ValueError(filldedent('''Marginal distribution can be
|
||||
intitialised only in terms of random variables or indexed random
|
||||
variables'''))
|
||||
rvs = Tuple.fromiter(rv for rv in rvs)
|
||||
if not isinstance(dist, JointDistribution) and len(random_symbols(dist)) == 0:
|
||||
return dist
|
||||
return Basic.__new__(cls, dist, rvs)
|
||||
|
||||
def check(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
rvs = [i for i in self.args[1] if isinstance(i, RandomSymbol)]
|
||||
return ProductSet(*[rv.pspace.set for rv in rvs])
|
||||
|
||||
@property
|
||||
def symbols(self):
|
||||
rvs = self.args[1]
|
||||
return {rv.pspace.symbol for rv in rvs}
|
||||
|
||||
def pdf(self, *x):
|
||||
expr, rvs = self.args[0], self.args[1]
|
||||
marginalise_out = [i for i in random_symbols(expr) if i not in rvs]
|
||||
if isinstance(expr, JointDistribution):
|
||||
count = len(expr.domain.args)
|
||||
x = Dummy('x', real=True)
|
||||
syms = tuple(Indexed(x, i) for i in count)
|
||||
expr = expr.pdf(syms)
|
||||
else:
|
||||
syms = tuple(rv.pspace.symbol if isinstance(rv, RandomSymbol) else rv.args[0] for rv in rvs)
|
||||
return Lambda(syms, self.compute_pdf(expr, marginalise_out))(*x)
|
||||
|
||||
def compute_pdf(self, expr, rvs):
|
||||
for rv in rvs:
|
||||
lpdf = 1
|
||||
if isinstance(rv, RandomSymbol):
|
||||
lpdf = rv.pspace.pdf
|
||||
expr = self.marginalise_out(expr*lpdf, rv)
|
||||
return expr
|
||||
|
||||
def marginalise_out(self, expr, rv):
|
||||
from sympy.concrete.summations import Sum
|
||||
if isinstance(rv, RandomSymbol):
|
||||
dom = rv.pspace.set
|
||||
elif isinstance(rv, Indexed):
|
||||
dom = rv.base.component_domain(
|
||||
rv.pspace.component_domain(rv.args[1]))
|
||||
expr = expr.xreplace({rv: rv.pspace.symbol})
|
||||
if rv.pspace.is_Continuous:
|
||||
#TODO: Modify to support integration
|
||||
#for all kinds of sets.
|
||||
expr = Integral(expr, (rv.pspace.symbol, dom))
|
||||
elif rv.pspace.is_Discrete:
|
||||
#incorporate this into `Sum`/`summation`
|
||||
if dom in (S.Integers, S.Naturals, S.Naturals0):
|
||||
dom = (dom.inf, dom.sup)
|
||||
expr = Sum(expr, (rv.pspace.symbol, dom))
|
||||
return expr
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.pdf(*args)
|
||||
945
venv/lib/python3.12/site-packages/sympy/stats/joint_rv_types.py
Normal file
945
venv/lib/python3.12/site-packages/sympy/stats/joint_rv_types.py
Normal file
|
|
@ -0,0 +1,945 @@
|
|||
from sympy.concrete.products import Product
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import (Integer, Rational, pi)
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.combinatorial.factorials import (rf, factorial)
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.bessel import besselk
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.matrices.dense import (Matrix, ones)
|
||||
from sympy.sets.fancysets import Range
|
||||
from sympy.sets.sets import (Intersection, Interval)
|
||||
from sympy.tensor.indexed import (Indexed, IndexedBase)
|
||||
from sympy.matrices import ImmutableMatrix, MatrixSymbol
|
||||
from sympy.matrices.expressions.determinant import det
|
||||
from sympy.matrices.expressions.matexpr import MatrixElement
|
||||
from sympy.stats.joint_rv import JointDistribution, JointPSpace, MarginalDistribution
|
||||
from sympy.stats.rv import _value_check, random_symbols
|
||||
|
||||
__all__ = ['JointRV',
|
||||
'MultivariateNormal',
|
||||
'MultivariateLaplace',
|
||||
'Dirichlet',
|
||||
'GeneralizedMultivariateLogGamma',
|
||||
'GeneralizedMultivariateLogGammaOmega',
|
||||
'Multinomial',
|
||||
'MultivariateBeta',
|
||||
'MultivariateEwens',
|
||||
'MultivariateT',
|
||||
'NegativeMultinomial',
|
||||
'NormalGamma'
|
||||
]
|
||||
|
||||
def multivariate_rv(cls, sym, *args):
|
||||
args = list(map(sympify, args))
|
||||
dist = cls(*args)
|
||||
args = dist.args
|
||||
dist.check(*args)
|
||||
return JointPSpace(sym, dist).value
|
||||
|
||||
|
||||
def marginal_distribution(rv, *indices):
|
||||
"""
|
||||
Marginal distribution function of a joint random variable.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
rv : A random variable with a joint probability distribution.
|
||||
indices : Component indices or the indexed random symbol
|
||||
for which the joint distribution is to be calculated
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
A Lambda expression in `sym`.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import MultivariateNormal, marginal_distribution
|
||||
>>> m = MultivariateNormal('X', [1, 2], [[2, 1], [1, 2]])
|
||||
>>> marginal_distribution(m, m[0])(1)
|
||||
1/(2*sqrt(pi))
|
||||
|
||||
"""
|
||||
indices = list(indices)
|
||||
for i in range(len(indices)):
|
||||
if isinstance(indices[i], Indexed):
|
||||
indices[i] = indices[i].args[1]
|
||||
prob_space = rv.pspace
|
||||
if not indices:
|
||||
raise ValueError(
|
||||
"At least one component for marginal density is needed.")
|
||||
if hasattr(prob_space.distribution, '_marginal_distribution'):
|
||||
return prob_space.distribution._marginal_distribution(indices, rv.symbol)
|
||||
return prob_space.marginal_distribution(*indices)
|
||||
|
||||
|
||||
class JointDistributionHandmade(JointDistribution):
|
||||
|
||||
_argnames = ('pdf',)
|
||||
is_Continuous = True
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return self.args[1]
|
||||
|
||||
|
||||
def JointRV(symbol, pdf, _set=None):
|
||||
"""
|
||||
Create a Joint Random Variable where each of its component is continuous,
|
||||
given the following:
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
symbol : Symbol
|
||||
Represents name of the random variable.
|
||||
pdf : A PDF in terms of indexed symbols of the symbol given
|
||||
as the first argument
|
||||
|
||||
NOTE
|
||||
====
|
||||
|
||||
As of now, the set for each component for a ``JointRV`` is
|
||||
equal to the set of all integers, which cannot be changed.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import exp, pi, Indexed, S
|
||||
>>> from sympy.stats import density, JointRV
|
||||
>>> x1, x2 = (Indexed('x', i) for i in (1, 2))
|
||||
>>> pdf = exp(-x1**2/2 + x1 - x2**2/2 - S(1)/2)/(2*pi)
|
||||
>>> N1 = JointRV('x', pdf) #Multivariate Normal distribution
|
||||
>>> density(N1)(1, 2)
|
||||
exp(-2)/(2*pi)
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
"""
|
||||
#TODO: Add support for sets provided by the user
|
||||
symbol = sympify(symbol)
|
||||
syms = [i for i in pdf.free_symbols if isinstance(i, Indexed)
|
||||
and i.base == IndexedBase(symbol)]
|
||||
syms = tuple(sorted(syms, key = lambda index: index.args[1]))
|
||||
_set = S.Reals**len(syms)
|
||||
pdf = Lambda(syms, pdf)
|
||||
dist = JointDistributionHandmade(pdf, _set)
|
||||
jrv = JointPSpace(symbol, dist).value
|
||||
rvs = random_symbols(pdf)
|
||||
if len(rvs) != 0:
|
||||
dist = MarginalDistribution(dist, (jrv,))
|
||||
return JointPSpace(symbol, dist).value
|
||||
return jrv
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Multivariate Normal distribution ---------------------------------------------
|
||||
|
||||
class MultivariateNormalDistribution(JointDistribution):
|
||||
_argnames = ('mu', 'sigma')
|
||||
|
||||
is_Continuous=True
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
k = self.mu.shape[0]
|
||||
return S.Reals**k
|
||||
|
||||
@staticmethod
|
||||
def check(mu, sigma):
|
||||
_value_check(mu.shape[0] == sigma.shape[0],
|
||||
"Size of the mean vector and covariance matrix are incorrect.")
|
||||
#check if covariance matrix is positive semi definite or not.
|
||||
if not isinstance(sigma, MatrixSymbol):
|
||||
_value_check(sigma.is_positive_semidefinite,
|
||||
"The covariance matrix must be positive semi definite. ")
|
||||
|
||||
def pdf(self, *args):
|
||||
mu, sigma = self.mu, self.sigma
|
||||
k = mu.shape[0]
|
||||
if len(args) == 1 and args[0].is_Matrix:
|
||||
args = args[0]
|
||||
else:
|
||||
args = ImmutableMatrix(args)
|
||||
x = args - mu
|
||||
density = S.One/sqrt((2*pi)**(k)*det(sigma))*exp(
|
||||
Rational(-1, 2)*x.transpose()*(sigma.inv()*x))
|
||||
return MatrixElement(density, 0, 0)
|
||||
|
||||
def _marginal_distribution(self, indices, sym):
|
||||
sym = ImmutableMatrix([Indexed(sym, i) for i in indices])
|
||||
_mu, _sigma = self.mu, self.sigma
|
||||
k = self.mu.shape[0]
|
||||
for i in range(k):
|
||||
if i not in indices:
|
||||
_mu = _mu.row_del(i)
|
||||
_sigma = _sigma.col_del(i)
|
||||
_sigma = _sigma.row_del(i)
|
||||
return Lambda(tuple(sym), S.One/sqrt((2*pi)**(len(_mu))*det(_sigma))*exp(
|
||||
Rational(-1, 2)*(_mu - sym).transpose()*(_sigma.inv()*\
|
||||
(_mu - sym)))[0])
|
||||
|
||||
def MultivariateNormal(name, mu, sigma):
|
||||
r"""
|
||||
Creates a continuous random variable with Multivariate Normal
|
||||
Distribution.
|
||||
|
||||
The density of the multivariate normal distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
mu : List representing the mean or the mean vector
|
||||
sigma : Positive semidefinite square matrix
|
||||
Represents covariance Matrix.
|
||||
If `\sigma` is noninvertible then only sampling is supported currently
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import MultivariateNormal, density, marginal_distribution
|
||||
>>> from sympy import symbols, MatrixSymbol
|
||||
>>> X = MultivariateNormal('X', [3, 4], [[2, 1], [1, 2]])
|
||||
>>> y, z = symbols('y z')
|
||||
>>> density(X)(y, z)
|
||||
sqrt(3)*exp(-y**2/3 + y*z/3 + 2*y/3 - z**2/3 + 5*z/3 - 13/3)/(6*pi)
|
||||
>>> density(X)(1, 2)
|
||||
sqrt(3)*exp(-4/3)/(6*pi)
|
||||
>>> marginal_distribution(X, X[1])(y)
|
||||
exp(-(y - 4)**2/4)/(2*sqrt(pi))
|
||||
>>> marginal_distribution(X, X[0])(y)
|
||||
exp(-(y - 3)**2/4)/(2*sqrt(pi))
|
||||
|
||||
The example below shows that it is also possible to use
|
||||
symbolic parameters to define the MultivariateNormal class.
|
||||
|
||||
>>> n = symbols('n', integer=True, positive=True)
|
||||
>>> Sg = MatrixSymbol('Sg', n, n)
|
||||
>>> mu = MatrixSymbol('mu', n, 1)
|
||||
>>> obs = MatrixSymbol('obs', n, 1)
|
||||
>>> X = MultivariateNormal('X', mu, Sg)
|
||||
|
||||
The density of a multivariate normal can be
|
||||
calculated using a matrix argument, as shown below.
|
||||
|
||||
>>> density(X)(obs)
|
||||
(exp(((1/2)*mu.T - (1/2)*obs.T)*Sg**(-1)*(-mu + obs))/sqrt((2*pi)**n*Determinant(Sg)))[0, 0]
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Multivariate_normal_distribution
|
||||
|
||||
"""
|
||||
return multivariate_rv(MultivariateNormalDistribution, name, mu, sigma)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Multivariate Laplace distribution --------------------------------------------
|
||||
|
||||
class MultivariateLaplaceDistribution(JointDistribution):
|
||||
_argnames = ('mu', 'sigma')
|
||||
is_Continuous=True
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
k = self.mu.shape[0]
|
||||
return S.Reals**k
|
||||
|
||||
@staticmethod
|
||||
def check(mu, sigma):
|
||||
_value_check(mu.shape[0] == sigma.shape[0],
|
||||
"Size of the mean vector and covariance matrix are incorrect.")
|
||||
# check if covariance matrix is positive definite or not.
|
||||
if not isinstance(sigma, MatrixSymbol):
|
||||
_value_check(sigma.is_positive_definite,
|
||||
"The covariance matrix must be positive definite. ")
|
||||
|
||||
def pdf(self, *args):
|
||||
mu, sigma = self.mu, self.sigma
|
||||
mu_T = mu.transpose()
|
||||
k = S(mu.shape[0])
|
||||
sigma_inv = sigma.inv()
|
||||
args = ImmutableMatrix(args)
|
||||
args_T = args.transpose()
|
||||
x = (mu_T*sigma_inv*mu)[0]
|
||||
y = (args_T*sigma_inv*args)[0]
|
||||
v = 1 - k/2
|
||||
return (2 * (y/(2 + x))**(v/2) * besselk(v, sqrt((2 + x)*y)) *
|
||||
exp((args_T * sigma_inv * mu)[0]) /
|
||||
((2 * pi)**(k/2) * sqrt(det(sigma))))
|
||||
|
||||
|
||||
def MultivariateLaplace(name, mu, sigma):
|
||||
"""
|
||||
Creates a continuous random variable with Multivariate Laplace
|
||||
Distribution.
|
||||
|
||||
The density of the multivariate Laplace distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
mu : List representing the mean or the mean vector
|
||||
sigma : Positive definite square matrix
|
||||
Represents covariance Matrix
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import MultivariateLaplace, density
|
||||
>>> from sympy import symbols
|
||||
>>> y, z = symbols('y z')
|
||||
>>> X = MultivariateLaplace('X', [2, 4], [[3, 1], [1, 3]])
|
||||
>>> density(X)(y, z)
|
||||
sqrt(2)*exp(y/4 + 5*z/4)*besselk(0, sqrt(15*y*(3*y/8 - z/8)/2 + 15*z*(-y/8 + 3*z/8)/2))/(4*pi)
|
||||
>>> density(X)(1, 2)
|
||||
sqrt(2)*exp(11/4)*besselk(0, sqrt(165)/4)/(4*pi)
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Multivariate_Laplace_distribution
|
||||
|
||||
"""
|
||||
return multivariate_rv(MultivariateLaplaceDistribution, name, mu, sigma)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Multivariate StudentT distribution -------------------------------------------
|
||||
|
||||
class MultivariateTDistribution(JointDistribution):
|
||||
_argnames = ('mu', 'shape_mat', 'dof')
|
||||
is_Continuous=True
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
k = self.mu.shape[0]
|
||||
return S.Reals**k
|
||||
|
||||
@staticmethod
|
||||
def check(mu, sigma, v):
|
||||
_value_check(mu.shape[0] == sigma.shape[0],
|
||||
"Size of the location vector and shape matrix are incorrect.")
|
||||
# check if covariance matrix is positive definite or not.
|
||||
if not isinstance(sigma, MatrixSymbol):
|
||||
_value_check(sigma.is_positive_definite,
|
||||
"The shape matrix must be positive definite. ")
|
||||
|
||||
def pdf(self, *args):
|
||||
mu, sigma = self.mu, self.shape_mat
|
||||
v = S(self.dof)
|
||||
k = S(mu.shape[0])
|
||||
sigma_inv = sigma.inv()
|
||||
args = ImmutableMatrix(args)
|
||||
x = args - mu
|
||||
return gamma((k + v)/2)/(gamma(v/2)*(v*pi)**(k/2)*sqrt(det(sigma)))\
|
||||
*(1 + 1/v*(x.transpose()*sigma_inv*x)[0])**((-v - k)/2)
|
||||
|
||||
def MultivariateT(syms, mu, sigma, v):
|
||||
"""
|
||||
Creates a joint random variable with multivariate T-distribution.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
syms : A symbol/str
|
||||
For identifying the random variable.
|
||||
mu : A list/matrix
|
||||
Representing the location vector
|
||||
sigma : The shape matrix for the distribution
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, MultivariateT
|
||||
>>> from sympy import Symbol
|
||||
|
||||
>>> x = Symbol("x")
|
||||
>>> X = MultivariateT("x", [1, 1], [[1, 0], [0, 1]], 2)
|
||||
|
||||
>>> density(X)(1, 2)
|
||||
2/(9*pi)
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
"""
|
||||
return multivariate_rv(MultivariateTDistribution, syms, mu, sigma, v)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Multivariate Normal Gamma distribution ---------------------------------------
|
||||
|
||||
class NormalGammaDistribution(JointDistribution):
|
||||
|
||||
_argnames = ('mu', 'lamda', 'alpha', 'beta')
|
||||
is_Continuous=True
|
||||
|
||||
@staticmethod
|
||||
def check(mu, lamda, alpha, beta):
|
||||
_value_check(mu.is_real, "Location must be real.")
|
||||
_value_check(lamda > 0, "Lambda must be positive")
|
||||
_value_check(alpha > 0, "alpha must be positive")
|
||||
_value_check(beta > 0, "beta must be positive")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return S.Reals*Interval(0, S.Infinity)
|
||||
|
||||
def pdf(self, x, tau):
|
||||
beta, alpha, lamda = self.beta, self.alpha, self.lamda
|
||||
mu = self.mu
|
||||
|
||||
return beta**alpha*sqrt(lamda)/(gamma(alpha)*sqrt(2*pi))*\
|
||||
tau**(alpha - S.Half)*exp(-1*beta*tau)*\
|
||||
exp(-1*(lamda*tau*(x - mu)**2)/S(2))
|
||||
|
||||
def _marginal_distribution(self, indices, *sym):
|
||||
if len(indices) == 2:
|
||||
return self.pdf(*sym)
|
||||
if indices[0] == 0:
|
||||
#For marginal over `x`, return non-standardized Student-T's
|
||||
#distribution
|
||||
x = sym[0]
|
||||
v, mu, sigma = self.alpha - S.Half, self.mu, \
|
||||
S(self.beta)/(self.lamda * self.alpha)
|
||||
return Lambda(sym, gamma((v + 1)/2)/(gamma(v/2)*sqrt(pi*v)*sigma)*\
|
||||
(1 + 1/v*((x - mu)/sigma)**2)**((-v -1)/2))
|
||||
#For marginal over `tau`, return Gamma distribution as per construction
|
||||
from sympy.stats.crv_types import GammaDistribution
|
||||
return Lambda(sym, GammaDistribution(self.alpha, self.beta)(sym[0]))
|
||||
|
||||
def NormalGamma(sym, mu, lamda, alpha, beta):
|
||||
"""
|
||||
Creates a bivariate joint random variable with multivariate Normal gamma
|
||||
distribution.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
sym : A symbol/str
|
||||
For identifying the random variable.
|
||||
mu : A real number
|
||||
The mean of the normal distribution
|
||||
lamda : A positive integer
|
||||
Parameter of joint distribution
|
||||
alpha : A positive integer
|
||||
Parameter of joint distribution
|
||||
beta : A positive integer
|
||||
Parameter of joint distribution
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, NormalGamma
|
||||
>>> from sympy import symbols
|
||||
|
||||
>>> X = NormalGamma('x', 0, 1, 2, 3)
|
||||
>>> y, z = symbols('y z')
|
||||
|
||||
>>> density(X)(y, z)
|
||||
9*sqrt(2)*z**(3/2)*exp(-3*z)*exp(-y**2*z/2)/(2*sqrt(pi))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Normal-gamma_distribution
|
||||
|
||||
"""
|
||||
return multivariate_rv(NormalGammaDistribution, sym, mu, lamda, alpha, beta)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Multivariate Beta/Dirichlet distribution -------------------------------------
|
||||
|
||||
class MultivariateBetaDistribution(JointDistribution):
|
||||
|
||||
_argnames = ('alpha',)
|
||||
is_Continuous = True
|
||||
|
||||
@staticmethod
|
||||
def check(alpha):
|
||||
_value_check(len(alpha) >= 2, "At least two categories should be passed.")
|
||||
for a_k in alpha:
|
||||
_value_check((a_k > 0) != False, "Each concentration parameter"
|
||||
" should be positive.")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
k = len(self.alpha)
|
||||
return Interval(0, 1)**k
|
||||
|
||||
def pdf(self, *syms):
|
||||
alpha = self.alpha
|
||||
B = Mul.fromiter(map(gamma, alpha))/gamma(Add(*alpha))
|
||||
return Mul.fromiter(sym**(a_k - 1) for a_k, sym in zip(alpha, syms))/B
|
||||
|
||||
def MultivariateBeta(syms, *alpha):
|
||||
"""
|
||||
Creates a continuous random variable with Dirichlet/Multivariate Beta
|
||||
Distribution.
|
||||
|
||||
The density of the Dirichlet distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
alpha : Positive real numbers
|
||||
Signifies concentration numbers.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, MultivariateBeta, marginal_distribution
|
||||
>>> from sympy import Symbol
|
||||
>>> a1 = Symbol('a1', positive=True)
|
||||
>>> a2 = Symbol('a2', positive=True)
|
||||
>>> B = MultivariateBeta('B', [a1, a2])
|
||||
>>> C = MultivariateBeta('C', a1, a2)
|
||||
>>> x = Symbol('x')
|
||||
>>> y = Symbol('y')
|
||||
>>> density(B)(x, y)
|
||||
x**(a1 - 1)*y**(a2 - 1)*gamma(a1 + a2)/(gamma(a1)*gamma(a2))
|
||||
>>> marginal_distribution(C, C[0])(x)
|
||||
x**(a1 - 1)*gamma(a1 + a2)/(a2*gamma(a1)*gamma(a2))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Dirichlet_distribution
|
||||
.. [2] https://mathworld.wolfram.com/DirichletDistribution.html
|
||||
|
||||
"""
|
||||
if not isinstance(alpha[0], list):
|
||||
alpha = (list(alpha),)
|
||||
return multivariate_rv(MultivariateBetaDistribution, syms, alpha[0])
|
||||
|
||||
Dirichlet = MultivariateBeta
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Multivariate Ewens distribution ----------------------------------------------
|
||||
|
||||
class MultivariateEwensDistribution(JointDistribution):
|
||||
|
||||
_argnames = ('n', 'theta')
|
||||
is_Discrete = True
|
||||
is_Continuous = False
|
||||
|
||||
@staticmethod
|
||||
def check(n, theta):
|
||||
_value_check((n > 0),
|
||||
"sample size should be positive integer.")
|
||||
_value_check(theta.is_positive, "mutation rate should be positive.")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
if not isinstance(self.n, Integer):
|
||||
i = Symbol('i', integer=True, positive=True)
|
||||
return Product(Intersection(S.Naturals0, Interval(0, self.n//i)),
|
||||
(i, 1, self.n))
|
||||
prod_set = Range(0, self.n + 1)
|
||||
for i in range(2, self.n + 1):
|
||||
prod_set *= Range(0, self.n//i + 1)
|
||||
return prod_set.flatten()
|
||||
|
||||
def pdf(self, *syms):
|
||||
n, theta = self.n, self.theta
|
||||
condi = isinstance(self.n, Integer)
|
||||
if not (isinstance(syms[0], IndexedBase) or condi):
|
||||
raise ValueError("Please use IndexedBase object for syms as "
|
||||
"the dimension is symbolic")
|
||||
term_1 = factorial(n)/rf(theta, n)
|
||||
if condi:
|
||||
term_2 = Mul.fromiter(theta**syms[j]/((j+1)**syms[j]*factorial(syms[j]))
|
||||
for j in range(n))
|
||||
cond = Eq(sum((k + 1)*syms[k] for k in range(n)), n)
|
||||
return Piecewise((term_1 * term_2, cond), (0, True))
|
||||
syms = syms[0]
|
||||
j, k = symbols('j, k', positive=True, integer=True)
|
||||
term_2 = Product(theta**syms[j]/((j+1)**syms[j]*factorial(syms[j])),
|
||||
(j, 0, n - 1))
|
||||
cond = Eq(Sum((k + 1)*syms[k], (k, 0, n - 1)), n)
|
||||
return Piecewise((term_1 * term_2, cond), (0, True))
|
||||
|
||||
|
||||
def MultivariateEwens(syms, n, theta):
|
||||
"""
|
||||
Creates a discrete random variable with Multivariate Ewens
|
||||
Distribution.
|
||||
|
||||
The density of the said distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n : Positive integer
|
||||
Size of the sample or the integer whose partitions are considered
|
||||
theta : Positive real number
|
||||
Denotes Mutation rate
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, marginal_distribution, MultivariateEwens
|
||||
>>> from sympy import Symbol
|
||||
>>> a1 = Symbol('a1', positive=True)
|
||||
>>> a2 = Symbol('a2', positive=True)
|
||||
>>> ed = MultivariateEwens('E', 2, 1)
|
||||
>>> density(ed)(a1, a2)
|
||||
Piecewise((1/(2**a2*factorial(a1)*factorial(a2)), Eq(a1 + 2*a2, 2)), (0, True))
|
||||
>>> marginal_distribution(ed, ed[0])(a1)
|
||||
Piecewise((1/factorial(a1), Eq(a1, 2)), (0, True))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Ewens%27s_sampling_formula
|
||||
.. [2] https://www.jstor.org/stable/24780825
|
||||
"""
|
||||
return multivariate_rv(MultivariateEwensDistribution, syms, n, theta)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Generalized Multivariate Log Gamma distribution ------------------------------
|
||||
|
||||
class GeneralizedMultivariateLogGammaDistribution(JointDistribution):
|
||||
|
||||
_argnames = ('delta', 'v', 'lamda', 'mu')
|
||||
is_Continuous=True
|
||||
|
||||
def check(self, delta, v, l, mu):
|
||||
_value_check((delta >= 0, delta <= 1), "delta must be in range [0, 1].")
|
||||
_value_check((v > 0), "v must be positive")
|
||||
for lk in l:
|
||||
_value_check((lk > 0), "lamda must be a positive vector.")
|
||||
for muk in mu:
|
||||
_value_check((muk > 0), "mu must be a positive vector.")
|
||||
_value_check(len(l) > 1,"the distribution should have at least"
|
||||
" two random variables.")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return S.Reals**len(self.lamda)
|
||||
|
||||
def pdf(self, *y):
|
||||
d, v, l, mu = self.delta, self.v, self.lamda, self.mu
|
||||
n = Symbol('n', negative=False, integer=True)
|
||||
k = len(l)
|
||||
sterm1 = Pow((1 - d), n)/\
|
||||
((gamma(v + n)**(k - 1))*gamma(v)*gamma(n + 1))
|
||||
sterm2 = Mul.fromiter(mui*li**(-v - n) for mui, li in zip(mu, l))
|
||||
term1 = sterm1 * sterm2
|
||||
sterm3 = (v + n) * sum(mui * yi for mui, yi in zip(mu, y))
|
||||
sterm4 = sum(exp(mui * yi)/li for (mui, yi, li) in zip(mu, y, l))
|
||||
term2 = exp(sterm3 - sterm4)
|
||||
return Pow(d, v) * Sum(term1 * term2, (n, 0, S.Infinity))
|
||||
|
||||
def GeneralizedMultivariateLogGamma(syms, delta, v, lamda, mu):
|
||||
"""
|
||||
Creates a joint random variable with generalized multivariate log gamma
|
||||
distribution.
|
||||
|
||||
The joint pdf can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
syms : list/tuple/set of symbols for identifying each component
|
||||
delta : A constant in range $[0, 1]$
|
||||
v : Positive real number
|
||||
lamda : List of positive real numbers
|
||||
mu : List of positive real numbers
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density
|
||||
>>> from sympy.stats.joint_rv_types import GeneralizedMultivariateLogGamma
|
||||
>>> from sympy import symbols, S
|
||||
>>> v = 1
|
||||
>>> l, mu = [1, 1, 1], [1, 1, 1]
|
||||
>>> d = S.Half
|
||||
>>> y = symbols('y_1:4', positive=True)
|
||||
>>> Gd = GeneralizedMultivariateLogGamma('G', d, v, l, mu)
|
||||
>>> density(Gd)(y[0], y[1], y[2])
|
||||
Sum(exp((n + 1)*(y_1 + y_2 + y_3) - exp(y_1) - exp(y_2) -
|
||||
exp(y_3))/(2**n*gamma(n + 1)**3), (n, 0, oo))/2
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Generalized_multivariate_log-gamma_distribution
|
||||
.. [2] https://www.researchgate.net/publication/234137346_On_a_multivariate_log-gamma_distribution_and_the_use_of_the_distribution_in_the_Bayesian_analysis
|
||||
|
||||
Note
|
||||
====
|
||||
|
||||
If the GeneralizedMultivariateLogGamma is too long to type use,
|
||||
|
||||
>>> from sympy.stats.joint_rv_types import GeneralizedMultivariateLogGamma as GMVLG
|
||||
>>> Gd = GMVLG('G', d, v, l, mu)
|
||||
|
||||
If you want to pass the matrix omega instead of the constant delta, then use
|
||||
``GeneralizedMultivariateLogGammaOmega``.
|
||||
|
||||
"""
|
||||
return multivariate_rv(GeneralizedMultivariateLogGammaDistribution,
|
||||
syms, delta, v, lamda, mu)
|
||||
|
||||
def GeneralizedMultivariateLogGammaOmega(syms, omega, v, lamda, mu):
|
||||
"""
|
||||
Extends GeneralizedMultivariateLogGamma.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
syms : list/tuple/set of symbols
|
||||
For identifying each component
|
||||
omega : A square matrix
|
||||
Every element of square matrix must be absolute value of
|
||||
square root of correlation coefficient
|
||||
v : Positive real number
|
||||
lamda : List of positive real numbers
|
||||
mu : List of positive real numbers
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density
|
||||
>>> from sympy.stats.joint_rv_types import GeneralizedMultivariateLogGammaOmega
|
||||
>>> from sympy import Matrix, symbols, S
|
||||
>>> omega = Matrix([[1, S.Half, S.Half], [S.Half, 1, S.Half], [S.Half, S.Half, 1]])
|
||||
>>> v = 1
|
||||
>>> l, mu = [1, 1, 1], [1, 1, 1]
|
||||
>>> G = GeneralizedMultivariateLogGammaOmega('G', omega, v, l, mu)
|
||||
>>> y = symbols('y_1:4', positive=True)
|
||||
>>> density(G)(y[0], y[1], y[2])
|
||||
sqrt(2)*Sum((1 - sqrt(2)/2)**n*exp((n + 1)*(y_1 + y_2 + y_3) - exp(y_1) -
|
||||
exp(y_2) - exp(y_3))/gamma(n + 1)**3, (n, 0, oo))/2
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Generalized_multivariate_log-gamma_distribution
|
||||
.. [2] https://www.researchgate.net/publication/234137346_On_a_multivariate_log-gamma_distribution_and_the_use_of_the_distribution_in_the_Bayesian_analysis
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
If the GeneralizedMultivariateLogGammaOmega is too long to type use,
|
||||
|
||||
>>> from sympy.stats.joint_rv_types import GeneralizedMultivariateLogGammaOmega as GMVLGO
|
||||
>>> G = GMVLGO('G', omega, v, l, mu)
|
||||
|
||||
"""
|
||||
_value_check((omega.is_square, isinstance(omega, Matrix)), "omega must be a"
|
||||
" square matrix")
|
||||
for val in omega.values():
|
||||
_value_check((val >= 0, val <= 1),
|
||||
"all values in matrix must be between 0 and 1(both inclusive).")
|
||||
_value_check(omega.diagonal().equals(ones(1, omega.shape[0])),
|
||||
"all the elements of diagonal should be 1.")
|
||||
_value_check((omega.shape[0] == len(lamda), len(lamda) == len(mu)),
|
||||
"lamda, mu should be of same length and omega should "
|
||||
" be of shape (length of lamda, length of mu)")
|
||||
_value_check(len(lamda) > 1,"the distribution should have at least"
|
||||
" two random variables.")
|
||||
delta = Pow(Rational(omega.det()), Rational(1, len(lamda) - 1))
|
||||
return GeneralizedMultivariateLogGamma(syms, delta, v, lamda, mu)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Multinomial distribution -----------------------------------------------------
|
||||
|
||||
class MultinomialDistribution(JointDistribution):
|
||||
|
||||
_argnames = ('n', 'p')
|
||||
is_Continuous=False
|
||||
is_Discrete = True
|
||||
|
||||
@staticmethod
|
||||
def check(n, p):
|
||||
_value_check(n > 0,
|
||||
"number of trials must be a positive integer")
|
||||
for p_k in p:
|
||||
_value_check((p_k >= 0, p_k <= 1),
|
||||
"probability must be in range [0, 1]")
|
||||
_value_check(Eq(sum(p), 1),
|
||||
"probabilities must sum to 1")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return Intersection(S.Naturals0, Interval(0, self.n))**len(self.p)
|
||||
|
||||
def pdf(self, *x):
|
||||
n, p = self.n, self.p
|
||||
term_1 = factorial(n)/Mul.fromiter(factorial(x_k) for x_k in x)
|
||||
term_2 = Mul.fromiter(p_k**x_k for p_k, x_k in zip(p, x))
|
||||
return Piecewise((term_1 * term_2, Eq(sum(x), n)), (0, True))
|
||||
|
||||
def Multinomial(syms, n, *p):
|
||||
"""
|
||||
Creates a discrete random variable with Multinomial Distribution.
|
||||
|
||||
The density of the said distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n : Positive integer
|
||||
Represents number of trials
|
||||
p : List of event probabilities
|
||||
Must be in the range of $[0, 1]$.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, Multinomial, marginal_distribution
|
||||
>>> from sympy import symbols
|
||||
>>> x1, x2, x3 = symbols('x1, x2, x3', nonnegative=True, integer=True)
|
||||
>>> p1, p2, p3 = symbols('p1, p2, p3', positive=True)
|
||||
>>> M = Multinomial('M', 3, p1, p2, p3)
|
||||
>>> density(M)(x1, x2, x3)
|
||||
Piecewise((6*p1**x1*p2**x2*p3**x3/(factorial(x1)*factorial(x2)*factorial(x3)),
|
||||
Eq(x1 + x2 + x3, 3)), (0, True))
|
||||
>>> marginal_distribution(M, M[0])(x1).subs(x1, 1)
|
||||
3*p1*p2**2 + 6*p1*p2*p3 + 3*p1*p3**2
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Multinomial_distribution
|
||||
.. [2] https://mathworld.wolfram.com/MultinomialDistribution.html
|
||||
|
||||
"""
|
||||
if not isinstance(p[0], list):
|
||||
p = (list(p), )
|
||||
return multivariate_rv(MultinomialDistribution, syms, n, p[0])
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Negative Multinomial Distribution --------------------------------------------
|
||||
|
||||
class NegativeMultinomialDistribution(JointDistribution):
|
||||
|
||||
_argnames = ('k0', 'p')
|
||||
is_Continuous=False
|
||||
is_Discrete = True
|
||||
|
||||
@staticmethod
|
||||
def check(k0, p):
|
||||
_value_check(k0 > 0,
|
||||
"number of failures must be a positive integer")
|
||||
for p_k in p:
|
||||
_value_check((p_k >= 0, p_k <= 1),
|
||||
"probability must be in range [0, 1].")
|
||||
_value_check(sum(p) <= 1,
|
||||
"success probabilities must not be greater than 1.")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
return Range(0, S.Infinity)**len(self.p)
|
||||
|
||||
def pdf(self, *k):
|
||||
k0, p = self.k0, self.p
|
||||
term_1 = (gamma(k0 + sum(k))*(1 - sum(p))**k0)/gamma(k0)
|
||||
term_2 = Mul.fromiter(pi**ki/factorial(ki) for pi, ki in zip(p, k))
|
||||
return term_1 * term_2
|
||||
|
||||
def NegativeMultinomial(syms, k0, *p):
|
||||
"""
|
||||
Creates a discrete random variable with Negative Multinomial Distribution.
|
||||
|
||||
The density of the said distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
k0 : positive integer
|
||||
Represents number of failures before the experiment is stopped
|
||||
p : List of event probabilities
|
||||
Must be in the range of $[0, 1]$
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, NegativeMultinomial, marginal_distribution
|
||||
>>> from sympy import symbols
|
||||
>>> x1, x2, x3 = symbols('x1, x2, x3', nonnegative=True, integer=True)
|
||||
>>> p1, p2, p3 = symbols('p1, p2, p3', positive=True)
|
||||
>>> N = NegativeMultinomial('M', 3, p1, p2, p3)
|
||||
>>> N_c = NegativeMultinomial('M', 3, 0.1, 0.1, 0.1)
|
||||
>>> density(N)(x1, x2, x3)
|
||||
p1**x1*p2**x2*p3**x3*(-p1 - p2 - p3 + 1)**3*gamma(x1 + x2 +
|
||||
x3 + 3)/(2*factorial(x1)*factorial(x2)*factorial(x3))
|
||||
>>> marginal_distribution(N_c, N_c[0])(1).evalf().round(2)
|
||||
0.25
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Negative_multinomial_distribution
|
||||
.. [2] https://mathworld.wolfram.com/NegativeBinomialDistribution.html
|
||||
|
||||
"""
|
||||
if not isinstance(p[0], list):
|
||||
p = (list(p), )
|
||||
return multivariate_rv(NegativeMultinomialDistribution, syms, k0, p[0])
|
||||
|
|
@ -0,0 +1,610 @@
|
|||
from math import prod
|
||||
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.singleton import S
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.special.gamma_functions import multigamma
|
||||
from sympy.core.sympify import sympify, _sympify
|
||||
from sympy.matrices import (ImmutableMatrix, Inverse, Trace, Determinant,
|
||||
MatrixSymbol, MatrixBase, Transpose, MatrixSet,
|
||||
matrix2numpy)
|
||||
from sympy.stats.rv import (_value_check, RandomMatrixSymbol, NamedArgsMixin, PSpace,
|
||||
_symbol_converter, MatrixDomain, Distribution)
|
||||
from sympy.external import import_module
|
||||
|
||||
|
||||
################################################################################
|
||||
#------------------------Matrix Probability Space------------------------------#
|
||||
################################################################################
|
||||
class MatrixPSpace(PSpace):
|
||||
"""
|
||||
Represents probability space for
|
||||
Matrix Distributions.
|
||||
"""
|
||||
def __new__(cls, sym, distribution, dim_n, dim_m):
|
||||
sym = _symbol_converter(sym)
|
||||
dim_n, dim_m = _sympify(dim_n), _sympify(dim_m)
|
||||
if not (dim_n.is_integer and dim_m.is_integer):
|
||||
raise ValueError("Dimensions should be integers")
|
||||
return Basic.__new__(cls, sym, distribution, dim_n, dim_m)
|
||||
|
||||
distribution = property(lambda self: self.args[1])
|
||||
symbol = property(lambda self: self.args[0])
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return MatrixDomain(self.symbol, self.distribution.set)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return RandomMatrixSymbol(self.symbol, self.args[2], self.args[3], self)
|
||||
|
||||
@property
|
||||
def values(self):
|
||||
return {self.value}
|
||||
|
||||
def compute_density(self, expr, *args):
|
||||
rms = expr.atoms(RandomMatrixSymbol)
|
||||
if len(rms) > 1 or (not isinstance(expr, RandomMatrixSymbol)):
|
||||
raise NotImplementedError("Currently, no algorithm has been "
|
||||
"implemented to handle general expressions containing "
|
||||
"multiple matrix distributions.")
|
||||
return self.distribution.pdf(expr)
|
||||
|
||||
def sample(self, size=(), library='scipy', seed=None):
|
||||
"""
|
||||
Internal sample method
|
||||
|
||||
Returns dictionary mapping RandomMatrixSymbol to realization value.
|
||||
"""
|
||||
return {self.value: self.distribution.sample(size, library=library, seed=seed)}
|
||||
|
||||
|
||||
def rv(symbol, cls, args):
|
||||
args = list(map(sympify, args))
|
||||
dist = cls(*args)
|
||||
dist.check(*args)
|
||||
dim = dist.dimension
|
||||
pspace = MatrixPSpace(symbol, dist, dim[0], dim[1])
|
||||
return pspace.value
|
||||
|
||||
|
||||
class SampleMatrixScipy:
|
||||
"""Returns the sample from scipy of the given distribution"""
|
||||
def __new__(cls, dist, size, seed=None):
|
||||
return cls._sample_scipy(dist, size, seed)
|
||||
|
||||
@classmethod
|
||||
def _sample_scipy(cls, dist, size, seed):
|
||||
"""Sample from SciPy."""
|
||||
|
||||
from scipy import stats as scipy_stats
|
||||
import numpy
|
||||
scipy_rv_map = {
|
||||
'WishartDistribution': lambda dist, size, rand_state: scipy_stats.wishart.rvs(
|
||||
df=int(dist.n), scale=matrix2numpy(dist.scale_matrix, float), size=size),
|
||||
'MatrixNormalDistribution': lambda dist, size, rand_state: scipy_stats.matrix_normal.rvs(
|
||||
mean=matrix2numpy(dist.location_matrix, float),
|
||||
rowcov=matrix2numpy(dist.scale_matrix_1, float),
|
||||
colcov=matrix2numpy(dist.scale_matrix_2, float), size=size, random_state=rand_state)
|
||||
}
|
||||
|
||||
sample_shape = {
|
||||
'WishartDistribution': lambda dist: dist.scale_matrix.shape,
|
||||
'MatrixNormalDistribution' : lambda dist: dist.location_matrix.shape
|
||||
}
|
||||
|
||||
dist_list = scipy_rv_map.keys()
|
||||
|
||||
if dist.__class__.__name__ not in dist_list:
|
||||
return None
|
||||
|
||||
if seed is None or isinstance(seed, int):
|
||||
rand_state = numpy.random.default_rng(seed=seed)
|
||||
else:
|
||||
rand_state = seed
|
||||
samp = scipy_rv_map[dist.__class__.__name__](dist, prod(size), rand_state)
|
||||
return samp.reshape(size + sample_shape[dist.__class__.__name__](dist))
|
||||
|
||||
|
||||
class SampleMatrixNumpy:
|
||||
"""Returns the sample from numpy of the given distribution"""
|
||||
|
||||
### TODO: Add tests after adding matrix distributions in numpy_rv_map
|
||||
def __new__(cls, dist, size, seed=None):
|
||||
return cls._sample_numpy(dist, size, seed)
|
||||
|
||||
@classmethod
|
||||
def _sample_numpy(cls, dist, size, seed):
|
||||
"""Sample from NumPy."""
|
||||
|
||||
numpy_rv_map = {
|
||||
}
|
||||
|
||||
sample_shape = {
|
||||
}
|
||||
|
||||
dist_list = numpy_rv_map.keys()
|
||||
|
||||
if dist.__class__.__name__ not in dist_list:
|
||||
return None
|
||||
|
||||
import numpy
|
||||
if seed is None or isinstance(seed, int):
|
||||
rand_state = numpy.random.default_rng(seed=seed)
|
||||
else:
|
||||
rand_state = seed
|
||||
samp = numpy_rv_map[dist.__class__.__name__](dist, prod(size), rand_state)
|
||||
return samp.reshape(size + sample_shape[dist.__class__.__name__](dist))
|
||||
|
||||
|
||||
class SampleMatrixPymc:
|
||||
"""Returns the sample from pymc of the given distribution"""
|
||||
|
||||
def __new__(cls, dist, size, seed=None):
|
||||
return cls._sample_pymc(dist, size, seed)
|
||||
|
||||
@classmethod
|
||||
def _sample_pymc(cls, dist, size, seed):
|
||||
"""Sample from PyMC."""
|
||||
|
||||
try:
|
||||
import pymc
|
||||
except ImportError:
|
||||
import pymc3 as pymc
|
||||
pymc_rv_map = {
|
||||
'MatrixNormalDistribution': lambda dist: pymc.MatrixNormal('X',
|
||||
mu=matrix2numpy(dist.location_matrix, float),
|
||||
rowcov=matrix2numpy(dist.scale_matrix_1, float),
|
||||
colcov=matrix2numpy(dist.scale_matrix_2, float),
|
||||
shape=dist.location_matrix.shape),
|
||||
'WishartDistribution': lambda dist: pymc.WishartBartlett('X',
|
||||
nu=int(dist.n), S=matrix2numpy(dist.scale_matrix, float))
|
||||
}
|
||||
|
||||
sample_shape = {
|
||||
'WishartDistribution': lambda dist: dist.scale_matrix.shape,
|
||||
'MatrixNormalDistribution' : lambda dist: dist.location_matrix.shape
|
||||
}
|
||||
|
||||
dist_list = pymc_rv_map.keys()
|
||||
|
||||
if dist.__class__.__name__ not in dist_list:
|
||||
return None
|
||||
import logging
|
||||
logging.getLogger("pymc").setLevel(logging.ERROR)
|
||||
with pymc.Model():
|
||||
pymc_rv_map[dist.__class__.__name__](dist)
|
||||
samps = pymc.sample(draws=prod(size), chains=1, progressbar=False, random_seed=seed, return_inferencedata=False, compute_convergence_checks=False)['X']
|
||||
return samps.reshape(size + sample_shape[dist.__class__.__name__](dist))
|
||||
|
||||
_get_sample_class_matrixrv = {
|
||||
'scipy': SampleMatrixScipy,
|
||||
'pymc3': SampleMatrixPymc,
|
||||
'pymc': SampleMatrixPymc,
|
||||
'numpy': SampleMatrixNumpy
|
||||
}
|
||||
|
||||
################################################################################
|
||||
#-------------------------Matrix Distribution----------------------------------#
|
||||
################################################################################
|
||||
|
||||
class MatrixDistribution(Distribution, NamedArgsMixin):
|
||||
"""
|
||||
Abstract class for Matrix Distribution.
|
||||
"""
|
||||
def __new__(cls, *args):
|
||||
args = [ImmutableMatrix(arg) if isinstance(arg, list)
|
||||
else _sympify(arg) for arg in args]
|
||||
return Basic.__new__(cls, *args)
|
||||
|
||||
@staticmethod
|
||||
def check(*args):
|
||||
pass
|
||||
|
||||
def __call__(self, expr):
|
||||
if isinstance(expr, list):
|
||||
expr = ImmutableMatrix(expr)
|
||||
return self.pdf(expr)
|
||||
|
||||
def sample(self, size=(), library='scipy', seed=None):
|
||||
"""
|
||||
Internal sample method
|
||||
|
||||
Returns dictionary mapping RandomSymbol to realization value.
|
||||
"""
|
||||
|
||||
libraries = ['scipy', 'numpy', 'pymc3', 'pymc']
|
||||
if library not in libraries:
|
||||
raise NotImplementedError("Sampling from %s is not supported yet."
|
||||
% str(library))
|
||||
if not import_module(library):
|
||||
raise ValueError("Failed to import %s" % library)
|
||||
|
||||
samps = _get_sample_class_matrixrv[library](self, size, seed)
|
||||
|
||||
if samps is not None:
|
||||
return samps
|
||||
raise NotImplementedError(
|
||||
"Sampling for %s is not currently implemented from %s"
|
||||
% (self.__class__.__name__, library)
|
||||
)
|
||||
|
||||
################################################################################
|
||||
#------------------------Matrix Distribution Types-----------------------------#
|
||||
################################################################################
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Matrix Gamma distribution ----------------------------------------------------
|
||||
|
||||
class MatrixGammaDistribution(MatrixDistribution):
|
||||
|
||||
_argnames = ('alpha', 'beta', 'scale_matrix')
|
||||
|
||||
@staticmethod
|
||||
def check(alpha, beta, scale_matrix):
|
||||
if not isinstance(scale_matrix, MatrixSymbol):
|
||||
_value_check(scale_matrix.is_positive_definite, "The shape "
|
||||
"matrix must be positive definite.")
|
||||
_value_check(scale_matrix.is_square, "Should "
|
||||
"be square matrix")
|
||||
_value_check(alpha.is_positive, "Shape parameter should be positive.")
|
||||
_value_check(beta.is_positive, "Scale parameter should be positive.")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
k = self.scale_matrix.shape[0]
|
||||
return MatrixSet(k, k, S.Reals)
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
return self.scale_matrix.shape
|
||||
|
||||
def pdf(self, x):
|
||||
alpha, beta, scale_matrix = self.alpha, self.beta, self.scale_matrix
|
||||
p = scale_matrix.shape[0]
|
||||
if isinstance(x, list):
|
||||
x = ImmutableMatrix(x)
|
||||
if not isinstance(x, (MatrixBase, MatrixSymbol)):
|
||||
raise ValueError("%s should be an isinstance of Matrix "
|
||||
"or MatrixSymbol" % str(x))
|
||||
sigma_inv_x = - Inverse(scale_matrix)*x / beta
|
||||
term1 = exp(Trace(sigma_inv_x))/((beta**(p*alpha)) * multigamma(alpha, p))
|
||||
term2 = (Determinant(scale_matrix))**(-alpha)
|
||||
term3 = (Determinant(x))**(alpha - S(p + 1)/2)
|
||||
return term1 * term2 * term3
|
||||
|
||||
def MatrixGamma(symbol, alpha, beta, scale_matrix):
|
||||
"""
|
||||
Creates a random variable with Matrix Gamma Distribution.
|
||||
|
||||
The density of the said distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
alpha: Positive Real number
|
||||
Shape Parameter
|
||||
beta: Positive Real number
|
||||
Scale Parameter
|
||||
scale_matrix: Positive definite real square matrix
|
||||
Scale Matrix
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, MatrixGamma
|
||||
>>> from sympy import MatrixSymbol, symbols
|
||||
>>> a, b = symbols('a b', positive=True)
|
||||
>>> M = MatrixGamma('M', a, b, [[2, 1], [1, 2]])
|
||||
>>> X = MatrixSymbol('X', 2, 2)
|
||||
>>> density(M)(X).doit()
|
||||
exp(Trace(Matrix([
|
||||
[-2/3, 1/3],
|
||||
[ 1/3, -2/3]])*X)/b)*Determinant(X)**(a - 3/2)/(3**a*sqrt(pi)*b**(2*a)*gamma(a)*gamma(a - 1/2))
|
||||
>>> density(M)([[1, 0], [0, 1]]).doit()
|
||||
exp(-4/(3*b))/(3**a*sqrt(pi)*b**(2*a)*gamma(a)*gamma(a - 1/2))
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Matrix_gamma_distribution
|
||||
|
||||
"""
|
||||
if isinstance(scale_matrix, list):
|
||||
scale_matrix = ImmutableMatrix(scale_matrix)
|
||||
return rv(symbol, MatrixGammaDistribution, (alpha, beta, scale_matrix))
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Wishart Distribution ---------------------------------------------------------
|
||||
|
||||
class WishartDistribution(MatrixDistribution):
|
||||
|
||||
_argnames = ('n', 'scale_matrix')
|
||||
|
||||
@staticmethod
|
||||
def check(n, scale_matrix):
|
||||
if not isinstance(scale_matrix, MatrixSymbol):
|
||||
_value_check(scale_matrix.is_positive_definite, "The shape "
|
||||
"matrix must be positive definite.")
|
||||
_value_check(scale_matrix.is_square, "Should "
|
||||
"be square matrix")
|
||||
_value_check(n.is_positive, "Shape parameter should be positive.")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
k = self.scale_matrix.shape[0]
|
||||
return MatrixSet(k, k, S.Reals)
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
return self.scale_matrix.shape
|
||||
|
||||
def pdf(self, x):
|
||||
n, scale_matrix = self.n, self.scale_matrix
|
||||
p = scale_matrix.shape[0]
|
||||
if isinstance(x, list):
|
||||
x = ImmutableMatrix(x)
|
||||
if not isinstance(x, (MatrixBase, MatrixSymbol)):
|
||||
raise ValueError("%s should be an isinstance of Matrix "
|
||||
"or MatrixSymbol" % str(x))
|
||||
sigma_inv_x = - Inverse(scale_matrix)*x / S(2)
|
||||
term1 = exp(Trace(sigma_inv_x))/((2**(p*n/S(2))) * multigamma(n/S(2), p))
|
||||
term2 = (Determinant(scale_matrix))**(-n/S(2))
|
||||
term3 = (Determinant(x))**(S(n - p - 1)/2)
|
||||
return term1 * term2 * term3
|
||||
|
||||
def Wishart(symbol, n, scale_matrix):
|
||||
"""
|
||||
Creates a random variable with Wishart Distribution.
|
||||
|
||||
The density of the said distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n: Positive Real number
|
||||
Represents degrees of freedom
|
||||
scale_matrix: Positive definite real square matrix
|
||||
Scale Matrix
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import density, Wishart
|
||||
>>> from sympy import MatrixSymbol, symbols
|
||||
>>> n = symbols('n', positive=True)
|
||||
>>> W = Wishart('W', n, [[2, 1], [1, 2]])
|
||||
>>> X = MatrixSymbol('X', 2, 2)
|
||||
>>> density(W)(X).doit()
|
||||
exp(Trace(Matrix([
|
||||
[-1/3, 1/6],
|
||||
[ 1/6, -1/3]])*X))*Determinant(X)**(n/2 - 3/2)/(2**n*3**(n/2)*sqrt(pi)*gamma(n/2)*gamma(n/2 - 1/2))
|
||||
>>> density(W)([[1, 0], [0, 1]]).doit()
|
||||
exp(-2/3)/(2**n*3**(n/2)*sqrt(pi)*gamma(n/2)*gamma(n/2 - 1/2))
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Wishart_distribution
|
||||
|
||||
"""
|
||||
if isinstance(scale_matrix, list):
|
||||
scale_matrix = ImmutableMatrix(scale_matrix)
|
||||
return rv(symbol, WishartDistribution, (n, scale_matrix))
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Matrix Normal distribution ---------------------------------------------------
|
||||
|
||||
class MatrixNormalDistribution(MatrixDistribution):
|
||||
|
||||
_argnames = ('location_matrix', 'scale_matrix_1', 'scale_matrix_2')
|
||||
|
||||
@staticmethod
|
||||
def check(location_matrix, scale_matrix_1, scale_matrix_2):
|
||||
if not isinstance(scale_matrix_1, MatrixSymbol):
|
||||
_value_check(scale_matrix_1.is_positive_definite, "The shape "
|
||||
"matrix must be positive definite.")
|
||||
if not isinstance(scale_matrix_2, MatrixSymbol):
|
||||
_value_check(scale_matrix_2.is_positive_definite, "The shape "
|
||||
"matrix must be positive definite.")
|
||||
_value_check(scale_matrix_1.is_square, "Scale matrix 1 should be "
|
||||
"be square matrix")
|
||||
_value_check(scale_matrix_2.is_square, "Scale matrix 2 should be "
|
||||
"be square matrix")
|
||||
n = location_matrix.shape[0]
|
||||
p = location_matrix.shape[1]
|
||||
_value_check(scale_matrix_1.shape[0] == n, "Scale matrix 1 should be"
|
||||
" of shape %s x %s"% (str(n), str(n)))
|
||||
_value_check(scale_matrix_2.shape[0] == p, "Scale matrix 2 should be"
|
||||
" of shape %s x %s"% (str(p), str(p)))
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
n, p = self.location_matrix.shape
|
||||
return MatrixSet(n, p, S.Reals)
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
return self.location_matrix.shape
|
||||
|
||||
def pdf(self, x):
|
||||
M, U, V = self.location_matrix, self.scale_matrix_1, self.scale_matrix_2
|
||||
n, p = M.shape
|
||||
if isinstance(x, list):
|
||||
x = ImmutableMatrix(x)
|
||||
if not isinstance(x, (MatrixBase, MatrixSymbol)):
|
||||
raise ValueError("%s should be an isinstance of Matrix "
|
||||
"or MatrixSymbol" % str(x))
|
||||
term1 = Inverse(V)*Transpose(x - M)*Inverse(U)*(x - M)
|
||||
num = exp(-Trace(term1)/S(2))
|
||||
den = (2*pi)**(S(n*p)/2) * Determinant(U)**(S(p)/2) * Determinant(V)**(S(n)/2)
|
||||
return num/den
|
||||
|
||||
def MatrixNormal(symbol, location_matrix, scale_matrix_1, scale_matrix_2):
|
||||
"""
|
||||
Creates a random variable with Matrix Normal Distribution.
|
||||
|
||||
The density of the said distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
location_matrix: Real ``n x p`` matrix
|
||||
Represents degrees of freedom
|
||||
scale_matrix_1: Positive definite matrix
|
||||
Scale Matrix of shape ``n x n``
|
||||
scale_matrix_2: Positive definite matrix
|
||||
Scale Matrix of shape ``p x p``
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import MatrixSymbol
|
||||
>>> from sympy.stats import density, MatrixNormal
|
||||
>>> M = MatrixNormal('M', [[1, 2]], [1], [[1, 0], [0, 1]])
|
||||
>>> X = MatrixSymbol('X', 1, 2)
|
||||
>>> density(M)(X).doit()
|
||||
exp(-Trace((Matrix([
|
||||
[-1],
|
||||
[-2]]) + X.T)*(Matrix([[-1, -2]]) + X))/2)/(2*pi)
|
||||
>>> density(M)([[3, 4]]).doit()
|
||||
exp(-4)/(2*pi)
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Matrix_normal_distribution
|
||||
|
||||
"""
|
||||
if isinstance(location_matrix, list):
|
||||
location_matrix = ImmutableMatrix(location_matrix)
|
||||
if isinstance(scale_matrix_1, list):
|
||||
scale_matrix_1 = ImmutableMatrix(scale_matrix_1)
|
||||
if isinstance(scale_matrix_2, list):
|
||||
scale_matrix_2 = ImmutableMatrix(scale_matrix_2)
|
||||
args = (location_matrix, scale_matrix_1, scale_matrix_2)
|
||||
return rv(symbol, MatrixNormalDistribution, args)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Matrix Student's T distribution ---------------------------------------------------
|
||||
|
||||
class MatrixStudentTDistribution(MatrixDistribution):
|
||||
|
||||
_argnames = ('nu', 'location_matrix', 'scale_matrix_1', 'scale_matrix_2')
|
||||
|
||||
@staticmethod
|
||||
def check(nu, location_matrix, scale_matrix_1, scale_matrix_2):
|
||||
if not isinstance(scale_matrix_1, MatrixSymbol):
|
||||
_value_check(scale_matrix_1.is_positive_definite != False, "The shape "
|
||||
"matrix must be positive definite.")
|
||||
if not isinstance(scale_matrix_2, MatrixSymbol):
|
||||
_value_check(scale_matrix_2.is_positive_definite != False, "The shape "
|
||||
"matrix must be positive definite.")
|
||||
_value_check(scale_matrix_1.is_square != False, "Scale matrix 1 should be "
|
||||
"be square matrix")
|
||||
_value_check(scale_matrix_2.is_square != False, "Scale matrix 2 should be "
|
||||
"be square matrix")
|
||||
n = location_matrix.shape[0]
|
||||
p = location_matrix.shape[1]
|
||||
_value_check(scale_matrix_1.shape[0] == p, "Scale matrix 1 should be"
|
||||
" of shape %s x %s" % (str(p), str(p)))
|
||||
_value_check(scale_matrix_2.shape[0] == n, "Scale matrix 2 should be"
|
||||
" of shape %s x %s" % (str(n), str(n)))
|
||||
_value_check(nu.is_positive != False, "Degrees of freedom must be positive")
|
||||
|
||||
@property
|
||||
def set(self):
|
||||
n, p = self.location_matrix.shape
|
||||
return MatrixSet(n, p, S.Reals)
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
return self.location_matrix.shape
|
||||
|
||||
def pdf(self, x):
|
||||
from sympy.matrices.dense import eye
|
||||
if isinstance(x, list):
|
||||
x = ImmutableMatrix(x)
|
||||
if not isinstance(x, (MatrixBase, MatrixSymbol)):
|
||||
raise ValueError("%s should be an isinstance of Matrix "
|
||||
"or MatrixSymbol" % str(x))
|
||||
nu, M, Omega, Sigma = self.nu, self.location_matrix, self.scale_matrix_1, self.scale_matrix_2
|
||||
n, p = M.shape
|
||||
|
||||
K = multigamma((nu + n + p - 1)/2, p) * Determinant(Omega)**(-n/2) * Determinant(Sigma)**(-p/2) \
|
||||
/ ((pi)**(n*p/2) * multigamma((nu + p - 1)/2, p))
|
||||
return K * (Determinant(eye(n) + Inverse(Sigma)*(x - M)*Inverse(Omega)*Transpose(x - M))) \
|
||||
**(-(nu + n + p -1)/2)
|
||||
|
||||
|
||||
|
||||
def MatrixStudentT(symbol, nu, location_matrix, scale_matrix_1, scale_matrix_2):
|
||||
"""
|
||||
Creates a random variable with Matrix Gamma Distribution.
|
||||
|
||||
The density of the said distribution can be found at [1].
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
nu: Positive Real number
|
||||
degrees of freedom
|
||||
location_matrix: Positive definite real square matrix
|
||||
Location Matrix of shape ``n x p``
|
||||
scale_matrix_1: Positive definite real square matrix
|
||||
Scale Matrix of shape ``p x p``
|
||||
scale_matrix_2: Positive definite real square matrix
|
||||
Scale Matrix of shape ``n x n``
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
RandomSymbol
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import MatrixSymbol,symbols
|
||||
>>> from sympy.stats import density, MatrixStudentT
|
||||
>>> v = symbols('v',positive=True)
|
||||
>>> M = MatrixStudentT('M', v, [[1, 2]], [[1, 0], [0, 1]], [1])
|
||||
>>> X = MatrixSymbol('X', 1, 2)
|
||||
>>> density(M)(X)
|
||||
gamma(v/2 + 1)*Determinant((Matrix([[-1, -2]]) + X)*(Matrix([
|
||||
[-1],
|
||||
[-2]]) + X.T) + Matrix([[1]]))**(-v/2 - 1)/(pi**1.0*gamma(v/2)*Determinant(Matrix([[1]]))**1.0*Determinant(Matrix([
|
||||
[1, 0],
|
||||
[0, 1]]))**0.5)
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Matrix_t-distribution
|
||||
|
||||
"""
|
||||
if isinstance(location_matrix, list):
|
||||
location_matrix = ImmutableMatrix(location_matrix)
|
||||
if isinstance(scale_matrix_1, list):
|
||||
scale_matrix_1 = ImmutableMatrix(scale_matrix_1)
|
||||
if isinstance(scale_matrix_2, list):
|
||||
scale_matrix_2 = ImmutableMatrix(scale_matrix_2)
|
||||
args = (nu, location_matrix, scale_matrix_1, scale_matrix_2)
|
||||
return rv(symbol, MatrixStudentTDistribution, args)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
from sympy.core.basic import Basic
|
||||
from sympy.stats.rv import PSpace, _symbol_converter, RandomMatrixSymbol
|
||||
|
||||
class RandomMatrixPSpace(PSpace):
|
||||
"""
|
||||
Represents probability space for
|
||||
random matrices. It contains the mechanics
|
||||
for handling the API calls for random matrices.
|
||||
"""
|
||||
def __new__(cls, sym, model=None):
|
||||
sym = _symbol_converter(sym)
|
||||
if model:
|
||||
return Basic.__new__(cls, sym, model)
|
||||
else:
|
||||
return Basic.__new__(cls, sym)
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
try:
|
||||
return self.args[1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def compute_density(self, expr, *args):
|
||||
rms = expr.atoms(RandomMatrixSymbol)
|
||||
if len(rms) > 2 or (not isinstance(expr, RandomMatrixSymbol)):
|
||||
raise NotImplementedError("Currently, no algorithm has been "
|
||||
"implemented to handle general expressions containing "
|
||||
"multiple random matrices.")
|
||||
return self.model.density(expr)
|
||||
|
|
@ -0,0 +1,457 @@
|
|||
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()
|
||||
1798
venv/lib/python3.12/site-packages/sympy/stats/rv.py
Normal file
1798
venv/lib/python3.12/site-packages/sympy/stats/rv.py
Normal file
File diff suppressed because it is too large
Load diff
519
venv/lib/python3.12/site-packages/sympy/stats/rv_interface.py
Normal file
519
venv/lib/python3.12/site-packages/sympy/stats/rv_interface.py
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
from sympy.sets import FiniteSet
|
||||
from sympy.core.numbers import Rational
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.functions.combinatorial.factorials import FallingFactorial
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import piecewise_fold
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.solvers.solveset import solveset
|
||||
from .rv import (probability, expectation, density, where, given, pspace, cdf, PSpace,
|
||||
characteristic_function, sample, sample_iter, random_symbols, independent, dependent,
|
||||
sampling_density, moment_generating_function, quantile, is_random,
|
||||
sample_stochastic_process)
|
||||
|
||||
|
||||
__all__ = ['P', 'E', 'H', 'density', 'where', 'given', 'sample', 'cdf',
|
||||
'characteristic_function', 'pspace', 'sample_iter', 'variance', 'std',
|
||||
'skewness', 'kurtosis', 'covariance', 'dependent', 'entropy', 'median',
|
||||
'independent', 'random_symbols', 'correlation', 'factorial_moment',
|
||||
'moment', 'cmoment', 'sampling_density', 'moment_generating_function',
|
||||
'smoment', 'quantile', 'sample_stochastic_process']
|
||||
|
||||
|
||||
|
||||
def moment(X, n, c=0, condition=None, *, evaluate=True, **kwargs):
|
||||
"""
|
||||
Return the nth moment of a random expression about c.
|
||||
|
||||
.. math::
|
||||
moment(X, c, n) = E((X-c)^{n})
|
||||
|
||||
Default value of c is 0.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Die, moment, E
|
||||
>>> X = Die('X', 6)
|
||||
>>> moment(X, 1, 6)
|
||||
-5/2
|
||||
>>> moment(X, 2)
|
||||
91/6
|
||||
>>> moment(X, 1) == E(X)
|
||||
True
|
||||
"""
|
||||
from sympy.stats.symbolic_probability import Moment
|
||||
if evaluate:
|
||||
return Moment(X, n, c, condition).doit()
|
||||
return Moment(X, n, c, condition).rewrite(Integral)
|
||||
|
||||
|
||||
def variance(X, condition=None, **kwargs):
|
||||
"""
|
||||
Variance of a random expression.
|
||||
|
||||
.. math::
|
||||
variance(X) = E((X-E(X))^{2})
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Die, Bernoulli, variance
|
||||
>>> from sympy import simplify, Symbol
|
||||
|
||||
>>> X = Die('X', 6)
|
||||
>>> p = Symbol('p')
|
||||
>>> B = Bernoulli('B', p, 1, 0)
|
||||
|
||||
>>> variance(2*X)
|
||||
35/3
|
||||
|
||||
>>> simplify(variance(B))
|
||||
p*(1 - p)
|
||||
"""
|
||||
if is_random(X) and pspace(X) == PSpace():
|
||||
from sympy.stats.symbolic_probability import Variance
|
||||
return Variance(X, condition)
|
||||
|
||||
return cmoment(X, 2, condition, **kwargs)
|
||||
|
||||
|
||||
def standard_deviation(X, condition=None, **kwargs):
|
||||
r"""
|
||||
Standard Deviation of a random expression
|
||||
|
||||
.. math::
|
||||
std(X) = \sqrt(E((X-E(X))^{2}))
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Bernoulli, std
|
||||
>>> from sympy import Symbol, simplify
|
||||
|
||||
>>> p = Symbol('p')
|
||||
>>> B = Bernoulli('B', p, 1, 0)
|
||||
|
||||
>>> simplify(std(B))
|
||||
sqrt(p*(1 - p))
|
||||
"""
|
||||
return sqrt(variance(X, condition, **kwargs))
|
||||
std = standard_deviation
|
||||
|
||||
def entropy(expr, condition=None, **kwargs):
|
||||
"""
|
||||
Calculates entropy of a probability distribution.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expression : the random expression whose entropy is to be calculated
|
||||
condition : optional, to specify conditions on random expression
|
||||
b: base of the logarithm, optional
|
||||
By default, it is taken as Euler's number
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
result : Entropy of the expression, a constant
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Normal, Die, entropy
|
||||
>>> X = Normal('X', 0, 1)
|
||||
>>> entropy(X)
|
||||
log(2)/2 + 1/2 + log(pi)/2
|
||||
|
||||
>>> D = Die('D', 4)
|
||||
>>> entropy(D)
|
||||
log(4)
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Entropy_%28information_theory%29
|
||||
.. [2] https://www.crmarsh.com/static/pdf/Charles_Marsh_Continuous_Entropy.pdf
|
||||
.. [3] https://kconrad.math.uconn.edu/blurbs/analysis/entropypost.pdf
|
||||
"""
|
||||
pdf = density(expr, condition, **kwargs)
|
||||
base = kwargs.get('b', exp(1))
|
||||
if isinstance(pdf, dict):
|
||||
return sum(-prob*log(prob, base) for prob in pdf.values())
|
||||
return expectation(-log(pdf(expr), base))
|
||||
|
||||
def covariance(X, Y, condition=None, **kwargs):
|
||||
"""
|
||||
Covariance of two random expressions.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The expectation that the two variables will rise and fall together
|
||||
|
||||
.. math::
|
||||
covariance(X,Y) = E((X-E(X)) (Y-E(Y)))
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Exponential, covariance
|
||||
>>> from sympy import Symbol
|
||||
|
||||
>>> rate = Symbol('lambda', positive=True, real=True)
|
||||
>>> X = Exponential('X', rate)
|
||||
>>> Y = Exponential('Y', rate)
|
||||
|
||||
>>> covariance(X, X)
|
||||
lambda**(-2)
|
||||
>>> covariance(X, Y)
|
||||
0
|
||||
>>> covariance(X, Y + rate*X)
|
||||
1/lambda
|
||||
"""
|
||||
if (is_random(X) and pspace(X) == PSpace()) or (is_random(Y) and pspace(Y) == PSpace()):
|
||||
from sympy.stats.symbolic_probability import Covariance
|
||||
return Covariance(X, Y, condition)
|
||||
|
||||
return expectation(
|
||||
(X - expectation(X, condition, **kwargs)) *
|
||||
(Y - expectation(Y, condition, **kwargs)),
|
||||
condition, **kwargs)
|
||||
|
||||
|
||||
def correlation(X, Y, condition=None, **kwargs):
|
||||
r"""
|
||||
Correlation of two random expressions, also known as correlation
|
||||
coefficient or Pearson's correlation.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
The normalized expectation that the two variables will rise
|
||||
and fall together
|
||||
|
||||
.. math::
|
||||
correlation(X,Y) = E((X-E(X))(Y-E(Y)) / (\sigma_x \sigma_y))
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Exponential, correlation
|
||||
>>> from sympy import Symbol
|
||||
|
||||
>>> rate = Symbol('lambda', positive=True, real=True)
|
||||
>>> X = Exponential('X', rate)
|
||||
>>> Y = Exponential('Y', rate)
|
||||
|
||||
>>> correlation(X, X)
|
||||
1
|
||||
>>> correlation(X, Y)
|
||||
0
|
||||
>>> correlation(X, Y + rate*X)
|
||||
1/sqrt(1 + lambda**(-2))
|
||||
"""
|
||||
return covariance(X, Y, condition, **kwargs)/(std(X, condition, **kwargs)
|
||||
* std(Y, condition, **kwargs))
|
||||
|
||||
|
||||
def cmoment(X, n, condition=None, *, evaluate=True, **kwargs):
|
||||
"""
|
||||
Return the nth central moment of a random expression about its mean.
|
||||
|
||||
.. math::
|
||||
cmoment(X, n) = E((X - E(X))^{n})
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Die, cmoment, variance
|
||||
>>> X = Die('X', 6)
|
||||
>>> cmoment(X, 3)
|
||||
0
|
||||
>>> cmoment(X, 2)
|
||||
35/12
|
||||
>>> cmoment(X, 2) == variance(X)
|
||||
True
|
||||
"""
|
||||
from sympy.stats.symbolic_probability import CentralMoment
|
||||
if evaluate:
|
||||
return CentralMoment(X, n, condition).doit()
|
||||
return CentralMoment(X, n, condition).rewrite(Integral)
|
||||
|
||||
|
||||
def smoment(X, n, condition=None, **kwargs):
|
||||
r"""
|
||||
Return the nth Standardized moment of a random expression.
|
||||
|
||||
.. math::
|
||||
smoment(X, n) = E(((X - \mu)/\sigma_X)^{n})
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import skewness, Exponential, smoment
|
||||
>>> from sympy import Symbol
|
||||
>>> rate = Symbol('lambda', positive=True, real=True)
|
||||
>>> Y = Exponential('Y', rate)
|
||||
>>> smoment(Y, 4)
|
||||
9
|
||||
>>> smoment(Y, 4) == smoment(3*Y, 4)
|
||||
True
|
||||
>>> smoment(Y, 3) == skewness(Y)
|
||||
True
|
||||
"""
|
||||
sigma = std(X, condition, **kwargs)
|
||||
return (1/sigma)**n*cmoment(X, n, condition, **kwargs)
|
||||
|
||||
def skewness(X, condition=None, **kwargs):
|
||||
r"""
|
||||
Measure of the asymmetry of the probability distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Positive skew indicates that most of the values lie to the right of
|
||||
the mean.
|
||||
|
||||
.. math::
|
||||
skewness(X) = E(((X - E(X))/\sigma_X)^{3})
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
condition : Expr containing RandomSymbols
|
||||
A conditional expression. skewness(X, X>0) is skewness of X given X > 0
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import skewness, Exponential, Normal
|
||||
>>> from sympy import Symbol
|
||||
>>> X = Normal('X', 0, 1)
|
||||
>>> skewness(X)
|
||||
0
|
||||
>>> skewness(X, X > 0) # find skewness given X > 0
|
||||
(-sqrt(2)/sqrt(pi) + 4*sqrt(2)/pi**(3/2))/(1 - 2/pi)**(3/2)
|
||||
|
||||
>>> rate = Symbol('lambda', positive=True, real=True)
|
||||
>>> Y = Exponential('Y', rate)
|
||||
>>> skewness(Y)
|
||||
2
|
||||
"""
|
||||
return smoment(X, 3, condition=condition, **kwargs)
|
||||
|
||||
def kurtosis(X, condition=None, **kwargs):
|
||||
r"""
|
||||
Characterizes the tails/outliers of a probability distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Kurtosis of any univariate normal distribution is 3. Kurtosis less than
|
||||
3 means that the distribution produces fewer and less extreme outliers
|
||||
than the normal distribution.
|
||||
|
||||
.. math::
|
||||
kurtosis(X) = E(((X - E(X))/\sigma_X)^{4})
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
condition : Expr containing RandomSymbols
|
||||
A conditional expression. kurtosis(X, X>0) is kurtosis of X given X > 0
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import kurtosis, Exponential, Normal
|
||||
>>> from sympy import Symbol
|
||||
>>> X = Normal('X', 0, 1)
|
||||
>>> kurtosis(X)
|
||||
3
|
||||
>>> kurtosis(X, X > 0) # find kurtosis given X > 0
|
||||
(-4/pi - 12/pi**2 + 3)/(1 - 2/pi)**2
|
||||
|
||||
>>> rate = Symbol('lamda', positive=True, real=True)
|
||||
>>> Y = Exponential('Y', rate)
|
||||
>>> kurtosis(Y)
|
||||
9
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Kurtosis
|
||||
.. [2] https://mathworld.wolfram.com/Kurtosis.html
|
||||
"""
|
||||
return smoment(X, 4, condition=condition, **kwargs)
|
||||
|
||||
|
||||
def factorial_moment(X, n, condition=None, **kwargs):
|
||||
"""
|
||||
The factorial moment is a mathematical quantity defined as the expectation
|
||||
or average of the falling factorial of a random variable.
|
||||
|
||||
.. math::
|
||||
factorial-moment(X, n) = E(X(X - 1)(X - 2)...(X - n + 1))
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
n: A natural number, n-th factorial moment.
|
||||
|
||||
condition : Expr containing RandomSymbols
|
||||
A conditional expression.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import factorial_moment, Poisson, Binomial
|
||||
>>> from sympy import Symbol, S
|
||||
>>> lamda = Symbol('lamda')
|
||||
>>> X = Poisson('X', lamda)
|
||||
>>> factorial_moment(X, 2)
|
||||
lamda**2
|
||||
>>> Y = Binomial('Y', 2, S.Half)
|
||||
>>> factorial_moment(Y, 2)
|
||||
1/2
|
||||
>>> factorial_moment(Y, 2, Y > 1) # find factorial moment for Y > 1
|
||||
2
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Factorial_moment
|
||||
.. [2] https://mathworld.wolfram.com/FactorialMoment.html
|
||||
"""
|
||||
return expectation(FallingFactorial(X, n), condition=condition, **kwargs)
|
||||
|
||||
def median(X, evaluate=True, **kwargs):
|
||||
r"""
|
||||
Calculates the median of the probability distribution.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Mathematically, median of Probability distribution is defined as all those
|
||||
values of `m` for which the following condition is satisfied
|
||||
|
||||
.. math::
|
||||
P(X\leq m) \geq \frac{1}{2} \text{ and} \text{ } P(X\geq m)\geq \frac{1}{2}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
X: The random expression whose median is to be calculated.
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
The FiniteSet or an Interval which contains the median of the
|
||||
random expression.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Normal, Die, median
|
||||
>>> N = Normal('N', 3, 1)
|
||||
>>> median(N)
|
||||
{3}
|
||||
>>> D = Die('D')
|
||||
>>> median(D)
|
||||
{3, 4}
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Median#Probability_distributions
|
||||
|
||||
"""
|
||||
if not is_random(X):
|
||||
return X
|
||||
|
||||
from sympy.stats.crv import ContinuousPSpace
|
||||
from sympy.stats.drv import DiscretePSpace
|
||||
from sympy.stats.frv import FinitePSpace
|
||||
|
||||
if isinstance(pspace(X), FinitePSpace):
|
||||
cdf = pspace(X).compute_cdf(X)
|
||||
result = []
|
||||
for key, value in cdf.items():
|
||||
if value>= Rational(1, 2) and (1 - value) + \
|
||||
pspace(X).probability(Eq(X, key)) >= Rational(1, 2):
|
||||
result.append(key)
|
||||
return FiniteSet(*result)
|
||||
if isinstance(pspace(X), (ContinuousPSpace, DiscretePSpace)):
|
||||
cdf = pspace(X).compute_cdf(X)
|
||||
x = Dummy('x')
|
||||
result = solveset(piecewise_fold(cdf(x) - Rational(1, 2)), x, pspace(X).set)
|
||||
return result
|
||||
raise NotImplementedError("The median of %s is not implemented."%str(pspace(X)))
|
||||
|
||||
|
||||
def coskewness(X, Y, Z, condition=None, **kwargs):
|
||||
r"""
|
||||
Calculates the co-skewness of three random variables.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Mathematically Coskewness is defined as
|
||||
|
||||
.. math::
|
||||
coskewness(X,Y,Z)=\frac{E[(X-E[X]) * (Y-E[Y]) * (Z-E[Z])]} {\sigma_{X}\sigma_{Y}\sigma_{Z}}
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
X : RandomSymbol
|
||||
Random Variable used to calculate coskewness
|
||||
Y : RandomSymbol
|
||||
Random Variable used to calculate coskewness
|
||||
Z : RandomSymbol
|
||||
Random Variable used to calculate coskewness
|
||||
condition : Expr containing RandomSymbols
|
||||
A conditional expression
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import coskewness, Exponential, skewness
|
||||
>>> from sympy import symbols
|
||||
>>> p = symbols('p', positive=True)
|
||||
>>> X = Exponential('X', p)
|
||||
>>> Y = Exponential('Y', 2*p)
|
||||
>>> coskewness(X, Y, Y)
|
||||
0
|
||||
>>> coskewness(X, Y + X, Y + 2*X)
|
||||
16*sqrt(85)/85
|
||||
>>> coskewness(X + 2*Y, Y + X, Y + 2*X, X > 3)
|
||||
9*sqrt(170)/85
|
||||
>>> coskewness(Y, Y, Y) == skewness(Y)
|
||||
True
|
||||
>>> coskewness(X, Y + p*X, Y + 2*p*X)
|
||||
4/(sqrt(1 + 1/(4*p**2))*sqrt(4 + 1/(4*p**2)))
|
||||
|
||||
Returns
|
||||
=======
|
||||
|
||||
coskewness : The coskewness of the three random variables
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Coskewness
|
||||
|
||||
"""
|
||||
num = expectation((X - expectation(X, condition, **kwargs)) \
|
||||
* (Y - expectation(Y, condition, **kwargs)) \
|
||||
* (Z - expectation(Z, condition, **kwargs)), condition, **kwargs)
|
||||
den = std(X, condition, **kwargs) * std(Y, condition, **kwargs) \
|
||||
* std(Z, condition, **kwargs)
|
||||
return num/den
|
||||
|
||||
|
||||
P = probability
|
||||
E = expectation
|
||||
H = entropy
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,105 @@
|
|||
from functools import singledispatch
|
||||
|
||||
from sympy.external import import_module
|
||||
from sympy.stats.crv_types import BetaDistribution, ChiSquaredDistribution, ExponentialDistribution, GammaDistribution, \
|
||||
LogNormalDistribution, NormalDistribution, ParetoDistribution, UniformDistribution, FDistributionDistribution, GumbelDistribution, LaplaceDistribution, \
|
||||
LogisticDistribution, RayleighDistribution, TriangularDistribution
|
||||
from sympy.stats.drv_types import GeometricDistribution, PoissonDistribution, ZetaDistribution
|
||||
from sympy.stats.frv_types import BinomialDistribution, HypergeometricDistribution
|
||||
|
||||
|
||||
numpy = import_module('numpy')
|
||||
|
||||
|
||||
@singledispatch
|
||||
def do_sample_numpy(dist, size, rand_state):
|
||||
return None
|
||||
|
||||
|
||||
# CRV:
|
||||
|
||||
@do_sample_numpy.register(BetaDistribution)
|
||||
def _(dist: BetaDistribution, size, rand_state):
|
||||
return rand_state.beta(a=float(dist.alpha), b=float(dist.beta), size=size)
|
||||
|
||||
|
||||
@do_sample_numpy.register(ChiSquaredDistribution)
|
||||
def _(dist: ChiSquaredDistribution, size, rand_state):
|
||||
return rand_state.chisquare(df=float(dist.k), size=size)
|
||||
|
||||
|
||||
@do_sample_numpy.register(ExponentialDistribution)
|
||||
def _(dist: ExponentialDistribution, size, rand_state):
|
||||
return rand_state.exponential(1 / float(dist.rate), size=size)
|
||||
|
||||
@do_sample_numpy.register(FDistributionDistribution)
|
||||
def _(dist: FDistributionDistribution, size, rand_state):
|
||||
return rand_state.f(dfnum = float(dist.d1), dfden = float(dist.d2), size=size)
|
||||
|
||||
@do_sample_numpy.register(GammaDistribution)
|
||||
def _(dist: GammaDistribution, size, rand_state):
|
||||
return rand_state.gamma(shape = float(dist.k), scale = float(dist.theta), size=size)
|
||||
|
||||
@do_sample_numpy.register(GumbelDistribution)
|
||||
def _(dist: GumbelDistribution, size, rand_state):
|
||||
return rand_state.gumbel(loc = float(dist.mu), scale = float(dist.beta), size=size)
|
||||
|
||||
@do_sample_numpy.register(LaplaceDistribution)
|
||||
def _(dist: LaplaceDistribution, size, rand_state):
|
||||
return rand_state.laplace(loc = float(dist.mu), scale = float(dist.b), size=size)
|
||||
|
||||
@do_sample_numpy.register(LogisticDistribution)
|
||||
def _(dist: LogisticDistribution, size, rand_state):
|
||||
return rand_state.logistic(loc = float(dist.mu), scale = float(dist.s), size=size)
|
||||
|
||||
@do_sample_numpy.register(LogNormalDistribution)
|
||||
def _(dist: LogNormalDistribution, size, rand_state):
|
||||
return rand_state.lognormal(mean = float(dist.mean), sigma = float(dist.std), size=size)
|
||||
|
||||
@do_sample_numpy.register(NormalDistribution)
|
||||
def _(dist: NormalDistribution, size, rand_state):
|
||||
return rand_state.normal(loc = float(dist.mean), scale = float(dist.std), size=size)
|
||||
|
||||
@do_sample_numpy.register(RayleighDistribution)
|
||||
def _(dist: RayleighDistribution, size, rand_state):
|
||||
return rand_state.rayleigh(scale = float(dist.sigma), size=size)
|
||||
|
||||
@do_sample_numpy.register(ParetoDistribution)
|
||||
def _(dist: ParetoDistribution, size, rand_state):
|
||||
return (numpy.random.pareto(a=float(dist.alpha), size=size) + 1) * float(dist.xm)
|
||||
|
||||
@do_sample_numpy.register(TriangularDistribution)
|
||||
def _(dist: TriangularDistribution, size, rand_state):
|
||||
return rand_state.triangular(left = float(dist.a), mode = float(dist.b), right = float(dist.c), size=size)
|
||||
|
||||
@do_sample_numpy.register(UniformDistribution)
|
||||
def _(dist: UniformDistribution, size, rand_state):
|
||||
return rand_state.uniform(low=float(dist.left), high=float(dist.right), size=size)
|
||||
|
||||
|
||||
# DRV:
|
||||
|
||||
@do_sample_numpy.register(GeometricDistribution)
|
||||
def _(dist: GeometricDistribution, size, rand_state):
|
||||
return rand_state.geometric(p=float(dist.p), size=size)
|
||||
|
||||
|
||||
@do_sample_numpy.register(PoissonDistribution)
|
||||
def _(dist: PoissonDistribution, size, rand_state):
|
||||
return rand_state.poisson(lam=float(dist.lamda), size=size)
|
||||
|
||||
|
||||
@do_sample_numpy.register(ZetaDistribution)
|
||||
def _(dist: ZetaDistribution, size, rand_state):
|
||||
return rand_state.zipf(a=float(dist.s), size=size)
|
||||
|
||||
|
||||
# FRV:
|
||||
|
||||
@do_sample_numpy.register(BinomialDistribution)
|
||||
def _(dist: BinomialDistribution, size, rand_state):
|
||||
return rand_state.binomial(n=int(dist.n), p=float(dist.p), size=size)
|
||||
|
||||
@do_sample_numpy.register(HypergeometricDistribution)
|
||||
def _(dist: HypergeometricDistribution, size, rand_state):
|
||||
return rand_state.hypergeometric(ngood = int(dist.N), nbad = int(dist.m), nsample = int(dist.n), size=size)
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
from functools import singledispatch
|
||||
from sympy.external import import_module
|
||||
from sympy.stats.crv_types import BetaDistribution, CauchyDistribution, ChiSquaredDistribution, ExponentialDistribution, \
|
||||
GammaDistribution, LogNormalDistribution, NormalDistribution, ParetoDistribution, UniformDistribution, \
|
||||
GaussianInverseDistribution
|
||||
from sympy.stats.drv_types import PoissonDistribution, GeometricDistribution, NegativeBinomialDistribution
|
||||
from sympy.stats.frv_types import BinomialDistribution, BernoulliDistribution
|
||||
|
||||
|
||||
try:
|
||||
import pymc
|
||||
except ImportError:
|
||||
pymc = import_module('pymc3')
|
||||
|
||||
@singledispatch
|
||||
def do_sample_pymc(dist):
|
||||
return None
|
||||
|
||||
|
||||
# CRV:
|
||||
|
||||
@do_sample_pymc.register(BetaDistribution)
|
||||
def _(dist: BetaDistribution):
|
||||
return pymc.Beta('X', alpha=float(dist.alpha), beta=float(dist.beta))
|
||||
|
||||
|
||||
@do_sample_pymc.register(CauchyDistribution)
|
||||
def _(dist: CauchyDistribution):
|
||||
return pymc.Cauchy('X', alpha=float(dist.x0), beta=float(dist.gamma))
|
||||
|
||||
|
||||
@do_sample_pymc.register(ChiSquaredDistribution)
|
||||
def _(dist: ChiSquaredDistribution):
|
||||
return pymc.ChiSquared('X', nu=float(dist.k))
|
||||
|
||||
|
||||
@do_sample_pymc.register(ExponentialDistribution)
|
||||
def _(dist: ExponentialDistribution):
|
||||
return pymc.Exponential('X', lam=float(dist.rate))
|
||||
|
||||
|
||||
@do_sample_pymc.register(GammaDistribution)
|
||||
def _(dist: GammaDistribution):
|
||||
return pymc.Gamma('X', alpha=float(dist.k), beta=1 / float(dist.theta))
|
||||
|
||||
|
||||
@do_sample_pymc.register(LogNormalDistribution)
|
||||
def _(dist: LogNormalDistribution):
|
||||
return pymc.Lognormal('X', mu=float(dist.mean), sigma=float(dist.std))
|
||||
|
||||
|
||||
@do_sample_pymc.register(NormalDistribution)
|
||||
def _(dist: NormalDistribution):
|
||||
return pymc.Normal('X', float(dist.mean), float(dist.std))
|
||||
|
||||
|
||||
@do_sample_pymc.register(GaussianInverseDistribution)
|
||||
def _(dist: GaussianInverseDistribution):
|
||||
return pymc.Wald('X', mu=float(dist.mean), lam=float(dist.shape))
|
||||
|
||||
|
||||
@do_sample_pymc.register(ParetoDistribution)
|
||||
def _(dist: ParetoDistribution):
|
||||
return pymc.Pareto('X', alpha=float(dist.alpha), m=float(dist.xm))
|
||||
|
||||
|
||||
@do_sample_pymc.register(UniformDistribution)
|
||||
def _(dist: UniformDistribution):
|
||||
return pymc.Uniform('X', lower=float(dist.left), upper=float(dist.right))
|
||||
|
||||
|
||||
# DRV:
|
||||
|
||||
@do_sample_pymc.register(GeometricDistribution)
|
||||
def _(dist: GeometricDistribution):
|
||||
return pymc.Geometric('X', p=float(dist.p))
|
||||
|
||||
|
||||
@do_sample_pymc.register(NegativeBinomialDistribution)
|
||||
def _(dist: NegativeBinomialDistribution):
|
||||
return pymc.NegativeBinomial('X', mu=float((dist.p * dist.r) / (1 - dist.p)),
|
||||
alpha=float(dist.r))
|
||||
|
||||
|
||||
@do_sample_pymc.register(PoissonDistribution)
|
||||
def _(dist: PoissonDistribution):
|
||||
return pymc.Poisson('X', mu=float(dist.lamda))
|
||||
|
||||
|
||||
# FRV:
|
||||
|
||||
@do_sample_pymc.register(BernoulliDistribution)
|
||||
def _(dist: BernoulliDistribution):
|
||||
return pymc.Bernoulli('X', p=float(dist.p))
|
||||
|
||||
|
||||
@do_sample_pymc.register(BinomialDistribution)
|
||||
def _(dist: BinomialDistribution):
|
||||
return pymc.Binomial('X', n=int(dist.n), p=float(dist.p))
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
from functools import singledispatch
|
||||
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.utilities.lambdify import lambdify
|
||||
from sympy.external import import_module
|
||||
from sympy.stats import DiscreteDistributionHandmade
|
||||
from sympy.stats.crv import SingleContinuousDistribution
|
||||
from sympy.stats.crv_types import ChiSquaredDistribution, ExponentialDistribution, GammaDistribution, \
|
||||
LogNormalDistribution, NormalDistribution, ParetoDistribution, UniformDistribution, BetaDistribution, \
|
||||
StudentTDistribution, CauchyDistribution
|
||||
from sympy.stats.drv_types import GeometricDistribution, LogarithmicDistribution, NegativeBinomialDistribution, \
|
||||
PoissonDistribution, SkellamDistribution, YuleSimonDistribution, ZetaDistribution
|
||||
from sympy.stats.frv import SingleFiniteDistribution
|
||||
|
||||
|
||||
scipy = import_module("scipy", import_kwargs={'fromlist':['stats']})
|
||||
|
||||
|
||||
@singledispatch
|
||||
def do_sample_scipy(dist, size, seed):
|
||||
return None
|
||||
|
||||
|
||||
# CRV
|
||||
|
||||
@do_sample_scipy.register(SingleContinuousDistribution)
|
||||
def _(dist: SingleContinuousDistribution, size, seed):
|
||||
# if we don't need to make a handmade pdf, we won't
|
||||
import scipy.stats
|
||||
|
||||
z = Dummy('z')
|
||||
handmade_pdf = lambdify(z, dist.pdf(z), ['numpy', 'scipy'])
|
||||
|
||||
class scipy_pdf(scipy.stats.rv_continuous):
|
||||
def _pdf(dist, x):
|
||||
return handmade_pdf(x)
|
||||
|
||||
scipy_rv = scipy_pdf(a=float(dist.set._inf),
|
||||
b=float(dist.set._sup), name='scipy_pdf')
|
||||
return scipy_rv.rvs(size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(ChiSquaredDistribution)
|
||||
def _(dist: ChiSquaredDistribution, size, seed):
|
||||
# same parametrisation
|
||||
return scipy.stats.chi2.rvs(df=float(dist.k), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(ExponentialDistribution)
|
||||
def _(dist: ExponentialDistribution, size, seed):
|
||||
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.expon.html#scipy.stats.expon
|
||||
return scipy.stats.expon.rvs(scale=1 / float(dist.rate), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(GammaDistribution)
|
||||
def _(dist: GammaDistribution, size, seed):
|
||||
# https://stackoverflow.com/questions/42150965/how-to-plot-gamma-distribution-with-alpha-and-beta-parameters-in-python
|
||||
return scipy.stats.gamma.rvs(a=float(dist.k), scale=float(dist.theta), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(LogNormalDistribution)
|
||||
def _(dist: LogNormalDistribution, size, seed):
|
||||
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.lognorm.html
|
||||
return scipy.stats.lognorm.rvs(scale=float(exp(dist.mean)), s=float(dist.std), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(NormalDistribution)
|
||||
def _(dist: NormalDistribution, size, seed):
|
||||
return scipy.stats.norm.rvs(loc=float(dist.mean), scale=float(dist.std), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(ParetoDistribution)
|
||||
def _(dist: ParetoDistribution, size, seed):
|
||||
# https://stackoverflow.com/questions/42260519/defining-pareto-distribution-in-python-scipy
|
||||
return scipy.stats.pareto.rvs(b=float(dist.alpha), scale=float(dist.xm), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(StudentTDistribution)
|
||||
def _(dist: StudentTDistribution, size, seed):
|
||||
return scipy.stats.t.rvs(df=float(dist.nu), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(UniformDistribution)
|
||||
def _(dist: UniformDistribution, size, seed):
|
||||
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.uniform.html
|
||||
return scipy.stats.uniform.rvs(loc=float(dist.left), scale=float(dist.right - dist.left), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(BetaDistribution)
|
||||
def _(dist: BetaDistribution, size, seed):
|
||||
# same parametrisation
|
||||
return scipy.stats.beta.rvs(a=float(dist.alpha), b=float(dist.beta), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(CauchyDistribution)
|
||||
def _(dist: CauchyDistribution, size, seed):
|
||||
return scipy.stats.cauchy.rvs(loc=float(dist.x0), scale=float(dist.gamma), size=size, random_state=seed)
|
||||
|
||||
|
||||
# DRV:
|
||||
|
||||
@do_sample_scipy.register(DiscreteDistributionHandmade)
|
||||
def _(dist: DiscreteDistributionHandmade, size, seed):
|
||||
from scipy.stats import rv_discrete
|
||||
|
||||
z = Dummy('z')
|
||||
handmade_pmf = lambdify(z, dist.pdf(z), ['numpy', 'scipy'])
|
||||
|
||||
class scipy_pmf(rv_discrete):
|
||||
def _pmf(dist, x):
|
||||
return handmade_pmf(x)
|
||||
|
||||
scipy_rv = scipy_pmf(a=float(dist.set._inf), b=float(dist.set._sup),
|
||||
name='scipy_pmf')
|
||||
return scipy_rv.rvs(size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(GeometricDistribution)
|
||||
def _(dist: GeometricDistribution, size, seed):
|
||||
return scipy.stats.geom.rvs(p=float(dist.p), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(LogarithmicDistribution)
|
||||
def _(dist: LogarithmicDistribution, size, seed):
|
||||
return scipy.stats.logser.rvs(p=float(dist.p), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(NegativeBinomialDistribution)
|
||||
def _(dist: NegativeBinomialDistribution, size, seed):
|
||||
return scipy.stats.nbinom.rvs(n=float(dist.r), p=float(dist.p), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(PoissonDistribution)
|
||||
def _(dist: PoissonDistribution, size, seed):
|
||||
return scipy.stats.poisson.rvs(mu=float(dist.lamda), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(SkellamDistribution)
|
||||
def _(dist: SkellamDistribution, size, seed):
|
||||
return scipy.stats.skellam.rvs(mu1=float(dist.mu1), mu2=float(dist.mu2), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(YuleSimonDistribution)
|
||||
def _(dist: YuleSimonDistribution, size, seed):
|
||||
return scipy.stats.yulesimon.rvs(alpha=float(dist.rho), size=size, random_state=seed)
|
||||
|
||||
|
||||
@do_sample_scipy.register(ZetaDistribution)
|
||||
def _(dist: ZetaDistribution, size, seed):
|
||||
return scipy.stats.zipf.rvs(a=float(dist.s), size=size, random_state=seed)
|
||||
|
||||
|
||||
# FRV:
|
||||
|
||||
@do_sample_scipy.register(SingleFiniteDistribution)
|
||||
def _(dist: SingleFiniteDistribution, size, seed):
|
||||
# scipy can handle with custom distributions
|
||||
|
||||
from scipy.stats import rv_discrete
|
||||
density_ = dist.dict
|
||||
x, y = [], []
|
||||
for k, v in density_.items():
|
||||
x.append(int(k))
|
||||
y.append(float(v))
|
||||
scipy_rv = rv_discrete(name='scipy_rv', values=(x, y))
|
||||
return scipy_rv.rvs(size=size, random_state=seed)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,181 @@
|
|||
from sympy.core.numbers import oo
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.sets.sets import Interval
|
||||
from sympy.external import import_module
|
||||
from sympy.stats import Beta, Chi, Normal, Gamma, Exponential, LogNormal, Pareto, ChiSquared, Uniform, sample, \
|
||||
BetaPrime, Cauchy, GammaInverse, GaussianInverse, StudentT, Weibull, density, ContinuousRV, FDistribution, \
|
||||
Gumbel, Laplace, Logistic, Rayleigh, Triangular
|
||||
from sympy.testing.pytest import skip, raises
|
||||
|
||||
|
||||
def test_sample_numpy():
|
||||
distribs_numpy = [
|
||||
Beta("B", 1, 1),
|
||||
Normal("N", 0, 1),
|
||||
Gamma("G", 2, 7),
|
||||
Exponential("E", 2),
|
||||
LogNormal("LN", 0, 1),
|
||||
Pareto("P", 1, 1),
|
||||
ChiSquared("CS", 2),
|
||||
Uniform("U", 0, 1),
|
||||
FDistribution("FD", 1, 2),
|
||||
Gumbel("GB", 1, 2),
|
||||
Laplace("L", 1, 2),
|
||||
Logistic("LO", 1, 2),
|
||||
Rayleigh("R", 1),
|
||||
Triangular("T", 1, 2, 2),
|
||||
]
|
||||
size = 3
|
||||
numpy = import_module('numpy')
|
||||
if not numpy:
|
||||
skip('Numpy is not installed. Abort tests for _sample_numpy.')
|
||||
else:
|
||||
for X in distribs_numpy:
|
||||
samps = sample(X, size=size, library='numpy')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
raises(NotImplementedError,
|
||||
lambda: sample(Chi("C", 1), library='numpy'))
|
||||
raises(NotImplementedError,
|
||||
lambda: Chi("C", 1).pspace.distribution.sample(library='tensorflow'))
|
||||
|
||||
|
||||
def test_sample_scipy():
|
||||
distribs_scipy = [
|
||||
Beta("B", 1, 1),
|
||||
BetaPrime("BP", 1, 1),
|
||||
Cauchy("C", 1, 1),
|
||||
Chi("C", 1),
|
||||
Normal("N", 0, 1),
|
||||
Gamma("G", 2, 7),
|
||||
GammaInverse("GI", 1, 1),
|
||||
GaussianInverse("GUI", 1, 1),
|
||||
Exponential("E", 2),
|
||||
LogNormal("LN", 0, 1),
|
||||
Pareto("P", 1, 1),
|
||||
StudentT("S", 2),
|
||||
ChiSquared("CS", 2),
|
||||
Uniform("U", 0, 1)
|
||||
]
|
||||
size = 3
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests for _sample_scipy.')
|
||||
else:
|
||||
for X in distribs_scipy:
|
||||
samps = sample(X, size=size, library='scipy')
|
||||
samps2 = sample(X, size=(2, 2), library='scipy')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
assert samps2[i][j] in X.pspace.domain.set
|
||||
|
||||
|
||||
def test_sample_pymc():
|
||||
distribs_pymc = [
|
||||
Beta("B", 1, 1),
|
||||
Cauchy("C", 1, 1),
|
||||
Normal("N", 0, 1),
|
||||
Gamma("G", 2, 7),
|
||||
GaussianInverse("GI", 1, 1),
|
||||
Exponential("E", 2),
|
||||
LogNormal("LN", 0, 1),
|
||||
Pareto("P", 1, 1),
|
||||
ChiSquared("CS", 2),
|
||||
Uniform("U", 0, 1)
|
||||
]
|
||||
size = 3
|
||||
pymc = import_module('pymc')
|
||||
if not pymc:
|
||||
skip('PyMC is not installed. Abort tests for _sample_pymc.')
|
||||
else:
|
||||
for X in distribs_pymc:
|
||||
samps = sample(X, size=size, library='pymc')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
raises(NotImplementedError,
|
||||
lambda: sample(Chi("C", 1), library='pymc'))
|
||||
|
||||
|
||||
def test_sampling_gamma_inverse():
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy not installed. Abort tests for sampling of gamma inverse.')
|
||||
X = GammaInverse("x", 1, 1)
|
||||
assert sample(X) in X.pspace.domain.set
|
||||
|
||||
|
||||
def test_lognormal_sampling():
|
||||
# Right now, only density function and sampling works
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests')
|
||||
for i in range(3):
|
||||
X = LogNormal('x', i, 1)
|
||||
assert sample(X) in X.pspace.domain.set
|
||||
|
||||
size = 5
|
||||
samps = sample(X, size=size)
|
||||
for samp in samps:
|
||||
assert samp in X.pspace.domain.set
|
||||
|
||||
|
||||
def test_sampling_gaussian_inverse():
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy not installed. Abort tests for sampling of Gaussian inverse.')
|
||||
X = GaussianInverse("x", 1, 1)
|
||||
assert sample(X, library='scipy') in X.pspace.domain.set
|
||||
|
||||
|
||||
def test_prefab_sampling():
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests')
|
||||
N = Normal('X', 0, 1)
|
||||
L = LogNormal('L', 0, 1)
|
||||
E = Exponential('Ex', 1)
|
||||
P = Pareto('P', 1, 3)
|
||||
W = Weibull('W', 1, 1)
|
||||
U = Uniform('U', 0, 1)
|
||||
B = Beta('B', 2, 5)
|
||||
G = Gamma('G', 1, 3)
|
||||
|
||||
variables = [N, L, E, P, W, U, B, G]
|
||||
niter = 10
|
||||
size = 5
|
||||
for var in variables:
|
||||
for _ in range(niter):
|
||||
assert sample(var) in var.pspace.domain.set
|
||||
samps = sample(var, size=size)
|
||||
for samp in samps:
|
||||
assert samp in var.pspace.domain.set
|
||||
|
||||
|
||||
def test_sample_continuous():
|
||||
z = Symbol('z')
|
||||
Z = ContinuousRV(z, exp(-z), set=Interval(0, oo))
|
||||
assert density(Z)(-1) == 0
|
||||
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests')
|
||||
assert sample(Z) in Z.pspace.domain.set
|
||||
sym, val = list(Z.pspace.sample().items())[0]
|
||||
assert sym == Z and val in Interval(0, oo)
|
||||
|
||||
libraries = ['scipy', 'numpy', 'pymc']
|
||||
for lib in libraries:
|
||||
try:
|
||||
imported_lib = import_module(lib)
|
||||
if imported_lib:
|
||||
s0, s1, s2 = [], [], []
|
||||
s0 = sample(Z, size=10, library=lib, seed=0)
|
||||
s1 = sample(Z, size=10, library=lib, seed=0)
|
||||
s2 = sample(Z, size=10, library=lib, seed=1)
|
||||
assert all(s0 == s1)
|
||||
assert all(s1 != s2)
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.external import import_module
|
||||
from sympy.stats import (
|
||||
Geometric,
|
||||
Poisson,
|
||||
Zeta,
|
||||
sample,
|
||||
Skellam,
|
||||
Logarithmic,
|
||||
NegativeBinomial,
|
||||
YuleSimon,
|
||||
DiscreteRV,
|
||||
)
|
||||
from sympy.testing.pytest import skip, raises, slow
|
||||
|
||||
|
||||
def test_sample_numpy():
|
||||
distribs_numpy = [
|
||||
Geometric('G', 0.5),
|
||||
Poisson('P', 1),
|
||||
Zeta('Z', 2)
|
||||
]
|
||||
size = 3
|
||||
numpy = import_module('numpy')
|
||||
if not numpy:
|
||||
skip('Numpy is not installed. Abort tests for _sample_numpy.')
|
||||
else:
|
||||
for X in distribs_numpy:
|
||||
samps = sample(X, size=size, library='numpy')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
raises(NotImplementedError,
|
||||
lambda: sample(Skellam('S', 1, 1), library='numpy'))
|
||||
raises(NotImplementedError,
|
||||
lambda: Skellam('S', 1, 1).pspace.distribution.sample(library='tensorflow'))
|
||||
|
||||
|
||||
def test_sample_scipy():
|
||||
p = S(2)/3
|
||||
x = Symbol('x', integer=True, positive=True)
|
||||
pdf = p*(1 - p)**(x - 1) # pdf of Geometric Distribution
|
||||
distribs_scipy = [
|
||||
DiscreteRV(x, pdf, set=S.Naturals),
|
||||
Geometric('G', 0.5),
|
||||
Logarithmic('L', 0.5),
|
||||
NegativeBinomial('N', 5, 0.4),
|
||||
Poisson('P', 1),
|
||||
Skellam('S', 1, 1),
|
||||
YuleSimon('Y', 1),
|
||||
Zeta('Z', 2)
|
||||
]
|
||||
size = 3
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests for _sample_scipy.')
|
||||
else:
|
||||
for X in distribs_scipy:
|
||||
samps = sample(X, size=size, library='scipy')
|
||||
samps2 = sample(X, size=(2, 2), library='scipy')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
assert samps2[i][j] in X.pspace.domain.set
|
||||
|
||||
|
||||
def test_sample_pymc():
|
||||
distribs_pymc = [
|
||||
Geometric('G', 0.5),
|
||||
Poisson('P', 1),
|
||||
NegativeBinomial('N', 5, 0.4)
|
||||
]
|
||||
size = 3
|
||||
pymc = import_module('pymc')
|
||||
if not pymc:
|
||||
skip('PyMC is not installed. Abort tests for _sample_pymc.')
|
||||
else:
|
||||
for X in distribs_pymc:
|
||||
samps = sample(X, size=size, library='pymc')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
raises(NotImplementedError,
|
||||
lambda: sample(Skellam('S', 1, 1), library='pymc'))
|
||||
|
||||
@slow
|
||||
def test_sample_discrete():
|
||||
X = Geometric('X', S.Half)
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy not installed. Abort tests')
|
||||
assert sample(X) in X.pspace.domain.set
|
||||
samps = sample(X, size=2) # This takes long time if ran without scipy
|
||||
for samp in samps:
|
||||
assert samp in X.pspace.domain.set
|
||||
|
||||
libraries = ['scipy', 'numpy', 'pymc']
|
||||
for lib in libraries:
|
||||
try:
|
||||
imported_lib = import_module(lib)
|
||||
if imported_lib:
|
||||
s0, s1, s2 = [], [], []
|
||||
s0 = sample(X, size=10, library=lib, seed=0)
|
||||
s1 = sample(X, size=10, library=lib, seed=0)
|
||||
s2 = sample(X, size=10, library=lib, seed=1)
|
||||
assert all(s0 == s1)
|
||||
assert not all(s1 == s2)
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
from sympy.core.numbers import Rational
|
||||
from sympy.core.singleton import S
|
||||
from sympy.external import import_module
|
||||
from sympy.stats import Binomial, sample, Die, FiniteRV, DiscreteUniform, Bernoulli, BetaBinomial, Hypergeometric, \
|
||||
Rademacher
|
||||
from sympy.testing.pytest import skip, raises
|
||||
|
||||
def test_given_sample():
|
||||
X = Die('X', 6)
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests')
|
||||
assert sample(X, X > 5) == 6
|
||||
|
||||
def test_sample_numpy():
|
||||
distribs_numpy = [
|
||||
Binomial("B", 5, 0.4),
|
||||
Hypergeometric("H", 2, 1, 1)
|
||||
]
|
||||
size = 3
|
||||
numpy = import_module('numpy')
|
||||
if not numpy:
|
||||
skip('Numpy is not installed. Abort tests for _sample_numpy.')
|
||||
else:
|
||||
for X in distribs_numpy:
|
||||
samps = sample(X, size=size, library='numpy')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
raises(NotImplementedError,
|
||||
lambda: sample(Die("D"), library='numpy'))
|
||||
raises(NotImplementedError,
|
||||
lambda: Die("D").pspace.sample(library='tensorflow'))
|
||||
|
||||
|
||||
def test_sample_scipy():
|
||||
distribs_scipy = [
|
||||
FiniteRV('F', {1: S.Half, 2: Rational(1, 4), 3: Rational(1, 4)}),
|
||||
DiscreteUniform("Y", list(range(5))),
|
||||
Die("D"),
|
||||
Bernoulli("Be", 0.3),
|
||||
Binomial("Bi", 5, 0.4),
|
||||
BetaBinomial("Bb", 2, 1, 1),
|
||||
Hypergeometric("H", 1, 1, 1),
|
||||
Rademacher("R")
|
||||
]
|
||||
|
||||
size = 3
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy not installed. Abort tests for _sample_scipy.')
|
||||
else:
|
||||
for X in distribs_scipy:
|
||||
samps = sample(X, size=size)
|
||||
samps2 = sample(X, size=(2, 2))
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
assert samps2[i][j] in X.pspace.domain.set
|
||||
|
||||
|
||||
def test_sample_pymc():
|
||||
distribs_pymc = [
|
||||
Bernoulli('B', 0.2),
|
||||
Binomial('N', 5, 0.4)
|
||||
]
|
||||
size = 3
|
||||
pymc = import_module('pymc')
|
||||
if not pymc:
|
||||
skip('PyMC is not installed. Abort tests for _sample_pymc.')
|
||||
else:
|
||||
for X in distribs_pymc:
|
||||
samps = sample(X, size=size, library='pymc')
|
||||
for sam in samps:
|
||||
assert sam in X.pspace.domain.set
|
||||
raises(NotImplementedError,
|
||||
lambda: (sample(Die("D"), library='pymc')))
|
||||
|
||||
|
||||
def test_sample_seed():
|
||||
F = FiniteRV('F', {1: S.Half, 2: Rational(1, 4), 3: Rational(1, 4)})
|
||||
size = 10
|
||||
libraries = ['scipy', 'numpy', 'pymc']
|
||||
for lib in libraries:
|
||||
try:
|
||||
imported_lib = import_module(lib)
|
||||
if imported_lib:
|
||||
s0 = sample(F, size=size, library=lib, seed=0)
|
||||
s1 = sample(F, size=size, library=lib, seed=0)
|
||||
s2 = sample(F, size=size, library=lib, seed=1)
|
||||
assert all(s0 == s1)
|
||||
assert not all(s1 == s2)
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
from sympy.core.basic import Basic
|
||||
from sympy.stats.joint_rv import ProductPSpace
|
||||
from sympy.stats.rv import ProductDomain, _symbol_converter, Distribution
|
||||
|
||||
|
||||
class StochasticPSpace(ProductPSpace):
|
||||
"""
|
||||
Represents probability space of stochastic processes
|
||||
and their random variables. Contains mechanics to do
|
||||
computations for queries of stochastic processes.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
Initialized by symbol, the specific process and
|
||||
distribution(optional) if the random indexed symbols
|
||||
of the process follows any specific distribution, like,
|
||||
in Bernoulli Process, each random indexed symbol follows
|
||||
Bernoulli distribution. For processes with memory, this
|
||||
parameter should not be passed.
|
||||
"""
|
||||
|
||||
def __new__(cls, sym, process, distribution=None):
|
||||
sym = _symbol_converter(sym)
|
||||
from sympy.stats.stochastic_process_types import StochasticProcess
|
||||
if not isinstance(process, StochasticProcess):
|
||||
raise TypeError("`process` must be an instance of StochasticProcess.")
|
||||
if distribution is None:
|
||||
distribution = Distribution()
|
||||
return Basic.__new__(cls, sym, process, distribution)
|
||||
|
||||
@property
|
||||
def process(self):
|
||||
"""
|
||||
The associated stochastic process.
|
||||
"""
|
||||
return self.args[1]
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
return ProductDomain(self.process.index_set,
|
||||
self.process.state_space)
|
||||
|
||||
@property
|
||||
def symbol(self):
|
||||
return self.args[0]
|
||||
|
||||
@property
|
||||
def distribution(self):
|
||||
return self.args[2]
|
||||
|
||||
def probability(self, condition, given_condition=None, evaluate=True, **kwargs):
|
||||
"""
|
||||
Transfers the task of handling queries to the specific stochastic
|
||||
process because every process has their own logic of handling such
|
||||
queries.
|
||||
"""
|
||||
return self.process.probability(condition, given_condition, evaluate, **kwargs)
|
||||
|
||||
def compute_expectation(self, expr, condition=None, evaluate=True, **kwargs):
|
||||
"""
|
||||
Transfers the task of handling queries to the specific stochastic
|
||||
process because every process has their own logic of handling such
|
||||
queries.
|
||||
"""
|
||||
return self.process.expectation(expr, condition, evaluate, **kwargs)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,308 @@
|
|||
import itertools
|
||||
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.function import expand as _expand
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.singleton import S
|
||||
from sympy.matrices.exceptions import ShapeError
|
||||
from sympy.matrices.expressions.matexpr import MatrixExpr
|
||||
from sympy.matrices.expressions.matmul import MatMul
|
||||
from sympy.matrices.expressions.special import ZeroMatrix
|
||||
from sympy.stats.rv import RandomSymbol, is_random
|
||||
from sympy.core.sympify import _sympify
|
||||
from sympy.stats.symbolic_probability import Variance, Covariance, Expectation
|
||||
|
||||
|
||||
class ExpectationMatrix(Expectation, MatrixExpr):
|
||||
"""
|
||||
Expectation of a random matrix expression.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import ExpectationMatrix, Normal
|
||||
>>> from sympy.stats.rv import RandomMatrixSymbol
|
||||
>>> from sympy import symbols, MatrixSymbol, Matrix
|
||||
>>> k = symbols("k")
|
||||
>>> A, B = MatrixSymbol("A", k, k), MatrixSymbol("B", k, k)
|
||||
>>> X, Y = RandomMatrixSymbol("X", k, 1), RandomMatrixSymbol("Y", k, 1)
|
||||
>>> ExpectationMatrix(X)
|
||||
ExpectationMatrix(X)
|
||||
>>> ExpectationMatrix(A*X).shape
|
||||
(k, 1)
|
||||
|
||||
To expand the expectation in its expression, use ``expand()``:
|
||||
|
||||
>>> ExpectationMatrix(A*X + B*Y).expand()
|
||||
A*ExpectationMatrix(X) + B*ExpectationMatrix(Y)
|
||||
>>> ExpectationMatrix((X + Y)*(X - Y).T).expand()
|
||||
ExpectationMatrix(X*X.T) - ExpectationMatrix(X*Y.T) + ExpectationMatrix(Y*X.T) - ExpectationMatrix(Y*Y.T)
|
||||
|
||||
To evaluate the ``ExpectationMatrix``, use ``doit()``:
|
||||
|
||||
>>> N11, N12 = Normal('N11', 11, 1), Normal('N12', 12, 1)
|
||||
>>> N21, N22 = Normal('N21', 21, 1), Normal('N22', 22, 1)
|
||||
>>> M11, M12 = Normal('M11', 1, 1), Normal('M12', 2, 1)
|
||||
>>> M21, M22 = Normal('M21', 3, 1), Normal('M22', 4, 1)
|
||||
>>> x1 = Matrix([[N11, N12], [N21, N22]])
|
||||
>>> x2 = Matrix([[M11, M12], [M21, M22]])
|
||||
>>> ExpectationMatrix(x1 + x2).doit()
|
||||
Matrix([
|
||||
[12, 14],
|
||||
[24, 26]])
|
||||
|
||||
"""
|
||||
def __new__(cls, expr, condition=None):
|
||||
expr = _sympify(expr)
|
||||
if condition is None:
|
||||
if not is_random(expr):
|
||||
return expr
|
||||
obj = Expr.__new__(cls, expr)
|
||||
else:
|
||||
condition = _sympify(condition)
|
||||
obj = Expr.__new__(cls, expr, condition)
|
||||
|
||||
obj._shape = expr.shape
|
||||
obj._condition = condition
|
||||
return obj
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
return self._shape
|
||||
|
||||
def expand(self, **hints):
|
||||
expr = self.args[0]
|
||||
condition = self._condition
|
||||
if not is_random(expr):
|
||||
return expr
|
||||
|
||||
if isinstance(expr, Add):
|
||||
return Add.fromiter(Expectation(a, condition=condition).expand()
|
||||
for a in expr.args)
|
||||
|
||||
expand_expr = _expand(expr)
|
||||
if isinstance(expand_expr, Add):
|
||||
return Add.fromiter(Expectation(a, condition=condition).expand()
|
||||
for a in expand_expr.args)
|
||||
|
||||
elif isinstance(expr, (Mul, MatMul)):
|
||||
rv = []
|
||||
nonrv = []
|
||||
postnon = []
|
||||
|
||||
for a in expr.args:
|
||||
if is_random(a):
|
||||
if rv:
|
||||
rv.extend(postnon)
|
||||
else:
|
||||
nonrv.extend(postnon)
|
||||
postnon = []
|
||||
rv.append(a)
|
||||
elif a.is_Matrix:
|
||||
postnon.append(a)
|
||||
else:
|
||||
nonrv.append(a)
|
||||
|
||||
# In order to avoid infinite-looping (MatMul may call .doit() again),
|
||||
# do not rebuild
|
||||
if len(nonrv) == 0:
|
||||
return self
|
||||
return Mul.fromiter(nonrv)*Expectation(Mul.fromiter(rv),
|
||||
condition=condition)*Mul.fromiter(postnon)
|
||||
|
||||
return self
|
||||
|
||||
class VarianceMatrix(Variance, MatrixExpr):
|
||||
"""
|
||||
Variance of a random matrix probability expression. Also known as
|
||||
Covariance matrix, auto-covariance matrix, dispersion matrix,
|
||||
or variance-covariance matrix.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import VarianceMatrix
|
||||
>>> from sympy.stats.rv import RandomMatrixSymbol
|
||||
>>> from sympy import symbols, MatrixSymbol
|
||||
>>> k = symbols("k")
|
||||
>>> A, B = MatrixSymbol("A", k, k), MatrixSymbol("B", k, k)
|
||||
>>> X, Y = RandomMatrixSymbol("X", k, 1), RandomMatrixSymbol("Y", k, 1)
|
||||
>>> VarianceMatrix(X)
|
||||
VarianceMatrix(X)
|
||||
>>> VarianceMatrix(X).shape
|
||||
(k, k)
|
||||
|
||||
To expand the variance in its expression, use ``expand()``:
|
||||
|
||||
>>> VarianceMatrix(A*X).expand()
|
||||
A*VarianceMatrix(X)*A.T
|
||||
>>> VarianceMatrix(A*X + B*Y).expand()
|
||||
2*A*CrossCovarianceMatrix(X, Y)*B.T + A*VarianceMatrix(X)*A.T + B*VarianceMatrix(Y)*B.T
|
||||
"""
|
||||
def __new__(cls, arg, condition=None):
|
||||
arg = _sympify(arg)
|
||||
|
||||
if 1 not in arg.shape:
|
||||
raise ShapeError("Expression is not a vector")
|
||||
|
||||
shape = (arg.shape[0], arg.shape[0]) if arg.shape[1] == 1 else (arg.shape[1], arg.shape[1])
|
||||
|
||||
if condition:
|
||||
obj = Expr.__new__(cls, arg, condition)
|
||||
else:
|
||||
obj = Expr.__new__(cls, arg)
|
||||
|
||||
obj._shape = shape
|
||||
obj._condition = condition
|
||||
return obj
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
return self._shape
|
||||
|
||||
def expand(self, **hints):
|
||||
arg = self.args[0]
|
||||
condition = self._condition
|
||||
|
||||
if not is_random(arg):
|
||||
return ZeroMatrix(*self.shape)
|
||||
|
||||
if isinstance(arg, RandomSymbol):
|
||||
return self
|
||||
elif isinstance(arg, Add):
|
||||
rv = []
|
||||
for a in arg.args:
|
||||
if is_random(a):
|
||||
rv.append(a)
|
||||
variances = Add(*(Variance(xv, condition).expand() for xv in rv))
|
||||
map_to_covar = lambda x: 2*Covariance(*x, condition=condition).expand()
|
||||
covariances = Add(*map(map_to_covar, itertools.combinations(rv, 2)))
|
||||
return variances + covariances
|
||||
elif isinstance(arg, (Mul, MatMul)):
|
||||
nonrv = []
|
||||
rv = []
|
||||
for a in arg.args:
|
||||
if is_random(a):
|
||||
rv.append(a)
|
||||
else:
|
||||
nonrv.append(a)
|
||||
if len(rv) == 0:
|
||||
return ZeroMatrix(*self.shape)
|
||||
# Avoid possible infinite loops with MatMul:
|
||||
if len(nonrv) == 0:
|
||||
return self
|
||||
# Variance of many multiple matrix products is not implemented:
|
||||
if len(rv) > 1:
|
||||
return self
|
||||
return Mul.fromiter(nonrv)*Variance(Mul.fromiter(rv),
|
||||
condition)*(Mul.fromiter(nonrv)).transpose()
|
||||
|
||||
# this expression contains a RandomSymbol somehow:
|
||||
return self
|
||||
|
||||
class CrossCovarianceMatrix(Covariance, MatrixExpr):
|
||||
"""
|
||||
Covariance of a random matrix probability expression.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import CrossCovarianceMatrix
|
||||
>>> from sympy.stats.rv import RandomMatrixSymbol
|
||||
>>> from sympy import symbols, MatrixSymbol
|
||||
>>> k = symbols("k")
|
||||
>>> A, B = MatrixSymbol("A", k, k), MatrixSymbol("B", k, k)
|
||||
>>> C, D = MatrixSymbol("C", k, k), MatrixSymbol("D", k, k)
|
||||
>>> X, Y = RandomMatrixSymbol("X", k, 1), RandomMatrixSymbol("Y", k, 1)
|
||||
>>> Z, W = RandomMatrixSymbol("Z", k, 1), RandomMatrixSymbol("W", k, 1)
|
||||
>>> CrossCovarianceMatrix(X, Y)
|
||||
CrossCovarianceMatrix(X, Y)
|
||||
>>> CrossCovarianceMatrix(X, Y).shape
|
||||
(k, k)
|
||||
|
||||
To expand the covariance in its expression, use ``expand()``:
|
||||
|
||||
>>> CrossCovarianceMatrix(X + Y, Z).expand()
|
||||
CrossCovarianceMatrix(X, Z) + CrossCovarianceMatrix(Y, Z)
|
||||
>>> CrossCovarianceMatrix(A*X, Y).expand()
|
||||
A*CrossCovarianceMatrix(X, Y)
|
||||
>>> CrossCovarianceMatrix(A*X, B.T*Y).expand()
|
||||
A*CrossCovarianceMatrix(X, Y)*B
|
||||
>>> CrossCovarianceMatrix(A*X + B*Y, C.T*Z + D.T*W).expand()
|
||||
A*CrossCovarianceMatrix(X, W)*D + A*CrossCovarianceMatrix(X, Z)*C + B*CrossCovarianceMatrix(Y, W)*D + B*CrossCovarianceMatrix(Y, Z)*C
|
||||
|
||||
"""
|
||||
def __new__(cls, arg1, arg2, condition=None):
|
||||
arg1 = _sympify(arg1)
|
||||
arg2 = _sympify(arg2)
|
||||
|
||||
if (1 not in arg1.shape) or (1 not in arg2.shape) or (arg1.shape[1] != arg2.shape[1]):
|
||||
raise ShapeError("Expression is not a vector")
|
||||
|
||||
shape = (arg1.shape[0], arg2.shape[0]) if arg1.shape[1] == 1 and arg2.shape[1] == 1 \
|
||||
else (1, 1)
|
||||
|
||||
if condition:
|
||||
obj = Expr.__new__(cls, arg1, arg2, condition)
|
||||
else:
|
||||
obj = Expr.__new__(cls, arg1, arg2)
|
||||
|
||||
obj._shape = shape
|
||||
obj._condition = condition
|
||||
return obj
|
||||
|
||||
@property
|
||||
def shape(self):
|
||||
return self._shape
|
||||
|
||||
def expand(self, **hints):
|
||||
arg1 = self.args[0]
|
||||
arg2 = self.args[1]
|
||||
condition = self._condition
|
||||
|
||||
if arg1 == arg2:
|
||||
return VarianceMatrix(arg1, condition).expand()
|
||||
|
||||
if not is_random(arg1) or not is_random(arg2):
|
||||
return ZeroMatrix(*self.shape)
|
||||
|
||||
if isinstance(arg1, RandomSymbol) and isinstance(arg2, RandomSymbol):
|
||||
return CrossCovarianceMatrix(arg1, arg2, condition)
|
||||
|
||||
coeff_rv_list1 = self._expand_single_argument(arg1.expand())
|
||||
coeff_rv_list2 = self._expand_single_argument(arg2.expand())
|
||||
|
||||
addends = [a*CrossCovarianceMatrix(r1, r2, condition=condition)*b.transpose()
|
||||
for (a, r1) in coeff_rv_list1 for (b, r2) in coeff_rv_list2]
|
||||
return Add.fromiter(addends)
|
||||
|
||||
@classmethod
|
||||
def _expand_single_argument(cls, expr):
|
||||
# return (coefficient, random_symbol) pairs:
|
||||
if isinstance(expr, RandomSymbol):
|
||||
return [(S.One, expr)]
|
||||
elif isinstance(expr, Add):
|
||||
outval = []
|
||||
for a in expr.args:
|
||||
if isinstance(a, (Mul, MatMul)):
|
||||
outval.append(cls._get_mul_nonrv_rv_tuple(a))
|
||||
elif is_random(a):
|
||||
outval.append((S.One, a))
|
||||
|
||||
return outval
|
||||
elif isinstance(expr, (Mul, MatMul)):
|
||||
return [cls._get_mul_nonrv_rv_tuple(expr)]
|
||||
elif is_random(expr):
|
||||
return [(S.One, expr)]
|
||||
|
||||
@classmethod
|
||||
def _get_mul_nonrv_rv_tuple(cls, m):
|
||||
rv = []
|
||||
nonrv = []
|
||||
for a in m.args:
|
||||
if is_random(a):
|
||||
rv.append(a)
|
||||
else:
|
||||
nonrv.append(a)
|
||||
return (Mul.fromiter(nonrv), Mul.fromiter(rv))
|
||||
|
|
@ -0,0 +1,698 @@
|
|||
import itertools
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.expr import Expr
|
||||
from sympy.core.function import expand as _expand
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.logic.boolalg import Not
|
||||
from sympy.core.parameters import global_parameters
|
||||
from sympy.core.sorting import default_sort_key
|
||||
from sympy.core.sympify import _sympify
|
||||
from sympy.core.relational import Relational
|
||||
from sympy.logic.boolalg import Boolean
|
||||
from sympy.stats import variance, covariance
|
||||
from sympy.stats.rv import (RandomSymbol, pspace, dependent,
|
||||
given, sampling_E, RandomIndexedSymbol, is_random,
|
||||
PSpace, sampling_P, random_symbols)
|
||||
|
||||
__all__ = ['Probability', 'Expectation', 'Variance', 'Covariance']
|
||||
|
||||
|
||||
@is_random.register(Expr)
|
||||
def _(x):
|
||||
atoms = x.free_symbols
|
||||
if len(atoms) == 1 and next(iter(atoms)) == x:
|
||||
return False
|
||||
return any(is_random(i) for i in atoms)
|
||||
|
||||
@is_random.register(RandomSymbol) # type: ignore
|
||||
def _(x):
|
||||
return True
|
||||
|
||||
|
||||
class Probability(Expr):
|
||||
"""
|
||||
Symbolic expression for the probability.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Probability, Normal
|
||||
>>> from sympy import Integral
|
||||
>>> X = Normal("X", 0, 1)
|
||||
>>> prob = Probability(X > 1)
|
||||
>>> prob
|
||||
Probability(X > 1)
|
||||
|
||||
Integral representation:
|
||||
|
||||
>>> prob.rewrite(Integral)
|
||||
Integral(sqrt(2)*exp(-_z**2/2)/(2*sqrt(pi)), (_z, 1, oo))
|
||||
|
||||
Evaluation of the integral:
|
||||
|
||||
>>> prob.evaluate_integral()
|
||||
sqrt(2)*(-sqrt(2)*sqrt(pi)*erf(sqrt(2)/2) + sqrt(2)*sqrt(pi))/(4*sqrt(pi))
|
||||
"""
|
||||
|
||||
is_commutative = True
|
||||
|
||||
def __new__(cls, prob, condition=None, **kwargs):
|
||||
prob = _sympify(prob)
|
||||
if condition is None:
|
||||
obj = Expr.__new__(cls, prob)
|
||||
else:
|
||||
condition = _sympify(condition)
|
||||
obj = Expr.__new__(cls, prob, condition)
|
||||
obj._condition = condition
|
||||
return obj
|
||||
|
||||
def doit(self, **hints):
|
||||
condition = self.args[0]
|
||||
given_condition = self._condition
|
||||
numsamples = hints.get('numsamples', False)
|
||||
evaluate = hints.get('evaluate', True)
|
||||
|
||||
if isinstance(condition, Not):
|
||||
return S.One - self.func(condition.args[0], given_condition,
|
||||
evaluate=evaluate).doit(**hints)
|
||||
|
||||
if condition.has(RandomIndexedSymbol):
|
||||
return pspace(condition).probability(condition, given_condition,
|
||||
evaluate=evaluate)
|
||||
|
||||
if isinstance(given_condition, RandomSymbol):
|
||||
condrv = random_symbols(condition)
|
||||
if len(condrv) == 1 and condrv[0] == given_condition:
|
||||
from sympy.stats.frv_types import BernoulliDistribution
|
||||
return BernoulliDistribution(self.func(condition).doit(**hints), 0, 1)
|
||||
if any(dependent(rv, given_condition) for rv in condrv):
|
||||
return Probability(condition, given_condition)
|
||||
else:
|
||||
return Probability(condition).doit()
|
||||
|
||||
if given_condition is not None and \
|
||||
not isinstance(given_condition, (Relational, Boolean)):
|
||||
raise ValueError("%s is not a relational or combination of relationals"
|
||||
% (given_condition))
|
||||
|
||||
if given_condition == False or condition is S.false:
|
||||
return S.Zero
|
||||
if not isinstance(condition, (Relational, Boolean)):
|
||||
raise ValueError("%s is not a relational or combination of relationals"
|
||||
% (condition))
|
||||
if condition is S.true:
|
||||
return S.One
|
||||
|
||||
if numsamples:
|
||||
return sampling_P(condition, given_condition, numsamples=numsamples)
|
||||
if given_condition is not None: # If there is a condition
|
||||
# Recompute on new conditional expr
|
||||
return Probability(given(condition, given_condition)).doit()
|
||||
|
||||
# Otherwise pass work off to the ProbabilitySpace
|
||||
if pspace(condition) == PSpace():
|
||||
return Probability(condition, given_condition)
|
||||
|
||||
result = pspace(condition).probability(condition)
|
||||
if hasattr(result, 'doit') and evaluate:
|
||||
return result.doit()
|
||||
else:
|
||||
return result
|
||||
|
||||
def _eval_rewrite_as_Integral(self, arg, condition=None, **kwargs):
|
||||
return self.func(arg, condition=condition).doit(evaluate=False)
|
||||
|
||||
_eval_rewrite_as_Sum = _eval_rewrite_as_Integral
|
||||
|
||||
def evaluate_integral(self):
|
||||
return self.rewrite(Integral).doit()
|
||||
|
||||
|
||||
class Expectation(Expr):
|
||||
"""
|
||||
Symbolic expression for the expectation.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Expectation, Normal, Probability, Poisson
|
||||
>>> from sympy import symbols, Integral, Sum
|
||||
>>> mu = symbols("mu")
|
||||
>>> sigma = symbols("sigma", positive=True)
|
||||
>>> X = Normal("X", mu, sigma)
|
||||
>>> Expectation(X)
|
||||
Expectation(X)
|
||||
>>> Expectation(X).evaluate_integral().simplify()
|
||||
mu
|
||||
|
||||
To get the integral expression of the expectation:
|
||||
|
||||
>>> Expectation(X).rewrite(Integral)
|
||||
Integral(sqrt(2)*X*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo))
|
||||
|
||||
The same integral expression, in more abstract terms:
|
||||
|
||||
>>> Expectation(X).rewrite(Probability)
|
||||
Integral(x*Probability(Eq(X, x)), (x, -oo, oo))
|
||||
|
||||
To get the Summation expression of the expectation for discrete random variables:
|
||||
|
||||
>>> lamda = symbols('lamda', positive=True)
|
||||
>>> Z = Poisson('Z', lamda)
|
||||
>>> Expectation(Z).rewrite(Sum)
|
||||
Sum(Z*lamda**Z*exp(-lamda)/factorial(Z), (Z, 0, oo))
|
||||
|
||||
This class is aware of some properties of the expectation:
|
||||
|
||||
>>> from sympy.abc import a
|
||||
>>> Expectation(a*X)
|
||||
Expectation(a*X)
|
||||
>>> Y = Normal("Y", 1, 2)
|
||||
>>> Expectation(X + Y)
|
||||
Expectation(X + Y)
|
||||
|
||||
To expand the ``Expectation`` into its expression, use ``expand()``:
|
||||
|
||||
>>> Expectation(X + Y).expand()
|
||||
Expectation(X) + Expectation(Y)
|
||||
>>> Expectation(a*X + Y).expand()
|
||||
a*Expectation(X) + Expectation(Y)
|
||||
>>> Expectation(a*X + Y)
|
||||
Expectation(a*X + Y)
|
||||
>>> Expectation((X + Y)*(X - Y)).expand()
|
||||
Expectation(X**2) - Expectation(Y**2)
|
||||
|
||||
To evaluate the ``Expectation``, use ``doit()``:
|
||||
|
||||
>>> Expectation(X + Y).doit()
|
||||
mu + 1
|
||||
>>> Expectation(X + Expectation(Y + Expectation(2*X))).doit()
|
||||
3*mu + 1
|
||||
|
||||
To prevent evaluating nested ``Expectation``, use ``doit(deep=False)``
|
||||
|
||||
>>> Expectation(X + Expectation(Y)).doit(deep=False)
|
||||
mu + Expectation(Expectation(Y))
|
||||
>>> Expectation(X + Expectation(Y + Expectation(2*X))).doit(deep=False)
|
||||
mu + Expectation(Expectation(Expectation(2*X) + Y))
|
||||
|
||||
"""
|
||||
|
||||
def __new__(cls, expr, condition=None, **kwargs):
|
||||
expr = _sympify(expr)
|
||||
if expr.is_Matrix:
|
||||
from sympy.stats.symbolic_multivariate_probability import ExpectationMatrix
|
||||
return ExpectationMatrix(expr, condition)
|
||||
if condition is None:
|
||||
if not is_random(expr):
|
||||
return expr
|
||||
obj = Expr.__new__(cls, expr)
|
||||
else:
|
||||
condition = _sympify(condition)
|
||||
obj = Expr.__new__(cls, expr, condition)
|
||||
obj._condition = condition
|
||||
return obj
|
||||
|
||||
def _eval_is_commutative(self):
|
||||
return(self.args[0].is_commutative)
|
||||
|
||||
def expand(self, **hints):
|
||||
expr = self.args[0]
|
||||
condition = self._condition
|
||||
|
||||
if not is_random(expr):
|
||||
return expr
|
||||
|
||||
if isinstance(expr, Add):
|
||||
return Add.fromiter(Expectation(a, condition=condition).expand()
|
||||
for a in expr.args)
|
||||
|
||||
expand_expr = _expand(expr)
|
||||
if isinstance(expand_expr, Add):
|
||||
return Add.fromiter(Expectation(a, condition=condition).expand()
|
||||
for a in expand_expr.args)
|
||||
|
||||
elif isinstance(expr, Mul):
|
||||
rv = []
|
||||
nonrv = []
|
||||
for a in expr.args:
|
||||
if is_random(a):
|
||||
rv.append(a)
|
||||
else:
|
||||
nonrv.append(a)
|
||||
return Mul.fromiter(nonrv)*Expectation(Mul.fromiter(rv), condition=condition)
|
||||
|
||||
return self
|
||||
|
||||
def doit(self, **hints):
|
||||
deep = hints.get('deep', True)
|
||||
condition = self._condition
|
||||
expr = self.args[0]
|
||||
numsamples = hints.get('numsamples', False)
|
||||
evaluate = hints.get('evaluate', True)
|
||||
|
||||
if deep:
|
||||
expr = expr.doit(**hints)
|
||||
|
||||
if not is_random(expr) or isinstance(expr, Expectation): # expr isn't random?
|
||||
return expr
|
||||
if numsamples: # Computing by monte carlo sampling?
|
||||
evalf = hints.get('evalf', True)
|
||||
return sampling_E(expr, condition, numsamples=numsamples, evalf=evalf)
|
||||
|
||||
if expr.has(RandomIndexedSymbol):
|
||||
return pspace(expr).compute_expectation(expr, condition)
|
||||
|
||||
# Create new expr and recompute E
|
||||
if condition is not None: # If there is a condition
|
||||
return self.func(given(expr, condition)).doit(**hints)
|
||||
|
||||
# A few known statements for efficiency
|
||||
|
||||
if expr.is_Add: # We know that E is Linear
|
||||
return Add(*[self.func(arg, condition).doit(**hints)
|
||||
if not isinstance(arg, Expectation) else self.func(arg, condition)
|
||||
for arg in expr.args])
|
||||
if expr.is_Mul:
|
||||
if expr.atoms(Expectation):
|
||||
return expr
|
||||
|
||||
if pspace(expr) == PSpace():
|
||||
return self.func(expr)
|
||||
# Otherwise case is simple, pass work off to the ProbabilitySpace
|
||||
result = pspace(expr).compute_expectation(expr, evaluate=evaluate)
|
||||
if hasattr(result, 'doit') and evaluate:
|
||||
return result.doit(**hints)
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
def _eval_rewrite_as_Probability(self, arg, condition=None, **kwargs):
|
||||
rvs = arg.atoms(RandomSymbol)
|
||||
if len(rvs) > 1:
|
||||
raise NotImplementedError()
|
||||
if len(rvs) == 0:
|
||||
return arg
|
||||
|
||||
rv = rvs.pop()
|
||||
if rv.pspace is None:
|
||||
raise ValueError("Probability space not known")
|
||||
|
||||
symbol = rv.symbol
|
||||
if symbol.name[0].isupper():
|
||||
symbol = Symbol(symbol.name.lower())
|
||||
else :
|
||||
symbol = Symbol(symbol.name + "_1")
|
||||
|
||||
if rv.pspace.is_Continuous:
|
||||
return Integral(arg.replace(rv, symbol)*Probability(Eq(rv, symbol), condition), (symbol, rv.pspace.domain.set.inf, rv.pspace.domain.set.sup))
|
||||
else:
|
||||
if rv.pspace.is_Finite:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
return Sum(arg.replace(rv, symbol)*Probability(Eq(rv, symbol), condition), (symbol, rv.pspace.domain.set.inf, rv.pspace.set.sup))
|
||||
|
||||
def _eval_rewrite_as_Integral(self, arg, condition=None, evaluate=False, **kwargs):
|
||||
return self.func(arg, condition=condition).doit(deep=False, evaluate=evaluate)
|
||||
|
||||
_eval_rewrite_as_Sum = _eval_rewrite_as_Integral # For discrete this will be Sum
|
||||
|
||||
def evaluate_integral(self):
|
||||
return self.rewrite(Integral).doit()
|
||||
|
||||
evaluate_sum = evaluate_integral
|
||||
|
||||
class Variance(Expr):
|
||||
"""
|
||||
Symbolic expression for the variance.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import symbols, Integral
|
||||
>>> from sympy.stats import Normal, Expectation, Variance, Probability
|
||||
>>> mu = symbols("mu", positive=True)
|
||||
>>> sigma = symbols("sigma", positive=True)
|
||||
>>> X = Normal("X", mu, sigma)
|
||||
>>> Variance(X)
|
||||
Variance(X)
|
||||
>>> Variance(X).evaluate_integral()
|
||||
sigma**2
|
||||
|
||||
Integral representation of the underlying calculations:
|
||||
|
||||
>>> Variance(X).rewrite(Integral)
|
||||
Integral(sqrt(2)*(X - Integral(sqrt(2)*X*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo)))**2*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo))
|
||||
|
||||
Integral representation, without expanding the PDF:
|
||||
|
||||
>>> Variance(X).rewrite(Probability)
|
||||
-Integral(x*Probability(Eq(X, x)), (x, -oo, oo))**2 + Integral(x**2*Probability(Eq(X, x)), (x, -oo, oo))
|
||||
|
||||
Rewrite the variance in terms of the expectation
|
||||
|
||||
>>> Variance(X).rewrite(Expectation)
|
||||
-Expectation(X)**2 + Expectation(X**2)
|
||||
|
||||
Some transformations based on the properties of the variance may happen:
|
||||
|
||||
>>> from sympy.abc import a
|
||||
>>> Y = Normal("Y", 0, 1)
|
||||
>>> Variance(a*X)
|
||||
Variance(a*X)
|
||||
|
||||
To expand the variance in its expression, use ``expand()``:
|
||||
|
||||
>>> Variance(a*X).expand()
|
||||
a**2*Variance(X)
|
||||
>>> Variance(X + Y)
|
||||
Variance(X + Y)
|
||||
>>> Variance(X + Y).expand()
|
||||
2*Covariance(X, Y) + Variance(X) + Variance(Y)
|
||||
|
||||
"""
|
||||
def __new__(cls, arg, condition=None, **kwargs):
|
||||
arg = _sympify(arg)
|
||||
|
||||
if arg.is_Matrix:
|
||||
from sympy.stats.symbolic_multivariate_probability import VarianceMatrix
|
||||
return VarianceMatrix(arg, condition)
|
||||
if condition is None:
|
||||
obj = Expr.__new__(cls, arg)
|
||||
else:
|
||||
condition = _sympify(condition)
|
||||
obj = Expr.__new__(cls, arg, condition)
|
||||
obj._condition = condition
|
||||
return obj
|
||||
|
||||
def _eval_is_commutative(self):
|
||||
return self.args[0].is_commutative
|
||||
|
||||
def expand(self, **hints):
|
||||
arg = self.args[0]
|
||||
condition = self._condition
|
||||
|
||||
if not is_random(arg):
|
||||
return S.Zero
|
||||
|
||||
if isinstance(arg, RandomSymbol):
|
||||
return self
|
||||
elif isinstance(arg, Add):
|
||||
rv = []
|
||||
for a in arg.args:
|
||||
if is_random(a):
|
||||
rv.append(a)
|
||||
variances = Add(*(Variance(xv, condition).expand() for xv in rv))
|
||||
map_to_covar = lambda x: 2*Covariance(*x, condition=condition).expand()
|
||||
covariances = Add(*map(map_to_covar, itertools.combinations(rv, 2)))
|
||||
return variances + covariances
|
||||
elif isinstance(arg, Mul):
|
||||
nonrv = []
|
||||
rv = []
|
||||
for a in arg.args:
|
||||
if is_random(a):
|
||||
rv.append(a)
|
||||
else:
|
||||
nonrv.append(a**2)
|
||||
if len(rv) == 0:
|
||||
return S.Zero
|
||||
return Mul.fromiter(nonrv)*Variance(Mul.fromiter(rv), condition)
|
||||
|
||||
# this expression contains a RandomSymbol somehow:
|
||||
return self
|
||||
|
||||
def _eval_rewrite_as_Expectation(self, arg, condition=None, **kwargs):
|
||||
e1 = Expectation(arg**2, condition)
|
||||
e2 = Expectation(arg, condition)**2
|
||||
return e1 - e2
|
||||
|
||||
def _eval_rewrite_as_Probability(self, arg, condition=None, **kwargs):
|
||||
return self.rewrite(Expectation).rewrite(Probability)
|
||||
|
||||
def _eval_rewrite_as_Integral(self, arg, condition=None, **kwargs):
|
||||
return variance(self.args[0], self._condition, evaluate=False)
|
||||
|
||||
_eval_rewrite_as_Sum = _eval_rewrite_as_Integral
|
||||
|
||||
def evaluate_integral(self):
|
||||
return self.rewrite(Integral).doit()
|
||||
|
||||
|
||||
class Covariance(Expr):
|
||||
"""
|
||||
Symbolic expression for the covariance.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.stats import Covariance
|
||||
>>> from sympy.stats import Normal
|
||||
>>> X = Normal("X", 3, 2)
|
||||
>>> Y = Normal("Y", 0, 1)
|
||||
>>> Z = Normal("Z", 0, 1)
|
||||
>>> W = Normal("W", 0, 1)
|
||||
>>> cexpr = Covariance(X, Y)
|
||||
>>> cexpr
|
||||
Covariance(X, Y)
|
||||
|
||||
Evaluate the covariance, `X` and `Y` are independent,
|
||||
therefore zero is the result:
|
||||
|
||||
>>> cexpr.evaluate_integral()
|
||||
0
|
||||
|
||||
Rewrite the covariance expression in terms of expectations:
|
||||
|
||||
>>> from sympy.stats import Expectation
|
||||
>>> cexpr.rewrite(Expectation)
|
||||
Expectation(X*Y) - Expectation(X)*Expectation(Y)
|
||||
|
||||
In order to expand the argument, use ``expand()``:
|
||||
|
||||
>>> from sympy.abc import a, b, c, d
|
||||
>>> Covariance(a*X + b*Y, c*Z + d*W)
|
||||
Covariance(a*X + b*Y, c*Z + d*W)
|
||||
>>> Covariance(a*X + b*Y, c*Z + d*W).expand()
|
||||
a*c*Covariance(X, Z) + a*d*Covariance(W, X) + b*c*Covariance(Y, Z) + b*d*Covariance(W, Y)
|
||||
|
||||
This class is aware of some properties of the covariance:
|
||||
|
||||
>>> Covariance(X, X).expand()
|
||||
Variance(X)
|
||||
>>> Covariance(a*X, b*Y).expand()
|
||||
a*b*Covariance(X, Y)
|
||||
"""
|
||||
|
||||
def __new__(cls, arg1, arg2, condition=None, **kwargs):
|
||||
arg1 = _sympify(arg1)
|
||||
arg2 = _sympify(arg2)
|
||||
|
||||
if arg1.is_Matrix or arg2.is_Matrix:
|
||||
from sympy.stats.symbolic_multivariate_probability import CrossCovarianceMatrix
|
||||
return CrossCovarianceMatrix(arg1, arg2, condition)
|
||||
|
||||
if kwargs.pop('evaluate', global_parameters.evaluate):
|
||||
arg1, arg2 = sorted([arg1, arg2], key=default_sort_key)
|
||||
|
||||
if condition is None:
|
||||
obj = Expr.__new__(cls, arg1, arg2)
|
||||
else:
|
||||
condition = _sympify(condition)
|
||||
obj = Expr.__new__(cls, arg1, arg2, condition)
|
||||
obj._condition = condition
|
||||
return obj
|
||||
|
||||
def _eval_is_commutative(self):
|
||||
return self.args[0].is_commutative
|
||||
|
||||
def expand(self, **hints):
|
||||
arg1 = self.args[0]
|
||||
arg2 = self.args[1]
|
||||
condition = self._condition
|
||||
|
||||
if arg1 == arg2:
|
||||
return Variance(arg1, condition).expand()
|
||||
|
||||
if not is_random(arg1):
|
||||
return S.Zero
|
||||
if not is_random(arg2):
|
||||
return S.Zero
|
||||
|
||||
arg1, arg2 = sorted([arg1, arg2], key=default_sort_key)
|
||||
|
||||
if isinstance(arg1, RandomSymbol) and isinstance(arg2, RandomSymbol):
|
||||
return Covariance(arg1, arg2, condition)
|
||||
|
||||
coeff_rv_list1 = self._expand_single_argument(arg1.expand())
|
||||
coeff_rv_list2 = self._expand_single_argument(arg2.expand())
|
||||
|
||||
addends = [a*b*Covariance(*sorted([r1, r2], key=default_sort_key), condition=condition)
|
||||
for (a, r1) in coeff_rv_list1 for (b, r2) in coeff_rv_list2]
|
||||
return Add.fromiter(addends)
|
||||
|
||||
@classmethod
|
||||
def _expand_single_argument(cls, expr):
|
||||
# return (coefficient, random_symbol) pairs:
|
||||
if isinstance(expr, RandomSymbol):
|
||||
return [(S.One, expr)]
|
||||
elif isinstance(expr, Add):
|
||||
outval = []
|
||||
for a in expr.args:
|
||||
if isinstance(a, Mul):
|
||||
outval.append(cls._get_mul_nonrv_rv_tuple(a))
|
||||
elif is_random(a):
|
||||
outval.append((S.One, a))
|
||||
|
||||
return outval
|
||||
elif isinstance(expr, Mul):
|
||||
return [cls._get_mul_nonrv_rv_tuple(expr)]
|
||||
elif is_random(expr):
|
||||
return [(S.One, expr)]
|
||||
|
||||
@classmethod
|
||||
def _get_mul_nonrv_rv_tuple(cls, m):
|
||||
rv = []
|
||||
nonrv = []
|
||||
for a in m.args:
|
||||
if is_random(a):
|
||||
rv.append(a)
|
||||
else:
|
||||
nonrv.append(a)
|
||||
return (Mul.fromiter(nonrv), Mul.fromiter(rv))
|
||||
|
||||
def _eval_rewrite_as_Expectation(self, arg1, arg2, condition=None, **kwargs):
|
||||
e1 = Expectation(arg1*arg2, condition)
|
||||
e2 = Expectation(arg1, condition)*Expectation(arg2, condition)
|
||||
return e1 - e2
|
||||
|
||||
def _eval_rewrite_as_Probability(self, arg1, arg2, condition=None, **kwargs):
|
||||
return self.rewrite(Expectation).rewrite(Probability)
|
||||
|
||||
def _eval_rewrite_as_Integral(self, arg1, arg2, condition=None, **kwargs):
|
||||
return covariance(self.args[0], self.args[1], self._condition, evaluate=False)
|
||||
|
||||
_eval_rewrite_as_Sum = _eval_rewrite_as_Integral
|
||||
|
||||
def evaluate_integral(self):
|
||||
return self.rewrite(Integral).doit()
|
||||
|
||||
|
||||
class Moment(Expr):
|
||||
"""
|
||||
Symbolic class for Moment
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Symbol, Integral
|
||||
>>> from sympy.stats import Normal, Expectation, Probability, Moment
|
||||
>>> mu = Symbol('mu', real=True)
|
||||
>>> sigma = Symbol('sigma', positive=True)
|
||||
>>> X = Normal('X', mu, sigma)
|
||||
>>> M = Moment(X, 3, 1)
|
||||
|
||||
To evaluate the result of Moment use `doit`:
|
||||
|
||||
>>> M.doit()
|
||||
mu**3 - 3*mu**2 + 3*mu*sigma**2 + 3*mu - 3*sigma**2 - 1
|
||||
|
||||
Rewrite the Moment expression in terms of Expectation:
|
||||
|
||||
>>> M.rewrite(Expectation)
|
||||
Expectation((X - 1)**3)
|
||||
|
||||
Rewrite the Moment expression in terms of Probability:
|
||||
|
||||
>>> M.rewrite(Probability)
|
||||
Integral((x - 1)**3*Probability(Eq(X, x)), (x, -oo, oo))
|
||||
|
||||
Rewrite the Moment expression in terms of Integral:
|
||||
|
||||
>>> M.rewrite(Integral)
|
||||
Integral(sqrt(2)*(X - 1)**3*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo))
|
||||
|
||||
"""
|
||||
def __new__(cls, X, n, c=0, condition=None, **kwargs):
|
||||
X = _sympify(X)
|
||||
n = _sympify(n)
|
||||
c = _sympify(c)
|
||||
if condition is not None:
|
||||
condition = _sympify(condition)
|
||||
return super().__new__(cls, X, n, c, condition)
|
||||
else:
|
||||
return super().__new__(cls, X, n, c)
|
||||
|
||||
def doit(self, **hints):
|
||||
return self.rewrite(Expectation).doit(**hints)
|
||||
|
||||
def _eval_rewrite_as_Expectation(self, X, n, c=0, condition=None, **kwargs):
|
||||
return Expectation((X - c)**n, condition)
|
||||
|
||||
def _eval_rewrite_as_Probability(self, X, n, c=0, condition=None, **kwargs):
|
||||
return self.rewrite(Expectation).rewrite(Probability)
|
||||
|
||||
def _eval_rewrite_as_Integral(self, X, n, c=0, condition=None, **kwargs):
|
||||
return self.rewrite(Expectation).rewrite(Integral)
|
||||
|
||||
|
||||
class CentralMoment(Expr):
|
||||
"""
|
||||
Symbolic class Central Moment
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import Symbol, Integral
|
||||
>>> from sympy.stats import Normal, Expectation, Probability, CentralMoment
|
||||
>>> mu = Symbol('mu', real=True)
|
||||
>>> sigma = Symbol('sigma', positive=True)
|
||||
>>> X = Normal('X', mu, sigma)
|
||||
>>> CM = CentralMoment(X, 4)
|
||||
|
||||
To evaluate the result of CentralMoment use `doit`:
|
||||
|
||||
>>> CM.doit().simplify()
|
||||
3*sigma**4
|
||||
|
||||
Rewrite the CentralMoment expression in terms of Expectation:
|
||||
|
||||
>>> CM.rewrite(Expectation)
|
||||
Expectation((-Expectation(X) + X)**4)
|
||||
|
||||
Rewrite the CentralMoment expression in terms of Probability:
|
||||
|
||||
>>> CM.rewrite(Probability)
|
||||
Integral((x - Integral(x*Probability(True), (x, -oo, oo)))**4*Probability(Eq(X, x)), (x, -oo, oo))
|
||||
|
||||
Rewrite the CentralMoment expression in terms of Integral:
|
||||
|
||||
>>> CM.rewrite(Integral)
|
||||
Integral(sqrt(2)*(X - Integral(sqrt(2)*X*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo)))**4*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo))
|
||||
|
||||
"""
|
||||
def __new__(cls, X, n, condition=None, **kwargs):
|
||||
X = _sympify(X)
|
||||
n = _sympify(n)
|
||||
if condition is not None:
|
||||
condition = _sympify(condition)
|
||||
return super().__new__(cls, X, n, condition)
|
||||
else:
|
||||
return super().__new__(cls, X, n)
|
||||
|
||||
def doit(self, **hints):
|
||||
return self.rewrite(Expectation).doit(**hints)
|
||||
|
||||
def _eval_rewrite_as_Expectation(self, X, n, condition=None, **kwargs):
|
||||
mu = Expectation(X, condition, **kwargs)
|
||||
return Moment(X, n, mu, condition, **kwargs).rewrite(Expectation)
|
||||
|
||||
def _eval_rewrite_as_Probability(self, X, n, condition=None, **kwargs):
|
||||
return self.rewrite(Expectation).rewrite(Probability)
|
||||
|
||||
def _eval_rewrite_as_Integral(self, X, n, condition=None, **kwargs):
|
||||
return self.rewrite(Expectation).rewrite(Integral)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,159 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.numbers import (oo, pi)
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.beta_functions import beta
|
||||
from sympy.functions.special.error_functions import erf
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.sets.sets import Interval
|
||||
from sympy.stats import (Normal, P, E, density, Gamma, Poisson, Rayleigh,
|
||||
variance, Bernoulli, Beta, Uniform, cdf)
|
||||
from sympy.stats.compound_rv import CompoundDistribution, CompoundPSpace
|
||||
from sympy.stats.crv_types import NormalDistribution
|
||||
from sympy.stats.drv_types import PoissonDistribution
|
||||
from sympy.stats.frv_types import BernoulliDistribution
|
||||
from sympy.testing.pytest import raises, ignore_warnings
|
||||
from sympy.stats.joint_rv_types import MultivariateNormalDistribution
|
||||
|
||||
from sympy.abc import x
|
||||
|
||||
|
||||
# helpers for testing troublesome unevaluated expressions
|
||||
flat = lambda s: ''.join(str(s).split())
|
||||
streq = lambda *a: len(set(map(flat, a))) == 1
|
||||
assert streq(x, x)
|
||||
assert streq(x, 'x')
|
||||
assert not streq(x, x + 1)
|
||||
|
||||
|
||||
def test_normal_CompoundDist():
|
||||
X = Normal('X', 1, 2)
|
||||
Y = Normal('X', X, 4)
|
||||
assert density(Y)(x).simplify() == sqrt(10)*exp(-x**2/40 + x/20 - S(1)/40)/(20*sqrt(pi))
|
||||
assert E(Y) == 1 # it is always equal to mean of X
|
||||
assert P(Y > 1) == S(1)/2 # as 1 is the mean
|
||||
assert P(Y > 5).simplify() == S(1)/2 - erf(sqrt(10)/5)/2
|
||||
assert variance(Y) == variance(X) + 4**2 # 2**2 + 4**2
|
||||
# https://math.stackexchange.com/questions/1484451/
|
||||
# (Contains proof of E and variance computation)
|
||||
|
||||
|
||||
def test_poisson_CompoundDist():
|
||||
k, t, y = symbols('k t y', positive=True, real=True)
|
||||
G = Gamma('G', k, t)
|
||||
D = Poisson('P', G)
|
||||
assert density(D)(y).simplify() == t**y*(t + 1)**(-k - y)*gamma(k + y)/(gamma(k)*gamma(y + 1))
|
||||
# https://en.wikipedia.org/wiki/Negative_binomial_distribution#Gamma%E2%80%93Poisson_mixture
|
||||
assert E(D).simplify() == k*t # mean of NegativeBinomialDistribution
|
||||
|
||||
|
||||
def test_bernoulli_CompoundDist():
|
||||
X = Beta('X', 1, 2)
|
||||
Y = Bernoulli('Y', X)
|
||||
assert density(Y).dict == {0: S(2)/3, 1: S(1)/3}
|
||||
assert E(Y) == P(Eq(Y, 1)) == S(1)/3
|
||||
assert variance(Y) == S(2)/9
|
||||
assert cdf(Y) == {0: S(2)/3, 1: 1}
|
||||
|
||||
# test issue 8128
|
||||
a = Bernoulli('a', S(1)/2)
|
||||
b = Bernoulli('b', a)
|
||||
assert density(b).dict == {0: S(1)/2, 1: S(1)/2}
|
||||
assert P(b > 0.5) == S(1)/2
|
||||
|
||||
X = Uniform('X', 0, 1)
|
||||
Y = Bernoulli('Y', X)
|
||||
assert E(Y) == S(1)/2
|
||||
assert P(Eq(Y, 1)) == E(Y)
|
||||
|
||||
|
||||
def test_unevaluated_CompoundDist():
|
||||
# these tests need to be removed once they work with evaluation as they are currently not
|
||||
# evaluated completely in sympy.
|
||||
R = Rayleigh('R', 4)
|
||||
X = Normal('X', 3, R)
|
||||
ans = '''
|
||||
Piecewise(((-sqrt(pi)*sinh(x/4 - 3/4) + sqrt(pi)*cosh(x/4 - 3/4))/(
|
||||
8*sqrt(pi)), Abs(arg(x - 3)) <= pi/4), (Integral(sqrt(2)*exp(-(x - 3)
|
||||
**2/(2*R**2))*exp(-R**2/32)/(32*sqrt(pi)), (R, 0, oo)), True))'''
|
||||
assert streq(density(X)(x), ans)
|
||||
|
||||
expre = '''
|
||||
Integral(X*Integral(sqrt(2)*exp(-(X-3)**2/(2*R**2))*exp(-R**2/32)/(32*
|
||||
sqrt(pi)),(R,0,oo)),(X,-oo,oo))'''
|
||||
with ignore_warnings(UserWarning): ### TODO: Restore tests once warnings are removed
|
||||
assert streq(E(X, evaluate=False).rewrite(Integral), expre)
|
||||
|
||||
X = Poisson('X', 1)
|
||||
Y = Poisson('Y', X)
|
||||
Z = Poisson('Z', Y)
|
||||
exprd = Sum(exp(-Y)*Y**x*Sum(exp(-1)*exp(-X)*X**Y/(factorial(X)*factorial(Y)
|
||||
), (X, 0, oo))/factorial(x), (Y, 0, oo))
|
||||
assert density(Z)(x) == exprd
|
||||
|
||||
N = Normal('N', 1, 2)
|
||||
M = Normal('M', 3, 4)
|
||||
D = Normal('D', M, N)
|
||||
exprd = '''
|
||||
Integral(sqrt(2)*exp(-(N-1)**2/8)*Integral(exp(-(x-M)**2/(2*N**2))*exp
|
||||
(-(M-3)**2/32)/(8*pi*N),(M,-oo,oo))/(4*sqrt(pi)),(N,-oo,oo))'''
|
||||
assert streq(density(D, evaluate=False)(x), exprd)
|
||||
|
||||
|
||||
def test_Compound_Distribution():
|
||||
X = Normal('X', 2, 4)
|
||||
N = NormalDistribution(X, 4)
|
||||
C = CompoundDistribution(N)
|
||||
assert C.is_Continuous
|
||||
assert C.set == Interval(-oo, oo)
|
||||
assert C.pdf(x, evaluate=True).simplify() == exp(-x**2/64 + x/16 - S(1)/16)/(8*sqrt(pi))
|
||||
|
||||
assert not isinstance(CompoundDistribution(NormalDistribution(2, 3)),
|
||||
CompoundDistribution)
|
||||
M = MultivariateNormalDistribution([1, 2], [[2, 1], [1, 2]])
|
||||
raises(NotImplementedError, lambda: CompoundDistribution(M))
|
||||
|
||||
X = Beta('X', 2, 4)
|
||||
B = BernoulliDistribution(X, 1, 0)
|
||||
C = CompoundDistribution(B)
|
||||
assert C.is_Finite
|
||||
assert C.set == {0, 1}
|
||||
y = symbols('y', negative=False, integer=True)
|
||||
assert C.pdf(y, evaluate=True) == Piecewise((S(1)/(30*beta(2, 4)), Eq(y, 0)),
|
||||
(S(1)/(60*beta(2, 4)), Eq(y, 1)), (0, True))
|
||||
|
||||
k, t, z = symbols('k t z', positive=True, real=True)
|
||||
G = Gamma('G', k, t)
|
||||
X = PoissonDistribution(G)
|
||||
C = CompoundDistribution(X)
|
||||
assert C.is_Discrete
|
||||
assert C.set == S.Naturals0
|
||||
assert C.pdf(z, evaluate=True).simplify() == t**z*(t + 1)**(-k - z)*gamma(k \
|
||||
+ z)/(gamma(k)*gamma(z + 1))
|
||||
|
||||
|
||||
def test_compound_pspace():
|
||||
X = Normal('X', 2, 4)
|
||||
Y = Normal('Y', 3, 6)
|
||||
assert not isinstance(Y.pspace, CompoundPSpace)
|
||||
N = NormalDistribution(1, 2)
|
||||
D = PoissonDistribution(3)
|
||||
B = BernoulliDistribution(0.2, 1, 0)
|
||||
pspace1 = CompoundPSpace('N', N)
|
||||
pspace2 = CompoundPSpace('D', D)
|
||||
pspace3 = CompoundPSpace('B', B)
|
||||
assert not isinstance(pspace1, CompoundPSpace)
|
||||
assert not isinstance(pspace2, CompoundPSpace)
|
||||
assert not isinstance(pspace3, CompoundPSpace)
|
||||
M = MultivariateNormalDistribution([1, 2], [[2, 1], [1, 2]])
|
||||
raises(ValueError, lambda: CompoundPSpace('M', M))
|
||||
Y = Normal('Y', X, 6)
|
||||
assert isinstance(Y.pspace, CompoundPSpace)
|
||||
assert Y.pspace.distribution == CompoundDistribution(NormalDistribution(X, 6))
|
||||
assert Y.pspace.domain.set == Interval(-oo, oo)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,312 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.numbers import (I, Rational, oo, pi)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import Symbol
|
||||
from sympy.functions.elementary.complexes import (im, re)
|
||||
from sympy.functions.elementary.exponential import log
|
||||
from sympy.functions.elementary.integers import floor
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.bessel import besseli
|
||||
from sympy.functions.special.beta_functions import beta
|
||||
from sympy.functions.special.zeta_functions import zeta
|
||||
from sympy.sets.sets import FiniteSet
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.utilities.lambdify import lambdify
|
||||
from sympy.core.relational import Eq, Ne
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.logic.boolalg import Or
|
||||
from sympy.sets.fancysets import Range
|
||||
from sympy.stats import (P, E, variance, density, characteristic_function,
|
||||
where, moment_generating_function, skewness, cdf,
|
||||
kurtosis, coskewness)
|
||||
from sympy.stats.drv_types import (PoissonDistribution, GeometricDistribution,
|
||||
FlorySchulz, Poisson, Geometric, Hermite, Logarithmic,
|
||||
NegativeBinomial, Skellam, YuleSimon, Zeta,
|
||||
DiscreteRV)
|
||||
from sympy.testing.pytest import slow, nocache_fail, raises, skip
|
||||
from sympy.stats.symbolic_probability import Expectation
|
||||
from sympy.functions.combinatorial.factorials import FallingFactorial
|
||||
|
||||
x = Symbol('x')
|
||||
|
||||
|
||||
def test_PoissonDistribution():
|
||||
l = 3
|
||||
p = PoissonDistribution(l)
|
||||
assert abs(p.cdf(10).evalf() - 1) < .001
|
||||
assert abs(p.cdf(10.4).evalf() - 1) < .001
|
||||
assert p.expectation(x, x) == l
|
||||
assert p.expectation(x**2, x) - p.expectation(x, x)**2 == l
|
||||
|
||||
|
||||
def test_Poisson():
|
||||
l = 3
|
||||
x = Poisson('x', l)
|
||||
assert E(x) == l
|
||||
assert E(2*x) == 2*l
|
||||
assert variance(x) == l
|
||||
assert density(x) == PoissonDistribution(l)
|
||||
assert isinstance(E(x, evaluate=False), Expectation)
|
||||
assert isinstance(E(2*x, evaluate=False), Expectation)
|
||||
# issue 8248
|
||||
assert x.pspace.compute_expectation(1) == 1
|
||||
# issue 27344
|
||||
try:
|
||||
import numpy as np
|
||||
except ImportError:
|
||||
skip("numpy not installed")
|
||||
y = Poisson('y', np.float64(4.72544290380919e-11))
|
||||
assert E(y) == 4.72544290380919e-11
|
||||
y = Poisson('y', np.float64(4.725442903809197e-11))
|
||||
assert E(y) == 4.725442903809197e-11
|
||||
l2 = 5
|
||||
z = Poisson('z', l2)
|
||||
assert E(z) == l2
|
||||
assert E(FallingFactorial(z, 3)) == l2**3
|
||||
assert E(z**2) == l2 + l2**2
|
||||
|
||||
|
||||
def test_FlorySchulz():
|
||||
a = Symbol("a")
|
||||
z = Symbol("z")
|
||||
x = FlorySchulz('x', a)
|
||||
assert E(x) == (2 - a)/a
|
||||
assert (variance(x) - 2*(1 - a)/a**2).simplify() == S(0)
|
||||
assert density(x)(z) == a**2*z*(1 - a)**(z - 1)
|
||||
|
||||
|
||||
@slow
|
||||
def test_GeometricDistribution():
|
||||
p = S.One / 5
|
||||
d = GeometricDistribution(p)
|
||||
assert d.expectation(x, x) == 1/p
|
||||
assert d.expectation(x**2, x) - d.expectation(x, x)**2 == (1-p)/p**2
|
||||
assert abs(d.cdf(20000).evalf() - 1) < .001
|
||||
assert abs(d.cdf(20000.8).evalf() - 1) < .001
|
||||
G = Geometric('G', p=S(1)/4)
|
||||
assert cdf(G)(S(7)/2) == P(G <= S(7)/2)
|
||||
|
||||
X = Geometric('X', Rational(1, 5))
|
||||
Y = Geometric('Y', Rational(3, 10))
|
||||
assert coskewness(X, X + Y, X + 2*Y).simplify() == sqrt(230)*Rational(81, 1150)
|
||||
|
||||
|
||||
def test_Hermite():
|
||||
a1 = Symbol("a1", positive=True)
|
||||
a2 = Symbol("a2", negative=True)
|
||||
raises(ValueError, lambda: Hermite("H", a1, a2))
|
||||
|
||||
a1 = Symbol("a1", negative=True)
|
||||
a2 = Symbol("a2", positive=True)
|
||||
raises(ValueError, lambda: Hermite("H", a1, a2))
|
||||
|
||||
a1 = Symbol("a1", positive=True)
|
||||
x = Symbol("x")
|
||||
H = Hermite("H", a1, a2)
|
||||
assert moment_generating_function(H)(x) == exp(a1*(exp(x) - 1)
|
||||
+ a2*(exp(2*x) - 1))
|
||||
assert characteristic_function(H)(x) == exp(a1*(exp(I*x) - 1)
|
||||
+ a2*(exp(2*I*x) - 1))
|
||||
assert E(H) == a1 + 2*a2
|
||||
|
||||
H = Hermite("H", a1=5, a2=4)
|
||||
assert density(H)(2) == 33*exp(-9)/2
|
||||
assert E(H) == 13
|
||||
assert variance(H) == 21
|
||||
assert kurtosis(H) == Rational(464,147)
|
||||
assert skewness(H) == 37*sqrt(21)/441
|
||||
|
||||
def test_Logarithmic():
|
||||
p = S.Half
|
||||
x = Logarithmic('x', p)
|
||||
assert E(x) == -p / ((1 - p) * log(1 - p))
|
||||
assert variance(x) == -1/log(2)**2 + 2/log(2)
|
||||
assert E(2*x**2 + 3*x + 4) == 4 + 7 / log(2)
|
||||
assert isinstance(E(x, evaluate=False), Expectation)
|
||||
|
||||
|
||||
@nocache_fail
|
||||
def test_negative_binomial():
|
||||
r = 5
|
||||
p = S.One / 3
|
||||
x = NegativeBinomial('x', r, p)
|
||||
assert E(x) == r * (1 - p) / p
|
||||
# This hangs when run with the cache disabled:
|
||||
assert variance(x) == r * (1 - p) / p**2
|
||||
assert E(x**5 + 2*x + 3) == E(x**5) + 2*E(x) + 3 == Rational(796473, 1)
|
||||
assert isinstance(E(x, evaluate=False), Expectation)
|
||||
|
||||
|
||||
def test_skellam():
|
||||
mu1 = Symbol('mu1')
|
||||
mu2 = Symbol('mu2')
|
||||
z = Symbol('z')
|
||||
X = Skellam('x', mu1, mu2)
|
||||
|
||||
assert density(X)(z) == (mu1/mu2)**(z/2) * \
|
||||
exp(-mu1 - mu2)*besseli(z, 2*sqrt(mu1*mu2))
|
||||
assert skewness(X).expand() == mu1/(mu1*sqrt(mu1 + mu2) + mu2 *
|
||||
sqrt(mu1 + mu2)) - mu2/(mu1*sqrt(mu1 + mu2) + mu2*sqrt(mu1 + mu2))
|
||||
assert variance(X).expand() == mu1 + mu2
|
||||
assert E(X) == mu1 - mu2
|
||||
assert characteristic_function(X)(z) == exp(
|
||||
mu1*exp(I*z) - mu1 - mu2 + mu2*exp(-I*z))
|
||||
assert moment_generating_function(X)(z) == exp(
|
||||
mu1*exp(z) - mu1 - mu2 + mu2*exp(-z))
|
||||
|
||||
|
||||
def test_yule_simon():
|
||||
from sympy.core.singleton import S
|
||||
rho = S(3)
|
||||
x = YuleSimon('x', rho)
|
||||
assert simplify(E(x)) == rho / (rho - 1)
|
||||
assert simplify(variance(x)) == rho**2 / ((rho - 1)**2 * (rho - 2))
|
||||
assert isinstance(E(x, evaluate=False), Expectation)
|
||||
# To test the cdf function
|
||||
assert cdf(x)(x) == Piecewise((-beta(floor(x), 4)*floor(x) + 1, x >= 1), (0, True))
|
||||
|
||||
|
||||
def test_zeta():
|
||||
s = S(5)
|
||||
x = Zeta('x', s)
|
||||
assert E(x) == zeta(s-1) / zeta(s)
|
||||
assert simplify(variance(x)) == (
|
||||
zeta(s) * zeta(s-2) - zeta(s-1)**2) / zeta(s)**2
|
||||
|
||||
|
||||
def test_discrete_probability():
|
||||
X = Geometric('X', Rational(1, 5))
|
||||
Y = Poisson('Y', 4)
|
||||
G = Geometric('e', x)
|
||||
assert P(Eq(X, 3)) == Rational(16, 125)
|
||||
assert P(X < 3) == Rational(9, 25)
|
||||
assert P(X > 3) == Rational(64, 125)
|
||||
assert P(X >= 3) == Rational(16, 25)
|
||||
assert P(X <= 3) == Rational(61, 125)
|
||||
assert P(Ne(X, 3)) == Rational(109, 125)
|
||||
assert P(Eq(Y, 3)) == 32*exp(-4)/3
|
||||
assert P(Y < 3) == 13*exp(-4)
|
||||
assert P(Y > 3).equals(32*(Rational(-71, 32) + 3*exp(4)/32)*exp(-4)/3)
|
||||
assert P(Y >= 3).equals(32*(Rational(-39, 32) + 3*exp(4)/32)*exp(-4)/3)
|
||||
assert P(Y <= 3) == 71*exp(-4)/3
|
||||
assert P(Ne(Y, 3)).equals(
|
||||
13*exp(-4) + 32*(Rational(-71, 32) + 3*exp(4)/32)*exp(-4)/3)
|
||||
assert P(X < S.Infinity) is S.One
|
||||
assert P(X > S.Infinity) is S.Zero
|
||||
assert P(G < 3) == x*(2-x)
|
||||
assert P(Eq(G, 3)) == x*(-x + 1)**2
|
||||
|
||||
|
||||
def test_DiscreteRV():
|
||||
p = S(1)/2
|
||||
x = Symbol('x', integer=True, positive=True)
|
||||
pdf = p*(1 - p)**(x - 1) # pdf of Geometric Distribution
|
||||
D = DiscreteRV(x, pdf, set=S.Naturals, check=True)
|
||||
assert E(D) == E(Geometric('G', S(1)/2)) == 2
|
||||
assert P(D > 3) == S(1)/8
|
||||
assert D.pspace.domain.set == S.Naturals
|
||||
raises(ValueError, lambda: DiscreteRV(x, x, FiniteSet(*range(4)), check=True))
|
||||
|
||||
# purposeful invalid pmf but it should not raise since check=False
|
||||
# see test_drv_types.test_ContinuousRV for explanation
|
||||
X = DiscreteRV(x, 1/x, S.Naturals)
|
||||
assert P(X < 2) == 1
|
||||
assert E(X) == oo
|
||||
|
||||
def test_precomputed_characteristic_functions():
|
||||
import mpmath
|
||||
|
||||
def test_cf(dist, support_lower_limit, support_upper_limit):
|
||||
pdf = density(dist)
|
||||
t = S('t')
|
||||
x = S('x')
|
||||
|
||||
# first function is the hardcoded CF of the distribution
|
||||
cf1 = lambdify([t], characteristic_function(dist)(t), 'mpmath')
|
||||
|
||||
# second function is the Fourier transform of the density function
|
||||
f = lambdify([x, t], pdf(x)*exp(I*x*t), 'mpmath')
|
||||
cf2 = lambda t: mpmath.nsum(lambda x: f(x, t), [
|
||||
support_lower_limit, support_upper_limit], maxdegree=10)
|
||||
|
||||
# compare the two functions at various points
|
||||
for test_point in [2, 5, 8, 11]:
|
||||
n1 = cf1(test_point)
|
||||
n2 = cf2(test_point)
|
||||
|
||||
assert abs(re(n1) - re(n2)) < 1e-12
|
||||
assert abs(im(n1) - im(n2)) < 1e-12
|
||||
|
||||
test_cf(Geometric('g', Rational(1, 3)), 1, mpmath.inf)
|
||||
test_cf(Logarithmic('l', Rational(1, 5)), 1, mpmath.inf)
|
||||
test_cf(NegativeBinomial('n', 5, Rational(1, 7)), 0, mpmath.inf)
|
||||
test_cf(Poisson('p', 5), 0, mpmath.inf)
|
||||
test_cf(YuleSimon('y', 5), 1, mpmath.inf)
|
||||
test_cf(Zeta('z', 5), 1, mpmath.inf)
|
||||
|
||||
|
||||
def test_moment_generating_functions():
|
||||
t = S('t')
|
||||
|
||||
geometric_mgf = moment_generating_function(Geometric('g', S.Half))(t)
|
||||
assert geometric_mgf.diff(t).subs(t, 0) == 2
|
||||
|
||||
logarithmic_mgf = moment_generating_function(Logarithmic('l', S.Half))(t)
|
||||
assert logarithmic_mgf.diff(t).subs(t, 0) == 1/log(2)
|
||||
|
||||
negative_binomial_mgf = moment_generating_function(
|
||||
NegativeBinomial('n', 5, Rational(1, 3)))(t)
|
||||
assert negative_binomial_mgf.diff(t).subs(t, 0) == Rational(10, 1)
|
||||
|
||||
poisson_mgf = moment_generating_function(Poisson('p', 5))(t)
|
||||
assert poisson_mgf.diff(t).subs(t, 0) == 5
|
||||
|
||||
skellam_mgf = moment_generating_function(Skellam('s', 1, 1))(t)
|
||||
assert skellam_mgf.diff(t).subs(
|
||||
t, 2) == (-exp(-2) + exp(2))*exp(-2 + exp(-2) + exp(2))
|
||||
|
||||
yule_simon_mgf = moment_generating_function(YuleSimon('y', 3))(t)
|
||||
assert simplify(yule_simon_mgf.diff(t).subs(t, 0)) == Rational(3, 2)
|
||||
|
||||
zeta_mgf = moment_generating_function(Zeta('z', 5))(t)
|
||||
assert zeta_mgf.diff(t).subs(t, 0) == pi**4/(90*zeta(5))
|
||||
|
||||
|
||||
def test_Or():
|
||||
X = Geometric('X', S.Half)
|
||||
assert P(Or(X < 3, X > 4)) == Rational(13, 16)
|
||||
assert P(Or(X > 2, X > 1)) == P(X > 1)
|
||||
assert P(Or(X >= 3, X < 3)) == 1
|
||||
|
||||
|
||||
def test_where():
|
||||
X = Geometric('X', Rational(1, 5))
|
||||
Y = Poisson('Y', 4)
|
||||
assert where(X**2 > 4).set == Range(3, S.Infinity, 1)
|
||||
assert where(X**2 >= 4).set == Range(2, S.Infinity, 1)
|
||||
assert where(Y**2 < 9).set == Range(0, 3, 1)
|
||||
assert where(Y**2 <= 9).set == Range(0, 4, 1)
|
||||
|
||||
|
||||
def test_conditional():
|
||||
X = Geometric('X', Rational(2, 3))
|
||||
Y = Poisson('Y', 3)
|
||||
assert P(X > 2, X > 3) == 1
|
||||
assert P(X > 3, X > 2) == Rational(1, 3)
|
||||
assert P(Y > 2, Y < 2) == 0
|
||||
assert P(Eq(Y, 3), Y >= 0) == 9*exp(-3)/2
|
||||
assert P(Eq(Y, 3), Eq(Y, 2)) == 0
|
||||
assert P(X < 2, Eq(X, 2)) == 0
|
||||
assert P(X > 2, Eq(X, 3)) == 1
|
||||
|
||||
|
||||
def test_product_spaces():
|
||||
X1 = Geometric('X1', S.Half)
|
||||
X2 = Geometric('X2', Rational(1, 3))
|
||||
assert str(P(X1 + X2 < 3).rewrite(Sum)) == (
|
||||
"Sum(Piecewise((1/(4*2**n), n >= -1), (0, True)), (n, -oo, -1))/3")
|
||||
assert str(P(X1 + X2 > 3).rewrite(Sum)) == (
|
||||
'Sum(Piecewise((2**(X2 - n - 2)*(2/3)**(X2 - 1)/6, '
|
||||
'X2 - n <= 2), (0, True)), (X2, 1, oo), (n, 1, oo))')
|
||||
assert P(Eq(X1 + X2, 3)) == Rational(1, 12)
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
from sympy.core.function import Function
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.stats.error_prop import variance_prop
|
||||
from sympy.stats.symbolic_probability import (RandomSymbol, Variance,
|
||||
Covariance)
|
||||
|
||||
|
||||
def test_variance_prop():
|
||||
x, y, z = symbols('x y z')
|
||||
phi, t = consts = symbols('phi t')
|
||||
a = RandomSymbol(x)
|
||||
var_x = Variance(a)
|
||||
var_y = Variance(RandomSymbol(y))
|
||||
var_z = Variance(RandomSymbol(z))
|
||||
f = Function('f')(x)
|
||||
cases = {
|
||||
x + y: var_x + var_y,
|
||||
a + y: var_x + var_y,
|
||||
x + y + z: var_x + var_y + var_z,
|
||||
2*x: 4*var_x,
|
||||
x*y: var_x*y**2 + var_y*x**2,
|
||||
1/x: var_x/x**4,
|
||||
x/y: (var_x*y**2 + var_y*x**2)/y**4,
|
||||
exp(x): var_x*exp(2*x),
|
||||
exp(2*x): 4*var_x*exp(4*x),
|
||||
exp(-x*t): t**2*var_x*exp(-2*t*x),
|
||||
f: Variance(f),
|
||||
}
|
||||
for inp, out in cases.items():
|
||||
obs = variance_prop(inp, consts=consts)
|
||||
assert out == obs
|
||||
|
||||
def test_variance_prop_with_covar():
|
||||
x, y, z = symbols('x y z')
|
||||
phi, t = consts = symbols('phi t')
|
||||
a = RandomSymbol(x)
|
||||
var_x = Variance(a)
|
||||
b = RandomSymbol(y)
|
||||
var_y = Variance(b)
|
||||
c = RandomSymbol(z)
|
||||
var_z = Variance(c)
|
||||
covar_x_y = Covariance(a, b)
|
||||
covar_x_z = Covariance(a, c)
|
||||
covar_y_z = Covariance(b, c)
|
||||
cases = {
|
||||
x + y: var_x + var_y + 2*covar_x_y,
|
||||
a + y: var_x + var_y + 2*covar_x_y,
|
||||
x + y + z: var_x + var_y + var_z + \
|
||||
2*covar_x_y + 2*covar_x_z + 2*covar_y_z,
|
||||
2*x: 4*var_x,
|
||||
x*y: var_x*y**2 + var_y*x**2 + 2*covar_x_y/(x*y),
|
||||
1/x: var_x/x**4,
|
||||
exp(x): var_x*exp(2*x),
|
||||
exp(2*x): 4*var_x*exp(4*x),
|
||||
exp(-x*t): t**2*var_x*exp(-2*t*x),
|
||||
}
|
||||
for inp, out in cases.items():
|
||||
obs = variance_prop(inp, consts=consts, include_covar=True)
|
||||
assert out == obs
|
||||
|
|
@ -0,0 +1,509 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.containers import (Dict, Tuple)
|
||||
from sympy.core.function import Function
|
||||
from sympy.core.numbers import (I, Rational, nan)
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, Symbol, symbols)
|
||||
from sympy.core.sympify import sympify
|
||||
from sympy.functions.combinatorial.factorials import binomial
|
||||
from sympy.functions.combinatorial.numbers import harmonic
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.elementary.trigonometric import cos
|
||||
from sympy.functions.special.beta_functions import beta
|
||||
from sympy.logic.boolalg import (And, Or)
|
||||
from sympy.polys.polytools import cancel
|
||||
from sympy.sets.sets import FiniteSet
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.matrices import Matrix
|
||||
from sympy.stats import (DiscreteUniform, Die, Bernoulli, Coin, Binomial, BetaBinomial,
|
||||
Hypergeometric, Rademacher, IdealSoliton, RobustSoliton, P, E, variance,
|
||||
covariance, skewness, density, where, FiniteRV, pspace, cdf,
|
||||
correlation, moment, cmoment, smoment, characteristic_function,
|
||||
moment_generating_function, quantile, kurtosis, median, coskewness)
|
||||
from sympy.stats.frv_types import DieDistribution, BinomialDistribution, \
|
||||
HypergeometricDistribution
|
||||
from sympy.stats.rv import Density
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
|
||||
def BayesTest(A, B):
|
||||
assert P(A, B) == P(And(A, B)) / P(B)
|
||||
assert P(A, B) == P(B, A) * P(A) / P(B)
|
||||
|
||||
|
||||
def test_discreteuniform():
|
||||
# Symbolic
|
||||
a, b, c, t = symbols('a b c t')
|
||||
X = DiscreteUniform('X', [a, b, c])
|
||||
|
||||
assert E(X) == (a + b + c)/3
|
||||
assert simplify(variance(X)
|
||||
- ((a**2 + b**2 + c**2)/3 - (a/3 + b/3 + c/3)**2)) == 0
|
||||
assert P(Eq(X, a)) == P(Eq(X, b)) == P(Eq(X, c)) == S('1/3')
|
||||
|
||||
Y = DiscreteUniform('Y', range(-5, 5))
|
||||
|
||||
# Numeric
|
||||
assert E(Y) == S('-1/2')
|
||||
assert variance(Y) == S('33/4')
|
||||
assert median(Y) == FiniteSet(-1, 0)
|
||||
|
||||
for x in range(-5, 5):
|
||||
assert P(Eq(Y, x)) == S('1/10')
|
||||
assert P(Y <= x) == S(x + 6)/10
|
||||
assert P(Y >= x) == S(5 - x)/10
|
||||
|
||||
assert dict(density(Die('D', 6)).items()) == \
|
||||
dict(density(DiscreteUniform('U', range(1, 7))).items())
|
||||
|
||||
assert characteristic_function(X)(t) == exp(I*a*t)/3 + exp(I*b*t)/3 + exp(I*c*t)/3
|
||||
assert moment_generating_function(X)(t) == exp(a*t)/3 + exp(b*t)/3 + exp(c*t)/3
|
||||
# issue 18611
|
||||
raises(ValueError, lambda: DiscreteUniform('Z', [a, a, a, b, b, c]))
|
||||
|
||||
def test_dice():
|
||||
# TODO: Make iid method!
|
||||
X, Y, Z = Die('X', 6), Die('Y', 6), Die('Z', 6)
|
||||
a, b, t, p = symbols('a b t p')
|
||||
|
||||
assert E(X) == 3 + S.Half
|
||||
assert variance(X) == Rational(35, 12)
|
||||
assert E(X + Y) == 7
|
||||
assert E(X + X) == 7
|
||||
assert E(a*X + b) == a*E(X) + b
|
||||
assert variance(X + Y) == variance(X) + variance(Y) == cmoment(X + Y, 2)
|
||||
assert variance(X + X) == 4 * variance(X) == cmoment(X + X, 2)
|
||||
assert cmoment(X, 0) == 1
|
||||
assert cmoment(4*X, 3) == 64*cmoment(X, 3)
|
||||
assert covariance(X, Y) is S.Zero
|
||||
assert covariance(X, X + Y) == variance(X)
|
||||
assert density(Eq(cos(X*S.Pi), 1))[True] == S.Half
|
||||
assert correlation(X, Y) == 0
|
||||
assert correlation(X, Y) == correlation(Y, X)
|
||||
assert smoment(X + Y, 3) == skewness(X + Y)
|
||||
assert smoment(X + Y, 4) == kurtosis(X + Y)
|
||||
assert smoment(X, 0) == 1
|
||||
assert P(X > 3) == S.Half
|
||||
assert P(2*X > 6) == S.Half
|
||||
assert P(X > Y) == Rational(5, 12)
|
||||
assert P(Eq(X, Y)) == P(Eq(X, 1))
|
||||
|
||||
assert E(X, X > 3) == 5 == moment(X, 1, 0, X > 3)
|
||||
assert E(X, Y > 3) == E(X) == moment(X, 1, 0, Y > 3)
|
||||
assert E(X + Y, Eq(X, Y)) == E(2*X)
|
||||
assert moment(X, 0) == 1
|
||||
assert moment(5*X, 2) == 25*moment(X, 2)
|
||||
assert quantile(X)(p) == Piecewise((nan, (p > 1) | (p < 0)),\
|
||||
(S.One, p <= Rational(1, 6)), (S(2), p <= Rational(1, 3)), (S(3), p <= S.Half),\
|
||||
(S(4), p <= Rational(2, 3)), (S(5), p <= Rational(5, 6)), (S(6), p <= 1))
|
||||
|
||||
assert P(X > 3, X > 3) is S.One
|
||||
assert P(X > Y, Eq(Y, 6)) is S.Zero
|
||||
assert P(Eq(X + Y, 12)) == Rational(1, 36)
|
||||
assert P(Eq(X + Y, 12), Eq(X, 6)) == Rational(1, 6)
|
||||
|
||||
assert density(X + Y) == density(Y + Z) != density(X + X)
|
||||
d = density(2*X + Y**Z)
|
||||
assert d[S(22)] == Rational(1, 108) and d[S(4100)] == Rational(1, 216) and S(3130) not in d
|
||||
|
||||
assert pspace(X).domain.as_boolean() == Or(
|
||||
*[Eq(X.symbol, i) for i in [1, 2, 3, 4, 5, 6]])
|
||||
|
||||
assert where(X > 3).set == FiniteSet(4, 5, 6)
|
||||
|
||||
assert characteristic_function(X)(t) == exp(6*I*t)/6 + exp(5*I*t)/6 + exp(4*I*t)/6 + exp(3*I*t)/6 + exp(2*I*t)/6 + exp(I*t)/6
|
||||
assert moment_generating_function(X)(t) == exp(6*t)/6 + exp(5*t)/6 + exp(4*t)/6 + exp(3*t)/6 + exp(2*t)/6 + exp(t)/6
|
||||
assert median(X) == FiniteSet(3, 4)
|
||||
D = Die('D', 7)
|
||||
assert median(D) == FiniteSet(4)
|
||||
# Bayes test for die
|
||||
BayesTest(X > 3, X + Y < 5)
|
||||
BayesTest(Eq(X - Y, Z), Z > Y)
|
||||
BayesTest(X > 3, X > 2)
|
||||
|
||||
# arg test for die
|
||||
raises(ValueError, lambda: Die('X', -1)) # issue 8105: negative sides.
|
||||
raises(ValueError, lambda: Die('X', 0))
|
||||
raises(ValueError, lambda: Die('X', 1.5)) # issue 8103: non integer sides.
|
||||
|
||||
# symbolic test for die
|
||||
n, k = symbols('n, k', positive=True)
|
||||
D = Die('D', n)
|
||||
dens = density(D).dict
|
||||
assert dens == Density(DieDistribution(n))
|
||||
assert set(dens.subs(n, 4).doit().keys()) == {1, 2, 3, 4}
|
||||
assert set(dens.subs(n, 4).doit().values()) == {Rational(1, 4)}
|
||||
k = Dummy('k', integer=True)
|
||||
assert E(D).dummy_eq(
|
||||
Sum(Piecewise((k/n, k <= n), (0, True)), (k, 1, n)))
|
||||
assert variance(D).subs(n, 6).doit() == Rational(35, 12)
|
||||
|
||||
ki = Dummy('ki')
|
||||
cumuf = cdf(D)(k)
|
||||
assert cumuf.dummy_eq(
|
||||
Sum(Piecewise((1/n, (ki >= 1) & (ki <= n)), (0, True)), (ki, 1, k)))
|
||||
assert cumuf.subs({n: 6, k: 2}).doit() == Rational(1, 3)
|
||||
|
||||
t = Dummy('t')
|
||||
cf = characteristic_function(D)(t)
|
||||
assert cf.dummy_eq(
|
||||
Sum(Piecewise((exp(ki*I*t)/n, (ki >= 1) & (ki <= n)), (0, True)), (ki, 1, n)))
|
||||
assert cf.subs(n, 3).doit() == exp(3*I*t)/3 + exp(2*I*t)/3 + exp(I*t)/3
|
||||
mgf = moment_generating_function(D)(t)
|
||||
assert mgf.dummy_eq(
|
||||
Sum(Piecewise((exp(ki*t)/n, (ki >= 1) & (ki <= n)), (0, True)), (ki, 1, n)))
|
||||
assert mgf.subs(n, 3).doit() == exp(3*t)/3 + exp(2*t)/3 + exp(t)/3
|
||||
|
||||
def test_given():
|
||||
X = Die('X', 6)
|
||||
assert density(X, X > 5) == {S(6): S.One}
|
||||
assert where(X > 2, X > 5).as_boolean() == Eq(X.symbol, 6)
|
||||
|
||||
|
||||
def test_domains():
|
||||
X, Y = Die('x', 6), Die('y', 6)
|
||||
x, y = X.symbol, Y.symbol
|
||||
# Domains
|
||||
d = where(X > Y)
|
||||
assert d.condition == (x > y)
|
||||
d = where(And(X > Y, Y > 3))
|
||||
assert d.as_boolean() == Or(And(Eq(x, 5), Eq(y, 4)), And(Eq(x, 6),
|
||||
Eq(y, 5)), And(Eq(x, 6), Eq(y, 4)))
|
||||
assert len(d.elements) == 3
|
||||
|
||||
assert len(pspace(X + Y).domain.elements) == 36
|
||||
|
||||
Z = Die('x', 4)
|
||||
|
||||
raises(ValueError, lambda: P(X > Z)) # Two domains with same internal symbol
|
||||
|
||||
assert pspace(X + Y).domain.set == FiniteSet(1, 2, 3, 4, 5, 6)**2
|
||||
|
||||
assert where(X > 3).set == FiniteSet(4, 5, 6)
|
||||
assert X.pspace.domain.dict == FiniteSet(
|
||||
*[Dict({X.symbol: i}) for i in range(1, 7)])
|
||||
|
||||
assert where(X > Y).dict == FiniteSet(*[Dict({X.symbol: i, Y.symbol: j})
|
||||
for i in range(1, 7) for j in range(1, 7) if i > j])
|
||||
|
||||
def test_bernoulli():
|
||||
p, a, b, t = symbols('p a b t')
|
||||
X = Bernoulli('B', p, a, b)
|
||||
|
||||
assert E(X) == a*p + b*(-p + 1)
|
||||
assert density(X)[a] == p
|
||||
assert density(X)[b] == 1 - p
|
||||
assert characteristic_function(X)(t) == p * exp(I * a * t) + (-p + 1) * exp(I * b * t)
|
||||
assert moment_generating_function(X)(t) == p * exp(a * t) + (-p + 1) * exp(b * t)
|
||||
|
||||
X = Bernoulli('B', p, 1, 0)
|
||||
z = Symbol("z")
|
||||
|
||||
assert E(X) == p
|
||||
assert simplify(variance(X)) == p*(1 - p)
|
||||
assert E(a*X + b) == a*E(X) + b
|
||||
assert simplify(variance(a*X + b)) == simplify(a**2 * variance(X))
|
||||
assert quantile(X)(z) == Piecewise((nan, (z > 1) | (z < 0)), (0, z <= 1 - p), (1, z <= 1))
|
||||
Y = Bernoulli('Y', Rational(1, 2))
|
||||
assert median(Y) == FiniteSet(0, 1)
|
||||
Z = Bernoulli('Z', Rational(2, 3))
|
||||
assert median(Z) == FiniteSet(1)
|
||||
raises(ValueError, lambda: Bernoulli('B', 1.5))
|
||||
raises(ValueError, lambda: Bernoulli('B', -0.5))
|
||||
|
||||
#issue 8248
|
||||
assert X.pspace.compute_expectation(1) == 1
|
||||
|
||||
p = Rational(1, 5)
|
||||
X = Binomial('X', 5, p)
|
||||
Y = Binomial('Y', 7, 2*p)
|
||||
Z = Binomial('Z', 9, 3*p)
|
||||
assert coskewness(Y + Z, X + Y, X + Z).simplify() == 0
|
||||
assert coskewness(Y + 2*X + Z, X + 2*Y + Z, X + 2*Z + Y).simplify() == \
|
||||
sqrt(1529)*Rational(12, 16819)
|
||||
assert coskewness(Y + 2*X + Z, X + 2*Y + Z, X + 2*Z + Y, X < 2).simplify() \
|
||||
== -sqrt(357451121)*Rational(2812, 4646864573)
|
||||
|
||||
def test_cdf():
|
||||
D = Die('D', 6)
|
||||
o = S.One
|
||||
|
||||
assert cdf(
|
||||
D) == sympify({1: o/6, 2: o/3, 3: o/2, 4: 2*o/3, 5: 5*o/6, 6: o})
|
||||
|
||||
|
||||
def test_coins():
|
||||
C, D = Coin('C'), Coin('D')
|
||||
H, T = symbols('H, T')
|
||||
assert P(Eq(C, D)) == S.Half
|
||||
assert density(Tuple(C, D)) == {(H, H): Rational(1, 4), (H, T): Rational(1, 4),
|
||||
(T, H): Rational(1, 4), (T, T): Rational(1, 4)}
|
||||
assert dict(density(C).items()) == {H: S.Half, T: S.Half}
|
||||
|
||||
F = Coin('F', Rational(1, 10))
|
||||
assert P(Eq(F, H)) == Rational(1, 10)
|
||||
|
||||
d = pspace(C).domain
|
||||
|
||||
assert d.as_boolean() == Or(Eq(C.symbol, H), Eq(C.symbol, T))
|
||||
|
||||
raises(ValueError, lambda: P(C > D)) # Can't intelligently compare H to T
|
||||
|
||||
def test_binomial_verify_parameters():
|
||||
raises(ValueError, lambda: Binomial('b', .2, .5))
|
||||
raises(ValueError, lambda: Binomial('b', 3, 1.5))
|
||||
|
||||
def test_binomial_numeric():
|
||||
nvals = range(5)
|
||||
pvals = [0, Rational(1, 4), S.Half, Rational(3, 4), 1]
|
||||
|
||||
for n in nvals:
|
||||
for p in pvals:
|
||||
X = Binomial('X', n, p)
|
||||
assert E(X) == n*p
|
||||
assert variance(X) == n*p*(1 - p)
|
||||
if n > 0 and 0 < p < 1:
|
||||
assert skewness(X) == (1 - 2*p)/sqrt(n*p*(1 - p))
|
||||
assert kurtosis(X) == 3 + (1 - 6*p*(1 - p))/(n*p*(1 - p))
|
||||
for k in range(n + 1):
|
||||
assert P(Eq(X, k)) == binomial(n, k)*p**k*(1 - p)**(n - k)
|
||||
|
||||
def test_binomial_quantile():
|
||||
X = Binomial('X', 50, S.Half)
|
||||
assert quantile(X)(0.95) == S(31)
|
||||
assert median(X) == FiniteSet(25)
|
||||
|
||||
X = Binomial('X', 5, S.Half)
|
||||
p = Symbol("p", positive=True)
|
||||
assert quantile(X)(p) == Piecewise((nan, p > S.One), (S.Zero, p <= Rational(1, 32)),\
|
||||
(S.One, p <= Rational(3, 16)), (S(2), p <= S.Half), (S(3), p <= Rational(13, 16)),\
|
||||
(S(4), p <= Rational(31, 32)), (S(5), p <= S.One))
|
||||
assert median(X) == FiniteSet(2, 3)
|
||||
|
||||
|
||||
def test_binomial_symbolic():
|
||||
n = 2
|
||||
p = symbols('p', positive=True)
|
||||
X = Binomial('X', n, p)
|
||||
t = Symbol('t')
|
||||
|
||||
assert simplify(E(X)) == n*p == simplify(moment(X, 1))
|
||||
assert simplify(variance(X)) == n*p*(1 - p) == simplify(cmoment(X, 2))
|
||||
assert cancel(skewness(X) - (1 - 2*p)/sqrt(n*p*(1 - p))) == 0
|
||||
assert cancel((kurtosis(X)) - (3 + (1 - 6*p*(1 - p))/(n*p*(1 - p)))) == 0
|
||||
assert characteristic_function(X)(t) == p ** 2 * exp(2 * I * t) + 2 * p * (-p + 1) * exp(I * t) + (-p + 1) ** 2
|
||||
assert moment_generating_function(X)(t) == p ** 2 * exp(2 * t) + 2 * p * (-p + 1) * exp(t) + (-p + 1) ** 2
|
||||
|
||||
# Test ability to change success/failure winnings
|
||||
H, T = symbols('H T')
|
||||
Y = Binomial('Y', n, p, succ=H, fail=T)
|
||||
assert simplify(E(Y) - (n*(H*p + T*(1 - p)))) == 0
|
||||
|
||||
# test symbolic dimensions
|
||||
n = symbols('n')
|
||||
B = Binomial('B', n, p)
|
||||
raises(NotImplementedError, lambda: P(B > 2))
|
||||
assert density(B).dict == Density(BinomialDistribution(n, p, 1, 0))
|
||||
assert set(density(B).dict.subs(n, 4).doit().keys()) == \
|
||||
{S.Zero, S.One, S(2), S(3), S(4)}
|
||||
assert set(density(B).dict.subs(n, 4).doit().values()) == \
|
||||
{(1 - p)**4, 4*p*(1 - p)**3, 6*p**2*(1 - p)**2, 4*p**3*(1 - p), p**4}
|
||||
k = Dummy('k', integer=True)
|
||||
assert E(B > 2).dummy_eq(
|
||||
Sum(Piecewise((k*p**k*(1 - p)**(-k + n)*binomial(n, k), (k >= 0)
|
||||
& (k <= n) & (k > 2)), (0, True)), (k, 0, n)))
|
||||
|
||||
def test_beta_binomial():
|
||||
# verify parameters
|
||||
raises(ValueError, lambda: BetaBinomial('b', .2, 1, 2))
|
||||
raises(ValueError, lambda: BetaBinomial('b', 2, -1, 2))
|
||||
raises(ValueError, lambda: BetaBinomial('b', 2, 1, -2))
|
||||
assert BetaBinomial('b', 2, 1, 1)
|
||||
|
||||
# test numeric values
|
||||
nvals = range(1,5)
|
||||
alphavals = [Rational(1, 4), S.Half, Rational(3, 4), 1, 10]
|
||||
betavals = [Rational(1, 4), S.Half, Rational(3, 4), 1, 10]
|
||||
|
||||
for n in nvals:
|
||||
for a in alphavals:
|
||||
for b in betavals:
|
||||
X = BetaBinomial('X', n, a, b)
|
||||
assert E(X) == moment(X, 1)
|
||||
assert variance(X) == cmoment(X, 2)
|
||||
|
||||
# test symbolic
|
||||
n, a, b = symbols('a b n')
|
||||
assert BetaBinomial('x', n, a, b)
|
||||
n = 2 # Because we're using for loops, can't do symbolic n
|
||||
a, b = symbols('a b', positive=True)
|
||||
X = BetaBinomial('X', n, a, b)
|
||||
t = Symbol('t')
|
||||
|
||||
assert E(X).expand() == moment(X, 1).expand()
|
||||
assert variance(X).expand() == cmoment(X, 2).expand()
|
||||
assert skewness(X) == smoment(X, 3)
|
||||
assert characteristic_function(X)(t) == exp(2*I*t)*beta(a + 2, b)/beta(a, b) +\
|
||||
2*exp(I*t)*beta(a + 1, b + 1)/beta(a, b) + beta(a, b + 2)/beta(a, b)
|
||||
assert moment_generating_function(X)(t) == exp(2*t)*beta(a + 2, b)/beta(a, b) +\
|
||||
2*exp(t)*beta(a + 1, b + 1)/beta(a, b) + beta(a, b + 2)/beta(a, b)
|
||||
|
||||
def test_hypergeometric_numeric():
|
||||
for N in range(1, 5):
|
||||
for m in range(0, N + 1):
|
||||
for n in range(1, N + 1):
|
||||
X = Hypergeometric('X', N, m, n)
|
||||
N, m, n = map(sympify, (N, m, n))
|
||||
assert sum(density(X).values()) == 1
|
||||
assert E(X) == n * m / N
|
||||
if N > 1:
|
||||
assert variance(X) == n*(m/N)*(N - m)/N*(N - n)/(N - 1)
|
||||
# Only test for skewness when defined
|
||||
if N > 2 and 0 < m < N and n < N:
|
||||
assert skewness(X) == simplify((N - 2*m)*sqrt(N - 1)*(N - 2*n)
|
||||
/ (sqrt(n*m*(N - m)*(N - n))*(N - 2)))
|
||||
|
||||
def test_hypergeometric_symbolic():
|
||||
N, m, n = symbols('N, m, n')
|
||||
H = Hypergeometric('H', N, m, n)
|
||||
dens = density(H).dict
|
||||
expec = E(H > 2)
|
||||
assert dens == Density(HypergeometricDistribution(N, m, n))
|
||||
assert dens.subs(N, 5).doit() == Density(HypergeometricDistribution(5, m, n))
|
||||
assert set(dens.subs({N: 3, m: 2, n: 1}).doit().keys()) == {S.Zero, S.One}
|
||||
assert set(dens.subs({N: 3, m: 2, n: 1}).doit().values()) == {Rational(1, 3), Rational(2, 3)}
|
||||
k = Dummy('k', integer=True)
|
||||
assert expec.dummy_eq(
|
||||
Sum(Piecewise((k*binomial(m, k)*binomial(N - m, -k + n)
|
||||
/binomial(N, n), k > 2), (0, True)), (k, 0, n)))
|
||||
|
||||
def test_rademacher():
|
||||
X = Rademacher('X')
|
||||
t = Symbol('t')
|
||||
|
||||
assert E(X) == 0
|
||||
assert variance(X) == 1
|
||||
assert density(X)[-1] == S.Half
|
||||
assert density(X)[1] == S.Half
|
||||
assert characteristic_function(X)(t) == exp(I*t)/2 + exp(-I*t)/2
|
||||
assert moment_generating_function(X)(t) == exp(t) / 2 + exp(-t) / 2
|
||||
|
||||
def test_ideal_soliton():
|
||||
raises(ValueError, lambda : IdealSoliton('sol', -12))
|
||||
raises(ValueError, lambda : IdealSoliton('sol', 13.2))
|
||||
raises(ValueError, lambda : IdealSoliton('sol', 0))
|
||||
f = Function('f')
|
||||
raises(ValueError, lambda : density(IdealSoliton('sol', 10)).pmf(f))
|
||||
|
||||
k = Symbol('k', integer=True, positive=True)
|
||||
x = Symbol('x', integer=True, positive=True)
|
||||
t = Symbol('t')
|
||||
sol = IdealSoliton('sol', k)
|
||||
assert density(sol).low == S.One
|
||||
assert density(sol).high == k
|
||||
assert density(sol).dict == Density(density(sol))
|
||||
assert density(sol).pmf(x) == Piecewise((1/k, Eq(x, 1)), (1/(x*(x - 1)), k >= x), (0, True))
|
||||
|
||||
k_vals = [5, 20, 50, 100, 1000]
|
||||
for i in k_vals:
|
||||
assert E(sol.subs(k, i)) == harmonic(i) == moment(sol.subs(k, i), 1)
|
||||
assert variance(sol.subs(k, i)) == (i - 1) + harmonic(i) - harmonic(i)**2 == cmoment(sol.subs(k, i),2)
|
||||
assert skewness(sol.subs(k, i)) == smoment(sol.subs(k, i), 3)
|
||||
assert kurtosis(sol.subs(k, i)) == smoment(sol.subs(k, i), 4)
|
||||
|
||||
assert exp(I*t)/10 + Sum(exp(I*t*x)/(x*x - x), (x, 2, k)).subs(k, 10).doit() == characteristic_function(sol.subs(k, 10))(t)
|
||||
assert exp(t)/10 + Sum(exp(t*x)/(x*x - x), (x, 2, k)).subs(k, 10).doit() == moment_generating_function(sol.subs(k, 10))(t)
|
||||
|
||||
def test_robust_soliton():
|
||||
raises(ValueError, lambda : RobustSoliton('robSol', -12, 0.1, 0.02))
|
||||
raises(ValueError, lambda : RobustSoliton('robSol', 13, 1.89, 0.1))
|
||||
raises(ValueError, lambda : RobustSoliton('robSol', 15, 0.6, -2.31))
|
||||
f = Function('f')
|
||||
raises(ValueError, lambda : density(RobustSoliton('robSol', 15, 0.6, 0.1)).pmf(f))
|
||||
|
||||
k = Symbol('k', integer=True, positive=True)
|
||||
delta = Symbol('delta', positive=True)
|
||||
c = Symbol('c', positive=True)
|
||||
robSol = RobustSoliton('robSol', k, delta, c)
|
||||
assert density(robSol).low == 1
|
||||
assert density(robSol).high == k
|
||||
|
||||
k_vals = [10, 20, 50]
|
||||
delta_vals = [0.2, 0.4, 0.6]
|
||||
c_vals = [0.01, 0.03, 0.05]
|
||||
for x in k_vals:
|
||||
for y in delta_vals:
|
||||
for z in c_vals:
|
||||
assert E(robSol.subs({k: x, delta: y, c: z})) == moment(robSol.subs({k: x, delta: y, c: z}), 1)
|
||||
assert variance(robSol.subs({k: x, delta: y, c: z})) == cmoment(robSol.subs({k: x, delta: y, c: z}), 2)
|
||||
assert skewness(robSol.subs({k: x, delta: y, c: z})) == smoment(robSol.subs({k: x, delta: y, c: z}), 3)
|
||||
assert kurtosis(robSol.subs({k: x, delta: y, c: z})) == smoment(robSol.subs({k: x, delta: y, c: z}), 4)
|
||||
|
||||
def test_FiniteRV():
|
||||
F = FiniteRV('F', {1: S.Half, 2: Rational(1, 4), 3: Rational(1, 4)}, check=True)
|
||||
p = Symbol("p", positive=True)
|
||||
|
||||
assert dict(density(F).items()) == {S.One: S.Half, S(2): Rational(1, 4), S(3): Rational(1, 4)}
|
||||
assert P(F >= 2) == S.Half
|
||||
assert quantile(F)(p) == Piecewise((nan, p > S.One), (S.One, p <= S.Half),\
|
||||
(S(2), p <= Rational(3, 4)),(S(3), True))
|
||||
|
||||
assert pspace(F).domain.as_boolean() == Or(
|
||||
*[Eq(F.symbol, i) for i in [1, 2, 3]])
|
||||
|
||||
assert F.pspace.domain.set == FiniteSet(1, 2, 3)
|
||||
raises(ValueError, lambda: FiniteRV('F', {1: S.Half, 2: S.Half, 3: S.Half}, check=True))
|
||||
raises(ValueError, lambda: FiniteRV('F', {1: S.Half, 2: Rational(-1, 2), 3: S.One}, check=True))
|
||||
raises(ValueError, lambda: FiniteRV('F', {1: S.One, 2: Rational(3, 2), 3: S.Zero,\
|
||||
4: Rational(-1, 2), 5: Rational(-3, 4), 6: Rational(-1, 4)}, check=True))
|
||||
|
||||
# purposeful invalid pmf but it should not raise since check=False
|
||||
# see test_drv_types.test_ContinuousRV for explanation
|
||||
X = FiniteRV('X', {1: 1, 2: 2})
|
||||
assert E(X) == 5
|
||||
assert P(X <= 2) + P(X > 2) != 1
|
||||
|
||||
def test_density_call():
|
||||
from sympy.abc import p
|
||||
x = Bernoulli('x', p)
|
||||
d = density(x)
|
||||
assert d(0) == 1 - p
|
||||
assert d(S.Zero) == 1 - p
|
||||
assert d(5) == 0
|
||||
|
||||
assert 0 in d
|
||||
assert 5 not in d
|
||||
assert d(S.Zero) == d[S.Zero]
|
||||
|
||||
|
||||
def test_DieDistribution():
|
||||
from sympy.abc import x
|
||||
X = DieDistribution(6)
|
||||
assert X.pmf(S.Half) is S.Zero
|
||||
assert X.pmf(x).subs({x: 1}).doit() == Rational(1, 6)
|
||||
assert X.pmf(x).subs({x: 7}).doit() == 0
|
||||
assert X.pmf(x).subs({x: -1}).doit() == 0
|
||||
assert X.pmf(x).subs({x: Rational(1, 3)}).doit() == 0
|
||||
raises(ValueError, lambda: X.pmf(Matrix([0, 0])))
|
||||
raises(ValueError, lambda: X.pmf(x**2 - 1))
|
||||
|
||||
def test_FinitePSpace():
|
||||
X = Die('X', 6)
|
||||
space = pspace(X)
|
||||
assert space.density == DieDistribution(6)
|
||||
|
||||
def test_symbolic_conditions():
|
||||
B = Bernoulli('B', Rational(1, 4))
|
||||
D = Die('D', 4)
|
||||
b, n = symbols('b, n')
|
||||
Y = P(Eq(B, b))
|
||||
Z = E(D > n)
|
||||
assert Y == \
|
||||
Piecewise((Rational(1, 4), Eq(b, 1)), (0, True)) + \
|
||||
Piecewise((Rational(3, 4), Eq(b, 0)), (0, True))
|
||||
assert Z == \
|
||||
Piecewise((Rational(1, 4), n < 1), (0, True)) + Piecewise((S.Half, n < 2), (0, True)) + \
|
||||
Piecewise((Rational(3, 4), n < 3), (0, True)) + Piecewise((S.One, n < 4), (0, True))
|
||||
|
|
@ -0,0 +1,436 @@
|
|||
from sympy.concrete.products import Product
|
||||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.numbers import (Rational, oo, pi)
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.combinatorial.factorials import (RisingFactorial, factorial)
|
||||
from sympy.functions.elementary.complexes import polar_lift
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.bessel import besselk
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.matrices.dense import eye
|
||||
from sympy.matrices.expressions.determinant import Determinant
|
||||
from sympy.sets.fancysets import Range
|
||||
from sympy.sets.sets import (Interval, ProductSet)
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.tensor.indexed import (Indexed, IndexedBase)
|
||||
from sympy.core.numbers import comp
|
||||
from sympy.integrals.integrals import integrate
|
||||
from sympy.matrices import Matrix, MatrixSymbol
|
||||
from sympy.matrices.expressions.matexpr import MatrixElement
|
||||
from sympy.stats import density, median, marginal_distribution, Normal, Laplace, E, sample
|
||||
from sympy.stats.joint_rv_types import (JointRV, MultivariateNormalDistribution,
|
||||
JointDistributionHandmade, MultivariateT, NormalGamma,
|
||||
GeneralizedMultivariateLogGammaOmega as GMVLGO, MultivariateBeta,
|
||||
GeneralizedMultivariateLogGamma as GMVLG, MultivariateEwens,
|
||||
Multinomial, NegativeMultinomial, MultivariateNormal,
|
||||
MultivariateLaplace)
|
||||
from sympy.testing.pytest import raises, XFAIL, skip, slow
|
||||
from sympy.external import import_module
|
||||
|
||||
from sympy.abc import x, y
|
||||
|
||||
|
||||
|
||||
def test_Normal():
|
||||
m = Normal('A', [1, 2], [[1, 0], [0, 1]])
|
||||
A = MultivariateNormal('A', [1, 2], [[1, 0], [0, 1]])
|
||||
assert m == A
|
||||
assert density(m)(1, 2) == 1/(2*pi)
|
||||
assert m.pspace.distribution.set == ProductSet(S.Reals, S.Reals)
|
||||
raises (ValueError, lambda:m[2])
|
||||
n = Normal('B', [1, 2, 3], [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
||||
p = Normal('C', Matrix([1, 2]), Matrix([[1, 0], [0, 1]]))
|
||||
assert density(m)(x, y) == density(p)(x, y)
|
||||
assert marginal_distribution(n, 0, 1)(1, 2) == 1/(2*pi)
|
||||
raises(ValueError, lambda: marginal_distribution(m))
|
||||
assert integrate(density(m)(x, y), (x, -oo, oo), (y, -oo, oo)).evalf() == 1.0
|
||||
N = Normal('N', [1, 2], [[x, 0], [0, y]])
|
||||
assert density(N)(0, 0) == exp(-((4*x + y)/(2*x*y)))/(2*pi*sqrt(x*y))
|
||||
|
||||
raises (ValueError, lambda: Normal('M', [1, 2], [[1, 1], [1, -1]]))
|
||||
# symbolic
|
||||
n = symbols('n', integer=True, positive=True)
|
||||
mu = MatrixSymbol('mu', n, 1)
|
||||
sigma = MatrixSymbol('sigma', n, n)
|
||||
X = Normal('X', mu, sigma)
|
||||
assert density(X) == MultivariateNormalDistribution(mu, sigma)
|
||||
raises (NotImplementedError, lambda: median(m))
|
||||
# Below tests should work after issue #17267 is resolved
|
||||
# assert E(X) == mu
|
||||
# assert variance(X) == sigma
|
||||
|
||||
# test symbolic multivariate normal densities
|
||||
n = 3
|
||||
|
||||
Sg = MatrixSymbol('Sg', n, n)
|
||||
mu = MatrixSymbol('mu', n, 1)
|
||||
obs = MatrixSymbol('obs', n, 1)
|
||||
|
||||
X = MultivariateNormal('X', mu, Sg)
|
||||
density_X = density(X)
|
||||
|
||||
eval_a = density_X(obs).subs({Sg: eye(3),
|
||||
mu: Matrix([0, 0, 0]), obs: Matrix([0, 0, 0])}).doit()
|
||||
eval_b = density_X(0, 0, 0).subs({Sg: eye(3), mu: Matrix([0, 0, 0])}).doit()
|
||||
|
||||
assert eval_a == sqrt(2)/(4*pi**Rational(3/2))
|
||||
assert eval_b == sqrt(2)/(4*pi**Rational(3/2))
|
||||
|
||||
n = symbols('n', integer=True, positive=True)
|
||||
|
||||
Sg = MatrixSymbol('Sg', n, n)
|
||||
mu = MatrixSymbol('mu', n, 1)
|
||||
obs = MatrixSymbol('obs', n, 1)
|
||||
|
||||
X = MultivariateNormal('X', mu, Sg)
|
||||
density_X_at_obs = density(X)(obs)
|
||||
|
||||
expected_density = MatrixElement(
|
||||
exp((S(1)/2) * (mu.T - obs.T) * Sg**(-1) * (-mu + obs)) / \
|
||||
sqrt((2*pi)**n * Determinant(Sg)), 0, 0)
|
||||
|
||||
assert density_X_at_obs == expected_density
|
||||
|
||||
|
||||
def test_MultivariateTDist():
|
||||
t1 = MultivariateT('T', [0, 0], [[1, 0], [0, 1]], 2)
|
||||
assert(density(t1))(1, 1) == 1/(8*pi)
|
||||
assert t1.pspace.distribution.set == ProductSet(S.Reals, S.Reals)
|
||||
assert integrate(density(t1)(x, y), (x, -oo, oo), \
|
||||
(y, -oo, oo)).evalf() == 1.0
|
||||
raises(ValueError, lambda: MultivariateT('T', [1, 2], [[1, 1], [1, -1]], 1))
|
||||
t2 = MultivariateT('t2', [1, 2], [[x, 0], [0, y]], 1)
|
||||
assert density(t2)(1, 2) == 1/(2*pi*sqrt(x*y))
|
||||
|
||||
|
||||
def test_multivariate_laplace():
|
||||
raises(ValueError, lambda: Laplace('T', [1, 2], [[1, 2], [2, 1]]))
|
||||
L = Laplace('L', [1, 0], [[1, 0], [0, 1]])
|
||||
L2 = MultivariateLaplace('L2', [1, 0], [[1, 0], [0, 1]])
|
||||
assert density(L)(2, 3) == exp(2)*besselk(0, sqrt(39))/pi
|
||||
L1 = Laplace('L1', [1, 2], [[x, 0], [0, y]])
|
||||
assert density(L1)(0, 1) == \
|
||||
exp(2/y)*besselk(0, sqrt((2 + 4/y + 1/x)/y))/(pi*sqrt(x*y))
|
||||
assert L.pspace.distribution.set == ProductSet(S.Reals, S.Reals)
|
||||
assert L.pspace.distribution == L2.pspace.distribution
|
||||
|
||||
|
||||
def test_NormalGamma():
|
||||
ng = NormalGamma('G', 1, 2, 3, 4)
|
||||
assert density(ng)(1, 1) == 32*exp(-4)/sqrt(pi)
|
||||
assert ng.pspace.distribution.set == ProductSet(S.Reals, Interval(0, oo))
|
||||
raises(ValueError, lambda:NormalGamma('G', 1, 2, 3, -1))
|
||||
assert marginal_distribution(ng, 0)(1) == \
|
||||
3*sqrt(10)*gamma(Rational(7, 4))/(10*sqrt(pi)*gamma(Rational(5, 4)))
|
||||
assert marginal_distribution(ng, y)(1) == exp(Rational(-1, 4))/128
|
||||
assert marginal_distribution(ng,[0,1])(x) == x**2*exp(-x/4)/128
|
||||
|
||||
|
||||
def test_GeneralizedMultivariateLogGammaDistribution():
|
||||
h = S.Half
|
||||
omega = Matrix([[1, h, h, h],
|
||||
[h, 1, h, h],
|
||||
[h, h, 1, h],
|
||||
[h, h, h, 1]])
|
||||
v, l, mu = (4, [1, 2, 3, 4], [1, 2, 3, 4])
|
||||
y_1, y_2, y_3, y_4 = symbols('y_1:5', real=True)
|
||||
delta = symbols('d', positive=True)
|
||||
G = GMVLGO('G', omega, v, l, mu)
|
||||
Gd = GMVLG('Gd', delta, v, l, mu)
|
||||
dend = ("d**4*Sum(4*24**(-n - 4)*(1 - d)**n*exp((n + 4)*(y_1 + 2*y_2 + 3*y_3 "
|
||||
"+ 4*y_4) - exp(y_1) - exp(2*y_2)/2 - exp(3*y_3)/3 - exp(4*y_4)/4)/"
|
||||
"(gamma(n + 1)*gamma(n + 4)**3), (n, 0, oo))")
|
||||
assert str(density(Gd)(y_1, y_2, y_3, y_4)) == dend
|
||||
den = ("5*2**(2/3)*5**(1/3)*Sum(4*24**(-n - 4)*(-2**(2/3)*5**(1/3)/4 + 1)**n*"
|
||||
"exp((n + 4)*(y_1 + 2*y_2 + 3*y_3 + 4*y_4) - exp(y_1) - exp(2*y_2)/2 - "
|
||||
"exp(3*y_3)/3 - exp(4*y_4)/4)/(gamma(n + 1)*gamma(n + 4)**3), (n, 0, oo))/64")
|
||||
assert str(density(G)(y_1, y_2, y_3, y_4)) == den
|
||||
marg = ("5*2**(2/3)*5**(1/3)*exp(4*y_1)*exp(-exp(y_1))*Integral(exp(-exp(4*G[3])"
|
||||
"/4)*exp(16*G[3])*Integral(exp(-exp(3*G[2])/3)*exp(12*G[2])*Integral(exp("
|
||||
"-exp(2*G[1])/2)*exp(8*G[1])*Sum((-1/4)**n*(-4 + 2**(2/3)*5**(1/3"
|
||||
"))**n*exp(n*y_1)*exp(2*n*G[1])*exp(3*n*G[2])*exp(4*n*G[3])/(24**n*gamma(n + 1)"
|
||||
"*gamma(n + 4)**3), (n, 0, oo)), (G[1], -oo, oo)), (G[2], -oo, oo)), (G[3]"
|
||||
", -oo, oo))/5308416")
|
||||
assert str(marginal_distribution(G, G[0])(y_1)) == marg
|
||||
omega_f1 = Matrix([[1, h, h]])
|
||||
omega_f2 = Matrix([[1, h, h, h],
|
||||
[h, 1, 2, h],
|
||||
[h, h, 1, h],
|
||||
[h, h, h, 1]])
|
||||
omega_f3 = Matrix([[6, h, h, h],
|
||||
[h, 1, 2, h],
|
||||
[h, h, 1, h],
|
||||
[h, h, h, 1]])
|
||||
v_f = symbols("v_f", positive=False, real=True)
|
||||
l_f = [1, 2, v_f, 4]
|
||||
m_f = [v_f, 2, 3, 4]
|
||||
omega_f4 = Matrix([[1, h, h, h, h],
|
||||
[h, 1, h, h, h],
|
||||
[h, h, 1, h, h],
|
||||
[h, h, h, 1, h],
|
||||
[h, h, h, h, 1]])
|
||||
l_f1 = [1, 2, 3, 4, 5]
|
||||
omega_f5 = Matrix([[1]])
|
||||
mu_f5 = l_f5 = [1]
|
||||
|
||||
raises(ValueError, lambda: GMVLGO('G', omega_f1, v, l, mu))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega_f2, v, l, mu))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega_f3, v, l, mu))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega, v_f, l, mu))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega, v, l_f, mu))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega, v, l, m_f))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega_f4, v, l, mu))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega, v, l_f1, mu))
|
||||
raises(ValueError, lambda: GMVLGO('G', omega_f5, v, l_f5, mu_f5))
|
||||
raises(ValueError, lambda: GMVLG('G', Rational(3, 2), v, l, mu))
|
||||
|
||||
|
||||
def test_MultivariateBeta():
|
||||
a1, a2 = symbols('a1, a2', positive=True)
|
||||
a1_f, a2_f = symbols('a1, a2', positive=False, real=True)
|
||||
mb = MultivariateBeta('B', [a1, a2])
|
||||
mb_c = MultivariateBeta('C', a1, a2)
|
||||
assert density(mb)(1, 2) == S(2)**(a2 - 1)*gamma(a1 + a2)/\
|
||||
(gamma(a1)*gamma(a2))
|
||||
assert marginal_distribution(mb_c, 0)(3) == S(3)**(a1 - 1)*gamma(a1 + a2)/\
|
||||
(a2*gamma(a1)*gamma(a2))
|
||||
raises(ValueError, lambda: MultivariateBeta('b1', [a1_f, a2]))
|
||||
raises(ValueError, lambda: MultivariateBeta('b2', [a1, a2_f]))
|
||||
raises(ValueError, lambda: MultivariateBeta('b3', [0, 0]))
|
||||
raises(ValueError, lambda: MultivariateBeta('b4', [a1_f, a2_f]))
|
||||
assert mb.pspace.distribution.set == ProductSet(Interval(0, 1), Interval(0, 1))
|
||||
|
||||
|
||||
def test_MultivariateEwens():
|
||||
n, theta, i = symbols('n theta i', positive=True)
|
||||
|
||||
# tests for integer dimensions
|
||||
theta_f = symbols('t_f', negative=True)
|
||||
a = symbols('a_1:4', positive = True, integer = True)
|
||||
ed = MultivariateEwens('E', 3, theta)
|
||||
assert density(ed)(a[0], a[1], a[2]) == Piecewise((6*2**(-a[1])*3**(-a[2])*
|
||||
theta**a[0]*theta**a[1]*theta**a[2]/
|
||||
(theta*(theta + 1)*(theta + 2)*
|
||||
factorial(a[0])*factorial(a[1])*
|
||||
factorial(a[2])), Eq(a[0] + 2*a[1] +
|
||||
3*a[2], 3)), (0, True))
|
||||
assert marginal_distribution(ed, ed[1])(a[1]) == Piecewise((6*2**(-a[1])*
|
||||
theta**a[1]/((theta + 1)*
|
||||
(theta + 2)*factorial(a[1])),
|
||||
Eq(2*a[1] + 1, 3)), (0, True))
|
||||
raises(ValueError, lambda: MultivariateEwens('e1', 5, theta_f))
|
||||
assert ed.pspace.distribution.set == ProductSet(Range(0, 4, 1),
|
||||
Range(0, 2, 1), Range(0, 2, 1))
|
||||
|
||||
# tests for symbolic dimensions
|
||||
eds = MultivariateEwens('E', n, theta)
|
||||
a = IndexedBase('a')
|
||||
j, k = symbols('j, k')
|
||||
den = Piecewise((factorial(n)*Product(theta**a[j]*(j + 1)**(-a[j])/
|
||||
factorial(a[j]), (j, 0, n - 1))/RisingFactorial(theta, n),
|
||||
Eq(n, Sum((k + 1)*a[k], (k, 0, n - 1)))), (0, True))
|
||||
assert density(eds)(a).dummy_eq(den)
|
||||
|
||||
|
||||
def test_Multinomial():
|
||||
n, x1, x2, x3, x4 = symbols('n, x1, x2, x3, x4', nonnegative=True, integer=True)
|
||||
p1, p2, p3, p4 = symbols('p1, p2, p3, p4', positive=True)
|
||||
p1_f, n_f = symbols('p1_f, n_f', negative=True)
|
||||
M = Multinomial('M', n, [p1, p2, p3, p4])
|
||||
C = Multinomial('C', 3, p1, p2, p3)
|
||||
f = factorial
|
||||
assert density(M)(x1, x2, x3, x4) == Piecewise((p1**x1*p2**x2*p3**x3*p4**x4*
|
||||
f(n)/(f(x1)*f(x2)*f(x3)*f(x4)),
|
||||
Eq(n, x1 + x2 + x3 + x4)), (0, True))
|
||||
assert marginal_distribution(C, C[0])(x1).subs(x1, 1) ==\
|
||||
3*p1*p2**2 +\
|
||||
6*p1*p2*p3 +\
|
||||
3*p1*p3**2
|
||||
raises(ValueError, lambda: Multinomial('b1', 5, [p1, p2, p3, p1_f]))
|
||||
raises(ValueError, lambda: Multinomial('b2', n_f, [p1, p2, p3, p4]))
|
||||
raises(ValueError, lambda: Multinomial('b3', n, 0.5, 0.4, 0.3, 0.1))
|
||||
|
||||
|
||||
def test_NegativeMultinomial():
|
||||
k0, x1, x2, x3, x4 = symbols('k0, x1, x2, x3, x4', nonnegative=True, integer=True)
|
||||
p1, p2, p3, p4 = symbols('p1, p2, p3, p4', positive=True)
|
||||
p1_f = symbols('p1_f', negative=True)
|
||||
N = NegativeMultinomial('N', 4, [p1, p2, p3, p4])
|
||||
C = NegativeMultinomial('C', 4, 0.1, 0.2, 0.3)
|
||||
g = gamma
|
||||
f = factorial
|
||||
assert simplify(density(N)(x1, x2, x3, x4) -
|
||||
p1**x1*p2**x2*p3**x3*p4**x4*(-p1 - p2 - p3 - p4 + 1)**4*g(x1 + x2 +
|
||||
x3 + x4 + 4)/(6*f(x1)*f(x2)*f(x3)*f(x4))) is S.Zero
|
||||
assert comp(marginal_distribution(C, C[0])(1).evalf(), 0.33, .01)
|
||||
raises(ValueError, lambda: NegativeMultinomial('b1', 5, [p1, p2, p3, p1_f]))
|
||||
raises(ValueError, lambda: NegativeMultinomial('b2', k0, 0.5, 0.4, 0.3, 0.4))
|
||||
assert N.pspace.distribution.set == ProductSet(Range(0, oo, 1),
|
||||
Range(0, oo, 1), Range(0, oo, 1), Range(0, oo, 1))
|
||||
|
||||
|
||||
@slow
|
||||
def test_JointPSpace_marginal_distribution():
|
||||
T = MultivariateT('T', [0, 0], [[1, 0], [0, 1]], 2)
|
||||
got = marginal_distribution(T, T[1])(x)
|
||||
ans = sqrt(2)*(x**2/2 + 1)/(4*polar_lift(x**2/2 + 1)**(S(5)/2))
|
||||
assert got == ans, got
|
||||
assert integrate(marginal_distribution(T, 1)(x), (x, -oo, oo)) == 1
|
||||
|
||||
t = MultivariateT('T', [0, 0, 0], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 3)
|
||||
assert comp(marginal_distribution(t, 0)(1).evalf(), 0.2, .01)
|
||||
|
||||
|
||||
def test_JointRV():
|
||||
x1, x2 = (Indexed('x', i) for i in (1, 2))
|
||||
pdf = exp(-x1**2/2 + x1 - x2**2/2 - S.Half)/(2*pi)
|
||||
X = JointRV('x', pdf)
|
||||
assert density(X)(1, 2) == exp(-2)/(2*pi)
|
||||
assert isinstance(X.pspace.distribution, JointDistributionHandmade)
|
||||
assert marginal_distribution(X, 0)(2) == sqrt(2)*exp(Rational(-1, 2))/(2*sqrt(pi))
|
||||
|
||||
|
||||
def test_expectation():
|
||||
m = Normal('A', [x, y], [[1, 0], [0, 1]])
|
||||
assert simplify(E(m[1])) == y
|
||||
|
||||
|
||||
@XFAIL
|
||||
def test_joint_vector_expectation():
|
||||
m = Normal('A', [x, y], [[1, 0], [0, 1]])
|
||||
assert E(m) == (x, y)
|
||||
|
||||
|
||||
def test_sample_numpy():
|
||||
distribs_numpy = [
|
||||
MultivariateNormal("M", [3, 4], [[2, 1], [1, 2]]),
|
||||
MultivariateBeta("B", [0.4, 5, 15, 50, 203]),
|
||||
Multinomial("N", 50, [0.3, 0.2, 0.1, 0.25, 0.15])
|
||||
]
|
||||
size = 3
|
||||
numpy = import_module('numpy')
|
||||
if not numpy:
|
||||
skip('Numpy is not installed. Abort tests for _sample_numpy.')
|
||||
else:
|
||||
for X in distribs_numpy:
|
||||
samps = sample(X, size=size, library='numpy')
|
||||
for sam in samps:
|
||||
assert tuple(sam) in X.pspace.distribution.set
|
||||
N_c = NegativeMultinomial('N', 3, 0.1, 0.1, 0.1)
|
||||
raises(NotImplementedError, lambda: sample(N_c, library='numpy'))
|
||||
|
||||
|
||||
def test_sample_scipy():
|
||||
distribs_scipy = [
|
||||
MultivariateNormal("M", [0, 0], [[0.1, 0.025], [0.025, 0.1]]),
|
||||
MultivariateBeta("B", [0.4, 5, 15]),
|
||||
Multinomial("N", 8, [0.3, 0.2, 0.1, 0.4])
|
||||
]
|
||||
|
||||
size = 3
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy not installed. Abort tests for _sample_scipy.')
|
||||
else:
|
||||
for X in distribs_scipy:
|
||||
samps = sample(X, size=size)
|
||||
samps2 = sample(X, size=(2, 2))
|
||||
for sam in samps:
|
||||
assert tuple(sam) in X.pspace.distribution.set
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
assert tuple(samps2[i][j]) in X.pspace.distribution.set
|
||||
N_c = NegativeMultinomial('N', 3, 0.1, 0.1, 0.1)
|
||||
raises(NotImplementedError, lambda: sample(N_c))
|
||||
|
||||
|
||||
def test_sample_pymc():
|
||||
distribs_pymc = [
|
||||
MultivariateNormal("M", [5, 2], [[1, 0], [0, 1]]),
|
||||
MultivariateBeta("B", [0.4, 5, 15]),
|
||||
Multinomial("N", 4, [0.3, 0.2, 0.1, 0.4])
|
||||
]
|
||||
size = 3
|
||||
pymc = import_module('pymc')
|
||||
if not pymc:
|
||||
skip('PyMC is not installed. Abort tests for _sample_pymc.')
|
||||
else:
|
||||
for X in distribs_pymc:
|
||||
samps = sample(X, size=size, library='pymc')
|
||||
for sam in samps:
|
||||
assert tuple(sam.flatten()) in X.pspace.distribution.set
|
||||
N_c = NegativeMultinomial('N', 3, 0.1, 0.1, 0.1)
|
||||
raises(NotImplementedError, lambda: sample(N_c, library='pymc'))
|
||||
|
||||
|
||||
def test_sample_seed():
|
||||
x1, x2 = (Indexed('x', i) for i in (1, 2))
|
||||
pdf = exp(-x1**2/2 + x1 - x2**2/2 - S.Half)/(2*pi)
|
||||
X = JointRV('x', pdf)
|
||||
|
||||
libraries = ['scipy', 'numpy', 'pymc']
|
||||
for lib in libraries:
|
||||
try:
|
||||
imported_lib = import_module(lib)
|
||||
if imported_lib:
|
||||
s0, s1, s2 = [], [], []
|
||||
s0 = sample(X, size=10, library=lib, seed=0)
|
||||
s1 = sample(X, size=10, library=lib, seed=0)
|
||||
s2 = sample(X, size=10, library=lib, seed=1)
|
||||
assert all(s0 == s1)
|
||||
assert all(s1 != s2)
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
||||
#
|
||||
# XXX: This fails for pymc. Previously the test appeared to pass but that is
|
||||
# just because the library argument was not passed so the test always used
|
||||
# scipy.
|
||||
#
|
||||
def test_issue_21057():
|
||||
m = Normal("x", [0, 0], [[0, 0], [0, 0]])
|
||||
n = MultivariateNormal("x", [0, 0], [[0, 0], [0, 0]])
|
||||
p = Normal("x", [0, 0], [[0, 0], [0, 1]])
|
||||
assert m == n
|
||||
libraries = ('scipy', 'numpy') # , 'pymc') # <-- pymc fails
|
||||
for library in libraries:
|
||||
try:
|
||||
imported_lib = import_module(library)
|
||||
if imported_lib:
|
||||
s1 = sample(m, size=8, library=library)
|
||||
s2 = sample(n, size=8, library=library)
|
||||
s3 = sample(p, size=8, library=library)
|
||||
assert tuple(s1.flatten()) == tuple(s2.flatten())
|
||||
for s in s3:
|
||||
assert tuple(s.flatten()) in p.pspace.distribution.set
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
||||
|
||||
#
|
||||
# When this passes the pymc part can be uncommented in test_issue_21057 above
|
||||
# and this can be deleted.
|
||||
#
|
||||
@XFAIL
|
||||
def test_issue_21057_pymc():
|
||||
m = Normal("x", [0, 0], [[0, 0], [0, 0]])
|
||||
n = MultivariateNormal("x", [0, 0], [[0, 0], [0, 0]])
|
||||
p = Normal("x", [0, 0], [[0, 0], [0, 1]])
|
||||
assert m == n
|
||||
libraries = ('pymc',)
|
||||
for library in libraries:
|
||||
try:
|
||||
imported_lib = import_module(library)
|
||||
if imported_lib:
|
||||
s1 = sample(m, size=8, library=library)
|
||||
s2 = sample(n, size=8, library=library)
|
||||
s3 = sample(p, size=8, library=library)
|
||||
assert tuple(s1.flatten()) == tuple(s2.flatten())
|
||||
for s in s3:
|
||||
assert tuple(s.flatten()) in p.pspace.distribution.set
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
from sympy.concrete.products import Product
|
||||
from sympy.core.numbers import pi
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Dummy, symbols)
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.matrices import Determinant, Matrix, Trace, MatrixSymbol, MatrixSet
|
||||
from sympy.stats import density, sample
|
||||
from sympy.stats.matrix_distributions import (MatrixGammaDistribution,
|
||||
MatrixGamma, MatrixPSpace, Wishart, MatrixNormal, MatrixStudentT)
|
||||
from sympy.testing.pytest import raises, skip
|
||||
from sympy.external import import_module
|
||||
|
||||
|
||||
def test_MatrixPSpace():
|
||||
M = MatrixGammaDistribution(1, 2, [[2, 1], [1, 2]])
|
||||
MP = MatrixPSpace('M', M, 2, 2)
|
||||
assert MP.distribution == M
|
||||
raises(ValueError, lambda: MatrixPSpace('M', M, 1.2, 2))
|
||||
|
||||
def test_MatrixGamma():
|
||||
M = MatrixGamma('M', 1, 2, [[1, 0], [0, 1]])
|
||||
assert M.pspace.distribution.set == MatrixSet(2, 2, S.Reals)
|
||||
assert isinstance(density(M), MatrixGammaDistribution)
|
||||
X = MatrixSymbol('X', 2, 2)
|
||||
num = exp(Trace(Matrix([[-S(1)/2, 0], [0, -S(1)/2]])*X))
|
||||
assert density(M)(X).doit() == num/(4*pi*sqrt(Determinant(X)))
|
||||
assert density(M)([[2, 1], [1, 2]]).doit() == sqrt(3)*exp(-2)/(12*pi)
|
||||
X = MatrixSymbol('X', 1, 2)
|
||||
Y = MatrixSymbol('Y', 1, 2)
|
||||
assert density(M)([X, Y]).doit() == exp(-X[0, 0]/2 - Y[0, 1]/2)/(4*pi*sqrt(
|
||||
X[0, 0]*Y[0, 1] - X[0, 1]*Y[0, 0]))
|
||||
# symbolic
|
||||
a, b = symbols('a b', positive=True)
|
||||
d = symbols('d', positive=True, integer=True)
|
||||
Y = MatrixSymbol('Y', d, d)
|
||||
Z = MatrixSymbol('Z', 2, 2)
|
||||
SM = MatrixSymbol('SM', d, d)
|
||||
M2 = MatrixGamma('M2', a, b, SM)
|
||||
M3 = MatrixGamma('M3', 2, 3, [[2, 1], [1, 2]])
|
||||
k = Dummy('k')
|
||||
exprd = pi**(-d*(d - 1)/4)*b**(-a*d)*exp(Trace((-1/b)*SM**(-1)*Y)
|
||||
)*Determinant(SM)**(-a)*Determinant(Y)**(a - d/2 - S(1)/2)/Product(
|
||||
gamma(-k/2 + a + S(1)/2), (k, 1, d))
|
||||
assert density(M2)(Y).dummy_eq(exprd)
|
||||
raises(NotImplementedError, lambda: density(M3 + M)(Z))
|
||||
raises(ValueError, lambda: density(M)(1))
|
||||
raises(ValueError, lambda: MatrixGamma('M', -1, 2, [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixGamma('M', -1, -2, [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixGamma('M', -1, 2, [[1, 0], [2, 1]]))
|
||||
raises(ValueError, lambda: MatrixGamma('M', -1, 2, [[1, 0], [0]]))
|
||||
|
||||
def test_Wishart():
|
||||
W = Wishart('W', 5, [[1, 0], [0, 1]])
|
||||
assert W.pspace.distribution.set == MatrixSet(2, 2, S.Reals)
|
||||
X = MatrixSymbol('X', 2, 2)
|
||||
term1 = exp(Trace(Matrix([[-S(1)/2, 0], [0, -S(1)/2]])*X))
|
||||
assert density(W)(X).doit() == term1 * Determinant(X)/(24*pi)
|
||||
assert density(W)([[2, 1], [1, 2]]).doit() == exp(-2)/(8*pi)
|
||||
n = symbols('n', positive=True)
|
||||
d = symbols('d', positive=True, integer=True)
|
||||
Y = MatrixSymbol('Y', d, d)
|
||||
SM = MatrixSymbol('SM', d, d)
|
||||
W = Wishart('W', n, SM)
|
||||
k = Dummy('k')
|
||||
exprd = 2**(-d*n/2)*pi**(-d*(d - 1)/4)*exp(Trace(-(S(1)/2)*SM**(-1)*Y)
|
||||
)*Determinant(SM)**(-n/2)*Determinant(Y)**(
|
||||
-d/2 + n/2 - S(1)/2)/Product(gamma(-k/2 + n/2 + S(1)/2), (k, 1, d))
|
||||
assert density(W)(Y).dummy_eq(exprd)
|
||||
raises(ValueError, lambda: density(W)(1))
|
||||
raises(ValueError, lambda: Wishart('W', -1, [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: Wishart('W', -1, [[1, 0], [2, 1]]))
|
||||
raises(ValueError, lambda: Wishart('W', 2, [[1, 0], [0]]))
|
||||
|
||||
def test_MatrixNormal():
|
||||
M = MatrixNormal('M', [[5, 6]], [4], [[2, 1], [1, 2]])
|
||||
assert M.pspace.distribution.set == MatrixSet(1, 2, S.Reals)
|
||||
X = MatrixSymbol('X', 1, 2)
|
||||
term1 = exp(-Trace(Matrix([[ S(2)/3, -S(1)/3], [-S(1)/3, S(2)/3]])*(
|
||||
Matrix([[-5], [-6]]) + X.T)*Matrix([[S(1)/4]])*(Matrix([[-5, -6]]) + X))/2)
|
||||
assert density(M)(X).doit() == (sqrt(3)) * term1/(24*pi)
|
||||
assert density(M)([[7, 8]]).doit() == sqrt(3)*exp(-S(1)/3)/(24*pi)
|
||||
d, n = symbols('d n', positive=True, integer=True)
|
||||
SM2 = MatrixSymbol('SM2', d, d)
|
||||
SM1 = MatrixSymbol('SM1', n, n)
|
||||
LM = MatrixSymbol('LM', n, d)
|
||||
Y = MatrixSymbol('Y', n, d)
|
||||
M = MatrixNormal('M', LM, SM1, SM2)
|
||||
exprd = (2*pi)**(-d*n/2)*exp(-Trace(SM2**(-1)*(-LM.T + Y.T)*SM1**(-1)*(-LM + Y)
|
||||
)/2)*Determinant(SM1)**(-d/2)*Determinant(SM2)**(-n/2)
|
||||
assert density(M)(Y).doit() == exprd
|
||||
raises(ValueError, lambda: density(M)(1))
|
||||
raises(ValueError, lambda: MatrixNormal('M', [1, 2], [[1, 0], [0, 1]], [[1, 0], [2, 1]]))
|
||||
raises(ValueError, lambda: MatrixNormal('M', [1, 2], [[1, 0], [2, 1]], [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixNormal('M', [1, 2], [[1, 0], [0, 1]], [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixNormal('M', [1, 2], [[1, 0], [2]], [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixNormal('M', [1, 2], [[1, 0], [2, 1]], [[1, 0], [0]]))
|
||||
raises(ValueError, lambda: MatrixNormal('M', [[1, 2]], [[1, 0], [0, 1]], [[1, 0]]))
|
||||
raises(ValueError, lambda: MatrixNormal('M', [[1, 2]], [1], [[1, 0]]))
|
||||
|
||||
def test_MatrixStudentT():
|
||||
M = MatrixStudentT('M', 2, [[5, 6]], [[2, 1], [1, 2]], [4])
|
||||
assert M.pspace.distribution.set == MatrixSet(1, 2, S.Reals)
|
||||
X = MatrixSymbol('X', 1, 2)
|
||||
D = pi ** (-1.0) * Determinant(Matrix([[4]])) ** (-1.0) * Determinant(Matrix([[2, 1], [1, 2]])) \
|
||||
** (-0.5) / Determinant(Matrix([[S(1) / 4]]) * (Matrix([[-5, -6]]) + X)
|
||||
* Matrix([[S(2) / 3, -S(1) / 3], [-S(1) / 3, S(2) / 3]]) * (
|
||||
Matrix([[-5], [-6]]) + X.T) + Matrix([[1]])) ** 2
|
||||
assert density(M)(X) == D
|
||||
|
||||
v = symbols('v', positive=True)
|
||||
n, p = 1, 2
|
||||
Omega = MatrixSymbol('Omega', p, p)
|
||||
Sigma = MatrixSymbol('Sigma', n, n)
|
||||
Location = MatrixSymbol('Location', n, p)
|
||||
Y = MatrixSymbol('Y', n, p)
|
||||
M = MatrixStudentT('M', v, Location, Omega, Sigma)
|
||||
|
||||
exprd = gamma(v/2 + 1)*Determinant(Matrix([[1]]) + Sigma**(-1)*(-Location + Y)*Omega**(-1)*(-Location.T + Y.T))**(-v/2 - 1) / \
|
||||
(pi*gamma(v/2)*sqrt(Determinant(Omega))*Determinant(Sigma))
|
||||
|
||||
assert density(M)(Y) == exprd
|
||||
raises(ValueError, lambda: density(M)(1))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', 1, [1, 2], [[1, 0], [0, 1]], [[1, 0], [2, 1]]))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', 1, [1, 2], [[1, 0], [2, 1]], [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', 1, [1, 2], [[1, 0], [0, 1]], [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', 1, [1, 2], [[1, 0], [2]], [[1, 0], [0, 1]]))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', 1, [1, 2], [[1, 0], [2, 1]], [[1], [2]]))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', 1, [[1, 2]], [[1, 0], [0, 1]], [[1, 0]]))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', 1, [[1, 2]], [1], [[1, 0]]))
|
||||
raises(ValueError, lambda: MatrixStudentT('M', -1, [1, 2], [[1, 0], [0, 1]], [4]))
|
||||
|
||||
def test_sample_scipy():
|
||||
distribs_scipy = [
|
||||
MatrixNormal('M', [[5, 6]], [4], [[2, 1], [1, 2]]),
|
||||
Wishart('W', 5, [[1, 0], [0, 1]])
|
||||
]
|
||||
|
||||
size = 5
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy not installed. Abort tests for _sample_scipy.')
|
||||
else:
|
||||
for X in distribs_scipy:
|
||||
samps = sample(X, size=size)
|
||||
for sam in samps:
|
||||
assert Matrix(sam) in X.pspace.distribution.set
|
||||
M = MatrixGamma('M', 1, 2, [[1, 0], [0, 1]])
|
||||
raises(NotImplementedError, lambda: sample(M, size=3))
|
||||
|
||||
def test_sample_pymc():
|
||||
distribs_pymc = [
|
||||
MatrixNormal('M', [[5, 6], [3, 4]], [[1, 0], [0, 1]], [[2, 1], [1, 2]]),
|
||||
Wishart('W', 7, [[2, 1], [1, 2]])
|
||||
]
|
||||
size = 3
|
||||
pymc = import_module('pymc')
|
||||
if not pymc:
|
||||
skip('PyMC is not installed. Abort tests for _sample_pymc.')
|
||||
else:
|
||||
for X in distribs_pymc:
|
||||
samps = sample(X, size=size, library='pymc')
|
||||
for sam in samps:
|
||||
assert Matrix(sam) in X.pspace.distribution.set
|
||||
M = MatrixGamma('M', 1, 2, [[1, 0], [0, 1]])
|
||||
raises(NotImplementedError, lambda: sample(M, size=3))
|
||||
|
||||
def test_sample_seed():
|
||||
X = MatrixNormal('M', [[5, 6], [3, 4]], [[1, 0], [0, 1]], [[2, 1], [1, 2]])
|
||||
|
||||
libraries = ['scipy', 'numpy', 'pymc']
|
||||
for lib in libraries:
|
||||
try:
|
||||
imported_lib = import_module(lib)
|
||||
if imported_lib:
|
||||
s0, s1, s2 = [], [], []
|
||||
s0 = sample(X, size=10, library=lib, seed=0)
|
||||
s1 = sample(X, size=10, library=lib, seed=0)
|
||||
s2 = sample(X, size=10, library=lib, seed=1)
|
||||
for i in range(10):
|
||||
assert (s0[i] == s1[i]).all()
|
||||
assert (s1[i] != s2[i]).all()
|
||||
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.add import Add
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import (Integer, oo, pi)
|
||||
from sympy.core.power import Pow
|
||||
from sympy.core.relational import (Eq, Ne)
|
||||
from sympy.core.symbol import (Dummy, Symbol, symbols)
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.delta_functions import DiracDelta
|
||||
from sympy.functions.special.gamma_functions import gamma
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy.tensor.indexed import (Indexed, IndexedBase)
|
||||
from sympy.functions.elementary.piecewise import ExprCondPair
|
||||
from sympy.stats import (Poisson, Beta, Exponential, P,
|
||||
Multinomial, MultivariateBeta)
|
||||
from sympy.stats.crv_types import Normal
|
||||
from sympy.stats.drv_types import PoissonDistribution
|
||||
from sympy.stats.compound_rv import CompoundPSpace, CompoundDistribution
|
||||
from sympy.stats.joint_rv import MarginalDistribution
|
||||
from sympy.stats.rv import pspace, density
|
||||
from sympy.testing.pytest import ignore_warnings
|
||||
|
||||
def test_density():
|
||||
x = Symbol('x')
|
||||
l = Symbol('l', positive=True)
|
||||
rate = Beta(l, 2, 3)
|
||||
X = Poisson(x, rate)
|
||||
assert isinstance(pspace(X), CompoundPSpace)
|
||||
assert density(X, Eq(rate, rate.symbol)) == PoissonDistribution(l)
|
||||
N1 = Normal('N1', 0, 1)
|
||||
N2 = Normal('N2', N1, 2)
|
||||
assert density(N2)(0).doit() == sqrt(10)/(10*sqrt(pi))
|
||||
assert simplify(density(N2, Eq(N1, 1))(x)) == \
|
||||
sqrt(2)*exp(-(x - 1)**2/8)/(4*sqrt(pi))
|
||||
assert simplify(density(N2)(x)) == sqrt(10)*exp(-x**2/10)/(10*sqrt(pi))
|
||||
|
||||
def test_MarginalDistribution():
|
||||
a1, p1, p2 = symbols('a1 p1 p2', positive=True)
|
||||
C = Multinomial('C', 2, p1, p2)
|
||||
B = MultivariateBeta('B', a1, C[0])
|
||||
MGR = MarginalDistribution(B, (C[0],))
|
||||
mgrc = Mul(Symbol('B'), Piecewise(ExprCondPair(Mul(Integer(2),
|
||||
Pow(Symbol('p1', positive=True), Indexed(IndexedBase(Symbol('C')),
|
||||
Integer(0))), Pow(Symbol('p2', positive=True),
|
||||
Indexed(IndexedBase(Symbol('C')), Integer(1))),
|
||||
Pow(factorial(Indexed(IndexedBase(Symbol('C')), Integer(0))), Integer(-1)),
|
||||
Pow(factorial(Indexed(IndexedBase(Symbol('C')), Integer(1))), Integer(-1))),
|
||||
Eq(Add(Indexed(IndexedBase(Symbol('C')), Integer(0)),
|
||||
Indexed(IndexedBase(Symbol('C')), Integer(1))), Integer(2))),
|
||||
ExprCondPair(Integer(0), True)), Pow(gamma(Symbol('a1', positive=True)),
|
||||
Integer(-1)), gamma(Add(Symbol('a1', positive=True),
|
||||
Indexed(IndexedBase(Symbol('C')), Integer(0)))),
|
||||
Pow(gamma(Indexed(IndexedBase(Symbol('C')), Integer(0))), Integer(-1)),
|
||||
Pow(Indexed(IndexedBase(Symbol('B')), Integer(0)),
|
||||
Add(Symbol('a1', positive=True), Integer(-1))),
|
||||
Pow(Indexed(IndexedBase(Symbol('B')), Integer(1)),
|
||||
Add(Indexed(IndexedBase(Symbol('C')), Integer(0)), Integer(-1))))
|
||||
assert MGR(C) == mgrc
|
||||
|
||||
def test_compound_distribution():
|
||||
Y = Poisson('Y', 1)
|
||||
Z = Poisson('Z', Y)
|
||||
assert isinstance(pspace(Z), CompoundPSpace)
|
||||
assert isinstance(pspace(Z).distribution, CompoundDistribution)
|
||||
assert Z.pspace.distribution.pdf(1).doit() == exp(-2)*exp(exp(-1))
|
||||
|
||||
def test_mix_expression():
|
||||
Y, E = Poisson('Y', 1), Exponential('E', 1)
|
||||
k = Dummy('k')
|
||||
expr1 = Integral(Sum(exp(-1)*Integral(exp(-k)*DiracDelta(k - 2), (k, 0, oo)
|
||||
)/factorial(k), (k, 0, oo)), (k, -oo, 0))
|
||||
expr2 = Integral(Sum(exp(-1)*Integral(exp(-k)*DiracDelta(k - 2), (k, 0, oo)
|
||||
)/factorial(k), (k, 0, oo)), (k, 0, oo))
|
||||
assert P(Eq(Y + E, 1)) == 0
|
||||
assert P(Ne(Y + E, 2)) == 1
|
||||
with ignore_warnings(UserWarning): ### TODO: Restore tests once warnings are removed
|
||||
assert P(E + Y < 2, evaluate=False).rewrite(Integral).dummy_eq(expr1)
|
||||
assert P(E + Y > 2, evaluate=False).rewrite(Integral).dummy_eq(expr2)
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
from sympy.concrete.products import Product
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.numbers import (I, Rational, 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.elementary.miscellaneous import sqrt
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
||||
from sympy.matrices.expressions.trace import Trace
|
||||
from sympy.tensor.indexed import IndexedBase
|
||||
from sympy.stats import (GaussianUnitaryEnsemble as GUE, density,
|
||||
GaussianOrthogonalEnsemble as GOE,
|
||||
GaussianSymplecticEnsemble as GSE,
|
||||
joint_eigen_distribution,
|
||||
CircularUnitaryEnsemble as CUE,
|
||||
CircularOrthogonalEnsemble as COE,
|
||||
CircularSymplecticEnsemble as CSE,
|
||||
JointEigenDistribution,
|
||||
level_spacing_distribution,
|
||||
Normal, Beta)
|
||||
from sympy.stats.joint_rv_types import JointDistributionHandmade
|
||||
from sympy.stats.rv import RandomMatrixSymbol
|
||||
from sympy.stats.random_matrix_models import GaussianEnsemble, RandomMatrixPSpace
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
def test_GaussianEnsemble():
|
||||
G = GaussianEnsemble('G', 3)
|
||||
assert density(G) == G.pspace.model
|
||||
raises(ValueError, lambda: GaussianEnsemble('G', 3.5))
|
||||
|
||||
def test_GaussianUnitaryEnsemble():
|
||||
H = RandomMatrixSymbol('H', 3, 3)
|
||||
G = GUE('U', 3)
|
||||
assert density(G)(H) == sqrt(2)*exp(-3*Trace(H**2)/2)/(4*pi**Rational(9, 2))
|
||||
i, j = (Dummy('i', integer=True, positive=True),
|
||||
Dummy('j', integer=True, positive=True))
|
||||
l = IndexedBase('l')
|
||||
assert joint_eigen_distribution(G).dummy_eq(
|
||||
Lambda((l[1], l[2], l[3]),
|
||||
27*sqrt(6)*exp(-3*(l[1]**2)/2 - 3*(l[2]**2)/2 - 3*(l[3]**2)/2)*
|
||||
Product(Abs(l[i] - l[j])**2, (j, i + 1, 3), (i, 1, 2))/(16*pi**Rational(3, 2))))
|
||||
s = Dummy('s')
|
||||
assert level_spacing_distribution(G).dummy_eq(Lambda(s, 32*s**2*exp(-4*s**2/pi)/pi**2))
|
||||
|
||||
|
||||
def test_GaussianOrthogonalEnsemble():
|
||||
H = RandomMatrixSymbol('H', 3, 3)
|
||||
_H = MatrixSymbol('_H', 3, 3)
|
||||
G = GOE('O', 3)
|
||||
assert density(G)(H) == exp(-3*Trace(H**2)/4)/Integral(exp(-3*Trace(_H**2)/4), _H)
|
||||
i, j = (Dummy('i', integer=True, positive=True),
|
||||
Dummy('j', integer=True, positive=True))
|
||||
l = IndexedBase('l')
|
||||
assert joint_eigen_distribution(G).dummy_eq(
|
||||
Lambda((l[1], l[2], l[3]),
|
||||
9*sqrt(2)*exp(-3*l[1]**2/2 - 3*l[2]**2/2 - 3*l[3]**2/2)*
|
||||
Product(Abs(l[i] - l[j]), (j, i + 1, 3), (i, 1, 2))/(32*pi)))
|
||||
s = Dummy('s')
|
||||
assert level_spacing_distribution(G).dummy_eq(Lambda(s, s*pi*exp(-s**2*pi/4)/2))
|
||||
|
||||
def test_GaussianSymplecticEnsemble():
|
||||
H = RandomMatrixSymbol('H', 3, 3)
|
||||
_H = MatrixSymbol('_H', 3, 3)
|
||||
G = GSE('O', 3)
|
||||
assert density(G)(H) == exp(-3*Trace(H**2))/Integral(exp(-3*Trace(_H**2)), _H)
|
||||
i, j = (Dummy('i', integer=True, positive=True),
|
||||
Dummy('j', integer=True, positive=True))
|
||||
l = IndexedBase('l')
|
||||
assert joint_eigen_distribution(G).dummy_eq(
|
||||
Lambda((l[1], l[2], l[3]),
|
||||
162*sqrt(3)*exp(-3*l[1]**2/2 - 3*l[2]**2/2 - 3*l[3]**2/2)*
|
||||
Product(Abs(l[i] - l[j])**4, (j, i + 1, 3), (i, 1, 2))/(5*pi**Rational(3, 2))))
|
||||
s = Dummy('s')
|
||||
assert level_spacing_distribution(G).dummy_eq(Lambda(s, S(262144)*s**4*exp(-64*s**2/(9*pi))/(729*pi**3)))
|
||||
|
||||
def test_CircularUnitaryEnsemble():
|
||||
CU = CUE('U', 3)
|
||||
j, k = (Dummy('j', integer=True, positive=True),
|
||||
Dummy('k', integer=True, positive=True))
|
||||
t = IndexedBase('t')
|
||||
assert joint_eigen_distribution(CU).dummy_eq(
|
||||
Lambda((t[1], t[2], t[3]),
|
||||
Product(Abs(exp(I*t[j]) - exp(I*t[k]))**2,
|
||||
(j, k + 1, 3), (k, 1, 2))/(48*pi**3))
|
||||
)
|
||||
|
||||
def test_CircularOrthogonalEnsemble():
|
||||
CO = COE('U', 3)
|
||||
j, k = (Dummy('j', integer=True, positive=True),
|
||||
Dummy('k', integer=True, positive=True))
|
||||
t = IndexedBase('t')
|
||||
assert joint_eigen_distribution(CO).dummy_eq(
|
||||
Lambda((t[1], t[2], t[3]),
|
||||
Product(Abs(exp(I*t[j]) - exp(I*t[k])),
|
||||
(j, k + 1, 3), (k, 1, 2))/(48*pi**2))
|
||||
)
|
||||
|
||||
def test_CircularSymplecticEnsemble():
|
||||
CS = CSE('U', 3)
|
||||
j, k = (Dummy('j', integer=True, positive=True),
|
||||
Dummy('k', integer=True, positive=True))
|
||||
t = IndexedBase('t')
|
||||
assert joint_eigen_distribution(CS).dummy_eq(
|
||||
Lambda((t[1], t[2], t[3]),
|
||||
Product(Abs(exp(I*t[j]) - exp(I*t[k]))**4,
|
||||
(j, k + 1, 3), (k, 1, 2))/(720*pi**3))
|
||||
)
|
||||
|
||||
def test_JointEigenDistribution():
|
||||
A = Matrix([[Normal('A00', 0, 1), Normal('A01', 1, 1)],
|
||||
[Beta('A10', 1, 1), Beta('A11', 1, 1)]])
|
||||
assert JointEigenDistribution(A) == \
|
||||
JointDistributionHandmade(-sqrt(A[0, 0]**2 - 2*A[0, 0]*A[1, 1] + 4*A[0, 1]*A[1, 0] + A[1, 1]**2)/2 +
|
||||
A[0, 0]/2 + A[1, 1]/2, sqrt(A[0, 0]**2 - 2*A[0, 0]*A[1, 1] + 4*A[0, 1]*A[1, 0] + A[1, 1]**2)/2 + A[0, 0]/2 + A[1, 1]/2)
|
||||
raises(ValueError, lambda: JointEigenDistribution(Matrix([[1, 0], [2, 1]])))
|
||||
|
||||
def test_issue_19841():
|
||||
G1 = GUE('U', 2)
|
||||
G2 = G1.xreplace({2: 2})
|
||||
assert G1.args == G2.args
|
||||
|
||||
X = MatrixSymbol('X', 2, 2)
|
||||
G = GSE('U', 2)
|
||||
h_pspace = RandomMatrixPSpace('P', model=density(G))
|
||||
H = RandomMatrixSymbol('H', 2, 2, pspace=h_pspace)
|
||||
H2 = RandomMatrixSymbol('H', 2, 2, pspace=None)
|
||||
assert H.doit() == H
|
||||
|
||||
assert (2*H).xreplace({H: X}) == 2*X
|
||||
assert (2*H).xreplace({H2: X}) == 2*H
|
||||
assert (2*H2).xreplace({H: X}) == 2*H2
|
||||
assert (2*H2).xreplace({H2: X}) == 2*X
|
||||
441
venv/lib/python3.12/site-packages/sympy/stats/tests/test_rv.py
Normal file
441
venv/lib/python3.12/site-packages/sympy/stats/tests/test_rv.py
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.basic import Basic
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.numbers import (Rational, nan, oo, pi)
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.combinatorial.factorials import (FallingFactorial, binomial)
|
||||
from sympy.functions.elementary.exponential import (exp, log)
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.functions.special.delta_functions import DiracDelta
|
||||
from sympy.integrals.integrals import integrate
|
||||
from sympy.logic.boolalg import (And, Or)
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.sets.sets import Interval
|
||||
from sympy.tensor.indexed import Indexed
|
||||
from sympy.stats import (Die, Normal, Exponential, FiniteRV, P, E, H, variance,
|
||||
density, given, independent, dependent, where, pspace, GaussianUnitaryEnsemble,
|
||||
random_symbols, sample, Geometric, factorial_moment, Binomial, Hypergeometric,
|
||||
DiscreteUniform, Poisson, characteristic_function, moment_generating_function,
|
||||
BernoulliProcess, Variance, Expectation, Probability, Covariance, covariance, cmoment,
|
||||
moment, median)
|
||||
from sympy.stats.rv import (IndependentProductPSpace, rs_swap, Density, NamedArgsMixin,
|
||||
RandomSymbol, sample_iter, PSpace, is_random, RandomIndexedSymbol, RandomMatrixSymbol)
|
||||
from sympy.testing.pytest import raises, skip, XFAIL, warns_deprecated_sympy
|
||||
from sympy.external import import_module
|
||||
from sympy.core.numbers import comp
|
||||
from sympy.stats.frv_types import BernoulliDistribution
|
||||
from sympy.core.symbol import Dummy
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
|
||||
def test_where():
|
||||
X, Y = Die('X'), Die('Y')
|
||||
Z = Normal('Z', 0, 1)
|
||||
|
||||
assert where(Z**2 <= 1).set == Interval(-1, 1)
|
||||
assert where(Z**2 <= 1).as_boolean() == Interval(-1, 1).as_relational(Z.symbol)
|
||||
assert where(And(X > Y, Y > 4)).as_boolean() == And(
|
||||
Eq(X.symbol, 6), Eq(Y.symbol, 5))
|
||||
|
||||
assert len(where(X < 3).set) == 2
|
||||
assert 1 in where(X < 3).set
|
||||
|
||||
X, Y = Normal('X', 0, 1), Normal('Y', 0, 1)
|
||||
assert where(And(X**2 <= 1, X >= 0)).set == Interval(0, 1)
|
||||
XX = given(X, And(X**2 <= 1, X >= 0))
|
||||
assert XX.pspace.domain.set == Interval(0, 1)
|
||||
assert XX.pspace.domain.as_boolean() == \
|
||||
And(0 <= X.symbol, X.symbol**2 <= 1, -oo < X.symbol, X.symbol < oo)
|
||||
|
||||
with raises(TypeError):
|
||||
XX = given(X, X + 3)
|
||||
|
||||
|
||||
def test_random_symbols():
|
||||
X, Y = Normal('X', 0, 1), Normal('Y', 0, 1)
|
||||
|
||||
assert set(random_symbols(2*X + 1)) == {X}
|
||||
assert set(random_symbols(2*X + Y)) == {X, Y}
|
||||
assert set(random_symbols(2*X + Y.symbol)) == {X}
|
||||
assert set(random_symbols(2)) == set()
|
||||
|
||||
|
||||
def test_characteristic_function():
|
||||
# Imports I from sympy
|
||||
from sympy.core.numbers import I
|
||||
X = Normal('X',0,1)
|
||||
Y = DiscreteUniform('Y', [1,2,7])
|
||||
Z = Poisson('Z', 2)
|
||||
t = symbols('_t')
|
||||
P = Lambda(t, exp(-t**2/2))
|
||||
Q = Lambda(t, exp(7*t*I)/3 + exp(2*t*I)/3 + exp(t*I)/3)
|
||||
R = Lambda(t, exp(2 * exp(t*I) - 2))
|
||||
|
||||
|
||||
assert characteristic_function(X).dummy_eq(P)
|
||||
assert characteristic_function(Y).dummy_eq(Q)
|
||||
assert characteristic_function(Z).dummy_eq(R)
|
||||
|
||||
|
||||
def test_moment_generating_function():
|
||||
|
||||
X = Normal('X',0,1)
|
||||
Y = DiscreteUniform('Y', [1,2,7])
|
||||
Z = Poisson('Z', 2)
|
||||
t = symbols('_t')
|
||||
P = Lambda(t, exp(t**2/2))
|
||||
Q = Lambda(t, (exp(7*t)/3 + exp(2*t)/3 + exp(t)/3))
|
||||
R = Lambda(t, exp(2 * exp(t) - 2))
|
||||
|
||||
|
||||
assert moment_generating_function(X).dummy_eq(P)
|
||||
assert moment_generating_function(Y).dummy_eq(Q)
|
||||
assert moment_generating_function(Z).dummy_eq(R)
|
||||
|
||||
def test_sample_iter():
|
||||
|
||||
X = Normal('X',0,1)
|
||||
Y = DiscreteUniform('Y', [1, 2, 7])
|
||||
Z = Poisson('Z', 2)
|
||||
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests')
|
||||
expr = X**2 + 3
|
||||
iterator = sample_iter(expr)
|
||||
|
||||
expr2 = Y**2 + 5*Y + 4
|
||||
iterator2 = sample_iter(expr2)
|
||||
|
||||
expr3 = Z**3 + 4
|
||||
iterator3 = sample_iter(expr3)
|
||||
|
||||
def is_iterator(obj):
|
||||
if (
|
||||
hasattr(obj, '__iter__') and
|
||||
(hasattr(obj, 'next') or
|
||||
hasattr(obj, '__next__')) and
|
||||
callable(obj.__iter__) and
|
||||
obj.__iter__() is obj
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
assert is_iterator(iterator)
|
||||
assert is_iterator(iterator2)
|
||||
assert is_iterator(iterator3)
|
||||
|
||||
def test_pspace():
|
||||
X, Y = Normal('X', 0, 1), Normal('Y', 0, 1)
|
||||
x = Symbol('x')
|
||||
|
||||
raises(ValueError, lambda: pspace(5 + 3))
|
||||
raises(ValueError, lambda: pspace(x < 1))
|
||||
assert pspace(X) == X.pspace
|
||||
assert pspace(2*X + 1) == X.pspace
|
||||
assert pspace(2*X + Y) == IndependentProductPSpace(Y.pspace, X.pspace)
|
||||
|
||||
def test_rs_swap():
|
||||
X = Normal('x', 0, 1)
|
||||
Y = Exponential('y', 1)
|
||||
|
||||
XX = Normal('x', 0, 2)
|
||||
YY = Normal('y', 0, 3)
|
||||
|
||||
expr = 2*X + Y
|
||||
assert expr.subs(rs_swap((X, Y), (YY, XX))) == 2*XX + YY
|
||||
|
||||
|
||||
def test_RandomSymbol():
|
||||
|
||||
X = Normal('x', 0, 1)
|
||||
Y = Normal('x', 0, 2)
|
||||
assert X.symbol == Y.symbol
|
||||
assert X != Y
|
||||
|
||||
assert X.name == X.symbol.name
|
||||
|
||||
X = Normal('lambda', 0, 1) # make sure we can use protected terms
|
||||
X = Normal('Lambda', 0, 1) # make sure we can use SymPy terms
|
||||
|
||||
|
||||
def test_RandomSymbol_diff():
|
||||
X = Normal('x', 0, 1)
|
||||
assert (2*X).diff(X)
|
||||
|
||||
|
||||
def test_random_symbol_no_pspace():
|
||||
x = RandomSymbol(Symbol('x'))
|
||||
assert x.pspace == PSpace()
|
||||
|
||||
def test_overlap():
|
||||
X = Normal('x', 0, 1)
|
||||
Y = Normal('x', 0, 2)
|
||||
|
||||
raises(ValueError, lambda: P(X > Y))
|
||||
|
||||
|
||||
def test_IndependentProductPSpace():
|
||||
X = Normal('X', 0, 1)
|
||||
Y = Normal('Y', 0, 1)
|
||||
px = X.pspace
|
||||
py = Y.pspace
|
||||
assert pspace(X + Y) == IndependentProductPSpace(px, py)
|
||||
assert pspace(X + Y) == IndependentProductPSpace(py, px)
|
||||
|
||||
|
||||
def test_E():
|
||||
assert E(5) == 5
|
||||
|
||||
|
||||
def test_H():
|
||||
X = Normal('X', 0, 1)
|
||||
D = Die('D', sides = 4)
|
||||
G = Geometric('G', 0.5)
|
||||
assert H(X, X > 0) == -log(2)/2 + S.Half + log(pi)/2
|
||||
assert H(D, D > 2) == log(2)
|
||||
assert comp(H(G).evalf().round(2), 1.39)
|
||||
|
||||
|
||||
def test_Sample():
|
||||
X = Die('X', 6)
|
||||
Y = Normal('Y', 0, 1)
|
||||
z = Symbol('z', integer=True)
|
||||
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests')
|
||||
assert sample(X) in [1, 2, 3, 4, 5, 6]
|
||||
assert isinstance(sample(X + Y), float)
|
||||
|
||||
assert P(X + Y > 0, Y < 0, numsamples=10).is_number
|
||||
assert E(X + Y, numsamples=10).is_number
|
||||
assert E(X**2 + Y, numsamples=10).is_number
|
||||
assert E((X + Y)**2, numsamples=10).is_number
|
||||
assert variance(X + Y, numsamples=10).is_number
|
||||
|
||||
raises(TypeError, lambda: P(Y > z, numsamples=5))
|
||||
|
||||
assert P(sin(Y) <= 1, numsamples=10) == 1.0
|
||||
assert P(sin(Y) <= 1, cos(Y) < 1, numsamples=10) == 1.0
|
||||
|
||||
assert all(i in range(1, 7) for i in density(X, numsamples=10))
|
||||
assert all(i in range(4, 7) for i in density(X, X>3, numsamples=10))
|
||||
|
||||
numpy = import_module('numpy')
|
||||
if not numpy:
|
||||
skip('Numpy is not installed. Abort tests')
|
||||
#Test Issue #21563: Output of sample must be a float or array
|
||||
assert isinstance(sample(X), (numpy.int32, numpy.int64))
|
||||
assert isinstance(sample(Y), numpy.float64)
|
||||
assert isinstance(sample(X, size=2), numpy.ndarray)
|
||||
|
||||
with warns_deprecated_sympy():
|
||||
sample(X, numsamples=2)
|
||||
|
||||
@XFAIL
|
||||
def test_samplingE():
|
||||
scipy = import_module('scipy')
|
||||
if not scipy:
|
||||
skip('Scipy is not installed. Abort tests')
|
||||
Y = Normal('Y', 0, 1)
|
||||
z = Symbol('z', integer=True)
|
||||
assert E(Sum(1/z**Y, (z, 1, oo)), Y > 2, numsamples=3).is_number
|
||||
|
||||
|
||||
def test_given():
|
||||
X = Normal('X', 0, 1)
|
||||
Y = Normal('Y', 0, 1)
|
||||
A = given(X, True)
|
||||
B = given(X, Y > 2)
|
||||
|
||||
assert X == A == B
|
||||
|
||||
|
||||
def test_factorial_moment():
|
||||
X = Poisson('X', 2)
|
||||
Y = Binomial('Y', 2, S.Half)
|
||||
Z = Hypergeometric('Z', 4, 2, 2)
|
||||
assert factorial_moment(X, 2) == 4
|
||||
assert factorial_moment(Y, 2) == S.Half
|
||||
assert factorial_moment(Z, 2) == Rational(1, 3)
|
||||
|
||||
x, y, z, l = symbols('x y z l')
|
||||
Y = Binomial('Y', 2, y)
|
||||
Z = Hypergeometric('Z', 10, 2, 3)
|
||||
assert factorial_moment(Y, l) == y**2*FallingFactorial(
|
||||
2, l) + 2*y*(1 - y)*FallingFactorial(1, l) + (1 - y)**2*\
|
||||
FallingFactorial(0, l)
|
||||
assert factorial_moment(Z, l) == 7*FallingFactorial(0, l)/\
|
||||
15 + 7*FallingFactorial(1, l)/15 + FallingFactorial(2, l)/15
|
||||
|
||||
|
||||
def test_dependence():
|
||||
X, Y = Die('X'), Die('Y')
|
||||
assert independent(X, 2*Y)
|
||||
assert not dependent(X, 2*Y)
|
||||
|
||||
X, Y = Normal('X', 0, 1), Normal('Y', 0, 1)
|
||||
assert independent(X, Y)
|
||||
assert dependent(X, 2*X)
|
||||
|
||||
# Create a dependency
|
||||
XX, YY = given(Tuple(X, Y), Eq(X + Y, 3))
|
||||
assert dependent(XX, YY)
|
||||
|
||||
def test_dependent_finite():
|
||||
X, Y = Die('X'), Die('Y')
|
||||
# Dependence testing requires symbolic conditions which currently break
|
||||
# finite random variables
|
||||
assert dependent(X, Y + X)
|
||||
|
||||
XX, YY = given(Tuple(X, Y), X + Y > 5) # Create a dependency
|
||||
assert dependent(XX, YY)
|
||||
|
||||
|
||||
def test_normality():
|
||||
X, Y = Normal('X', 0, 1), Normal('Y', 0, 1)
|
||||
x = Symbol('x', real=True)
|
||||
z = Symbol('z', real=True)
|
||||
dens = density(X - Y, Eq(X + Y, z))
|
||||
|
||||
assert integrate(dens(x), (x, -oo, oo)) == 1
|
||||
|
||||
|
||||
def test_Density():
|
||||
X = Die('X', 6)
|
||||
d = Density(X)
|
||||
assert d.doit() == density(X)
|
||||
|
||||
def test_NamedArgsMixin():
|
||||
class Foo(Basic, NamedArgsMixin):
|
||||
_argnames = 'foo', 'bar'
|
||||
|
||||
a = Foo(S(1), S(2))
|
||||
|
||||
assert a.foo == 1
|
||||
assert a.bar == 2
|
||||
|
||||
raises(AttributeError, lambda: a.baz)
|
||||
|
||||
class Bar(Basic, NamedArgsMixin):
|
||||
pass
|
||||
|
||||
raises(AttributeError, lambda: Bar(S(1), S(2)).foo)
|
||||
|
||||
def test_density_constant():
|
||||
assert density(3)(2) == 0
|
||||
assert density(3)(3) == DiracDelta(0)
|
||||
|
||||
def test_cmoment_constant():
|
||||
assert variance(3) == 0
|
||||
assert cmoment(3, 3) == 0
|
||||
assert cmoment(3, 4) == 0
|
||||
x = Symbol('x')
|
||||
assert variance(x) == 0
|
||||
assert cmoment(x, 15) == 0
|
||||
assert cmoment(x, 0) == 1
|
||||
|
||||
def test_moment_constant():
|
||||
assert moment(3, 0) == 1
|
||||
assert moment(3, 1) == 3
|
||||
assert moment(3, 2) == 9
|
||||
x = Symbol('x')
|
||||
assert moment(x, 2) == x**2
|
||||
|
||||
def test_median_constant():
|
||||
assert median(3) == 3
|
||||
x = Symbol('x')
|
||||
assert median(x) == x
|
||||
|
||||
def test_real():
|
||||
x = Normal('x', 0, 1)
|
||||
assert x.is_real
|
||||
|
||||
|
||||
def test_issue_10052():
|
||||
X = Exponential('X', 3)
|
||||
assert P(X < oo) == 1
|
||||
assert P(X > oo) == 0
|
||||
assert P(X < 2, X > oo) == 0
|
||||
assert P(X < oo, X > oo) == 0
|
||||
assert P(X < oo, X > 2) == 1
|
||||
assert P(X < 3, X == 2) == 0
|
||||
raises(ValueError, lambda: P(1))
|
||||
raises(ValueError, lambda: P(X < 1, 2))
|
||||
|
||||
def test_issue_11934():
|
||||
density = {0: .5, 1: .5}
|
||||
X = FiniteRV('X', density)
|
||||
assert E(X) == 0.5
|
||||
assert P( X>= 2) == 0
|
||||
|
||||
def test_issue_8129():
|
||||
X = Exponential('X', 4)
|
||||
assert P(X >= X) == 1
|
||||
assert P(X > X) == 0
|
||||
assert P(X > X+1) == 0
|
||||
|
||||
def test_issue_12237():
|
||||
X = Normal('X', 0, 1)
|
||||
Y = Normal('Y', 0, 1)
|
||||
U = P(X > 0, X)
|
||||
V = P(Y < 0, X)
|
||||
W = P(X + Y > 0, X)
|
||||
assert W == P(X + Y > 0, X)
|
||||
assert U == BernoulliDistribution(S.Half, S.Zero, S.One)
|
||||
assert V == S.Half
|
||||
|
||||
def test_is_random():
|
||||
X = Normal('X', 0, 1)
|
||||
Y = Normal('Y', 0, 1)
|
||||
a, b = symbols('a, b')
|
||||
G = GaussianUnitaryEnsemble('U', 2)
|
||||
B = BernoulliProcess('B', 0.9)
|
||||
assert not is_random(a)
|
||||
assert not is_random(a + b)
|
||||
assert not is_random(a * b)
|
||||
assert not is_random(Matrix([a**2, b**2]))
|
||||
assert is_random(X)
|
||||
assert is_random(X**2 + Y)
|
||||
assert is_random(Y + b**2)
|
||||
assert is_random(Y > 5)
|
||||
assert is_random(B[3] < 1)
|
||||
assert is_random(G)
|
||||
assert is_random(X * Y * B[1])
|
||||
assert is_random(Matrix([[X, B[2]], [G, Y]]))
|
||||
assert is_random(Eq(X, 4))
|
||||
|
||||
def test_issue_12283():
|
||||
x = symbols('x')
|
||||
X = RandomSymbol(x)
|
||||
Y = RandomSymbol('Y')
|
||||
Z = RandomMatrixSymbol('Z', 2, 1)
|
||||
W = RandomMatrixSymbol('W', 2, 1)
|
||||
RI = RandomIndexedSymbol(Indexed('RI', 3))
|
||||
assert pspace(Z) == PSpace()
|
||||
assert pspace(RI) == PSpace()
|
||||
assert pspace(X) == PSpace()
|
||||
assert E(X) == Expectation(X)
|
||||
assert P(Y > 3) == Probability(Y > 3)
|
||||
assert variance(X) == Variance(X)
|
||||
assert variance(RI) == Variance(RI)
|
||||
assert covariance(X, Y) == Covariance(X, Y)
|
||||
assert covariance(W, Z) == Covariance(W, Z)
|
||||
|
||||
def test_issue_6810():
|
||||
X = Die('X', 6)
|
||||
Y = Normal('Y', 0, 1)
|
||||
assert P(Eq(X, 2)) == S(1)/6
|
||||
assert P(Eq(Y, 0)) == 0
|
||||
assert P(Or(X > 2, X < 3)) == 1
|
||||
assert P(And(X > 3, X > 2)) == S(1)/2
|
||||
|
||||
def test_issue_20286():
|
||||
n, p = symbols('n p')
|
||||
B = Binomial('B', n, p)
|
||||
k = Dummy('k', integer = True)
|
||||
eq = Sum(Piecewise((-p**k*(1 - p)**(-k + n)*log(p**k*(1 - p)**(-k + n)*binomial(n, k))*binomial(n, k), (k >= 0) & (k <= n)), (nan, True)), (k, 0, n))
|
||||
assert eq.dummy_eq(H(B))
|
||||
|
|
@ -0,0 +1,763 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.containers import Tuple
|
||||
from sympy.core.function import Lambda
|
||||
from sympy.core.numbers import (Float, Rational, oo, pi)
|
||||
from sympy.core.relational import (Eq, Ge, Gt, Le, Lt, Ne)
|
||||
from sympy.core.singleton import S
|
||||
from sympy.core.symbol import (Symbol, symbols)
|
||||
from sympy.functions.combinatorial.factorials import factorial
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.integers import ceiling
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.piecewise import Piecewise
|
||||
from sympy.functions.special.error_functions import erf
|
||||
from sympy.functions.special.gamma_functions import (gamma, lowergamma)
|
||||
from sympy.logic.boolalg import (And, Not)
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
||||
from sympy.matrices.immutable import ImmutableMatrix
|
||||
from sympy.sets.contains import Contains
|
||||
from sympy.sets.fancysets import Range
|
||||
from sympy.sets.sets import (FiniteSet, Interval)
|
||||
from sympy.stats import (DiscreteMarkovChain, P, TransitionMatrixOf, E,
|
||||
StochasticStateSpaceOf, variance, ContinuousMarkovChain,
|
||||
BernoulliProcess, PoissonProcess, WienerProcess,
|
||||
GammaProcess, sample_stochastic_process)
|
||||
from sympy.stats.joint_rv import JointDistribution
|
||||
from sympy.stats.joint_rv_types import JointDistributionHandmade
|
||||
from sympy.stats.rv import RandomIndexedSymbol
|
||||
from sympy.stats.symbolic_probability import Probability, Expectation
|
||||
from sympy.testing.pytest import (raises, skip, ignore_warnings,
|
||||
warns_deprecated_sympy)
|
||||
from sympy.external import import_module
|
||||
from sympy.stats.frv_types import BernoulliDistribution
|
||||
from sympy.stats.drv_types import PoissonDistribution
|
||||
from sympy.stats.crv_types import NormalDistribution, GammaDistribution
|
||||
from sympy.core.symbol import Str
|
||||
|
||||
|
||||
def test_DiscreteMarkovChain():
|
||||
|
||||
# pass only the name
|
||||
X = DiscreteMarkovChain("X")
|
||||
assert isinstance(X.state_space, Range)
|
||||
assert X.index_set == S.Naturals0
|
||||
assert isinstance(X.transition_probabilities, MatrixSymbol)
|
||||
t = symbols('t', positive=True, integer=True)
|
||||
assert isinstance(X[t], RandomIndexedSymbol)
|
||||
assert E(X[0]) == Expectation(X[0])
|
||||
raises(TypeError, lambda: DiscreteMarkovChain(1))
|
||||
raises(NotImplementedError, lambda: X(t))
|
||||
raises(NotImplementedError, lambda: X.communication_classes())
|
||||
raises(NotImplementedError, lambda: X.canonical_form())
|
||||
raises(NotImplementedError, lambda: X.decompose())
|
||||
|
||||
nz = Symbol('n', integer=True)
|
||||
TZ = MatrixSymbol('M', nz, nz)
|
||||
SZ = Range(nz)
|
||||
YZ = DiscreteMarkovChain('Y', SZ, TZ)
|
||||
assert P(Eq(YZ[2], 1), Eq(YZ[1], 0)) == TZ[0, 1]
|
||||
|
||||
raises(ValueError, lambda: sample_stochastic_process(t))
|
||||
raises(ValueError, lambda: next(sample_stochastic_process(X)))
|
||||
# pass name and state_space
|
||||
# any hashable object should be a valid state
|
||||
# states should be valid as a tuple/set/list/Tuple/Range
|
||||
sym, rainy, cloudy, sunny = symbols('a Rainy Cloudy Sunny', real=True)
|
||||
state_spaces = [(1, 2, 3), [Str('Hello'), sym, DiscreteMarkovChain("Y", (1,2,3))],
|
||||
Tuple(S(1), exp(sym), Str('World'), sympify=False), Range(-1, 5, 2),
|
||||
[rainy, cloudy, sunny]]
|
||||
chains = [DiscreteMarkovChain("Y", state_space) for state_space in state_spaces]
|
||||
|
||||
for i, Y in enumerate(chains):
|
||||
assert isinstance(Y.transition_probabilities, MatrixSymbol)
|
||||
assert Y.state_space == state_spaces[i] or Y.state_space == FiniteSet(*state_spaces[i])
|
||||
assert Y.number_of_states == 3
|
||||
|
||||
with ignore_warnings(UserWarning): # TODO: Restore tests once warnings are removed
|
||||
assert P(Eq(Y[2], 1), Eq(Y[0], 2), evaluate=False) == Probability(Eq(Y[2], 1), Eq(Y[0], 2))
|
||||
assert E(Y[0]) == Expectation(Y[0])
|
||||
|
||||
raises(ValueError, lambda: next(sample_stochastic_process(Y)))
|
||||
|
||||
raises(TypeError, lambda: DiscreteMarkovChain("Y", {1: 1}))
|
||||
Y = DiscreteMarkovChain("Y", Range(1, t, 2))
|
||||
assert Y.number_of_states == ceiling((t-1)/2)
|
||||
|
||||
# pass name and transition_probabilities
|
||||
chains = [DiscreteMarkovChain("Y", trans_probs=Matrix([])),
|
||||
DiscreteMarkovChain("Y", trans_probs=Matrix([[0, 1], [1, 0]])),
|
||||
DiscreteMarkovChain("Y", trans_probs=Matrix([[pi, 1-pi], [sym, 1-sym]]))]
|
||||
for Z in chains:
|
||||
assert Z.number_of_states == Z.transition_probabilities.shape[0]
|
||||
assert isinstance(Z.transition_probabilities, ImmutableMatrix)
|
||||
|
||||
# pass name, state_space and transition_probabilities
|
||||
T = Matrix([[0.5, 0.2, 0.3],[0.2, 0.5, 0.3],[0.2, 0.3, 0.5]])
|
||||
TS = MatrixSymbol('T', 3, 3)
|
||||
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||||
YS = DiscreteMarkovChain("Y", ['One', 'Two', 3], TS)
|
||||
assert Y.joint_distribution(1, Y[2], 3) == JointDistribution(Y[1], Y[2], Y[3])
|
||||
raises(ValueError, lambda: Y.joint_distribution(Y[1].symbol, Y[2].symbol))
|
||||
assert P(Eq(Y[3], 2), Eq(Y[1], 1)).round(2) == Float(0.36, 2)
|
||||
assert (P(Eq(YS[3], 2), Eq(YS[1], 1)) -
|
||||
(TS[0, 2]*TS[1, 0] + TS[1, 1]*TS[1, 2] + TS[1, 2]*TS[2, 2])).simplify() == 0
|
||||
assert P(Eq(YS[1], 1), Eq(YS[2], 2)) == Probability(Eq(YS[1], 1))
|
||||
assert P(Eq(YS[3], 3), Eq(YS[1], 1)) == TS[0, 2]*TS[1, 0] + TS[1, 1]*TS[1, 2] + TS[1, 2]*TS[2, 2]
|
||||
TO = Matrix([[0.25, 0.75, 0],[0, 0.25, 0.75],[0.75, 0, 0.25]])
|
||||
assert P(Eq(Y[3], 2), Eq(Y[1], 1) & TransitionMatrixOf(Y, TO)).round(3) == Float(0.375, 3)
|
||||
with ignore_warnings(UserWarning): ### TODO: Restore tests once warnings are removed
|
||||
assert E(Y[3], evaluate=False) == Expectation(Y[3])
|
||||
assert E(Y[3], Eq(Y[2], 1)).round(2) == Float(1.1, 3)
|
||||
TSO = MatrixSymbol('T', 4, 4)
|
||||
raises(ValueError, lambda: str(P(Eq(YS[3], 2), Eq(YS[1], 1) & TransitionMatrixOf(YS, TSO))))
|
||||
raises(TypeError, lambda: DiscreteMarkovChain("Z", [0, 1, 2], symbols('M')))
|
||||
raises(ValueError, lambda: DiscreteMarkovChain("Z", [0, 1, 2], MatrixSymbol('T', 3, 4)))
|
||||
raises(ValueError, lambda: E(Y[3], Eq(Y[2], 6)))
|
||||
raises(ValueError, lambda: E(Y[2], Eq(Y[3], 1)))
|
||||
|
||||
|
||||
# extended tests for probability queries
|
||||
TO1 = Matrix([[Rational(1, 4), Rational(3, 4), 0],[Rational(1, 3), Rational(1, 3), Rational(1, 3)],[0, Rational(1, 4), Rational(3, 4)]])
|
||||
assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)),
|
||||
Eq(Probability(Eq(Y[0], 0)), Rational(1, 4)) & TransitionMatrixOf(Y, TO1)) == Rational(1, 16)
|
||||
assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)), TransitionMatrixOf(Y, TO1)) == \
|
||||
Probability(Eq(Y[0], 0))/4
|
||||
assert P(Lt(X[1], 2) & Gt(X[1], 0), Eq(X[0], 2) &
|
||||
StochasticStateSpaceOf(X, [0, 1, 2]) & TransitionMatrixOf(X, TO1)) == Rational(1, 4)
|
||||
assert P(Lt(X[1], 2) & Gt(X[1], 0), Eq(X[0], 2) &
|
||||
StochasticStateSpaceOf(X, [S(0), '0', 1]) & TransitionMatrixOf(X, TO1)) == Rational(1, 4)
|
||||
assert P(Ne(X[1], 2) & Ne(X[1], 1), Eq(X[0], 2) &
|
||||
StochasticStateSpaceOf(X, [0, 1, 2]) & TransitionMatrixOf(X, TO1)) is S.Zero
|
||||
assert P(Ne(X[1], 2) & Ne(X[1], 1), Eq(X[0], 2) &
|
||||
StochasticStateSpaceOf(X, [S(0), '0', 1]) & TransitionMatrixOf(X, TO1)) is S.Zero
|
||||
assert P(And(Eq(Y[2], 1), Eq(Y[1], 1), Eq(Y[0], 0)), Eq(Y[1], 1)) == 0.1*Probability(Eq(Y[0], 0))
|
||||
|
||||
# testing properties of Markov chain
|
||||
TO2 = Matrix([[S.One, 0, 0],[Rational(1, 3), Rational(1, 3), Rational(1, 3)],[0, Rational(1, 4), Rational(3, 4)]])
|
||||
TO3 = Matrix([[Rational(1, 4), Rational(3, 4), 0],[Rational(1, 3), Rational(1, 3), Rational(1, 3)], [0, Rational(1, 4), Rational(3, 4)]])
|
||||
Y2 = DiscreteMarkovChain('Y', trans_probs=TO2)
|
||||
Y3 = DiscreteMarkovChain('Y', trans_probs=TO3)
|
||||
assert Y3.fundamental_matrix() == ImmutableMatrix([[176, 81, -132], [36, 141, -52], [-44, -39, 208]])/125
|
||||
assert Y2.is_absorbing_chain() == True
|
||||
assert Y3.is_absorbing_chain() == False
|
||||
assert Y2.canonical_form() == ([0, 1, 2], TO2)
|
||||
assert Y3.canonical_form() == ([0, 1, 2], TO3)
|
||||
assert Y2.decompose() == ([0, 1, 2], TO2[0:1, 0:1], TO2[1:3, 0:1], TO2[1:3, 1:3])
|
||||
assert Y3.decompose() == ([0, 1, 2], TO3, Matrix(0, 3, []), Matrix(0, 0, []))
|
||||
TO4 = Matrix([[Rational(1, 5), Rational(2, 5), Rational(2, 5)], [Rational(1, 10), S.Half, Rational(2, 5)], [Rational(3, 5), Rational(3, 10), Rational(1, 10)]])
|
||||
Y4 = DiscreteMarkovChain('Y', trans_probs=TO4)
|
||||
w = ImmutableMatrix([[Rational(11, 39), Rational(16, 39), Rational(4, 13)]])
|
||||
assert Y4.limiting_distribution == w
|
||||
assert Y4.is_regular() == True
|
||||
assert Y4.is_ergodic() == True
|
||||
TS1 = MatrixSymbol('T', 3, 3)
|
||||
Y5 = DiscreteMarkovChain('Y', trans_probs=TS1)
|
||||
assert Y5.limiting_distribution(w, TO4).doit() == True
|
||||
assert Y5.stationary_distribution(condition_set=True).subs(TS1, TO4).contains(w).doit() == S.true
|
||||
TO6 = Matrix([[S.One, 0, 0, 0, 0],[S.Half, 0, S.Half, 0, 0],[0, S.Half, 0, S.Half, 0], [0, 0, S.Half, 0, S.Half], [0, 0, 0, 0, 1]])
|
||||
Y6 = DiscreteMarkovChain('Y', trans_probs=TO6)
|
||||
assert Y6.fundamental_matrix() == ImmutableMatrix([[Rational(3, 2), S.One, S.Half], [S.One, S(2), S.One], [S.Half, S.One, Rational(3, 2)]])
|
||||
assert Y6.absorbing_probabilities() == ImmutableMatrix([[Rational(3, 4), Rational(1, 4)], [S.Half, S.Half], [Rational(1, 4), Rational(3, 4)]])
|
||||
with warns_deprecated_sympy():
|
||||
Y6.absorbing_probabilites()
|
||||
TO7 = Matrix([[Rational(1, 2), Rational(1, 4), Rational(1, 4)], [Rational(1, 2), 0, Rational(1, 2)], [Rational(1, 4), Rational(1, 4), Rational(1, 2)]])
|
||||
Y7 = DiscreteMarkovChain('Y', trans_probs=TO7)
|
||||
assert Y7.is_absorbing_chain() == False
|
||||
assert Y7.fundamental_matrix() == ImmutableMatrix([[Rational(86, 75), Rational(1, 25), Rational(-14, 75)],
|
||||
[Rational(2, 25), Rational(21, 25), Rational(2, 25)],
|
||||
[Rational(-14, 75), Rational(1, 25), Rational(86, 75)]])
|
||||
|
||||
# test for zero-sized matrix functionality
|
||||
X = DiscreteMarkovChain('X', trans_probs=Matrix([]))
|
||||
assert X.number_of_states == 0
|
||||
assert X.stationary_distribution() == Matrix([[]])
|
||||
assert X.communication_classes() == []
|
||||
assert X.canonical_form() == ([], Matrix([]))
|
||||
assert X.decompose() == ([], Matrix([]), Matrix([]), Matrix([]))
|
||||
assert X.is_regular() == False
|
||||
assert X.is_ergodic() == False
|
||||
|
||||
# test communication_class
|
||||
# see https://drive.google.com/drive/folders/1HbxLlwwn2b3U8Lj7eb_ASIUb5vYaNIjg?usp=sharing
|
||||
# tutorial 2.pdf
|
||||
TO7 = Matrix([[0, 5, 5, 0, 0],
|
||||
[0, 0, 0, 10, 0],
|
||||
[5, 0, 5, 0, 0],
|
||||
[0, 10, 0, 0, 0],
|
||||
[0, 3, 0, 3, 4]])/10
|
||||
Y7 = DiscreteMarkovChain('Y', trans_probs=TO7)
|
||||
tuples = Y7.communication_classes()
|
||||
classes, recurrence, periods = list(zip(*tuples))
|
||||
assert classes == ([1, 3], [0, 2], [4])
|
||||
assert recurrence == (True, False, False)
|
||||
assert periods == (2, 1, 1)
|
||||
|
||||
TO8 = Matrix([[0, 0, 0, 10, 0, 0],
|
||||
[5, 0, 5, 0, 0, 0],
|
||||
[0, 4, 0, 0, 0, 6],
|
||||
[10, 0, 0, 0, 0, 0],
|
||||
[0, 10, 0, 0, 0, 0],
|
||||
[0, 0, 0, 5, 5, 0]])/10
|
||||
Y8 = DiscreteMarkovChain('Y', trans_probs=TO8)
|
||||
tuples = Y8.communication_classes()
|
||||
classes, recurrence, periods = list(zip(*tuples))
|
||||
assert classes == ([0, 3], [1, 2, 5, 4])
|
||||
assert recurrence == (True, False)
|
||||
assert periods == (2, 2)
|
||||
|
||||
TO9 = Matrix([[2, 0, 0, 3, 0, 0, 3, 2, 0, 0],
|
||||
[0, 10, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 2, 2, 0, 0, 0, 0, 0, 3, 3],
|
||||
[0, 0, 0, 3, 0, 0, 6, 1, 0, 0],
|
||||
[0, 0, 0, 0, 5, 5, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 10, 0, 0, 0, 0],
|
||||
[4, 0, 0, 5, 0, 0, 1, 0, 0, 0],
|
||||
[2, 0, 0, 4, 0, 0, 2, 2, 0, 0],
|
||||
[3, 0, 1, 0, 0, 0, 0, 0, 4, 2],
|
||||
[0, 0, 4, 0, 0, 0, 0, 0, 3, 3]])/10
|
||||
Y9 = DiscreteMarkovChain('Y', trans_probs=TO9)
|
||||
tuples = Y9.communication_classes()
|
||||
classes, recurrence, periods = list(zip(*tuples))
|
||||
assert classes == ([0, 3, 6, 7], [1], [2, 8, 9], [5], [4])
|
||||
assert recurrence == (True, True, False, True, False)
|
||||
assert periods == (1, 1, 1, 1, 1)
|
||||
|
||||
# test canonical form
|
||||
# see https://web.archive.org/web/20201230182007/https://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/Chapter11.pdf
|
||||
# example 11.13
|
||||
T = Matrix([[1, 0, 0, 0, 0],
|
||||
[S(1) / 2, 0, S(1) / 2, 0, 0],
|
||||
[0, S(1) / 2, 0, S(1) / 2, 0],
|
||||
[0, 0, S(1) / 2, 0, S(1) / 2],
|
||||
[0, 0, 0, 0, S(1)]])
|
||||
DW = DiscreteMarkovChain('DW', [0, 1, 2, 3, 4], T)
|
||||
states, A, B, C = DW.decompose()
|
||||
assert states == [0, 4, 1, 2, 3]
|
||||
assert A == Matrix([[1, 0], [0, 1]])
|
||||
assert B == Matrix([[S(1)/2, 0], [0, 0], [0, S(1)/2]])
|
||||
assert C == Matrix([[0, S(1)/2, 0], [S(1)/2, 0, S(1)/2], [0, S(1)/2, 0]])
|
||||
states, new_matrix = DW.canonical_form()
|
||||
assert states == [0, 4, 1, 2, 3]
|
||||
assert new_matrix == Matrix([[1, 0, 0, 0, 0],
|
||||
[0, 1, 0, 0, 0],
|
||||
[S(1)/2, 0, 0, S(1)/2, 0],
|
||||
[0, 0, S(1)/2, 0, S(1)/2],
|
||||
[0, S(1)/2, 0, S(1)/2, 0]])
|
||||
|
||||
# test regular and ergodic
|
||||
# https://web.archive.org/web/20201230182007/https://www.dartmouth.edu/~chance/teaching_aids/books_articles/probability_book/Chapter11.pdf
|
||||
T = Matrix([[0, 4, 0, 0, 0],
|
||||
[1, 0, 3, 0, 0],
|
||||
[0, 2, 0, 2, 0],
|
||||
[0, 0, 3, 0, 1],
|
||||
[0, 0, 0, 4, 0]])/4
|
||||
X = DiscreteMarkovChain('X', trans_probs=T)
|
||||
assert not X.is_regular()
|
||||
assert X.is_ergodic()
|
||||
T = Matrix([[0, 1], [1, 0]])
|
||||
X = DiscreteMarkovChain('X', trans_probs=T)
|
||||
assert not X.is_regular()
|
||||
assert X.is_ergodic()
|
||||
# http://www.math.wisc.edu/~valko/courses/331/MC2.pdf
|
||||
T = Matrix([[2, 1, 1],
|
||||
[2, 0, 2],
|
||||
[1, 1, 2]])/4
|
||||
X = DiscreteMarkovChain('X', trans_probs=T)
|
||||
assert X.is_regular()
|
||||
assert X.is_ergodic()
|
||||
# https://docs.ufpr.br/~lucambio/CE222/1S2014/Kemeny-Snell1976.pdf
|
||||
T = Matrix([[1, 1], [1, 1]])/2
|
||||
X = DiscreteMarkovChain('X', trans_probs=T)
|
||||
assert X.is_regular()
|
||||
assert X.is_ergodic()
|
||||
|
||||
# test is_absorbing_chain
|
||||
T = Matrix([[0, 1, 0],
|
||||
[1, 0, 0],
|
||||
[0, 0, 1]])
|
||||
X = DiscreteMarkovChain('X', trans_probs=T)
|
||||
assert not X.is_absorbing_chain()
|
||||
# https://en.wikipedia.org/wiki/Absorbing_Markov_chain
|
||||
T = Matrix([[1, 1, 0, 0],
|
||||
[0, 1, 1, 0],
|
||||
[1, 0, 0, 1],
|
||||
[0, 0, 0, 2]])/2
|
||||
X = DiscreteMarkovChain('X', trans_probs=T)
|
||||
assert X.is_absorbing_chain()
|
||||
T = Matrix([[2, 0, 0, 0, 0],
|
||||
[1, 0, 1, 0, 0],
|
||||
[0, 1, 0, 1, 0],
|
||||
[0, 0, 1, 0, 1],
|
||||
[0, 0, 0, 0, 2]])/2
|
||||
X = DiscreteMarkovChain('X', trans_probs=T)
|
||||
assert X.is_absorbing_chain()
|
||||
|
||||
# test custom state space
|
||||
Y10 = DiscreteMarkovChain('Y', [1, 2, 3], TO2)
|
||||
tuples = Y10.communication_classes()
|
||||
classes, recurrence, periods = list(zip(*tuples))
|
||||
assert classes == ([1], [2, 3])
|
||||
assert recurrence == (True, False)
|
||||
assert periods == (1, 1)
|
||||
assert Y10.canonical_form() == ([1, 2, 3], TO2)
|
||||
assert Y10.decompose() == ([1, 2, 3], TO2[0:1, 0:1], TO2[1:3, 0:1], TO2[1:3, 1:3])
|
||||
|
||||
# testing miscellaneous queries
|
||||
T = Matrix([[S.Half, Rational(1, 4), Rational(1, 4)],
|
||||
[Rational(1, 3), 0, Rational(2, 3)],
|
||||
[S.Half, S.Half, 0]])
|
||||
X = DiscreteMarkovChain('X', [0, 1, 2], T)
|
||||
assert P(Eq(X[1], 2) & Eq(X[2], 1) & Eq(X[3], 0),
|
||||
Eq(P(Eq(X[1], 0)), Rational(1, 4)) & Eq(P(Eq(X[1], 1)), Rational(1, 4))) == Rational(1, 12)
|
||||
assert P(Eq(X[2], 1) | Eq(X[2], 2), Eq(X[1], 1)) == Rational(2, 3)
|
||||
assert P(Eq(X[2], 1) & Eq(X[2], 2), Eq(X[1], 1)) is S.Zero
|
||||
assert P(Ne(X[2], 2), Eq(X[1], 1)) == Rational(1, 3)
|
||||
assert E(X[1]**2, Eq(X[0], 1)) == Rational(8, 3)
|
||||
assert variance(X[1], Eq(X[0], 1)) == Rational(8, 9)
|
||||
raises(ValueError, lambda: E(X[1], Eq(X[2], 1)))
|
||||
raises(ValueError, lambda: DiscreteMarkovChain('X', [0, 1], T))
|
||||
|
||||
# testing miscellaneous queries with different state space
|
||||
X = DiscreteMarkovChain('X', ['A', 'B', 'C'], T)
|
||||
assert P(Eq(X[1], 2) & Eq(X[2], 1) & Eq(X[3], 0),
|
||||
Eq(P(Eq(X[1], 0)), Rational(1, 4)) & Eq(P(Eq(X[1], 1)), Rational(1, 4))) == Rational(1, 12)
|
||||
assert P(Eq(X[2], 1) | Eq(X[2], 2), Eq(X[1], 1)) == Rational(2, 3)
|
||||
assert P(Eq(X[2], 1) & Eq(X[2], 2), Eq(X[1], 1)) is S.Zero
|
||||
assert P(Ne(X[2], 2), Eq(X[1], 1)) == Rational(1, 3)
|
||||
a = X.state_space.args[0]
|
||||
c = X.state_space.args[2]
|
||||
assert (E(X[1] ** 2, Eq(X[0], 1)) - (a**2/3 + 2*c**2/3)).simplify() == 0
|
||||
assert (variance(X[1], Eq(X[0], 1)) - (2*(-a/3 + c/3)**2/3 + (2*a/3 - 2*c/3)**2/3)).simplify() == 0
|
||||
raises(ValueError, lambda: E(X[1], Eq(X[2], 1)))
|
||||
|
||||
#testing queries with multiple RandomIndexedSymbols
|
||||
T = Matrix([[Rational(5, 10), Rational(3, 10), Rational(2, 10)], [Rational(2, 10), Rational(7, 10), Rational(1, 10)], [Rational(3, 10), Rational(3, 10), Rational(4, 10)]])
|
||||
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||||
assert P(Eq(Y[7], Y[5]), Eq(Y[2], 0)).round(5) == Float(0.44428, 5)
|
||||
assert P(Gt(Y[3], Y[1]), Eq(Y[0], 0)).round(2) == Float(0.36, 2)
|
||||
assert P(Le(Y[5], Y[10]), Eq(Y[4], 2)).round(6) == Float(0.583120, 6)
|
||||
assert Float(P(Eq(Y[10], Y[5]), Eq(Y[4], 1)), 14) == Float(1 - P(Ne(Y[10], Y[5]), Eq(Y[4], 1)), 14)
|
||||
assert Float(P(Gt(Y[8], Y[9]), Eq(Y[3], 2)), 14) == Float(1 - P(Le(Y[8], Y[9]), Eq(Y[3], 2)), 14)
|
||||
assert Float(P(Lt(Y[1], Y[4]), Eq(Y[0], 0)), 14) == Float(1 - P(Ge(Y[1], Y[4]), Eq(Y[0], 0)), 14)
|
||||
assert P(Eq(Y[5], Y[10]), Eq(Y[2], 1)) == P(Eq(Y[10], Y[5]), Eq(Y[2], 1))
|
||||
assert P(Gt(Y[1], Y[2]), Eq(Y[0], 1)) == P(Lt(Y[2], Y[1]), Eq(Y[0], 1))
|
||||
assert P(Ge(Y[7], Y[6]), Eq(Y[4], 1)) == P(Le(Y[6], Y[7]), Eq(Y[4], 1))
|
||||
|
||||
#test symbolic queries
|
||||
a, b, c, d = symbols('a b c d')
|
||||
T = Matrix([[Rational(1, 10), Rational(4, 10), Rational(5, 10)], [Rational(3, 10), Rational(4, 10), Rational(3, 10)], [Rational(7, 10), Rational(2, 10), Rational(1, 10)]])
|
||||
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||||
query = P(Eq(Y[a], b), Eq(Y[c], d))
|
||||
assert query.subs({a:10, b:2, c:5, d:1}).evalf().round(4) == P(Eq(Y[10], 2), Eq(Y[5], 1)).round(4)
|
||||
assert query.subs({a:15, b:0, c:10, d:1}).evalf().round(4) == P(Eq(Y[15], 0), Eq(Y[10], 1)).round(4)
|
||||
query_gt = P(Gt(Y[a], b), Eq(Y[c], d))
|
||||
query_le = P(Le(Y[a], b), Eq(Y[c], d))
|
||||
assert query_gt.subs({a:5, b:2, c:1, d:0}).evalf() + query_le.subs({a:5, b:2, c:1, d:0}).evalf() == 1.0
|
||||
query_ge = P(Ge(Y[a], b), Eq(Y[c], d))
|
||||
query_lt = P(Lt(Y[a], b), Eq(Y[c], d))
|
||||
assert query_ge.subs({a:4, b:1, c:0, d:2}).evalf() + query_lt.subs({a:4, b:1, c:0, d:2}).evalf() == 1.0
|
||||
|
||||
#test issue 20078
|
||||
assert (2*Y[1] + 3*Y[1]).simplify() == 5*Y[1]
|
||||
assert (2*Y[1] - 3*Y[1]).simplify() == -Y[1]
|
||||
assert (2*(0.25*Y[1])).simplify() == 0.5*Y[1]
|
||||
assert ((2*Y[1]) * (0.25*Y[1])).simplify() == 0.5*Y[1]**2
|
||||
assert (Y[1]**2 + Y[1]**3).simplify() == (Y[1] + 1)*Y[1]**2
|
||||
|
||||
def test_sample_stochastic_process():
|
||||
if not import_module('scipy'):
|
||||
skip('SciPy Not installed. Skip sampling tests')
|
||||
import random
|
||||
random.seed(0)
|
||||
numpy = import_module('numpy')
|
||||
if numpy:
|
||||
numpy.random.seed(0) # scipy uses numpy to sample so to set its seed
|
||||
T = Matrix([[0.5, 0.2, 0.3],[0.2, 0.5, 0.3],[0.2, 0.3, 0.5]])
|
||||
Y = DiscreteMarkovChain("Y", [0, 1, 2], T)
|
||||
for samps in range(10):
|
||||
assert next(sample_stochastic_process(Y)) in Y.state_space
|
||||
Z = DiscreteMarkovChain("Z", ['1', 1, 0], T)
|
||||
for samps in range(10):
|
||||
assert next(sample_stochastic_process(Z)) in Z.state_space
|
||||
|
||||
T = Matrix([[S.Half, Rational(1, 4), Rational(1, 4)],
|
||||
[Rational(1, 3), 0, Rational(2, 3)],
|
||||
[S.Half, S.Half, 0]])
|
||||
X = DiscreteMarkovChain('X', [0, 1, 2], T)
|
||||
for samps in range(10):
|
||||
assert next(sample_stochastic_process(X)) in X.state_space
|
||||
W = DiscreteMarkovChain('W', [1, pi, oo], T)
|
||||
for samps in range(10):
|
||||
assert next(sample_stochastic_process(W)) in W.state_space
|
||||
|
||||
|
||||
def test_ContinuousMarkovChain():
|
||||
T1 = Matrix([[S(-2), S(2), S.Zero],
|
||||
[S.Zero, S.NegativeOne, S.One],
|
||||
[Rational(3, 2), Rational(3, 2), S(-3)]])
|
||||
C1 = ContinuousMarkovChain('C', [0, 1, 2], T1)
|
||||
assert C1.limiting_distribution() == ImmutableMatrix([[Rational(3, 19), Rational(12, 19), Rational(4, 19)]])
|
||||
|
||||
T2 = Matrix([[-S.One, S.One, S.Zero], [S.One, -S.One, S.Zero], [S.Zero, S.One, -S.One]])
|
||||
C2 = ContinuousMarkovChain('C', [0, 1, 2], T2)
|
||||
A, t = C2.generator_matrix, symbols('t', positive=True)
|
||||
assert C2.transition_probabilities(A)(t) == Matrix([[S.Half + exp(-2*t)/2, S.Half - exp(-2*t)/2, 0],
|
||||
[S.Half - exp(-2*t)/2, S.Half + exp(-2*t)/2, 0],
|
||||
[S.Half - exp(-t) + exp(-2*t)/2, S.Half - exp(-2*t)/2, exp(-t)]])
|
||||
with ignore_warnings(UserWarning): ### TODO: Restore tests once warnings are removed
|
||||
assert P(Eq(C2(1), 1), Eq(C2(0), 1), evaluate=False) == Probability(Eq(C2(1), 1), Eq(C2(0), 1))
|
||||
assert P(Eq(C2(1), 1), Eq(C2(0), 1)) == exp(-2)/2 + S.Half
|
||||
assert P(Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 1),
|
||||
Eq(P(Eq(C2(1), 0)), S.Half)) == (Rational(1, 4) - exp(-2)/4)*(exp(-2)/2 + S.Half)
|
||||
assert P(Not(Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 2)) |
|
||||
(Eq(C2(1), 0) & Eq(C2(2), 1) & Eq(C2(3), 2)),
|
||||
Eq(P(Eq(C2(1), 0)), Rational(1, 4)) & Eq(P(Eq(C2(1), 1)), Rational(1, 4))) is S.One
|
||||
assert E(C2(Rational(3, 2)), Eq(C2(0), 2)) == -exp(-3)/2 + 2*exp(Rational(-3, 2)) + S.Half
|
||||
assert variance(C2(Rational(3, 2)), Eq(C2(0), 1)) == ((S.Half - exp(-3)/2)**2*(exp(-3)/2 + S.Half)
|
||||
+ (Rational(-1, 2) - exp(-3)/2)**2*(S.Half - exp(-3)/2))
|
||||
raises(KeyError, lambda: P(Eq(C2(1), 0), Eq(P(Eq(C2(1), 1)), S.Half)))
|
||||
assert P(Eq(C2(1), 0), Eq(P(Eq(C2(5), 1)), S.Half)) == Probability(Eq(C2(1), 0))
|
||||
TS1 = MatrixSymbol('G', 3, 3)
|
||||
CS1 = ContinuousMarkovChain('C', [0, 1, 2], TS1)
|
||||
A = CS1.generator_matrix
|
||||
assert CS1.transition_probabilities(A)(t) == exp(t*A)
|
||||
|
||||
C3 = ContinuousMarkovChain('C', [Symbol('0'), Symbol('1'), Symbol('2')], T2)
|
||||
assert P(Eq(C3(1), 1), Eq(C3(0), 1)) == exp(-2)/2 + S.Half
|
||||
assert P(Eq(C3(1), Symbol('1')), Eq(C3(0), Symbol('1'))) == exp(-2)/2 + S.Half
|
||||
|
||||
#test probability queries
|
||||
G = Matrix([[-S(1), Rational(1, 10), Rational(9, 10)], [Rational(2, 5), -S(1), Rational(3, 5)], [Rational(1, 2), Rational(1, 2), -S(1)]])
|
||||
C = ContinuousMarkovChain('C', state_space=[0, 1, 2], gen_mat=G)
|
||||
assert P(Eq(C(7.385), C(3.19)), Eq(C(0.862), 0)).round(5) == Float(0.35469, 5)
|
||||
assert P(Gt(C(98.715), C(19.807)), Eq(C(11.314), 2)).round(5) == Float(0.32452, 5)
|
||||
assert P(Le(C(5.9), C(10.112)), Eq(C(4), 1)).round(6) == Float(0.675214, 6)
|
||||
assert Float(P(Eq(C(7.32), C(2.91)), Eq(C(2.63), 1)), 14) == Float(1 - P(Ne(C(7.32), C(2.91)), Eq(C(2.63), 1)), 14)
|
||||
assert Float(P(Gt(C(3.36), C(1.101)), Eq(C(0.8), 2)), 14) == Float(1 - P(Le(C(3.36), C(1.101)), Eq(C(0.8), 2)), 14)
|
||||
assert Float(P(Lt(C(4.9), C(2.79)), Eq(C(1.61), 0)), 14) == Float(1 - P(Ge(C(4.9), C(2.79)), Eq(C(1.61), 0)), 14)
|
||||
assert P(Eq(C(5.243), C(10.912)), Eq(C(2.174), 1)) == P(Eq(C(10.912), C(5.243)), Eq(C(2.174), 1))
|
||||
assert P(Gt(C(2.344), C(9.9)), Eq(C(1.102), 1)) == P(Lt(C(9.9), C(2.344)), Eq(C(1.102), 1))
|
||||
assert P(Ge(C(7.87), C(1.008)), Eq(C(0.153), 1)) == P(Le(C(1.008), C(7.87)), Eq(C(0.153), 1))
|
||||
|
||||
#test symbolic queries
|
||||
a, b, c, d = symbols('a b c d')
|
||||
query = P(Eq(C(a), b), Eq(C(c), d))
|
||||
assert query.subs({a:3.65, b:2, c:1.78, d:1}).evalf().round(10) == P(Eq(C(3.65), 2), Eq(C(1.78), 1)).round(10)
|
||||
query_gt = P(Gt(C(a), b), Eq(C(c), d))
|
||||
query_le = P(Le(C(a), b), Eq(C(c), d))
|
||||
assert query_gt.subs({a:13.2, b:0, c:3.29, d:2}).evalf() + query_le.subs({a:13.2, b:0, c:3.29, d:2}).evalf() == 1.0
|
||||
query_ge = P(Ge(C(a), b), Eq(C(c), d))
|
||||
query_lt = P(Lt(C(a), b), Eq(C(c), d))
|
||||
assert query_ge.subs({a:7.43, b:1, c:1.45, d:0}).evalf() + query_lt.subs({a:7.43, b:1, c:1.45, d:0}).evalf() == 1.0
|
||||
|
||||
#test issue 20078
|
||||
assert (2*C(1) + 3*C(1)).simplify() == 5*C(1)
|
||||
assert (2*C(1) - 3*C(1)).simplify() == -C(1)
|
||||
assert (2*(0.25*C(1))).simplify() == 0.5*C(1)
|
||||
assert (2*C(1) * 0.25*C(1)).simplify() == 0.5*C(1)**2
|
||||
assert (C(1)**2 + C(1)**3).simplify() == (C(1) + 1)*C(1)**2
|
||||
|
||||
def test_BernoulliProcess():
|
||||
|
||||
B = BernoulliProcess("B", p=0.6, success=1, failure=0)
|
||||
assert B.state_space == FiniteSet(0, 1)
|
||||
assert B.index_set == S.Naturals0
|
||||
assert B.success == 1
|
||||
assert B.failure == 0
|
||||
|
||||
X = BernoulliProcess("X", p=Rational(1,3), success='H', failure='T')
|
||||
assert X.state_space == FiniteSet('H', 'T')
|
||||
H, T = symbols("H,T")
|
||||
assert E(X[1]+X[2]*X[3]) == H**2/9 + 4*H*T/9 + H/3 + 4*T**2/9 + 2*T/3
|
||||
|
||||
t, x = symbols('t, x', positive=True, integer=True)
|
||||
assert isinstance(B[t], RandomIndexedSymbol)
|
||||
|
||||
raises(ValueError, lambda: BernoulliProcess("X", p=1.1, success=1, failure=0))
|
||||
raises(NotImplementedError, lambda: B(t))
|
||||
|
||||
raises(IndexError, lambda: B[-3])
|
||||
assert B.joint_distribution(B[3], B[9]) == JointDistributionHandmade(Lambda((B[3], B[9]),
|
||||
Piecewise((0.6, Eq(B[3], 1)), (0.4, Eq(B[3], 0)), (0, True))
|
||||
*Piecewise((0.6, Eq(B[9], 1)), (0.4, Eq(B[9], 0)), (0, True))))
|
||||
|
||||
assert B.joint_distribution(2, B[4]) == JointDistributionHandmade(Lambda((B[2], B[4]),
|
||||
Piecewise((0.6, Eq(B[2], 1)), (0.4, Eq(B[2], 0)), (0, True))
|
||||
*Piecewise((0.6, Eq(B[4], 1)), (0.4, Eq(B[4], 0)), (0, True))))
|
||||
|
||||
# Test for the sum distribution of Bernoulli Process RVs
|
||||
Y = B[1] + B[2] + B[3]
|
||||
assert P(Eq(Y, 0)).round(2) == Float(0.06, 1)
|
||||
assert P(Eq(Y, 2)).round(2) == Float(0.43, 2)
|
||||
assert P(Eq(Y, 4)).round(2) == 0
|
||||
assert P(Gt(Y, 1)).round(2) == Float(0.65, 2)
|
||||
# Test for independency of each Random Indexed variable
|
||||
assert P(Eq(B[1], 0) & Eq(B[2], 1) & Eq(B[3], 0) & Eq(B[4], 1)).round(2) == Float(0.06, 1)
|
||||
|
||||
assert E(2 * B[1] + B[2]).round(2) == Float(1.80, 3)
|
||||
assert E(2 * B[1] + B[2] + 5).round(2) == Float(6.80, 3)
|
||||
assert E(B[2] * B[4] + B[10]).round(2) == Float(0.96, 2)
|
||||
assert E(B[2] > 0, Eq(B[1],1) & Eq(B[2],1)).round(2) == Float(0.60,2)
|
||||
assert E(B[1]) == 0.6
|
||||
assert P(B[1] > 0).round(2) == Float(0.60, 2)
|
||||
assert P(B[1] < 1).round(2) == Float(0.40, 2)
|
||||
assert P(B[1] > 0, B[2] <= 1).round(2) == Float(0.60, 2)
|
||||
assert P(B[12] * B[5] > 0).round(2) == Float(0.36, 2)
|
||||
assert P(B[12] * B[5] > 0, B[4] < 1).round(2) == Float(0.36, 2)
|
||||
assert P(Eq(B[2], 1), B[2] > 0) == 1.0
|
||||
assert P(Eq(B[5], 3)) == 0
|
||||
assert P(Eq(B[1], 1), B[1] < 0) == 0
|
||||
assert P(B[2] > 0, Eq(B[2], 1)) == 1
|
||||
assert P(B[2] < 0, Eq(B[2], 1)) == 0
|
||||
assert P(B[2] > 0, B[2]==7) == 0
|
||||
assert P(B[5] > 0, B[5]) == BernoulliDistribution(0.6, 0, 1)
|
||||
raises(ValueError, lambda: P(3))
|
||||
raises(ValueError, lambda: P(B[3] > 0, 3))
|
||||
|
||||
# test issue 19456
|
||||
expr = Sum(B[t], (t, 0, 4))
|
||||
expr2 = Sum(B[t], (t, 1, 3))
|
||||
expr3 = Sum(B[t]**2, (t, 1, 3))
|
||||
assert expr.doit() == B[0] + B[1] + B[2] + B[3] + B[4]
|
||||
assert expr2.doit() == Y
|
||||
assert expr3.doit() == B[1]**2 + B[2]**2 + B[3]**2
|
||||
assert B[2*t].free_symbols == {B[2*t], t}
|
||||
assert B[4].free_symbols == {B[4]}
|
||||
assert B[x*t].free_symbols == {B[x*t], x, t}
|
||||
|
||||
#test issue 20078
|
||||
assert (2*B[t] + 3*B[t]).simplify() == 5*B[t]
|
||||
assert (2*B[t] - 3*B[t]).simplify() == -B[t]
|
||||
assert (2*(0.25*B[t])).simplify() == 0.5*B[t]
|
||||
assert (2*B[t] * 0.25*B[t]).simplify() == 0.5*B[t]**2
|
||||
assert (B[t]**2 + B[t]**3).simplify() == (B[t] + 1)*B[t]**2
|
||||
|
||||
def test_PoissonProcess():
|
||||
X = PoissonProcess("X", 3)
|
||||
assert X.state_space == S.Naturals0
|
||||
assert X.index_set == Interval(0, oo)
|
||||
assert X.lamda == 3
|
||||
|
||||
t, d, x, y = symbols('t d x y', positive=True)
|
||||
assert isinstance(X(t), RandomIndexedSymbol)
|
||||
assert X.distribution(t) == PoissonDistribution(3*t)
|
||||
with warns_deprecated_sympy():
|
||||
X.distribution(X(t))
|
||||
raises(ValueError, lambda: PoissonProcess("X", -1))
|
||||
raises(NotImplementedError, lambda: X[t])
|
||||
raises(IndexError, lambda: X(-5))
|
||||
|
||||
assert X.joint_distribution(X(2), X(3)) == JointDistributionHandmade(Lambda((X(2), X(3)),
|
||||
6**X(2)*9**X(3)*exp(-15)/(factorial(X(2))*factorial(X(3)))))
|
||||
|
||||
assert X.joint_distribution(4, 6) == JointDistributionHandmade(Lambda((X(4), X(6)),
|
||||
12**X(4)*18**X(6)*exp(-30)/(factorial(X(4))*factorial(X(6)))))
|
||||
|
||||
assert P(X(t) < 1) == exp(-3*t)
|
||||
assert P(Eq(X(t), 0), Contains(t, Interval.Lopen(3, 5))) == exp(-6) # exp(-2*lamda)
|
||||
res = P(Eq(X(t), 1), Contains(t, Interval.Lopen(3, 4)))
|
||||
assert res == 3*exp(-3)
|
||||
|
||||
# Equivalent to P(Eq(X(t), 1))**4 because of non-overlapping intervals
|
||||
assert P(Eq(X(t), 1) & Eq(X(d), 1) & Eq(X(x), 1) & Eq(X(y), 1), Contains(t, Interval.Lopen(0, 1))
|
||||
& Contains(d, Interval.Lopen(1, 2)) & Contains(x, Interval.Lopen(2, 3))
|
||||
& Contains(y, Interval.Lopen(3, 4))) == res**4
|
||||
|
||||
# Return Probability because of overlapping intervals
|
||||
assert P(Eq(X(t), 2) & Eq(X(d), 3), Contains(t, Interval.Lopen(0, 2))
|
||||
& Contains(d, Interval.Ropen(2, 4))) == \
|
||||
Probability(Eq(X(d), 3) & Eq(X(t), 2), Contains(t, Interval.Lopen(0, 2))
|
||||
& Contains(d, Interval.Ropen(2, 4)))
|
||||
|
||||
raises(ValueError, lambda: P(Eq(X(t), 2) & Eq(X(d), 3),
|
||||
Contains(t, Interval.Lopen(0, 4)) & Contains(d, Interval.Lopen(3, oo)))) # no bound on d
|
||||
assert P(Eq(X(3), 2)) == 81*exp(-9)/2
|
||||
assert P(Eq(X(t), 2), Contains(t, Interval.Lopen(0, 5))) == 225*exp(-15)/2
|
||||
|
||||
# Check that probability works correctly by adding it to 1
|
||||
res1 = P(X(t) <= 3, Contains(t, Interval.Lopen(0, 5)))
|
||||
res2 = P(X(t) > 3, Contains(t, Interval.Lopen(0, 5)))
|
||||
assert res1 == 691*exp(-15)
|
||||
assert (res1 + res2).simplify() == 1
|
||||
|
||||
# Check Not and Or
|
||||
assert P(Not(Eq(X(t), 2) & (X(d) > 3)), Contains(t, Interval.Ropen(2, 4)) & \
|
||||
Contains(d, Interval.Lopen(7, 8))).simplify() == -18*exp(-6) + 234*exp(-9) + 1
|
||||
assert P(Eq(X(t), 2) | Ne(X(t), 4), Contains(t, Interval.Ropen(2, 4))) == 1 - 36*exp(-6)
|
||||
raises(ValueError, lambda: P(X(t) > 2, X(t) + X(d)))
|
||||
assert E(X(t)) == 3*t # property of the distribution at a given timestamp
|
||||
assert E(X(t)**2 + X(d)*2 + X(y)**3, Contains(t, Interval.Lopen(0, 1))
|
||||
& Contains(d, Interval.Lopen(1, 2)) & Contains(y, Interval.Ropen(3, 4))) == 75
|
||||
assert E(X(t)**2, Contains(t, Interval.Lopen(0, 1))) == 12
|
||||
assert E(x*(X(t) + X(d))*(X(t)**2+X(d)**2), Contains(t, Interval.Lopen(0, 1))
|
||||
& Contains(d, Interval.Ropen(1, 2))) == \
|
||||
Expectation(x*(X(d) + X(t))*(X(d)**2 + X(t)**2), Contains(t, Interval.Lopen(0, 1))
|
||||
& Contains(d, Interval.Ropen(1, 2)))
|
||||
|
||||
# Value Error because of infinite time bound
|
||||
raises(ValueError, lambda: E(X(t)**3, Contains(t, Interval.Lopen(1, oo))))
|
||||
|
||||
# Equivalent to E(X(t)**2) - E(X(d)**2) == E(X(1)**2) - E(X(1)**2) == 0
|
||||
assert E((X(t) + X(d))*(X(t) - X(d)), Contains(t, Interval.Lopen(0, 1))
|
||||
& Contains(d, Interval.Lopen(1, 2))) == 0
|
||||
assert E(X(2) + x*E(X(5))) == 15*x + 6
|
||||
assert E(x*X(1) + y) == 3*x + y
|
||||
assert P(Eq(X(1), 2) & Eq(X(t), 3), Contains(t, Interval.Lopen(1, 2))) == 81*exp(-6)/4
|
||||
Y = PoissonProcess("Y", 6)
|
||||
Z = X + Y
|
||||
assert Z.lamda == X.lamda + Y.lamda == 9
|
||||
raises(ValueError, lambda: X + 5) # should be added be only PoissonProcess instance
|
||||
N, M = Z.split(4, 5)
|
||||
assert N.lamda == 4
|
||||
assert M.lamda == 5
|
||||
raises(ValueError, lambda: Z.split(3, 2)) # 2+3 != 9
|
||||
|
||||
raises(ValueError, lambda :P(Eq(X(t), 0), Contains(t, Interval.Lopen(1, 3)) & Eq(X(1), 0)))
|
||||
# check if it handles queries with two random variables in one args
|
||||
res1 = P(Eq(N(3), N(5)))
|
||||
assert res1 == P(Eq(N(t), 0), Contains(t, Interval(3, 5)))
|
||||
res2 = P(N(3) > N(1))
|
||||
assert res2 == P((N(t) > 0), Contains(t, Interval(1, 3)))
|
||||
assert P(N(3) < N(1)) == 0 # condition is not possible
|
||||
res3 = P(N(3) <= N(1)) # holds only for Eq(N(3), N(1))
|
||||
assert res3 == P(Eq(N(t), 0), Contains(t, Interval(1, 3)))
|
||||
|
||||
# tests from https://www.probabilitycourse.com/chapter11/11_1_2_basic_concepts_of_the_poisson_process.php
|
||||
X = PoissonProcess('X', 10) # 11.1
|
||||
assert P(Eq(X(S(1)/3), 3) & Eq(X(1), 10)) == exp(-10)*Rational(8000000000, 11160261)
|
||||
assert P(Eq(X(1), 1), Eq(X(S(1)/3), 3)) == 0
|
||||
assert P(Eq(X(1), 10), Eq(X(S(1)/3), 3)) == P(Eq(X(S(2)/3), 7))
|
||||
|
||||
X = PoissonProcess('X', 2) # 11.2
|
||||
assert P(X(S(1)/2) < 1) == exp(-1)
|
||||
assert P(X(3) < 1, Eq(X(1), 0)) == exp(-4)
|
||||
assert P(Eq(X(4), 3), Eq(X(2), 3)) == exp(-4)
|
||||
|
||||
X = PoissonProcess('X', 3)
|
||||
assert P(Eq(X(2), 5) & Eq(X(1), 2)) == Rational(81, 4)*exp(-6)
|
||||
|
||||
# check few properties
|
||||
assert P(X(2) <= 3, X(1)>=1) == 3*P(Eq(X(1), 0)) + 2*P(Eq(X(1), 1)) + P(Eq(X(1), 2))
|
||||
assert P(X(2) <= 3, X(1) > 1) == 2*P(Eq(X(1), 0)) + 1*P(Eq(X(1), 1))
|
||||
assert P(Eq(X(2), 5) & Eq(X(1), 2)) == P(Eq(X(1), 3))*P(Eq(X(1), 2))
|
||||
assert P(Eq(X(3), 4), Eq(X(1), 3)) == P(Eq(X(2), 1))
|
||||
|
||||
#test issue 20078
|
||||
assert (2*X(t) + 3*X(t)).simplify() == 5*X(t)
|
||||
assert (2*X(t) - 3*X(t)).simplify() == -X(t)
|
||||
assert (2*(0.25*X(t))).simplify() == 0.5*X(t)
|
||||
assert (2*X(t) * 0.25*X(t)).simplify() == 0.5*X(t)**2
|
||||
assert (X(t)**2 + X(t)**3).simplify() == (X(t) + 1)*X(t)**2
|
||||
|
||||
def test_WienerProcess():
|
||||
X = WienerProcess("X")
|
||||
assert X.state_space == S.Reals
|
||||
assert X.index_set == Interval(0, oo)
|
||||
|
||||
t, d, x, y = symbols('t d x y', positive=True)
|
||||
assert isinstance(X(t), RandomIndexedSymbol)
|
||||
assert X.distribution(t) == NormalDistribution(0, sqrt(t))
|
||||
with warns_deprecated_sympy():
|
||||
X.distribution(X(t))
|
||||
raises(ValueError, lambda: PoissonProcess("X", -1))
|
||||
raises(NotImplementedError, lambda: X[t])
|
||||
raises(IndexError, lambda: X(-2))
|
||||
|
||||
assert X.joint_distribution(X(2), X(3)) == JointDistributionHandmade(
|
||||
Lambda((X(2), X(3)), sqrt(6)*exp(-X(2)**2/4)*exp(-X(3)**2/6)/(12*pi)))
|
||||
assert X.joint_distribution(4, 6) == JointDistributionHandmade(
|
||||
Lambda((X(4), X(6)), sqrt(6)*exp(-X(4)**2/8)*exp(-X(6)**2/12)/(24*pi)))
|
||||
|
||||
assert P(X(t) < 3).simplify() == erf(3*sqrt(2)/(2*sqrt(t)))/2 + S(1)/2
|
||||
assert P(X(t) > 2, Contains(t, Interval.Lopen(3, 7))).simplify() == S(1)/2 -\
|
||||
erf(sqrt(2)/2)/2
|
||||
|
||||
# Equivalent to P(X(1)>1)**4
|
||||
assert P((X(t) > 4) & (X(d) > 3) & (X(x) > 2) & (X(y) > 1),
|
||||
Contains(t, Interval.Lopen(0, 1)) & Contains(d, Interval.Lopen(1, 2))
|
||||
& Contains(x, Interval.Lopen(2, 3)) & Contains(y, Interval.Lopen(3, 4))).simplify() ==\
|
||||
(1 - erf(sqrt(2)/2))*(1 - erf(sqrt(2)))*(1 - erf(3*sqrt(2)/2))*(1 - erf(2*sqrt(2)))/16
|
||||
|
||||
# Contains an overlapping interval so, return Probability
|
||||
assert P((X(t)< 2) & (X(d)> 3), Contains(t, Interval.Lopen(0, 2))
|
||||
& Contains(d, Interval.Ropen(2, 4))) == Probability((X(d) > 3) & (X(t) < 2),
|
||||
Contains(d, Interval.Ropen(2, 4)) & Contains(t, Interval.Lopen(0, 2)))
|
||||
|
||||
assert str(P(Not((X(t) < 5) & (X(d) > 3)), Contains(t, Interval.Ropen(2, 4)) &
|
||||
Contains(d, Interval.Lopen(7, 8))).simplify()) == \
|
||||
'-(1 - erf(3*sqrt(2)/2))*(2 - erfc(5/2))/4 + 1'
|
||||
# Distribution has mean 0 at each timestamp
|
||||
assert E(X(t)) == 0
|
||||
assert E(x*(X(t) + X(d))*(X(t)**2+X(d)**2), Contains(t, Interval.Lopen(0, 1))
|
||||
& Contains(d, Interval.Ropen(1, 2))) == Expectation(x*(X(d) + X(t))*(X(d)**2 + X(t)**2),
|
||||
Contains(d, Interval.Ropen(1, 2)) & Contains(t, Interval.Lopen(0, 1)))
|
||||
assert E(X(t) + x*E(X(3))) == 0
|
||||
|
||||
#test issue 20078
|
||||
assert (2*X(t) + 3*X(t)).simplify() == 5*X(t)
|
||||
assert (2*X(t) - 3*X(t)).simplify() == -X(t)
|
||||
assert (2*(0.25*X(t))).simplify() == 0.5*X(t)
|
||||
assert (2*X(t) * 0.25*X(t)).simplify() == 0.5*X(t)**2
|
||||
assert (X(t)**2 + X(t)**3).simplify() == (X(t) + 1)*X(t)**2
|
||||
|
||||
|
||||
def test_GammaProcess_symbolic():
|
||||
t, d, x, y, g, l = symbols('t d x y g l', positive=True)
|
||||
X = GammaProcess("X", l, g)
|
||||
|
||||
raises(NotImplementedError, lambda: X[t])
|
||||
raises(IndexError, lambda: X(-1))
|
||||
assert isinstance(X(t), RandomIndexedSymbol)
|
||||
assert X.state_space == Interval(0, oo)
|
||||
assert X.distribution(t) == GammaDistribution(g*t, 1/l)
|
||||
with warns_deprecated_sympy():
|
||||
X.distribution(X(t))
|
||||
assert X.joint_distribution(5, X(3)) == JointDistributionHandmade(Lambda(
|
||||
(X(5), X(3)), l**(8*g)*exp(-l*X(3))*exp(-l*X(5))*X(3)**(3*g - 1)*X(5)**(5*g
|
||||
- 1)/(gamma(3*g)*gamma(5*g))))
|
||||
# property of the gamma process at any given timestamp
|
||||
assert E(X(t)) == g*t/l
|
||||
assert variance(X(t)).simplify() == g*t/l**2
|
||||
|
||||
# Equivalent to E(2*X(1)) + E(X(1)**2) + E(X(1)**3), where E(X(1)) == g/l
|
||||
assert E(X(t)**2 + X(d)*2 + X(y)**3, Contains(t, Interval.Lopen(0, 1))
|
||||
& Contains(d, Interval.Lopen(1, 2)) & Contains(y, Interval.Ropen(3, 4))) == \
|
||||
2*g/l + (g**2 + g)/l**2 + (g**3 + 3*g**2 + 2*g)/l**3
|
||||
|
||||
assert P(X(t) > 3, Contains(t, Interval.Lopen(3, 4))).simplify() == \
|
||||
1 - lowergamma(g, 3*l)/gamma(g) # equivalent to P(X(1)>3)
|
||||
|
||||
|
||||
#test issue 20078
|
||||
assert (2*X(t) + 3*X(t)).simplify() == 5*X(t)
|
||||
assert (2*X(t) - 3*X(t)).simplify() == -X(t)
|
||||
assert (2*(0.25*X(t))).simplify() == 0.5*X(t)
|
||||
assert (2*X(t) * 0.25*X(t)).simplify() == 0.5*X(t)**2
|
||||
assert (X(t)**2 + X(t)**3).simplify() == (X(t) + 1)*X(t)**2
|
||||
def test_GammaProcess_numeric():
|
||||
t, d, x, y = symbols('t d x y', positive=True)
|
||||
X = GammaProcess("X", 1, 2)
|
||||
assert X.state_space == Interval(0, oo)
|
||||
assert X.index_set == Interval(0, oo)
|
||||
assert X.lamda == 1
|
||||
assert X.gamma == 2
|
||||
|
||||
raises(ValueError, lambda: GammaProcess("X", -1, 2))
|
||||
raises(ValueError, lambda: GammaProcess("X", 0, -2))
|
||||
raises(ValueError, lambda: GammaProcess("X", -1, -2))
|
||||
|
||||
# all are independent because of non-overlapping intervals
|
||||
assert P((X(t) > 4) & (X(d) > 3) & (X(x) > 2) & (X(y) > 1), Contains(t,
|
||||
Interval.Lopen(0, 1)) & Contains(d, Interval.Lopen(1, 2)) & Contains(x,
|
||||
Interval.Lopen(2, 3)) & Contains(y, Interval.Lopen(3, 4))).simplify() == \
|
||||
120*exp(-10)
|
||||
|
||||
# Check working with Not and Or
|
||||
assert P(Not((X(t) < 5) & (X(d) > 3)), Contains(t, Interval.Ropen(2, 4)) &
|
||||
Contains(d, Interval.Lopen(7, 8))).simplify() == -4*exp(-3) + 472*exp(-8)/3 + 1
|
||||
assert P((X(t) > 2) | (X(t) < 4), Contains(t, Interval.Ropen(1, 4))).simplify() == \
|
||||
-643*exp(-4)/15 + 109*exp(-2)/15 + 1
|
||||
|
||||
assert E(X(t)) == 2*t # E(X(t)) == gamma*t/l
|
||||
assert E(X(2) + x*E(X(5))) == 10*x + 4
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
from sympy.stats import Expectation, Normal, Variance, Covariance
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.matrices.exceptions import ShapeError
|
||||
from sympy.matrices.dense import Matrix
|
||||
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
||||
from sympy.matrices.expressions.special import ZeroMatrix
|
||||
from sympy.stats.rv import RandomMatrixSymbol
|
||||
from sympy.stats.symbolic_multivariate_probability import (ExpectationMatrix,
|
||||
VarianceMatrix, CrossCovarianceMatrix)
|
||||
|
||||
j, k = symbols("j,k")
|
||||
|
||||
A = MatrixSymbol("A", k, k)
|
||||
B = MatrixSymbol("B", k, k)
|
||||
C = MatrixSymbol("C", k, k)
|
||||
D = MatrixSymbol("D", k, k)
|
||||
|
||||
a = MatrixSymbol("a", k, 1)
|
||||
b = MatrixSymbol("b", k, 1)
|
||||
|
||||
A2 = MatrixSymbol("A2", 2, 2)
|
||||
B2 = MatrixSymbol("B2", 2, 2)
|
||||
|
||||
X = RandomMatrixSymbol("X", k, 1)
|
||||
Y = RandomMatrixSymbol("Y", k, 1)
|
||||
Z = RandomMatrixSymbol("Z", k, 1)
|
||||
W = RandomMatrixSymbol("W", k, 1)
|
||||
|
||||
R = RandomMatrixSymbol("R", k, k)
|
||||
|
||||
X2 = RandomMatrixSymbol("X2", 2, 1)
|
||||
|
||||
normal = Normal("normal", 0, 1)
|
||||
|
||||
m1 = Matrix([
|
||||
[1, j*Normal("normal2", 2, 1)],
|
||||
[normal, 0]
|
||||
])
|
||||
|
||||
def test_multivariate_expectation():
|
||||
expr = Expectation(a)
|
||||
assert expr == Expectation(a) == ExpectationMatrix(a)
|
||||
assert expr.expand() == a
|
||||
|
||||
expr = Expectation(X)
|
||||
assert expr == Expectation(X) == ExpectationMatrix(X)
|
||||
assert expr.shape == (k, 1)
|
||||
assert expr.rows == k
|
||||
assert expr.cols == 1
|
||||
assert isinstance(expr, ExpectationMatrix)
|
||||
|
||||
expr = Expectation(A*X + b)
|
||||
assert expr == ExpectationMatrix(A*X + b)
|
||||
assert expr.expand() == A*ExpectationMatrix(X) + b
|
||||
assert isinstance(expr, ExpectationMatrix)
|
||||
assert expr.shape == (k, 1)
|
||||
|
||||
expr = Expectation(m1*X2)
|
||||
assert expr.expand() == expr
|
||||
|
||||
expr = Expectation(A2*m1*B2*X2)
|
||||
assert expr.args[0].args == (A2, m1, B2, X2)
|
||||
assert expr.expand() == A2*ExpectationMatrix(m1*B2*X2)
|
||||
|
||||
expr = Expectation((X + Y)*(X - Y).T)
|
||||
assert expr.expand() == ExpectationMatrix(X*X.T) - ExpectationMatrix(X*Y.T) +\
|
||||
ExpectationMatrix(Y*X.T) - ExpectationMatrix(Y*Y.T)
|
||||
|
||||
expr = Expectation(A*X + B*Y)
|
||||
assert expr.expand() == A*ExpectationMatrix(X) + B*ExpectationMatrix(Y)
|
||||
|
||||
assert Expectation(m1).doit() == Matrix([[1, 2*j], [0, 0]])
|
||||
|
||||
x1 = Matrix([
|
||||
[Normal('N11', 11, 1), Normal('N12', 12, 1)],
|
||||
[Normal('N21', 21, 1), Normal('N22', 22, 1)]
|
||||
])
|
||||
x2 = Matrix([
|
||||
[Normal('M11', 1, 1), Normal('M12', 2, 1)],
|
||||
[Normal('M21', 3, 1), Normal('M22', 4, 1)]
|
||||
])
|
||||
|
||||
assert Expectation(Expectation(x1 + x2)).doit(deep=False) == ExpectationMatrix(x1 + x2)
|
||||
assert Expectation(Expectation(x1 + x2)).doit() == Matrix([[12, 14], [24, 26]])
|
||||
|
||||
|
||||
def test_multivariate_variance():
|
||||
raises(ShapeError, lambda: Variance(A))
|
||||
|
||||
expr = Variance(a)
|
||||
assert expr == Variance(a) == VarianceMatrix(a)
|
||||
assert expr.expand() == ZeroMatrix(k, k)
|
||||
expr = Variance(a.T)
|
||||
assert expr == Variance(a.T) == VarianceMatrix(a.T)
|
||||
assert expr.expand() == ZeroMatrix(k, k)
|
||||
|
||||
expr = Variance(X)
|
||||
assert expr == Variance(X) == VarianceMatrix(X)
|
||||
assert expr.shape == (k, k)
|
||||
assert expr.rows == k
|
||||
assert expr.cols == k
|
||||
assert isinstance(expr, VarianceMatrix)
|
||||
|
||||
expr = Variance(A*X)
|
||||
assert expr == VarianceMatrix(A*X)
|
||||
assert expr.expand() == A*VarianceMatrix(X)*A.T
|
||||
assert isinstance(expr, VarianceMatrix)
|
||||
assert expr.shape == (k, k)
|
||||
|
||||
expr = Variance(A*B*X)
|
||||
assert expr.expand() == A*B*VarianceMatrix(X)*B.T*A.T
|
||||
|
||||
expr = Variance(m1*X2)
|
||||
assert expr.expand() == expr
|
||||
|
||||
expr = Variance(A2*m1*B2*X2)
|
||||
assert expr.args[0].args == (A2, m1, B2, X2)
|
||||
assert expr.expand() == expr
|
||||
|
||||
expr = Variance(A*X + B*Y)
|
||||
assert expr.expand() == 2*A*CrossCovarianceMatrix(X, Y)*B.T +\
|
||||
A*VarianceMatrix(X)*A.T + B*VarianceMatrix(Y)*B.T
|
||||
|
||||
def test_multivariate_crosscovariance():
|
||||
raises(ShapeError, lambda: Covariance(X, Y.T))
|
||||
raises(ShapeError, lambda: Covariance(X, A))
|
||||
|
||||
|
||||
expr = Covariance(a.T, b.T)
|
||||
assert expr.shape == (1, 1)
|
||||
assert expr.expand() == ZeroMatrix(1, 1)
|
||||
|
||||
expr = Covariance(a, b)
|
||||
assert expr == Covariance(a, b) == CrossCovarianceMatrix(a, b)
|
||||
assert expr.expand() == ZeroMatrix(k, k)
|
||||
assert expr.shape == (k, k)
|
||||
assert expr.rows == k
|
||||
assert expr.cols == k
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
|
||||
expr = Covariance(A*X + a, b)
|
||||
assert expr.expand() == ZeroMatrix(k, k)
|
||||
|
||||
expr = Covariance(X, Y)
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
assert expr.expand() == expr
|
||||
|
||||
expr = Covariance(X, X)
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
assert expr.expand() == VarianceMatrix(X)
|
||||
|
||||
expr = Covariance(X + Y, Z)
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
assert expr.expand() == CrossCovarianceMatrix(X, Z) + CrossCovarianceMatrix(Y, Z)
|
||||
|
||||
expr = Covariance(A*X, Y)
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
assert expr.expand() == A*CrossCovarianceMatrix(X, Y)
|
||||
|
||||
expr = Covariance(X, B*Y)
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
assert expr.expand() == CrossCovarianceMatrix(X, Y)*B.T
|
||||
|
||||
expr = Covariance(A*X + a, B.T*Y + b)
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
assert expr.expand() == A*CrossCovarianceMatrix(X, Y)*B
|
||||
|
||||
expr = Covariance(A*X + B*Y + a, C.T*Z + D.T*W + b)
|
||||
assert isinstance(expr, CrossCovarianceMatrix)
|
||||
assert expr.expand() == A*CrossCovarianceMatrix(X, W)*D + A*CrossCovarianceMatrix(X, Z)*C \
|
||||
+ B*CrossCovarianceMatrix(Y, W)*D + B*CrossCovarianceMatrix(Y, Z)*C
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
from sympy.concrete.summations import Sum
|
||||
from sympy.core.mul import Mul
|
||||
from sympy.core.numbers import (oo, pi)
|
||||
from sympy.core.relational import Eq
|
||||
from sympy.core.symbol import (Dummy, symbols)
|
||||
from sympy.functions.elementary.exponential import exp
|
||||
from sympy.functions.elementary.miscellaneous import sqrt
|
||||
from sympy.functions.elementary.trigonometric import sin
|
||||
from sympy.integrals.integrals import Integral
|
||||
from sympy.core.expr import unchanged
|
||||
from sympy.stats import (Normal, Poisson, variance, Covariance, Variance,
|
||||
Probability, Expectation, Moment, CentralMoment)
|
||||
from sympy.stats.rv import probability, expectation
|
||||
|
||||
|
||||
def test_literal_probability():
|
||||
X = Normal('X', 2, 3)
|
||||
Y = Normal('Y', 3, 4)
|
||||
Z = Poisson('Z', 4)
|
||||
W = Poisson('W', 3)
|
||||
x = symbols('x', real=True)
|
||||
y, w, z = symbols('y, w, z')
|
||||
|
||||
assert Probability(X > 0).evaluate_integral() == probability(X > 0)
|
||||
assert Probability(X > x).evaluate_integral() == probability(X > x)
|
||||
assert Probability(X > 0).rewrite(Integral).doit() == probability(X > 0)
|
||||
assert Probability(X > x).rewrite(Integral).doit() == probability(X > x)
|
||||
|
||||
assert Expectation(X).evaluate_integral() == expectation(X)
|
||||
assert Expectation(X).rewrite(Integral).doit() == expectation(X)
|
||||
assert Expectation(X**2).evaluate_integral() == expectation(X**2)
|
||||
assert Expectation(x*X).args == (x*X,)
|
||||
assert Expectation(x*X).expand() == x*Expectation(X)
|
||||
assert Expectation(2*X + 3*Y + z*X*Y).expand() == 2*Expectation(X) + 3*Expectation(Y) + z*Expectation(X*Y)
|
||||
assert Expectation(2*X + 3*Y + z*X*Y).args == (2*X + 3*Y + z*X*Y,)
|
||||
assert Expectation(sin(X)) == Expectation(sin(X)).expand()
|
||||
assert Expectation(2*x*sin(X)*Y + y*X**2 + z*X*Y).expand() == 2*x*Expectation(sin(X)*Y) \
|
||||
+ y*Expectation(X**2) + z*Expectation(X*Y)
|
||||
assert Expectation(X + Y).expand() == Expectation(X) + Expectation(Y)
|
||||
assert Expectation((X + Y)*(X - Y)).expand() == Expectation(X**2) - Expectation(Y**2)
|
||||
assert Expectation((X + Y)*(X - Y)).expand().doit() == -12
|
||||
assert Expectation(X + Y, evaluate=True).doit() == 5
|
||||
assert Expectation(X + Expectation(Y)).doit() == 5
|
||||
assert Expectation(X + Expectation(Y)).doit(deep=False) == 2 + Expectation(Expectation(Y))
|
||||
assert Expectation(X + Expectation(Y + Expectation(2*X))).doit(deep=False) == 2 \
|
||||
+ Expectation(Expectation(Y + Expectation(2*X)))
|
||||
assert Expectation(X + Expectation(Y + Expectation(2*X))).doit() == 9
|
||||
assert Expectation(Expectation(2*X)).doit() == 4
|
||||
assert Expectation(Expectation(2*X)).doit(deep=False) == Expectation(2*X)
|
||||
assert Expectation(4*Expectation(2*X)).doit(deep=False) == 4*Expectation(2*X)
|
||||
assert Expectation((X + Y)**3).expand() == 3*Expectation(X*Y**2) +\
|
||||
3*Expectation(X**2*Y) + Expectation(X**3) + Expectation(Y**3)
|
||||
assert Expectation((X - Y)**3).expand() == 3*Expectation(X*Y**2) -\
|
||||
3*Expectation(X**2*Y) + Expectation(X**3) - Expectation(Y**3)
|
||||
assert Expectation((X - Y)**2).expand() == -2*Expectation(X*Y) +\
|
||||
Expectation(X**2) + Expectation(Y**2)
|
||||
|
||||
assert Variance(w).args == (w,)
|
||||
assert Variance(w).expand() == 0
|
||||
assert Variance(X).evaluate_integral() == Variance(X).rewrite(Integral).doit() == variance(X)
|
||||
assert Variance(X + z).args == (X + z,)
|
||||
assert Variance(X + z).expand() == Variance(X)
|
||||
assert Variance(X*Y).args == (Mul(X, Y),)
|
||||
assert type(Variance(X*Y)) == Variance
|
||||
assert Variance(z*X).expand() == z**2*Variance(X)
|
||||
assert Variance(X + Y).expand() == Variance(X) + Variance(Y) + 2*Covariance(X, Y)
|
||||
assert Variance(X + Y + Z + W).expand() == (Variance(X) + Variance(Y) + Variance(Z) + Variance(W) +
|
||||
2 * Covariance(X, Y) + 2 * Covariance(X, Z) + 2 * Covariance(X, W) +
|
||||
2 * Covariance(Y, Z) + 2 * Covariance(Y, W) + 2 * Covariance(W, Z))
|
||||
assert Variance(X**2).evaluate_integral() == variance(X**2)
|
||||
assert unchanged(Variance, X**2)
|
||||
assert Variance(x*X**2).expand() == x**2*Variance(X**2)
|
||||
assert Variance(sin(X)).args == (sin(X),)
|
||||
assert Variance(sin(X)).expand() == Variance(sin(X))
|
||||
assert Variance(x*sin(X)).expand() == x**2*Variance(sin(X))
|
||||
|
||||
assert Covariance(w, z).args == (w, z)
|
||||
assert Covariance(w, z).expand() == 0
|
||||
assert Covariance(X, w).expand() == 0
|
||||
assert Covariance(w, X).expand() == 0
|
||||
assert Covariance(X, Y).args == (X, Y)
|
||||
assert type(Covariance(X, Y)) == Covariance
|
||||
assert Covariance(z*X + 3, Y).expand() == z*Covariance(X, Y)
|
||||
assert Covariance(X, X).args == (X, X)
|
||||
assert Covariance(X, X).expand() == Variance(X)
|
||||
assert Covariance(z*X + 3, w*Y + 4).expand() == w*z*Covariance(X,Y)
|
||||
assert Covariance(X, Y) == Covariance(Y, X)
|
||||
assert Covariance(X + Y, Z + W).expand() == Covariance(W, X) + Covariance(W, Y) + Covariance(X, Z) + Covariance(Y, Z)
|
||||
assert Covariance(x*X + y*Y, z*Z + w*W).expand() == (x*w*Covariance(W, X) + w*y*Covariance(W, Y) +
|
||||
x*z*Covariance(X, Z) + y*z*Covariance(Y, Z))
|
||||
assert Covariance(x*X**2 + y*sin(Y), z*Y*Z**2 + w*W).expand() == (w*x*Covariance(W, X**2) + w*y*Covariance(sin(Y), W) +
|
||||
x*z*Covariance(Y*Z**2, X**2) + y*z*Covariance(Y*Z**2, sin(Y)))
|
||||
assert Covariance(X, X**2).expand() == Covariance(X, X**2)
|
||||
assert Covariance(X, sin(X)).expand() == Covariance(sin(X), X)
|
||||
assert Covariance(X**2, sin(X)*Y).expand() == Covariance(sin(X)*Y, X**2)
|
||||
assert Covariance(w, X).evaluate_integral() == 0
|
||||
|
||||
|
||||
def test_probability_rewrite():
|
||||
X = Normal('X', 2, 3)
|
||||
Y = Normal('Y', 3, 4)
|
||||
Z = Poisson('Z', 4)
|
||||
W = Poisson('W', 3)
|
||||
x, y, w, z = symbols('x, y, w, z')
|
||||
|
||||
assert Variance(w).rewrite(Expectation) == 0
|
||||
assert Variance(X).rewrite(Expectation) == Expectation(X ** 2) - Expectation(X) ** 2
|
||||
assert Variance(X, condition=Y).rewrite(Expectation) == Expectation(X ** 2, Y) - Expectation(X, Y) ** 2
|
||||
assert Variance(X, Y) != Expectation(X**2) - Expectation(X)**2
|
||||
assert Variance(X + z).rewrite(Expectation) == Expectation((X + z) ** 2) - Expectation(X + z) ** 2
|
||||
assert Variance(X * Y).rewrite(Expectation) == Expectation(X ** 2 * Y ** 2) - Expectation(X * Y) ** 2
|
||||
|
||||
assert Covariance(w, X).rewrite(Expectation) == -w*Expectation(X) + Expectation(w*X)
|
||||
assert Covariance(X, Y).rewrite(Expectation) == Expectation(X*Y) - Expectation(X)*Expectation(Y)
|
||||
assert Covariance(X, Y, condition=W).rewrite(Expectation) == Expectation(X * Y, W) - Expectation(X, W) * Expectation(Y, W)
|
||||
|
||||
w, x, z = symbols("W, x, z")
|
||||
px = Probability(Eq(X, x))
|
||||
pz = Probability(Eq(Z, z))
|
||||
|
||||
assert Expectation(X).rewrite(Probability) == Integral(x*px, (x, -oo, oo))
|
||||
assert Expectation(Z).rewrite(Probability) == Sum(z*pz, (z, 0, oo))
|
||||
assert Variance(X).rewrite(Probability) == Integral(x**2*px, (x, -oo, oo)) - Integral(x*px, (x, -oo, oo))**2
|
||||
assert Variance(Z).rewrite(Probability) == Sum(z**2*pz, (z, 0, oo)) - Sum(z*pz, (z, 0, oo))**2
|
||||
assert Covariance(w, X).rewrite(Probability) == \
|
||||
-w*Integral(x*Probability(Eq(X, x)), (x, -oo, oo)) + Integral(w*x*Probability(Eq(X, x)), (x, -oo, oo))
|
||||
|
||||
# To test rewrite as sum function
|
||||
assert Variance(X).rewrite(Sum) == Variance(X).rewrite(Integral)
|
||||
assert Expectation(X).rewrite(Sum) == Expectation(X).rewrite(Integral)
|
||||
|
||||
assert Covariance(w, X).rewrite(Sum) == 0
|
||||
|
||||
assert Covariance(w, X).rewrite(Integral) == 0
|
||||
|
||||
assert Variance(X, condition=Y).rewrite(Probability) == Integral(x**2*Probability(Eq(X, x), Y), (x, -oo, oo)) - \
|
||||
Integral(x*Probability(Eq(X, x), Y), (x, -oo, oo))**2
|
||||
|
||||
|
||||
def test_symbolic_Moment():
|
||||
mu = symbols('mu', real=True)
|
||||
sigma = symbols('sigma', positive=True)
|
||||
x = symbols('x')
|
||||
X = Normal('X', mu, sigma)
|
||||
M = Moment(X, 4, 2)
|
||||
assert M.rewrite(Expectation) == Expectation((X - 2)**4)
|
||||
assert M.rewrite(Probability) == Integral((x - 2)**4*Probability(Eq(X, x)),
|
||||
(x, -oo, oo))
|
||||
k = Dummy('k')
|
||||
expri = Integral(sqrt(2)*(k - 2)**4*exp(-(k - \
|
||||
mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (k, -oo, oo))
|
||||
assert M.rewrite(Integral).dummy_eq(expri)
|
||||
assert M.doit() == (mu**4 - 8*mu**3 + 6*mu**2*sigma**2 + \
|
||||
24*mu**2 - 24*mu*sigma**2 - 32*mu + 3*sigma**4 + 24*sigma**2 + 16)
|
||||
M = Moment(2, 5)
|
||||
assert M.doit() == 2**5
|
||||
|
||||
|
||||
def test_symbolic_CentralMoment():
|
||||
mu = symbols('mu', real=True)
|
||||
sigma = symbols('sigma', positive=True)
|
||||
x = symbols('x')
|
||||
X = Normal('X', mu, sigma)
|
||||
CM = CentralMoment(X, 6)
|
||||
assert CM.rewrite(Expectation) == Expectation((X - Expectation(X))**6)
|
||||
assert CM.rewrite(Probability) == Integral((x - Integral(x*Probability(True),
|
||||
(x, -oo, oo)))**6*Probability(Eq(X, x)), (x, -oo, oo))
|
||||
k = Dummy('k')
|
||||
expri = Integral(sqrt(2)*(k - Integral(sqrt(2)*k*exp(-(k - \
|
||||
mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (k, -oo, oo)))**6*exp(-(k - \
|
||||
mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (k, -oo, oo))
|
||||
assert CM.rewrite(Integral).dummy_eq(expri)
|
||||
assert CM.doit().simplify() == 15*sigma**6
|
||||
CM = Moment(5, 5)
|
||||
assert CM.doit() == 5**5
|
||||
Loading…
Add table
Add a link
Reference in a new issue