123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- 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))
- """
- 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)
- for_rewrite = not hints.get('for_rewrite', False)
- if isinstance(condition, Not):
- return S.One - self.func(condition.args[0], given_condition,
- evaluate=for_rewrite).doit(**hints)
- if condition.has(RandomIndexedSymbol):
- return pspace(condition).probability(condition, given_condition,
- evaluate=for_rewrite)
- 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 for_rewrite:
- return result.doit()
- else:
- return result
- def _eval_rewrite_as_Integral(self, arg, condition=None, **kwargs):
- return self.func(arg, condition=condition).doit(for_rewrite=True)
- _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(Y + Expectation(2*X)))
- """
- 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 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)
- for_rewrite = not hints.get('for_rewrite', False)
- 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=for_rewrite)
- if hasattr(result, 'doit') and for_rewrite:
- 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, **kwargs):
- return self.func(arg, condition=condition).doit(deep=False, for_rewrite=True)
- _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 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(*map(lambda xv: Variance(xv, condition).expand(), 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 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((X - Expectation(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)
|