def __init__(self, *args): ak = args[4][0] k = ak.variables[0] self.ak_seq = sequence(ak.formula, (k, 1, oo)) self.fact_seq = sequence(factorial(k), (k, 1, oo)) self.bell_coeff_seq = self.ak_seq * self.fact_seq self.sign_seq = sequence((-1, 1), (k, 1, oo))
def __init__(self, *args): ffps, gfps = self.ffps, self.gfps k = ffps.ak.variables[0] self.coeff1 = sequence(ffps.ak.formula, (k, 0, oo)) k = gfps.ak.variables[0] self.coeff2 = sequence(gfps.ak.formula, (k, 0, oo))
def test_sequence(): form = SeqFormula(n**2, (n, 0, 5)) per = SeqPer((1, 2, 3), (n, 0, 5)) inter = SeqFormula(n**2) assert sequence(n**2, (n, 0, 5)) == form assert sequence((1, 2, 3), (n, 0, 5)) == per assert sequence(n**2) == inter
def test_mul__coeff_mul(): assert SeqPer((1, 2), (n, 0, oo)).coeff_mul(2) == SeqPer((2, 4), (n, 0, oo)) assert SeqFormula(n**2).coeff_mul(2) == SeqFormula(2*n**2) assert S.EmptySequence.coeff_mul(100) == S.EmptySequence assert SeqPer((1, 2), (n, 0, oo)) * (SeqPer((2, 3))) == \ SeqPer((2, 6), (n, 0, oo)) assert SeqFormula(n**2) * SeqFormula(n**3) == SeqFormula(n**5) assert S.EmptySequence * SeqFormula(n**2) == S.EmptySequence assert SeqFormula(n**2) * S.EmptySequence == S.EmptySequence raises(TypeError, lambda: sequence(n**2) * n) raises(TypeError, lambda: n * sequence(n**2))
def __init__(self, *args): ffps = self.ffps k = ffps.xk.variables[0] inv = ffps.zero_coeff() inv_seq = sequence(inv ** (-(k + 1)), (k, 1, oo)) self.aux_seq = ffps.sign_seq * ffps.fact_seq * inv_seq
def coeff_bell(self, n): r""" self.coeff_bell(n) returns a sequence of Bell polynomials of the second kind. Note that ``n`` should be a integer. The second kind of Bell polynomials (are sometimes called "partial" Bell polynomials or incomplete Bell polynomials) are defined as .. math:: B_{n,k}(x_1, x_2,\dotsc x_{n-k+1}) = \sum_{j_1+j_2+j_2+\dotsb=k \atop j_1+2j_2+3j_2+\dotsb=n} \frac{n!}{j_1!j_2!\dotsb j_{n-k+1}!} \left(\frac{x_1}{1!} \right)^{j_1} \left(\frac{x_2}{2!} \right)^{j_2} \dotsb \left(\frac{x_{n-k+1}}{(n-k+1)!} \right) ^{j_{n-k+1}}. * ``bell(n, k, (x1, x2, ...))`` gives Bell polynomials of the second kind, `B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1})`. See Also ======== sympy.functions.combinatorial.numbers.bell """ inner_coeffs = [bell(n, j, tuple(self.bell_coeff_seq[:n-j+1])) for j in range(1, n+1)] k = Dummy('k') return sequence(tuple(inner_coeffs), (k, 1, oo))
def integrate(self, x=None, **kwargs): """Integrate Formal Power Series. Examples ======== >>> from sympy import fps, sin, integrate >>> from sympy.abc import x >>> f = fps(sin(x)) >>> f.integrate(x).truncate() -1 + x**2/2 - x**4/24 + O(x**6) >>> integrate(f, (x, 0, 1)) -cos(1) + 1 """ from sympy.integrals import integrate if x is None: x = self.x elif iterable(x): return integrate(self.function, x) f = integrate(self.function, x) ind = integrate(self.ind, x) ind += (f - ind).limit(x, 0) # constant of integration pow_xk = self._get_pow_x(self.xk.formula) ak = self.ak k = ak.variables[0] if ak.formula.has(x): form = [] for e, c in ak.formula.args: temp = S.Zero for t in Add.make_args(e): pow_x = self._get_pow_x(t) temp += t / (pow_xk + pow_x + 1) form.append((temp, c)) form = Piecewise(*form) ak = sequence(form.subs(k, k - 1), (k, ak.start + 1, ak.stop)) else: ak = sequence((ak.formula / (pow_xk + 1)).subs(k, k - 1), (k, ak.start + 1, ak.stop)) return self.func(f, self.x, self.x0, self.dir, (ak, self.xk, ind))
def _eval_derivative(self, x): f = self.function.diff(x) ind = self.ind.diff(x) pow_xk = self._get_pow_x(self.xk.formula) ak = self.ak k = ak.variables[0] if ak.formula.has(x): form = [] for e, c in ak.formula.args: temp = S.Zero for t in Add.make_args(e): pow_x = self._get_pow_x(t) temp += t * (pow_xk + pow_x) form.append((temp, c)) form = Piecewise(*form) ak = sequence(form.subs(k, k + 1), (k, ak.start - 1, ak.stop)) else: ak = sequence((ak.formula * pow_xk).subs(k, k + 1), (k, ak.start - 1, ak.stop)) return self.func(f, self.x, self.x0, self.dir, (ak, self.xk, ind))
def test_find_linear_recurrence(): assert sequence((0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55), \ (n, 0, 10)).find_linear_recurrence(11) == [1, 1] assert sequence((1, 2, 4, 7, 28, 128, 582, 2745, 13021, 61699, 292521, \ 1387138), (n, 0, 11)).find_linear_recurrence(12) == [5, -2, 6, -11] assert sequence(x*n**3+y*n, (n, 0, oo)).find_linear_recurrence(10) \ == [4, -6, 4, -1] assert sequence(x**n, (n,0,20)).find_linear_recurrence(21) == [x] assert sequence((1,2,3)).find_linear_recurrence(10, 5) == [0, 0, 1] assert sequence(((1 + sqrt(5))/2)**n + \ (-(1 + sqrt(5))/2)**(-n)).find_linear_recurrence(10) == [1, 1] assert sequence(x*((1 + sqrt(5))/2)**n + y*(-(1 + sqrt(5))/2)**(-n), \ (n,0,oo)).find_linear_recurrence(10) == [1, 1] assert sequence((1,2,3,4,6),(n, 0, 4)).find_linear_recurrence(5) == [] assert sequence((2,3,4,5,6,79),(n, 0, 5)).find_linear_recurrence(6,gfvar=x) \ == ([], None) assert sequence((2,3,4,5,8,30),(n, 0, 5)).find_linear_recurrence(6,gfvar=x) \ == ([Rational(19, 2), -20, Rational(27, 2)], (-31*x**2 + 32*x - 4)/(27*x**3 - 40*x**2 + 19*x -2)) assert sequence(fibonacci(n)).find_linear_recurrence(30,gfvar=x) \ == ([1, 1], -x/(x**2 + x - 1)) assert sequence(tribonacci(n)).find_linear_recurrence(30,gfvar=x) \ == ([1, 1, 1], -x/(x**3 + x**2 + x - 1))
def _compute_fps(f, x, x0, dir, hyper, order, rational, full): """Recursive wrapper to compute fps. See :func:`compute_fps` for details. """ if x0 in [S.Infinity, -S.Infinity]: dir = S.One if x0 is S.Infinity else -S.One temp = f.subs(x, 1/x) result = _compute_fps(temp, x, 0, dir, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, 1/x), result[2].subs(x, 1/x)) elif x0 or dir == -S.One: if dir == -S.One: rep = -x + x0 rep2 = -x rep2b = x0 else: rep = x + x0 rep2 = x rep2b = -x0 temp = f.subs(x, rep) result = _compute_fps(temp, x, 0, S.One, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, rep2 + rep2b), result[2].subs(x, rep2 + rep2b)) if f.is_polynomial(x): return None # Break instances of Add # this allows application of different # algorithms on different terms increasing the # range of admissible functions. if isinstance(f, Add): result = False ak = sequence(S.Zero, (0, oo)) ind, xk = S.Zero, None for t in Add.make_args(f): res = _compute_fps(t, x, 0, S.One, hyper, order, rational, full) if res: if not result: result = True xk = res[1] if res[0].start > ak.start: seq = ak s, f = ak.start, res[0].start else: seq = res[0] s, f = res[0].start, ak.start save = Add(*[z[0]*z[1] for z in zip(seq[0:(f - s)], xk[s:f])]) ak += res[0] ind += res[2] + save else: ind += t if result: return ak, xk, ind return None result = None # from here on it's x0=0 and dir=1 handling k = Dummy('k') if rational: result = rational_algorithm(f, x, k, order, full) if result is None and hyper: result = hyper_algorithm(f, x, k, order) if result is None: return None ak = sequence(result[0], (k, result[2], oo)) xk = sequence(x**k, (k, 0, oo)) ind = result[1] return ak, xk, ind
def rsolve_hypergeometric(f, x, P, Q, k, m): """Solves RE of hypergeometric type. Attempts to solve RE of the form Q(k)*a(k + m) - P(k)*a(k) Transformations that preserve Hypergeometric type: a. x**n*f(x): b(k + m) = R(k - n)*b(k) b. f(A*x): b(k + m) = A**m*R(k)*b(k) c. f(x**n): b(k + n*m) = R(k/n)*b(k) d. f(x**(1/m)): b(k + 1) = R(k*m)*b(k) e. f'(x): b(k + m) = ((k + m + 1)/(k + 1))*R(k + 1)*b(k) Some of these transformations have been used to solve the RE. Returns ======= formula : Expr ind : Expr Independent terms. order : int Examples ======== >>> from sympy import exp, ln, S >>> from sympy.series.formal import rsolve_hypergeometric as rh >>> from sympy.abc import x, k >>> rh(exp(x), x, -S.One, (k + 1), k, 1) (Piecewise((1/factorial(k), Eq(Mod(k, 1), 0)), (0, True)), 1, 1) >>> rh(ln(1 + x), x, k**2, k*(k + 1), k, 1) (Piecewise(((-1)**(k - 1)*factorial(k - 1)/RisingFactorial(2, k - 1), Eq(Mod(k, 1), 0)), (0, True)), x, 2) References ========== .. [1] Formal Power Series - Dominik Gruntz, Wolfram Koepf .. [2] Power Series in Computer Algebra - Wolfram Koepf """ result = _rsolve_hypergeometric(f, x, P, Q, k, m) if result is None: return None sol_list, ind, mp = result sol_dict = defaultdict(lambda: S.Zero) for res, cond in sol_list: j, mk = cond.as_coeff_Add() c = mk.coeff(k) if j.is_integer is False: res *= x**frac(j) j = floor(j) res = res.subs(k, (k - j) / c) cond = Eq(k % c, j % c) sol_dict[cond] += res # Group together formula for same conditions sol = [] for cond, res in sol_dict.items(): sol.append((res, cond)) sol.append((S.Zero, True)) sol = Piecewise(*sol) if mp is -oo: s = S.Zero elif mp.is_integer is False: s = ceiling(mp) else: s = mp + 1 # save all the terms of # form 1/x**k in ind if s < 0: ind += sum(sequence(sol * x**k, (k, s, -1))) s = S.Zero return (sol, ind, s)
def _compute_fps(f, x, x0, dir, hyper, order, rational, full, extract=True): """Recursive wrapper to compute fps. See :func:`compute_fps` for details. """ if x0 in [S.Infinity, -S.Infinity]: dir = S.One if x0 is S.Infinity else -S.One temp = f.subs(x, 1/x) result = _compute_fps(temp, x, 0, dir, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, 1/x), result[2].subs(x, 1/x), result[3].subs(x, 1/x)) elif x0 or dir == -S.One: if dir == -S.One: rep = -x + x0 rep2 = -x rep2b = x0 else: rep = x + x0 rep2 = x rep2b = -x0 temp = f.subs(x, rep) result = _compute_fps(temp, x, 0, S.One, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, rep2 + rep2b), result[2].subs(x, rep2 + rep2b), result[3].subs(x, rep2 + rep2b)) if f.is_polynomial(x): return None # extract x**n from f(x) mul = S.One if extract and f.free_symbols.difference(set([x])): m = Wild('m') n = Wild('n', exclude=[m]) f = f.factor().powsimp() s = f.match(x**n*m) if s[n]: for t in Add.make_args(s[n]): if t.has(Symbol): mul *= (x)**t if mul is not S.One: f = (f / mul) f = f.expand() # Break instances of Add # this allows application of different # algorithms on different terms increasing the # range of admissible functions. if isinstance(f, Add): result = False ak = sequence(S.Zero, (0, oo)) ind, xk = S.Zero, None for t in Add.make_args(f): res = _compute_fps(t, x, 0, S.One, hyper, order, rational, full, False) if res: if not result: result = True xk = res[1] if res[0].start > ak.start: seq = ak s, f = ak.start, res[0].start else: seq = res[0] s, f = res[0].start, ak.start save = Add(*[z[0]*z[1] for z in zip(seq[0:(f - s)], xk[s:f])]) ak += res[0] ind += res[2] + save else: ind += t if result: return ak, xk, ind, mul return None result = None # from here on it's x0=0 and dir=1 handling k = Dummy('k') if rational: result = rational_algorithm(f, x, k, order, full) if result is None and hyper: result = hyper_algorithm(f, x, k, order) if result is None: return None ak = sequence(result[0], (k, result[2], oo)) xk = sequence(x**k, (k, 0, oo)) ind = result[1] return ak, xk, ind, mul
def inverse(self, x=None, n=6): r""" Returns the truncated terms of the inverse of the formal power series, up to specified `n`. If `f` and `g` are two formal power series of two different functions, then the coefficient sequence ``ak`` of the composed formal power series `fp` will be as follows. .. math:: \sum\limits_{k=0}^{n} (-1)^{k} x_0^{-k-1} B_{n,k}(x_1, x_2, \dotsc, x_{n-k+1}) Parameters ========== n : Number, optional Specifies the order of the term up to which the polynomial should be truncated. Examples ======== >>> from sympy import fps, exp, cos, bell >>> from sympy.abc import x >>> f1 = fps(exp(x)) >>> f2 = fps(cos(x)) >>> f1.inverse(x) 1 - x + x**2/2 - x**3/6 + x**4/24 - x**5/120 + O(x**6) >>> f2.inverse(x, n=8) 1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + O(x**8) See Also ======== sympy.functions.combinatorial.numbers.bell References ========== .. [1] Comtet, Louis: Advanced combinatorics; the art of finite and infinite expansions. Reidel, 1974. """ if x is None: x = self.x if n is None: return iter(self) f = self.function if self._eval_term(0) is S.Zero: raise ValueError( "Constant coefficient should exist for an inverse of a formal" " power series to exist.") inv = self._eval_term(0) k = Dummy('k') terms = [] inv_seq = sequence(inv**(-(k + 1)), (k, 1, oo)) aux_seq = self.sign_seq * self.fact_seq * inv_seq for i in range(1, n): bell_seq = self.coeff_bell(i) seq = (aux_seq * bell_seq) terms.append( Add(*(seq[:i])) * self.xk.coeff(i) / self.fact_seq[i - 1]) return self._eval_term(0) + Add(*terms) + Order( self.xk.coeff(n), (self.x, self.x0))
def _compute_fps(f, x, x0, dir, hyper, order, rational, full): """Recursive wrapper to compute fps. See :func:`compute_fps` for details. """ if x0 in [S.Infinity, S.NegativeInfinity]: dir = S.One if x0 is S.Infinity else -S.One temp = f.subs(x, 1/x) result = _compute_fps(temp, x, 0, dir, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, 1/x), result[2].subs(x, 1/x)) elif x0 or dir == -S.One: if dir == -S.One: rep = -x + x0 rep2 = -x rep2b = x0 else: rep = x + x0 rep2 = x rep2b = -x0 temp = f.subs(x, rep) result = _compute_fps(temp, x, 0, S.One, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, rep2 + rep2b), result[2].subs(x, rep2 + rep2b)) if f.is_polynomial(x): k = Dummy('k') ak = sequence(Coeff(f, x, k), (k, 1, oo)) xk = sequence(x**k, (k, 0, oo)) ind = f.coeff(x, 0) return ak, xk, ind # Break instances of Add # this allows application of different # algorithms on different terms increasing the # range of admissible functions. if isinstance(f, Add): result = False ak = sequence(S.Zero, (0, oo)) ind, xk = S.Zero, None for t in Add.make_args(f): res = _compute_fps(t, x, 0, S.One, hyper, order, rational, full) if res: if not result: result = True xk = res[1] if res[0].start > ak.start: seq = ak s, f = ak.start, res[0].start else: seq = res[0] s, f = res[0].start, ak.start save = Add(*[z[0]*z[1] for z in zip(seq[0:(f - s)], xk[s:f])]) ak += res[0] ind += res[2] + save else: ind += t if result: return ak, xk, ind return None # The symbolic term - symb, if present, is being separated from the function # Otherwise symb is being set to S.One syms = f.free_symbols.difference({x}) (f, symb) = expand(f).as_independent(*syms) if symb.is_zero: symb = S.One symb = powsimp(symb) result = None # from here on it's x0=0 and dir=1 handling k = Dummy('k') if rational: result = rational_algorithm(f, x, k, order, full) if result is None and hyper: result = hyper_algorithm(f, x, k, order) if result is None: return None ak = sequence(result[0], (k, result[2], oo)) xk_formula = powsimp(x**k * symb) xk = sequence(xk_formula, (k, 0, oo)) ind = powsimp(result[1] * symb) return ak, xk, ind
def _compute_fps(f, x, x0, dir, hyper, order, rational, full): """Recursive wrapper to compute fps. See :func:`compute_fps` for details. """ if x0 in [S.Infinity, -S.Infinity]: dir = S.One if x0 is S.Infinity else -S.One temp = f.subs(x, 1 / x) result = _compute_fps(temp, x, 0, dir, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, 1 / x), result[2].subs(x, 1 / x)) elif x0 or dir == -S.One: if dir == -S.One: rep = -x + x0 rep2 = -x rep2b = x0 else: rep = x + x0 rep2 = x rep2b = -x0 temp = f.subs(x, rep) result = _compute_fps(temp, x, 0, S.One, hyper, order, rational, full) if result is None: return None return (result[0], result[1].subs(x, rep2 + rep2b), result[2].subs(x, rep2 + rep2b)) if f.is_polynomial(x): return None # Break instances of Add # this allows application of different # algorithms on different terms increasing the # range of admissible functions. if isinstance(f, Add): result = False ak = sequence(S.Zero, (0, oo)) ind, xk = S.Zero, None for t in Add.make_args(f): res = _compute_fps(t, x, 0, S.One, hyper, order, rational, full) if res: if not result: result = True xk = res[1] if res[0].start > ak.start: seq = ak s, f = ak.start, res[0].start else: seq = res[0] s, f = res[0].start, ak.start save = Add( *[z[0] * z[1] for z in zip(seq[0:(f - s)], xk[s:f])]) ak += res[0] ind += res[2] + save else: ind += t if result: return ak, xk, ind return None result = None # from here on it's x0=0 and dir=1 handling k = Dummy('k') if rational: result = rational_algorithm(f, x, k, order, full) if result is None and hyper: result = hyper_algorithm(f, x, k, order) if result is None: return None ak = sequence(result[0], (k, result[2], oo)) xk = sequence(x**k, (k, 0, oo)) ind = result[1] return ak, xk, ind
def product(self, other, x=None, n=6): """Multiplies two Formal Power Series, using discrete convolution and return the truncated terms upto specified order. Parameters ========== n : Number, optional Specifies the order of the term up to which the polynomial should be truncated. Examples ======== >>> from sympy import fps, sin, exp, convolution >>> from sympy.abc import x >>> f1 = fps(sin(x)) >>> f2 = fps(exp(x)) >>> f1.product(f2, x, 4) x + x**2 + x**3/3 + O(x**4) See Also ======== sympy.discrete.convolutions """ if x is None: x = self.x if n is None: return iter(self) other = sympify(other) if not isinstance(other, FormalPowerSeries): raise ValueError( "Both series should be an instance of FormalPowerSeries" " class.") if self.dir != other.dir: raise ValueError("Both series should be calculated from the" " same direction.") elif self.x0 != other.x0: raise ValueError("Both series should be calculated about the" " same point.") elif self.x != other.x: raise ValueError("Both series should have the same symbol.") k = self.ak.variables[0] coeff1 = sequence(self.ak.formula, (k, 0, oo)) k = other.ak.variables[0] coeff2 = sequence(other.ak.formula, (k, 0, oo)) conv_coeff = convolution(coeff1[:n], coeff2[:n]) conv_seq = sequence(tuple(conv_coeff), (k, 0, oo)) k = self.xk.variables[0] xk_seq = sequence(self.xk.formula, (k, 0, oo)) terms_seq = xk_seq * conv_seq return Add(*(terms_seq[:n])) + Order(self.xk.coeff(n), (self.x, self.x0))