symbolic_probability.py 22 KB

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