symbolic_probability.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. import itertools
  2. from sympy.concrete.summations import Sum
  3. from sympy.core.add import Add
  4. from sympy.core.expr import Expr
  5. from sympy.core.function import expand as _expand
  6. from sympy.core.mul import Mul
  7. from sympy.core.relational import Eq
  8. from sympy.core.singleton import S
  9. from sympy.core.symbol import Symbol
  10. from sympy.integrals.integrals import Integral
  11. from sympy.logic.boolalg import Not
  12. from sympy.core.parameters import global_parameters
  13. from sympy.core.sorting import default_sort_key
  14. from sympy.core.sympify import _sympify
  15. from sympy.core.relational import Relational
  16. from sympy.logic.boolalg import Boolean
  17. from sympy.stats import variance, covariance
  18. from sympy.stats.rv import (RandomSymbol, pspace, dependent,
  19. given, sampling_E, RandomIndexedSymbol, is_random,
  20. PSpace, sampling_P, random_symbols)
  21. __all__ = ['Probability', 'Expectation', 'Variance', 'Covariance']
  22. @is_random.register(Expr)
  23. def _(x):
  24. atoms = x.free_symbols
  25. if len(atoms) == 1 and next(iter(atoms)) == x:
  26. return False
  27. return any(is_random(i) for i in atoms)
  28. @is_random.register(RandomSymbol) # type: ignore
  29. def _(x):
  30. return True
  31. class Probability(Expr):
  32. """
  33. Symbolic expression for the probability.
  34. Examples
  35. ========
  36. >>> from sympy.stats import Probability, Normal
  37. >>> from sympy import Integral
  38. >>> X = Normal("X", 0, 1)
  39. >>> prob = Probability(X > 1)
  40. >>> prob
  41. Probability(X > 1)
  42. Integral representation:
  43. >>> prob.rewrite(Integral)
  44. Integral(sqrt(2)*exp(-_z**2/2)/(2*sqrt(pi)), (_z, 1, oo))
  45. Evaluation of the integral:
  46. >>> prob.evaluate_integral()
  47. sqrt(2)*(-sqrt(2)*sqrt(pi)*erf(sqrt(2)/2) + sqrt(2)*sqrt(pi))/(4*sqrt(pi))
  48. """
  49. is_commutative = True
  50. def __new__(cls, prob, condition=None, **kwargs):
  51. prob = _sympify(prob)
  52. if condition is None:
  53. obj = Expr.__new__(cls, prob)
  54. else:
  55. condition = _sympify(condition)
  56. obj = Expr.__new__(cls, prob, condition)
  57. obj._condition = condition
  58. return obj
  59. def doit(self, **hints):
  60. condition = self.args[0]
  61. given_condition = self._condition
  62. numsamples = hints.get('numsamples', False)
  63. evaluate = hints.get('evaluate', True)
  64. if isinstance(condition, Not):
  65. return S.One - self.func(condition.args[0], given_condition,
  66. evaluate=evaluate).doit(**hints)
  67. if condition.has(RandomIndexedSymbol):
  68. return pspace(condition).probability(condition, given_condition,
  69. evaluate=evaluate)
  70. if isinstance(given_condition, RandomSymbol):
  71. condrv = random_symbols(condition)
  72. if len(condrv) == 1 and condrv[0] == given_condition:
  73. from sympy.stats.frv_types import BernoulliDistribution
  74. return BernoulliDistribution(self.func(condition).doit(**hints), 0, 1)
  75. if any(dependent(rv, given_condition) for rv in condrv):
  76. return Probability(condition, given_condition)
  77. else:
  78. return Probability(condition).doit()
  79. if given_condition is not None and \
  80. not isinstance(given_condition, (Relational, Boolean)):
  81. raise ValueError("%s is not a relational or combination of relationals"
  82. % (given_condition))
  83. if given_condition == False or condition is S.false:
  84. return S.Zero
  85. if not isinstance(condition, (Relational, Boolean)):
  86. raise ValueError("%s is not a relational or combination of relationals"
  87. % (condition))
  88. if condition is S.true:
  89. return S.One
  90. if numsamples:
  91. return sampling_P(condition, given_condition, numsamples=numsamples)
  92. if given_condition is not None: # If there is a condition
  93. # Recompute on new conditional expr
  94. return Probability(given(condition, given_condition)).doit()
  95. # Otherwise pass work off to the ProbabilitySpace
  96. if pspace(condition) == PSpace():
  97. return Probability(condition, given_condition)
  98. result = pspace(condition).probability(condition)
  99. if hasattr(result, 'doit') and evaluate:
  100. return result.doit()
  101. else:
  102. return result
  103. def _eval_rewrite_as_Integral(self, arg, condition=None, **kwargs):
  104. return self.func(arg, condition=condition).doit(evaluate=False)
  105. _eval_rewrite_as_Sum = _eval_rewrite_as_Integral
  106. def evaluate_integral(self):
  107. return self.rewrite(Integral).doit()
  108. class Expectation(Expr):
  109. """
  110. Symbolic expression for the expectation.
  111. Examples
  112. ========
  113. >>> from sympy.stats import Expectation, Normal, Probability, Poisson
  114. >>> from sympy import symbols, Integral, Sum
  115. >>> mu = symbols("mu")
  116. >>> sigma = symbols("sigma", positive=True)
  117. >>> X = Normal("X", mu, sigma)
  118. >>> Expectation(X)
  119. Expectation(X)
  120. >>> Expectation(X).evaluate_integral().simplify()
  121. mu
  122. To get the integral expression of the expectation:
  123. >>> Expectation(X).rewrite(Integral)
  124. Integral(sqrt(2)*X*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo))
  125. The same integral expression, in more abstract terms:
  126. >>> Expectation(X).rewrite(Probability)
  127. Integral(x*Probability(Eq(X, x)), (x, -oo, oo))
  128. To get the Summation expression of the expectation for discrete random variables:
  129. >>> lamda = symbols('lamda', positive=True)
  130. >>> Z = Poisson('Z', lamda)
  131. >>> Expectation(Z).rewrite(Sum)
  132. Sum(Z*lamda**Z*exp(-lamda)/factorial(Z), (Z, 0, oo))
  133. This class is aware of some properties of the expectation:
  134. >>> from sympy.abc import a
  135. >>> Expectation(a*X)
  136. Expectation(a*X)
  137. >>> Y = Normal("Y", 1, 2)
  138. >>> Expectation(X + Y)
  139. Expectation(X + Y)
  140. To expand the ``Expectation`` into its expression, use ``expand()``:
  141. >>> Expectation(X + Y).expand()
  142. Expectation(X) + Expectation(Y)
  143. >>> Expectation(a*X + Y).expand()
  144. a*Expectation(X) + Expectation(Y)
  145. >>> Expectation(a*X + Y)
  146. Expectation(a*X + Y)
  147. >>> Expectation((X + Y)*(X - Y)).expand()
  148. Expectation(X**2) - Expectation(Y**2)
  149. To evaluate the ``Expectation``, use ``doit()``:
  150. >>> Expectation(X + Y).doit()
  151. mu + 1
  152. >>> Expectation(X + Expectation(Y + Expectation(2*X))).doit()
  153. 3*mu + 1
  154. To prevent evaluating nested ``Expectation``, use ``doit(deep=False)``
  155. >>> Expectation(X + Expectation(Y)).doit(deep=False)
  156. mu + Expectation(Expectation(Y))
  157. >>> Expectation(X + Expectation(Y + Expectation(2*X))).doit(deep=False)
  158. mu + Expectation(Expectation(Expectation(2*X) + Y))
  159. """
  160. def __new__(cls, expr, condition=None, **kwargs):
  161. expr = _sympify(expr)
  162. if expr.is_Matrix:
  163. from sympy.stats.symbolic_multivariate_probability import ExpectationMatrix
  164. return ExpectationMatrix(expr, condition)
  165. if condition is None:
  166. if not is_random(expr):
  167. return expr
  168. obj = Expr.__new__(cls, expr)
  169. else:
  170. condition = _sympify(condition)
  171. obj = Expr.__new__(cls, expr, condition)
  172. obj._condition = condition
  173. return obj
  174. def _eval_is_commutative(self):
  175. return(self.args[0].is_commutative)
  176. def expand(self, **hints):
  177. expr = self.args[0]
  178. condition = self._condition
  179. if not is_random(expr):
  180. return expr
  181. if isinstance(expr, Add):
  182. return Add.fromiter(Expectation(a, condition=condition).expand()
  183. for a in expr.args)
  184. expand_expr = _expand(expr)
  185. if isinstance(expand_expr, Add):
  186. return Add.fromiter(Expectation(a, condition=condition).expand()
  187. for a in expand_expr.args)
  188. elif isinstance(expr, Mul):
  189. rv = []
  190. nonrv = []
  191. for a in expr.args:
  192. if is_random(a):
  193. rv.append(a)
  194. else:
  195. nonrv.append(a)
  196. return Mul.fromiter(nonrv)*Expectation(Mul.fromiter(rv), condition=condition)
  197. return self
  198. def doit(self, **hints):
  199. deep = hints.get('deep', True)
  200. condition = self._condition
  201. expr = self.args[0]
  202. numsamples = hints.get('numsamples', False)
  203. evaluate = hints.get('evaluate', True)
  204. if deep:
  205. expr = expr.doit(**hints)
  206. if not is_random(expr) or isinstance(expr, Expectation): # expr isn't random?
  207. return expr
  208. if numsamples: # Computing by monte carlo sampling?
  209. evalf = hints.get('evalf', True)
  210. return sampling_E(expr, condition, numsamples=numsamples, evalf=evalf)
  211. if expr.has(RandomIndexedSymbol):
  212. return pspace(expr).compute_expectation(expr, condition)
  213. # Create new expr and recompute E
  214. if condition is not None: # If there is a condition
  215. return self.func(given(expr, condition)).doit(**hints)
  216. # A few known statements for efficiency
  217. if expr.is_Add: # We know that E is Linear
  218. return Add(*[self.func(arg, condition).doit(**hints)
  219. if not isinstance(arg, Expectation) else self.func(arg, condition)
  220. for arg in expr.args])
  221. if expr.is_Mul:
  222. if expr.atoms(Expectation):
  223. return expr
  224. if pspace(expr) == PSpace():
  225. return self.func(expr)
  226. # Otherwise case is simple, pass work off to the ProbabilitySpace
  227. result = pspace(expr).compute_expectation(expr, evaluate=evaluate)
  228. if hasattr(result, 'doit') and evaluate:
  229. return result.doit(**hints)
  230. else:
  231. return result
  232. def _eval_rewrite_as_Probability(self, arg, condition=None, **kwargs):
  233. rvs = arg.atoms(RandomSymbol)
  234. if len(rvs) > 1:
  235. raise NotImplementedError()
  236. if len(rvs) == 0:
  237. return arg
  238. rv = rvs.pop()
  239. if rv.pspace is None:
  240. raise ValueError("Probability space not known")
  241. symbol = rv.symbol
  242. if symbol.name[0].isupper():
  243. symbol = Symbol(symbol.name.lower())
  244. else :
  245. symbol = Symbol(symbol.name + "_1")
  246. if rv.pspace.is_Continuous:
  247. return Integral(arg.replace(rv, symbol)*Probability(Eq(rv, symbol), condition), (symbol, rv.pspace.domain.set.inf, rv.pspace.domain.set.sup))
  248. else:
  249. if rv.pspace.is_Finite:
  250. raise NotImplementedError
  251. else:
  252. return Sum(arg.replace(rv, symbol)*Probability(Eq(rv, symbol), condition), (symbol, rv.pspace.domain.set.inf, rv.pspace.set.sup))
  253. def _eval_rewrite_as_Integral(self, arg, condition=None, evaluate=False, **kwargs):
  254. return self.func(arg, condition=condition).doit(deep=False, evaluate=evaluate)
  255. _eval_rewrite_as_Sum = _eval_rewrite_as_Integral # For discrete this will be Sum
  256. def evaluate_integral(self):
  257. return self.rewrite(Integral).doit()
  258. evaluate_sum = evaluate_integral
  259. class Variance(Expr):
  260. """
  261. Symbolic expression for the variance.
  262. Examples
  263. ========
  264. >>> from sympy import symbols, Integral
  265. >>> from sympy.stats import Normal, Expectation, Variance, Probability
  266. >>> mu = symbols("mu", positive=True)
  267. >>> sigma = symbols("sigma", positive=True)
  268. >>> X = Normal("X", mu, sigma)
  269. >>> Variance(X)
  270. Variance(X)
  271. >>> Variance(X).evaluate_integral()
  272. sigma**2
  273. Integral representation of the underlying calculations:
  274. >>> Variance(X).rewrite(Integral)
  275. 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))
  276. Integral representation, without expanding the PDF:
  277. >>> Variance(X).rewrite(Probability)
  278. -Integral(x*Probability(Eq(X, x)), (x, -oo, oo))**2 + Integral(x**2*Probability(Eq(X, x)), (x, -oo, oo))
  279. Rewrite the variance in terms of the expectation
  280. >>> Variance(X).rewrite(Expectation)
  281. -Expectation(X)**2 + Expectation(X**2)
  282. Some transformations based on the properties of the variance may happen:
  283. >>> from sympy.abc import a
  284. >>> Y = Normal("Y", 0, 1)
  285. >>> Variance(a*X)
  286. Variance(a*X)
  287. To expand the variance in its expression, use ``expand()``:
  288. >>> Variance(a*X).expand()
  289. a**2*Variance(X)
  290. >>> Variance(X + Y)
  291. Variance(X + Y)
  292. >>> Variance(X + Y).expand()
  293. 2*Covariance(X, Y) + Variance(X) + Variance(Y)
  294. """
  295. def __new__(cls, arg, condition=None, **kwargs):
  296. arg = _sympify(arg)
  297. if arg.is_Matrix:
  298. from sympy.stats.symbolic_multivariate_probability import VarianceMatrix
  299. return VarianceMatrix(arg, condition)
  300. if condition is None:
  301. obj = Expr.__new__(cls, arg)
  302. else:
  303. condition = _sympify(condition)
  304. obj = Expr.__new__(cls, arg, condition)
  305. obj._condition = condition
  306. return obj
  307. def _eval_is_commutative(self):
  308. return self.args[0].is_commutative
  309. def expand(self, **hints):
  310. arg = self.args[0]
  311. condition = self._condition
  312. if not is_random(arg):
  313. return S.Zero
  314. if isinstance(arg, RandomSymbol):
  315. return self
  316. elif isinstance(arg, Add):
  317. rv = []
  318. for a in arg.args:
  319. if is_random(a):
  320. rv.append(a)
  321. variances = Add(*(Variance(xv, condition).expand() for xv in rv))
  322. map_to_covar = lambda x: 2*Covariance(*x, condition=condition).expand()
  323. covariances = Add(*map(map_to_covar, itertools.combinations(rv, 2)))
  324. return variances + covariances
  325. elif isinstance(arg, Mul):
  326. nonrv = []
  327. rv = []
  328. for a in arg.args:
  329. if is_random(a):
  330. rv.append(a)
  331. else:
  332. nonrv.append(a**2)
  333. if len(rv) == 0:
  334. return S.Zero
  335. return Mul.fromiter(nonrv)*Variance(Mul.fromiter(rv), condition)
  336. # this expression contains a RandomSymbol somehow:
  337. return self
  338. def _eval_rewrite_as_Expectation(self, arg, condition=None, **kwargs):
  339. e1 = Expectation(arg**2, condition)
  340. e2 = Expectation(arg, condition)**2
  341. return e1 - e2
  342. def _eval_rewrite_as_Probability(self, arg, condition=None, **kwargs):
  343. return self.rewrite(Expectation).rewrite(Probability)
  344. def _eval_rewrite_as_Integral(self, arg, condition=None, **kwargs):
  345. return variance(self.args[0], self._condition, evaluate=False)
  346. _eval_rewrite_as_Sum = _eval_rewrite_as_Integral
  347. def evaluate_integral(self):
  348. return self.rewrite(Integral).doit()
  349. class Covariance(Expr):
  350. """
  351. Symbolic expression for the covariance.
  352. Examples
  353. ========
  354. >>> from sympy.stats import Covariance
  355. >>> from sympy.stats import Normal
  356. >>> X = Normal("X", 3, 2)
  357. >>> Y = Normal("Y", 0, 1)
  358. >>> Z = Normal("Z", 0, 1)
  359. >>> W = Normal("W", 0, 1)
  360. >>> cexpr = Covariance(X, Y)
  361. >>> cexpr
  362. Covariance(X, Y)
  363. Evaluate the covariance, `X` and `Y` are independent,
  364. therefore zero is the result:
  365. >>> cexpr.evaluate_integral()
  366. 0
  367. Rewrite the covariance expression in terms of expectations:
  368. >>> from sympy.stats import Expectation
  369. >>> cexpr.rewrite(Expectation)
  370. Expectation(X*Y) - Expectation(X)*Expectation(Y)
  371. In order to expand the argument, use ``expand()``:
  372. >>> from sympy.abc import a, b, c, d
  373. >>> Covariance(a*X + b*Y, c*Z + d*W)
  374. Covariance(a*X + b*Y, c*Z + d*W)
  375. >>> Covariance(a*X + b*Y, c*Z + d*W).expand()
  376. a*c*Covariance(X, Z) + a*d*Covariance(W, X) + b*c*Covariance(Y, Z) + b*d*Covariance(W, Y)
  377. This class is aware of some properties of the covariance:
  378. >>> Covariance(X, X).expand()
  379. Variance(X)
  380. >>> Covariance(a*X, b*Y).expand()
  381. a*b*Covariance(X, Y)
  382. """
  383. def __new__(cls, arg1, arg2, condition=None, **kwargs):
  384. arg1 = _sympify(arg1)
  385. arg2 = _sympify(arg2)
  386. if arg1.is_Matrix or arg2.is_Matrix:
  387. from sympy.stats.symbolic_multivariate_probability import CrossCovarianceMatrix
  388. return CrossCovarianceMatrix(arg1, arg2, condition)
  389. if kwargs.pop('evaluate', global_parameters.evaluate):
  390. arg1, arg2 = sorted([arg1, arg2], key=default_sort_key)
  391. if condition is None:
  392. obj = Expr.__new__(cls, arg1, arg2)
  393. else:
  394. condition = _sympify(condition)
  395. obj = Expr.__new__(cls, arg1, arg2, condition)
  396. obj._condition = condition
  397. return obj
  398. def _eval_is_commutative(self):
  399. return self.args[0].is_commutative
  400. def expand(self, **hints):
  401. arg1 = self.args[0]
  402. arg2 = self.args[1]
  403. condition = self._condition
  404. if arg1 == arg2:
  405. return Variance(arg1, condition).expand()
  406. if not is_random(arg1):
  407. return S.Zero
  408. if not is_random(arg2):
  409. return S.Zero
  410. arg1, arg2 = sorted([arg1, arg2], key=default_sort_key)
  411. if isinstance(arg1, RandomSymbol) and isinstance(arg2, RandomSymbol):
  412. return Covariance(arg1, arg2, condition)
  413. coeff_rv_list1 = self._expand_single_argument(arg1.expand())
  414. coeff_rv_list2 = self._expand_single_argument(arg2.expand())
  415. addends = [a*b*Covariance(*sorted([r1, r2], key=default_sort_key), condition=condition)
  416. for (a, r1) in coeff_rv_list1 for (b, r2) in coeff_rv_list2]
  417. return Add.fromiter(addends)
  418. @classmethod
  419. def _expand_single_argument(cls, expr):
  420. # return (coefficient, random_symbol) pairs:
  421. if isinstance(expr, RandomSymbol):
  422. return [(S.One, expr)]
  423. elif isinstance(expr, Add):
  424. outval = []
  425. for a in expr.args:
  426. if isinstance(a, Mul):
  427. outval.append(cls._get_mul_nonrv_rv_tuple(a))
  428. elif is_random(a):
  429. outval.append((S.One, a))
  430. return outval
  431. elif isinstance(expr, Mul):
  432. return [cls._get_mul_nonrv_rv_tuple(expr)]
  433. elif is_random(expr):
  434. return [(S.One, expr)]
  435. @classmethod
  436. def _get_mul_nonrv_rv_tuple(cls, m):
  437. rv = []
  438. nonrv = []
  439. for a in m.args:
  440. if is_random(a):
  441. rv.append(a)
  442. else:
  443. nonrv.append(a)
  444. return (Mul.fromiter(nonrv), Mul.fromiter(rv))
  445. def _eval_rewrite_as_Expectation(self, arg1, arg2, condition=None, **kwargs):
  446. e1 = Expectation(arg1*arg2, condition)
  447. e2 = Expectation(arg1, condition)*Expectation(arg2, condition)
  448. return e1 - e2
  449. def _eval_rewrite_as_Probability(self, arg1, arg2, condition=None, **kwargs):
  450. return self.rewrite(Expectation).rewrite(Probability)
  451. def _eval_rewrite_as_Integral(self, arg1, arg2, condition=None, **kwargs):
  452. return covariance(self.args[0], self.args[1], self._condition, evaluate=False)
  453. _eval_rewrite_as_Sum = _eval_rewrite_as_Integral
  454. def evaluate_integral(self):
  455. return self.rewrite(Integral).doit()
  456. class Moment(Expr):
  457. """
  458. Symbolic class for Moment
  459. Examples
  460. ========
  461. >>> from sympy import Symbol, Integral
  462. >>> from sympy.stats import Normal, Expectation, Probability, Moment
  463. >>> mu = Symbol('mu', real=True)
  464. >>> sigma = Symbol('sigma', positive=True)
  465. >>> X = Normal('X', mu, sigma)
  466. >>> M = Moment(X, 3, 1)
  467. To evaluate the result of Moment use `doit`:
  468. >>> M.doit()
  469. mu**3 - 3*mu**2 + 3*mu*sigma**2 + 3*mu - 3*sigma**2 - 1
  470. Rewrite the Moment expression in terms of Expectation:
  471. >>> M.rewrite(Expectation)
  472. Expectation((X - 1)**3)
  473. Rewrite the Moment expression in terms of Probability:
  474. >>> M.rewrite(Probability)
  475. Integral((x - 1)**3*Probability(Eq(X, x)), (x, -oo, oo))
  476. Rewrite the Moment expression in terms of Integral:
  477. >>> M.rewrite(Integral)
  478. Integral(sqrt(2)*(X - 1)**3*exp(-(X - mu)**2/(2*sigma**2))/(2*sqrt(pi)*sigma), (X, -oo, oo))
  479. """
  480. def __new__(cls, X, n, c=0, condition=None, **kwargs):
  481. X = _sympify(X)
  482. n = _sympify(n)
  483. c = _sympify(c)
  484. if condition is not None:
  485. condition = _sympify(condition)
  486. return super().__new__(cls, X, n, c, condition)
  487. else:
  488. return super().__new__(cls, X, n, c)
  489. def doit(self, **hints):
  490. return self.rewrite(Expectation).doit(**hints)
  491. def _eval_rewrite_as_Expectation(self, X, n, c=0, condition=None, **kwargs):
  492. return Expectation((X - c)**n, condition)
  493. def _eval_rewrite_as_Probability(self, X, n, c=0, condition=None, **kwargs):
  494. return self.rewrite(Expectation).rewrite(Probability)
  495. def _eval_rewrite_as_Integral(self, X, n, c=0, condition=None, **kwargs):
  496. return self.rewrite(Expectation).rewrite(Integral)
  497. class CentralMoment(Expr):
  498. """
  499. Symbolic class Central Moment
  500. Examples
  501. ========
  502. >>> from sympy import Symbol, Integral
  503. >>> from sympy.stats import Normal, Expectation, Probability, CentralMoment
  504. >>> mu = Symbol('mu', real=True)
  505. >>> sigma = Symbol('sigma', positive=True)
  506. >>> X = Normal('X', mu, sigma)
  507. >>> CM = CentralMoment(X, 4)
  508. To evaluate the result of CentralMoment use `doit`:
  509. >>> CM.doit().simplify()
  510. 3*sigma**4
  511. Rewrite the CentralMoment expression in terms of Expectation:
  512. >>> CM.rewrite(Expectation)
  513. Expectation((-Expectation(X) + X)**4)
  514. Rewrite the CentralMoment expression in terms of Probability:
  515. >>> CM.rewrite(Probability)
  516. Integral((x - Integral(x*Probability(True), (x, -oo, oo)))**4*Probability(Eq(X, x)), (x, -oo, oo))
  517. Rewrite the CentralMoment expression in terms of Integral:
  518. >>> CM.rewrite(Integral)
  519. 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))
  520. """
  521. def __new__(cls, X, n, condition=None, **kwargs):
  522. X = _sympify(X)
  523. n = _sympify(n)
  524. if condition is not None:
  525. condition = _sympify(condition)
  526. return super().__new__(cls, X, n, condition)
  527. else:
  528. return super().__new__(cls, X, n)
  529. def doit(self, **hints):
  530. return self.rewrite(Expectation).doit(**hints)
  531. def _eval_rewrite_as_Expectation(self, X, n, condition=None, **kwargs):
  532. mu = Expectation(X, condition, **kwargs)
  533. return Moment(X, n, mu, condition, **kwargs).rewrite(Expectation)
  534. def _eval_rewrite_as_Probability(self, X, n, condition=None, **kwargs):
  535. return self.rewrite(Expectation).rewrite(Probability)
  536. def _eval_rewrite_as_Integral(self, X, n, condition=None, **kwargs):
  537. return self.rewrite(Expectation).rewrite(Integral)