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 test_totient(): assert [totient(k) for k in range(1, 12)] == \ [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10] assert totient(5005) == 2880 assert totient(5006) == 2502 assert totient(5009) == 5008 assert totient(2**100) == 2**99 raises(ValueError, lambda: totient(30.1)) raises(ValueError, lambda: totient(20.001)) m = Symbol("m", integer=True) assert totient(m) assert totient(m).subs(m, 3**10) == 3**10 - 3**9 assert summation(totient(m), (m, 1, 11)) == 42 n = Symbol("n", integer=True, positive=True) assert totient(n).is_integer x = Symbol("x", integer=False) raises(ValueError, lambda: totient(x)) y = Symbol("y", positive=False) raises(ValueError, lambda: totient(y)) z = Symbol("z", positive=True, integer=True) raises(ValueError, lambda: totient(2**(-z)))
def test_divisor_sigma(): assert [divisor_sigma(k) for k in range(1, 12)] == \ [1, 3, 4, 7, 6, 12, 8, 15, 13, 18, 12] assert [divisor_sigma(k, 2) for k in range(1, 12)] == \ [1, 5, 10, 21, 26, 50, 50, 85, 91, 130, 122] assert divisor_sigma(23450) == 50592 assert divisor_sigma(23450, 0) == 24 assert divisor_sigma(23450, 1) == 50592 assert divisor_sigma(23450, 2) == 730747500 assert divisor_sigma(23450, 3) == 14666785333344 a = Symbol("a", prime=True) b = Symbol("b", prime=True) j = Symbol("j", integer=True, positive=True) k = Symbol("k", integer=True, positive=True) assert divisor_sigma( a**j * b**k) == (a**(j + 1) - 1) * (b**(k + 1) - 1) / ((a - 1) * (b - 1)) assert divisor_sigma( a**j * b**k, 2) == (a**(2 * j + 2) - 1) * (b**(2 * k + 2) - 1) / ((a**2 - 1) * (b**2 - 1)) assert divisor_sigma(a**j * b**k, 0) == (j + 1) * (k + 1) m = Symbol("m", integer=True) k = Symbol("k", integer=True) assert divisor_sigma(m) assert divisor_sigma(m, k) assert divisor_sigma(m).subs(m, 3**10) == 88573 assert divisor_sigma(m, k).subs([(m, 3**10), (k, 3)]) == 213810021790597 assert summation(divisor_sigma(m), (m, 1, 11)) == 99
def apply(given): assert given.is_ForAll eq = given.function assert eq.is_Equality limits = given.limits if len(limits) == 2: (j, j_domain), i_limit = limits assert j_domain.is_Complement _, i = j_domain.args assert i.is_FiniteSet and len(i) == 1 i, *_ = i.args else: assert len(limits) == 1 i, j_domain = limits[0] assert j_domain.is_Complement universe, j = j_domain.args assert j.is_FiniteSet and len(j) == 1 j, *_ = j.args i_limit = (i, universe) intersection, emptyset = eq.args assert emptyset.is_EmptySet xi, xj = intersection.args if not xi._has(i): xi, xj = xj, xi assert xi._subs(i, j) == xj return Equality(abs(UNION(xi, i_limit).simplify()), summation(abs(xi), i_limit), given=given)
def apply(given, excludes=None): assert given.is_Equality x_union_abs, x_abs_sum = given.args if not x_union_abs.is_Abs: tmp = x_union_abs x_union_abs = x_abs_sum x_abs_sum = tmp assert x_union_abs.is_Abs x_union = x_union_abs.arg assert x_union.is_UNION if x_abs_sum.is_Sum: assert x_abs_sum.function.is_Abs assert x_abs_sum.function.arg == x_union.function else: assert x_abs_sum == summation(abs(x_union.function), *x_union.limits) limits_dict = x_union.limits_dict i, *_ = limits_dict.keys() xi = x_union.function kwargs = i._assumptions.copy() if 'domain' in kwargs: del kwargs['domain'] j = xi.generate_free_symbol(excludes=excludes, **kwargs) xj = xi.subs(i, j) i_domain = limits_dict[i] or i.domain limits = [(j, i_domain - {i})] + [*x_union.limits] return ForAll(Equality(xi & xj, S.EmptySet).simplify(), *limits, given=given)
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 test_primeomega(): assert primeomega(2) == 1 assert primeomega(2 * 2) == 2 assert primeomega(2 * 2 * 3) == 3 assert primeomega(3 * 25) == primeomega(3) + primeomega(25) assert [primeomega(p) for p in primerange(1, 10)] == [1, 1, 1, 1] assert primeomega(fac(50)) == 108 assert primeomega(2**9941 - 1) == 1 n = Symbol('n', integer=True) assert primeomega(n) assert primeomega(n).subs(n, 2**31 - 1) == 1 assert summation(primeomega(n), (n, 2, 30)) == 59
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 marginal_distribution(self, *indices): count = self.component_count all_syms = [Symbol(str(Indexed(self.symbol, i))) for i in range(count)] sym = [Symbol(str(Indexed(self.symbol, i))) for i in indices] limits = list([i,] for i in all_syms if i not in sym) for i in range(len(limits)): if i not in indices: limits[i].append(self.distribution.set.args[i]) limits[i] = tuple(limits[i]) limits = tuple(limits) if self.distribution.is_Continuous: return Lambda(sym, integrate(self.distribution(*all_syms), limits)) if self.distribution.is_Discrete: return Lambda(sym, summation(self.distribution(all_syms), limits))
def test_reduced_totient(): assert [reduced_totient(k) for k in range(1, 16)] == \ [1, 1, 2, 2, 4, 2, 6, 2, 6, 4, 10, 2, 12, 6, 4] assert reduced_totient(5005) == 60 assert reduced_totient(5006) == 2502 assert reduced_totient(5009) == 5008 assert reduced_totient(2**100) == 2**98 m = Symbol("m", integer=True) assert reduced_totient(m) assert reduced_totient(m).subs(m, 2**3 * 3**10) == 3**10 - 3**9 assert summation(reduced_totient(m), (m, 1, 16)) == 68 n = Symbol("n", integer=True, positive=True) assert reduced_totient(n).is_integer
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 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 marginal_distribution(self, *indices): count = self.component_count 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 = [Symbol(str(Indexed(self.symbol, i))) for i in indices] limits = list([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 test_udivisor_sigma(): assert [udivisor_sigma(k) for k in range(1, 12)] == \ [1, 3, 4, 5, 6, 12, 8, 9, 10, 18, 12] assert [udivisor_sigma(k, 3) for k in range(1, 12)] == \ [1, 9, 28, 65, 126, 252, 344, 513, 730, 1134, 1332] assert udivisor_sigma(23450) == 42432 assert udivisor_sigma(23450, 0) == 16 assert udivisor_sigma(23450, 1) == 42432 assert udivisor_sigma(23450, 2) == 702685000 assert udivisor_sigma(23450, 4) == 321426961814978248 m = Symbol("m", integer=True) k = Symbol("k", integer=True) assert udivisor_sigma(m) assert udivisor_sigma(m, k) assert udivisor_sigma(m).subs(m, 4**9) == 262145 assert udivisor_sigma(m, k).subs([(m, 4**9), (k, 2)]) == 68719476737 assert summation(udivisor_sigma(m), (m, 2, 15)) == 169
def marginal_distribution(self, *indices): count = self.component_count 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 = [Symbol(str(Indexed(self.symbol, i))) for i in indices] limits = list([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 limits = tuple(limits) 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 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 = list([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 deltasummation(f, limit, no_piecewise=False): """ Handle summations containing a KroneckerDelta. The idea for summation is the following: - If we are dealing with a KroneckerDelta expression, i.e. KroneckerDelta(g(x), j), we try to simplify it. If we could simplify it, then we sum the resulting expression. We already know we can sum a simplified expression, because only simple KroneckerDelta expressions are involved. If we couldn't simplify it, there are two cases: 1) The expression is a simple expression: we return the summation, taking care if we are dealing with a Derivative or with a proper KroneckerDelta. 2) The expression is not simple (i.e. KroneckerDelta(cos(x))): we can do nothing at all. - If the expr is a multiplication expr having a KroneckerDelta term: First we expand it. If the expansion did work, then we try to sum the expansion. If not, we try to extract a simple KroneckerDelta term, then we have two cases: 1) We have a simple KroneckerDelta term, so we return the summation. 2) We didn't have a simple term, but we do have an expression with simplified KroneckerDelta terms, so we sum this expression. Examples ======== >>> from sympy import oo >>> from sympy.abc import i, j, k >>> from sympy.concrete.delta import deltasummation >>> from sympy import KroneckerDelta, Piecewise >>> deltasummation(KroneckerDelta(i, k), (k, -oo, oo)) 1 >>> deltasummation(KroneckerDelta(i, k), (k, 0, oo)) Piecewise((1, i >= 0), (0, True)) >>> deltasummation(KroneckerDelta(i, k), (k, 1, 3)) Piecewise((1, And(1 <= i, i <= 3)), (0, True)) >>> deltasummation(k*KroneckerDelta(i, j)*KroneckerDelta(j, k), (k, -oo, oo)) j*KroneckerDelta(i, j) >>> deltasummation(j*KroneckerDelta(i, j), (j, -oo, oo)) i >>> deltasummation(i*KroneckerDelta(i, j), (i, -oo, oo)) j See Also ======== deltaproduct sympy.functions.special.tensor_functions.KroneckerDelta sympy.concrete.sums.summation """ from sympy.concrete.summations import summation from sympy.solvers import solve if ((limit[2] - limit[1]) < 0) is True: return S.Zero if not f.has(KroneckerDelta): return summation(f, limit) x = limit[0] g = _expand_delta(f, x) if g.is_Add: return piecewise_fold( g.func(*[deltasummation(h, limit, no_piecewise) for h in g.args])) # try to extract a simple KroneckerDelta term delta, expr = _extract_delta(g, x) if not delta: return summation(f, limit) solns = solve(delta.args[0] - delta.args[1], x) if len(solns) == 0: return S.Zero elif len(solns) != 1: return Sum(f, limit) value = solns[0] if no_piecewise: return expr.subs(x, value) return Piecewise( (expr.subs(x, value), Interval(*limit[1:3]).as_relational(value)), (S.Zero, True) )
def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta, RisingFactorial (k, a, n) = limits if k not in term.free_symbols: if (term - 1).is_zero: return S.One return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a if dif.is_Integer: return Mul(*[term.subs(k, a + i) for i in range(dif + 1)]) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly) M = 0 for r, m in all_roots.items(): M += m A *= RisingFactorial(a - r, n - a + 1)**m Q *= (n - r)**m if M < poly.degree(): arg = quo(poly, Q.as_poly(k)) B = self.func(arg, (k, a, n)).doit() return poly.LC()**(n - a + 1) * A * B elif term.is_Add: p, q = term.as_numer_denom() p = self._eval_product(p, (k, a, n)) q = self._eval_product(q, (k, a, n)) return p / q elif term.is_Mul: exclude, include = [], [] for t in term.args: p = self._eval_product(t, (k, a, n)) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: arg = term._new_rawargs(*include) A = Mul(*exclude) B = self.func(arg, (k, a, n)).doit() return A * B elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) return term.base**s elif not term.exp.has(k): p = self._eval_product(term.base, (k, a, n)) if p is not None: return p**term.exp elif isinstance(term, Product): evaluated = term.doit() f = self._eval_product(evaluated, limits) if f is None: return self.func(evaluated, limits) else: return f
def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta, RisingFactorial (k, a, n) = limits if k not in term.free_symbols: if (term - 1).is_zero: return S.One return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a if dif.is_Integer: return Mul(*[term.subs(k, a + i) for i in range(dif + 1)]) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly) M = 0 for r, m in all_roots.items(): M += m A *= RisingFactorial(a - r, n - a + 1)**m Q *= (n - r)**m if M < poly.degree(): arg = quo(poly, Q.as_poly(k)) B = self.func(arg, (k, a, n)).doit() return poly.LC()**(n - a + 1) * A * B elif term.is_Add: factored = factor_terms(term, fraction=True) if factored.is_Mul: return self._eval_product(factored, (k, a, n)) elif term.is_Mul: exclude, include = [], [] for t in term.args: p = self._eval_product(t, (k, a, n)) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: arg = term._new_rawargs(*include) A = Mul(*exclude) B = self.func(arg, (k, a, n)).doit() return A * B elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) return term.base**s elif not term.exp.has(k): p = self._eval_product(term.base, (k, a, n)) if p is not None: return p**term.exp elif isinstance(term, Product): evaluated = term.doit() f = self._eval_product(evaluated, limits) if f is None: return self.func(evaluated, limits) else: return f
def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta (k, a, n) = limits if k not in term.free_symbols: return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a if dif.is_Integer: return Mul(*[term.subs(k, a + i) for i in xrange(dif + 1)]) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly, multiple=True) for r in all_roots: A *= C.RisingFactorial(a - r, n - a + 1) Q *= n - r if len(all_roots) < poly.degree(): arg = quo(poly, Q.as_poly(k)) B = self.func(arg, (k, a, n)).doit() return poly.LC()**(n - a + 1) * A * B elif term.is_Add: p, q = term.as_numer_denom() p = self._eval_product(p, (k, a, n)) q = self._eval_product(q, (k, a, n)) return p / q elif term.is_Mul: exclude, include = [], [] for t in term.args: p = self._eval_product(t, (k, a, n)) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: arg = term._new_rawargs(*include) A = Mul(*exclude) B = self.func(arg, (k, a, n)).doit() return A * B elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) return term.base**s elif not term.exp.has(k): p = self._eval_product(term.base, (k, a, n)) if p is not None: return p**term.exp elif isinstance(term, Product): evaluated = term.doit() f = self._eval_product(evaluated, limits) if f is None: return self.func(evaluated, limits) else: return f
def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta, RisingFactorial (k, a, n) = limits if k not in term.free_symbols: if (term - 1).is_zero: return S.One return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a if dif.is_Integer: return Mul(*[term.subs(k, a + i) for i in range(dif + 1)]) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly) M = 0 for r, m in all_roots.items(): M += m A *= RisingFactorial(a - r, n - a + 1)**m Q *= (n - r)**m if M < poly.degree(): arg = quo(poly, Q.as_poly(k)) B = self.func(arg, (k, a, n)).doit() return poly.LC()**(n - a + 1) * A * B elif term.is_Add: p, q = term.as_numer_denom() q = self._eval_product(q, (k, a, n)) if q.is_Number: # There is expression, which couldn't change by # as_numer_denom(). E.g. n**(2/3) + 1 --> (n**(2/3) + 1, 1). # We have to catch this case. p = sum([self._eval_product(i, (k, a, n)) for i in p.as_coeff_Add()]) else: p = self._eval_product(p, (k, a, n)) return p / q elif term.is_Mul: exclude, include = [], [] for t in term.args: p = self._eval_product(t, (k, a, n)) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: arg = term._new_rawargs(*include) A = Mul(*exclude) B = self.func(arg, (k, a, n)).doit() return A * B elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) return term.base**s elif not term.exp.has(k): p = self._eval_product(term.base, (k, a, n)) if p is not None: return p**term.exp elif isinstance(term, Product): evaluated = term.doit() f = self._eval_product(evaluated, limits) if f is None: return self.func(evaluated, limits) else: return f
def test_Sum(): assert str(summation(cos(3*z), (z, x, y))) == "Sum(cos(3*z), (z, x, y))" assert str(Sum(x*y**2, (x, -2, 2), (y, -5, 5))) == \ "Sum(x*y**2, (x, -2, 2), (y, -5, 5))"
def _eval_product(self, term, limits): from sympy.concrete.delta import deltaproduct, _has_simple_delta from sympy.concrete.summations import summation from sympy.functions import KroneckerDelta, RisingFactorial (k, a, n) = limits if k not in term.free_symbols: if (term - 1).is_zero: return S.One return term**(n - a + 1) if a == n: return term.subs(k, a) if term.has(KroneckerDelta) and _has_simple_delta(term, limits[0]): return deltaproduct(term, limits) dif = n - a definite = dif.is_Integer if definite and (dif < 100): return self._eval_product_direct(term, limits) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly) M = 0 for r, m in all_roots.items(): M += m A *= RisingFactorial(a - r, n - a + 1)**m Q *= (n - r)**m if M < poly.degree(): arg = quo(poly, Q.as_poly(k)) B = self.func(arg, (k, a, n)).doit() return poly.LC()**(n - a + 1) * A * B elif term.is_Add: factored = factor_terms(term, fraction=True) if factored.is_Mul: return self._eval_product(factored, (k, a, n)) elif term.is_Mul: # Factor in part without the summation variable and part with without_k, with_k = term.as_coeff_mul(k) if len(with_k) >= 2: # More than one term including k, so still a multiplication exclude, include = [], [] for t in with_k: p = self._eval_product(t, (k, a, n)) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: arg = term._new_rawargs(*include) A = Mul(*exclude) B = self.func(arg, (k, a, n)).doit() return without_k**(n - a + 1) * A * B else: # Just a single term p = self._eval_product(with_k[0], (k, a, n)) if p is None: p = self.func(with_k[0], (k, a, n)).doit() return without_k**(n - a + 1) * p elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) return term.base**s elif not term.exp.has(k): p = self._eval_product(term.base, (k, a, n)) if p is not None: return p**term.exp elif isinstance(term, Product): evaluated = term.doit() f = self._eval_product(evaluated, limits) if f is None: return self.func(evaluated, limits) else: return f if definite: return self._eval_product_direct(term, limits)
def _eval_rewrite_as_factorial(self, arg, **kwargs): from sympy.concrete.summations import summation i = Dummy('i') f = S.NegativeOne**i / factorial(i) return factorial(arg) * summation(f, (i, 0, arg))
def KL(p, q, *limit): return summation(p * log(p / q), *limit)
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)